type tokens is big_map (address, nat) type allowances is big_map (address * address, nat) (* (sender,account) -> value *) type storage is record [ tokens : tokens; allowances : allowances; total_amount : nat; ] type transfer is record [ address_from : address; address_to : address; value : nat; ] type approve is record [ spender : address; value : nat; ] type getAllowance is record [ owner : address; spender : address; callback : contract (nat); ] type getBalance is record [ owner : address; callback : contract (nat); ] type getTotalSupply is record [ callback : contract (nat); ] type action is Transfer of transfer | Approve of approve | GetAllowance of getAllowance | GetBalance of getBalance | GetTotalSupply of getTotalSupply function transfer (const p : transfer; const s: storage) : list (operation) * storage is block { var new_allowances : allowances := Big_map.empty; if Tezos.sender = p.address_from then { new_allowances := s.allowances; } else { var authorized_value : nat := case (Big_map.find_opt ((Tezos.sender,p.address_from), s.allowances)) of Some (value) -> value | None -> 0n end; if (authorized_value < p.value) then { failwith("Not Enough Allowance")} else { new_allowances := Big_map.update ((Tezos.sender,p.address_from), (Some (abs(authorized_value - p.value))), s.allowances) } }; var sender_balance : nat := case (Big_map.find_opt (p.address_from, s.tokens)) of Some (value) -> value | None -> 0n end; var new_tokens : tokens := Big_map.empty; if (sender_balance < p.value) then { failwith ("Not Enough Balance")} else { new_tokens := Big_map.update (p.address_from, (Some (abs(sender_balance - p.value))), s.tokens); var receiver_balance : nat := case (Big_map.find_opt (p.address_to, s.tokens)) of Some (value) -> value | None -> 0n end; new_tokens := Big_map.update (p.address_to, (Some (receiver_balance + p.value)), new_tokens); } } with ((nil: list (operation)), s with record [tokens = new_tokens; allowances = new_allowances]) function approve (const p : approve; const s : storage) : list (operation) * storage is block { var previous_value : nat := case Big_map.find_opt ((p.spender, Tezos.sender), s.allowances) of Some (value) -> value | None -> 0n end; var new_allowances : allowances := Big_map.empty; if previous_value > 0n and p.value > 0n then { failwith ("Unsafe Allowance Change")} else { new_allowances := Big_map.update ((p.spender, Tezos.sender), (Some (p.value)), s.allowances); } } with ((nil: list (operation)), s with record [allowances = new_allowances]) function getAllowance (const p : getAllowance; const s : storage) : list (operation) * storage is block { var value : nat := case Big_map.find_opt ((p.owner, p.spender), s.allowances) of Some (value) -> value | None -> 0n end; var op : operation := Tezos.transaction (value, 0mutez, p.callback); } with (list [op],s) function getBalance (const p : getBalance; const s : storage) : list (operation) * storage is block { var value : nat := case Big_map.find_opt (p.owner, s.tokens) of Some (value) -> value | None -> 0n end; var op : operation := Tezos.transaction (value, 0mutez, p.callback); } with (list [op],s) function getTotalSupply (const p : getTotalSupply; const s : storage) : list (operation) * storage is block { var total : nat := s.total_amount; var op : operation := Tezos.transaction (total, 0mutez, p.callback); } with (list [op],s) function main (const a : action; const s : storage) : list (operation) * storage is case a of Transfer (p) -> transfer (p,s) | Approve (p) -> approve (p,s) | GetAllowance (p) -> getAllowance (p,s) | GetBalance (p) -> getBalance (p,s) | GetTotalSupply (p) -> getTotalSupply (p,s) end;