Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

types.gno

5.42 Kb · 133 lines
  1package grc721
  2
  3import (
  4	"errors"
  5
  6	"gno.land/p/nt/avl/v0"
  7)
  8
  9type TokenID string
 10
 11func (t TokenID) String() string { return string(t) }
 12
 13// A Teller is a capability that acts as some account whenever it writes.
 14// IsCanonicalTeller confirms a Teller was minted by this package — an embedding
 15// forgery fails the check — but it does NOT reveal which account the Teller acts
 16// as: a caller-scoped CallerTeller and the admin-grade ImpersonateTeller (which
 17// acts as an arbitrary address) are both canonical. So it is an authenticity
 18// check on the implementation, not an authorization guard on the acting account;
 19// a caller accepting a Teller from outside must still establish, out of band, what
 20// account that Teller is entitled to act as.
 21// safeTransferFrom is omitted: EIP-721's receiver check needs a registry, and aliasing it would imply false safety.
 22type Teller interface {
 23	GetName() string
 24	GetSymbol() string
 25	ID() string
 26	TotalSupply() int64
 27	BalanceOf(owner address) (int64, error)
 28	OwnerOf(tid TokenID) (address, error)
 29	GetApproved(tid TokenID) (address, error)
 30	IsApprovedForAll(owner, operator address) bool
 31
 32	Approve(_ int, rlm realm, to address, tid TokenID) error
 33	SetApprovalForAll(_ int, rlm realm, operator address, approved bool) error
 34	TransferFrom(_ int, rlm realm, from, to address, tid TokenID) error
 35}
 36
 37// Extension hooks fire on every mint/transfer/burn. This is a trust grant to the
 38// issuer, not an EIP-721 feature: only the issuer can attach one (RegisterExtension
 39// holds the PrivateLedger), but once attached the hook runs arbitrary issuer code on
 40// every movement, a panic in OnMint/OnTransfer/OnBurn aborts the transaction before
 41// the movement is announced — an implicit veto over transfers — and gas scales
 42// linearly with the extension count. Hooks run after the ledger write so they observe
 43// the post-movement state; only a caller that recovers from the panic keeps that write.
 44// Holders of a collection's tokens are therefore trusting the issuer not to freeze or
 45// tax movement through this surface. Hooks take no rlm params (they cannot capture cur);
 46// attach only via RegisterExtension.
 47type Extension interface {
 48	ExtensionKind() string // unique; duplicates rejected at register
 49	OnMint(to address, tid TokenID)
 50	OnTransfer(from, to address, tid TokenID)
 51	OnBurn(tid TokenID)
 52}
 53
 54type ExtensionView interface {
 55	ExtensionKind() string
 56	TokenID() string // core Token.ID
 57}
 58
 59type Token struct {
 60	id     string // origRealm + "." + symbol + "." + id
 61	name   string
 62	symbol string
 63	ledger *PrivateLedger
 64	// origRealm is the PkgPath of the realm that created the token, captured
 65	// unforgeably in NewToken. A frame-relative teller only works there.
 66	origRealm string
 67}
 68
 69type PrivateLedger struct {
 70	token             *Token
 71	totalSupply       int64
 72	owners            avl.Tree // TokenID -> owner address
 73	balances          avl.Tree // owner address -> int64
 74	tokenApprovals    avl.Tree // TokenID -> approved address
 75	operatorApprovals avl.Tree // "owner:operator" -> bool
 76	extensions        []Extension
 77}
 78
 79var (
 80	ErrInvalidTokenId             = errors.New("invalid token id")
 81	ErrInvalidAddress             = errors.New("invalid address")
 82	ErrTokenIdNotApproved         = errors.New("token id not approved for anyone")
 83	ErrApprovalToCurrentOwner     = errors.New("approval to current owner")
 84	ErrCallerIsNotOwner           = errors.New("caller is not token owner")
 85	ErrCannotTransferToSelf       = errors.New("cannot send transfer to self")
 86	ErrTransferFromIncorrectOwner = errors.New("transfer from incorrect owner")
 87	ErrCallerIsNotOwnerOrApproved = errors.New("caller is not token owner or approved")
 88	ErrTokenIdAlreadyExists       = errors.New("token id already exists")
 89	ErrReadonly                   = errors.New("teller is readonly")
 90	ErrSpoofedRealm               = errors.New("rlm does not match the current crossing frame")
 91	ErrForeignCallerTeller        = errors.New("frame-relative teller used outside the token's realm")
 92	ErrNotRealm                   = errors.New("rlm must be a realm (got EOA/origin)")
 93	ErrInvalidName                = errors.New("invalid token name (empty, too long, or contains control chars)")
 94	ErrInvalidSymbol              = errors.New("invalid token symbol (empty, too long, or contains chars outside [A-Za-z0-9_-])")
 95)
 96
 97// Symbol charset matches grc20reg slug (embedded in Token.ID / events).
 98const (
 99	MaxNameLen   = 64
100	MaxSymbolLen = 11
101)
102
103const (
104	// NewToken announces every token creation; see NewToken for why it is a
105	// complete provenance signal.
106	NewTokenEvent = "NewToken"
107	// Mint emits from empty addr; burn emits to empty addr (EIP-721).
108	TransferEvent       = "Transfer"
109	ApprovalEvent       = "Approval"
110	ApprovalForAllEvent = "ApprovalForAll"
111)
112
113var zeroAddress = address("")
114
115type fnTeller struct {
116	accountFn func(_ int, rlm realm) address
117	// homeGuard marks a frame-relative teller, whose writes are confined to
118	// the token's own realm. See fnTeller.guardHome.
119	homeGuard bool
120	*Token
121}
122
123var _ Teller = (*fnTeller)(nil)
124
125// IsCanonicalTeller reports whether t was minted by this package, rejecting Tellers
126// forged by embedding *fnTeller in a wrapper. It does NOT discriminate by capability
127// grade: CallerTeller, ReadonlyTeller, RealmTeller, RealmSubTeller and the admin-grade
128// ImpersonateTeller all return true. Do not treat a true result as proof that a Teller
129// is a caller-scoped capability — establish the acting account separately.
130func IsCanonicalTeller(t Teller) bool {
131	_, ok := t.(*fnTeller)
132	return ok
133}