Opinionated Forge Guide

I couldn’t find a complete project setup guide with forge that satisfied my use-case so I made one. And by complete I mean one that does:

  • Build, test, debug
  • Deployment
  • Verification

Here’s an opinionated guide on how to setup forge, its roughly the same as the github readme and exists here solely as an SEO funnel.


Uses vscode + eslint plugin + solidity plugin.

I prefer to write the deployment scripts as programatically as possible on a widely supported language as every deployment itself is unique, and sometimes there’s inter-dependencies between contracts.

For example:

  1. Deploy contract A
  2. Deploy contract B
  3. Call addOwner on B with A’s address

This is overly tricky with bash for me, and would require a LOT of unreadable (and error-prone) commands-line composition with jq and awk to achieve that with the default forge create.


Building and testing

forge build
forge test

# forking from existing state
# -vvv = very very verbose
forge test -f -vvv

# To access the debugger
forge run --debug src/test/Contract.t.sol --sig "testExample()"

Contract Deployment

Copy .env.example to .env and fill it out with correct details.

node --experimental-json-modules scripts/deploy.js

Etherscan Verification

Forge has a very useful in-built etherscan verification utility.

# For list of compiler versions https://etherscan.io/solcversions
forge verify-contract --compiler-version v0.8.12+commit.f00d7308 [CONTRACT ADDRESS] --constructor-args <ARGS> --num-of-optimizations 200 [CONTRACT_PATH:CONTRACT_NAME] [ETHERSCAN_API_KEY]

# To check verification status
forge verify-check [GUID] [ETHERSCAN_KEY]

If you have multiple contracts with pragma abiencoderv2, there is a possibility that you can’t verify your contracts on etherscan, there’s an open issue on this.

One way to get around it is via paulrberg’s multisol or hjubb’s solt and verify it manually via the official interface.

The caveat to the approach above is that you have to have to copy the libraries in lib to node_modules and make sure the path matches as it doesn’t read remappings.

For example:

cp -r lib/openzeppelin-contracts node_modules/@openzeppelin
mv node_modules/@openzeppelin/contracts/* node_modules/@openzeppelin
multisol src/Contract.sol