tellers.gno
3.91 Kb ยท 143 lines
1package grc20
2
3import (
4 "std"
5)
6
7// CallerTeller returns a GRC20 compatible teller that checks the PreviousRealm
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.PreviousRealm().Address()
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.
42// WARN: Should be initialized within a crossing function
43// This way the the realm that created the teller will match CurrentRealm
44func (tok *Token) RealmTeller() Teller {
45 if tok == nil {
46 panic("Token cannot be nil")
47 }
48
49 caller := std.CurrentRealm().Address()
50
51 return &fnTeller{
52 accountFn: func() std.Address {
53 return caller
54 },
55 Token: tok,
56 }
57}
58
59// RealmSubTeller is like RealmTeller but uses the provided slug to derive a
60// subaccount.
61// WARN: Should be initialized within a crossing function
62// This way the realm that created the teller will match CurrentRealm
63func (tok *Token) RealmSubTeller(slug string) Teller {
64 if tok == nil {
65 panic("Token cannot be nil")
66 }
67
68 caller := std.CurrentRealm().Address()
69 account := accountSlugAddr(caller, slug)
70
71 return &fnTeller{
72 accountFn: func() std.Address {
73 return account
74 },
75 Token: tok,
76 }
77}
78
79// ImpersonateTeller returns a GRC20 compatible teller that impersonates as a
80// specified address. This allows operations to be performed as if they were
81// executed by the given address, enabling the caller to manipulate tokens on
82// behalf of that address.
83//
84// It is particularly useful in scenarios where a contract needs to perform
85// actions on behalf of a user or another account, without exposing the
86// underlying logic or requiring direct access to the user's account. The
87// returned teller will use the provided address for all operations, effectively
88// masking the original caller.
89//
90// This method should be used with caution, as it allows for potentially
91// sensitive operations to be performed under the guise of another address.
92func (ledger *PrivateLedger) ImpersonateTeller(addr std.Address) Teller {
93 if ledger == nil {
94 panic("Ledger cannot be nil")
95 }
96
97 return &fnTeller{
98 accountFn: func() std.Address {
99 return addr
100 },
101 Token: ledger.token,
102 }
103}
104
105// generic tellers methods.
106//
107
108func (ft *fnTeller) Transfer(to std.Address, amount int64) error {
109 if ft.accountFn == nil {
110 return ErrReadonly
111 }
112 caller := ft.accountFn()
113 return ft.Token.ledger.Transfer(caller, to, amount)
114}
115
116func (ft *fnTeller) Approve(spender std.Address, amount int64) error {
117 if ft.accountFn == nil {
118 return ErrReadonly
119 }
120 caller := ft.accountFn()
121 return ft.Token.ledger.Approve(caller, spender, amount)
122}
123
124func (ft *fnTeller) TransferFrom(owner, to std.Address, amount int64) error {
125 if ft.accountFn == nil {
126 return ErrReadonly
127 }
128 spender := ft.accountFn()
129 return ft.Token.ledger.TransferFrom(owner, spender, to, amount)
130}
131
132// helpers
133//
134
135// accountSlugAddr returns the address derived from the specified address and slug.
136func accountSlugAddr(addr std.Address, slug string) std.Address {
137 // XXX: use a new `std.XXX` call for this.
138 if slug == "" {
139 return addr
140 }
141 key := addr.String() + "/" + slug
142 return std.DerivePkgAddr(key) // temporarily using this helper
143}