Blockchain and web3 have changed the way we think about our digital assets and the way we transfer value. One revolutionary primitive is the digital token, a programmable asset that can represent anything from physical assets to voting rights. Tokens enable complex interactions and a rich roster of functions through decentralized applications (dapps) built on networks like Ethereum and Linea.
However, during the early Ethereum days there were no standard ways to implement tokens. Each and every project had its own unique implementation, which made it difficult for exchanges, wallets and dapps to support all types of tokens; this lack of compatibility also created difficulties in terms of growth and adoption.
To solve this issue, the Ethereum community proposed the EIP20 (Ethereum Improvement Proposal) standard in 2015. ERC20 (Ethereum Requests for Comment 20) defined a common interface and some set of rules for fungible tokens, which is mostly tokens where each unit is exactly equivalent to any other unit. By using this standard, token projects could ensure compatibility with the broader Ethereum ecosystem with very few adjustments.
Today ERC20 has become the standard for the fungible tokens not just on Ethereum but also on compatible blockchains like Linea zkEVM as well. Most of the tokens that you interact with in the web3 ecosystem – from stablecoins to governance tokens or utility tokens – are most likely ERC20 under the hood.
In the rest of this post, we’ll dive deeper into the specification of ERC20 and walk through the implementation using OpenZeppelin library and deploying your tokens on Linea. By the end, you will be deploying your own token on Linea while having a solid understanding of tokens.
For more, also check out our earlier Getting Started Guide on Deploying a Smart Contract.
Linea, a zkEVM (zero-knowledge Ethereum Virtual Machine) launched by Consensys in 2023, offers an exciting solution regarding scalability challenges faced by Ethereum. As a type 2 zkEVM, Linea maintains compatibility with existing Ethereum infrastructure while improving transaction speed and reducing costs. This compatibility helps developers to easily migrate dapps and tokens to Linea without making any changes to their codebases.
By leveraging Linea's advanced cryptographic techniques, such as SNARKs (Succinct Non-Interactive Argument of Knowledge), transactions are processed off-chain and verified on the Ethereum network using compact proofs. This approach not only reduces gas fees and confirmation times but also ensures the security and decentralization principles that are central to Ethereum.
At the core of the ERC20 standard defines six functions and two events. By implementing these functions and events, a token contract can be considered ERC20 and can interact seamlessly with other contracts and applications that support the ERC20 standard.
Functions are:
totalSupply() , balanceOf(address account) , transfer(address recipient, uint256 amount), allowance(address owner, address spender) , approve(address spender, uint256 amount) and transferFrom(address sender, address recipient, uint256 amount)
And the two events are:
Transfer(address indexed from, address indexed to, uint256 value) and Approval(address indexed owner, address indexed spender, uint256 value) .
While this basic implementation is functional, it's generally recommended to use battle-tested, audited implementations like those provided by the OpenZeppelin library.
Let’s create a simple Token called RadToken
using Foundry.
To get started, let's install Foundry toolchain installer.
curl -L https://foundry.paradigm.xyz | bash
Now we will install foundry by running the following command below. 👇
foundryup
We finally installed Foundry. 🎉
Now we can use the CLI tools from Foundry. To verify if Foundry was truly installed we can run forge --help .
We need to initialize a new project.
forge init linea-erc20
It will create few files and folders.
Let's take a look at the folder structure to understand what each of them are for:
src: default directory where we will mostly write and create the smart contracts
lib: contains dependencies which are mostly helpful contracts to use with foundry
script: contains examples of solidity scripting files
tests: this is the default directory that contains an example test
Let’s create a simple basic ERC20 contract and a deploy script called RadToken.sol
and DeployRadToken.s.sol
.
First, let's take a look at Radtoken.sol
. This contract, named RadToken
, is an ERC20 token contract. It imports the ERC20
contract from the OpenZeppelin library, which provides a standard implementation of the ERC20 token standard.
The RadToken
contract inherits from the ERC20
contract using the is
keyword. This means that RadToken
will have all the functionality of an ERC20 token.
// RadToken.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract RadToken is ERC20 {
constructor(uint256 initialSupply) ERC20("RadToken", "RAD") {_
mint(msg.sender, initialSupply);
}
}
The contract has a constructor function that takes a parameter initialSupply
of type uint256
. This parameter represents the initial supply of tokens when the contract is deployed. Inside the constructor, two things happen:
The ERC20
constructor is called with the arguments "RadToken
" and "RAD
", which set the name and symbol of the token, respectively.
The _mint
function is called, which mints initialSupply
tokens and assigns them to the address that deploys the contract (msg.sender)
.
By inheriting from ERC20
, the RadToken
contract automatically inherits all the standard ERC20 functions and events, such as totalSupply()
, balanceOf()
, transfer()
, approve()
, allowance()
, transferFrom()
, and the Transfer
and Approval
events.
Also here’s DeployRadToken.s.sol
:
This contract, DeployRadToken
, is a script contract used for deploying the RadToken
contract. It imports the necessary contracts and libraries:
Script
and console2
from "forge-std/Script.sol
", which provide utilities for writing deployment scripts in Forge.
RadToken
from "../src/RadToken.sol
", which is the contract we want to deploy.
The DeployRadToken
contract inherits from the Script
contract, indicating that it is a deployment script.
// SPDX-License-Identifier: MITpragma solidity ^0.8.24;import {Script, console2} from "forge-std/Script.sol";
import {RadToken} from "../src/RadToken.sol";
contract DeployRadToken is Script {
uint256 public constant INITIAL_SUPPLY = 1_000_000 ether;
function run() external returns (RadToken) {
vm.startBroadcast();
RadToken radToken = new RadToken(INITIAL_SUPPLY);
vm.stopBroadcast();
return radToken;
}
}
Inside the contract, a constant variable INITIAL_SUPPLY is defined with a value of 1_000_000 ether. This represents 1 million tokens with 18 decimal places (the default for most ERC20 tokens). The ether keyword is used as a unit of measurement for the token supply.
The run
function is an external function that returns an instance of the deployed RadToken
contract. It is the entry point for the deployment script. Here's what happens inside the run
function:
vm.startBroadcast()
is called to start the broadcasting of transactions.
A new instance of the RadToken
contract is created using new RadToken(INITIAL_SUPPLY)
, passing the INITIAL_SUPPLY
as an argument to the constructor.
vm.stopBroadcast()
is called to stop the broadcasting of transactions.
The deployed RadToken
instance is returned.
This script contract can be run to deploy the RadToken
contract with an initial supply of 1 million tokens.
In summary, RadToken.sol
defines an ERC20 token contract, and DeployRadToken.s.sol
is a script contract used to deploy the RadToken
contract with a specified initial supply.
Now that we wrote a contract we can run a build and compile ABIs by running forge build
We will notice that it created an out
directory for the ABIs for the smart contracts and will also create a cache folder.
To deploy the contract with Hardhat, we’ll need to initiate a Hardhat project. To do that we’ll have to do the following steps.
mkdir linea-erc20-hardhat
cd linea-erc20-hardhat
npx hardhat init
We’ll see the following:
$ npx hardhat init
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.22.15 👷
? What do you want to do? …
Create a JavaScript project
❯ Create a TypeScript project
Create a TypeScript project (with Viem)
Create an empty hardhat.config.js
Quit
We’ll pick the Typescript project. Now we’ll have some files, but we need to head over to hardhat.config.ts
file and add the following:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
dotenv.config();
const config: HardhatUserConfig = {
solidity: "0.8.24",
networks: {
"linea-testnet": {
url: `https://linea-sepolia.infura.io/v3/${process.env.INFURA_API_KEY}`,
accounts: [process.env.ACCOUNT_PRIVATE_KEY!],
},
},
};
export default config;
We will need to install OpenZeppelin library for our ERC20 smart contract as we’re using the library
for Hardhat, we’ll use this command:
npm install @openzeppelin/contracts
for Foundry we’ll use this command:
forge install Openzeppelin/openzeppelin-contracts
Now that our ERC20 contract is ready, it’s time for us to deploy the smart contract on the Linea network. To deploy the smart contract on Linea we will need a few things. We will need an Infura endpoint url and API key, some LineaSepolia testnet tokens? and the private key of your MetaMask wallet.
With Hardhat:
To deploy the contract with Hardhat we’ll need to write a Hardhat ignition module called RadToken.sol
, here’s how it would look like:
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
// Default initial supply: 1 million tokens with 18 decimals
const DEFAULT_INITIAL_SUPPLY: bigint = 1_000_000n * 10n ** 18n;
const RadTokenModule = buildModule("RadTokenModule", (m) => {
// Allow the initial supply to be configurable, with a default value
const initialSupply = m.getParameter("initialSupply", DEFAULT_INITIAL_SUPPLY);
// Deploy the RadToken contract
const radToken = m.contract("RadToken", [initialSupply]);
return { radToken };
});
export default RadTokenModule;
Now we’ll run npx hardhat compile
to compile the contract.
We’ll need to create a .env
file and add the following environment variable like Infura API key and MetaMask wallet private key.
To deploy the contract we’ll use the following command on the terminal:
npx hardhat ignition deploy ignition/modules/RadToken.ts --network linea-testnet
Once deployed we’ll see the following on the terminal:
With Foundry:
Now to deploy the contract with Foundry we don’t need to write any specific deploy script again, we already wrote RadToken.s.sol
. We will use the following command.
$ forge script script/DeployRadToken.s.sol:DeployRadToken --rpc-url <https://linea-sepolia.infura.io/v3/><API-KEY> --private-key <your_MetaMask_private_key> --broadcast
But it’s not the best practice to paste your private key and api key on the terminal. The best way to go about it is to create a .env file and add the following:
PRIVATE_KEY=your metamask private key here
INFURA_API_KEY=your infura api key here
And on the foundry.toml
file add the following:
[rpc_endpoints]
linea-sepolia = "https://linea-sepolia.infura.io/v3/${INFURA_API_KEY}"
Now on the terminal we can run this command:
forge script script/DeployRadToken.s.sol:DeployRadToken --rpc-url linea-sepolia --private-key $PRIVATE_KEY
In this way we’re not pasting sensitive information on the terminal.
We’ll see the following on our terminal:
We’ve successfully deployed our ERC20 contract on Linea Sepolia Testnet.
You can find the whole Foundry code here: https://github.com/meowyx/linea-erc20
And the Hardhat one here: https://github.com/meowyx/linea-erc20-hardhat
ERC20 tokens are an important part of web3 and decentralized web applications from decentralized finance to gaming. By mastering the art of implementing and deploying ERC20 tokens on scalable platforms like Linea, developers are empowered to build the next generation of innovative, user-friendly, and cost-effective dapps.
Whether you choose to write your ERC20 implementation from scratch or leverage audited libraries like OpenZeppelin, deploying your tokens on Linea is the first step forward. Maybe experiment and create some more exciting use-cases. There are also the ERC721 standard and ERC-1155 to look forward to and experiment with!
Happy Coding! 🚀
For more, also check out our earlier Getting Started Guide on Deploying a Smart Contract.