ERC-20 Token Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
ERC-20 Token Development
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1215
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1043
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

ERC-20 Token Development

ERC-20 is an interface, not an implementation. Six mandatory functions (totalSupply, balanceOf, transfer, transferFrom, approve, allowance) and two events (Transfer, Approval). Everything else is implementation detail that matters.

What you actually need to solve before writing code

Before opening Foundry, answer several questions that determine architecture:

Is the token upgradeable or not? Upgradeable (Proxy + Implementation) provides flexibility but adds complexity and risk. For most simple utility tokens — immutable contract is the right choice. If token parameters can change through governance — you need a proxy.

Who and how can create tokens? Fixed supply (all supply at deploy), mintable (minter can create), mintable with cap (not more than X total supply). Mintable requires strict access control.

Do you need special mechanisms? Burn (token burning), pause (emergency stop), blacklist (USDC and Tether do this), transfer tax (deflationary tokens — usually a bad idea).

Basic implementation

OpenZeppelin is the standard for ERC-20. Don't write ERC-20 from scratch. Auditors, exchanges, and integrators know the OpenZeppelin codebase, its vulnerabilities have been found, fixed, and verified over years.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/access/Ownable2Step.sol";

contract MyToken is ERC20, ERC20Burnable, ERC20Permit, Ownable2Step {
    uint256 public constant MAX_SUPPLY = 100_000_000 * 10**18; // 100M tokens
    
    constructor(
        address initialOwner,
        address treasury
    ) 
        ERC20("My Token", "MTK")
        ERC20Permit("My Token")
        Ownable2Step()
    {
        _transferOwnership(initialOwner);
        _mint(treasury, MAX_SUPPLY);  // all supply at deploy
    }
}

ERC20Permit (EIP-2612) — important extension: allows approve without a separate transaction through off-chain signature. This improves UX (one transaction instead of two for first DeFi protocol interaction) and reduces cost for the user.

Ownable2Step instead of Ownable — ownership transfer requires confirmation from the new owner. Protection against accidental transfer of control to wrong address.

Mintable token with access control

If the token should have a mint function after deploy:

import "@openzeppelin/contracts/access/AccessControl.sol";

contract MintableToken is ERC20, ERC20Burnable, ERC20Permit, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
    uint256 public immutable maxSupply;
    
    constructor(
        string memory name,
        string memory symbol,
        uint256 _maxSupply,
        address admin
    ) ERC20(name, symbol) ERC20Permit(name) {
        maxSupply = _maxSupply;
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
    }
    
    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        require(totalSupply() + amount <= maxSupply, "Exceeds max supply");
        _mint(to, amount);
    }
}

MINTER_ROLE should be assigned to a smart contract (staking reward contract, vesting contract), not EOA. If minter is a person, this is centralized risk: the key leaks or owner can print tokens unlimited.

Decimals: standard and exceptions

By default ERC-20 decimals = 18 (like ETH). This is correct for most tokens. Exception: USDC/USDT use 6 decimals because dollar amounts don't require 18 places. If creating a stablecoin or wrapping known asset — check original decimals.

Never use 0 decimals if token will be traded on DEX — AMM works poorly with integer quantities.

Common mistakes

Transfer tax / fee-on-transfer: each transfer takes X% and sends it somewhere. This breaks most DeFi protocols — AMM pools, lending protocols don't expect to receive less after transferFrom. Such token will be incompatible with Uniswap V2/V3, Aave, Compound. If still necessary — use whitelist for DeFi contracts.

Centralised blacklist without timelock: USDC has blacklist, and that's normal for regulated stablecoin. For community token — no. If blacklist is needed, limit it through multisig + timelock.

Reentrancy in transfer hooks: ERC-20 without hooks has no reentrancy risk in transfer. If adding _beforeTokenTransfer or _afterTokenTransfer — ensure callbacks don't call external code.

Verification and deployment

# Tests
forge test -vvv

# Deployment with verification
forge script script/Deploy.s.sol \
  --rpc-url $RPC_URL \
  --private-key $PRIVATE_KEY \
  --broadcast \
  --verify \
  --etherscan-api-key $ETHERSCAN_KEY

After deployment — verify contract on Etherscan/Polygonscan. Users and exchanges check code before interaction. Unverified token raises legitimate suspicion.

Timeline for ERC-20 token development: 3–5 days including tests and mainnet deploy. If audit needed — additional 2–4 weeks.