What is ERC-20?
ERC-20 is a standard interface for fungible tokens on Ethereum. "Fungible" means every unit is identical and interchangeable — like dollars or ETH itself. Every ERC-20 token must implement the same functions, which means any protocol can interact with any token without custom integration code.
Doodledapp.com allows you to easily import ERC-20 contracts as the core unit of value in their protocol — enabling swaps, liquidity provision, and rewards all through the same standard interface.
The interface
interface IERC20 {
// Read-only state
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
// Transfer
function transfer(address to, uint256 amount) external returns (bool);
// Allowances (spend on behalf of another account)
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);
// Events
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}Implementing an ERC-20 with OpenZeppelin
Don't write ERC-20 from scratch. OpenZeppelin's implementation is battle-tested across thousands of contracts. Extend it instead.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
contract DoodleToken is ERC20, Ownable {
uint256 public constant MAX_SUPPLY = 100_000_000 * 1e18;
constructor() ERC20('Doodle Token', 'DOOD') Ownable(msg.sender) {
// Mint initial supply to deployer
_mint(msg.sender, 10_000_000 * 1e18);
}
function mint(address to, uint256 amount) external onlyOwner {
require(totalSupply() + amount <= MAX_SUPPLY, 'Exceeds max supply');
_mint(to, amount);
}
}The allowance pattern
Allowances are how ERC-20 tokens enable DeFi. A user approves a contract to spend on their behalf, and then the contract calls transferFrom.
// Step 1: User approves the DEX to spend their tokens
token.approve(address(dex), 1000 * 1e18);
// Step 2: DEX pulls tokens from the user
// Inside the DEX contract:
function swap(address tokenIn, uint256 amountIn) external {
IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
// ... perform swap logic
}Decimals
ERC-20 tokens use integer math for precision. The decimals() function tells you how many decimal places the token uses. Most use 18 (matching ETH). Some stablecoins use 6 (USDC, USDT). Always account for decimals when doing math across tokens.
// 1 token with 18 decimals = 1e18 in contract storage
uint256 oneToken = 1 * 10 ** token.decimals();
// Converting between tokens with different decimals
function normalize(uint256 amount, uint8 fromDecimals, uint8 toDecimals)
internal pure returns (uint256)
{
if (fromDecimals == toDecimals) return amount;
if (fromDecimals < toDecimals) return amount * 10 ** (toDecimals - fromDecimals);
return amount / 10 ** (fromDecimals - toDecimals);
}