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}