⚠️ The Hardhat Team released version 3 of their product. The guide below works only with existing version 2 projects. We are working on a new guide that will support both new and version 3 projects.
This article provides a detailed guide on how to leverage Hardhat for smart contract deployment and verification using JavaScript or TypeScript. Learn how to efficiently verify your contracts on Routescan using the hardhat-verify plugin.
Initial Setup
Before creating the project, your environment must be set up correctly.
1. Install Node.js and npm
Hardhat is a JavaScript-based tool, so it requires Node.js to run. Node Package Manager (npm
) is installed automatically with Node.js and is used to manage project packages like Hardhat itself.
ℹ️ If you see the error 'npm' is not recognized as the name of a cmdlet...
, it means Node.js is not installed correctly or not in your system's PATH. Re-running the installer and choosing system PATH is the best fix.
Go to the official Node.js website and download the LTS (Long-Term Support) version.
During installation, ensure the 'Add to PATH' option is enabled. This allows you to run
node
andnpm
commands from any folder in your terminal.
2. Configure Your Terminal (If Needed)
ℹ️ If you see an error like ...cannot be loaded because running scripts is disabled on this system
, this step is the solution.
macOS/Linux Users
This step is generally not required. The default terminal settings on macOS/Linux are typically sufficient for running npm
scripts without any changes.
Windows Users (PowerShell)
By default, PowerShell has a security policy that can prevent npm
from running scripts. You may need to change it.
Open PowerShell as an Administrator (Start Menu > type 'PowerShell' > right-click > Run as administrator).
Run the command
Set-ExecutionPolicy RemoteSigned
When prompted, type
Y
and press Enter.
3. Prepare Your Crypto Wallet
You will need a browser extension wallet like MetaMask to hold your test funds and sign transactions. Deployment costs gas, even on a Testnet.
Creating the Hardhat Project
1. Initialize the Project
Open your terminal (PowerShell, Terminal, CMD, etc.) and run these commands one by one.
Create a new folder for the project.
mkdir my-hardhat-project
Navigate into it.
cd my-hardhat-project
Initialize a Node.js project.
npm init -y
Install Hardhat.
npm install --save-dev hardhat
Run the Hardhat setup wizard.
npx hardhat --init
During the npx hardhat
wizard, answer the prompts as follows:
What do you want to do?
→ Create a JavaScript project OR Create a TypeScript project (you can pick any for your convenience)Hardhat project root:
→ Press Enter to accept the defaultDo you want to add a .gitignore?
→ yes
ℹ️ This creates a .gitignore
file. This is crucial for security as it prevents your private .env
file from being accidentally uploaded to version control systems like GitHub.
Do you want to install this sample project's dependencies...?
→ yes
ℹ️ It automatically installs essential packages like @nomicfoundation/hardhat-toolbox
, saving you a manual step.
Please note that this guide provides code for both JavaScript (.js
) and TypeScript (.ts
). Choose the option that best fits your needs.
2. Project Structure Overview
Hardhat creates several folders. The most important for us are:
/contracts
: Where your Solidity source code (.sol
files) goes./ignition/modules
: Deployment logic.hardhat.config.js
(or.ts
): The main configuration file for the project.
Configuring Your Project for Deployment
1. Write Your Smart Contract
Go to the
/contracts
folder and modify the sampleLock.sol
file or create a new.sol
file.Write your smart contract code inside the file. As example, we will add the following code to
Greeter.sol
.// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
contract Greeter {
string private greeting;
constructor(string memory _greeting) {
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
}
2. Securely Store Your Private Key
You must provide your private key to deploy. There are two ways to do this: using a persistent .env
file or a temporary terminal variable. In MetaMask, click the three dots (⋮) > Account details > Show private key. If you are using other Crypto Wallets look for a corresponding setting to display private key.
⚠️ Important Note: Your private key gives total control over your wallet. Never share it.
Option A. Using a .env
File
This method securely stores your key in a file for the project, so you only have to set it up once.
Install
dotenv
. To load the.env
file, we first need to install thedotenv
package. In your terminal, run the following command from your project's root folder.npm install dotenv
Create the
.env
file. In the root of your project folder (my-hardhat-project
), create a new file named exactly.env
. You can do this manually in your file manager, or directly from your terminal.For macOS/Linux:
touch .env
For Windows (PowerShell):
New-Item .env
ℹ️ Files starting with a dot (.
) are hidden by default in macOS/Linux. In the file manager, you can press Cmd + Shift + .
to show/hide hidden files.
ℹ️ Windows sometimes hides file extensions. If your file is named .env.txt
, go to File Explorer > View tab > check the 'File name extensions' box. Then you can rename the file to remove the .txt
part.
Add Content to
.env
. Open the file and add the following, replacing the placeholder.PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY"
SEPOLIA_RPC_URL="RPC_URL"
ℹ️ Visit Routescan RPCs page for detailed information about all supported Chain IDs and it’s RPC URLs.
ℹ️ If you see an error like Error HH8: Invalid account... received undefined
, it means this file is misnamed, it’s path or content is incorrect. The log injecting env (0)
confirms that zero variables were loaded.
Option B. Using a Temporary Terminal Variable
This method sets your private key for your current terminal session only. If you close the terminal, you must run the command again. You do not need to install dotenv
or create a .env
file if you use this method.
Set the Environment Variable. Run the appropriate command for your operating system in the terminal.
For macOS/Linux
export PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY"
For Windows (PowerShell)
$env:PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY"
Set the RPC URL (if not in config). You will also need to set the RPC URL variable.
For macOS/Linux
export SEPOLIA_RPC_URL="RPC_URL"
For Windows (PowerShell)
$env:SEPOLIA_RPC_URL="RPC_URL"
3. Configure hardhat.config.js
(or .ts
)
This file tells Hardhat everything it needs to know. Replace its entire content with the appropriate version below.
JavaScript (hardhat.config.js
)
require("@nomicfoundation/hardhat-toolbox");
// IMPORTANT: If you are NOT using a .env file, you should remove the following line
require("dotenv").config();
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.30",
networks: {
routescan: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: [process.env.PRIVATE_KEY],
},
},
etherscan: {
apiKey: {
routescan: "ANY_STRING_WORKS", // API key is not required by Routescan
},
customChains: [
{
network: "routescan",
// Use the end goal Chain ID. In this case we are using Sepolia Chain ID.
chainId: 11155111,
urls: {
// This is the specific API endpoint for Routescan's Sepolia verifier. Make sure you change the request with either Testnet or Mainnet value and use the correct end goal Chain ID.
apiURL: "https://api.routescan.io/v2/network/testnet/evm/11155111/etherscan",
browserURL: "https://11155111.testnet.routescan.io/",
},
},
],
},
};
TypeScript (hardhat.config.ts
)
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
// IMPORTANT: If you are NOT using a .env file, you should remove the following line
import "dotenv/config";
const config: HardhatUserConfig = {
solidity: "0.8.30",
networks: {
routescan: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
},
},
etherscan: {
apiKey: {
routescan: "ANY_STRING_WORKS", // API key is not required by Routescan
},
customChains: [
{
network: "routescan",
chainId: 11155111,
urls: {
apiURL: "https://api.routescan.io/v2/network/testnet/evm/11155111/etherscan",
browserURL: "https://11155111.testnet.routescan.io/",
},
},
],
},
};
export default config;
Deploying and Verifying
1. Write the Deployment Module
Navigate to the
/ignition/modules
folder and modify the sample file (Lock.js
orLock.ts
) or create a new file (.js
or.ts
).Add the appropriate script below.
JavaScript (Deploy.js
)
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
module.exports = buildModule("GreeterModule", (m) => {
const initialGreeting = "Hello World!";
const greeter = m.contract("Greeter", [initialGreeting]);
return { greeter };
});
TypeScript (Deploy.ts
)
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const GreeterModule = buildModule("GreeterModule", (m) => {
const initialGreeting = "Hello World!";
const greeter = m.contract("Greeter", [initialGreeting]);
return { greeter };
});
export default GreeterModule;
2. Deploy the Contract
Run the Ignition deployment command from your terminal, targeting the network configured. Use the correct path for your .js
or .ts
file.
npx hardhat ignition deploy ./ignition/modules/Counter.js --network routescan "Hello World!"
You should use single quotes (' '
) for arguments with special characters on macOS/Linux to prevent the shell from interpreting them incorrectly.
The output will show the deployment status and, upon completion, will log the deployed contract address. Copy the contract address.
3. Verify the Contract
Block explorers might often find a 'Partial Match' automatically. To get an 'Exact
Match,' you must verify manually.
The command structure is
npx hardhat verify --network <network-name> <contract-address> <constructor-args>
.Use the address you copied and the same constructor arguments from your deployment module, if any.
npx hardhat verify --network routescan <YOUR_DEPLOYED_ADDRESS> "Hello World!"
ℹ️ If you see an error like The contract ... has already been verified
, add the --force
flag at the end of your command.
npx hardhat verify --network routescan --force <YOUR_DEPLOYED_ADDRESS> "Hello World!"
After a successful run, the status on Routescan Contracts Tab will update to Contract Source Code Verified (Exact Match).
Practical Tips
To deploy or verify with complex constructor arguments, create a
constructor-arg.js
or.ts
file in project root directory and add this at the end of your command:--constructor-args constructor-args.js
. Example arguments file:module.exports = [
"My Token", // string
"MTK", // string
1000000, // number
"0x1234567890123456789012345678901234567890" // address
];Before deploying to a live network, you can always run your tests using
npx hardhat test
. This ensures your contract logic is correct and can save you from costly mistakes.Mismatch between the network name in the
networks
block and theetherscan.customChains
block can cause verification to fail. Always ensure these names are identical.If you accidentally leave off a closing quote (
"
or'
) when typing a command, your terminal will wait for more input (showing adquote>
or>>
prompt). If you get stuck, pressCtrl + C
to cancel the command and start over.If verification fails and the error message is unclear, run the command again with the
--verbose
flag. This provides detailed logs that can help you diagnose the issue.npx hardhat verify --verbose --network ...
If your project has multiple contracts, Hardhat might not know which one to verify. Use the
--contract
flag to specify the exact contract path and name.npx hardhat verify --contract contracts/MyContract.sol:MyContract ...
Hardhat automatically compiles changes to
.sol
files, but not always tohardhat.config.js
or.ts
. If you change the compiler version or optimizer settings, force a re-compile to ensure the bytecode is updated.npx hardhat compile --force