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  }