banker_grc20.gno
3.36 Kb · 131 lines
1package treasury
2
3import (
4 "errors"
5 "strconv"
6
7 "gno.land/p/demo/tokens/grc20"
8 "gno.land/p/nt/ufmt/v0"
9)
10
11var (
12 ErrNoListerProvided = errors.New("no lister provided")
13 ErrGRC20TokenNotFound = errors.New("GRC20 token not found")
14)
15
16// GRC20Banker is a Banker that sends GRC20 tokens listed using a getter
17// set during initialization.
18type GRC20Banker struct {
19 owner address // The address of this GRC20 banker owner.
20 lister TokenListerFunc // Allows to list tokens from methods that require it.
21}
22
23// TokenListerFunc is a function type that returns a map of GRC20 tokens.
24type TokenListerFunc func() map[string]*grc20.Token
25
26var _ Banker = (*GRC20Banker)(nil)
27
28// ID implements Banker.
29func (GRC20Banker) ID() string {
30 return "GRC20"
31}
32
33// Send implements Banker.
34//
35// rlm must be the caller's own captured cur (i.e. the cur of the
36// immediate crossing-function caller). Sending with rlm = cur.Previous()
37// or any other realm value is rejected: rlm.IsCurrent() asserts pointer
38// identity against the topmost crossing frame. Combined with the
39// rlm.Address() == gb.owner check, this restricts Send to the owning
40// realm acting in its own frame.
41func (gb *GRC20Banker) Send(_ int, rlm realm, p Payment) error {
42 if !rlm.IsCurrent() {
43 return ErrSpoofedRealm
44 }
45 if rlm.Address() != gb.owner {
46 return ErrCurrentRealmIsNotOwner
47 }
48
49 payment, ok := p.(grc20Payment)
50 if !ok {
51 return ErrInvalidPaymentType
52 }
53
54 // Get the GRC20 tokens using the lister.
55 tokens := gb.lister()
56
57 // Look for the token corresponding to the payment tokenKey.
58 token, ok := tokens[payment.tokenKey]
59 if !ok {
60 return ufmt.Errorf("%v: %s", ErrGRC20TokenNotFound, payment.tokenKey)
61 }
62
63 // Send the token from the owner's balance.
64 return token.RealmTeller(0, rlm).Transfer(0, rlm, payment.toAddress, payment.amount)
65}
66
67// Balances implements Banker.
68func (gb *GRC20Banker) Balances() []Balance {
69 // Get the GRC20 tokens from the lister.
70 tokens := gb.lister()
71
72 // Convert GRC20 tokens to []Balance.
73 var balances []Balance
74 for key, token := range tokens {
75 balances = append(balances, Balance{
76 Denom: key,
77 Amount: token.BalanceOf(gb.owner),
78 })
79 }
80 return balances
81}
82
83// Address implements Banker.
84func (gb *GRC20Banker) Address() string {
85 return gb.owner.String()
86}
87
88// NewGRC20BankerWithOwner creates a new GRC20Banker with the given address.
89func NewGRC20BankerWithOwner(owner address, lister TokenListerFunc) (*GRC20Banker, error) {
90 if owner == "" {
91 return nil, ErrNoOwnerProvided
92 }
93
94 if lister == nil {
95 return nil, ErrNoListerProvided
96 }
97
98 return &GRC20Banker{
99 owner: owner,
100 lister: lister,
101 }, nil
102}
103
104// grc20Payment represents a payment that is issued by a GRC20Banker.
105type grc20Payment struct {
106 tokenKey string // The key associated with the GRC20 token.
107 amount int64 // The amount of token to send.
108 toAddress address // The recipient of the payment.
109}
110
111var _ Payment = (*grc20Payment)(nil)
112
113// BankerID implements Payment.
114func (grc20Payment) BankerID() string {
115 return GRC20Banker{}.ID()
116}
117
118// String implements Payment.
119func (gp grc20Payment) String() string {
120 amount := strconv.Itoa(int(gp.amount))
121 return amount + gp.tokenKey + " to " + gp.toAddress.String()
122}
123
124// NewGRC20Payment creates a new grc20Payment.
125func NewGRC20Payment(tokenKey string, amount int64, toAddress address) Payment {
126 return grc20Payment{
127 tokenKey: tokenKey,
128 amount: amount,
129 toAddress: toAddress,
130 }
131}