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.
_10mkdir hardhat-example_10cd hardhat-example_10_10npm init_10_10npm install --save-dev hardhat_10_10npx 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
:
_15import { HardhatUserConfig } from "hardhat/config";_15import "@nomicfoundation/hardhat-toolbox";_15_15const 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_15export 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_25pragma solidity ^0.8.0;_25_25contract 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:
- Create a file named
HelloWorld.sol
undercontracts
directory. - Add above
HelloWorld.sol
contract code to new file. - Create a
deploy.ts
file inscripts
directory. - Paste in the following TypeScript code.
_18import { ethers } from "hardhat";_18_18async 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_18main()_18 .then(() => process.exit(0))_18 .catch((error) => {_18 console.error(error);_18 process.exit(1);_18 });
- Run
npx hardhat run scripts/deploy.ts --network previewnet
in the project root. - 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_10Deploying contracts with the account: ..._10HelloWorld address: 0x3Fe94f43Fb5CdB8268A801f274521a07F7b99dfb
Get HelloWorld Contract Greeting
Now, we want to get the greeting from the deployed HelloWorld
smart contract.
_19import { ethers } from "hardhat";_19import HelloWorldABI from "../artifacts/contracts/HelloWorld.sol/HelloWorld.json";_19_19async 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_19main().catch((error) => {_19 console.error(error);_19 process.exit(1);_19});
Steps:
- Create a
getGreeting.ts
file in thescripts
directory. - Paste contents of script above. Make sure to update the contract address with the one from deployment in earlier step.
- Call script to get the greeting,
npx hardhat run scripts/getGreeting.ts --network previewnet
- The output should be as follows:
_10❯ npx hardhat run scripts/getGreeting.ts --network previewnet_10The greeting is: Hello, World!
Update Greeting on HelloWorld Smart Contract
Next, we'll add a script to update the greeting and log it.
_34import { ethers } from "hardhat";_34import HelloWorldABI from "../artifacts/contracts/HelloWorld.sol/HelloWorld.json";_34_34async 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_34main().catch((error) => {_34 console.error(error);_34 process.exit(1);_34});
Here are the steps to follow:
- Create an
updateGreeting.ts
script in thescripts
directory. - Paste in the TypeScript above, make sure to update the contract address with the one from deployment in earlier step.
- Call the new script,
NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network previewnet
- The output should be
_10❯ NEW_GREETING='Howdy!' npx hardhat run ./scripts/updateGreeting.ts --network previewnet_10The greeting is: Hello, World!_10Transaction hash: 0x03136298875d405e0814f54308390e73246e4e8b4502022c657f04f3985e0906_10Greeting updated successfully!_10The greeting is: Howdy!
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.