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 }