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}