Understanding ERC-20 Code
Balances are contract state
A token balance is recorded by the token contract. A wallet reads that state and displays the amount associated with an address.
An ERC-20 token is a standardized smart contract. Because every ERC-20 contract has the exact same function names (like transfer and balanceOf), wallets like MetaMask know exactly how to interact with all of them.
The following simplified example shows balance storage and transfers. It is not a complete production token implementation.
The Core Data Structure: Mappings
To keep track of balances, Solidity uses a mapping. A mapping is like a dictionary or a hash table.
contract SimpleToken {
// This creates a dictionary linking an address to a number
mapping(address => uint256) public balanceOf;
// Total supply of the token
uint256 public totalSupply;
// Token metadata
string public name = "HashtagToken";
string public symbol = "HTG";
uint8 public decimals = 18;
}
In the balanceOf mapping:
- The Key is an
address(e.g.,0xabc...) - The Value is a
uint256(an unsigned integer, meaning no negative numbers).
If you call balanceOf[0xabc...], it returns the token balance of that address.
The Transfer Function
How do tokens move? We just subtract from one address in the mapping and add to another.
// An event we will trigger when tokens move
event Transfer(address indexed from, address indexed to, uint256 value);
function transfer(address _to, uint256 _amount) public returns (bool) {
// 1. Check: Does the sender have enough tokens?
require(balanceOf[msg.sender] >= _amount, "Not enough tokens");
// 2. Execute: Subtract from sender
balanceOf[msg.sender] = balanceOf[msg.sender] - _amount;
// 3. Execute: Add to receiver
balanceOf[_to] = balanceOf[_to] + _amount;
// 4. Log: Emit an event for the outside world
emit Transfer(msg.sender, _to, _amount);
return true;
}
Breaking down the transfer
require: The balance check rejects a transfer larger than the sender's balance. A revert rolls back state changes, but gas already consumed by execution is still charged.- Math: We directly modify the
balanceOfmapping. The sender loses tokens, the receiver gains them. emit Transfer(...): Blockchains are closed systems. To let frontends (like Etherscan or your React app) know that a transfer happened without them having to constantly read the contract state, we emit an event. Apps listen for these events to update UI histories.
The Decimals Problem
Solidity does not support floating point numbers (decimals). You cannot have 1.5 tokens in the code.
To fix this, tokens use very large integers. When you define decimals = 18 in the contract, it tells the frontend (like MetaMask) to move the decimal point 18 places to the left when showing the number to the user.
If you want to transfer exactly 1 token, you must pass the number 1000000000000000000 (1 followed by 18 zeros) to the transfer function. The smart contract handles the massive integer, but MetaMask formats it nicely as "1.00" for the user.
Key takeaways
- Tokens do not live in wallets; they live in a
mappinginside a smart contract. - Transferring tokens just means doing math: subtracting from the sender's mapping balance and adding to the receiver's.
requirestatements protect against invalid actions (like spending more than you have).- Events are emitted so off-chain apps know when things happen.
- Solidity has no decimals. It uses massive integers, and the frontend handles the visual formatting.
Quiz: Understanding ERC-20 Code
1 / 5What data structure does an ERC-20 contract use to keep track of everyone's token balances?