Skip to main content

Flow Hardhat Guide

Hardhat is an Ethereum development tool designed to facilitate the deployment, testing, and debugging of smart contracts. It provides a streamlined experience for developers working with Solidity contracts.

Prerequisites

Node

Node v18 or higher, available for download here.

For those new to Hardhat, we recommend exploring the official documentation to get acquainted. The following instructions utilize npm to initialize a project and install dependencies:

Wallet

You'll also need a wallet that supports EVM. For this guide, a MetaMask account and its corresponding private key will work.


_10
mkdir hardhat-example
_10
cd hardhat-example
_10
_10
npm init
_10
_10
npm install --save-dev hardhat
_10
_10
npx hardhat init

When prompted, select TypeScript and to use @nomicfoundation/hardhat-toolbox to follow along with this guide.

Fund Your Wallet

To deploy smart contracts, ensure your wallet has $FLOW. Obtain funds by navigating to the Flow Previewnet Faucet and entering your wallet address.

Deploying a Smart Contract with Hardhat

This section guides you through the process of deploying smart contracts on the Flow network using Hardhat.

Configuration

First, incorporate the Previewnet network into your hardhat.config.ts:


_15
import { HardhatUserConfig } from "hardhat/config";
_15
import "@nomicfoundation/hardhat-toolbox";
_15
_15
const config: HardhatUserConfig = {
_15
solidity: "0.8.24",
_15
networks: {
_15
previewnet: {
_15
url: "https://previewnet.evm.nodes.onflow.org",
_15
accounts: [`<PRIVATE_KEY>`], // In practice, this should come from an environment variable and not be commited
_15
gas: 500000, // Example gas limit
_15
}
_15
}
_15
};
_15
_15
export default config;

To keep this example straightforward, we've included the account's private key directly in hardhat.config.ts. However, it is crucial to avoid committing private keys to your Git repository for security reasons. Instead, opt for using environment variables for safer handling of sensitive information.

Deploying HelloWorld Smart Contract

HelloWorld Smart Contract


_25
// SPDX-License-Identifier: MIT
_25
pragma solidity ^0.8.0;
_25
_25
contract HelloWorld {
_25
// Declare a public field of type string.
_25
string public greeting;
_25
_25
// Constructor to initialize the greeting.
_25
// In Solidity, the constructor is defined with the "constructor" keyword.
_25
constructor() {
_25
greeting = "Hello, World!";
_25
}
_25
_25
// Public function to change the greeting.
_25
// The "public" keyword makes the function accessible from outside the contract.
_25
function changeGreeting(string memory newGreeting) public {
_25
greeting = newGreeting;
_25
}
_25
_25
// Public function that returns the greeting.
_25
// In Solidity, explicit return types are declared.
_25
function hello() public view returns (string memory) {
_25
return greeting;
_25
}
_25
}

Deploying:

  1. Create a file named HelloWorld.sol under contracts directory.
  2. Add above HelloWorld.sol contract code to new file.
  3. Create a deploy.ts file in scripts directory.
  4. Paste in the following TypeScript code.

_18
import { ethers } from "hardhat";
_18
_18
async function main() {
_18
const [deployer] = await ethers.getSigners();
_18
_18
console.log("Deploying contracts with the account:", deployer.address);
_18
_18
const deployment = await ethers.deployContract("HelloWorld");
_18
_18
console.log("HelloWorld address:", await deployment.getAddress());
_18
}
_18
_18
main()
_18
.then(() => process.exit(0))
_18
.catch((error) => {
_18
console.error(error);
_18
process.exit(1);
_18
});

  1. Run npx hardhat run scripts/deploy.ts --network previewnet in the project root.
  2. Copy the deployed HelloWorld address. This address will be used in other scripts.

Output should look like this (with the exception that your address will be different):


_10
❯ npx hardhat run scripts/deploy.ts --network previewnet
_10
Deploying contracts with the account: ...
_10
HelloWorld address: 0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb

Get HelloWorld Contract Greeting

Now, we want to get the greeting from the deployed HelloWorld smart contract.


_19
import { ethers } from "hardhat";
_19
import HelloWorldABI from "../artifacts/contracts/HelloWorld.sol/HelloWorld.json";
_19
_19
async function main() {
_19
// Replace with your contract's address
_19
const contractAddress = "0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb";
_19
// Get hardhat provider
_19
const provider = ethers.provider;
_19
// Create a new contract instance
_19
const helloWorldContract = new ethers.Contract(contractAddress, HelloWorldABI.abi, provider);
_19
// Call the greeting function
_19
const greeting = await helloWorldContract.hello();
_19
console.log("The greeting is:", greeting);
_19
}
_19
_19
main().catch((error) => {
_19
console.error(error);
_19
process.exit(1);
_19
});

Steps:

  1. Create a getGreeting.ts file in the scripts directory.
  2. Paste contents of script above. Make sure to update the contract address with the one from deployment in earlier step.
  3. Call script to get the greeting, npx hardhat run scripts/getGreeting.ts --network previewnet
  4. The output should be as follows:

_10
❯ npx hardhat run scripts/getGreeting.ts --network previewnet
_10
The greeting is: Hello, World!

Update Greeting on HelloWorld Smart Contract

Next, we'll add a script to update the greeting and log it.


_34
import { ethers } from "hardhat";
_34
import HelloWorldABI from "../artifacts/contracts/HelloWorld.sol/HelloWorld.json";
_34
_34
async function main() {
_34
const contractAddress = "0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb";
_34
_34
const newGreeting = process.env.NEW_GREETING;
_34
if (!newGreeting) {
_34
console.error("Please set the NEW_GREETING environment variable.");
_34
process.exit(1);
_34
}
_34
_34
// Signer to send the transaction (e.g., the first account from the hardhat node)
_34
const [signer] = await ethers.getSigners();
_34
_34
// Contract instance with signer
_34
const helloWorldContract = new ethers.Contract(contractAddress, HelloWorldABI.abi, signer);
_34
_34
console.log("The greeting is:", await helloWorldContract.hello());
_34
_34
// Create and send the transaction
_34
const tx = await helloWorldContract.changeGreeting(newGreeting);
_34
console.log("Transaction hash:", tx.hash);
_34
_34
// Wait for the transaction to be mined
_34
await tx.wait().catch((error: Error) => {});
_34
console.log("Greeting updated successfully!");
_34
console.log("The greeting is:", await helloWorldContract.hello());
_34
}
_34
_34
main().catch((error) => {
_34
console.error(error);
_34
process.exit(1);
_34
});

Here are the steps to follow:

  1. Create an updateGreeting.ts script in the scripts directory.
  2. Paste in the TypeScript above, make sure to update the contract address with the one from deployment in earlier step.
  3. Call the new script, NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network previewnet
  4. The output should be

_10
❯ NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network previewnet
_10
The greeting is: Hello, World!
_10
Transaction hash: 0x03136298875d405e0814f54308390e73246e4e8b4502022c657f04f3985e0906
_10
Greeting updated successfully!
_10
The greeting is: Howdy!

info

Coming Soon

  • Comprehensive Guides: Step-by-step tutorials on deploying various types of smart contracts, including NFTs (ERC-721), using Hardhat on the Flow network.
  • Requirements: Detailed prerequisites for using Hardhat with EVM on Flow, including Node.js setup, wallet preparation, and obtaining testnet FLOW for gas fees.
  • Verification and Interaction: Steps to verify deployment of your smart contracts and interact with them using tools like Flowdiver.

Stay tuned for updates and feel free to check back soon for the full guide.