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

grc20 source pure

Constants 2

const MaxNameLen, MaxSymbolLen, MaxDecimals

1const (
2	MaxNameLen   = 64
3	MaxSymbolLen = 11
4	MaxDecimals  = 18
5)
source

Construction limits. Symbol is restricted to the same charset as grc20reg.validateSlug because it is included in Token.ID(), which is emitted in events and frequently used as a registry slug; banning `.` `/` and whitespace here prevents downstream parsers from being fooled by ambiguous IDs. Name is for display only and allows any valid UTF-8 except control characters.

Variables 1

var ErrInsufficientBalance, ErrInsufficientAllowance, ErrInvalidAddress, ErrCannotTransferToSelf, ErrReadonly, ErrRestrictedTokenOwner, ErrMintOverflow, ErrInvalidAmount, ErrSpoofedRealm, ErrNotRealm, ErrInvalidName, ErrInvalidSymbol, ErrInvalidDecimals

 1var (
 2	ErrInsufficientBalance   = errors.New("insufficient balance")
 3	ErrInsufficientAllowance = errors.New("insufficient allowance")
 4	ErrInvalidAddress        = errors.New("invalid address")
 5	ErrCannotTransferToSelf  = errors.New("cannot send transfer to self")
 6	ErrReadonly              = errors.New("banker is readonly")
 7	ErrRestrictedTokenOwner  = errors.New("restricted to bank owner")
 8	ErrMintOverflow          = errors.New("mint overflow")
 9	ErrInvalidAmount         = errors.New("invalid amount")
10	ErrSpoofedRealm          = errors.New("rlm does not match the current crossing frame")
11	ErrNotRealm              = errors.New("rlm must be a realm (got EOA/origin)")
12	ErrInvalidName           = errors.New("invalid token name (empty, too long, or contains control chars)")
13	ErrInvalidSymbol         = errors.New("invalid token symbol (empty, too long, or contains chars outside [A-Za-z0-9_-])")
14	ErrInvalidDecimals       = errors.New("invalid decimals (must be 0..18)")
15)
source

Functions 2

func IsCanonicalTeller

1func IsCanonicalTeller(t Teller) bool
source

IsCanonicalTeller reports whether t is the canonical *fnTeller produced by Token.CallerTeller / RealmTeller / RealmSubTeller / ReadonlyTeller / ImpersonateTeller. Use this at any public entry point that accepts a Teller from an external caller before invoking its methods.

Foreign types — including embedding-based wrappers like `type Evil struct { grc20.Teller }` — are rejected because type assertions are nominal: *Evil is not *fnTeller, regardless of method promotion. This is the reliable defense; the unexported-marker "seal" pattern is bypassable via embedding (see p/test/seal/filetests/z_seal_iface_embedding_filetest.gno).

Mirrors the precedent of chain/banker.IsCanonical and p/jaekwon/allowancesender's canonical-impl check.

func NewToken

1func NewToken(name, symbol string, decimals int, id seqid.ID, rlm realm) (*Token, *PrivateLedger)
source

NewToken creates a Token whose origRealm is bound to the calling realm. rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()), and rlm.PkgPath() — the calling realm itself — becomes the Token's origRealm. Token.ID() returns origRealm + "." + symbol + "." + id.

Because IsCurrent runtime-validates that rlm came from the live crossing frame, origRealm is unforgeable: an external realm cannot fabricate a Token claiming to belong to a different package.

Realms that create multiple tokens should allocate id from one persistent seqid.ID, shared by every creation path, to avoid conflicting identifiers:

Example
1var nextTokenID seqid.ID
2Token, ledger := grc20.NewToken("Foo", "FOO", 4, nextTokenID.Next(), cur)

A realm that creates only a single token can pass 0 directly, since no other token of that realm can collide with it.

If the Token should be discoverable, follow up with grc20reg.Register(cross(cur), Token, slug). The registry key is Token.ID().

Types 3

type PrivateLedger

struct
 1type PrivateLedger struct {
 2	// Total supply of the token managed by this ledger.
 3	totalSupply int64
 4	// chain.Address -> int64
 5	balances avl.Tree
 6	// owner.(chain.Address)+":"+spender.(chain.Address)) -> int64
 7	allowances avl.Tree
 8	// Pointer to the associated Token struct
 9	token *Token
10}
source

PrivateLedger is a struct that holds the balances and allowances for the token. It provides administrative functions for minting, burning, transferring tokens, and managing allowances.

The PrivateLedger is not safe to expose publicly, as it contains sensitive information regarding token balances and allowances, and allows direct, unrestricted access to all administrative functions.

Methods on PrivateLedger

func Approve

method on PrivateLedger
1func (led *PrivateLedger) Approve(owner, spender address, amount int64) error
source

Approve sets the allowance of the specified owner and spender.

func Burn

method on PrivateLedger
1func (led *PrivateLedger) Burn(addr address, amount int64) error
source

Burn decreases the total supply of the token and subtracts the specified amount from the specified address.

func ImpersonateTeller

method on PrivateLedger
1func (ledger *PrivateLedger) ImpersonateTeller(addr address) Teller
source

ImpersonateTeller returns a GRC20 compatible teller that impersonates as a specified address. This allows operations to be performed as if they were executed by the given address, enabling the caller to manipulate tokens on behalf of that address.

It is particularly useful in scenarios where a contract needs to perform actions on behalf of a user or another account, without exposing the underlying logic or requiring direct access to the user's account. The returned teller will use the provided address for all operations, effectively masking the original caller.

This method should be used with caution, as it allows for potentially sensitive operations to be performed under the guise of another address.

func Mint

method on PrivateLedger
1func (led *PrivateLedger) Mint(addr address, amount int64) error
source

Mint increases the total supply of the token and adds the specified amount to the specified address.

func SpendAllowance

method on PrivateLedger
1func (led *PrivateLedger) SpendAllowance(owner, spender address, amount int64) error
source

SpendAllowance decreases the allowance of the specified owner and spender.

func Transfer

method on PrivateLedger
1func (led *PrivateLedger) Transfer(from, to address, amount int64) error
source

Transfer transfers tokens from the specified from address to the specified to address.

func TransferFrom

method on PrivateLedger
1func (led *PrivateLedger) TransferFrom(owner, spender, to address, amount int64) error
source

TransferFrom transfers tokens from the specified owner to the specified to address. It first checks if the owner has sufficient balance and then decreases the allowance.

type Teller

interface
 1type Teller interface {
 2	// Returns the name of the token.
 3	GetName() string
 4
 5	// Returns the symbol of the token, usually a shorter version of the
 6	// name.
 7	GetSymbol() string
 8
 9	// Returns the decimals places of the token.
10	GetDecimals() int
11
12	// Returns the amount of tokens in existence.
13	TotalSupply() int64
14
15	// Returns the amount of tokens owned by `account`.
16	BalanceOf(account address) int64
17
18	// Moves `amount` tokens from the caller's account to `to`. rlm must
19	// be the caller's own captured cur — verified via rlm.IsCurrent().
20	//
21	// Returns an error if the operation failed.
22	Transfer(_ int, rlm realm, to address, amount int64) error
23
24	// Returns the remaining number of tokens that `spender` will be
25	// allowed to spend on behalf of `owner` through {transferFrom}. This is
26	// zero by default.
27	//
28	// This value changes when {approve} or {transferFrom} are called.
29	Allowance(owner, spender address) int64
30
31	// Sets `amount` as the allowance of `spender` over the caller's tokens.
32	//
33	// Returns an error if the operation failed.
34	//
35	// IMPORTANT: Beware that changing an allowance with this method brings
36	// the risk that someone may use both the old and the new allowance by
37	// unfortunate transaction ordering. One possible solution to mitigate
38	// this race condition is to first reduce the spender's allowance to 0
39	// and set the desired value afterwards:
40	// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
41	Approve(_ int, rlm realm, spender address, amount int64) error
42
43	// Moves `amount` tokens from `from` to `to` using the
44	// allowance mechanism. `amount` is then deducted from the caller's
45	// allowance.
46	//
47	// Returns an error if the operation failed.
48	TransferFrom(_ int, rlm realm, from, to address, amount int64) error
49}
source

Teller interface defines the methods that a GRC20 token must implement. It extends the TokenMetadata interface to include methods for managing token transfers, allowances, and querying balances.

The Teller interface is designed to ensure that any token adhering to this standard provides a consistent API for interacting with fungible tokens.

SECURITY: Transfer/Approve/TransferFrom take (_ int, rlm realm, ...), so handing a Teller value to untrusted code yields a capability token to whatever Transfer/Approve/TransferFrom impl that code dispatches into. Any /p/ or /r/ function that accepts a Teller as a parameter from external callers MUST type-assert against the canonical concrete type (*fnTeller) via IsCanonicalTeller and reject otherwise. An unexported-marker "seal" does NOT defend against this — see p/test/seal/filetests/z_seal_iface_embedding_filetest.gno for the realistic embedding-bypass attack. Reference impl for the canonical-allowlist pattern: p/jaekwon/allowancesender.

type Token

struct
 1type Token struct {
 2	// Identifier precomputed in NewToken to make ID() a cheap field read.
 3	id string
 4	// Name of the token (e.g., "Dummy Token").
 5	name string
 6	// Symbol of the token (e.g., "DUMMY").
 7	symbol string
 8	// Number of decimal places used for the token's precision.
 9	decimals int
10	// Pointer to the PrivateLedger that manages balances and allowances.
11	ledger *PrivateLedger
12}
source

Token represents a fungible token with an ID, name, symbol, and a certain number of decimal places. It maintains a ledger for tracking balances and allowances of addresses.

The Token struct provides methods for retrieving token metadata, such as the name, symbol, and decimals, as well as methods for interacting with the ledger, including checking balances and allowances.

Methods on Token

func Allowance

method on Token
1func (tok Token) Allowance(owner, spender address) int64
source

Allowance returns the allowance of the specified owner and spender.

func BalanceOf

method on Token
1func (tok Token) BalanceOf(addr address) int64
source

BalanceOf returns the balance of the specified address.

func CallerTeller

method on Token
1func (tok *Token) CallerTeller() Teller
source

CallerTeller returns a GRC20 compatible teller that, at each write call, resolves the caller as rlm.Previous() — the realm that crossed into the caller. rlm must be the caller's own captured cur (asserted via rlm.IsCurrent() inside the Teller methods).

func GetDecimals

method on Token
1func (tok Token) GetDecimals() int
source

GetDecimals returns the number of decimals used to get the token's precision.

func GetName

method on Token
1func (tok Token) GetName() string
source

GetName returns the name of the token.

func GetSymbol

method on Token
1func (tok Token) GetSymbol() string
source

GetSymbol returns the symbol of the token.

func HasAddr

method on Token
1func (tok Token) HasAddr(addr address) bool
source

HasAddr checks if the specified address is a known account in the bank.

func ID

method on Token
1func (tok *Token) ID() string
source

ID returns the Identifier of the token. It is composed of the original realm, the symbol, and the provided id.

func KnownAccounts

method on Token
1func (tok Token) KnownAccounts() int
source

KnownAccounts returns the number of known accounts in the bank.

func ReadonlyTeller

method on Token
1func (tok *Token) ReadonlyTeller() Teller
source

ReadonlyTeller is a GRC20 compatible teller that panics for any write operation.

func RealmSubTeller

method on Token
1func (tok *Token) RealmSubTeller(_ int, rlm realm, slug string) Teller
source

RealmSubTeller is like RealmTeller but uses the provided slug to derive a subaccount.

rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()). The subaccount address is frozen eagerly at construction.

func RealmTeller

method on Token
1func (tok *Token) RealmTeller(_ int, rlm realm) Teller
source

RealmTeller returns a GRC20 compatible teller that will store the caller realm permanently. Calling anything through this teller will result in allowance or balance changes for the realm that initialized the teller. The initializer of this teller should usually never share the resulting Teller from this method except maybe for advanced delegation flows such as a DAO treasury management.

rlm must be the caller's own captured cur (asserted via rlm.IsCurrent()). The address is frozen eagerly at construction.

func TotalSupply

method on Token
1func (tok Token) TotalSupply() int64
source

TotalSupply returns the total supply of the token.

Imports 8

Source Files 9