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}