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/g1n4pl5uc4yt5r96m9w6fmdznx3x0jyg8l6arhmt/bazaar/grc721/v0

Package
Open in gnoweb ↗

Overview

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

Files (4)

  • gnomod.tomltoml
  • tellers.gnogno
  • token.gnogno
  • types.gnogno
token.gnogno
1package grc72123import (4	"chain"5	"math/overflow"6	"strconv"78	"gno.land/p/nt/seqid/v0"9	"gno.land/p/nt/ufmt/v0"10)1112// NewToken creates a core token and its ledger.13// rlm must be the caller's own captured cur (IsCurrent); its PkgPath becomes the unforgeable origRealm.14//15// Every successful call emits a NewToken event carrying the resulting Token.ID().16// Because Token's fields are unexported, NewToken is the only way a Token can come17// into existence, so this event makes token creation fully observable: an indexer18// that sees the same Token.ID() announced twice knows the realm built two19// independent ledgers behind one identifier, and that every later20// Mint/Burn/Transfer/Approval carrying that id is ambiguous. Such a realm is21// emitting untrustworthy events and should be flagged or ignored wholesale.22func NewToken(name, symbol string, id seqid.ID, rlm realm) (*Token, *PrivateLedger) {23	if !rlm.IsCurrent() {24		panic(ErrSpoofedRealm)25	}2627	pkgPath := rlm.PkgPath()28	if pkgPath == "" {29		panic(ErrNotRealm)30	}3132	if !validName(name) {33		panic(ErrInvalidName)34	}3536	if !validSymbol(symbol) {37		panic(ErrInvalidSymbol)38	}3940	// origRealm is the host path, with any "#subpath" synthesized by realm.Sub41	// stripped. guardHome strips the invoking path the same way, so a token42	// created from a sub frame is not locked out of its own host realm, and a43	// host's sub-realms are not falsely rejected. Token.ID() keeps the raw44	// pkgPath, so identities and the NewToken event are unaffected.45	origRealm, _, _ := splitPkgSubPath(pkgPath)4647	ledger := &PrivateLedger{}48	token := &Token{49		id:        pkgPath + "." + symbol + "." + id.String(),50		name:      name,51		symbol:    symbol,52		ledger:    ledger,53		origRealm: origRealm,54	}55	ledger.token = token5657	chain.Emit(58		NewTokenEvent,59		"token", token.id,60		"name", name,61		"symbol", symbol,62	)6364	return token, ledger65}6667func validName(name string) bool {68	if name == "" || len(name) > MaxNameLen {69		return false70	}7172	for _, c := range name {73		if c < 0x20 || c == 0x7f {74			return false75		}76	}7778	return true79}8081func validSymbol(s string) bool {82	if s == "" || len(s) > MaxSymbolLen {83		return false84	}8586	for _, c := range s {87		if !isAlnum(c) && c != '_' && c != '-' {88			return false89		}90	}9192	return true93}9495func isAlnum(c rune) bool {96	return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')97}9899func (tok *Token) GetName() string { return tok.name }100101func (tok *Token) GetSymbol() string { return tok.symbol }102103func (tok *Token) ID() string { return tok.id }104105func (tok *Token) TotalSupply() int64 { return tok.ledger.totalSupply }106107func (tok *Token) KnownAccounts() int { return tok.ledger.balances.Size() }108109// EIP-721: balanceOf throws for queries about the zero address,110// since NFTs assigned to it are considered invalid.111func (tok *Token) BalanceOf(addr address) (int64, error) {112	if !addr.IsValid() {113		return 0, ErrInvalidAddress114	}115116	return tok.ledger.balanceOf(addr), nil117}118119func (tok *Token) OwnerOf(tid TokenID) (address, error) { return tok.ledger.ownerOf(tid) }120121// EIP-721: getApproved throws for a token that is not a valid NFT, so a missing122// token is ErrInvalidTokenId and a live but unapproved one is ErrTokenIdNotApproved.123func (tok *Token) GetApproved(tid TokenID) (address, error) {124	if !tok.ledger.exists(tid) {125		return zeroAddress, ErrInvalidTokenId126	}127128	addr := tok.ledger.tokenApprovals.Get(tid.String())129	if addr == nil {130		return zeroAddress, ErrTokenIdNotApproved131	}132133	return addr.(address), nil134}135136func (tok *Token) IsApprovedForAll(owner, operator address) bool {137	return tok.ledger.isApprovedForAll(owner, operator)138}139140func (tok *Token) RenderHome() string {141	str := ""142	str += ufmt.Sprintf("# %s ($%s)\n\n", tok.name, tok.symbol)143	str += ufmt.Sprintf("* **Total supply**: %d\n", tok.ledger.totalSupply)144	str += ufmt.Sprintf("* **Known accounts**: %d\n", tok.KnownAccounts())145146	return str147}148149// RegisterExtension attaches an issuer-supplied hook set. Issuer-only (holds the150// PrivateLedger), which keeps the surface from being attacker-attachable — but the151// attachment is still a privilege: per the Extension doc, an attached hook can veto152// transfers (a panic aborts the transaction before the Transfer event is emitted) and153// adds gas to every movement.154// One extension per kind; a second registration of an attached kind panics.155func (led *PrivateLedger) RegisterExtension(ext Extension) {156	if ext == nil {157		panic("grc721: nil extension")158	}159160	kind := ext.ExtensionKind()161	for _, e := range led.extensions {162		if e.ExtensionKind() == kind {163			panic("grc721: extension kind already registered: " + kind)164		}165	}166167	led.extensions = append(led.extensions, ext)168}169170func (led *PrivateLedger) ReadToken() *Token { return led.token }171172func (led *PrivateLedger) Mint(to address, tid TokenID) error {173	if !to.IsValid() {174		return ErrInvalidAddress175	}176177	if tid == "" {178		return ErrInvalidTokenId179	}180181	if led.exists(tid) {182		return ErrTokenIdAlreadyExists183	}184185	led.balances.Set(to.String(), overflow.Add64p(led.balanceOf(to), 1))186	led.owners.Set(tid.String(), to)187	led.totalSupply = overflow.Add64p(led.totalSupply, 1)188189	for _, ext := range led.extensions {190		ext.OnMint(to, tid)191	}192193	// Mint emits Transfer from the empty address (EIP-721); indexers reconstruct ownership from Transfer alone.194	chain.Emit(195		TransferEvent,196		"token", led.token.id,197		"from", "",198		"to", to.String(),199		"tokenId", tid.String(),200	)201202	return nil203}204205// Fans OnBurn out to every extension so per-token state cannot resurface if tid is re-minted.206func (led *PrivateLedger) Burn(tid TokenID) error {207	owner, err := led.ownerOf(tid)208	if err != nil {209		return err210	}211212	tidStr := tid.String()213	led.tokenApprovals.Remove(tidStr)214	led.setBalance(owner, overflow.Sub64p(led.balanceOf(owner), 1))215	led.owners.Remove(tidStr)216	led.totalSupply = overflow.Sub64p(led.totalSupply, 1)217218	for _, ext := range led.extensions {219		ext.OnBurn(tid)220	}221222	// Burn emits Transfer to the empty address (EIP-721).223	chain.Emit(224		TransferEvent,225		"token", led.token.id,226		"from", owner.String(),227		"to", "",228		"tokenId", tidStr,229	)230231	return nil232}233234// spender must own or be approved for tid; callers derive it from a trusted per-frame identity.235func (led *PrivateLedger) TransferFrom(spender, from, to address, tid TokenID) error {236	if !led.isApprovedOrOwner(spender, tid) {237		return ErrCallerIsNotOwnerOrApproved238	}239240	return led.transfer(from, to, tid)241}242243// caller must own tid or be an approved operator of the owner.244// EIP-721: approving the zero address clears (revokes) any single-token approval.245func (led *PrivateLedger) Approve(caller, to address, tid TokenID) error {246	revoke := to == zeroAddress247	if !revoke && !to.IsValid() {248		return ErrInvalidAddress249	}250251	owner, err := led.ownerOf(tid)252	if err != nil {253		return err254	}255256	if owner == to {257		return ErrApprovalToCurrentOwner258	}259260	if caller != owner && !led.isApprovedForAll(owner, caller) {261		return ErrCallerIsNotOwnerOrApproved262	}263264	if revoke {265		led.tokenApprovals.Remove(tid.String())266	} else {267		led.tokenApprovals.Set(tid.String(), to)268	}269270	chain.Emit(271		ApprovalEvent,272		"token", led.token.id,273		"owner", owner.String(),274		"to", to.String(), // empty for a revoke275		"tokenId", tid.String(),276	)277278	return nil279}280281func (led *PrivateLedger) SetApprovalForAll(owner, operator address, approved bool) error {282	if !owner.IsValid() || !operator.IsValid() {283		return ErrInvalidAddress284	}285286	if owner == operator {287		return ErrApprovalToCurrentOwner288	}289290	key := operatorKey(owner, operator)291	if approved {292		led.operatorApprovals.Set(key, approved)293	} else {294		led.operatorApprovals.Remove(key)295	}296297	chain.Emit(298		ApprovalForAllEvent,299		"token", led.token.id,300		"owner", owner.String(),301		"to", operator.String(),302		"approved", strconv.FormatBool(approved),303	)304305	return nil306}307308func (led *PrivateLedger) transfer(from, to address, tid TokenID) error {309	if !from.IsValid() || !to.IsValid() {310		return ErrInvalidAddress311	}312313	if from == to {314		return ErrCannotTransferToSelf315	}316317	owner, err := led.ownerOf(tid)318	if err != nil {319		return err320	}321322	if owner != from {323		return ErrTransferFromIncorrectOwner324	}325326	tidStr := tid.String()327	led.tokenApprovals.Remove(tidStr)328	led.setBalance(from, overflow.Sub64p(led.balanceOf(from), 1))329	led.setBalance(to, overflow.Add64p(led.balanceOf(to), 1))330	led.owners.Set(tidStr, to)331332	for _, ext := range led.extensions {333		ext.OnTransfer(from, to, tid)334	}335336	chain.Emit(337		TransferEvent,338		"token", led.token.id,339		"from", from.String(),340		"to", to.String(),341		"tokenId", tidStr,342	)343344	return nil345}346347func (led *PrivateLedger) ownerOf(tid TokenID) (address, error) {348	owner := led.owners.Get(tid.String())349	if owner == nil {350		return zeroAddress, ErrInvalidTokenId351	}352353	return owner.(address), nil354}355356func (led *PrivateLedger) balanceOf(addr address) int64 {357	balance := led.balances.Get(addr.String())358	if balance == nil {359		return 0360	}361362	return balance.(int64)363}364365func (led *PrivateLedger) setBalance(addr address, balance int64) {366	if balance == 0 {367		led.balances.Remove(addr.String())368369		return370	}371372	led.balances.Set(addr.String(), balance)373}374375func (led *PrivateLedger) isApprovedForAll(owner, operator address) bool {376	approved := led.operatorApprovals.Get(operatorKey(owner, operator))377	if approved == nil {378		return false379	}380381	return approved.(bool)382}383384func (led *PrivateLedger) isApprovedOrOwner(addr address, tid TokenID) bool {385	owner := led.owners.Get(tid.String())386	if owner == nil {387		return false388	}389390	ownerAddr := owner.(address)391	if addr == ownerAddr || led.isApprovedForAll(ownerAddr, addr) {392		return true393	}394395	approved := led.tokenApprovals.Get(tid.String())396397	return approved != nil && approved.(address) == addr398}399400func (led *PrivateLedger) exists(tid TokenID) bool {401	return led.owners.Has(tid.String())402}403404func operatorKey(owner, operator address) string {405	return owner.String() + ":" + operator.String()406}407

Functions

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

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