Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Inspired by
- // https://ide.ligolang.org/p/4n70T9Z_iDwy6hH2Y0b3Pw linked to from
- // https://assets.tqtezos.com/docs/token-contracts/fa12/2-fa12-ligo/
- // This is an implementation of the FA1.2 specification in PascaLIGO
- // WARNING: this function might be susceptible to a Transfer/Approve attack. For more information, see:
- // https://github.com/ecadlabs/token-contract-example/issues/6
- type amt is nat;
- type account is record
- balance : amt;
- allowances: map(address, amt);
- end
- type action is
- | Transfer of (address * address * amt)
- | Approve of (address * amt)
- | GetAllowance of (address * address * contract(amt))
- | GetBalance of (address * contract(amt))
- | GetTotalSupply of (unit * contract(amt))
- | Mint of amt
- | Burn of amt
- type contract_storage is record
- ledger: big_map(address, account);
- owner: address;
- totalSupply: amt;
- end
- function isAllowed ( const spender : address ; const value : amt ; var s : contract_storage) : bool is
- begin
- var allowed: bool := False;
- if Tezos.sender =/= Tezos.source then block {
- const src: account = case s.ledger[spender] of
- Some (acc) -> acc
- | None -> (failwith("NoAccount"): account)
- end;
- const allowanceAmount: amt = case src.allowances[Tezos.sender] of
- Some (allowance) -> allowance
- | None -> (failwith("NoAllowance"): amt)
- end;
- allowed := allowanceAmount >= value;
- };
- else allowed := True;
- end with allowed
- function burn ( const value: amt ; var s : contract_storage ) : contract_storage is
- begin
- // We could let everyone burn as they can only burn their own tokens
- if Tezos.sender =/= s.owner then failwith("Only owner can burn");
- else skip;
- // Verify that caller has the sufficient balance to burn this amount
- // This *should* thrown if owner is not present in ledger, but get_force is deprecated
- // so we should probably use something else
- var ownerAccount := get_force(s.owner, s.ledger);
- if ownerAccount.balance < value then failwith("Insufficient balance to burn this amount");
- else skip;
- ownerAccount.balance := abs(ownerAccount.balance - value);
- s.ledger[s.owner] := ownerAccount;
- s.totalSupply := abs(s.totalSupply - value);
- end with s
- function mint ( const value : amt ; var s : contract_storage ) : contract_storage is
- begin
- if Tezos.sender =/= s.owner then failwith("Only owner can mint");
- else skip;
- var ownerAccount: account := record
- balance = 0n;
- allowances = (map end : map(address, amt));
- end;
- case s.ledger[s.owner] of
- Some (acc) -> ownerAccount := acc
- | None -> skip
- end;
- // For some reason I couldn't get this to work with `abs`
- // Do I need to write back the value to s?
- ownerAccount.balance := ownerAccount.balance + value;
- s.ledger[s.owner] := ownerAccount;
- s.totalSupply := s.totalSupply + value;
- end with s
- // Transfer a specific amount of tokens from the accountFrom address to a destination address
- // Pre conditions:
- // The sender address is the account owner or is allowed to spend x in the name of accountFrom
- // The accountFrom account has a balance higher than amount
- // Post conditions:
- // The balance of accountFrom is decreased by amount
- // The balance of destination is increased by amount
- function transfer (const accountFrom : address ; const destination : address ; const value : amt ; var s : contract_storage) : contract_storage is
- begin
- // If accountFrom = destination transfer is not necessary
- if accountFrom = destination then skip;
- else block {
- // Is sender allowed to spend value in the name of source
- //case isAllowed(accountFrom, value, s) of
- //| False -> failwith ("Sender not allowed to spend token from source")
- //| True -> skip
- //end;
- const allowed = isAllowed(accountFrom, value, s);
- if allowed then skip;
- else failwith ("Sender not allowed to spend token from source");
- // Fetch src account
- const src: account = case s.ledger[accountFrom] of
- Some (acc) -> acc
- | None -> (failwith("NoAccount"): account)
- end;
- // Check that the source can spend that much
- if value > src.balance
- then failwith ("Source balance is too low");
- else skip;
- // Update the source balance
- // Using the abs function to convert int to nat
- src.balance := abs(src.balance - value);
- s.ledger[accountFrom] := src;
- // Fetch dst account or add empty dst account to ledger
- var dst: account := record
- balance = 0n;
- allowances = (map end : map(address, amt));
- end;
- case s.ledger[destination] of
- | None -> skip
- | Some(n) -> dst := n
- end;
- // Update the destination balance
- dst.balance := dst.balance + value;
- // Decrease the allowance amount if necessary
- if accountFrom =/= sender then block {
- const allowanceAmount: amt = case src.allowances[Tezos.sender] of
- Some (allowance) -> allowance
- | None -> (failwith("NoAllowance"): amt)
- end;
- if allowanceAmount - value < 0 then failwith ("Allowance amount cannot be negative");
- else src.allowances[Tezos.sender] := abs(allowanceAmount - value);
- } else skip;
- s.ledger[destination] := dst;
- }
- end with s
- // Approve an amount to be spent by another address in the name of the sender
- // Pre conditions:
- // The spender account is not the sender account
- // Post conditions:
- // The allowance of spender in the name of sender is value
- function approve (const spender : address ; const value : amt ; var s : contract_storage) : contract_storage is
- begin
- // If sender is the spender approving is not necessary
- if Tezos.sender = spender then skip;
- else block {
- const src: account = case s.ledger[Tezos.sender] of
- Some (acc) -> acc
- | None -> (failwith("NoAccount"): account)
- end;
- src.allowances[spender] := value;
- s.ledger[Tezos.sender] := src; // Not sure if this last step is necessary
- }
- end with s
- // Note that the following three view functions are intended for contract-2-contract interaction,
- // they are not like Ethereum's view functions which can run without writing to the blockchain.
- // If you want to read a balance or another value from a deployed contract, you should read
- // directly from memory.
- // View function that forwards the allowance amount of spender in the name of tokenOwner to a contract
- // Pre conditions:
- // None
- // Post conditions:
- // The state is unchanged
- function getAllowance (const owner : address ; const spender : address ; const contr : contract(amt) ; var s : contract_storage) : list(operation) is
- begin
- const src: account = case s.ledger[owner] of
- Some (acc) -> acc
- | None -> (failwith("NoAccount"): account)
- end;
- const destAllowance: amt = case src.allowances[spender] of
- Some (allowance) -> allowance
- | None -> (failwith("NoAllowance"): amt)
- end;
- end with list [transaction(destAllowance, 0tz, contr)]
- // View function that forwards the balance of source to a contract
- // Pre conditions:
- // None
- // Post conditions:
- // The state is unchanged
- function getBalance (const src : address ; const contr : contract(amt) ; var s : contract_storage) : list(operation) is
- begin
- const src: account = case s.ledger[src] of
- Some (acc) -> acc
- | None -> (failwith("NoAccount"): account)
- end;
- end with list [transaction(src.balance, 0tz, contr)]
- // View function that forwards the totalSupply to a contract
- // Pre conditions:
- // None
- // Post conditions:
- // The state is unchanged
- function getTotalSupply (const contr : contract(amt) ; var s : contract_storage) : list(operation) is
- list [transaction(s.totalSupply, 0tz, contr)]
- function main (const p : action ; const s : contract_storage) :
- (list(operation) * contract_storage) is
- block {
- // Reject any transaction that try to transfer token to this contract
- if amount =/= 0tz then failwith ("This contract does not accept tezi deposits");
- else skip;
- } with case p of
- | Transfer(n) -> ((nil : list(operation)), transfer(n.0, n.1, n.2, s))
- | Approve(n) -> ((nil : list(operation)), approve(n.0, n.1, s))
- | Mint(n) -> ((nil : list(operation)), mint(n, s))
- | Burn(n) -> ((nil : list(operation)), burn(n, s))
- | GetAllowance(n) -> (getAllowance(n.0, n.1, n.2, s), s)
- | GetBalance(n) -> (getBalance(n.0, n.1, s), s)
- | GetTotalSupply(n) -> (getTotalSupply(n.1, s), s)
- end
Add Comment
Please, Sign In to add comment