Understand the ERC-20 Token Smart Contract

·

The ERC-20 token standard is one of the most foundational elements in the world of Ethereum-based decentralized applications. As a widely adopted technical specification, it defines a uniform set of rules for fungible tokens on the Ethereum blockchain. Whether you're a developer creating your first token or an enthusiast exploring how digital assets work, understanding ERC-20 smart contracts is essential.

This guide breaks down the core components of ERC-20, explains each function and event, and walks through a basic implementation using Solidity — the primary programming language for Ethereum smart contracts.


What Is ERC-20?

ERC-20 stands for Ethereum Request for Comment 20, a technical standard used for implementing fungible tokens on the Ethereum network. These tokens can represent assets like currency, points, or shares, and they are interchangeable — just like dollars or bitcoins.

By adhering to the ERC-20 standard, developers ensure that their tokens are compatible with wallets, exchanges, decentralized finance (DeFi) protocols, and other smart contracts across the ecosystem.

👉 Learn how blockchain platforms support token creation and management.


Core Functions of ERC-20

An ERC-20 contract must implement six mandatory functions and two events. These are typically defined in an interface before being implemented in a contract.

Here’s the standard interface:

pragma solidity ^0.8.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);

    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

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

Let’s explore what each part does.


Getter Functions

Getter functions retrieve data from the blockchain without modifying any state.

totalSupply()

function totalSupply() external view returns (uint256);

Returns the total number of tokens in circulation. Since Solidity doesn’t support floating-point numbers, decimals are handled by multiplying values (e.g., 1 token = 10¹⁸ units). Most tokens use 18 decimals, but this varies — always verify the decimal count when interacting with a token.

balanceOf(address account)

function balanceOf(address account) external view returns (uint256);

Returns the token balance of a specific Ethereum address. This allows users and applications to check how many tokens an account holds.

allowance(address owner, address spender)

function allowance(address owner, address spender) external view returns (uint256);

Checks how many tokens a third party (spender) is allowed to transfer from an owner’s account. This supports delegated spending, crucial for DeFi interactions like staking or trading.


State-Changing Functions

These functions modify the contract’s state and require gas to execute.

transfer(address recipient, uint256 amount)

function transfer(address recipient, uint256 amount) external returns (bool);

Sends a specified amount of tokens from the caller’s address to another. Emits a Transfer event and returns true on success.

approve(address spender, uint256 amount)

function approve(address spender, uint256 amount) external returns (bool);

Grants permission to a designated address (spender) to withdraw tokens up to a certain limit from the caller’s account. This is essential for secure interaction with exchanges or lending platforms.

transferFrom(address sender, address recipient, uint256 amount)

function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

Transfers tokens from one account to another using the allowance mechanism. Commonly used in decentralized exchanges where users trade tokens via smart contracts.


Events in ERC-20

Events provide a way for off-chain applications to monitor changes on the blockchain.

event Transfer(...)

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

Emitted when tokens are transferred. In cases of minting (token creation), from is the zero address (0x0). For burning (destruction), to is the zero address.

event Approval(...)

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

Triggered when an allowance is set via approve(). Helps track authorization actions across wallets and dApps.


A Basic ERC-20 Implementation

Below is a minimal yet functional Solidity contract implementing the ERC-20 standard:

pragma solidity ^0.8.0;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address owner, address spender) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract ERC20Basic is IERC20 {
    string public constant name = "ERC20Basic";
    string public constant symbol = "ERC";
    uint8 public constant decimals = 18;

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

    uint256 private totalSupply_ = 10 ether;

    constructor() {
        balances[msg.sender] = totalSupply_;
    }

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

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

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

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

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

    function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
        require(numTokens <= balances[owner], "Insufficient owner balance");
        require(numTokens <= allowed[owner][msg.sender], "Allowance exceeded");
        balances[owner] -= numTokens;
        allowed[owner][msg.sender] -= numTokens;
        balances[buyer] += numTokens;
        emit Transfer(owner, buyer, numTokens);
        return true;
    }
}

This implementation includes safety checks using require() statements to prevent underflows and unauthorized transfers — a best practice in secure smart contract development.

👉 Discover how developers test and deploy token contracts securely.

For production use, consider leveraging well-audited libraries like OpenZeppelin’s ERC-20 implementation, which includes additional features such as pausability, minting, and burning.


Frequently Asked Questions

What does "fungible" mean in the context of ERC-20?

Fungibility means each token is identical and interchangeable with another. Just like one dollar bill equals another, one ERC-20 token unit equals any other unit of the same type.

Can I change the total supply after deployment?

In the basic example above, supply is fixed. However, advanced implementations may include minting and burning functions to increase or reduce supply dynamically.

Why do some tokens have different decimal values?

While 18 is standard (matching Ether's precision), projects may choose fewer decimals based on use case — for example, stablecoins with 6 decimals (like USDC). Always confirm decimals before integrating or trading tokens.

How do wallets detect ERC-20 tokens?

Wallets scan user addresses for known token contracts and use the balanceOf() function to display holdings automatically.

Is every token on Ethereum ERC-20?

No. While ERC-20 dominates fungible tokens, others like ERC-721 (for NFTs) and ERC-1155 (multi-token standard) serve different purposes.

What security risks should I watch for?

Common issues include reentrancy attacks, integer overflow/underflow (mitigated in Solidity 0.8+), and improper access control. Always audit code before deployment.


Understanding the ERC-20 smart contract structure empowers you to build, interact with, or evaluate blockchain-based tokens confidently. With its standardized interface and broad adoption, ERC-20 remains a cornerstone of Ethereum's growing ecosystem.