DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Mahesh
Mahesh

Posted on

Deploy and Verify smart contracts using Hardhat

For this tutorial we are going to deploy 3 smart contracts:

  1. basic contract
  2. erc20 contract
  3. erc721 contract

Setup project

Install hardhat

npm init -y     
npm install hardhat   
Enter fullscreen mode Exit fullscreen mode

start hardhat project

  1. Start project with npx hardhat.
  2. Choose 'Create a basic sample project' from options
  3. Download dependencies asked for download, if you missed them, then download them by running npm install @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

Add contracts to deploy

Inside contracts directory put the the contracts you want to deploy.
I am putting 3 contracts as:

1. Basic contract (contracts/Greeter.sol)

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Greeter {
    string private greeting;

    constructor(string memory _greeting) {
        console.log("Deploying a Greeter with greeting:", _greeting);
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }
}
Enter fullscreen mode Exit fullscreen mode

2. erc20 contract (contracts/PopatToken.sol)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Popat is ERC20, ERC20Burnable, Ownable {
    constructor() ERC20("Popat", "PT") {}

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}
Enter fullscreen mode Exit fullscreen mode

As we are importing openzepplin files in our contract, hence we are going to install the OpenZepplin npm package in our project as:

npm install @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

3. erc721 contract (contract/PikachuNFT.sol)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Pikachu is ERC721, ERC721Burnable, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("Pikachu", "PK") {}

    function safeMint(address to) public onlyOwner {
        _safeMint(to, _tokenIdCounter.current());
        _tokenIdCounter.increment();
    }
}
Enter fullscreen mode Exit fullscreen mode

Modify hardhat configuration (hardhat.confi.js):

1. Configure solidity versions

Hardhat supports projects that use different, incompatible versions of solc. You can configure Hardhat to use compiler versions compatible with those files like this:

module.exports = {
  solidity: {
    compilers: [
      {
        version: "0.8.4",
      },
      {
        version: "0.6.7",
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This setup means that a file with a pragma solidity ^0.8.0, ^0.8.1, ..., ^0.8.4 will be compiled with solc 0.8.4 and a file with a pragma solidity ^0.6.0, ..., ^0.6.7 will be compiled with solc 0.6.7

2. Define network configuration

module.exports = {
defaultNetwork: 'rinkeby',
  networks: {
    rinkeby: {
      url: // NETWORK_ENDPOINT_RINKEBY,
      accounts: // [ACCOUNT_0_PRIVATE_KEY, ACCOUNT_1_PRIVATE_KEY]
    }
    namedAccounts: {
      account0: 0,
      account1: 1
    }
  }
Enter fullscreen mode Exit fullscreen mode

In each network you define, put node-endpoint-url through which you are going to connect with the network as the value of url field, the most popular endpoint provider is: https://infura.io/
Get the private keys of the accounts from which you want to deploy the contract and put them as array as value of accounts field
In namedAccounts field, you have to define name of account, which you will use while writing deploy code. Value of each account-name is the index-of-account's private-key inside <network-name>.accounts: [] array

3. Define ethereum-api key

module.exports = {
  solidity: {
  etherscan: {
    apiKey: // ETHERSCAN_API_KEY
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Import npm packages required in config file

require("@nomiclabs/hardhat-waffle");
require('hardhat-deploy');
Enter fullscreen mode Exit fullscreen mode

The final configuration file on is look like this:

// hardhat.config.js
require("@nomiclabs/hardhat-waffle");
require('hardhat-deploy');
require('dotenv').config()

module.exports = {
  solidity: {
    compilers: [
      {
        version: "0.8.4"
      }
    ]
  },
  defaultNetwork: 'rinkeby',
  networks: {
    rinkeby: {
      url: process.env.NETWORK_ENDPOINT_RINKEBY,
      accounts: [process.env.ACCOUNT_0_PRIVATE_KEY, process.env.ACCOUNT_1_PRIVATE_KEY]
    }
  },
  namedAccounts: {
    account0: 0,
    account1: 1
  },
  etherscan: {
    apiKey: process.env.ETHERSCAN_API_KEY
  }
};
Enter fullscreen mode Exit fullscreen mode

You have to install dotenv package to use environment variables as used in above file, else you can also put values directly if your repository is not public.

Deploy Contracts

Install hardhat-deploy plugin as npm install hardhat-deploy and import it inside 'hardhat.config.js' file as:

require('hardhat-deploy')
Enter fullscreen mode Exit fullscreen mode

Put all deploy scripts under 'deploy' folder so deploy-plugin can detect and execute them

1. Deploy script for Basic contract

// deploy/0_greeter.js
module.exports = async ({getNamedAccounts, deployments}) => {
    const {deploy} = deployments;
    const {account0, account1} = await getNamedAccounts();

    await deploy('Greeter', {
      from: account0,
      args: ['Mahesh is Learning HardHat!'],
      log: true,
    });
  };
Enter fullscreen mode Exit fullscreen mode

2. Deploy script for erc20 cotract

// deploy/1_popatToken.js
module.exports = async({getNamedAccounts, deployments}) => {
    const { deploy } = deployments
    const { account0 } = await getNamedAccounts()
    await deploy('Popat', {
        from: account0,
        args: [],
        log: true
    })
}
Enter fullscreen mode Exit fullscreen mode

3. Deploy script for erc721 contract

// deploy/2_pikchuNFT.js
module.exports = async ({deployments, getNamedAccounts}) => {
    const {deploy} = deployments
    const {account0} = await getNamedAccounts()
    await deploy('Pikachu', {
        from: account0,
        args: [],
        log: true
    })
}
Enter fullscreen mode Exit fullscreen mode

As you can see deploy scripts are same for all type of cotracts, the deploy scripts finds the contract by looking at the name of cotract defined inside .sol file.
E.g. In Greeter.sol file we defined contract name as contract Greeter {, hence we are using that same contract name inside deploy script as await deploy('Greeter', {

Now, we are ready for the deployment and verification πŸ‘

Deploy contracts

npx hardhat deploy    # deploy contracts to defaulNetwork
# OR
npx hardhat --network rinkeby deploy   # deploy contrats to a specific network
Enter fullscreen mode Exit fullscreen mode

Vefify Contracts

npx hardhat etherscan-verify
Enter fullscreen mode Exit fullscreen mode

You can see the progress and contract-addresses in the console when you execute above deploy and vefication commands.

My simple tutorial ends here. Happy Coding ! 😊

Top comments (0)

Hacktoberfest is happening now!


It is a month-long celebration of open source. For a lot of devs, its their introduction to open source.



Check out the Hacktoberfest tag on DEV to keep up with the latest!