Understanding ERC20 tokens And Making One

Understanding ERC20 tokens And Making One

WHAT IS ERC20

ERC20, a token standard proposed by Fabian Vogelsteller in 2015, These tokens are fungible tokens and have properties that make each token exactly the same(1 token always equals 1token). A token can be virtually anything in Ethereum, Lottery tickets, financial assets like shares, A good example of an ERC20 token would be a flat/stable coin like USDT.

FUNCTIONALITIES OF THE ERC20 TOKEN

Here are the main functionalities an ERC20 token has.

  • Transfer token from one account to another

  • Get the total supply of tokens stored on the network/contract

  • Approve spending of a token by a third party account

  • Get the current token balance of an account

THE ERC20 INTERFACE

An interface defines a set of events and methods a contract must have, ensuring it is composable with decentralized exchanges.

These are the set of methods and events every ERC20 contract must have.

METHODS

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)

EVENTS

event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)

NOW LET's CREATE AN ERC20 TOKEN

We'll explain all the functions and events while we create this token. Our contract would inherit from IERC20(interface ERC20), ensuring it won't compile without having all the methods and events. We'll start by creating two files, IERC20.sol(Interface ERC20) and ERC20.

// SPDX-License-Identifier: MIT
// IERC20.sol
pragma solidity ^0.8.0;
interface IERC20{
function name() external view returns (string memory);
function symbol() external view returns (string mem
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

WHAT EACH FUNCTION AND EVENT DOES

  • name : This returns the name of the token collection

  • symbol: This returns the symbol of the token collection

  • decimals: This returns the amount of decimals that represents one token, example, let's say we made a token collection called iyostack (name) and we set the decimals to 10, it means the token must be divided by 10¹⁰ or 10000000000 to get its user representation. Most ERC20 tokens use 18.

  • totalSupply : This returns the total supply of tokens minted on the contract

  • balanceOf : AS the name implies, it returns the amount of tokens owned by a particular address.

  • transfer : This is used to transfer a token from one address to another, it returns true if successful and false if not.

  • approve : This function allows an address to approve another address to spends some of its balance. Let's represent this is a JSON file called approvals :

[
"demo_address":{
"approved_address": amount_allowed_to_spend
},
"demo_address2":{
"approved_address": amount_allowed_to_spend,
"approved_address_1": amount_allowed_to_spend,
},
]

This above provides a better description of how approvals work.

  • transferFrom : This allows transferring of tokens from an address( only if approved) to another address.

  • allowance : This returns the total allowance approved by an address to be spent by another address.

CREATING OUR OWN ERC20 TOKEN

Now let's create an ERC20 token based on the interface above

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import './IERC20.sol';
contract ERC20 is IERC20{
string tokenName;
string tokenSymbol;
uint8 tokenDecimal;
uint256 totalTokensOnNetwork;
// Mapping of owner to spender , and spender to amount approved to spend
mapping(address => mapping(address => uint256)) approvedByOwner;
// Mapping of token owner to balance
mapping(address => uint256) tokenOwnerToBalance;
// Mapping of owner to spender
mapping(address => address) allowedToSpend;
constructor(string memory _tokenName , string memory _tokenSymbol , uint8 _tokenDecimal){
tokenName = _tokenName;
tokenSymbol = _tokenSymbol;
tokenDecimal = _tokenDecimal;
}
// Allows creation of new tokens
function mint(uint256 _value) public {
totalTokensOnNetwork+=_value;
tokenOwnerToBalance[msg.sender] +=_value;
}
function name() public override view returns (string memory){
return tokenName;
}
function symbol() public override view returns (string memory){
return tokenSymbol;
}
function decimals() public override view returns (uint8){
return tokenDecimal;
}
function totalSupply() public override view returns (uint256){
return totalTokensOnNetwork;
}
function balanceOf(address _owner) public override view returns (uint256 balance){
if(_owner == address(0)){
revert("Inavid address");
}
return tokenOwnerToBalance[_owner];
}
function transfer(address _to, uint256 _value) public override returns (bool success){
if(balanceOf(msg.sender)< _value){
return false;
}
tokenOwnerToBalance[msg.sender]-=_value;
tokenOwnerToBalance[_to] += _value;
emit Transfer(msg.sender , _to , _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public override returns (bool success){
if(approvedByOwner[_from][msg.sender] < _value ){
return false;
}
if(balanceOf(_from) < _value){
return false;
}
tokenOwnerToBalance[_from] -=_value;
tokenOwnerToBalance[_to] +=_value;
approvedByOwner[_from][msg.sender] -=_value;
emit Transfer(msg.sender , _to , _value);
return true;
}
function approve(address _spender, uint256 _value) public override returns (bool success){
if(msg.sender == _spender){
return false;
}
approvedByOwner[msg.sender][_spender] = _value;
emit Approval(msg.sender , _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public override view returns (uint256 remaining){
return approvedByOwner[_owner][_spender];
}
}

And there You have it an ERC20 token, notice we added a mint function, this allows us to create new tokens, for this example anyone is allowed to create a token, it could also be customized to allowing minting in exchange for ETH or allowing only the owner of the contract to mint tokens.