How to create an ERC-20 Token with Solidity

How to create an ERC-20 Token with Solidity

ERC-20 Token creation

To create an ERC-20 Token we first need to know what is a Token

Simply put, tokens are smart contracts that make use of the blockchain in this case its Ethereum blockchain.

Tokens can represent virtually anything in Ethereum:

  • reputation points in an online platform
  • skills of a character in a game
  • lottery tickets
  • financial assets like a share in a company
  • a fiat currency like INR or USD
  • an ounce of gold and more...

What is an ERC-20 Token?

The ERC-20 (Ethereum Request for Comments 20), proposed by Fabian Vogelsteller in November 2015, is a Token Standard that implements an API for tokens within Smart Contracts. It is a standard for Fungible Tokens which have a property that makes each Token be exactly the same as another Token. For example, an ERC-20 Token acts just like the ETH, meaning that 1 Token is and will always be equal to all the other Tokens.

The ERC-20 Body

The body of an ERC-20 token contains the methods and events an ERC-20 token must have.

Example functionalities ERC-20 provides:

  • transfer tokens from one account to another

  • get the current token balance of an account

  • get the total supply of the token available on the network

  • approve whether an amount of token from an account can be spent by a third-party account

In practice, an ERC-20 would look something like this in Solidity:

function name() public view returns (string)

function symbol() public view returns (string)

function decimals() public view returns (uint8)

function totalSupply() public view returns (uint256)

function balanceOf(address _owner) public view returns (uint256 balance)

function transfer(address _to, uint256 _value) public returns (bool success)

function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)

function approve(address _spender, uint256 _value) public returns (bool success)

function allowance(address _owner, address _spender) public view returns (uint256 remaining)

The following methods can be available in an ERC-20 token:

  • name returns the name of the token (e.g., Binance Coin)

  • symbol returns the symbol of the token (e.g., BNB)

  • decimals returns the number of decimals the token uses

  • totalSupply returns the total number initially supplied to the token

  • balanceOf returns the balance of an account

  • transfer transfers a certain amount of tokens to an address

  • transferFrom transfers a certain amount of tokens from a beneficiary address to a recipient address

  • approve withdraws tokens from the owner’s address up to a certain amount of tokens

  • allowance returns the number of tokens withdrawable from the owner’s account

ERC-20 tokens have the following events:

event Transfer(address indexed _from, address indexed _to, uint256 _value)

event Approval(address indexed _owner, address indexed _spender, uint256 _value)
  • Transfer, which must be triggered when tokens are transferred

  • Approval, which must be triggered when an account is approved to collect a certain amount of tokens

Creating an ERC-20 Token

Let’s write a simple token. We’ll call it HN Coin 😀

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

contract HNCoinERC20 {}

In the first line of the code, we set the license identifier MIT License and the version of Solidity v0.7.0–0.9.0 the code was written for. We declared our contract by using the contract keyword and then giving it the name HNCoinERC20.

Set the events of the token

event Transfer(address indexed from, address indexed to, uint tokens);

event Approval(address indexed tokenOwner, address indexed spender, uint tokens);

Declare all the variables

I will tell you the use of the variables in a bit.

string public constant name;
string public constant symbol;
uint8 public constant decimals;
uint256 totalSupply_;

Set the balances and allowances mappings

mapping(address => uint256) balances;

mapping(address => mapping (address => uint256)) allowed;

Create the Constructor

Next, we have the constructor. We know that constructors are called when the class is being created. In smart contracts, the constructor is called when the contract is deployed to the network.

constructor(uint256 total) {
  totalSupply_ = total;
  balances[msg.sender] = totalSupply_;
  name = "HN Coin";
  symbol = "HN";
  decimals = 18;
}

Here, the constructor is called with the total number of tokens we want to be in our contract (total). The total is set to totalSupply_, and the balance of the deploying address is set to the total tokens. The msg.sender contains the Ethereum account of the currently executing contract function. Then, the name , symbol and decimals of our coin is set to the previously created name, symbol and decimals variable.

Writing functions of the smart contract

Get the balance of an owner

function balanceOf(address tokenOwner) public view returns (uint) {
    return balances[tokenOwner];
}

This method has an argument, tokenOwner. This argument is the address of the token owner to whom we want to return the balance of the token in the contract. So the method gets the balance by referencing the tokenOwner address from the balances.

Transfer tokens to an account

The method is transfer:

function transfer(address receiver, uint numTokens) public returns (bool) {
    require(numTokens <= balances[msg.sender]);
    balances[msg.sender] -= numTokens;
    balances[receiver] += numTokens;
    emit Transfer(msg.sender, receiver, numTokens);
    return true;
}

This method has the following arguments:

receiver, the address of the account that will receive tokens

numTokens, the number of tokens that will be sent to the receiver account

In the body of the method, we see that a check is made to verify that the number of tokens to be sent to the recipient is enough according to the deployer’s address balance.

Next, the numTokens is subtracted from the deployer’s balance and credited to the receiver‘s balance. Then, a Transfer event is emitted. Finally, the Boolean true is returned.

Approve a token transfer

The next method is approve:

function approve(address delegate, uint numTokens) public returns (bool) {
    allowed[msg.sender][delegate] = numTokens;
    emit Approval(msg.sender, delegate, numTokens);
    return true;
}

This method has arguments called delegate and numTokens.

delegate is the address we want to set the number of tokens that the deployer can send to it

numTokens is the number of tokens the deployer can send to the delegate

We reference the delegate map in the allowed mapping to set the number of tokens to it. Then, we emit the Approval event and return true.

Get the allowance status of an account

The method is allowance:

function allowance(address owner, address delegate) public view returns (uint) {
    return allowed[owner][delegate];
}

This method has the following arguments: owner and delegate.

owner is the address to return the number of tokens transferable to the recipient in the delegate.

Transfer tokens from an account to another account

The method to transfer tokens from one account to another is transferFrom:

function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {
    require(numTokens <= balances[owner]);
    require(numTokens <= allowed[owner][msg.sender]);
    balances[owner] -= numTokens;
    allowed[owner][msg.sender] -= numTokens;
    balances[buyer] += numTokens;
    emit Transfer(owner, buyer, numTokens);
    return true;
}

transferFrom has args called owner, buyer and numTokens.

owner is the address of the balances from which we will transfer the numTokens buyer is the address in the balances that we will credit the numTokens numTokens is the number of tokens to be transferred from owner to buyer

In the method body, we first check whether the balance in the owner is enough and whether the owner is approved to send that amount of tokens to the buyer.

Next, the transfer is made by subtracting the number of tokens from the owner’s balance and allowed balance. Then, the number of tokens is added to the buyer’s balance.

The Transfer event is emitted and the boolean returned is true.

Full code

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

contract HNCoinERC20 {

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

    string public constant name;
    string public constant symbol;
    uint8 public constant decimals;
    uint256 totalSupply_;

    mapping(address => uint256) balances;

    mapping(address => mapping (address => uint256)) allowed;

    constructor(uint256 total) {
      totalSupply_ = total;
      balances[msg.sender] = totalSupply_;
      name = "HN Coin";
      symbol = "HN";
      decimals = 18;
    }

    function totalSupply() public view returns (uint256) {
      return totalSupply_;
    }

    function balanceOf(address tokenOwner) public view returns (uint) {
        return balances[tokenOwner];
    }

    function transfer(address receiver, uint numTokens) public returns (bool) {
        require(numTokens <= balances[msg.sender]);
        balances[msg.sender] -= numTokens;
        balances[receiver] += numTokens;
        emit Transfer(msg.sender, receiver, numTokens);
        return true;
    }

    function approve(address delegate, uint numTokens) public returns (bool) {
        allowed[msg.sender][delegate] = numTokens;
        emit Approval(msg.sender, delegate, numTokens);
        return true;
    }

    function allowance(address owner, address delegate) public view returns (uint) {
        return allowed[owner][delegate];
    }

    function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) {
        require(numTokens <= balances[owner]);
        require(numTokens <= allowed[owner][msg.sender]);

        balances[owner] -= numTokens;
        allowed[owner][msg.sender] -= numTokens;
        balances[buyer] += numTokens;
        emit Transfer(owner, buyer, numTokens);
        return true;
    }
}

Conclusion

In this tutorial we learned how to create an ERC-20 Token on the Ethereum blockchain.

This is my first blog so of you liked the tutorial and it helped you somehow then please give it a like and let me know how it helped you and what are the things in the writing style that can be improved.

Thank you for reading ❤️