How to create your own ERC-20 token on Ethereum

Đinh Phong
Kudosvn
Published in
5 min readJun 5, 2019

Smart Contract Programming

Ethereum is a decentralized computing platform for executing smart contracts. These are programs that run code in a transparent environment, without the possibility of third-party tampering. A user’s private key is used to create a fundamentally unique signature for every request that executes a smart contract (see this wiki if you are not familiar with asymmetrical cryptography).

The Ethereum community has a sizable following of open source software engineers that are relentlessly developing amazing tools for the greater good of humanity. To make your own cryptocurrency on the Ethereum network, you need these four tools:

  • Solidity — An Ethereum smart contract programming language.
  • Truffle Framework — An Ethereum development kit.
  • Web3.js — A JavaScript package for interacting with the Ethereum network with an internet browser or Node.js.
  • Ether (ETH) — The currency of the Ethereum network.

Truffle Framework

Install: npm install -g truffle

Create project

$ mkdir ClashCoin && cd ClashCoin
$ truffle init

Once this operation is completed, you’ll now have a project structure with the following items:

Coding Token Contract

truffle.config.js

module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: "7545",
network_id: "*"
}
}
};

Create token file at contracts/ directory. Named ClashCoin.sol

pragma solidity ^0.5.0;

// ----------------------------------------------------------------------------
// 'FIXED' 'Example Fixed Supply Token' token contract
//
// Symbol : CLATR
// Name : Clash Trial
// Total supply: 1,000,000.000000000000000000
// Decimals : 18
//
// Enjoy.
//
// (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence.
// ----------------------------------------------------------------------------


// ----------------------------------------------------------------------------
// Safe maths
// ----------------------------------------------------------------------------
library SafeMath {
function add(uint a, uint b) internal pure returns (uint c) {
c = a + b;
require(c >= a);
}
function sub(uint a, uint b) internal pure returns (uint c) {
require(b <= a);
c = a - b;
}
function mul(uint a, uint b) internal pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b);
}
function div(uint a, uint b) internal pure returns (uint c) {
require(b > 0);
c = a / b;
}
}


// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
// ----------------------------------------------------------------------------
contract ERC20Interface {
function totalSupply() public view returns (uint);
function balanceOf(address tokenOwner) public view returns (uint balance);
function allowance(address tokenOwner, address spender) public view returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);

event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}


// ----------------------------------------------------------------------------
// Contract function to receive approval and execute function in one call
//
// Borrowed from MiniMeToken
// ----------------------------------------------------------------------------
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public;
}


// ----------------------------------------------------------------------------
// Owned contract
// ----------------------------------------------------------------------------
contract Owned {
address public owner;
address public newOwner;

event OwnershipTransferred(address indexed _from, address indexed _to);

constructor() public {
owner = msg.sender;
}

modifier onlyOwner {
require(msg.sender == owner);
_;
}

function transferOwnership(address _newOwner) public onlyOwner {
newOwner = _newOwner;
}
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}


// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and a
// fixed supply
// ----------------------------------------------------------------------------
contract ClashCoin is ERC20Interface, Owned {
using SafeMath for uint;

string public symbol;
string public name;
uint8 public decimals;
uint _totalSupply;

mapping(address => uint) balances;
mapping(address => mapping(address => uint)) allowed;


// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
constructor() public {
symbol = "CLATR";
name = "Clash Trial";
decimals = 0;
_totalSupply = 100000000 * 10**uint(decimals);
balances[owner] = _totalSupply;
emit Transfer(address(0), owner, _totalSupply);
}


// ------------------------------------------------------------------------
// Total supply
// ------------------------------------------------------------------------
function totalSupply() public view returns (uint) {
return _totalSupply.sub(balances[address(0)]);
}


// ------------------------------------------------------------------------
// Get the token balance for account `tokenOwner`
// ------------------------------------------------------------------------
function balanceOf(address tokenOwner) public view returns (uint balance) {
return balances[tokenOwner];
}


// ------------------------------------------------------------------------
// Transfer the balance from token owner's account to `to` account
// - Owner's account must have sufficient balance to transfer
// - 0 value transfers are allowed
// ------------------------------------------------------------------------
function transfer(address to, uint tokens) public returns (bool success) {
balances[msg.sender] = balances[msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
emit Transfer(msg.sender, to, tokens);
return true;
}


// ------------------------------------------------------------------------
// Token owner can approve for `spender` to transferFrom(...) `tokens`
// from the token owner's account
//
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// recommends that there are no checks for the approval double-spend attack
// as this should be implemented in user interfaces
// ------------------------------------------------------------------------
function approve(address spender, uint tokens) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
return true;
}


// ------------------------------------------------------------------------
// Transfer `tokens` from the `from` account to the `to` account
//
// The calling account must already have sufficient tokens approve(...)-d
// for spending from the `from` account and
// - From account must have sufficient balance to transfer
// - Spender must have sufficient allowance to transfer
// - 0 value transfers are allowed
// ------------------------------------------------------------------------
function transferFrom(address from, address to, uint tokens) public returns (bool success) {
balances[from] = balances[from].sub(tokens);
allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
emit Transfer(from, to, tokens);
return true;
}


// ------------------------------------------------------------------------
// Returns the amount of tokens approved by the owner that can be
// transferred to the spender's account
// ------------------------------------------------------------------------
function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
return allowed[tokenOwner][spender];
}


// ------------------------------------------------------------------------
// Token owner can approve for `spender` to transferFrom(...) `tokens`
// from the token owner's account. The `spender` contract function
// `receiveApproval(...)` is then executed
// ------------------------------------------------------------------------
function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);
return true;
}


// ------------------------------------------------------------------------
// Don't accept ETH
// ------------------------------------------------------------------------
function () external payable {
revert();
}


// ------------------------------------------------------------------------
// Owner can transfer out any accidentally sent ERC20 tokens
// ------------------------------------------------------------------------
function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
return ERC20Interface(tokenAddress).transfer(owner, tokens);
}
}

Edit name, symbol, decimals & totalSupply.

Write migration contract

Create file at migration/ directory. Named 2_deploy_contracts.js

const ClashCoin = artifacts.require("ClashCoin");

module.exports = function(deployer) {
deployer.deploy(ClashCoin);
};

Write Testing:

Create file at test/ directory. Named ClashCoin.js

const ClashCoin = artifacts.require("ClashCoin");

contract('ClashCoin', (accounts) => {
it('should put 1000000 ClashCoin in the first account', async () => {
const clashCoinInstance = await ClashCoin.deployed();
const balance = await clashCoinInstance.balanceOf(accounts[0]);

assert.equal(balance.toNumber(), 1000000, "1000000 wasn't in the first account");
});

it('should send coin correctly', async () => {
const clashCoinInstance = await ClashCoin.deployed();

// Setup 2 accounts.
const accountOne = accounts[0];
const accountTwo = accounts[1];

// Get initial balances of first and second account.
const accountOneStartingBalance = (await clashCoinInstance.balanceOf(accountOne)).toNumber();
const accountTwoStartingBalance = (await clashCoinInstance.balanceOf(accountTwo)).toNumber();

// Make transaction from first account to second.
const amount = 10;
await clashCoinInstance.transfer(accountTwo, amount, { from: accountOne });

// Get balances of first and second account after the transactions.
const accountOneEndingBalance = (await clashCoinInstance.balanceOf(accountOne)).toNumber();
const accountTwoEndingBalance = (await clashCoinInstance.balanceOf(accountTwo)).toNumber();

assert.equal(accountOneEndingBalance, accountOneStartingBalance - amount, "Amount wasn't correctly taken from the sender");
assert.equal(accountTwoEndingBalance, accountTwoStartingBalance + amount, "Amount wasn't correctly sent to the receiver");
});
});

Command:

  • Migrate: truffle migrate
  • Test: truffle test
  • Launch test enviroment: ganache

--

--