An ERC20 Token Contract

An ERC20 Token Contract

A deep drive into the code of a standard ERC20 Token Contract

ยท

8 min read

A token in blockchain is a digital representation of an asset. In the case of Ethereum, they live on the Ethereum blockchain, which means they aren't independent as they fully rely on the blockchain.
Tokens are created from smart contracts, you can earn them, transfer them, and use them within an Ethereum-based project.

ERC20 is the standard interface for tokens on the Ethereum blockchain and it was proposed by Fabian Vogelsteller and Vitalik Buterin. This standard ensures that all tokens work smoothly and can interact with each other easily, this makes it a standard for anyone that wants to create a token.

Let's build!

Now, let's delve into building/writing an ERC20 token!

In this Token contract, users can transfer their tokens to another account by specifying the address. When any transfer of tokens occurs in this contract, the contract logic deducts a 10% fee from every transaction. The fee deducted is from the account of the person who is calling the transfer function.

I will be doing a walk-through of the contract, if this is your first token contract, stay with me, I will do as much as I can to break it down for you to understand.

If you would like the full code as we progress, here it is:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract ERC20Token {
    string public name = "Vickish";
    string public symbol = "VVA";
    uint8 public decimals =  18;
    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowanceMap;

    constructor(uint256 _initialSupply) {
        totalSupply = _initialSupply *  10**uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Burn(address indexed from, uint256 value);

    function transfer(address _to, uint256 _value) external returns(bool success){
        uint _userBalance =  balanceOf[msg.sender];

        require(_userBalance >= _value, "You don't have enough tokens");

        uint256 tokensToBurn = _value / 10;

        _userBalance =  _userBalance - tokensToBurn;
        balanceOf[_to] =  balanceOf[_to] + _value;

        totalSupply = totalSupply - tokensToBurn;

        emit Transfer(msg.sender, _to, _value);
        emit Burn(msg.sender, tokensToBurn);

        return true;
    }

    function approve(address _spender, uint256 _value) external returns (bool success) {
        allowanceMap[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    function allowance(address _owner, address _spender) external view returns (uint256 remaining) {
        return allowanceMap[_owner][_spender];
    }

    function transferFrom(address _from, address _to, uint256 _value) external returns (bool success) {
        require(_value >  0, "Value must be greater than zero.");
        require(_value <= balanceOf[_from], "Insufficient balance.");
        require(_value <= allowanceMap[_from][msg.sender], "Approved allowance exceeded.");

        uint256 tokensToBurn = _value / 10;

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        balanceOf[msg.sender] = balanceOf[msg.sender] - tokensToBurn;
        allowanceMap[_from][msg.sender] -= _value;

        totalSupply = totalSupply - tokensToBurn;

        emit Transfer(_from, _to, _value);
        emit Approval(_from, msg.sender, allowanceMap[_from][msg.sender]);
        emit Burn(msg.sender, tokensToBurn);

        return true;
    }
}

Code Explanation

Here we go, make sure to follow along.

This is the structure of a Solidity smart contract.

// Structure of a solidity smart contract
contract ERC20Token {
       //Code here
}

The first thing we want to do inside our contract is declare our variables.
There's another important thing which is the mapping.
The first mapping takes in the address and gives you the token balance of that user.

The second mapping takes in two addresses(the owner, and the spender), and returns the amount that the owner has approved that the spender can spend.

contract ERC20Token {
   // Token Name
    string public name = "Vickish";

    // Token Symbol
    string public symbol = "VVA";

    // This represents the total number of decimal place in the token which is set to 18
    // We made it private because it's a helper variable, so we only need it inside the contract 
    uint8 public decimals = 18;

    // Declares the total supply of the token
    uint256 public totalSupply;

    // A mapping that takes in the address and gives you it's balance
    mapping(address => uint256) public balanceOf;

    // A mapping that takes in the from and to address, then returns the value that spender has been approved to spend 
    mapping(address => mapping(address => uint256)) public allowanceMap;
}

Let's talk about the Constructor.

A constructor is a unique kind of function in Solidity. It's not required, and it runs just once during the creation of a contract.

    // This Constructor is executed upon contract creation
    // If you have parameters in your constructor, you must input those parameters before you are able to deploy your contract. 
    // In this case, _initialSupply
    constructor(uint256 _initialSupply) {
        totalSupply = _initialSupply * 10**uint256(decimals);
        balanceOf[msg.sender] = totalSupply;
    }

Moving on, events are just ways that you can log what happened in the blockchain to the user.

    // This is an event that will emits the transfer that the user has performed,
    // it emits the sender, reciever, and sent token amount
    event Transfer(address indexed from, address indexed to, uint256 value);

    // This is an event that will emit the address that the burnt token is coming fom when is the sender
    // It also emit the burnt token value 
    event Burn(address indexed from, uint256 value);

    // This event is emitted when an approval is granted.
    event Approval(address indexed owner, address indexed spender, uint256 value);

The transfer function takes in the address(_to) that would receive the token and the amount(_value). This transfer function is used when it's a direct transaction, which means the from account is performing the transaction themselves.


    // This is a transfer function that takes in an address _to and _value, 
    function transfer(address _to, uint256 _value) external returns(bool success){
        // stores the balance of the user to variable name _userBalance
        uint _userBalance =  balanceOf[msg.sender];

        // This does a check that _userBalance must be greater or equal the value you want to send, if it doesnt it emit the that you dont ahave enough toekn to tranfer
        require(_userBalance >= _value, "You don't have enough tokens");

        // Saves the token to burn into a variable, it divides the amount you want to send by 10, which gives us the 10% fee that we are going to burn 
        uint256 tokensToBurn = _value / 10;

        // removes the token we want to burn from the account of the person calling this tranfer function
        _userBalance =  _userBalance - tokensToBurn;

        // This just basically transfers the value we want to send to the _to address, which is the person reciveing the token 
        balanceOf[_to] =  balanceOf[_to] + _value;

        // Remove the 10% from totalSupply thereby burning it
        totalSupply = totalSupply - tokensToBurn;

        // Emit transfer and burn events
        emit Transfer(msg.sender, _to, _value);
        emit Burn(msg.sender, tokensToBurn);

        return true;
    }

Approve function: This function approves the address in the function parameter (spender)*to spend the amount(* value) on your behalf. The spender cannot spend more than you have approved it to spend, this makes it possible for the transferFrom function to remove the money from your account.

 // This function to approves another address to spend tokens on behalf of the sender(from address).
 function approve(address _spender, uint256 _value) external returns (bool success) {
        //Sets the amount that spender can spend.
        allowanceMap[msg.sender][_spender] = _value;

        //Emits an Approval event.
        emit Approval(msg.sender, _spender, _value);

        // Indicates successful approval.
        return true;
    }

Allowance function: This returns the amount that the from address has approved that the to address can spend.

 // This function returns the amount that spender is still allowed to spend
 function allowance(address _owner, address _spender) external view returns (uint256 remaining) {
        // A mapping that returns the amount
        return allowanceMap[_owner][_spender];
    }

tansferFrom function: this is an important function that relies on the approve function because the from address must have approved that they want a certain amount to be transferred from their account to another account. Note that compared to the transfer function, it takes in three parameters.

This function would transfer the value from the from address to the to address provided the value is not greater than the amount that has been approved in the allowanceMap.

 // This function transfers tokens from one address to another if the _to address has been approved.
 function transferFrom(address _from, address _to, uint256 _value) external returns (bool success) {
        // Requires that the transfer amount is greater than zero.
        require(_value >  0, "Value must be greater than zero.");

        // Requires that the sender has enough tokens.
        require(_value <= balanceOf[_from], "Insufficient balance.");

        // Requires that the value is not greater than the approve amount in the allowanceMap
        require(_value <= allowanceMap[_from][msg.sender], "Approved allowance exceeded.");

        // Calcute amount of token to be burnt, which is 10 percent of the value.
        uint256 tokensToBurn = _value / 10;

        // Subtracts the transferred amount from the balance of the from address.
        balanceOf[_from] -= _value;

        // Adds the transferred tokens to the recipient's balance.
        balanceOf[_to] += _value;

        // Subtracts the burn amount from the balance of the address(_to) that called this function.
        balanceOf[msg.sender] = balanceOf[msg.sender] - tokensToBurn;

        // Substracts the sent amount from allowance.
        allowanceMap[_from][msg.sender] -= _value;

        // Reduces the total supply of the token by subtracting the burned tokens from the totalSupply.
        totalSupply = totalSupply - tokensToBurn;

        // Emits the Transfer, Approval and Burn event
        emit Transfer(_from, _to, _value);
        emit Approval(_from, msg.sender, allowanceMap[_from][msg.sender]);
        emit Burn(msg.sender, tokensToBurn);

        // Indicates successful transfer.
        return true;
    }

Conclusion

And it's a wrap! We now understand what tokens are and why we use the ERC20 token standard. Tokens are a very important part of the blockchain and knowing how to create one just gives an edge.
Remember, this is just the beginning! If you are curious like me and would like to explore more about the ERC token standard => Click me.
Thanks for reading ๐Ÿ‘‹๐Ÿผ