Search Apps Documentation Source Content File Folder Download Copy Actions Download

tellers.gno

4.26 Kb · 159 lines
  1package grc20
  2
  3import (
  4	"chain"
  5)
  6
  7// CallerTeller returns a GRC20 compatible teller that, at each write call,
  8// resolves the caller as rlm.Previous() — the realm that crossed into the
  9// caller. rlm must be the caller's own captured cur (asserted via
 10// rlm.IsCurrent() inside the Teller methods).
 11func (tok *Token) CallerTeller() Teller {
 12	if tok == nil {
 13		panic("Token cannot be nil")
 14	}
 15
 16	return &fnTeller{
 17		accountFn: func(_ int, rlm realm) address {
 18			return rlm.Previous().Address()
 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//
 43// rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()).
 44// The address is frozen eagerly at construction.
 45func (tok *Token) RealmTeller(_ int, rlm realm) Teller {
 46	if tok == nil {
 47		panic("Token cannot be nil")
 48	}
 49	if !rlm.IsCurrent() {
 50		panic(ErrSpoofedRealm)
 51	}
 52
 53	caller := rlm.Address()
 54
 55	return &fnTeller{
 56		accountFn: func(_ int, _ realm) address {
 57			return caller
 58		},
 59		Token: tok,
 60	}
 61}
 62
 63// RealmSubTeller is like RealmTeller but uses the provided slug to derive a
 64// subaccount.
 65//
 66// rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()).
 67// The subaccount address is frozen eagerly at construction.
 68func (tok *Token) RealmSubTeller(_ int, rlm realm, slug string) Teller {
 69	if tok == nil {
 70		panic("Token cannot be nil")
 71	}
 72	if !rlm.IsCurrent() {
 73		panic(ErrSpoofedRealm)
 74	}
 75
 76	account := accountSlugAddr(rlm.Address(), slug)
 77
 78	return &fnTeller{
 79		accountFn: func(_ int, _ realm) address {
 80			return account
 81		},
 82		Token: tok,
 83	}
 84}
 85
 86// ImpersonateTeller returns a GRC20 compatible teller that impersonates as a
 87// specified address. This allows operations to be performed as if they were
 88// executed by the given address, enabling the caller to manipulate tokens on
 89// behalf of that address.
 90//
 91// It is particularly useful in scenarios where a contract needs to perform
 92// actions on behalf of a user or another account, without exposing the
 93// underlying logic or requiring direct access to the user's account. The
 94// returned teller will use the provided address for all operations, effectively
 95// masking the original caller.
 96//
 97// This method should be used with caution, as it allows for potentially
 98// sensitive operations to be performed under the guise of another address.
 99func (ledger *PrivateLedger) ImpersonateTeller(addr address) Teller {
100	if ledger == nil {
101		panic("Ledger cannot be nil")
102	}
103
104	return &fnTeller{
105		accountFn: func(_ int, _ realm) address {
106			return addr
107		},
108		Token: ledger.token,
109	}
110}
111
112// generic tellers methods.
113//
114
115func (ft *fnTeller) Transfer(_ int, rlm realm, to address, amount int64) error {
116	if ft.accountFn == nil {
117		return ErrReadonly
118	}
119	if !rlm.IsCurrent() {
120		return ErrSpoofedRealm
121	}
122	caller := ft.accountFn(0, rlm)
123	return ft.Token.ledger.Transfer(caller, to, amount)
124}
125
126func (ft *fnTeller) Approve(_ int, rlm realm, spender address, amount int64) error {
127	if ft.accountFn == nil {
128		return ErrReadonly
129	}
130	if !rlm.IsCurrent() {
131		return ErrSpoofedRealm
132	}
133	caller := ft.accountFn(0, rlm)
134	return ft.Token.ledger.Approve(caller, spender, amount)
135}
136
137func (ft *fnTeller) TransferFrom(_ int, rlm realm, owner, to address, amount int64) error {
138	if ft.accountFn == nil {
139		return ErrReadonly
140	}
141	if !rlm.IsCurrent() {
142		return ErrSpoofedRealm
143	}
144	spender := ft.accountFn(0, rlm)
145	return ft.Token.ledger.TransferFrom(owner, spender, to, amount)
146}
147
148// helpers
149//
150
151// accountSlugAddr returns the address derived from the specified address and slug.
152func accountSlugAddr(addr address, slug string) address {
153	// XXX: use a new `std.XXX` call for this.
154	if slug == "" {
155		return addr
156	}
157	key := addr.String() + "/" + slug
158	return chain.PackageAddress(key) // temporarily using this helper
159}