currency.gno
3.20 Kb · 93 lines
1// Package currency moves launchpad payments in native ugnot or any GRC20 token.
2//
3// Every function takes the calling realm's own cur as rlm, so funds always move
4// to or from that realm and never from a realm the caller can name.
5package currency
6
7import (
8 "chain"
9 "chain/banker"
10 "chain/runtime/unsafe"
11
12 "gno.land/p/nt/grc20/v0"
13 "gno.land/p/samcrew/launchpad/meta/v1"
14)
15
16const NativeDenom = "ugnot"
17
18// CollectNative accepts exactly amount ugnot attached by the user who called
19// the realm directly, and returns that user. Coins attached to a call that
20// reaches the realm through another realm belong to that other realm and are
21// refused.
22func CollectNative(_ int, rlm realm, amount int64) address {
23 if !rlm.IsCurrent() {
24 panic("launchpad/currency: rlm is not the current realm")
25 }
26 prev := rlm.Previous()
27 if !prev.IsUserCall() {
28 panic("launchpad/currency: native payment must be attached to a direct user call")
29 }
30 sent := unsafe.OriginSend()
31 if len(sent) != 1 || sent[0].Denom != NativeDenom || sent[0].Amount != amount {
32 panic("launchpad/currency: attached coins do not match the amount due")
33 }
34 return prev.Address()
35}
36
37// AssertNoCoins refuses coins attached by a user who calls the realm directly:
38// they would land in the realm, which keeps no account of them. Coins attached
39// to a call that reaches the realm through another realm went to that other
40// realm and are its business. The pinned chain also fails a call whose coins
41// no code reads; this check does not depend on that.
42func AssertNoCoins(_ int, rlm realm) {
43 if !rlm.IsCurrent() {
44 panic("launchpad/currency: rlm is not the current realm")
45 }
46 if rlm.Previous().IsUserCall() && len(unsafe.OriginSend()) != 0 {
47 panic("launchpad/currency: this call does not accept coins")
48 }
49}
50
51// A token ledger keys balances by the receiver's text, so a receiver in any
52// spelling but the one the chain gives a caller would be paid into a balance
53// no caller can reach.
54func assertReceiver(to address) {
55 if !meta.ValidAddress(to) {
56 panic("launchpad/currency: invalid receiver")
57 }
58}
59
60// PayNative sends amount ugnot from the calling realm.
61func PayNative(_ int, rlm realm, to address, amount int64) {
62 if !rlm.IsCurrent() {
63 panic("launchpad/currency: rlm is not the current realm")
64 }
65 assertReceiver(to)
66 banker.NewBanker(banker.BankerTypeRealmSend, rlm).
67 SendCoins(rlm.Address(), to, chain.Coins{{NativeDenom, amount}})
68}
69
70// CollectToken pulls amount of tok from the realm's direct caller, who must have
71// approved the calling realm, and returns that caller.
72func CollectToken(_ int, rlm realm, tok *grc20.Token, amount int64) address {
73 if !rlm.IsCurrent() {
74 panic("launchpad/currency: rlm is not the current realm")
75 }
76 AssertNoCoins(0, rlm)
77 payer := rlm.Previous().Address()
78 if err := tok.RealmTeller(0, rlm).TransferFrom(0, rlm, payer, rlm.Address(), amount); err != nil {
79 panic(err.Error())
80 }
81 return payer
82}
83
84// PayToken sends amount of tok from the calling realm's own balance.
85func PayToken(_ int, rlm realm, tok *grc20.Token, to address, amount int64) {
86 if !rlm.IsCurrent() {
87 panic("launchpad/currency: rlm is not the current realm")
88 }
89 assertReceiver(to)
90 if err := tok.RealmTeller(0, rlm).Transfer(0, rlm, to, amount); err != nil {
91 panic(err.Error())
92 }
93}