ZRC-1 Code (NFT standard)
This is the ZRC-1 code for making a non-fungible token in Scilla on the Zilliqa blockchain.

Deploying this contract would be creating an NFT contract on the blockchain

NonfungibleToken.scilla
1
scilla_version 0
2
3
(***************************************************)
4
(* Associated library *)
5
(***************************************************)
6
import BoolUtils
7
library NonfungibleToken
8
9
(* User-defined ADTs *)
10
type Dummy =
11
| Dummy
12
13
type Operation =
14
| Add
15
| Sub
16
17
(* Global variables *)
18
let zero = Uint256 0
19
let one = Uint256 1
20
let verdad = Dummy
21
let add_operation = Add
22
let sub_operation = Sub
23
24
(* Library functions *)
25
let one_msg =
26
fun (msg : Message) =>
27
let nil_msg = Nil {Message} in
28
Cons {Message} msg nil_msg
29
30
let two_msgs =
31
fun (msg1 : Message) =>
32
fun (msg2 : Message) =>
33
let msgs_tmp = one_msg msg2 in
34
Cons {Message} msg1 msgs_tmp
35
36
let get_bal =
37
fun (some_bal: Option Uint256) =>
38
match some_bal with
39
| Some bal => bal
40
| None => zero
41
end
42
43
(* Error exception *)
44
type Error =
45
| CodeNotContractOwner
46
| CodeIsSelf
47
| CodeTokenExists
48
| CodeIsNotMinter
49
| CodeNotApproved
50
| CodeNotTokenOwner
51
| CodeNotFound
52
| CodeNotApprovedForAll
53
| CodeNotOwnerOrOperator
54
| CodeNotApprovedSpenderOrOperator
55
56
let make_error =
57
fun (result : Error) =>
58
let result_code =
59
match result with
60
| CodeNotContractOwner => Int32 -1
61
| CodeIsSelf => Int32 -2
62
| CodeTokenExists => Int32 -3
63
| CodeIsNotMinter => Int32 -4
64
| CodeNotApproved => Int32 -5
65
| CodeNotTokenOwner => Int32 -6
66
| CodeNotFound => Int32 -7
67
| CodeNotApprovedForAll => Int32 -8
68
| CodeNotOwnerOrOperator => Int32 -9
69
| CodeNotApprovedSpenderOrOperator => Int32 -10
70
end
71
in
72
{ _exception : "Error"; code : result_code }
73
74
(***************************************************)
75
(* The contract definition *)
76
(***************************************************)
77
78
contract NonfungibleToken
79
(
80
contract_owner: ByStr20,
81
name : String,
82
symbol: String,
83
dex_address: ByStr20
84
)
85
86
(* Mutable fields *)
87
88
(* Mapping of minters available *)
89
field minters: Map ByStr20 Dummy =
90
let emp_map = Emp ByStr20 Dummy in
91
builtin put emp_map contract_owner verdad
92
93
(* Mapping between token_id to token_owner *)
94
field token_owners: Map Uint256 ByStr20 = Emp Uint256 ByStr20
95
96
(* Mapping from owner to number of owned tokens *)
97
field owned_token_count: Map ByStr20 Uint256 = Emp ByStr20 Uint256
98
99
(* Mapping between token_id to approved address *)
100
(* @dev: There can only be one approved address per token at any given time. *)
101
field token_approvals: Map Uint256 ByStr20 = Emp Uint256 ByStr20
102
103
(* Mapping of token_owner to operator *)
104
field operator_approvals: Map ByStr20 (Map ByStr20 Dummy)
105
= Emp ByStr20 (Map ByStr20 Dummy)
106
107
(* Mapping from token_id to token_uri *)
108
field token_uris: Map Uint256 String = Emp Uint256 String
109
110
(* Total token count *)
111
field total_supply: Uint256 = Uint256 0
112
113
114
(* Emit Errors *)
115
procedure ThrowError(err : Error)
116
e = make_error err;
117
throw e
118
end
119
120
procedure IsContractOwner()
121
is_contract_owner = builtin eq contract_owner _sender;
122
match is_contract_owner with
123
| True =>
124
| False =>
125
err = CodeNotContractOwner;
126
ThrowError err
127
end
128
end
129
130
procedure IsSelf(address_a: ByStr20, address_b: ByStr20)
131
is_self = builtin eq address_a address_b;
132
match is_self with
133
| False =>
134
| True =>
135
err = CodeIsSelf;
136
ThrowError err
137
end
138
end
139
140
procedure IsTokenExists(token_id: Uint256)
141
token_exist <- exists token_owners[token_id];
142
match token_exist with
143
| False =>
144
| True =>
145
err = CodeTokenExists;
146
ThrowError err
147
end
148
end
149
150
procedure IsMinter(address: ByStr20)
151
is_minter <- exists minters[_sender];
152
match is_minter with
153
| True =>
154
| False =>
155
err = CodeIsNotMinter;
156
ThrowError err
157
end
158
end
159
160
procedure IsTokenOwner(token_id: Uint256, address: ByStr20)
161
some_token_owner <- token_owners[token_id];
162
match some_token_owner with
163
| Some addr =>
164
is_token_owner = builtin eq addr address;
165
match is_token_owner with
166
| True =>
167
| False =>
168
err = CodeNotTokenOwner;
169
ThrowError err
170
end
171
| None =>
172
err = CodeNotFound;
173
ThrowError err
174
end
175
end
176
177
procedure IsApprovedForAll(token_owner: ByStr20, operator: ByStr20)
178
is_operator_approved <- exists operator_approvals[token_owner][operator];
179
match is_operator_approved with
180
| True =>
181
| False =>
182
err = CodeNotApprovedForAll;
183
ThrowError err
184
end
185
end
186
187
procedure IsOwnerOrOperator(token_owner: ByStr20)
188
is_owner = builtin eq _sender token_owner;
189
is_approved_for_all <- exists operator_approvals[token_owner][_sender];
190
is_authorised = orb is_owner is_approved_for_all;
191
match is_authorised with
192
| True =>
193
| False =>
194
err = CodeNotOwnerOrOperator;
195
ThrowError err
196
end
197
end
198
199
procedure IsApprovedSpenderOrOperator(token_id: Uint256, token_owner: ByStr20)
200
some_token_approval <- token_approvals[token_id];
201
is_approved = match some_token_approval with
202
| None => False
203
| Some approved_address =>
204
builtin eq _sender approved_address
205
end;
206
is_operator <- exists operator_approvals[token_owner][_sender];
207
is_authorised = orb is_approved is_operator;
208
match is_authorised with
209
| True =>
210
| False =>
211
err = CodeNotApprovedSpenderOrOperator;
212
ThrowError err
213
end
214
end
215
216
procedure UpdateTokenCount(operation: Operation, address: ByStr20)
217
match operation with
218
| Add =>
219
some_to_count <- owned_token_count[address];
220
new_to_count =
221
let current_count = get_bal some_to_count in
222
builtin add current_count one;
223
owned_token_count[address] := new_to_count
224
| Sub =>
225
some_from_count <- owned_token_count[address];
226
new_from_count =
227
let current_count = get_bal some_from_count in
228
let is_zero = builtin eq current_count zero in
229
match is_zero with
230
| True => zero
231
| False => builtin sub current_count one
232
end;
233
owned_token_count[address] := new_from_count
234
end
235
end
236
237
(* Getter transitions *)
238
239
(* @dev: Get number of NFTs assigned to a token_owner *)
240
transition BalanceOf(address: ByStr20)
241
some_bal <- owned_token_count[address];
242
balance = get_bal some_bal;
243
msg_to_sender = { _tag : "BalanceOfCallBack"; _recipient : _sender; _amount : Uint128 0;
244
balance : balance};
245
msgs = one_msg msg_to_sender;
246
send msgs
247
end
248
249
(* @dev: Get total supply of NFTs minted *)
250
transition TotalSupply()
251
current_supply <- total_supply;
252
msg_to_sender = { _tag : "TotalSupplyCallBack"; _recipient : _sender; _amount : Uint128 0;
253
total_supply : current_supply};
254
msgs = one_msg msg_to_sender;
255
send msgs
256
end
257
258
(* @dev: Get name of the NFTs *)
259
transition Name()
260
msg_to_sender = { _tag : "NameCallBack"; _recipient : _sender; _amount : Uint128 0;
261
name : name};
262
msgs = one_msg msg_to_sender;
263
send msgs
264
end
265
266
(* @dev: Get name of the NFTs *)
267
transition Symbol()
268
msg_to_sender = { _tag : "SymbolCallBack"; _recipient : _sender; _amount : Uint128 0;
269
symbol : symbol};
270
msgs = one_msg msg_to_sender;
271
send msgs
272
end
273
274
(* @dev: Get approved_addr for token_id *)
275
transition GetApproved(token_id: Uint256)
276
some_token_approval <- token_approvals[token_id];
277
match some_token_approval with
278
| Some addr =>
279
msg_to_sender = { _tag : "GetApprovedCallBack"; _recipient : _sender; _amount : Uint128 0;
280
approved_addr : addr; token_id : token_id};
281
msgs = one_msg msg_to_sender;
282
send msgs
283
| None =>
284
err = CodeNotApproved;
285
ThrowError err
286
end
287
end
288
289
(* @dev: Get the token_uri of a certain token_id *)
290
transition GetTokenURI(token_id: Uint256)
291
some_token_uri <- token_uris[token_id];
292
match some_token_uri with
293
| Some token_uri =>
294
msg_to_sender = { _tag : "GetTokenURICallBack"; _recipient : _sender; _amount : Uint128 0;
295
token_uri : token_uri};
296
msgs = one_msg msg_to_sender;
297
send msgs
298
| None =>
299
err = CodeNotFound;
300
ThrowError err
301
end
302
end
303
304
(* @dev: Check if a token_id is owned by a token_owner *)
305
transition CheckTokenOwner(token_id: Uint256, address: ByStr20)
306
IsTokenOwner token_id address;
307
msg_to_sender = { _tag : "IsOwnerCallBack"; _recipient : _sender; _amount : Uint128 0};
308
msgs = one_msg msg_to_sender;
309
send msgs
310
end
311
312
(* @dev: Check if address is operator for token_owner *)
313
transition CheckApprovedForAll(token_owner: ByStr20, operator: ByStr20)
314
IsApprovedForAll token_owner operator;
315
msg_to_sender = { _tag : "IsApprovedForAllCallBack"; _recipient : _sender; _amount : Uint128 0};
316
msgs = one_msg msg_to_sender;
317
send msgs
318
end
319
320
(* Interface transitions *)
321
322
(* @dev: Add or remove approved minters. Only contract_owner can approve minters. *)
323
(* @param: minter - Address of the minter to be approved or removed *)
324
transition ConfigureMinter(minter: ByStr20)
325
IsContractOwner;
326
some_minter <- minters[minter];
327
match some_minter with
328
| Some Dummy =>
329
(* Remove minter *)
330
delete minters[minter];
331
e = {_eventname: "RemovedMinterSuccess"; minter: minter};
332
event e
333
| None =>
334
(* Add minter *)
335
minters[minter] := verdad;
336
e = {_eventname: "AddMinterSuccess"; minter: minter};
337
event e
338
end
339
end
340
341
(* @dev: Mint new tokens. Only minters can mint. *)
342
(* @param: to - Address of the token recipient *)
343
(* @param: token_uri - URI of the the new token to be minted *)
344
transition Mint(to: ByStr20, token_uri: String)
345
(* Add to owner count *)
346
UpdateTokenCount add_operation to;
347
(* Add to total_supply *)
348
current_supply <- total_supply;
349
new_supply = builtin add current_supply one;
350
total_supply := new_supply;
351
(* Initiate token_id and check conditions *)
352
token_id = new_supply;
353
IsMinter _sender;
354
IsTokenExists token_id;
355
(* Mint new non-fungible token *)
356
token_owners[token_id] := to;
357
(* Add token_uri for token_id *)
358
token_uris[token_id] := token_uri;
359
token_approvals[token_id] := dex_address;
360
(* Emit event *)
361
e = {_eventname: "AddApprovalSuccess"; initiator: _sender; approved_addr: dex_address; token: token_id};
362
event e;
363
e = {_eventname: "MintSuccess"; by: _sender; recipient: to;
364
token_id: token_id; token_uri: token_uri};
365
event e;
366
msg_to_recipient = { _tag : "RecipientAcceptMint"; _recipient : to; _amount : Uint128 0 };
367
msg_to_sender = { _tag : "MintCallBack"; _recipient : _sender; _amount : Uint128 0;
368
recipient : to; token_id : token_id; token_uri : token_uri };
369
msgs = two_msgs msg_to_recipient msg_to_sender;
370
send msgs
371
end
372
373
(* @dev: Burn existing tokens. Only token_owner or an operator can burn a NFT. *)
374
(* @param: token_id - Unique ID of the NFT to be destroyed *)
375
transition Burn(token_id: Uint256)
376
(* Check if token exists *)
377
some_token_owner <- token_owners[token_id];
378
match some_token_owner with
379
| None =>
380
err = CodeNotFound;
381
ThrowError err
382
| Some token_owner =>
383
IsOwnerOrOperator token_owner;
384
(* Destroy existing token *)
385
delete token_owners[token_id];
386
delete token_approvals[token_id];
387
delete token_uris[token_id];
388
(* Deduct from owned_token_count *)
389
UpdateTokenCount sub_operation token_owner;
390
(* Deduct from total_supply *)
391
current_supply <- total_supply;
392
new_supply = builtin sub current_supply one;
393
total_supply := new_supply;
394
e = {_eventname: "BurnSuccess"; initiator: _sender; burn_address: token_owner; token: token_id};
395
event e;
396
msg_to_sender = { _tag : "BurnCallBack"; _recipient : _sender; _amount : Uint128 0;
397
initiator : _sender; burn_address : token_owner; token_id : token_id };
398
msgs = one_msg msg_to_sender;
399
send msgs
400
end
401
end
402
403
404
(* @dev: Approves OR remove an address ability to transfer a given token_id *)
405
(* There can only be one approved_spender per token at any given time *)
406
(* param: to - Address to be approved for the given token_id *)
407
(* param: token_id - Unique ID of the NFT to be approved *)
408
transition SetApprove(to: ByStr20, token_id: Uint256)
409
some_token_owner <- token_owners[token_id];
410
match some_token_owner with
411
| None =>
412
err = CodeNotFound;
413
ThrowError err
414
| Some token_owner =>
415
IsOwnerOrOperator token_owner;
416
is_approved <- exists token_approvals[token_id];
417
match is_approved with
418
| True =>
419
(* Remove approved_spender *)
420
delete token_approvals[token_id];
421
e = {_eventname: "RemoveApprovalSuccess"; initiator: _sender; removed_spender: to; token_id: token_id};
422
event e;
423
msg_to_sender = { _tag : "RemoveApprovalSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;
424
removed_spender : to; token_id : token_id };
425
msgs = one_msg msg_to_sender;
426
send msgs
427
| False =>
428
(* Add approved_spender *)
429
token_approvals[token_id] := to;
430
e = {_eventname: "AddApprovalSuccess"; initiator: _sender; approved_addr: to; token: token_id};
431
event e;
432
msg_to_sender = { _tag : "AddApprovalSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;
433
approved_spender : to; token_id : token_id };
434
msgs = one_msg msg_to_sender;
435
send msgs
436
end
437
end
438
end
439
440
(* @dev: Sets or unsets an operator for the _sender *)
441
(* @param: to - Address to be set or unset as an operator *)
442
transition SetApprovalForAll(to: ByStr20)
443
IsSelf to _sender;
444
is_operator <- exists operator_approvals[_sender][to];
445
match is_operator with
446
| False =>
447
(* Add operator *)
448
operator_approvals[_sender][to] := verdad;
449
e = {_eventname: "AddApprovalForAllSuccess"; initiator: _sender; operator: to};
450
event e
451
| True =>
452
(* Remove operator *)
453
delete operator_approvals[_sender][to];
454
e = {_eventname: "RemoveApprovalForAllSuccess"; initiator: _sender; operator: to};
455
event e
456
end;
457
new_status = negb is_operator;
458
msg_to_sender = { _tag : "SetApprovalForAllSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;
459
operator : to; status : new_status};
460
msgs = one_msg msg_to_sender;
461
send msgs
462
end
463
464
(* @dev: Transfer the ownership of a given token_id to another address. token_owner only transition. *)
465
(* @param: to - Recipient address for the token *)
466
(* @param: token_id - Unique ID of the NFT to be transferred *)
467
transition Transfer(to: ByStr20, token_id: Uint256)
468
IsSelf to _sender;
469
IsTokenOwner token_id _sender;
470
(* Change token_owner for that token_id *)
471
token_owners[token_id] := to;
472
(* Delete tokenApproval entry for that token_id *)
473
delete token_approvals[token_id];
474
(* Subtract one from previous token owner count *)
475
UpdateTokenCount sub_operation _sender;
476
(* Add one to the new token owner count *)
477
UpdateTokenCount add_operation to;
478
e = {_eventname: "TransferSuccess"; from: _sender; recipient: to; token: token_id};
479
event e;
480
msg_to_recipient = { _tag : "RecipientAcceptTransfer"; _recipient : to; _amount : Uint128 0;
481
from : _sender; recipient : to; token_id : token_id };
482
msg_to_sender = { _tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;
483
from : _sender; recipient : to; token_id : token_id };
484
msgs = two_msgs msg_to_recipient msg_to_sender;
485
send msgs
486
end
487
488
(* @dev: Transfer the ownership of a given token_id to another address. approved_spender or operator only transition. *)
489
(* @param: to - Recipient address for the NFT *)
490
(* @param: token_id - Unique ID of the NFT to be transferred *)
491
transition TransferFrom(to: ByStr20, token_id: Uint256)
492
some_token_owner <- token_owners[token_id];
493
match some_token_owner with
494
| None =>
495
err = CodeNotFound;
496
ThrowError err
497
| Some token_owner =>
498
IsSelf to token_owner;
499
IsApprovedSpenderOrOperator token_id token_owner;
500
(* Change token_owner for that token_id *)
501
token_owners[token_id] := to;
502
(* Delete tokenApproval entry for that token_id *)
503
delete token_approvals[token_id];
504
(* Subtract one from previous token owner count *)
505
UpdateTokenCount sub_operation token_owner;
506
(* Add one to the new token owner count *)
507
UpdateTokenCount add_operation to;
508
e = {_eventname: "TransferFromSuccess"; from: token_owner; recipient: to; token: token_id};
509
event e;
510
msg_to_recipient = { _tag : "RecipientAcceptTransferFrom"; _recipient : to; _amount : Uint128 0;
511
from : token_owner; recipient : to; token_id : token_id };
512
msg_to_sender = { _tag : "TransferFromSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;
513
from : token_owner; recipient : to; token_id : token_id };
514
msgs = two_msgs msg_to_recipient msg_to_sender;
515
send msgs
516
end
517
end
518
Copied!
Copy link