github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/r/demo/nft/nft.gno (about)

     1  package nft
     2  
     3  import (
     4  	"std"
     5  	"strconv"
     6  
     7  	"gno.land/p/demo/avl"
     8  	"gno.land/p/demo/grc/grc721"
     9  )
    10  
    11  type token struct {
    12  	grc721.IGRC721 // implements the GRC721 interface
    13  
    14  	tokenCounter int
    15  	tokens       avl.Tree // grc721.TokenID -> *NFToken{}
    16  	operators    avl.Tree // owner std.Address -> operator std.Address
    17  }
    18  
    19  type NFToken struct {
    20  	owner    std.Address
    21  	approved std.Address
    22  	tokenID  grc721.TokenID
    23  	data     string
    24  }
    25  
    26  var gToken = &token{}
    27  
    28  func GetToken() *token { return gToken }
    29  
    30  func (grc *token) nextTokenID() grc721.TokenID {
    31  	grc.tokenCounter++
    32  	s := strconv.Itoa(grc.tokenCounter)
    33  	return grc721.TokenID(s)
    34  }
    35  
    36  func (grc *token) getToken(tid grc721.TokenID) (*NFToken, bool) {
    37  	token, ok := grc.tokens.Get(string(tid))
    38  	if !ok {
    39  		return nil, false
    40  	}
    41  	return token.(*NFToken), true
    42  }
    43  
    44  func (grc *token) Mint(to std.Address, data string) grc721.TokenID {
    45  	tid := grc.nextTokenID()
    46  	grc.tokens.Set(string(tid), &NFToken{
    47  		owner:   to,
    48  		tokenID: tid,
    49  		data:    data,
    50  	})
    51  	return tid
    52  }
    53  
    54  func (grc *token) BalanceOf(owner std.Address) (count int64) {
    55  	panic("not yet implemented")
    56  }
    57  
    58  func (grc *token) OwnerOf(tid grc721.TokenID) std.Address {
    59  	token, ok := grc.getToken(tid)
    60  	if !ok {
    61  		panic("token does not exist")
    62  	}
    63  	return token.owner
    64  }
    65  
    66  // XXX not fully implemented yet.
    67  func (grc *token) SafeTransferFrom(from, to std.Address, tid grc721.TokenID) {
    68  	grc.TransferFrom(from, to, tid)
    69  	// When transfer is complete, this function checks if `_to` is a smart
    70  	// contract (code size > 0). If so, it calls `onERC721Received` on
    71  	// `_to` and throws if the return value is not
    72  	// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    73  	// XXX ensure "to" is a realm with onERC721Received() signature.
    74  }
    75  
    76  func (grc *token) TransferFrom(from, to std.Address, tid grc721.TokenID) {
    77  	caller := std.GetCallerAt(2)
    78  	token, ok := grc.getToken(tid)
    79  	// Throws if `_tokenId` is not a valid NFT.
    80  	if !ok {
    81  		panic("token does not exist")
    82  	}
    83  	// Throws unless `msg.sender` is the current owner, an authorized
    84  	// operator, or the approved address for this NFT.
    85  	if caller != token.owner && caller != token.approved {
    86  		operator, ok := grc.operators.Get(token.owner.String())
    87  		if !ok || caller != operator.(std.Address) {
    88  			panic("unauthorized")
    89  		}
    90  	}
    91  	// Throws if `_from` is not the current owner.
    92  	if from != token.owner {
    93  		panic("from is not the current owner")
    94  	}
    95  	// Throws if `_to` is the zero address.
    96  	if to == "" {
    97  		panic("to cannot be empty")
    98  	}
    99  	// Good.
   100  	token.owner = to
   101  }
   102  
   103  func (grc *token) Approve(approved std.Address, tid grc721.TokenID) {
   104  	caller := std.GetCallerAt(2)
   105  	token, ok := grc.getToken(tid)
   106  	// Throws if `_tokenId` is not a valid NFT.
   107  	if !ok {
   108  		panic("token does not exist")
   109  	}
   110  	// Throws unless `msg.sender` is the current owner,
   111  	// or an authorized operator.
   112  	if caller != token.owner {
   113  		operator, ok := grc.operators.Get(token.owner.String())
   114  		if !ok || caller != operator.(std.Address) {
   115  			panic("unauthorized")
   116  		}
   117  	}
   118  	// Good.
   119  	token.approved = approved
   120  }
   121  
   122  // XXX make it work for set of operators.
   123  func (grc *token) SetApprovalForAll(operator std.Address, approved bool) {
   124  	caller := std.GetCallerAt(2)
   125  	grc.operators.Set(caller.String(), operator)
   126  }
   127  
   128  func (grc *token) GetApproved(tid grc721.TokenID) std.Address {
   129  	token, ok := grc.getToken(tid)
   130  	// Throws if `_tokenId` is not a valid NFT.
   131  	if !ok {
   132  		panic("token does not exist")
   133  	}
   134  	return token.approved
   135  }
   136  
   137  // XXX make it work for set of operators
   138  func (grc *token) IsApprovedForAll(owner, operator std.Address) bool {
   139  	operator2, ok := grc.operators.Get(owner.String())
   140  	if !ok {
   141  		return false
   142  	}
   143  	return operator == operator2.(std.Address)
   144  }