github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/grc/grc721/grc721_metadata.gno (about)

     1  package grc721
     2  
     3  import (
     4  	"std"
     5  
     6  	"gno.land/p/demo/avl"
     7  )
     8  
     9  // metadataNFT represents an NFT with metadata extensions.
    10  type metadataNFT struct {
    11  	*basicNFT            // Embedded basicNFT struct for basic NFT functionality
    12  	extensions *avl.Tree // AVL tree for storing metadata extensions
    13  }
    14  
    15  // Ensure that metadataNFT implements the IGRC721MetadataOnchain interface.
    16  var _ IGRC721MetadataOnchain = (*metadataNFT)(nil)
    17  
    18  // NewNFTWithMetadata creates a new basic NFT with metadata extensions.
    19  func NewNFTWithMetadata(name string, symbol string) *metadataNFT {
    20  	// Create a new basic NFT
    21  	nft := NewBasicNFT(name, symbol)
    22  
    23  	// Return a metadataNFT with basicNFT embedded and an empty AVL tree for extensions
    24  	return &metadataNFT{
    25  		basicNFT:   nft,
    26  		extensions: avl.NewTree(),
    27  	}
    28  }
    29  
    30  // SetTokenMetadata sets metadata for a given token ID.
    31  func (s *metadataNFT) SetTokenMetadata(tid TokenID, metadata Metadata) error {
    32  	// Check if the caller is the owner of the token
    33  	owner, err := s.basicNFT.OwnerOf(tid)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	caller := std.PrevRealm().Addr()
    38  	if caller != owner {
    39  		return ErrCallerIsNotOwner
    40  	}
    41  
    42  	// Set the metadata for the token ID in the extensions AVL tree
    43  	s.extensions.Set(string(tid), metadata)
    44  	return nil
    45  }
    46  
    47  // TokenMetadata retrieves metadata for a given token ID.
    48  func (s *metadataNFT) TokenMetadata(tid TokenID) (Metadata, error) {
    49  	// Retrieve metadata from the extensions AVL tree
    50  	metadata, found := s.extensions.Get(string(tid))
    51  	if !found {
    52  		return Metadata{}, ErrInvalidTokenId
    53  	}
    54  
    55  	return metadata.(Metadata), nil
    56  }
    57  
    58  // mint mints a new token and assigns it to the specified address.
    59  func (s *metadataNFT) mint(to std.Address, tid TokenID) error {
    60  	// Check if the address is valid
    61  	if err := isValidAddress(to); err != nil {
    62  		return err
    63  	}
    64  
    65  	// Check if the token ID already exists
    66  	if s.basicNFT.exists(tid) {
    67  		return ErrTokenIdAlreadyExists
    68  	}
    69  
    70  	s.basicNFT.beforeTokenTransfer(zeroAddress, to, tid, 1)
    71  
    72  	// Check if the token ID was minted by beforeTokenTransfer
    73  	if s.basicNFT.exists(tid) {
    74  		return ErrTokenIdAlreadyExists
    75  	}
    76  
    77  	// Increment balance of the recipient address
    78  	toBalance, err := s.basicNFT.BalanceOf(to)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	toBalance += 1
    83  	s.basicNFT.balances.Set(to.String(), toBalance)
    84  
    85  	// Set owner of the token ID to the recipient address
    86  	s.basicNFT.owners.Set(string(tid), to)
    87  
    88  	// Emit transfer event
    89  	event := TransferEvent{zeroAddress, to, tid}
    90  	emit(&event)
    91  
    92  	s.basicNFT.afterTokenTransfer(zeroAddress, to, tid, 1)
    93  
    94  	return nil
    95  }