Building Smart Contract with Hardhat

Before deploying a smart contract to any network, you write it in Solidity, compile it with Hardhat, and test it locally. Hardhat gives you a local blockchain, a testing framework, and deployment scripts all in one package.

TL;DR: Write, compile, and test a Solidity smart contract using Hardhat with a local blockchain network.
Stack: Hardhat, Solidity, Node.js, Ethers.js
Level: Intermediate
Reading time: ~10 min

Initialize the project

npx hardhat

Choose “Create a TypeScript project” and install the sample project dependencies.

Create the smart contract

Create a file in the contracts/ folder, for example contracts/HelpChains.sol:

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

contract HelpChains {
    address payable public address1 = payable(0xAAAA1111BBBB2222CCCC3333DDDD4444EEEE5555);
    address payable public address2 = payable(0xBBBB2222CCCC3333DDDD4444EEEE5555FFFF6666);

    struct Transaction {
        uint256 amount;
        address from;
        address to;
        uint256 timestamp;
    }

    Transaction[] public donations;
    Transaction[] public withdrawals;

    function donate() external payable {
        require(msg.value > 0, "Value must be greater than 0");
        uint256 half = msg.value / 2;
        address1.transfer(half);
        address2.transfer(msg.value - half);
        donations.push(Transaction({
            amount: msg.value,
            from: msg.sender,
            to: address(0),
            timestamp: block.timestamp
        }));
    }

    function withdraw() external {
        uint256 balance1 = address1.balance;
        uint256 balance2 = address2.balance;
        require(balance1 > 0 || balance2 > 0, "No balance to withdraw");
        if (balance1 > 0) {
            address1.transfer(balance1);
            withdrawals.push(Transaction({amount: balance1, from: address1, to: address1, timestamp: block.timestamp}));
        }
        if (balance2 > 0) {
            address2.transfer(balance2);
            withdrawals.push(Transaction({amount: balance2, from: address2, to: address2, timestamp: block.timestamp}));
        }
    }

    function getDonations() external view returns (Transaction[] memory) { return donations; }
    function getWithdrawals() external view returns (Transaction[] memory) { return withdrawals; }
}

Compile

npx hardhat compile

Configure ignition module

// ignition/modules/HelpChainsModule.ts
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

const HelpChainsModule = buildModule("HelpChainsModule", (m) => {
    const helpChains = m.contract("HelpChains", []);
    return { helpChains };
});

export default HelpChainsModule;

Configure local network (hardhat.config.ts)

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
    solidity: "0.8.24",
    defaultNetwork: "local",
    networks: {
        local: {
            url: "http://127.0.0.1:8545",
            chainId: 31337,
            accounts: {
                mnemonic: "test test test test test test test test test test test junk"
            }
        }
    }
};

export default config;

Run locally and deploy

# Start local node
npx hardhat node

# Deploy (add to package.json scripts first)
# "deploy:dev": "npx hardhat ignition deploy ignition/modules/HelpChains.ts --network local"
npm run deploy:dev

Interact via console

npx hardhat console --network local

const HelpChains = await ethers.getContractFactory("HelpChains");
const helpChains = await HelpChains.attach("DEPLOYED_CONTRACT_ADDRESS");
await helpChains.donate({ value: ethers.parseEther("1.0") });
let donations = await helpChains.getDonations();
console.log(donations);

What you’ve built

A Hardhat project with a compiled Solidity contract, a working local deployment script, and the tools to interact with it via console.

Next steps

  • Write tests that cover edge cases and failure conditions, not just the happy path. Smart contract bugs cannot be patched after deployment.
  • Use Hardhat’s console.sol for debugging during development: import “hardhat/console.sol” lets you print variables to the terminal from Solidity.
  • Run npx hardhat coverage to see which lines of your Solidity code are covered by tests before deploying to a testnet.

Questions or feedback? Find me on LinkedIn or GitHub.

Leave a Comment