In this post, we'll explore how ERC20 tokens on Ethereum work and how to set them up.

ERC20 Tokens

ERC20 tokens are contracts on Ethereum adhering to a simple interface:

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);
}

The interface defines six core functions:

  • totalSupply(): Returns the total number of tokens in circulation
  • balanceOf(): Returns the token balance for a given address
  • transfer(): Moves tokens from the caller to a recipient
  • approve(): Grants permission for another address to spend tokens on your behalf
  • allowance(): Checks how many tokens a spender is authorized to use
  • transferFrom(): Enables delegated transfers (used by DEXs and other protocols)

To create a new token, we provide the name, symbol, and decimals in the constructor when deploying the contract:

constructor(
    string memory _name,
    string memory _symbol,
    uint8 _decimals
) {}

The constructor only sets the token's metadata. To actually create tokens, you must call the mint() function after deployment to create tokens at a specific address.

ERC20 Faucet

Deploying tokens is useful, but sometimes you want a "faucet" for that token as well. A faucet is a contract that distributes tokens to users on demand, which is particularly useful on testnets where users need tokens to interact with dApps.

The ERC20Faucet contract extends the standard ERC20 contract with an additional method that allows any caller to request tokens:

function money_pweese() public {
    uint256 amount = 5 * 10 ** decimals;
    require(_balances[address(this)] >= amount, "Insufficient faucet balance");
    _transfer(address(this), msg.sender, amount);
}

This function transfers 5 tokens (5 × 10^decimals) from the contract's own balance to the caller. Before users can claim tokens, the deployer must first mint tokens to the contract address using mint(address(this), amount).


Talking is a hydrant in the yard and writing is a faucet upstairs in the house. Opening the first takes the pressure off the second. - Robert Frost