payment.gno
2.78 Kb · 93 lines
1package drops
2
3import (
4 "chain/banker"
5
6 "gno.land/p/samcrew/launchpad/currency/v1"
7 sm "gno.land/p/samcrew/launchpad/safemath/v1"
8 "gno.land/r/nt/grc20reg/v0"
9 "gno.land/r/samcrew/launchpad/config/v1"
10)
11
12// terms is what this realm needs from config.
13type terms struct {
14 treasury address
15 primaryFeeBPS int64 // share of each mint price
16 collectionFee int64 // in the currency asked for
17}
18
19// termsOf reads the terms in force. The collection fee is -1 for a currency
20// that has none.
21func termsOf(currencyKey string) terms {
22 p := config.GetVersion(config.GetCurrentVersion())
23 t := terms{treasury: p.Treasury, primaryFeeBPS: p.NFTPrimaryFeeBPS, collectionFee: -1}
24 for _, c := range p.Currencies {
25 if c.Key == currencyKey {
26 t.collectionFee = c.CollectionFee
27 }
28 }
29 return t
30}
31
32// policy returns the terms in force after checking that the lane is open for
33// the currency. It is a variable only so that unit tests can stand in for the
34// config realm, whose admin cannot be set from another package's tests.
35var policy = func(lane, currencyKey string) terms {
36 config.AssertLaneOpen(lane, currencyKey)
37 return termsOf(currencyKey)
38}
39
40// balance reads one denomination, so coins of other denominations sent to
41// the realm never add to the cost of a payment.
42func balance(cur realm, key string) int64 {
43 if key == config.NativeCurrency {
44 return banker.NewReadonlyBanker().GetCoin(cur.Address(), key)
45 }
46 return grc20reg.MustGet(key).BalanceOf(cur.Address())
47}
48
49// take collects price from the direct caller and returns what the realm held
50// before. Native coins come attached: the caller attaches exactly offered, at
51// least the price, and the difference goes straight back. A token is pulled
52// for exactly the price.
53func take(cur realm, key string, price, offered int64) int64 {
54 if offered < price {
55 panic("launchpad/drops: price above the amount offered")
56 }
57 if key != config.NativeCurrency {
58 held := balance(cur, key)
59 if price == 0 {
60 currency.AssertNoCoins(0, cur)
61 } else {
62 currency.CollectToken(0, cur, grc20reg.MustGet(key), price)
63 }
64 return held
65 }
66 if offered == 0 {
67 currency.AssertNoCoins(0, cur)
68 return balance(cur, key)
69 }
70 buyer := currency.CollectNative(0, cur, offered)
71 held := sm.Sub(balance(cur, key), offered)
72 pay(cur, key, buyer, offered-price)
73 return held
74}
75
76func pay(cur realm, key string, to address, amount int64) {
77 if amount == 0 {
78 return
79 }
80 if key == config.NativeCurrency {
81 currency.PayNative(0, cur, to, amount)
82 } else {
83 currency.PayToken(0, cur, grc20reg.MustGet(key), to, amount)
84 }
85}
86
87// settled aborts the transaction unless everything taken has been paid out:
88// the realm must hold exactly what it held before the call.
89func settled(cur realm, key string, held int64) {
90 if balance(cur, key) != held {
91 panic("launchpad/drops: payment not fully settled")
92 }
93}