ERC-20 is a standard way to create tokens on the Ethereum blockchain. It helps different tokens work well together.
ERC-20 implementation in Blockchain / Solidity
pragma solidity ^0.8.0; interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, 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 from, address to, 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 MyToken is IERC20 { string public name = "MyToken"; string public symbol = "MTK"; uint8 public decimals = 18; uint256 private _totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; constructor(uint256 initialSupply) { _totalSupply = initialSupply * 10 ** decimals; _balances[msg.sender] = _totalSupply; emit Transfer(address(0), msg.sender, _totalSupply); } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } function transfer(address to, uint256 amount) public override returns (bool) { require(to != address(0), "Transfer to zero address"); require(_balances[msg.sender] >= amount, "Not enough balance"); _balances[msg.sender] -= amount; _balances[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function allowance(address owner, address spender) public view override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public override returns (bool) { require(spender != address(0), "Approve to zero address"); _allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) public override returns (bool) { require(to != address(0), "Transfer to zero address"); require(_balances[from] >= amount, "Not enough balance"); require(_allowances[from][msg.sender] >= amount, "Allowance exceeded"); _balances[from] -= amount; _balances[to] += amount; _allowances[from][msg.sender] -= amount; emit Transfer(from, to, amount); return true; } }
This code uses Solidity version 0.8.0 or higher for safety features.
The contract implements the IERC20 interface to follow the ERC-20 standard.
contract SimpleToken is IERC20 {
// minimal implementation
}function balanceOf(address account) public view override returns (uint256) {
return _balances[account];
}function transfer(address to, uint256 amount) public override returns (bool) { require(_balances[msg.sender] >= amount, "Not enough balance"); _balances[msg.sender] -= amount; _balances[to] += amount; emit Transfer(msg.sender, to, amount); return true; }
This complete contract creates a token named MyToken with symbol MTK. The creator gets all tokens at the start. It supports checking balances, transferring tokens, approving others to spend tokens, and transferring tokens on behalf of others.
pragma solidity ^0.8.0; interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address to, 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 from, address to, 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 MyToken is IERC20 { string public name = "MyToken"; string public symbol = "MTK"; uint8 public decimals = 18; uint256 private _totalSupply; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; constructor(uint256 initialSupply) { _totalSupply = initialSupply * 10 ** decimals; _balances[msg.sender] = _totalSupply; emit Transfer(address(0), msg.sender, _totalSupply); } function totalSupply() public view override returns (uint256) { return _totalSupply; } function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } function transfer(address to, uint256 amount) public override returns (bool) { require(to != address(0), "Transfer to zero address"); require(_balances[msg.sender] >= amount, "Not enough balance"); _balances[msg.sender] -= amount; _balances[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function allowance(address owner, address spender) public view override returns (uint256) { return _allowances[owner][spender]; } function approve(address spender, uint256 amount) public override returns (bool) { require(spender != address(0), "Approve to zero address"); _allowances[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) public override returns (bool) { require(to != address(0), "Transfer to zero address"); require(_balances[from] >= amount, "Not enough balance"); require(_allowances[from][msg.sender] >= amount, "Allowance exceeded"); _balances[from] -= amount; _balances[to] += amount; _allowances[from][msg.sender] -= amount; emit Transfer(from, to, amount); return true; } }
Always check for zero address to avoid sending tokens to nowhere.
Events like Transfer and Approval help wallets and apps track token movements.
Decimals define how divisible your token is; 18 is common like Ether.
ERC-20 is a standard for tokens on Ethereum.
It defines functions to check balances, transfer tokens, and approve spending.
Following ERC-20 makes your token work with many wallets and exchanges.
Practice
balanceOf function in an ERC-20 token contract?Solution
Step 1: Understand the role of
ThebalanceOfbalanceOffunction returns how many tokens a given address owns.Step 2: Compare with other functions
Functions liketransfermove tokens,approveallows spending, and minting creates tokens. These are different from checking balance.Final Answer:
To check the token balance of a specific address -> Option CQuick Check:
balanceOf= check balance [OK]
- Confusing balanceOf with transfer function
- Thinking approve checks balances
- Mixing minting with balance checking
Solution
Step 1: Recall ERC-20 transfer signature
The standard transfer function takes the recipient address first, then the amount, and returns a boolean.Step 2: Check each option
function transfer(address to, uint256 amount) public returns (bool); matches the standard:transfer(address to, uint256 amount) public returns (bool). Others have wrong parameter order, missing return, or extra parameters.Final Answer:
function transfer(address to, uint256 amount) public returns (bool); -> Option AQuick Check:
transfer(to, amount) returns bool [OK]
- Swapping parameter order
- Omitting return type
- Adding extra parameters not in standard
mapping(address => uint256) balances;
function transfer(address to, uint256 amount) public returns (bool) {
if (balances[msg.sender] >= amount) {
balances[msg.sender] -= amount;
balances[to] += amount;
return true;
}
return false;
}What will
transfer return if msg.sender has 50 tokens and tries to send 60 tokens?Solution
Step 1: Check balance condition
The function checks ifbalances[msg.sender]is at leastamount. Here, 50 < 60, so condition fails.Step 2: Determine function return
Since condition fails, the function skips token transfer and returnsfalse.Final Answer:
false -> Option DQuick Check:
Insufficient balance returns false [OK]
- Assuming transfer throws error on insufficient balance
- Thinking it returns true anyway
- Confusing return value with balance amount
approve function snippet:mapping(address => mapping(address => uint256)) allowances;
function approve(address spender, uint256 amount) public returns (bool) {
allowances[msg.sender][spender] = amount;
return true;
}Solution
Step 1: Review ERC-20 approve requirements
The standard requires emitting anApprovalevent after changing allowance for transparency.Step 2: Check function code
The function sets allowance but does not emit theApprovalevent, missing a key part of the standard.Final Answer:
Should emit an Approval event after setting allowance -> Option BQuick Check:
approve must emit Approval event [OK]
- Ignoring event emission
- Checking balance in approve (not required)
- Decreasing allowance instead of setting
Solution
Step 1: Understand pause feature integration
To pause transfers, the contract needs a state variable likepausedand checks in transfer functions.Step 2: Check ERC-20 compliance
Removing or renamingtransferbreaks the standard. ChangingbalanceOfoutput breaks expected behavior. Adding a separate function for pause is not standard.Final Answer:
Add a paused boolean and require it false in transfer and transferFrom functions -> Option AQuick Check:
Pause by require(!paused) in transfer functions [OK]
- Removing or renaming standard functions
- Changing balanceOf behavior
- Adding extra steps users must call
Ready to prove it?
Pass the test and this pattern moves unknown
