The Ethereum blockchain has revolutionized digital asset creation through its smart contract functionality, and at the heart of this innovation lies the ERC-20 token standard. As one of the most widely adopted protocols in the decentralized ecosystem, ERC-20 provides a consistent framework for creating and managing fungible tokens on the Ethereum network. Whether you're building a decentralized application (DApp), launching a community-driven project, or exploring blockchain development, understanding ERC-20 is essential.
This guide breaks down the core components of the ERC-20 standard, explains key functions and events, and walks through a practical implementation with enhanced features like minting, freezing, and token trading.
What Is the ERC-20 Token Standard?
ERC-20, short for Ethereum Request for Comment 20, is a technical specification that defines a set of rules for Ethereum-based tokens. Introduced in 2015, it enables developers to create interoperable tokens that can be used across wallets, exchanges, and DApps without compatibility issues.
👉 Discover how token standards power modern blockchain applications.
By adhering to ERC-20, a token ensures predictable behavior—such as transferring value and checking balances—which allows services like MetaMask, Uniswap, and OKX to seamlessly integrate new tokens.
Core Keywords
- ERC-20
- Ethereum token standard
- Smart contract
- Fungible tokens
- Blockchain development
- Decentralized applications (DApps)
- Token deployment
- Solidity programming
These terms reflect the primary search intent behind users exploring this topic: learning how to build, deploy, and manage tokens using Ethereum’s most established protocol.
Essential Functions in ERC-20
Every ERC-20 compliant contract must implement a specific set of functions. Below are the mandatory and optional methods defined by the standard.
name()
Returns the full name of the token (e.g., "LeBron James Token").
symbol()
Returns the ticker symbol (e.g., "LBJ"), typically used in exchanges and wallet interfaces.
decimals()
Specifies how divisible the token is. Most tokens use 18 decimals, meaning 1 full unit equals 10¹⁸ smallest units—mirroring Ether's precision.
totalSupply()
Indicates the total number of tokens in circulation. This can be fixed or dynamic depending on whether minting is allowed.
balanceOf(address _owner)
Retrieves the token balance of a specific Ethereum address.
transfer(address _to, uint256 _value)
Enables a user to send tokens directly to another address. A successful transfer must emit a Transfer event.
Note: Even zero-value transfers must trigger the Transfer event to maintain consistency in blockchain tracking.transferFrom(address _from, address _to, uint256 _value)
Allows a third party (like a smart contract) to transfer tokens on behalf of a user—but only up to an approved limit set via approve().
This is crucial for automated workflows such as staking, payments, or decentralized exchange swaps.
approve(address _spender, uint256 _value)
Grants permission to another address to spend a specified amount of tokens from the caller’s account.
allowance(address _owner, address _spender)
Queries how many tokens a spender is still allowed to withdraw from an owner’s balance.
Required Events
Events provide transparency by logging actions on the blockchain. Two events are required under ERC-20:
Transfer(address indexed _from, address indexed _to, uint256 _value)
Triggered whenever tokens change hands—including when new tokens are minted (with _from set to 0x0) or burned.
Approval(address indexed _owner, address indexed _spender, uint256 _value)
Emitted after a successful call to approve(), indicating delegation of spending rights.
Practical ERC-20 Token Implementation
Below is a simplified yet functional Solidity contract based on the original example. This version includes comments for clarity and follows best practices.
pragma solidity ^0.4.20;
contract TokenLBJ {
string public name = "LeBron James";
string public symbol = "LBJ";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Burn(address indexed from, uint256 value);
function TokenLBJ(uint256 initialSupply) public {
totalSupply = initialSupply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
}
function _transfer(address _from, address _to, uint _value) internal {
require(_to != address(0));
require(balanceOf[_from] >= _value);
require(balanceOf[_to] + _value > balanceOf[_to]);
uint previousBalances = balanceOf[_from] + balanceOf[_to];
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
Transfer(_from, _to, _value);
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
function transfer(address _to, uint256 _value) public returns (bool success) {
_transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]);
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
}Enhancing Tokens: Ownership, Minting & Freezing
While basic ERC-20 tokens work well for simple use cases, real-world applications often require advanced features.
Adding Ownership Control
Using an owned contract pattern introduces access control:
contract owned {
address public owner;
function owned() public { owner = msg.sender; }
modifier onlyOwner { require(msg.sender == owner); _; }
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}Inherit this in your token contract to restrict sensitive operations like minting or price updates.
Token Minting (Supply Expansion)
Allow the owner to create new tokens:
function mintToken(address target, uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
Transfer(0, owner, mintedAmount);
Transfer(owner, target, mintedAmount);
}👉 Learn how token economics influence long-term project success.
Useful for rewarding users or funding development—but beware of inflation risks.
Account Freezing
For compliance or security reasons:
mapping(address => bool) public frozenAccount;
event FrozenFunds(address target, bool frozen);
function freezeAccount(address target, bool freeze) onlyOwner public {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}Then modify _transfer() to block frozen accounts:
require(!frozenAccount[_from]);Implementing Token Buy/Sell Mechanics
You can embed exchange logic directly into your contract:
uint256 public sellPrice = 1000000000000000; // 0.001 ETH per token
uint256 public buyPrice = 2000000000000000; // 0.002 ETH per token
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
function buy() payable public returns (uint amount) {
amount = msg.value / buyPrice;
require(balanceOf[this] >= amount);
balanceOf[msg.sender] += amount;
balanceOf[this] -= amount;
Transfer(this, msg.sender, amount);
return amount;
}
function sell(uint amount) public returns (uint revenue) {
require(balanceOf[msg.sender] >= amount);
balanceOf[this] += amount;
balanceOf[msg.sender] -= amount;
revenue = amount * sellPrice;
msg.sender.transfer(revenue);
Transfer(msg.sender, this, amount);
return revenue;
}Important: Ensure sufficient ETH reserves when launching so users can actually sell back their tokens.
Frequently Asked Questions (FAQ)
Q: Can I change the total supply after deployment?
A: Only if your contract includes a minting function. Otherwise, supply remains fixed unless you upgrade the contract using proxies.
Q: Are all ERC-20 tokens the same?
A: While they follow the same interface, behavior varies based on implementation—some allow burning, freezing, or dynamic pricing.
Q: How do wallets detect my token?
A: Wallets read name, symbol, and decimals automatically once you provide the contract address.
Q: Is ERC-20 secure by default?
A: No—security depends on proper coding practices. Always audit your contract before deployment.
Q: Can I upgrade my ERC-20 token?
A: Native ERC-20 contracts are immutable. For upgrades, consider using proxy patterns or migrating to newer standards like ERC-4626.
Q: What happens if I send tokens to a contract that doesn’t support them?
A: They may become stuck unless the receiving contract implements token recovery methods.
With foundational knowledge of ERC-20 and practical extensions like ownership control and trading logic, you're equipped to launch meaningful digital assets on Ethereum. Always test thoroughly on testnets before going live.