Skip to content

Latest commit

 

History

History
370 lines (251 loc) · 9.34 KB

Hardhat.md

File metadata and controls

370 lines (251 loc) · 9.34 KB

HARDHAT️‍🔥

  • 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

INSTALLING HARDHAT AND INITIALLIZING PROJECT

npm init
npm install --save-dev hardhat
npx hardhat init
npm i --save @openzeppelin/contracts
npm install --save-dev @nomicfoundation/hardhat-toolbox

Compiling contracts

  • Start by creating a new directory called contracts and create a file inside the directory called Web3.sol
npx hardhat compile

Testing contracts

*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
Reusing common test setups with fixtures
  • 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!!!)

Debugging with Hardhat Network

  • 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.

Store your API_KEY

  • 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,
  },
};

Deploying to a live network (COMMANDS)

  • 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.

WRITING DEPLOY SCRIPT (Creating Ignition Modules)

  • 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;

Verify Smart Contract on etherscan

// 1.
npx hardhat verify --network sepolia DEPLOYED_CONTRACT_ADDRESS
// 2.
npx hardhat ignition verify sepolia-deployment

INSTALL .env DIRECTORY

npm install dotenv --save

const infuraAPIKey = import.meta.env.INFURA_API_KEY;

CONNECT WALLET BTN FUNCTION (FRONTEND)

'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;