1package grc72123import (4 "chain"5)67// CallerTeller resolves the acting account at each write as rlm.Previous() —8// the realm that crossed into the realm holding the teller.9//10// SECURITY: this accessor hangs off *PrivateLedger, not *Token, and that is11// load-bearing. A frame-relative teller acts as whoever crossed into its12// holder, so it is only meaningful inside the token's own realm, where the13// wrappers act for a caller who knowingly invoked the collection. Anywhere14// else it is a confused deputy: a realm a user merely calls could move that15// user's tokens. The *Token pointer is published — exported vars, the16// collection facade, grc721reg — while the ledger is not, since NewToken hands17// it to the creating realm and nowhere else. So a foreign realm cannot mint18// one.19//20// Construction privacy alone is not enough: a realm may legally build a teller21// and then export the VALUE. The write methods therefore also verify that the22// invoking realm is the token's own (see guardHome), which leaves a leaked23// teller inert everywhere but home.24func (led *PrivateLedger) CallerTeller() Teller {25 if led == nil {26 panic("Ledger cannot be nil")27 }2829 return &fnTeller{30 accountFn: func(_ int, rlm realm) address {31 return rlm.Previous().Address()32 },33 homeGuard: true,34 Token: led.token,35 }36}3738// Write methods return ErrReadonly.39func (tok *Token) ReadonlyTeller() Teller {40 if tok == nil {41 panic("Token cannot be nil")42 }4344 return &fnTeller{45 accountFn: nil,46 Token: tok,47 }48}4950// Permanently acts as the calling realm (verified via IsCurrent).51func (tok *Token) RealmTeller(_ int, rlm realm) Teller {52 if tok == nil {53 panic("Token cannot be nil")54 }5556 if !rlm.IsCurrent() {57 panic(ErrSpoofedRealm)58 }5960 caller := rlm.Address()6162 return &fnTeller{63 accountFn: func(_ int, _ realm) address {64 return caller65 },66 Token: tok,67 }68}6970// Like RealmTeller but acts as a sub-account derived from slug.71func (tok *Token) RealmSubTeller(_ int, rlm realm, slug string) Teller {72 if tok == nil {73 panic("Token cannot be nil")74 }7576 if !rlm.IsCurrent() {77 panic(ErrSpoofedRealm)78 }7980 account := accountSlugAddr(rlm.Address(), slug)8182 return &fnTeller{83 accountFn: func(_ int, _ realm) address {84 return account85 },86 Token: tok,87 }88}8990// Admin-grade: issuer-only (holds the PrivateLedger). Panics on invalid addr to close the empty-sentinel hole.91func (led *PrivateLedger) ImpersonateTeller(addr address) Teller {92 if led == nil {93 panic("Ledger cannot be nil")94 }9596 if !addr.IsValid() {97 panic(ErrInvalidAddress)98 }99100 return &fnTeller{101 accountFn: func(_ int, _ realm) address {102 return addr103 },104 Token: led.token,105 }106}107108// guardHome confines a frame-relative teller to the token's own realm.109// Construction privacy stops a foreign realm from minting one; this stops a110// minted one from travelling, which is what happens when a realm legally builds111// a teller and then exports the value.112//113// The check is on the invoking realm's path alone — deliberately NOT on whether114// the resolved actor is an end user. Keying on the actor only blocks the case115// where the debited party is the signing user, and leaves two doors open: a116// realm can be charged by a realm it calls, and TransferFrom resolves the117// *spender* from the frame, so a realm reached from an honest hub spends that118// hub's approvals against any owner who granted one. Both are the same defect119// as the original one level up — frame-relative resolution means whoever you120// call can act as you — and neither is reachable once the teller only works at121// home.122//123// The host is compared after stripping any "#subpath" synthesized by realm.Sub,124// so the token's own sub-realms are not falsely rejected.125//126// A foreign realm that needs to move a user's tokens uses the ordinary route:127// the owner Approves it, and it spends as itself through RealmTeller, which is128// eagerly bound to its own address and approval-gated.129//130// The leading int keeps this a plain method.131func (ft *fnTeller) guardHome(_ int, rlm realm) error {132 if !ft.homeGuard {133 return nil134 }135136 host, _, _ := splitPkgSubPath(rlm.PkgPath())137 if host != ft.Token.origRealm {138 return ErrForeignCallerTeller139 }140141 return nil142}143144func (ft *fnTeller) Approve(_ int, rlm realm, to address, tid TokenID) error {145 if ft.accountFn == nil {146 return ErrReadonly147 }148149 if !rlm.IsCurrent() {150 return ErrSpoofedRealm151 }152153 if err := ft.guardHome(0, rlm); err != nil {154 return err155 }156157 caller := ft.accountFn(0, rlm)158159 return ft.Token.ledger.Approve(caller, to, tid)160}161162func (ft *fnTeller) SetApprovalForAll(_ int, rlm realm, operator address, approved bool) error {163 if ft.accountFn == nil {164 return ErrReadonly165 }166167 if !rlm.IsCurrent() {168 return ErrSpoofedRealm169 }170171 if err := ft.guardHome(0, rlm); err != nil {172 return err173 }174175 caller := ft.accountFn(0, rlm)176177 return ft.Token.ledger.SetApprovalForAll(caller, operator, approved)178}179180func (ft *fnTeller) TransferFrom(_ int, rlm realm, from, to address, tid TokenID) error {181 if ft.accountFn == nil {182 return ErrReadonly183 }184185 if !rlm.IsCurrent() {186 return ErrSpoofedRealm187 }188189 if err := ft.guardHome(0, rlm); err != nil {190 return err191 }192193 spender := ft.accountFn(0, rlm)194195 return ft.Token.ledger.TransferFrom(spender, from, to, tid)196}197198func accountSlugAddr(addr address, slug string) address {199 if slug == "" {200 return addr201 }202203 key := addr.String() + "/" + slug204205 return chain.PackageAddress(key)206}207Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.