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/akkadia/beta/grc721

Package
Open in gnoweb ↗

Overview

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

Files (5)

  • gnomod.tomltoml
  • basic_nft.gnogno
  • errors.gnogno
  • igrc721.gnogno
  • util.gnogno
basic_nft.gnogno
1package grc72123import (4	"chain"5	"math/overflow"6	"strconv"78	"gno.land/p/nt/avl/v0"9	"gno.land/p/nt/ufmt/v0"10)1112type basicNFT struct {13	name              string14	symbol            string15	owners            avl.Tree // tokenId -> OwnerAddress16	balances          avl.Tree // OwnerAddress -> TokenCount17	tokenApprovals    avl.Tree // TokenId -> ApprovedAddress18	tokenURIs         avl.Tree // TokenId -> URIs19	operatorApprovals avl.Tree // "OwnerAddress:OperatorAddress" -> bool20}2122// Returns new basic NFT23func NewBasicNFT(name string, symbol string) *basicNFT {24	return &basicNFT{25		name:   name,26		symbol: symbol,2728		owners:            avl.Tree{},29		balances:          avl.Tree{},30		tokenApprovals:    avl.Tree{},31		tokenURIs:         avl.Tree{},32		operatorApprovals: avl.Tree{},33	}34}3536func (s *basicNFT) Name() string      { return s.name }37func (s *basicNFT) Symbol() string    { return s.symbol }38func (s *basicNFT) TokenCount() int64 { return int64(s.owners.Size()) }3940// BalanceOf returns balance of input address41func (s *basicNFT) BalanceOf(addr address) (int64, error) {42	if err := isValidAddress(addr); err != nil {43		return 0, err44	}4546	balance := s.balances.Get(addr.String())47	if balance == nil {48		return 0, nil49	}5051	return balance.(int64), nil52}5354// OwnerOf returns owner of input token id55func (s *basicNFT) OwnerOf(tid TokenID) (address, error) {56	owner := s.owners.Get(string(tid))57	if owner == nil {58		return "", ErrInvalidTokenId59	}6061	return owner.(address), nil62}6364// TokenURI returns the URI of input token id65func (s *basicNFT) TokenURI(tid TokenID) (string, error) {66	uri := s.tokenURIs.Get(tid.String())67	if uri == nil {68		return "", ErrInvalidTokenId69	}7071	return uri.(string), nil72}7374// SetTokenURI sets the URI of a token. caller must equal the token's75// owner. The owning realm's public wrapper is responsible for deriving76// caller from rlm.Previous().Address() under an rlm.IsCurrent() guard77// before invoking this method; this method trusts the supplied caller.78func (s *basicNFT) SetTokenURI(caller address, tid TokenID, tURI TokenURI) (bool, error) {79	// check for invalid TokenID80	if !s.exists(tid) {81		return false, ErrInvalidTokenId82	}8384	// check for the right owner85	owner, err := s.OwnerOf(tid)86	if err != nil {87		return false, err88	}89	if caller != owner {90		return false, ErrCallerIsNotOwner91	}92	s.tokenURIs.Set(tid.String(), tURI.String())93	return true, nil94}9596// IsApprovedForAll returns true if operator is approved for all by the owner.97// Otherwise, returns false98func (s *basicNFT) IsApprovedForAll(owner, operator address) bool {99	key := owner.String() + ":" + operator.String()100	approved := s.operatorApprovals.Get(key)101	if approved == nil {102		return false103	}104105	return approved.(bool)106}107108// Approve approves the input address for a particular token. caller109// must be the owner OR an operator approved for all on owner's behalf.110// The owning realm's wrapper validates IsCurrent and derives caller111// from rlm.Previous().Address() before calling.112func (s *basicNFT) Approve(caller, to address, tid TokenID) error {113	if err := isValidAddress(to); err != nil {114		return err115	}116117	owner, err := s.OwnerOf(tid)118	if err != nil {119		return err120	}121	if owner == to {122		return ErrApprovalToCurrentOwner123	}124125	if caller != owner && !s.IsApprovedForAll(owner, caller) {126		return ErrCallerIsNotOwnerOrApproved127	}128129	tidStr := tid.String()130	s.tokenApprovals.Set(tidStr, to)131132	chain.Emit(133		ApprovalEvent,134		"slug", s.symbol,135		"owner", owner.String(),136		"to", to.String(),137		"tokenId", tidStr,138	)139140	return nil141}142143// GetApproved return the approved address for token144func (s *basicNFT) GetApproved(tid TokenID) (address, error) {145	addr := s.tokenApprovals.Get(tid.String())146	if addr == nil {147		return zeroAddress, ErrTokenIdNotHasApproved148	}149150	return addr.(address), nil151}152153// SetApprovalForAll grants/revokes operator permission across all of154// the caller's tokens. caller is the owner whose approvals are mutated;155// the owning realm's wrapper derives it from rlm.Previous().Address()156// under an IsCurrent() guard.157func (s *basicNFT) SetApprovalForAll(caller, operator address, approved bool) error {158	if err := isValidAddress(operator); err != nil {159		return ErrInvalidAddress160	}161	return s.setApprovalForAll(caller, operator, approved)162}163164// SafeTransferFrom transfers a token from `from` to `to`, checking that165// contract recipients are aware of the GRC721 protocol to prevent166// tokens from being forever locked. caller must be the owner or an167// approved operator. The owning realm's wrapper derives caller from168// rlm.Previous().Address() under an IsCurrent() guard.169func (s *basicNFT) SafeTransferFrom(caller, from, to address, tid TokenID) error {170	if !s.isApprovedOrOwner(caller, tid) {171		return ErrCallerIsNotOwnerOrApproved172	}173174	err := s.transfer(from, to, tid)175	if err != nil {176		return err177	}178179	if !s.checkOnGRC721Received(from, to, tid) {180		return ErrTransferToNonGRC721Receiver181	}182183	return nil184}185186// TransferFrom transfers a token from `from` to `to`. Same caller187// contract as SafeTransferFrom.188func (s *basicNFT) TransferFrom(caller, from, to address, tid TokenID) error {189	if !s.isApprovedOrOwner(caller, tid) {190		return ErrCallerIsNotOwnerOrApproved191	}192193	err := s.transfer(from, to, tid)194	if err != nil {195		return err196	}197198	return nil199}200201// Mints `tokenId` and transfers it to `to`.202func (s *basicNFT) Mint(to address, tid TokenID) error {203	return s.mint(to, tid)204}205206// Mints `tokenId` and transfers it to `to`. Also checks that207// contract recipients are using GRC721 protocol208func (s *basicNFT) SafeMint(to address, tid TokenID) error {209	err := s.mint(to, tid)210	if err != nil {211		return err212	}213214	if !s.checkOnGRC721Received(zeroAddress, to, tid) {215		return ErrTransferToNonGRC721Receiver216	}217218	return nil219}220221func (s *basicNFT) Burn(tid TokenID) error {222	owner, err := s.OwnerOf(tid)223	if err != nil {224		return err225	}226227	s.beforeTokenTransfer(owner, zeroAddress, tid, 1)228229	tidStr := tid.String()230	s.tokenApprovals.Remove(tidStr)231	balance, err := s.BalanceOf(owner)232	if err != nil {233		return err234	}235	balance = overflow.Sub64p(balance, 1)236237	ownerStr := owner.String()238	s.balances.Set(ownerStr, balance)239	s.owners.Remove(tidStr)240241	chain.Emit(242		BurnEvent,243		"slug", s.symbol,244		"from", ownerStr,245		"tokenId", tidStr,246	)247248	s.afterTokenTransfer(owner, zeroAddress, tid, 1)249250	return nil251}252253/* Helper methods */254255// Helper for SetApprovalForAll()256func (s *basicNFT) setApprovalForAll(owner, operator address, approved bool) error {257	if owner == operator {258		return ErrApprovalToCurrentOwner259	}260261	key := owner.String() + ":" + operator.String()262	s.operatorApprovals.Set(key, approved)263264	chain.Emit(265		ApprovalForAllEvent,266		"slug", s.symbol,267		"owner", owner.String(),268		"to", operator.String(),269		"approved", strconv.FormatBool(approved),270	)271272	return nil273}274275// Helper for TransferFrom() and SafeTransferFrom()276func (s *basicNFT) transfer(from, to address, tid TokenID) error {277	if err := isValidAddress(from); err != nil {278		return ErrInvalidAddress279	}280	if err := isValidAddress(to); err != nil {281		return ErrInvalidAddress282	}283284	if from == to {285		return ErrCannotTransferToSelf286	}287288	owner, err := s.OwnerOf(tid)289	if err != nil {290		return err291	}292	if owner != from {293		return ErrTransferFromIncorrectOwner294	}295296	s.beforeTokenTransfer(from, to, tid, 1)297298	// Check that tokenId was not transferred by `beforeTokenTransfer`299	owner, err = s.OwnerOf(tid)300	if err != nil {301		return err302	}303	if owner != from {304		return ErrTransferFromIncorrectOwner305	}306307	tidStr := tid.String()308	s.tokenApprovals.Remove(tidStr)309	fromBalance, err := s.BalanceOf(from)310	if err != nil {311		return err312	}313	toBalance, err := s.BalanceOf(to)314	if err != nil {315		return err316	}317	fromBalance = overflow.Sub64p(fromBalance, 1)318	toBalance = overflow.Add64p(toBalance, 1)319320	fromStr := from.String()321	toStr := to.String()322323	s.balances.Set(fromStr, fromBalance)324	s.balances.Set(toStr, toBalance)325	s.owners.Set(tidStr, to)326327	chain.Emit(328		TransferEvent,329		"slug", s.symbol,330		"from", fromStr,331		"to", toStr,332		"tokenId", tidStr,333	)334335	s.afterTokenTransfer(from, to, tid, 1)336337	return nil338}339340// Helper for Mint() and SafeMint()341func (s *basicNFT) mint(to address, tid TokenID) error {342	if err := isValidAddress(to); err != nil {343		return err344	}345346	if s.exists(tid) {347		return ErrTokenIdAlreadyExists348	}349350	s.beforeTokenTransfer(zeroAddress, to, tid, 1)351352	// Check that tokenId was not minted by `beforeTokenTransfer`353	if s.exists(tid) {354		return ErrTokenIdAlreadyExists355	}356357	toBalance, err := s.BalanceOf(to)358	if err != nil {359		return err360	}361	toBalance = overflow.Add64p(toBalance, 1)362	toStr := to.String()363	tidStr := tid.String()364	s.balances.Set(toStr, toBalance)365	s.owners.Set(tidStr, to)366367	chain.Emit(368		MintEvent,369		"slug", s.symbol,370		"to", toStr,371		"tokenId", tidStr,372	)373374	s.afterTokenTransfer(zeroAddress, to, tid, 1)375376	return nil377}378379func (s *basicNFT) isApprovedOrOwner(addr address, tid TokenID) bool {380	owner := s.owners.Get(tid.String())381	if owner == nil {382		return false383	}384385	ownerAddr := owner.(address)386	if addr == ownerAddr || s.IsApprovedForAll(ownerAddr, addr) {387		return true388	}389390	approved, err := s.GetApproved(tid)391	if err != nil {392		return false393	}394395	return approved == addr396}397398// Checks if token id already exists399func (s *basicNFT) exists(tid TokenID) bool {400	return s.owners.Has(tid.String())401}402403func (s *basicNFT) beforeTokenTransfer(from, to address, firstTokenId TokenID, batchSize int64) {404	// TODO: Implementation405}406407func (s *basicNFT) afterTokenTransfer(from, to address, firstTokenId TokenID, batchSize int64) {408	// TODO: Implementation409}410411func (s *basicNFT) checkOnGRC721Received(from, to address, tid TokenID) bool {412	// TODO: Implementation413	return true414}415416func (s *basicNFT) RenderHome() (str string) {417	str += ufmt.Sprintf("# %s ($%s)\n\n", s.name, s.symbol)418	str += ufmt.Sprintf("* **Total supply**: %d\n", s.TokenCount())419	str += ufmt.Sprintf("* **Known accounts**: %d\n", s.balances.Size())420421	return422}423424// Getter returns an NFTGetter that yields a reader-only view of the NFT.425// Safe to register with cross-realm aggregators like tokenhub — readers426// only, no rlm-typed methods, so no cur can be captured via this surface.427func (n *basicNFT) Getter() NFTGetter {428	return func() IGRC721Reader {429		return n430	}431}432

Functions

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

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