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/r/moul/x/daily/erc721/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
moul / x / daily / erc721
Files
5 (README)(gnomod.toml)
Exported functions
7
Module
gno.land/r/moul/x/daily/erc721/v1
gno
0.9

Files (5)

  • README.mdmarkdown
  • gnomod.tomltoml
  • erc721.gnogno
  • erc721_test.gnogno
  • render_example_test.gnogno
erc721.gnogno
1// Package erc721 is an idiomatic gno.land port of the Solidity ERC-7212// non-fungible token standard. Each token has a unique integer id owned by3// exactly one address; ids are minted sequentially. Ownership, per-owner4// balances and single-token approvals are kept in ordered avl trees so that5// Render can iterate deterministically.6package erc72178import (9	"strconv"1011	"chain"12	"chain/runtime/unsafe"1314	"gno.land/p/moul/kit/store/v0"15	"gno.land/p/nt/avl/v0"16)1718const (19	name   = "Gno NFT"20	symbol = "GNFT"21)2223var (24	// owners assigns the token ids. v0 kept its own nextID plus a key() that25	// zero-padded to width 12; the store keys by seqid, so the iteration order26	// Render depends on is numeric for every uint64 rather than up to 10^12.27	owners = store.Named("erc721: token") // tokenID -> address2829	balances  avl.Tree // owner address (string) -> uint6430	approvals avl.Tree // tokenID (store key) -> approved address31)3233// Mint creates the next token id and assigns it to `to`. Only sequential34// minting is supported (id is returned via the Mint event).35func Mint(cur realm, to address) int64 {36	if !to.IsValid() {37		panic("erc721: mint to invalid address")38	}39	id := int64(owners.Add(to))40	balances.Set(to.String(), balanceOf(to)+1)4142	chain.Emit("Mint", "to", to.String(), "tokenID", strconv.FormatInt(id, 10))43	return id44}4546// Transfer moves token `id` from the caller to `to`. The caller must own the47// token (approvals are cleared on transfer).48func Transfer(cur realm, to address, id int64) {49	if !to.IsValid() {50		panic("erc721: transfer to invalid address")51	}52	caller := unsafe.PreviousRealm().Address()53	from := ownerOf(id) // panics if the token does not exist5455	if caller != from {56		// allow the single-token approved operator too57		if approvedOf(id) != caller {58			panic("erc721: caller is neither owner nor approved")59		}60	}61	if from == to {62		panic("erc721: transfer to current owner")63	}6465	owners.Set(store.ID(id), to)66	balances.Set(from.String(), balanceOf(from)-1)67	balances.Set(to.String(), balanceOf(to)+1)68	approvals.Remove(store.ID(id).Key()) // clear approval on transfer6970	chain.Emit("Transfer", "from", from.String(), "to", to.String(),71		"tokenID", strconv.FormatInt(id, 10))72}7374// Approve grants `spender` the right to transfer token `id`. Only the current75// owner may approve.76func Approve(cur realm, spender address, id int64) {77	caller := unsafe.PreviousRealm().Address()78	owner := ownerOf(id)79	if caller != owner {80		panic("erc721: approve caller is not owner")81	}82	approvals.Set(store.ID(id).Key(), spender)83	chain.Emit("Approval", "owner", owner.String(), "spender", spender.String(),84		"tokenID", strconv.FormatInt(id, 10))85}8687// --- read-only helpers (safe to call from tests and Render) ---8889// ownerOf returns the owner of token `id`, panicking if it does not exist.90// The store's label puts the id in the message, which v0's fixed string91// ("erc721: query for nonexistent token") left out.92func ownerOf(id int64) address {93	return owners.MustGet(store.ID(id)).(address)94}9596// approvedOf returns the approved address for token `id`, or the zero address.97func approvedOf(id int64) address {98	v := approvals.Get(store.ID(id).Key())99	if v == nil {100		return address("")101	}102	return v.(address)103}104105// balanceOf returns how many tokens `owner` holds.106func balanceOf(owner address) uint64 {107	v := balances.Get(owner.String())108	if v == nil {109		return 0110	}111	return v.(uint64)112}113114// exists reports whether token `id` has been minted (and not since moved away).115func exists(id int64) bool {116	return owners.Has(store.ID(id))117}118119// totalSupply returns the number of tokens in circulation. There is no burn,120// so the highest id ever assigned is the count.121func totalSupply() int64 { return int64(owners.LastID()) }122123// OwnerOf is the exported read-only accessor for ownerOf.124func OwnerOf(id int64) address { return ownerOf(id) }125126// BalanceOf is the exported read-only accessor for balanceOf.127func BalanceOf(owner address) uint64 { return balanceOf(owner) }128129// TotalSupply is the exported read-only accessor for totalSupply.130func TotalSupply() int64 { return totalSupply() }131132// Render displays collection metadata and a token -> owner table.133func Render(path string) string {134	out := "# " + name + " (" + symbol + ")\n\n"135	out += "**Total supply:** " + strconv.FormatInt(totalSupply(), 10) + "\n\n"136137	if owners.Len() == 0 {138		out += "_No tokens minted yet._\n"139		return out140	}141142	out += "| Token ID | Owner | Approved |\n"143	out += "|---------:|-------|----------|\n"144	owners.Each(func(id store.ID, v any) {145		owner := v.(address)146		appr := approvedOf(int64(id))147		apprStr := "—"148		if appr != address("") {149			apprStr = appr.String()150		}151		out += "| " + id.String() + " | " +152			owner.String() + " | " + apprStr + " |\n"153	})154	return out155}156

Functions

  • Approve(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, spender string, id int64)

  • BalanceOf(owner string) uint64

  • Mint(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string) int64

  • OwnerOf(id int64) string

  • Render(path string) string

  • TotalSupply() int64

  • Transfer(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, id int64)

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

Rendered

RenderedRawgnoweb ↗

vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.