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}