Token Smart Contract

The code below is an example and requires further refinement for specific business tasks.

Interface BasicStandard is necessary to create a token (implementation is mandatory and provided below).

public interface BasicStandard { String getName(); String getSymbol(); int getDecimal(); boolean setFrozen(boolean frozen); String totalSupply(); String balanceOf(String owner); String allowance(String owner, String spender); boolean transfer(String to, String amount); boolean transferFrom(String from, String to, String amount); void approve(String spender, String amount); boolean burn(String amount); }

As well as interface ExtensionStandard (Implementation is not mandatory, but recommended and is provided below).

public interface ExtensionStandard extends BasicStandard { void register(); boolean buyTokens(String amount); }

Further is the example of a token implementation according to Credits standard.

Examples sets the following in the class constructor:

  • Token name (name)

  • Token symbol (symbol)

  • Number of decimals (decimal)

  • Public key of the token owner (owner)

  • Total number of tokens (totalCoins)

Examples of implementation of Interfaces BasicStandard and ExtensionsStandard are provided below.

import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; import java.util.Optional; import com.credits.scapi.annotations.*; import com.credits.scapi.v0.*; import static java.math.BigDecimal.ROUND_FLOOR; import static java.math.BigDecimal.ZERO; public class Token12 extends SmartContract implements ExtensionStandard { private final String owner; private final BigDecimal tokenCost; private final int decimal; HashMap<String, BigDecimal> balances; private String name; private String symbol; private BigDecimal totalCoins; private HashMap<String, Map<String, BigDecimal>> allowed; private boolean frozen; public Token12() { super(); name = "1"; symbol = "12"; decimal = 1; tokenCost = new BigDecimal(0).setScale(decimal, ROUND_FLOOR); totalCoins = new BigDecimal(100).setScale(decimal, ROUND_FLOOR); owner = initiator; allowed = new HashMap<>(); balances = new HashMap<>(); balances.put(owner, new BigDecimal(100L).setScale(decimal, ROUND_FLOOR)); } @Override public int getDecimal() { return decimal; } @Override public void register() { balances.putIfAbsent(initiator, ZERO.setScale(decimal, ROUND_FLOOR)); } @Override public boolean setFrozen(boolean isFrozen) { if (!initiator.equals(owner)) { throw new RuntimeException("unable change frozen state! The wallet "+ initiator + " is not owner"); } this.frozen = isFrozen; return true; } @Override public String getName() { return name; } @Override public String getSymbol() { return symbol; } @Override public String totalSupply() { return totalCoins.toString(); } @Override public String balanceOf(String owner) { return getTokensBalance(owner).toString(); } @Override public String allowance(String owner, String spender) { if (allowed.get(owner) == null) { return "0"; } BigDecimal amount = allowed.get(owner).get(spender); return amount != null ? amount.toString() : "0"; } @Override public boolean transfer(String to, String amount) { contractIsNotFrozen(); if (!to.equals(initiator)) { BigDecimal decimalAmount = toBigDecimal(amount); BigDecimal sourceBalance = getTokensBalance(initiator); BigDecimal targetTokensBalance = getTokensBalance(to); if (decimalAmount.compareTo(ZERO.setScale(decimal, ROUND_FLOOR)) < 0) { throw new RuntimeException("invalid amount"); } if(targetTokensBalance == null) { targetTokensBalance = new BigDecimal(0).setScale(decimal, ROUND_FLOOR); } if (sourceBalance.compareTo(decimalAmount) < 0) { throw new RuntimeException("the wallet" + initiator + "doesn't have enough tokens to transfer"); } balances.put(initiator, sourceBalance.subtract(decimalAmount)); balances.put(to, targetTokensBalance.add(decimalAmount)); } return true; } @Override public boolean transferFrom(String from, String to, String amount) { contractIsNotFrozen(); if (!from.equals(to)) { BigDecimal sourceBalance = getTokensBalance(from); BigDecimal targetTokensBalance = getTokensBalance(to); if(targetTokensBalance == null) { targetTokensBalance = new BigDecimal(0).setScale(decimal, ROUND_FLOOR); } BigDecimal decimalAmount = toBigDecimal(amount); if (decimalAmount.compareTo(ZERO.setScale(decimal, ROUND_FLOOR)) < 0) { throw new RuntimeException("invalid amount"); } if (sourceBalance.compareTo(decimalAmount) < 0) throw new RuntimeException("unable transfer tokens! The balance of " + from + " less then " + amount); Map<String, BigDecimal> spender = allowed.get(from); if (spender == null || !spender.containsKey(initiator)) throw new RuntimeException("unable transfer tokens! The wallet " + from + " not allow transfer tokens for " + to); BigDecimal allowTokens = spender.get(initiator); if (allowTokens.compareTo(decimalAmount) < 0) { throw new RuntimeException("unable transfer tokens! Not enough allowed tokens. For the wallet " + initiator + " allow only " + allowTokens + " tokens"); } spender.put(initiator, allowTokens.subtract(decimalAmount)); balances.put(from, sourceBalance.subtract(decimalAmount)); balances.put(to, targetTokensBalance.add(decimalAmount)); } return true; } @Override public void approve(String spender, String amount) { initiatorIsRegistered(); BigDecimal decimalAmount = toBigDecimal(amount); if (decimalAmount.compareTo(ZERO.setScale(decimal, ROUND_FLOOR)) < 0) { throw new RuntimeException("invalid amount"); } Map<String, BigDecimal> initiatorSpenders = allowed.get(initiator); if (initiatorSpenders == null) { Map<String, BigDecimal> newSpender = new HashMap<>(); newSpender.put(spender, decimalAmount); allowed.put(initiator, newSpender); } else { BigDecimal spenderAmount = initiatorSpenders.get(spender); initiatorSpenders.put(spender, spenderAmount == null ? decimalAmount : spenderAmount.add(decimalAmount)); } } @Override public boolean burn(String amount) { contractIsNotFrozen(); BigDecimal decimalAmount = toBigDecimal(amount); if (decimalAmount.compareTo(ZERO.setScale(decimal, ROUND_FLOOR)) < 0) { throw new RuntimeException("invalid amount"); } if (!initiator.equals(owner)) throw new RuntimeException("can not burn tokens! The wallet " + initiator + " is not owner"); if (totalCoins.compareTo(decimalAmount) < 0) totalCoins = ZERO; else totalCoins = totalCoins.subtract(decimalAmount); return true; } public void payable(String amount, String currency) { contractIsNotFrozen(); BigDecimal decimalAmount = toBigDecimal(amount); if (totalCoins.compareTo(decimalAmount) < 0) throw new RuntimeException("not enough tokes to buy"); balances.put(initiator, Optional.ofNullable(balances.get(initiator)).orElse(ZERO).add(decimalAmount)); totalCoins = totalCoins.subtract(decimalAmount); } @Override public boolean buyTokens(String amount) { return false; } private void contractIsNotFrozen() { if (frozen) throw new RuntimeException("unavailable action! The smart-contract is frozen"); } private void initiatorIsRegistered() { if (!balances.containsKey(initiator)) throw new RuntimeException("unavailable action! The wallet " + initiator + " is not registered"); } private BigDecimal toBigDecimal(String stringValue) { return new BigDecimal(stringValue).setScale(decimal, ROUND_FLOOR); } private BigDecimal getTokensBalance(String address) { return balances.get(address); } }


On This Page
Token Smart Contract