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 SWT 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);
    }
}

Last updated