- artifacts folder in our root directory contains our smart contract abi array.
- Hardhat comes built-in with Hardhat Network, a local Ethereum network designed for development.
- It allows you to deploy your contracts, run your tests and debug your code, all within the confines of your local machine
npm init
npm install --save-dev hardhat
npx hardhat init
npm i --save @openzeppelin/contracts
npm install --save-dev @nomicfoundation/hardhat-toolbox
- Start by creating a new directory called contracts and create a file inside the directory called Web3.sol
npx hardhat compile
*Note : Hardhat will run every .js file in test/
- Start the testing using following command :
npx hardhat test
- We will use Ether.js and Mocha-Chai for our testing.
- Mocha-Chai is popular JavaScript assertion library
// installing ether.js and mocha
npm install ethers
npm install --save-dev mocha
- Create a new directory called test inside our project root directory and create a new file in there called web3test.js
- To test our contract, we are going to use Hardhat Network, a local Ethereum network designed for development.
// test/web3test.cjs
const { expect } = require("chai");
const { ethers } = require("hardhat");
const assert = require('assert');
describe("Token contract", function () {
// first test check
it("Deployment should assign the total supply of tokens to the owner", async function () {
// RETURNS THE ETHEREUM ACCOUNT.
// And, this account will execute the deployment
const [owner, addr1, addr2] = await ethers.getSigners();
// start the deployment of our token contract
// return a Promise that resolves to a Contract
// This is the contract instance that has a method for each of your smart contract functions.
const token = await ethers.deployContract("Web3");
// Once contract is deployed, we can call contract methods using contract instance(token).
// the account in the owner variable executed the deployment,
const ownerBalance = await token.balanceOf(owner.address);
// Here, the token's supply amount and we're checking that it's equal to ownerBalance, as it should be.
// We have used expect(...) function from Mocha-chai to compare
expect(await token.totalSupply()).to.equal(ownerBalance);
// either we can use expect(...) method or assert.equal(...) method
assert.equal(await toke.totalSupply(), ownerBalance);
});
});
- Above, we can use either use expect(...) or assert.equal(...) method
-
This setup could involve multiple deployments and other transactions.
-
Doing that in every test means a lot of code duplication.
-
Plus, executing many transactions at the beginning of each test can make the test suite much slower.
-
To can avoid code duplication and improve the performance of your test suite we can use fixtures
-
A fixture is a setup function that is run only the first time it's invoked.
-
On every invocationshardhat will reset the state of networks.
const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers");
const { expect } = require("chai");
const assert = require('assert');
const { ethers } = require("hardhat");
describe("Token contract", function () {
// deploy token fixtures
async function deployTokenFixture() {
const [owner, addr1, addr2] = await ethers.getSigners();
const token = await ethers.deployContract("Token");
await token.waitForDeployment();
// Fixtures can return anything you consider useful for your tests
return { token, owner, addr1, addr2 };
}
// first test check
it("Deployment should assign the total supply of tokens to the owner", async function () {
// for every test now we can use fixture instead of redepolying contract.
const { token, owner } = await loadFixture(deployTokenFixture);
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
assert.equal(await token.name(), "ERC721");
});
// second test check
it("Test function", async function (){
const { token, owner } = await loadFixture(deployTokenFixture);
// ...rest of code
})
});
Note : To know more testing methods read hardhat testing docs (you know!!!)
- For debugging we will use console.log() in soilidity similar to JS.
- you can print logging messages and contract variables from your Solidity code.
// contract/Web3.sol
import "hardhat/console.sol";
contract TestContract {
// some code ...
function publicMint() public payable{
console.log("Amount transfer :", msg.value);
// rest of code...
}
}
- Lastly, run npx hardhat test to seee the result of debugging in terminal.
- We will store our API_KEY, private_key and seed-phrase in var and not in
.env || .env.local
// RUN THE FOLLOWING COMMAND TO STORE THE KEY IN VAR
npx hardhat vars set API_KEY_NAME
hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
const { vars } = require("hardhat/config");
const INFURA_API_KEY = vars.get("INFURA_API_KEY");
const PRIVATE_KEY = vars.get("PRIVATE_KEY");
module.exports = {
solidity: "0.8.28",
networks: {
sepolia: {
url: `https://sepolia.infura.io/v3/${INFURA_API_KEY}`,
accounts: [PRIVATE_KEY],
},
},
etherscan: {
apiKey: vars.get("ETHERSCAN_ID"),
},
sourcify: {
enabled: true,
},
};
- To deploy our smart contract use following code:
// THIS INITITALIZE AN RPC SERVER
npx hardhat node
// DEPLOYING ON LOCALHOST
npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network localhost
// DEPLOYING ON TESTNET
npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network sepolia
- In Hardhat, deployments are defined through Ignition Modules.
- Ignition modules are written inside ./ignition/modules directory.
- Now to deploy our smart contract we will initialize an RPC server locally in our terminal(port:8545)
- We need to modify our hardhat.config.js file.
./ignition/modules/deploy.cjs
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
const TokenModule = buildModule("TokenModule", (m) => {
const token = m.contract("Web3");
return { token };
});
module.exports = TokenModule;
// 1.
npx hardhat verify --network sepolia DEPLOYED_CONTRACT_ADDRESS
// 2.
npx hardhat ignition verify sepolia-deployment
npm install dotenv --save
const infuraAPIKey = import.meta.env.INFURA_API_KEY;
'use client';
import React from 'react'
import { ConnectWalletBtn } from '../components/ConnectWalletBtn';
import ToDoList from '../components/ToDoList';
import { Web3 } from 'web3';
import address from "../config";
import abi from "../abi";
const Home = () => {
const [correctNet, setCorrectNet] = React.useState(false);
const [loggedIn, setLoggedIn] = React.useState(false);
const [currentAcc, setCurrentAcc] = React.useState('');
const [input, setInput] = React.useState('');
const [task,setTasks] = React.useState([]);
const web3 = new Web3('https://sepolia.infura.io/v3/6e2aaaa2ff0c4e00995a96624cca8e7a');
// INITIALIZE THE SMART CONTRACT
const token = new web3.eth.Contract(abi.abi, address.contractAddress);
// CONNECT WALLET BTN FUNCTION
const ConnectWallet = async ()=>{
try {
const {ethereum} = window;
if(!ethereum){
console.log("MetaMask not detected!!!");
return;
}
const chainId = await ethereum.request({method:"eth_chainId"});
console.log("connected to chain id : ", chainId);
const sepoliaChainId = "0xaa36a7";
if(chainId != sepoliaChainId){
alert("You are not connected to sepolia test network!!!")
setCorrectNet(false);
return;
}
setCorrectNet(true);
const accounts = await ethereum.request({method:"eth_requestAccounts"});
console.log("account address detected : ", accounts[0]);
setLoggedIn(true);
setCurrentAcc(accounts[0]);
} catch (error) {
console.log(error);
}
}
// ADD TASK FUNCTION
const AddTask = async ()=>{
try {
const {ethereum} = window;
if(ethereum){
await token.methods.addTask(input).send({from:currentAcc})
.then(res=>{
setTasks(...task,task);
console.log("Task added");
})
.catch(error =>{
console.log(error);
})
} else{
console.log("Eth does not exist");
}
} catch (error) {
console.log(error);
}
}
return (
<div>
<div>
<h1 className='btn btn-primary m-4'>{currentAcc}</h1>
{
loggedIn ? <ToDoList setInput={setInput} AddTask={AddTask}></ToDoList> : <ConnectWalletBtn ConnectWallet={ConnectWallet} />
}
</div>
</div>
)
}
export default Home;