Search Apps Documentation Source Content File Folder Download Copy Actions Download

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, 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(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, found := s.operatorApprovals.Get(key)
 80	if !found {
 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}