basic_grc1155_token.gno

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