github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/grc/grc1155/basic_grc1155_token.gno (about) 1 package grc1155 2 3 import ( 4 "std" 5 6 "gno.land/p/demo/avl" 7 "gno.land/p/demo/ufmt" 8 ) 9 10 type basicGRC1155Token struct { 11 uri string 12 balances avl.Tree // "TokenId:Address" -> uint64 13 operatorApprovals avl.Tree // "OwnerAddress:OperatorAddress" -> bool 14 } 15 16 var _ IGRC1155 = (*basicGRC1155Token)(nil) 17 18 // Returns new basic GRC1155 token 19 func NewBasicGRC1155Token(uri string) *basicGRC1155Token { 20 return &basicGRC1155Token{ 21 uri: uri, 22 balances: avl.Tree{}, 23 operatorApprovals: avl.Tree{}, 24 } 25 } 26 27 func (s *basicGRC1155Token) Uri() string { return s.uri } 28 29 // BalanceOf returns the input address's balance of the token type requested 30 func (s *basicGRC1155Token) BalanceOf(addr std.Address, tid TokenID) (uint64, error) { 31 if !isValidAddress(addr) { 32 return 0, ErrInvalidAddress 33 } 34 35 key := string(tid) + ":" + addr.String() 36 balance, found := s.balances.Get(key) 37 if !found { 38 return 0, nil 39 } 40 41 return balance.(uint64), nil 42 } 43 44 // BalanceOfBatch returns the balance of multiple account/token pairs 45 func (s *basicGRC1155Token) BalanceOfBatch(owners []std.Address, batch []TokenID) ([]uint64, error) { 46 if len(owners) != len(batch) { 47 return nil, ErrMismatchLength 48 } 49 50 balanceOfBatch := make([]uint64, len(owners)) 51 52 for i := 0; i < len(owners); i++ { 53 balanceOfBatch[i], _ = s.BalanceOf(owners[i], batch[i]) 54 } 55 56 return balanceOfBatch, nil 57 } 58 59 // SetApprovalForAll can approve the operator to operate on all tokens 60 func (s *basicGRC1155Token) SetApprovalForAll(operator std.Address, approved bool) error { 61 if !isValidAddress(operator) { 62 return ErrInvalidAddress 63 } 64 65 caller := std.GetOrigCaller() 66 return s.setApprovalForAll(caller, operator, approved) 67 } 68 69 // IsApprovedForAll returns true if operator is the owner or is approved for all by the owner. 70 // Otherwise, returns false 71 func (s *basicGRC1155Token) IsApprovedForAll(owner, operator std.Address) bool { 72 if operator == owner { 73 return true 74 } 75 key := owner.String() + ":" + operator.String() 76 _, found := s.operatorApprovals.Get(key) 77 if !found { 78 return false 79 } 80 81 return true 82 } 83 84 // Safely transfers `tokenId` token from `from` to `to`, checking that 85 // contract recipients are aware of the GRC1155 protocol to prevent 86 // tokens from being forever locked. 87 func (s *basicGRC1155Token) SafeTransferFrom(from, to std.Address, tid TokenID, amount uint64) error { 88 caller := std.GetOrigCaller() 89 if !s.IsApprovedForAll(caller, from) { 90 return ErrCallerIsNotOwnerOrApproved 91 } 92 93 err := s.safeBatchTransferFrom(from, to, []TokenID{tid}, []uint64{amount}) 94 if err != nil { 95 return err 96 } 97 98 if !s.doSafeTransferAcceptanceCheck(caller, from, to, tid, amount) { 99 return ErrTransferToRejectedOrNonGRC1155Receiver 100 } 101 102 emit(&TransferSingleEvent{caller, from, to, tid, amount}) 103 104 return nil 105 } 106 107 // Safely transfers a `batch` of tokens from `from` to `to`, checking that 108 // contract recipients are aware of the GRC1155 protocol to prevent 109 // tokens from being forever locked. 110 func (s *basicGRC1155Token) SafeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error { 111 caller := std.GetOrigCaller() 112 if !s.IsApprovedForAll(caller, from) { 113 return ErrCallerIsNotOwnerOrApproved 114 } 115 116 err := s.safeBatchTransferFrom(from, to, batch, amounts) 117 if err != nil { 118 return err 119 } 120 121 if !s.doSafeBatchTransferAcceptanceCheck(caller, from, to, batch, amounts) { 122 return ErrTransferToRejectedOrNonGRC1155Receiver 123 } 124 125 emit(&TransferBatchEvent{caller, from, to, batch, amounts}) 126 127 return nil 128 } 129 130 // Creates `amount` tokens of token type `id`, and assigns them to `to`. Also checks that 131 // contract recipients are using GRC1155 protocol. 132 func (s *basicGRC1155Token) SafeMint(to std.Address, tid TokenID, amount uint64) error { 133 caller := std.GetOrigCaller() 134 135 err := s.mintBatch(to, []TokenID{tid}, []uint64{amount}) 136 if err != nil { 137 return err 138 } 139 140 if !s.doSafeTransferAcceptanceCheck(caller, zeroAddress, to, tid, amount) { 141 return ErrTransferToRejectedOrNonGRC1155Receiver 142 } 143 144 emit(&TransferSingleEvent{caller, zeroAddress, to, tid, amount}) 145 146 return nil 147 } 148 149 // Batch version of `SafeMint()`. Also checks that 150 // contract recipients are using GRC1155 protocol. 151 func (s *basicGRC1155Token) SafeBatchMint(to std.Address, batch []TokenID, amounts []uint64) error { 152 caller := std.GetOrigCaller() 153 154 err := s.mintBatch(to, batch, amounts) 155 if err != nil { 156 return err 157 } 158 159 if !s.doSafeBatchTransferAcceptanceCheck(caller, zeroAddress, to, batch, amounts) { 160 return ErrTransferToRejectedOrNonGRC1155Receiver 161 } 162 163 emit(&TransferBatchEvent{caller, zeroAddress, to, batch, amounts}) 164 165 return nil 166 } 167 168 // Destroys `amount` tokens of token type `id` from `from`. 169 func (s *basicGRC1155Token) Burn(from std.Address, tid TokenID, amount uint64) error { 170 caller := std.GetOrigCaller() 171 172 err := s.burnBatch(from, []TokenID{tid}, []uint64{amount}) 173 if err != nil { 174 return err 175 } 176 177 emit(&TransferSingleEvent{caller, from, zeroAddress, tid, amount}) 178 179 return nil 180 } 181 182 // Batch version of `Burn()` 183 func (s *basicGRC1155Token) BatchBurn(from std.Address, batch []TokenID, amounts []uint64) error { 184 caller := std.GetOrigCaller() 185 186 err := s.burnBatch(from, batch, amounts) 187 if err != nil { 188 return err 189 } 190 191 emit(&TransferBatchEvent{caller, from, zeroAddress, batch, amounts}) 192 193 return nil 194 } 195 196 /* Helper methods */ 197 198 // Helper for SetApprovalForAll(): approve `operator` to operate on all of `owner` tokens 199 func (s *basicGRC1155Token) setApprovalForAll(owner, operator std.Address, approved bool) error { 200 if owner == operator { 201 return nil 202 } 203 204 key := owner.String() + ":" + operator.String() 205 if approved { 206 s.operatorApprovals.Set(key, approved) 207 } else { 208 s.operatorApprovals.Remove(key) 209 } 210 211 emit(&ApprovalForAllEvent{owner, operator, approved}) 212 213 return nil 214 } 215 216 // Helper for SafeTransferFrom() and SafeBatchTransferFrom() 217 func (s *basicGRC1155Token) safeBatchTransferFrom(from, to std.Address, batch []TokenID, amounts []uint64) error { 218 if len(batch) != len(amounts) { 219 return ErrMismatchLength 220 } 221 if !isValidAddress(from) || !isValidAddress(to) { 222 return ErrInvalidAddress 223 } 224 if from == to { 225 return ErrCannotTransferToSelf 226 } 227 228 caller := std.GetOrigCaller() 229 s.beforeTokenTransfer(caller, from, to, batch, amounts) 230 231 for i := 0; i < len(batch); i++ { 232 tid := batch[i] 233 amount := amounts[i] 234 fromBalance, err := s.BalanceOf(from, tid) 235 if err != nil { 236 return err 237 } 238 if fromBalance < amount { 239 return ErrInsufficientBalance 240 } 241 toBalance, err := s.BalanceOf(to, tid) 242 if err != nil { 243 return err 244 } 245 246 fromBalance -= amount 247 toBalance += amount 248 fromBalanceKey := string(tid) + ":" + from.String() 249 toBalanceKey := string(tid) + ":" + to.String() 250 s.balances.Set(fromBalanceKey, fromBalance) 251 s.balances.Set(toBalanceKey, toBalance) 252 } 253 254 s.afterTokenTransfer(caller, from, to, batch, amounts) 255 256 return nil 257 } 258 259 // Helper for SafeMint() and SafeBatchMint() 260 func (s *basicGRC1155Token) mintBatch(to std.Address, batch []TokenID, amounts []uint64) error { 261 if len(batch) != len(amounts) { 262 return ErrMismatchLength 263 } 264 if !isValidAddress(to) { 265 return ErrInvalidAddress 266 } 267 268 caller := std.GetOrigCaller() 269 s.beforeTokenTransfer(caller, zeroAddress, to, batch, amounts) 270 271 for i := 0; i < len(batch); i++ { 272 tid := batch[i] 273 amount := amounts[i] 274 toBalance, err := s.BalanceOf(to, tid) 275 if err != nil { 276 return err 277 } 278 toBalance += amount 279 toBalanceKey := string(tid) + ":" + to.String() 280 s.balances.Set(toBalanceKey, toBalance) 281 } 282 283 s.afterTokenTransfer(caller, zeroAddress, to, batch, amounts) 284 285 return nil 286 } 287 288 // Helper for Burn() and BurnBatch() 289 func (s *basicGRC1155Token) burnBatch(from std.Address, batch []TokenID, amounts []uint64) error { 290 if len(batch) != len(amounts) { 291 return ErrMismatchLength 292 } 293 if !isValidAddress(from) { 294 return ErrInvalidAddress 295 } 296 297 caller := std.GetOrigCaller() 298 s.beforeTokenTransfer(caller, from, zeroAddress, batch, amounts) 299 300 for i := 0; i < len(batch); i++ { 301 tid := batch[i] 302 amount := amounts[i] 303 fromBalance, err := s.BalanceOf(from, tid) 304 if err != nil { 305 return err 306 } 307 if fromBalance < amount { 308 return ErrBurnAmountExceedsBalance 309 } 310 fromBalance -= amount 311 fromBalanceKey := string(tid) + ":" + from.String() 312 s.balances.Set(fromBalanceKey, fromBalance) 313 } 314 315 s.afterTokenTransfer(caller, from, zeroAddress, batch, amounts) 316 317 return nil 318 } 319 320 func (s *basicGRC1155Token) setUri(newUri string) { 321 s.uri = newUri 322 emit(&UpdateURIEvent{newUri}) 323 } 324 325 func (s *basicGRC1155Token) beforeTokenTransfer(operator, from, to std.Address, batch []TokenID, amounts []uint64) { 326 // TODO: Implementation 327 } 328 329 func (s *basicGRC1155Token) afterTokenTransfer(operator, from, to std.Address, batch []TokenID, amounts []uint64) { 330 // TODO: Implementation 331 } 332 333 func (s *basicGRC1155Token) doSafeTransferAcceptanceCheck(operator, from, to std.Address, tid TokenID, amount uint64) bool { 334 // TODO: Implementation 335 return true 336 } 337 338 func (s *basicGRC1155Token) doSafeBatchTransferAcceptanceCheck(operator, from, to std.Address, batch []TokenID, amounts []uint64) bool { 339 // TODO: Implementation 340 return true 341 } 342 343 func (s *basicGRC1155Token) RenderHome() (str string) { 344 str += ufmt.Sprintf("# URI:%s\n", s.uri) 345 346 return 347 }