1package grc20
2
3import (
4 "std"
5)
6
7// CallerTeller returns a GRC20 compatible teller that checks the PrevRealm
8// caller for each call. It's usually safe to expose it publicly to let users
9// manipulate their tokens directly, or for realms to use their allowance.
10func (tok *Token) CallerTeller() Teller {
11 if tok == nil {
12 panic("Token cannot be nil")
13 }
14
15 return &fnTeller{
16 accountFn: func() std.Address {
17 caller := std.PrevRealm().Addr()
18 return caller
19 },
20 Token: tok,
21 }
22}
23
24// ReadonlyTeller is a GRC20 compatible teller that panics for any write operation.
25func (tok *Token) ReadonlyTeller() Teller {
26 if tok == nil {
27 panic("Token cannot be nil")
28 }
29
30 return &fnTeller{
31 accountFn: nil,
32 Token: tok,
33 }
34}
35
36// RealmTeller returns a GRC20 compatible teller that will store the
37// caller realm permanently. Calling anything through this teller will
38// result in allowance or balance changes for the realm that initialized the teller.
39// The initializer of this teller should usually never share the resulting Teller from
40// this method except maybe for advanced delegation flows such as a DAO treasury
41// management.
42func (tok *Token) RealmTeller() Teller {
43 if tok == nil {
44 panic("Token cannot be nil")
45 }
46
47 caller := std.PrevRealm().Addr()
48
49 return &fnTeller{
50 accountFn: func() std.Address {
51 return caller
52 },
53 Token: tok,
54 }
55}
56
57// RealmSubTeller is like RealmTeller but uses the provided slug to derive a
58// subaccount.
59func (tok *Token) RealmSubTeller(slug string) Teller {
60 if tok == nil {
61 panic("Token cannot be nil")
62 }
63
64 caller := std.PrevRealm().Addr()
65 account := accountSlugAddr(caller, slug)
66
67 return &fnTeller{
68 accountFn: func() std.Address {
69 return account
70 },
71 Token: tok,
72 }
73}
74
75// ImpersonateTeller returns a GRC20 compatible teller that impersonates as a
76// specified address. This allows operations to be performed as if they were
77// executed by the given address, enabling the caller to manipulate tokens on
78// behalf of that address.
79//
80// It is particularly useful in scenarios where a contract needs to perform
81// actions on behalf of a user or another account, without exposing the
82// underlying logic or requiring direct access to the user's account. The
83// returned teller will use the provided address for all operations, effectively
84// masking the original caller.
85//
86// This method should be used with caution, as it allows for potentially
87// sensitive operations to be performed under the guise of another address.
88func (ledger *PrivateLedger) ImpersonateTeller(addr std.Address) Teller {
89 if ledger == nil {
90 panic("Ledger cannot be nil")
91 }
92
93 return &fnTeller{
94 accountFn: func() std.Address {
95 return addr
96 },
97 Token: ledger.token,
98 }
99}
100
101// generic tellers methods.
102//
103
104func (ft *fnTeller) Transfer(to std.Address, amount uint64) error {
105 if ft.accountFn == nil {
106 return ErrReadonly
107 }
108 caller := ft.accountFn()
109 return ft.Token.ledger.Transfer(caller, to, amount)
110}
111
112func (ft *fnTeller) Approve(spender std.Address, amount uint64) error {
113 if ft.accountFn == nil {
114 return ErrReadonly
115 }
116 caller := ft.accountFn()
117 return ft.Token.ledger.Approve(caller, spender, amount)
118}
119
120func (ft *fnTeller) TransferFrom(owner, to std.Address, amount uint64) error {
121 if ft.accountFn == nil {
122 return ErrReadonly
123 }
124 spender := ft.accountFn()
125 return ft.Token.ledger.TransferFrom(owner, spender, to, amount)
126}
127
128// helpers
129//
130
131// accountSlugAddr returns the address derived from the specified address and slug.
132func accountSlugAddr(addr std.Address, slug string) std.Address {
133 // XXX: use a new `std.XXX` call for this.
134 if slug == "" {
135 return addr
136 }
137 key := addr.String() + "/" + slug
138 return std.DerivePkgAddr(key) // temporarily using this helper
139}
tellers.gno
3.63 Kb ยท 139 lines