Create and Deploy your ERC20 Smart Contract to Somnia Network

The Somnia mission is to enable the development of mass-consumer real-time applications. To achieve this as a developer, you will need to build applications that are Token enabled, as this is a requirement for many Blockchain applications. This guide will teach you how to connect to and deploy your ERC20 Smart Contract to the Somia Network using the Remix IDE.

Pre-requisite

  1. This guide is not an introduction to Solidity Programming; you are expected to understand Basic Solidity Programming.

  2. To complete this guide, you will need MetaMask installed and the Somnia DevNet added to the list of Networks. If you have yet to install MetaMask, please follow this guide to Connect Your Wallet.

Somnia Network is an EVM-compatible Layer 1 Blockchain. This means that critical implementation on Ethereum is available on Somnia, with higher throughput and faster finality. Smart Contract that follows the ERC-20 standard is an ERC-20 token; these Smart Contracts are often referred to as Token Contracts.

ERC-20 tokens provide functionality to

  • Transfer tokens

  • Allow others to transfer tokens on behalf of the token holder

It is important to note that ERC20 Smart Contracts are different from the Native Somnia Contracts, which are used to pay gas fees when transacting on Somnia. In the following steps, we will create an ERC20 Token by following the EIP Standard and also demonstrate the option to use a Library to create the ERC20 Token.

IERC-20

According to the EIP Standard, certain Smart Contract methods must be implemented adhering to the standard so that other Smart Contracts can interact with the deployed Smart Contracts and call the method. To achieve this, we will use the Solidity Interface Type to create the Smart Contract standard.

In Solidity, an interface is a special contract that defines a set of function signatures without implementation. It acts as a "blueprint" for other contracts, ensuring they adhere to a specific structure. Interfaces are crucial for creating standards, such as the ERC-20 token standards, allowing different contracts to interact seamlessly within the EVM ecosystem.

Create an Interface for the ERC20 Token. Copy and paste the code below into a file named IERC20.sol

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

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount)
        external
        returns (bool);
    function allowance(address owner, address spender)
        external
        view
        returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount)
        external
        returns (bool);
}

ERC-20 Token Contract

With the Interface created and all the Token standard methods implemented, the next step is to import the Interface into the ERC20 Smart Contract implementation.

Create a file called ERC20.sol and paste the code below into it.

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

import "./IERC20.sol";

contract ERC20 is IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner, address indexed spender, uint256 value
    );

    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    string public name;
    string public symbol;
    uint8 public decimals;

    constructor(string memory _name, string memory _symbol, uint8 _decimals) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
    }

    function transfer(address recipient, uint256 amount)
        external
        returns (bool)
    {
        balanceOf[msg.sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount)
        external
        returns (bool)
    {
        allowance[sender][msg.sender] -= amount;
        balanceOf[sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(sender, recipient, amount);
        return true;
    }

    function _mint(address to, uint256 amount) internal {
        balanceOf[to] += amount;
        totalSupply += amount;
        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal {
        balanceOf[from] -= amount;
        totalSupply -= amount;
        emit Transfer(from, address(0), amount);
    }

    function mint(address to, uint256 amount) external {
        _mint(to, amount);
    }


    function burn(address from, uint256 amount) external {
        _burn(from, amount);
    }
}

We have implemented the various requirements for the ERC20 Token Standard:

constructor: Initializes the token's basic properties.

Accepts the token's name, symbol, and the number of decimals as parameters and sets these values as the token's immutable metadata.

Example: ERC20("MyToken", "MTK", 18) initializes a token named MyToken with the symbol

MTK and 18 decimal places (the standard for ERC-20).

Functions

transfer: Moves a specified amount of tokens from the sender to a recipient.

It checks if the sender has enough balance and deducts the amount from the sender's balance, and adds it to the recipient's. It also emits a Transfer event to log the transaction and returns `true` if the transfer is successful.

approve: It allows a spender to spend up to a certain amount of tokens on behalf of the owner. It sets the allowance for the spender to the specified amount. The spender is usually another Smart Contract. It then emits an Approval event to record the spender's allowance. It returns true if the approval is successful. A common use case is to enable spending through third-party contracts like decentralized exchanges (DEXs).

transferFrom: It allows a spender to transfer tokens on behalf of another account.

It checks if the sender has an approved allowance for the spender and ensures it is sufficient for the amount and deducts the amount from the allowance and the sender's balance and adds the amount to the recipient's balance. It also emits a Transfer event to log the transaction. It returns `true` if the transfer is successful. A common use case is DEXs or smart contracts to handle token transactions on behalf of users.

_mint: Creates new tokens and adds them to a specified account's balance.

Calling the method increases the recipient's balanceOf by the specified amount. It also increases the totalSupply of the ERC20 Tokens by the same amount. A transfer event with the from address as address(0) (indicating tokens are created).

_burn: Destroys a specified number of tokens from an account's balance.

It reduces the account's balanceOf and decreases the totalSupply by the amount. It emits a Transfer event with the to address as address(0) (indicating tokens are burned).

It is typically implemented in token-burning mechanisms to reduce supply, increasing scarcity.

mint: Public wrapper for _mint. It calls _mint to create new tokens for a specified to address. Allows the contract owner or authorized accounts to mint new tokens.

burn: Public wrapper for _burn. Calls _burn to destroy tokens from a specified from address.

Events

Transfer:

Logs token transfers, including minting and burning events. Parameters: from (sender), to (recipient), value (amount).

Approval:

Logs approvals of allowances for spenders. Parameters: owner, spender, value (allowance amount).

Compile Smart Contract

The ERC20 Token is now ready to be deployed to the Somnia Blockchain.

On the left tab, click the “Solidity Compiler” menu item and then the “ Compile ERC20.sol” button. This will compile the Solidity file and convert the Solidity code into machine-readable bytecode.

Deploy Smart Contract

The Smart Contract has been created and compiled into ByteCode, and the ABI has also been created. The next step is to deploy the Smart Contract to the Somnia DevNet so that you can perform READ and WRITE operations.

On the left tab, click the “Deploy and run transactions” menu item. To deploy the Smart Contract, we will require a wallet connection. In the Environment dropdown, select the option: “Injected Provider - MetaMask”. Then select the MetaMask account where you have STT Tokens.

In the “DEPLOY” field, enter the property values for the ERC20 Token:

  • “_NAME” - type string

  • “_SYMBOL” - type string

  • “_DECIMALS” - type uint8

Click Deploy.

When prompted, approve the Contract deployment on your MetaMask.

Look at the terminal for the response and the deployed Smart Contract address. You can interact with the Smart Contract via the Remix IDE. Send a transaction to change the name.

Congratulations. 🎉 You have deployed an ERC20 Smart Contract to the Somnia Network. 🎉


OpenZeppelin

As was mentioned at the beginning of this Tutorial, there is the option to use a Library to create the ERC20 Token. The OpenZeppelin Smart Contract Library can be used to create an ERC20 Token, and developers can rely on the Smart Contracts wizard to specify particular properties for the created Token. See an example below:

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


import "@openzeppelin/contracts@4.0.0/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts@4.0.0/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts@4.0.0/access/Ownable.sol";


contract MyToken is ERC20, ERC20Burnable, Ownable {
    constructor(address initialOwner)
        ERC20("MyToken", "MTK")
        Ownable()
    {}


    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}

The process for deploying this Smart Contract implementation built with OpenZeppelin is the same as Steps 3 and 4 above.

Last updated