Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

basic_grc1155_token.gno

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