NonfungibleToken.scillascilla_version 0​(***************************************************)(* Associated library *)(***************************************************)import BoolUtilslibrary NonfungibleToken​(* User-defined ADTs *)type Dummy =| Dummy​type Operation =| Add| Sub​(* Global variables *)let zero = Uint256 0let one = Uint256 1let verdad = Dummylet add_operation = Addlet sub_operation = Sub​(* Library functions *)let one_msg =fun (msg : Message) =>let nil_msg = Nil {Message} inCons {Message} msg nil_msg​let two_msgs =fun (msg1 : Message) =>fun (msg2 : Message) =>let msgs_tmp = one_msg msg2 inCons {Message} msg1 msgs_tmp​let get_bal =fun (some_bal: Option Uint256) =>match some_bal with| Some bal => bal| None => zeroend​(* Error exception *)type Error =| CodeNotContractOwner| CodeIsSelf| CodeTokenExists| CodeIsNotMinter| CodeNotApproved| CodeNotTokenOwner| CodeNotFound| CodeNotApprovedForAll| CodeNotOwnerOrOperator| CodeNotApprovedSpenderOrOperator​let make_error =fun (result : Error) =>let result_code =match result with| CodeNotContractOwner => Int32 -1| CodeIsSelf => Int32 -2| CodeTokenExists => Int32 -3| CodeIsNotMinter => Int32 -4| CodeNotApproved => Int32 -5| CodeNotTokenOwner => Int32 -6| CodeNotFound => Int32 -7| CodeNotApprovedForAll => Int32 -8| CodeNotOwnerOrOperator => Int32 -9| CodeNotApprovedSpenderOrOperator => Int32 -10endin{ _exception : "Error"; code : result_code }​(***************************************************)(* The contract definition *)(***************************************************)​contract NonfungibleToken(contract_owner: ByStr20,name : String,symbol: String,dex_address: ByStr20)​(* Mutable fields *)​(* Mapping of minters available *)field minters: Map ByStr20 Dummy =let emp_map = Emp ByStr20 Dummy inbuiltin put emp_map contract_owner verdad​(* Mapping between token_id to token_owner *)field token_owners: Map Uint256 ByStr20 = Emp Uint256 ByStr20​(* Mapping from owner to number of owned tokens *)field owned_token_count: Map ByStr20 Uint256 = Emp ByStr20 Uint256​(* Mapping between token_id to approved address *)(* @dev: There can only be one approved address per token at any given time. *)field token_approvals: Map Uint256 ByStr20 = Emp Uint256 ByStr20​(* Mapping of token_owner to operator *)field operator_approvals: Map ByStr20 (Map ByStr20 Dummy)= Emp ByStr20 (Map ByStr20 Dummy)​(* Mapping from token_id to token_uri *)field token_uris: Map Uint256 String = Emp Uint256 String​(* Total token count *)field total_supply: Uint256 = Uint256 0​​(* Emit Errors *)procedure ThrowError(err : Error)e = make_error err;throw eend​procedure IsContractOwner()is_contract_owner = builtin eq contract_owner _sender;match is_contract_owner with| True =>| False =>err = CodeNotContractOwner;ThrowError errendend​procedure IsSelf(address_a: ByStr20, address_b: ByStr20)is_self = builtin eq address_a address_b;match is_self with| False =>| True =>err = CodeIsSelf;ThrowError errendend​procedure IsTokenExists(token_id: Uint256)token_exist <- exists token_owners[token_id];match token_exist with| False =>| True =>err = CodeTokenExists;ThrowError errendend​procedure IsMinter(address: ByStr20)is_minter <- exists minters[_sender];match is_minter with| True =>| False =>err = CodeIsNotMinter;ThrowError errendend​procedure IsTokenOwner(token_id: Uint256, address: ByStr20)some_token_owner <- token_owners[token_id];match some_token_owner with| Some addr =>is_token_owner = builtin eq addr address;match is_token_owner with| True =>| False =>err = CodeNotTokenOwner;ThrowError errend| None =>err = CodeNotFound;ThrowError errendend​procedure IsApprovedForAll(token_owner: ByStr20, operator: ByStr20)is_operator_approved <- exists operator_approvals[token_owner][operator];match is_operator_approved with| True =>| False =>err = CodeNotApprovedForAll;ThrowError errendend​procedure IsOwnerOrOperator(token_owner: ByStr20)is_owner = builtin eq _sender token_owner;is_approved_for_all <- exists operator_approvals[token_owner][_sender];is_authorised = orb is_owner is_approved_for_all;match is_authorised with| True =>| False =>err = CodeNotOwnerOrOperator;ThrowError errendend​procedure IsApprovedSpenderOrOperator(token_id: Uint256, token_owner: ByStr20)some_token_approval <- token_approvals[token_id];is_approved = match some_token_approval with| None => False| Some approved_address =>builtin eq _sender approved_addressend;is_operator <- exists operator_approvals[token_owner][_sender];is_authorised = orb is_approved is_operator;match is_authorised with| True =>| False =>err = CodeNotApprovedSpenderOrOperator;ThrowError errendend​procedure UpdateTokenCount(operation: Operation, address: ByStr20)match operation with| Add =>some_to_count <- owned_token_count[address];new_to_count =let current_count = get_bal some_to_count inbuiltin add current_count one;owned_token_count[address] := new_to_count| Sub =>some_from_count <- owned_token_count[address];new_from_count =let current_count = get_bal some_from_count inlet is_zero = builtin eq current_count zero inmatch is_zero with| True => zero| False => builtin sub current_count oneend;owned_token_count[address] := new_from_countendend​(* Getter transitions *)​(* @dev: Get number of NFTs assigned to a token_owner *)transition BalanceOf(address: ByStr20)some_bal <- owned_token_count[address];balance = get_bal some_bal;msg_to_sender = { _tag : "BalanceOfCallBack"; _recipient : _sender; _amount : Uint128 0;balance : balance};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Get total supply of NFTs minted *)transition TotalSupply()current_supply <- total_supply;msg_to_sender = { _tag : "TotalSupplyCallBack"; _recipient : _sender; _amount : Uint128 0;total_supply : current_supply};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Get name of the NFTs *)transition Name()msg_to_sender = { _tag : "NameCallBack"; _recipient : _sender; _amount : Uint128 0;name : name};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Get name of the NFTs *)transition Symbol()msg_to_sender = { _tag : "SymbolCallBack"; _recipient : _sender; _amount : Uint128 0;symbol : symbol};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Get approved_addr for token_id *)transition GetApproved(token_id: Uint256)some_token_approval <- token_approvals[token_id];match some_token_approval with| Some addr =>msg_to_sender = { _tag : "GetApprovedCallBack"; _recipient : _sender; _amount : Uint128 0;approved_addr : addr; token_id : token_id};msgs = one_msg msg_to_sender;send msgs| None =>err = CodeNotApproved;ThrowError errendend​(* @dev: Get the token_uri of a certain token_id *)transition GetTokenURI(token_id: Uint256)some_token_uri <- token_uris[token_id];match some_token_uri with| Some token_uri =>msg_to_sender = { _tag : "GetTokenURICallBack"; _recipient : _sender; _amount : Uint128 0;token_uri : token_uri};msgs = one_msg msg_to_sender;send msgs| None =>err = CodeNotFound;ThrowError errendend​(* @dev: Check if a token_id is owned by a token_owner *)transition CheckTokenOwner(token_id: Uint256, address: ByStr20)IsTokenOwner token_id address;msg_to_sender = { _tag : "IsOwnerCallBack"; _recipient : _sender; _amount : Uint128 0};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Check if address is operator for token_owner *)transition CheckApprovedForAll(token_owner: ByStr20, operator: ByStr20)IsApprovedForAll token_owner operator;msg_to_sender = { _tag : "IsApprovedForAllCallBack"; _recipient : _sender; _amount : Uint128 0};msgs = one_msg msg_to_sender;send msgsend​(* Interface transitions *)​(* @dev: Add or remove approved minters. Only contract_owner can approve minters. *)(* @param: minter - Address of the minter to be approved or removed *)transition ConfigureMinter(minter: ByStr20)IsContractOwner;some_minter <- minters[minter];match some_minter with| Some Dummy =>(* Remove minter *)delete minters[minter];e = {_eventname: "RemovedMinterSuccess"; minter: minter};event e| None =>(* Add minter *)minters[minter] := verdad;e = {_eventname: "AddMinterSuccess"; minter: minter};event eendend​(* @dev: Mint new tokens. Only minters can mint. *)(* @param: to - Address of the token recipient *)(* @param: token_uri - URI of the the new token to be minted *)transition Mint(to: ByStr20, token_uri: String)(* Add to owner count *)UpdateTokenCount add_operation to;(* Add to total_supply *)current_supply <- total_supply;new_supply = builtin add current_supply one;total_supply := new_supply;(* Initiate token_id and check conditions *)token_id = new_supply;IsMinter _sender;IsTokenExists token_id;(* Mint new non-fungible token *)token_owners[token_id] := to;(* Add token_uri for token_id *)token_uris[token_id] := token_uri;token_approvals[token_id] := dex_address;(* Emit event *)e = {_eventname: "AddApprovalSuccess"; initiator: _sender; approved_addr: dex_address; token: token_id};event e;e = {_eventname: "MintSuccess"; by: _sender; recipient: to;token_id: token_id; token_uri: token_uri};event e;msg_to_recipient = { _tag : "RecipientAcceptMint"; _recipient : to; _amount : Uint128 0 };msg_to_sender = { _tag : "MintCallBack"; _recipient : _sender; _amount : Uint128 0;recipient : to; token_id : token_id; token_uri : token_uri };msgs = two_msgs msg_to_recipient msg_to_sender;send msgsend​(* @dev: Burn existing tokens. Only token_owner or an operator can burn a NFT. *)(* @param: token_id - Unique ID of the NFT to be destroyed *)transition Burn(token_id: Uint256)(* Check if token exists *)some_token_owner <- token_owners[token_id];match some_token_owner with| None =>err = CodeNotFound;ThrowError err| Some token_owner =>IsOwnerOrOperator token_owner;(* Destroy existing token *)delete token_owners[token_id];delete token_approvals[token_id];delete token_uris[token_id];(* Deduct from owned_token_count *)UpdateTokenCount sub_operation token_owner;(* Deduct from total_supply *)current_supply <- total_supply;new_supply = builtin sub current_supply one;total_supply := new_supply;e = {_eventname: "BurnSuccess"; initiator: _sender; burn_address: token_owner; token: token_id};event e;msg_to_sender = { _tag : "BurnCallBack"; _recipient : _sender; _amount : Uint128 0;initiator : _sender; burn_address : token_owner; token_id : token_id };msgs = one_msg msg_to_sender;send msgsendend​​(* @dev: Approves OR remove an address ability to transfer a given token_id *)(* There can only be one approved_spender per token at any given time *)(* param: to - Address to be approved for the given token_id *)(* param: token_id - Unique ID of the NFT to be approved *)transition SetApprove(to: ByStr20, token_id: Uint256)some_token_owner <- token_owners[token_id];match some_token_owner with| None =>err = CodeNotFound;ThrowError err| Some token_owner =>IsOwnerOrOperator token_owner;is_approved <- exists token_approvals[token_id];match is_approved with| True =>(* Remove approved_spender *)delete token_approvals[token_id];e = {_eventname: "RemoveApprovalSuccess"; initiator: _sender; removed_spender: to; token_id: token_id};event e;msg_to_sender = { _tag : "RemoveApprovalSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;removed_spender : to; token_id : token_id };msgs = one_msg msg_to_sender;send msgs| False =>(* Add approved_spender *)token_approvals[token_id] := to;e = {_eventname: "AddApprovalSuccess"; initiator: _sender; approved_addr: to; token: token_id};event e;msg_to_sender = { _tag : "AddApprovalSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;approved_spender : to; token_id : token_id };msgs = one_msg msg_to_sender;send msgsendendend​(* @dev: Sets or unsets an operator for the _sender *)(* @param: to - Address to be set or unset as an operator *)transition SetApprovalForAll(to: ByStr20)IsSelf to _sender;is_operator <- exists operator_approvals[_sender][to];match is_operator with| False =>(* Add operator *)operator_approvals[_sender][to] := verdad;e = {_eventname: "AddApprovalForAllSuccess"; initiator: _sender; operator: to};event e| True =>(* Remove operator *)delete operator_approvals[_sender][to];e = {_eventname: "RemoveApprovalForAllSuccess"; initiator: _sender; operator: to};event eend;new_status = negb is_operator;msg_to_sender = { _tag : "SetApprovalForAllSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;operator : to; status : new_status};msgs = one_msg msg_to_sender;send msgsend​(* @dev: Transfer the ownership of a given token_id to another address. token_owner only transition. *)(* @param: to - Recipient address for the token *)(* @param: token_id - Unique ID of the NFT to be transferred *)transition Transfer(to: ByStr20, token_id: Uint256)IsSelf to _sender;IsTokenOwner token_id _sender;(* Change token_owner for that token_id *)token_owners[token_id] := to;(* Delete tokenApproval entry for that token_id *)delete token_approvals[token_id];(* Subtract one from previous token owner count *)UpdateTokenCount sub_operation _sender;(* Add one to the new token owner count *)UpdateTokenCount add_operation to;e = {_eventname: "TransferSuccess"; from: _sender; recipient: to; token: token_id};event e;msg_to_recipient = { _tag : "RecipientAcceptTransfer"; _recipient : to; _amount : Uint128 0;from : _sender; recipient : to; token_id : token_id };msg_to_sender = { _tag : "TransferSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;from : _sender; recipient : to; token_id : token_id };msgs = two_msgs msg_to_recipient msg_to_sender;send msgsend​(* @dev: Transfer the ownership of a given token_id to another address. approved_spender or operator only transition. *)(* @param: to - Recipient address for the NFT *)(* @param: token_id - Unique ID of the NFT to be transferred *)transition TransferFrom(to: ByStr20, token_id: Uint256)some_token_owner <- token_owners[token_id];match some_token_owner with| None =>err = CodeNotFound;ThrowError err| Some token_owner =>IsSelf to token_owner;IsApprovedSpenderOrOperator token_id token_owner;(* Change token_owner for that token_id *)token_owners[token_id] := to;(* Delete tokenApproval entry for that token_id *)delete token_approvals[token_id];(* Subtract one from previous token owner count *)UpdateTokenCount sub_operation token_owner;(* Add one to the new token owner count *)UpdateTokenCount add_operation to;e = {_eventname: "TransferFromSuccess"; from: token_owner; recipient: to; token: token_id};event e;msg_to_recipient = { _tag : "RecipientAcceptTransferFrom"; _recipient : to; _amount : Uint128 0;from : token_owner; recipient : to; token_id : token_id };msg_to_sender = { _tag : "TransferFromSuccessCallBack"; _recipient : _sender; _amount : Uint128 0;from : token_owner; recipient : to; token_id : token_id };msgs = two_msgs msg_to_recipient msg_to_sender;send msgsendend​
​