tellers.gno

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