PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/nt/grc20/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
nt / grc20
Files
12 (gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/nt/grc20/v0
gno
0.9

Files (12)

  • gnomod.tomltoml
  • mock.gnogno
  • tellers.gnogno
  • token.gnogno
  • types.gnogno
  • caller_teller_sub_realm_filetest.gnogno
  • event_provenance_filetest.gno
gno
  • examples_test.gnogno
  • newtoken_event_filetest.gnogno
  • tellers_test.gnogno
  • token_identity_filetest.gnogno
  • token_test.gnogno
  • tellers.gnogno
    1package grc2023import (4	"chain"5)67// CallerTeller returns a GRC20 compatible teller that, at each write call,8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    // 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).
    11//
    12// SECURITY: this accessor hangs off *PrivateLedger, not *Token, and that is
    13// load-bearing. A frame-relative teller debits whoever crossed into the realm
    14// holding it, so it is only ever meaningful inside the token's own realm,
    15// whose wrappers act for a caller who knowingly invoked the token. Anywhere
    16// else it is a confused deputy. The *Token pointer is published (exported
    17// vars, grc20factory, grc20reg) but the ledger is not — NewToken hands it to
    18// the creating realm and nowhere else — so a foreign realm cannot mint one.
    19//
    20// Construction privacy alone is not enough: a realm may legally build a teller
    21// and then export the VALUE. The write methods therefore also verify that the
    22// invoking realm is the token's own (see guardHome), which makes a leaked
    23// teller inert everywhere but home.
    24func (ledger *PrivateLedger) CallerTeller() Teller {
    25 if ledger == nil {
    26 panic("Ledger cannot be nil")
    27 }
    28
    29 return &fnTeller{
    30 accountFn: func(_ int, rlm realm) address {
    31 return rlm.Previous().Address()
    32 },
    33 homeGuard: true,
    34 Token: ledger.token,
    35 }
    36}
    37
    38// ReadonlyTeller is a GRC20 compatible teller that panics for any write operation.
    39func (tok *Token) ReadonlyTeller() Teller {
    40 if tok == nil {
    41 panic("Token cannot be nil")
    42 }
    43
    44 return &fnTeller{
    45 accountFn: nil,
    46 Token: tok,
    47 }
    48}
    49
    50// RealmTeller returns a GRC20 compatible teller that will store the
    51// caller realm permanently. Calling anything through this teller will
    52// result in allowance or balance changes for the realm that initialized the teller.
    53// The initializer of this teller should usually never share the resulting Teller from
    54// this method except maybe for advanced delegation flows such as a DAO treasury
    55// management.
    56//
    57// rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()).
    58// The address is frozen eagerly at construction.
    59func (tok *Token) RealmTeller(_ int, rlm realm) Teller {
    60 if tok == nil {
    61 panic("Token cannot be nil")
    62 }
    63 if !rlm.IsCurrent() {
    64 panic(ErrSpoofedRealm)
    65 }
    66
    67 caller := rlm.Address()
    68
    69 return &fnTeller{
    70 accountFn: func(_ int, _ realm) address {
    71 return caller
    72 },
    73 Token: tok,
    74 }
    75}
    76
    77// RealmSubTeller is like RealmTeller but uses the provided slug to derive a
    78// subaccount.
    79//
    80// rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()).
    81// The subaccount address is frozen eagerly at construction.
    82func (tok *Token) RealmSubTeller(_ int, rlm realm, slug string) Teller {
    83 if tok == nil {
    84 panic("Token cannot be nil")
    85 }
    86 if !rlm.IsCurrent() {
    87 panic(ErrSpoofedRealm)
    88 }
    89
    90 account := accountSlugAddr(rlm.Address(), slug)
    91
    92 return &fnTeller{
    93 accountFn: func(_ int, _ realm) address {
    94 return account
    95 },
    96 Token: tok,
    97 }
    98}
    99
    100// ImpersonateTeller returns a GRC20 compatible teller that impersonates as a
    101// specified address. This allows operations to be performed as if they were
    102// executed by the given address, enabling the caller to manipulate tokens on
    103// behalf of that address.
    104//
    105// It is particularly useful in scenarios where a contract needs to perform
    106// actions on behalf of a user or another account, without exposing the
    107// underlying logic or requiring direct access to the user's account. The
    108// returned teller will use the provided address for all operations, effectively
    109// masking the original caller.
    110//
    111// This method should be used with caution, as it allows for potentially
    112// sensitive operations to be performed under the guise of another address.
    113func (ledger *PrivateLedger) ImpersonateTeller(addr address) Teller {
    114 if ledger == nil {
    115 panic("Ledger cannot be nil")
    116 }
    117
    118 return &fnTeller{
    119 accountFn: func(_ int, _ realm) address {
    120 return addr
    121 },
    122 Token: ledger.token,
    123 }
    124}
    125
    126// generic tellers methods.
    127//
    128
    129// guardHome confines a frame-relative (homeGuard) teller to the token's own
    130// realm. Construction privacy stops a foreign realm from minting one;
    131// this stops a minted one from travelling, which is what happens when a realm
    132// legally builds a teller and then exports the value.
    133//
    134// The check is on the invoking realm's path alone — deliberately NOT on
    135// whether the resolved actor is an end user. Keying on the actor only blocks
    136// the case where the debited party is the signing user, which leaves two doors
    137// open: a realm can be charged by a realm it calls, and TransferFrom resolves
    138// the *spender* from the frame, so a realm reached from an honest hub spends
    139// that hub's allowance against any owner who granted one. Both are the same
    140// defect as the original one level up — frame-relative resolution means
    141// whoever you call can act as you — and neither is reachable once the teller
    142// only works at home.
    143//
    144// The host is compared after stripping any ":subpath" synthesized by
    145// realm.Sub, so the token's own sub-realms are not falsely rejected.
    146//
    147// A foreign realm that needs to move a user's funds uses the ordinary route:
    148// Approve, then RealmTeller().TransferFrom, which is eagerly bound to that
    149// realm's own address and allowance-gated.
    150//
    151// The leading int keeps this a plain method.
    152func (ft *fnTeller) guardHome(_ int, rlm realm) error {
    153 if !ft.homeGuard {
    154 return nil
    155 }
    156 host, _, _ := chain.SplitPkgSubPath(rlm.PkgPath())
    157 if host != ft.Token.origRealm {
    158 return ErrForeignCallerTeller
    159 }
    160 return nil
    161}
    162
    163func (ft *fnTeller) Transfer(_ int, rlm realm, to address, amount int64) error {
    164 if ft.accountFn == nil {
    165 return ErrReadonly
    166 }
    167 if !rlm.IsCurrent() {
    168 return ErrSpoofedRealm
    169 }
    170 if err := ft.guardHome(0, rlm); err != nil {
    171 return err
    172 }
    173 caller := ft.accountFn(0, rlm)
    174 return ft.Token.ledger.Transfer(caller, to, amount)
    175}
    176
    177func (ft *fnTeller) Approve(_ int, rlm realm, spender address, amount int64) error {
    178 if ft.accountFn == nil {
    179 return ErrReadonly
    180 }
    181 if !rlm.IsCurrent() {
    182 return ErrSpoofedRealm
    183 }
    184 if err := ft.guardHome(0, rlm); err != nil {
    185 return err
    186 }
    187 caller := ft.accountFn(0, rlm)
    188 return ft.Token.ledger.Approve(caller, spender, amount)
    189}
    190
    191func (ft *fnTeller) TransferFrom(_ int, rlm realm, owner, to address, amount int64) error {
    192 if ft.accountFn == nil {
    193 return ErrReadonly
    194 }
    195 if !rlm.IsCurrent() {
    196 return ErrSpoofedRealm
    197 }
    198 if err := ft.guardHome(0, rlm); err != nil {
    199 return err
    200 }
    201 spender := ft.accountFn(0, rlm)
    202 return ft.Token.ledger.TransferFrom(owner, spender, to, amount)
    203}
    204
    205// helpers
    206//
    207
    208// accountSlugAddr returns the address derived from the specified address and slug.
    209func accountSlugAddr(addr address, slug string) address {
    210 // XXX: use a new `std.XXX` call for this.
    211 if slug == "" {
    212 return addr
    213 }
    214 key := addr.String() + "/" + slug
    215 return chain.PackageAddress(key) // temporarily using this helper
    216}
    217