The Ethereum Request for Comment (ERC)-20 is a technical standard for creating and implementing tokens on the Ethereum blockchain. It defines a set of rules and functions that a token contract must follow. This ensures that tokens can be easily exchanged and integrated with various decentralised applications (dApps) and platforms within the Ethereum ecosystem. ERC-20 tokens can be used as rewards, company shares, a sense of identity in a community, or a means of exchange on Layer Two (L2) networks. Ethereum Request for Comment Layer Two Tokens and coins are not the same. Coins are native currencies of Layer 1 blockchains, e.g. BTC for Bitcoin, ETH for Ethereum, and SOL for Solana, etc. Tokens, on the other hand, are cryptocurrencies built on top of existing blockchains, often using smart contracts, e.g. LINK, AAVE, UN,I etc Tokens and coins are not the same. Coins are native currencies of Layer 1 blockchains, e.g. BTC for Bitcoin, ETH for Ethereum, and SOL for Solana, etc. Tokens, on the other hand, are cryptocurrencies built on top of existing blockchains, often using smart contracts, e.g. LINK, AAVE, UN,I etc Tokens and coins are not the same. BTC ETH SOL built on top of existing blockchains In this article, you will learn how to build a multi-chain ERC-20 generator using GetBlock RPC (a web3 RPC provider). This means users can create their token from a user interface without writing code, as seen below: In this article, you will learn how to build a multi-chain ERC-20 generator using In this article, you will learn how to build a multi-chain ERC-20 generator using GetBlock GetBlock GetBlock RPC (a web3 RPC provider). This means users can create their token from a user interface without writing code, as seen below: RPC (a web3 RPC provider). This means users can create their token from a user interface without writing code, as seen below: Prerequisites A web3 wallet, preferably Metamask A code editor, such as VSCode Must have installed Node A package manager installed on your machine, like yarn, npm, or pnpm Basic programming knowledge A bottle of water A web3 wallet, preferably Metamask Metamask A code editor, such as VSCode VSCode Must have installed Node Node A package manager installed on your machine, like yarn, npm, or pnpm yarn npm pnpm Basic programming knowledge A bottle of water Tools and Technologies Needed OpenZeppelin Contract A frontend framework(I will be using React) Ether.js TailwindCss for styling Smart contract OpenZeppelin Contract A frontend framework(I will be using React) Ether.js TailwindCss for styling Smart contract Setting Up Your GetBlock Account Sign up on GetBlock using either your Gmail, GitHub or Metamask wallet After signing up, you will be routed to your dashboard Scroll down to My endpoints Select your choice of protocol, network, and then click “Get” Automatically, a URL will be generated for you containing your access token Sign up on GetBlock using either your Gmail, GitHub or Metamask wallet Sign up on GetBlock using either your Gmail, GitHub or Metamask wallet GetBlock After signing up, you will be routed to your dashboard After signing up, you will be routed to your dashboard Scroll down to My endpoints Scroll down to My endpoints My endpoints Select your choice of protocol, network, and then click “Get” Select your choice of protocol, network, and then click “Get” protocol “Get” Automatically, a URL will be generated for you containing your access token Automatically, a URL will be generated for you containing your access token Ensure you keep this URL safe.Please note that you are on a free plan, which means you are limited to only two endpoints and cannot access other features like Usage Statistics. I encourage you to upgrade your account to enjoy more features. Ensure you keep this URL safe.Please note that you are on a free plan, which means you are limited to only two endpoints and cannot access other features like Usage Statistics. I encourage you to upgrade your account to enjoy more features. Setting Up Your Dev Environment Create a new Folder with any name of your choice, e.g ERC-20-generator' Install the frontend framework of your choice Install Hardhat using the following command: //npm npm install --save-dev hardhat //yarn yarn add --dev hardhat //pnpm pnpm add -D hardhat Initialise hardhat using this command: npx hardhat init Select Create an empty hardhat.config.js using the arrow key and accept all prompts ileolami@MacBookAir erc-20 % npx hardhat init WARNING: You are currently using Node.js v23.9.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://hardhat.org/nodejs-versions 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.23.0 👷 ✔ What do you want to do? · Create an empty hardhat.config.js ✨ Config file created ✨ Give Hardhat a star on Github if you're enjoying it! ⭐️✨ https://github.com/NomicFoundation/hardhat Install other dependencies using this command: npm install --save-dev @nomicfoundation/hardhat-toolbox npm install @openzeppelin/contracts ether Create a new Folder with any name of your choice, e.g ERC-20-generator' Create a new Folder with any name of your choice, e.g ERC-20-generator' ERC-20-generator' Install the frontend framework of your choice Install the frontend framework of your choice Install Hardhat using the following command: //npm npm install --save-dev hardhat //yarn yarn add --dev hardhat //pnpm pnpm add -D hardhat Install Hardhat using the following command: //npm npm install --save-dev hardhat //yarn yarn add --dev hardhat //pnpm pnpm add -D hardhat //npm npm install --save-dev hardhat //yarn yarn add --dev hardhat //pnpm pnpm add -D hardhat Initialise hardhat using this command: npx hardhat init Initialise hardhat using this command: npx hardhat init npx hardhat init Select Create an empty hardhat.config.js using the arrow key and accept all prompts ileolami@MacBookAir erc-20 % npx hardhat init WARNING: You are currently using Node.js v23.9.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://hardhat.org/nodejs-versions 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.23.0 👷 ✔ What do you want to do? · Create an empty hardhat.config.js ✨ Config file created ✨ Give Hardhat a star on Github if you're enjoying it! ⭐️✨ https://github.com/NomicFoundation/hardhat Select Create an empty hardhat.config.js using the arrow key and accept all prompts Create an empty hardhat.config.js ileolami@MacBookAir erc-20 % npx hardhat init WARNING: You are currently using Node.js v23.9.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://hardhat.org/nodejs-versions 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.23.0 👷 ✔ What do you want to do? · Create an empty hardhat.config.js ✨ Config file created ✨ Give Hardhat a star on Github if you're enjoying it! ⭐️✨ https://github.com/NomicFoundation/hardhat ileolami@MacBookAir erc-20 % npx hardhat init WARNING: You are currently using Node.js v23.9.0, which is not supported by Hardhat. This can lead to unexpected behavior. See https://hardhat.org/nodejs-versions 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.23.0 👷 ✔ What do you want to do? · Create an empty hardhat.config.js ✨ Config file created ✨ Give Hardhat a star on Github if you're enjoying it! ⭐️✨ https://github.com/NomicFoundation/hardhat Install other dependencies using this command: npm install --save-dev @nomicfoundation/hardhat-toolbox npm install @openzeppelin/contracts ether Install other dependencies using this command: npm install --save-dev @nomicfoundation/hardhat-toolbox npm install @openzeppelin/contracts ether npm install --save-dev @nomicfoundation/hardhat-toolbox npm install @openzeppelin/contracts ether You may also install your choice of CSS framework at this point You may also install your choice of CSS framework at this point Writing and Compiling the ERC-20 Contract You're only writing and compiling the ERC-20 contract for this project because it will serve as a template. Users will provide their token details, and deployment will be handled from the frontend. Create a new folder contract Create a new file token.sol and import the following code: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract XToken is ERC20 { constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) { _mint(msg.sender, initialSupply); } } Create a new folder contract Create a new folder contract contract Create a new file token.sol and import the following code: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract XToken is ERC20 { constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) { _mint(msg.sender, initialSupply); } } Create a new file token.sol and import the following code: token.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract XToken is ERC20 { constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) { _mint(msg.sender, initialSupply); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract XToken is ERC20 { constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) { _mint(msg.sender, initialSupply); } } import "@openzeppelin/contracts/token/ERC20/ERC20.sol” : This imports the OpenZeppelin ERC20 implementation, which provides all standard ERC-20 functionality. Inside XToken is ERC20, it defines the template in which the users will be able to create their token. After that, it mints the initialSupply tokens and assigns them to the address that deployed the contract (msg.sender) import "@openzeppelin/contracts/token/ERC20/ERC20.sol” : This imports the OpenZeppelin ERC20 implementation, which provides all standard ERC-20 functionality. import "@openzeppelin/contracts/token/ERC20/ERC20.sol” : This imports the OpenZeppelin ERC20 implementation, which provides all standard ERC-20 functionality. import "@openzeppelin/contracts/token/ERC20/ERC20.sol” Inside XToken is ERC20, it defines the template in which the users will be able to create their token. After that, it mints the initialSupply tokens and assigns them to the address that deployed the contract (msg.sender) Inside XToken is ERC20, it defines the template in which the users will be able to create their token. After that, it mints the initialSupply tokens and assigns them to the address that deployed the contract (msg.sender) XToken is ERC20 initialSupply msg.sender To compile this code, run the following command: npx hardhat compile This will compile the smart contract and generate an abi and bytecode. This JSON file is what you will use to interact with the smart contract. You will see this file under artifacts folder: In your src folder, create two new files and name them token.json and tokenBytecode.json respectively Copy the abi from XToken.json and paste it inside token.json using this format: { "abi": [ ... ] } In the XToken.json file, scroll down, copy the bytecode, and paste it into tokenBytecode.json using this format: { "bytecode": "0x608060405234801561001057600080fd5b50604051..." } To compile this code, run the following command: npx hardhat compile This will compile the smart contract and generate an abi and bytecode. This JSON file is what you will use to interact with the smart contract. You will see this file under artifacts folder: To compile this code, run the following command: npx hardhat compile npx hardhat compile This will compile the smart contract and generate an abi and bytecode. This JSON file is what you will use to interact with the smart contract. You will see this file under artifacts folder: abi bytecode artifacts In your src folder, create two new files and name them token.json and tokenBytecode.json respectively In your src folder, create two new files and name them token.json and tokenBytecode.json respectively src token.json tokenBytecode.json Copy the abi from XToken.json and paste it inside token.json using this format: { "abi": [ ... ] } Copy the abi from XToken.json and paste it inside token.json using this format: abi XToken.json token.json { "abi": [ ... ] } { "abi": [ ... ] } In the XToken.json file, scroll down, copy the bytecode, and paste it into tokenBytecode.json using this format: { "bytecode": "0x608060405234801561001057600080fd5b50604051..." } In the XToken.json file, scroll down, copy the bytecode, and paste it into tokenBytecode.json using this format: XToken.json bytecode tokenBytecode.json { "bytecode": "0x608060405234801561001057600080fd5b50604051..." } { "bytecode": "0x608060405234801561001057600080fd5b50604051..." } Building the User Interface (UI) Creating and Storing Access Tokens in .env Go to your GetBlock account and create multiple node endpoints. Go to your GetBlock account and create multiple node endpoints. GetBlock I will be using Ethereum(Sepolia) and Rootstock Testnet. I will be using Ethereum(Sepolia) and Rootstock Testnet. Create a .env file. Copy the endpoints you created earlier and store them in the .env file ROOTSTOCK_RPC=https://go.getblock.io/<ACCESS_TOKEN> ETHEREUM_RPC=https://go.getblock.io/<ACCESS_TOKEN> Create a .env file. Create a .env file. .env Copy the endpoints you created earlier and store them in the .env file ROOTSTOCK_RPC=https://go.getblock.io/<ACCESS_TOKEN> ETHEREUM_RPC=https://go.getblock.io/<ACCESS_TOKEN> Copy the endpoints you created earlier and store them in the .env file .env ROOTSTOCK_RPC=https://go.getblock.io/<ACCESS_TOKEN> ETHEREUM_RPC=https://go.getblock.io/<ACCESS_TOKEN> ROOTSTOCK_RPC=https://go.getblock.io/<ACCESS_TOKEN> ETHEREUM_RPC=https://go.getblock.io/<ACCESS_TOKEN> Network Configuration Create a config folder under src directory Create a networks.jsx file and add this code: // Ensure to edit this code to suit your choice of networks. export const networks = { ethereum: { chainId: 11155111, rpc: import.meta.env.ETHEREUM_RPC_URL, name: "Ethereum", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18, }, blockExplorer: "https://sepolia.etherscan.io", }, rootstock: { chainId: 31, rpc: import.meta.env.ROOTSTOCK_RPC_URL, name: "Rootstock", nativeCurrency: { name: "TRBTC", symbol: "TRBTC", decimals: 18, }, blockExplorer: "https://rootstock-testnet.blockscout.com", }, }; You can list of networks’ detail here Create a config folder under src directory Create a config folder under src directory config src Create a networks.jsx file and add this code: // Ensure to edit this code to suit your choice of networks. export const networks = { ethereum: { chainId: 11155111, rpc: import.meta.env.ETHEREUM_RPC_URL, name: "Ethereum", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18, }, blockExplorer: "https://sepolia.etherscan.io", }, rootstock: { chainId: 31, rpc: import.meta.env.ROOTSTOCK_RPC_URL, name: "Rootstock", nativeCurrency: { name: "TRBTC", symbol: "TRBTC", decimals: 18, }, blockExplorer: "https://rootstock-testnet.blockscout.com", }, }; You can list of networks’ detail here Create a networks.jsx file and add this code: networks.jsx // Ensure to edit this code to suit your choice of networks. export const networks = { ethereum: { chainId: 11155111, rpc: import.meta.env.ETHEREUM_RPC_URL, name: "Ethereum", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18, }, blockExplorer: "https://sepolia.etherscan.io", }, rootstock: { chainId: 31, rpc: import.meta.env.ROOTSTOCK_RPC_URL, name: "Rootstock", nativeCurrency: { name: "TRBTC", symbol: "TRBTC", decimals: 18, }, blockExplorer: "https://rootstock-testnet.blockscout.com", }, }; // Ensure to edit this code to suit your choice of networks. export const networks = { ethereum: { chainId: 11155111, rpc: import.meta.env.ETHEREUM_RPC_URL, name: "Ethereum", nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18, }, blockExplorer: "https://sepolia.etherscan.io", }, rootstock: { chainId: 31, rpc: import.meta.env.ROOTSTOCK_RPC_URL, name: "Rootstock", nativeCurrency: { name: "TRBTC", symbol: "TRBTC", decimals: 18, }, blockExplorer: "https://rootstock-testnet.blockscout.com", }, }; You can list of networks’ detail here You can list of networks’ detail here here Utility Functions Under src folder, create another subfolder and name it utils Under /utilsCreate two files called token.jsx and wallet.jsx At this point, your folder should be looking like this:/src ├── config/ │ └── networks.js # Network configuration │ ├── utils/ │ ├── tokenUtils.jsx # Token deployment and wallet integration functions │ └── walletUtils.jsx # Wallet connection and network switching functions │ ├── App.jsx # Main TokenCreator component ├── index.css # Global styles including glass effect ├── main.jsx # Entry point for the React application ├── token.json # ERC-20 token ABI └── tokenBytecode.json # ERC-20 token bytecode Inside token.jsx , Copy and paste the following code: import { ethers } from "ethers"; import XToken from "../token.json"; import XTokenBytecode from "../tokenBytecode.json"; export const deployToken = async (name, symbol, supply, signer) => { const factory = new ethers.ContractFactory( XToken.abi, XTokenBytecode.bytecode, signer ); const contract = await factory.deploy( name, symbol, ethers.parseUnits(supply, 18) ); await contract.waitForDeployment(); const tokenAddress = await contract.getAddress(); return tokenAddress; }; export const addTokenToWallet = async (address, symbol) => { await window.ethereum.request({ method: "wallet_watchAsset", params: { type: "ERC20", options: { address, symbol, decimals: 18, }, }, }); }; This codebase: Creates a contract factory using the smart contract ABI and bytecode Deploys the token with its name, symbol, and supply Converts supply to proper units (with 18 decimals) Waits for deployment confirmation Returns the deployed token's address Adds the newly created token to MetaMask Uses the MetaMask API's wallet_watchAsset method Inside wallet.jsx, Copy and paste the following code: import { ethers } from "ethers"; import { networks } from "../config/networks"; export const connectWallet = async (networkId) => { if (!window.ethereum) throw new Error("Please install MetaMask"); const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); // Switch or add chain try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { // Add the network if it doesn't exist in MetaMask if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } // Now connect const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); }; export const switchNetwork = async (networkId, currentChainId) => { const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); if (currentChainId !== selected.chainId) { try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); } return null; }; This codebase: Auto-adds networks to MetaMask if they don't exist Handles network switching with proper error management Request for transaction approval Converts chain IDs to hex format for MetaMask compatibility Under src folder, create another subfolder and name it utils Under src folder, create another subfolder and name it utils src utils Under /utilsCreate two files called token.jsx and wallet.jsx At this point, your folder should be looking like this:/src ├── config/ │ └── networks.js # Network configuration │ ├── utils/ │ ├── tokenUtils.jsx # Token deployment and wallet integration functions │ └── walletUtils.jsx # Wallet connection and network switching functions │ ├── App.jsx # Main TokenCreator component ├── index.css # Global styles including glass effect ├── main.jsx # Entry point for the React application ├── token.json # ERC-20 token ABI └── tokenBytecode.json # ERC-20 token bytecode Under /utilsCreate two files called token.jsx and wallet.jsx /utils token.jsx wallet.jsx At this point, your folder should be looking like this: /src ├── config/ │ └── networks.js # Network configuration │ ├── utils/ │ ├── tokenUtils.jsx # Token deployment and wallet integration functions │ └── walletUtils.jsx # Wallet connection and network switching functions │ ├── App.jsx # Main TokenCreator component ├── index.css # Global styles including glass effect ├── main.jsx # Entry point for the React application ├── token.json # ERC-20 token ABI └── tokenBytecode.json # ERC-20 token bytecode /src ├── config/ │ └── networks.js # Network configuration │ ├── utils/ │ ├── tokenUtils.jsx # Token deployment and wallet integration functions │ └── walletUtils.jsx # Wallet connection and network switching functions │ ├── App.jsx # Main TokenCreator component ├── index.css # Global styles including glass effect ├── main.jsx # Entry point for the React application ├── token.json # ERC-20 token ABI └── tokenBytecode.json # ERC-20 token bytecode Inside token.jsx , Copy and paste the following code: import { ethers } from "ethers"; import XToken from "../token.json"; import XTokenBytecode from "../tokenBytecode.json"; export const deployToken = async (name, symbol, supply, signer) => { const factory = new ethers.ContractFactory( XToken.abi, XTokenBytecode.bytecode, signer ); const contract = await factory.deploy( name, symbol, ethers.parseUnits(supply, 18) ); await contract.waitForDeployment(); const tokenAddress = await contract.getAddress(); return tokenAddress; }; export const addTokenToWallet = async (address, symbol) => { await window.ethereum.request({ method: "wallet_watchAsset", params: { type: "ERC20", options: { address, symbol, decimals: 18, }, }, }); }; This codebase: Creates a contract factory using the smart contract ABI and bytecode Deploys the token with its name, symbol, and supply Converts supply to proper units (with 18 decimals) Waits for deployment confirmation Returns the deployed token's address Adds the newly created token to MetaMask Uses the MetaMask API's wallet_watchAsset method Inside token.jsx , Copy and paste the following code: token.jsx import { ethers } from "ethers"; import XToken from "../token.json"; import XTokenBytecode from "../tokenBytecode.json"; export const deployToken = async (name, symbol, supply, signer) => { const factory = new ethers.ContractFactory( XToken.abi, XTokenBytecode.bytecode, signer ); const contract = await factory.deploy( name, symbol, ethers.parseUnits(supply, 18) ); await contract.waitForDeployment(); const tokenAddress = await contract.getAddress(); return tokenAddress; }; export const addTokenToWallet = async (address, symbol) => { await window.ethereum.request({ method: "wallet_watchAsset", params: { type: "ERC20", options: { address, symbol, decimals: 18, }, }, }); }; import { ethers } from "ethers"; import XToken from "../token.json"; import XTokenBytecode from "../tokenBytecode.json"; export const deployToken = async (name, symbol, supply, signer) => { const factory = new ethers.ContractFactory( XToken.abi, XTokenBytecode.bytecode, signer ); const contract = await factory.deploy( name, symbol, ethers.parseUnits(supply, 18) ); await contract.waitForDeployment(); const tokenAddress = await contract.getAddress(); return tokenAddress; }; export const addTokenToWallet = async (address, symbol) => { await window.ethereum.request({ method: "wallet_watchAsset", params: { type: "ERC20", options: { address, symbol, decimals: 18, }, }, }); }; This codebase: Creates a contract factory using the smart contract ABI and bytecode Deploys the token with its name, symbol, and supply Converts supply to proper units (with 18 decimals) Waits for deployment confirmation Returns the deployed token's address Adds the newly created token to MetaMask Uses the MetaMask API's wallet_watchAsset method Creates a contract factory using the smart contract ABI and bytecode Deploys the token with its name, symbol, and supply Converts supply to proper units (with 18 decimals) Waits for deployment confirmation Returns the deployed token's address Adds the newly created token to MetaMask Uses the MetaMask API's wallet_watchAsset method wallet_watchAsset Inside wallet.jsx, Copy and paste the following code: import { ethers } from "ethers"; import { networks } from "../config/networks"; export const connectWallet = async (networkId) => { if (!window.ethereum) throw new Error("Please install MetaMask"); const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); // Switch or add chain try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { // Add the network if it doesn't exist in MetaMask if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } // Now connect const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); }; export const switchNetwork = async (networkId, currentChainId) => { const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); if (currentChainId !== selected.chainId) { try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); } return null; }; This codebase: Auto-adds networks to MetaMask if they don't exist Handles network switching with proper error management Request for transaction approval Converts chain IDs to hex format for MetaMask compatibility Inside wallet.jsx, Copy and paste the following code: wallet.jsx import { ethers } from "ethers"; import { networks } from "../config/networks"; export const connectWallet = async (networkId) => { if (!window.ethereum) throw new Error("Please install MetaMask"); const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); // Switch or add chain try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { // Add the network if it doesn't exist in MetaMask if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } // Now connect const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); }; export const switchNetwork = async (networkId, currentChainId) => { const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); if (currentChainId !== selected.chainId) { try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); } return null; }; import { ethers } from "ethers"; import { networks } from "../config/networks"; export const connectWallet = async (networkId) => { if (!window.ethereum) throw new Error("Please install MetaMask"); const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); // Switch or add chain try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { // Add the network if it doesn't exist in MetaMask if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } // Now connect const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); }; export const switchNetwork = async (networkId, currentChainId) => { const selected = networks[networkId]; const hexChainId = "0x" + selected.chainId.toString(16); if (currentChainId !== selected.chainId) { try { await window.ethereum.request({ method: "wallet_switchEthereumChain", params: [{ chainId: hexChainId }], }); } catch (switchError) { if (switchError.code === 4902) { await window.ethereum.request({ method: "wallet_addEthereumChain", params: [ { chainId: hexChainId, chainName: selected.name, rpcUrls: [selected.rpc], nativeCurrency: selected.nativeCurrency, blockExplorerUrls: [selected.blockExplorer], }, ], }); } else { throw switchError; } } const provider = new ethers.BrowserProvider(window.ethereum); await provider.send("eth_requestAccounts", []); return await provider.getSigner(); } return null; }; This codebase: Auto-adds networks to MetaMask if they don't exist Handles network switching with proper error management Request for transaction approval Converts chain IDs to hex format for MetaMask compatibility Auto-adds networks to MetaMask if they don't exist Handles network switching with proper error management Request for transaction approval Converts chain IDs to hex format for MetaMask compatibility The User Interface At this point, you must have created the network configuration and the utility functions. In this phase, you are building the UI for your users to create their token. Clear out what is inside App.jsx and add the following code: import React, { useState } from "react"; import { networks } from "./config/networks"; import { connectWallet, switchNetwork } from "./utils/wallet"; import { deployToken, addTokenToWallet } from "./utils/token"; const TokenCreator = () => { const [form, setForm] = useState({ name: "", symbol: "", supply: "", network: "ethereum", }); const [status, setStatus] = useState(""); const [walletConnected, setWalletConnected] = useState(false); const [signer, setSigner] = useState(null); const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); }; const handleConnectWallet = async () => { try { const signerInstance = await connectWallet(form.network); setSigner(signerInstance); setWalletConnected(true); setStatus("✅ Wallet connected to " + form.network); } catch (err) { console.error(err); setStatus("❌ Failed to connect or switch network"); } }; const handleDeployToken = async () => { const { name, symbol, supply, network } = form; if (!signer) { setStatus("❌ Wallet not connected"); return; } try { // Check if we need to switch networks const currentNetwork = await signer.provider.getNetwork(); const selected = networks[network]; if (currentNetwork.chainId !== selected.chainId) { setStatus("Switching to " + network + " network..."); const newSigner = await switchNetwork(network, currentNetwork.chainId); if (newSigner) setSigner(newSigner); } setStatus("Deploying token..."); const tokenAddress = await deployToken(name, symbol, supply, signer); setStatus(`✅ Token deployed at ${tokenAddress}`); await addTokenToWallet(tokenAddress, symbol); } catch (err) { console.error(err); setStatus("❌ Failed to deploy token"); } }; return ( <div className="glass-background p-4 max-w-md mx-auto my-48 rounded-xl shadow-md border space-y-4"> <h2 className="text-xl font-semibold text-center">ERC-20 Token Maker</h2> {!walletConnected ? ( <button onClick={handleConnectWallet} className="w-full bg-green-600 text-white p-2 rounded hover:bg-green-700 shadow-2xl" > Connect Wallet </button> ) : ( <> <input type="text" name="name" placeholder="Token Name" value={form.name} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="text" name="symbol" placeholder="Token Symbol" value={form.symbol} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="number" name="supply" placeholder="Initial Supply" value={form.supply} onChange={handleChange} className="w-full p-2 border rounded " /> <select name="network" value={form.network} onChange={handleChange} className="w-full p-2 border rounded" > <option value="ethereum">Ethereum</option> <option value="rootstock">Rootstock</option> </select> <button onClick={handleDeployToken} className="w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700" > Deploy Token </button> </> )} {status && <p className="text-sm mt-2">{status}</p>} </div> ); }; export default TokenCreator; Clear out what is inside App.jsx and add the following code: import React, { useState } from "react"; import { networks } from "./config/networks"; import { connectWallet, switchNetwork } from "./utils/wallet"; import { deployToken, addTokenToWallet } from "./utils/token"; const TokenCreator = () => { const [form, setForm] = useState({ name: "", symbol: "", supply: "", network: "ethereum", }); const [status, setStatus] = useState(""); const [walletConnected, setWalletConnected] = useState(false); const [signer, setSigner] = useState(null); const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); }; const handleConnectWallet = async () => { try { const signerInstance = await connectWallet(form.network); setSigner(signerInstance); setWalletConnected(true); setStatus("✅ Wallet connected to " + form.network); } catch (err) { console.error(err); setStatus("❌ Failed to connect or switch network"); } }; const handleDeployToken = async () => { const { name, symbol, supply, network } = form; if (!signer) { setStatus("❌ Wallet not connected"); return; } try { // Check if we need to switch networks const currentNetwork = await signer.provider.getNetwork(); const selected = networks[network]; if (currentNetwork.chainId !== selected.chainId) { setStatus("Switching to " + network + " network..."); const newSigner = await switchNetwork(network, currentNetwork.chainId); if (newSigner) setSigner(newSigner); } setStatus("Deploying token..."); const tokenAddress = await deployToken(name, symbol, supply, signer); setStatus(`✅ Token deployed at ${tokenAddress}`); await addTokenToWallet(tokenAddress, symbol); } catch (err) { console.error(err); setStatus("❌ Failed to deploy token"); } }; return ( <div className="glass-background p-4 max-w-md mx-auto my-48 rounded-xl shadow-md border space-y-4"> <h2 className="text-xl font-semibold text-center">ERC-20 Token Maker</h2> {!walletConnected ? ( <button onClick={handleConnectWallet} className="w-full bg-green-600 text-white p-2 rounded hover:bg-green-700 shadow-2xl" > Connect Wallet </button> ) : ( <> <input type="text" name="name" placeholder="Token Name" value={form.name} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="text" name="symbol" placeholder="Token Symbol" value={form.symbol} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="number" name="supply" placeholder="Initial Supply" value={form.supply} onChange={handleChange} className="w-full p-2 border rounded " /> <select name="network" value={form.network} onChange={handleChange} className="w-full p-2 border rounded" > <option value="ethereum">Ethereum</option> <option value="rootstock">Rootstock</option> </select> <button onClick={handleDeployToken} className="w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700" > Deploy Token </button> </> )} {status && <p className="text-sm mt-2">{status}</p>} </div> ); }; export default TokenCreator; Clear out what is inside App.jsx and add the following code: App.jsx import React, { useState } from "react"; import { networks } from "./config/networks"; import { connectWallet, switchNetwork } from "./utils/wallet"; import { deployToken, addTokenToWallet } from "./utils/token"; const TokenCreator = () => { const [form, setForm] = useState({ name: "", symbol: "", supply: "", network: "ethereum", }); const [status, setStatus] = useState(""); const [walletConnected, setWalletConnected] = useState(false); const [signer, setSigner] = useState(null); const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); }; const handleConnectWallet = async () => { try { const signerInstance = await connectWallet(form.network); setSigner(signerInstance); setWalletConnected(true); setStatus("✅ Wallet connected to " + form.network); } catch (err) { console.error(err); setStatus("❌ Failed to connect or switch network"); } }; const handleDeployToken = async () => { const { name, symbol, supply, network } = form; if (!signer) { setStatus("❌ Wallet not connected"); return; } try { // Check if we need to switch networks const currentNetwork = await signer.provider.getNetwork(); const selected = networks[network]; if (currentNetwork.chainId !== selected.chainId) { setStatus("Switching to " + network + " network..."); const newSigner = await switchNetwork(network, currentNetwork.chainId); if (newSigner) setSigner(newSigner); } setStatus("Deploying token..."); const tokenAddress = await deployToken(name, symbol, supply, signer); setStatus(`✅ Token deployed at ${tokenAddress}`); await addTokenToWallet(tokenAddress, symbol); } catch (err) { console.error(err); setStatus("❌ Failed to deploy token"); } }; return ( <div className="glass-background p-4 max-w-md mx-auto my-48 rounded-xl shadow-md border space-y-4"> <h2 className="text-xl font-semibold text-center">ERC-20 Token Maker</h2> {!walletConnected ? ( <button onClick={handleConnectWallet} className="w-full bg-green-600 text-white p-2 rounded hover:bg-green-700 shadow-2xl" > Connect Wallet </button> ) : ( <> <input type="text" name="name" placeholder="Token Name" value={form.name} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="text" name="symbol" placeholder="Token Symbol" value={form.symbol} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="number" name="supply" placeholder="Initial Supply" value={form.supply} onChange={handleChange} className="w-full p-2 border rounded " /> <select name="network" value={form.network} onChange={handleChange} className="w-full p-2 border rounded" > <option value="ethereum">Ethereum</option> <option value="rootstock">Rootstock</option> </select> <button onClick={handleDeployToken} className="w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700" > Deploy Token </button> </> )} {status && <p className="text-sm mt-2">{status}</p>} </div> ); }; export default TokenCreator; import React, { useState } from "react"; import { networks } from "./config/networks"; import { connectWallet, switchNetwork } from "./utils/wallet"; import { deployToken, addTokenToWallet } from "./utils/token"; const TokenCreator = () => { const [form, setForm] = useState({ name: "", symbol: "", supply: "", network: "ethereum", }); const [status, setStatus] = useState(""); const [walletConnected, setWalletConnected] = useState(false); const [signer, setSigner] = useState(null); const handleChange = (e) => { setForm({ ...form, [e.target.name]: e.target.value }); }; const handleConnectWallet = async () => { try { const signerInstance = await connectWallet(form.network); setSigner(signerInstance); setWalletConnected(true); setStatus("✅ Wallet connected to " + form.network); } catch (err) { console.error(err); setStatus("❌ Failed to connect or switch network"); } }; const handleDeployToken = async () => { const { name, symbol, supply, network } = form; if (!signer) { setStatus("❌ Wallet not connected"); return; } try { // Check if we need to switch networks const currentNetwork = await signer.provider.getNetwork(); const selected = networks[network]; if (currentNetwork.chainId !== selected.chainId) { setStatus("Switching to " + network + " network..."); const newSigner = await switchNetwork(network, currentNetwork.chainId); if (newSigner) setSigner(newSigner); } setStatus("Deploying token..."); const tokenAddress = await deployToken(name, symbol, supply, signer); setStatus(`✅ Token deployed at ${tokenAddress}`); await addTokenToWallet(tokenAddress, symbol); } catch (err) { console.error(err); setStatus("❌ Failed to deploy token"); } }; return ( <div className="glass-background p-4 max-w-md mx-auto my-48 rounded-xl shadow-md border space-y-4"> <h2 className="text-xl font-semibold text-center">ERC-20 Token Maker</h2> {!walletConnected ? ( <button onClick={handleConnectWallet} className="w-full bg-green-600 text-white p-2 rounded hover:bg-green-700 shadow-2xl" > Connect Wallet </button> ) : ( <> <input type="text" name="name" placeholder="Token Name" value={form.name} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="text" name="symbol" placeholder="Token Symbol" value={form.symbol} onChange={handleChange} className="w-full p-2 border rounded" /> <input type="number" name="supply" placeholder="Initial Supply" value={form.supply} onChange={handleChange} className="w-full p-2 border rounded " /> <select name="network" value={form.network} onChange={handleChange} className="w-full p-2 border rounded" > <option value="ethereum">Ethereum</option> <option value="rootstock">Rootstock</option> </select> <button onClick={handleDeployToken} className="w-full bg-blue-600 text-white p-2 rounded hover:bg-blue-700" > Deploy Token </button> </> )} {status && <p className="text-sm mt-2">{status}</p>} </div> ); }; export default TokenCreator; This codebase contains Tailwindcss styling, ensure to edit this code. This codebase contains Tailwindcss styling, ensure to edit this code. In this codebase: Conditional rendering based on wallet connection Form for token creation (name, symbol, supply) Network selection dropdown Status messages for user feedback Conditional rendering based on wallet connection Form for token creation (name, symbol, supply) Network selection dropdown Status messages for user feedback At the end, you should have something like this: Congratulations 🥂, you just created your own ERC-20 Maker Conclusion GetBlock allows developers to create multi-chain dApps using their RPC. This projects eliminate the need for users to write code when creating their token. You can add extra features to this project e.g: a. Airdrop feature that lets users distribute their tokens to their community based on activities, contributions, or tasks. b. Logo for token identity You can add extra features to this project e.g: a. Airdrop feature that lets users distribute their tokens to their community based on activities, contributions, or tasks. b. Logo for token identity You can access the GitHub repo here and live site here. here here