Search Apps Documentation Source Content File Folder Download Copy Actions Download

basic_grc1155_token.gno

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