Home

How to Deploy an ERC-20 Token

Deploy an ERC-20 token from contract code to a verified mainnet address using NOWNodes RPC — OpenZeppelin, Hardhat config, real gas costs, and common mistakes.

Deploying an ERC-20 token means writing a Solidity smart contract that implements the standard functions and events, compiling it, and broadcasting a contract-creation transaction to an EVM-compatible network. Once that transaction confirms, the token exists at a fixed address and any wallet, exchange, or dApp that speaks the ERC-20 interface can hold and move it without custom integration work.

The mechanics are simple enough to finish in an afternoon. The part that actually needs care is everything around the deployment itself: decimals, ownership, gas, and verification.

This guide skips the "what is a token" preamble and goes straight into the technical path: the interface a contract must implement, how to connect a deployment script to the chain through NOWNodes, how Remix, Hardhat, and Foundry compare for shipping the contract, what deployment actually costs in gas, and the mistakes that show up most often in post-launch audits.

What Exactly Are You Deploying?

An ERC-20 token is not a separate kind of blockchain object. It is a regular smart contract that happens to implement a specific interface. EIP-20, authored by Fabian Vogelsteller and Vitalik Buterin and finalized on November 19, 2015, defines that interface as six required functions and two required events. Nothing else about the contract is standardized; everything beyond this interface, including minting rules, pausability, and access control, is up to you.

ERC-20 is a token standard requiring totalSupply(), balanceOf(address), transfer(address,uint256), transferFrom(address,address,uint256), approve(address,uint256), and allowance(address,address), plus Transfer and Approval events. See the official specification for exact signatures.

The motivation behind the standard is that a standard interface allows tokens on Ethereum to be reused by other applications, from wallets to decentralized exchanges. That is the main reason to use ERC-20 instead of a bespoke contract: a wallet that has never heard of your token can still display its balance and submit a transfer, because it already knows how to call the standard interface.

Three more functions, name(), symbol(), and decimals(), are optional in the spec but expected by nearly every wallet and explorer. Skip them and you get a technically valid token that shows up everywhere as an unlabeled contract address.

What You Need Before Deploying

Four things need to be in place before you write a line of contract code. A wallet such as MetaMask handles signing for both testing and the real deployment. Testnet ETH, free from a Sepolia faucet, covers gas on Ethereum's main public testnet, so nothing here costs real money until mainnet.

You will also need a compiler, a deployment environment, and a decision on where the token launches. ERC-20 is not Ethereum-exclusive: the same interface runs unmodified on any EVM-compatible chain, including BNB Smart Chain and Polygon, since they execute the identical opcode set Solidity compiles to.

The fourth piece is what actually broadcasts the signed deployment transaction to the network: an RPC connection.

Connecting to Ethereum Through NOWNodes

Neither Remix, Hardhat, nor Foundry talks to Ethereum directly. Each one hands a signed transaction to an RPC endpoint and waits for a response. Running that endpoint yourself means syncing and maintaining a full Ethereum client; NOWNodes gives you the endpoint without the client, on both mainnet and Sepolia, under the same account.

Getting a working connection takes three steps:

  1. Create a NOWNodes account.
  2. Open the Ethereum node in the dashboard.
  3. Click Add a New Key to generate an API key.

The Start plan covers 100,000 requests at no cost, which is enough to compile, deploy, and test a token several times before anything needs a paid plan.

NOWNodes exposes mainnet and Sepolia as separate endpoints, and the API key goes either in the URL path or in an api-key header, whichever a given tool handles more cleanly.

NetworkEndpointTypical use
Ethereum mainnethttps://eth.nownodes.io with key via api-key headerReal deployment, once tested
Sepolia testnethttps://eth-sepolia.nownodes.io with key via api-key headerDevelopment and dry runs

A quick cURL confirms the connection before any Solidity gets involved, calling eth_blockNumber and expecting the current block back in hex:

Copied!
curl -X POST https://eth-sepolia.nownodes.io \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

A result instead of an error means the endpoint is live. Teams deploying through an AI coding assistant rather than hand-written scripts can reach the same documented methods through NOWNodes MCP Server, which connects tools like Claude Code and Cursor to this API directly.

Choosing a Deployment Tool: Remix vs. Hardhat vs. Foundry

The three common paths trade setup effort for control and repeatability. Which one fits depends on whether this is a one-off token or the first of many deployments a team will script and re-run.

ToolSetupBest forTrade-off
Remix IDENone, runs in-browserA single token, fast iterationNo scripting, awkward for repeat deployments
HardhatLocal Node.js projectScripted, repeatable deployments with testsMore setup than Remix
FoundryLocal, Solidity-nativeFast test suites, CI pipelinesSteeper learning curve

Remix is the fastest way to deploy a single token: write the contract, compile it, connect MetaMask through Injected Provider, and deploy. No local environment is required, and no separate RPC setup is needed because MetaMask supplies the connection. The trade-off shows up the moment you need to deploy the same contract to three networks with different constructor arguments. That is where a scripted Hardhat or Foundry deployment, pointed at a NOWNodes endpoint instead of MetaMask's default, starts paying for its extra setup.

Writing the Contract

Writing an ERC-20 from scratch is rarely the right call. OpenZeppelin Contracts implements the full standard, audited and battle-tested across thousands of production deployments, and its Contracts Wizard generates a working starting point without writing the interface by hand.

A minimal but complete token looks like this, built on OpenZeppelin Contracts 5.x and Solidity 0.8.36:

Copied!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.36;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
        _mint(msg.sender, initialSupply * 10 ** decimals());
    }
}

Inheriting ERC20 gives you all standard functions and events for free. The constructor only needs to set the name, symbol, and initial supply. decimals() defaults to 18, matching ETH itself, and _mint handles the storage write and fires the required Transfer event from the zero address, which is the on-chain signal that new supply entered circulation.

Extending the Base Contract

Real deployments usually add at least one extension on top of the base. Ownable restricts admin functions to a single address, Burnable lets holders destroy their own tokens, and Pausable adds an emergency stop. Each extension is a separate import and a few words in the declaration, for example contract MyToken is ERC20, Ownable, Burnable, rather than custom access-control code.

How to Deploy an ERC-20 Token, Step by Step

The sequence below assumes Hardhat, since it scales past a single deployment, with NOWNodes doing the actual talking to Ethereum.

  1. Initialize a project with npx hardhat init and install OpenZeppelin Contracts as a dependency.
  2. Point Hardhat at NOWNodes by adding a Sepolia network to hardhat.config.js.
  3. Write the contract in contracts/MyToken.sol, importing the ERC20 base as shown above.
  4. Compile it with npx hardhat compile.
  5. Write a deployment script that instantiates the contract factory and calls .deploy() with your constructor arguments.
  6. Deploy to Sepolia first with npx hardhat run scripts/deploy.js --network sepolia.
  7. Confirm on-chain by checking the returned contract address on a block explorer.
  8. Deploy to mainnet only after the testnet version has been used, read, and reviewed.

Example Hardhat network config:

Copied!
require("dotenv").config();

module.exports = {
  solidity: "0.8.36",
  networks: {
    sepolia: {
      url: "https://eth-sepolia.nownodes.io",
      httpHeaders: { "api-key": process.env.NOWNODES_API_KEY },
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};

Swapping url to https://eth.nownodes.io in a second network entry is the main change needed to repeat the same script against mainnet.

A plain ethers.js script skips Hardhat's config layer entirely: point a JsonRpcProvider at the NOWNodes URL, attach a signing wallet, and deploy through a ContractFactory. That can be useful for a one-off deployment that does not need a full project scaffold.

How Much Does It Cost to Deploy an ERC-20 Token?

Deployment cost comes down to three gas components, and knowing them lets you estimate a real number instead of guessing. A standard transaction costs a flat 21,000 gas, contract creation adds another 32,000, and each byte of deployed bytecode costs 200 gas. On top of that, every storage slot your constructor initializes from zero, such as the token's balance mapping entry or an admin address, costs additional gas.

A minimal, empty contract with no storage writes runs about 66,862 gas total. An OpenZeppelin ERC-20 contract carries meaningfully more compiled bytecode, typically a few thousand bytes once you add extensions like Ownable, plus at least one storage write for the initial mint. A real token deployment lands well above that baseline, and the exact figure depends on which extensions you include.

Do not guess at the exact figure for your own contract. Ask the network directly. An eth_estimateGas call against your compiled bytecode, sent through the same NOWNodes endpoint used for deployment, returns the precise gas figure for your specific constructor arguments and extensions before you sign a transaction.

Gas componentCostSource
Base transaction21,000 gasFixed, every transaction
Contract creation32,000 gasFixed, every deployment
Deployed bytecode200 gas/byteScales with contract size
Storage write, zero to non-zero22,100 gasPer slot

Verifying Your Contract on a Block Explorer

An unverified contract shows up on Etherscan as raw bytecode: no source code, no readable function list, and nothing a user can audit before interacting with it. Verification uploads your Solidity source and compiler settings so the explorer can recompile it and confirm the bytecode matches. The explorer then displays the readable source alongside a Read Contract and Write Contract interface anyone can use directly.

Hardhat automates this with the hardhat-verify plugin. One command, using an Etherscan API key, submits the source for verification right after deployment. Before that, it is worth confirming the bytecode actually landed where you expect. An eth_getCode call against the deployed address through NOWNodes returns the on-chain bytecode directly, which is a faster sanity check than waiting on a block explorer's indexer to catch up.

Skipping verification itself does not break the token, but it is the first thing a cautious holder, exchange, or auditor checks. Its absence is a common reason a legitimate token gets mistaken for something riskier than it is.

Common Technical Pitfalls

A handful of mistakes account for most of the problems that show up after an ERC-20 goes live. None of them are exotic; they are mismatches between what the standard assumes and what a specific implementation actually does.

Getting decimals wrong

decimals() defaults to 18 in OpenZeppelin's implementation, matching ETH, but nothing forces that. A token with decimals() == 6 that a frontend treats as 18 displays balances off by a factor of a trillion. That issue can stay invisible in testing if the test script hardcodes the same wrong assumption.

Trusting approve() blindly

The standard approve and transferFrom pattern has a known race condition: changing an existing allowance from one non-zero value to another can, in specific transaction-ordering scenarios, let a spender use both the old and new allowance. The practical fix is safeIncreaseAllowance and safeDecreaseAllowance instead of resetting approve() directly.

Unrestricted minting

If _mint is not gated behind Ownable or a similar access-control layer, anyone can call it and inflate supply to zero value in one transaction. This is one of the most common reasons a token contract fails a basic review.

Treating immutability as optional

Once deployed, a contract's logic cannot be changed unless you built in an upgrade pattern before launch, and upgrade patterns carry their own attack surface. A contract that holds real value deserves an independent security audit before mainnet, not after something goes wrong.

Conclusion

Deploying an ERC-20 token is a small technical task wrapped in decisions that matter more than the deployment transaction itself: which extensions to inherit, who controls minting, whether decimals matches what your frontend expects, and whether the contract gets reviewed before it touches real funds.

Ship on Sepolia through NOWNodes first, verify the contract the moment it is live, and treat OpenZeppelin's audited base contracts as the default rather than a shortcut. Once the same endpoint has carried a token from a test deployment through gas estimation, bytecode checks, and a mainnet launch, swapping between networks is a one-line config change rather than a new integration.

FAQ

How long does it take to deploy an ERC-20 token?

Writing and testing a standard token using OpenZeppelin's base contracts takes an hour or two for someone comfortable with Solidity. The deployment transaction itself confirms in one block, typically around 12 seconds on Ethereum mainnet.

Can you change an ERC-20 contract after deployment?

Not by default. Deployed bytecode is immutable, so changing supply rules or fixing a bug means deploying a new contract and migrating holders, unless a proxy upgrade pattern was built in from the start. That pattern needs its own audit.

Do you need Solidity experience to deploy a token?

Basic familiarity is enough if you use OpenZeppelin's audited base contracts and the Wizard to generate a starting point. Writing custom logic beyond the standard, such as vesting schedules, taxes on transfer, or custom access control, needs real Solidity competence and, for anything holding value, a professional review.

What happens if you forget to verify the contract?

The token still functions normally on-chain; verification only affects whether the source code is readable on a block explorer. Most wallets and exchanges still display balances and process transfers for unverified contracts, but many treat an unverified contract as a caution flag before listing or integrating it.

Do you need to run your own Ethereum node to deploy a token?

No. A deployment script only needs an RPC endpoint to broadcast the signed transaction, not a full synced node. A provider such as NOWNodes handles that connection over mainnet and Sepolia, which is why the Hardhat and ethers.js examples above never touch node software directly.