🤔 Got questions? Schedule an office hours session.
Virtual TestNets
CI/CD
Hardhat & Github Actions

Setting up Github Actions with Hardhat

Learn how to configure a GitHub Action to set up a Continuous Integration and Continuous Deployment pipeline (CI/CD) with Hardhat on Virtual TestNets.

Use the Tenderly Virtual TestNet Setup to enable automated builds that test your contracts. After successful testing, you can stage them for the rest of your team by deploying them the provisioned Virtual Testnet.

In this guide, you need to complete two stages:

  1. Local setup: Set up Hardhat and create and test the workflow file.
  2. Github setup: Set up a GitHub Action, configure GitHub secrets and variables, and test the build.

For reference, use this example project:

This guide demonstrates a CI setup that relies on Hardhat-ignition. For hardhat-verify setup, the process and configuration are similar.

Tenderly Docs
Continuous integration (CI) and continous deployment (CD) with Virtual TestNets

Local setup

First, we’ll install necessary dependencies and create a workflow file.

Install dependencies

Make sure you have the following package installed to get automatic contract verification:

@tenderly/hardhat-tenderly

Create the workflow file

To set up the GitHub Action, create a new workflow:

mkdir -p .github/workflows
touch ci-cd.yaml

Next, paste the following yaml file that configures two steps:

  • test step to run hardhat tests using test:vnet command defined in package.json. This step uses a fresh testnet to run tests against, and pauses the testnet after completion, taking it to read-only mode.
  • deploy step to deploy contracts using deploy:vnet command defined in package.json. This step creates a fresh testnet, stages contracts to it, and keeps it alive for further development.

The mode argument takes values CI and CD.

  • The CD mode keeps the Virtual TestNet active and you can work with deployed contracts.
  • The CI mode pauses the Virtual TestNet after the step completes. You’ll be able to inspect transactions but won’t be able to send further RPC requests.
ci.yaml
name: Hardhat CI/CD
 
on: [push, pull_request]
env:
  DEBUG: '@tenderly/github-action'
  ## Needed available as env variables for hardhat.config.js
  TENDERLY_PROJECT_NAME: ${{ vars.TENDERLY_PROJECT_NAME }}
  TENDERLY_ACCOUNT_NAME: ${{ vars.TENDERLY_ACCOUNT_NAME }}
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
 
      - name: Setup Virtual TestNet
        uses: Tenderly/vnet-github-action@V1.0.11
        with:
          mode: CI
          access_key: ${{ secrets.TENDERLY_ACCESS_KEY }}
          project_name: ${{ vars.TENDERLY_PROJECT_NAME }}
          account_name: ${{ vars.TENDERLY_ACCOUNT_NAME }}
          testnet_name: "Testing"
          network_id: 1
          chain_id: 1
          state_sync: true
          public_explorer: true
          verification_visibility: bytecode
 
      - name: Install dependencies
        run: npm install
 
      - name: Run Tests
        run: npm run test:vnet
 
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
 
      - name: Setup Virtual TestNet
        uses: Tenderly/vnet-github-action@V1.0.11
        with:
          mode: CD
          access_key: ${{ secrets.TENDERLY_ACCESS_KEY }}
          project_name: ${{ vars.TENDERLY_PROJECT_NAME }}
          account_name: ${{ vars.TENDERLY_ACCOUNT_NAME }}
          testnet_name: "Staging"
          network_id: 1
          chain_id: 1 # custom chain ID
          state_sync: true
          public_explorer: true
          verification_visibility: bytecode
 
      - name: Install dependencies
        run: npm install
 
      - name: Deploy Contracts
        run: npm run deploy:vnet

Prepare environment variables

For local testing, set up an .env file using this template:

## Virtual TestNet config
## Paste your local development version
TENDERLY_CHAIN_ID=...
TENDERLY_ADMIN_RPC_URL=...

## Access parameters
TENDERLY_ACCESS_KEY=...
TENDERLY_PROJECT_NAME=...
TENDERLY_ACCOUNT_NAME=...
  • TENDERLY_CHAIN_ID: The Chain ID assigned to the Virtual TestNet you created.
  • TENDERLY_ADMIN_RPC_URL: The Admin RPC of your Virtual TestNet.
  • TENDERLY_ACCESS_KEY: Learn how to get your access key.
  • TENDERLY_ACCOUNT_NAME and TENDERLY_PROJECT_NAME: Follow a few steps to get your account and project name.

Configure Hardhat

Add the Virtual TestNet configuration to hardhat.config.ts:

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@tenderly/hardhat-tenderly";
 
import * as dotenv from 'dotenv';
dotenv.config();
 
const config: HardhatUserConfig = {
  solidity: "0.8.27",
  networks: {
    tenderly_ci: {
      url: process.env.TENDERLY_ADMIN_RPC_URL,
      chainId: parseInt(process.env.TENDERLY_CHAIN_ID!)
    },
  },
  etherscan: {
    apiKey: {
      tenderly_ci: process.env.TENDERLY_ACCESS_KEY!
    },
    customChains: [
      {
        network: "tenderly_ci",
        chainId: parseInt(process.env.TENDERLY_CHAIN_ID!),
        urls: {
          apiURL: `${process.env.TENDERLY_ADMIN_RPC_URL}/verify/etherscan`,
          browserURL: process.env.TENDERLY_ADMIN_RPC_URL!
        }
      }
    ]
  },
  tenderly: {
    project: process.env.TENDERLY_PROJECT_NAME!,
    username: process.env.TENDERLY_ACCOUNT_NAME!,
    accessKey: process.env.TENDERLY_ACCESS_KEY!
  },
  sourcify: {
    enabled: false
  }
};

Add test and deploy scripts

Extend package.json with the following scripts that your workflow uses to test and stage contracts on the tenderly_ci network.

{
  "scripts": {
    "deploy:vnet": "yes | npx hardhat ignition deploy ./ignition/modules/Counter.js --network tenderly_ci --deployment-id deploy",
    "test:vnet": "npx hardhat test --network tenderly_ci"
  }
 }

Test the build locally

To test the build locally, run the following command:

npx hardhat test:vnet
npx hardhat deploy:vnet

Test the Github Action locally

To test your action locally, you can use Act, the local task runner for GitHub Actions.

act --secret-file .env --var-file .env

Github Setup

After successful local setup, proceed by configuring Github with environment variables and necessary secrets.

Configure secrets and environment variables in GitHub

You must configure the following Github variables and one secret. Use the values from the .env file you’ve set up previously.

  • TENDERLY_PROJECT_NAME as a variable
  • TENDERLY_ACCOUNT_NAME as a variable
  • TENDERLY_ACCESS_KEY as a secret

You can achieve this via the GitHub UI or using the gh command line:

source .env
 
gh variable set TENDERLY_PROJECT_NAME --body ${TENDERLY_PROJECT_NAME}
gh variable set TENDERLY_ACCOUNT_NAME --body ${TENDERLY_ACCOUNT_NAME}
gh secret set TENDERLY_ACCESS_KEY --body ${TENDERLY_ACCESS_KEY}

Push the updates

To see the GitHub Action run, simply push the updates:

git add .
git commit -m "Adds CI/CD workflow"
git push