Wallace Coin

Building an ERC-20 token.

Building an ERC-20 token. | Blog

Building an ERC-20 token.

As a newcomer to the world of blockchain and cryptocurrency, it’s essential to grasp how tokens work. This blog post aims to demystify WallaceCoin, a hypothetical ERC-20 token, by breaking down its code and discussing potential improvements.

By the end, you and I will have a clearer understanding of how cryptocurrencies function and some best practices for security and decentralisation.

Setting Up the Basics


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

contract WallaceCoin {
    string public name = "WallaceCoin";
    string public symbol = "WLC";
    uint8 public decimals = 18;
    uint256 public totalSupply = 1000000 * 10 ** uint256(decimals);

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

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

Explanation: This segment defines the basic characteristics of WallaceCoin. The name, symbol, and decimals are straightforward identifiers, while totalSupply represents the total number of tokens created. The balanceOf mapping tracks each user’s balance, and allowance allows a user to let another user spend a specified amount of their tokens.

Potential Improvements:

  • Immutable Variables: Marking name, symbol, decimals, and totalSupply as immutable (i.e., constants) once set can improve efficiency and security.
  • OpenZeppelin Library: Using a well-tested library like OpenZeppelin’s ERC-20 implementation can enhance security by relying on a community-vetted codebase.

Constructor: Initialising the Total Supply


    constructor() {
        balanceOf[msg.sender] = totalSupply;
    }

Explanation: The constructor function assigns the total supply of WallaceCoin to the deployer’s address when the contract is created. This initial allocation centralises control to the deployer.

Potential Improvements:

  • Decentralised Distribution: To ensure decentralisation, we should consider implementing a fair distribution mechanism, such as an airdrop or initial coin offering (ICO) that distributes tokens to multiple addresses.

Transfer Function: Moving Tokens


    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value, "Insufficient balance");
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
        emit Transfer(msg.sender, _to, _value);
        return true;
    }

Explanation: The transfer function allows users to send tokens to another address. It checks if the sender has enough balance, updates the balances, and emits a Transfer event to log the transaction.

Potential Improvements:

  • Re-entrancy Protection: Although transfer does not involve external calls, adding re-entrancy protection can be a good practice for more complex functions to prevent malicious re-entry attacks.
  • Gas Optimisation: Ensuring minimal gas consumption by optimising code and using efficient algorithms.

Approve Function: Allowing Others to Spend


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

Explanation: The approve function allows a user to authorise another address to spend a specific amount of their tokens. This would work for delegated transactions.

Potential Improvements:

  • Mitigating Race Conditions: Instead of setting the allowance directly, we could consider using increaseAllowance and decreaseAllowance functions to prevent potential race conditions.

TransferFrom Function: Delegated Transfers


    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= balanceOf[_from], "Insufficient balance");
        require(_value <= allowance[_from][msg.sender], "Allowance exceeded");
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;
        emit Transfer(_from, _to, _value);
        return true;
    }
}

Explanation: The transferFrom function enables an authorised spender to transfer tokens from one address to another. It ensures that the sender has enough balance and the spender is authorised to make the transaction.

Potential Improvements:

  • Security Enhancements: Using unchecked blocks for arithmetic operations that are safe from overflow can reduce gas usage.
  • ERC-20 Extensions: Implementing advanced features like ERC-20Permit can enable gasless approvals, enhancing user experience.

The WallaceCoin is not quite ready…

WallaceCoin serves as a practical example to understand how ERC-20 tokens work. While the basic implementation is functional, there are several ways to enhance security and promote decentralisation, the former is key to increasing trust in the web3 space and the latter is what we should be aiming for in a Web3 world:

  1. Immutable Variables: Reduce the risk of changes and improve gas efficiency.
  2. OpenZeppelin Libraries: Leverage well-tested code for enhanced security.
  3. Advanced Approvals: Mitigate race conditions and improve user control.
  4. Gas Optimisation: Implement unchecked arithmetic and other gas-saving techniques.
  5. Decentralised Distribution: Ensure fair token distribution to promote decentralisation.

I don’t plan on taking this token further, there are already too many alt-coins out there, but understanding these concepts is useful as I delve deeper into blockchain and cryptocurrency, and for others to appreciate the importance of security and decentralisation in creating robust and trustworthy tokens.