Skip to content

Upgrade Hardhat to V3 - #120

Merged
matejos merged 6 commits into
mainfrom
hardhat-v3
Nov 10, 2026
Merged

matejos merged 6 commits into
mainfrom
hardhat-v3

Conversation

@matejos

@matejos matejos commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Closes #45

Moves the repo to Hardhat 3, and with it to hardhat-deploy v2, which needs Hardhat 3. The network, chain descriptor and verify config come from hardhatConfig.v3 in @api3/contracts 43.0.0, the first release that has it.


For consumers of the package:

  • It is ES modules only now ("type": "module"), so a CommonJS require breaks. The one consumer I found, dapp-management, already uses ES module imports of getDeploymentName and the .sol paths, which work unchanged.
  • deploy/ is no longer published. The v2 scripts import rocketh/ and generated/, which the package does not ship, so the published copies could not run anyway.
  • Contract bytecode is the same as 0.3.0 apart from the CBOR metadata hash, and deployment names do not change.

For deploying: the README covers the changes. One ETHERSCAN_API_KEY now serves every Etherscan chain, and hardhat-deploy v2 asks you to confirm the gas price on a live network unless you pass --skip-prompts. The verification step in rocketh/utils.ts has not run against a real explorer yet, so the first live deployment will be its first run.

Notable detail:

  • evmVersion is pinned to paris. Hardhat 2 forced it, and Hardhat 3 leaves the choice to solc, which would change the bytecode.

matejos and others added 6 commits November 9, 2026 15:46
…ules

Hardhat 3 requires the project to be an ES module and drops the plugin set the repo used. hardhat-deploy v2 is a rewrite on rocketh that needs Hardhat 3, so the two upgrades happen together.

Dependencies:
- hardhat 2 -> ^3.16.0, which passes the 14 day minimumReleaseAge.
- hardhat-deploy 1 -> ^2.0.26, plus rocketh and @rocketh/deploy. The deploy scripts only call deploy, so the repo needs no other rocketh extension.
- @nomicfoundation/hardhat-toolbox -> hardhat-toolbox-mocha-ethers. pnpm installs the plugins that the toolbox declares as peer dependencies, so package.json lists only hardhat-ethers and hardhat-verify, which the repo imports directly.
- solidity-coverage goes, because Hardhat 3 has coverage built in (hardhat test --coverage).
- typechain goes, because hardhat-typechain bundles it.
- ts-node -> tsx, since ts-node needs a loader flag under ESM.
- @api3/contracts 42 -> ^43.0.0, the first release with the Hardhat 3 network, chain descriptor and verify config (hardhatConfig.v3).

Scripts:
- compile -> build, and hardhat.build.config.ts goes away. It differed from hardhat.config.ts only by leaving out the networks, and the Hardhat 3 network config from @api3/contracts needs no environment variables to load.
- --parallel moves into the config.
- verify-vendor-contracts runs with tsx. It does not use the Hardhat runtime.

ES module conversion:
- eslint.config.js becomes eslint.config.cjs so it keeps loading as CommonJS.
- .solcover.js goes with solidity-coverage.
- __dirname -> import.meta.dirname in scripts/tag-and-release.ts.
- relative imports in src get the .js extensions that Node's ES module resolution requires.

pnpm-workspace.yaml gets allowBuilds entries for @parcel/watcher and esbuild, which come in with Hardhat 3 and tsx. The ignore files list generated/, which holds the hardhat-deploy v2 typed artifacts. These are build output, the same as typechain-types.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The config moves to defineConfig with the toolbox and hardhat-deploy in the plugins array. networks, chainDescriptors and verify come from the Hardhat 3 exports of @api3/contracts, which replace the etherscan() and networks() helpers that were shaped for Hardhat 2.

evmVersion is pinned to "paris". Hardhat 2 forced it whenever it was unset, and Hardhat 3 leaves the choice to solc, which picks a newer default for 0.8.27.

The default and production profiles hold the same compilers. "hardhat deploy" and verifyContract() build with production, and "hardhat build" with default, so a difference between the two would make the verified bytecode differ from the tested one.

The two @api3/contracts interfaces that the contracts import are listed in npmFilesToBuild. Hardhat 2 generated TypeChain types for them and the package exports those types, and Hardhat 3 builds npm sources only when they are listed. With them in the build, typechain-types has the same files and index.ts has the same exports as the published 0.3.0 package.

Compared with a Hardhat 2 build of the same sources, every contract has the same creation and runtime bytecode once the CBOR metadata is stripped. The metadata hash differs, because Hardhat 3 names project sources "project/contracts/..." and solc hashes the source names into the metadata.

.solcover.js skipFiles moves to coverage.skipFiles, and --parallel moves to test.mocha.parallel.

dotenv stays: the @api3/contracts helpers read MNEMONIC, the RPC URLs and the Etherscan key from process.env when the config loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hardhat 3 no longer exports ethers from the hardhat module. Each network connection carries its own ethers and network helpers, so each test file opens one connection at the top and takes both from it. The test files do not import from each other, so they do not need a shared connection.

networkHelpers is taken under the name helpers, so the existing helpers.loadFixture and helpers.time call sites are untouched.

@nomicfoundation/hardhat-ethers no longer exports ./signers, so HardhatEthersSigner comes from ./types.

No matcher changes are needed: the tests use none of the matchers whose signature changed.

71 tests pass, the same as on Hardhat 2. "hardhat test --coverage" leaves out the mock and vendor contracts, as .solcover.js did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rocketh/config.ts holds the deployer account. There were never any named accounts here, and the deployer was always the first unnamed account, so it is index 0, which resolves against eth_accounts of the network provider. Only the deploy extension is wired in, because the scripts do not call read or execute and deploy no proxies.

Each script keeps its steps: deploy a mock on a local network or read the address from the environment, check it, derive the deployment name from the constructor arguments, deploy, verify. The deployment names do not change: getDeploymentName receives the same values, and the PriceCappedApi3ReaderProxyV1 name on a local network is the same as on Hardhat 2.

The local network check compared against the network names hardhat and localhost. Hardhat 3 calls its built-in in-memory network default, so the check is now default or localhost.

run('verify:verify') is gone, since a v2 deploy script gets no task runner. rocketh/utils.ts calls verifyContract from hardhat-verify instead, with the provider that @api3/contracts gives for the chain ("other" is Blockscout, as in the Hardhat 3 chain descriptors). A chain with no verification API is skipped with a message.

hardhat-deploy v1 waited for 5 confirmations as part of the deployment, so that the block explorer knew the contract by the time it was verified. rocketh reads the confirmation count only from a per-chain config, so rocketh/utils.ts waits for the 5 confirmations before it verifies a new deployment.

The deploy scripts give hardhat-deploy their default export, so the ESLint override for deploy/ turns off import-x/no-default-export. The scripts no longer export an anonymous function, so the override drops unicorn/no-anonymous-default-export.

The typed artifacts come from generated/, and the arguments are typed against their ABIs, so the mock values become bigint where the ABI takes a bigint.

On the default network, all five scripts deploy and skip verification. Against hyperliquid with an empty account, the Product script reads both proxies and gives the same deployment name as the ProductApi3ReaderProxyV1_5883d33e record in dapp-management, then fails on the deploy transaction for lack of funds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Hardhat 3 verify config from @api3/contracts reads one ETHERSCAN_API_KEY for every chain that Etherscan serves, so the per-network key names go. Blockscout chains need no key.

The config note names the Hardhat 3 helpers: networks, chainDescriptors and verify, from hardhat-config-v3.ts.

hardhat-deploy v2 records keep the constructor arguments ABI-encoded in argsData and have no args field.

hardhat-deploy v2 asks to confirm the gas price before it sends transactions to a live network, and --skip-prompts turns that off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The deploy scripts were added to the package in 1e749ab (#52) without a named consumer. No api3dao repository references the published deploy folder, and the one consumer of the package, dapp-management, has deploy scripts of its own and imports only getDeploymentName and the Solidity sources.

After the move to hardhat-deploy v2, the scripts import ../rocketh/*.js and ../generated/artifacts/index.js, and the package publishes neither, so the published copies could not run anyway.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matejos
matejos requested a review from matejfalat November 9, 2026 16:54

@matejfalat matejfalat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@matejos
matejos merged commit 97058f5 into main Nov 10, 2026
3 checks passed
@matejos
matejos deleted the hardhat-v3 branch November 10, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade Hardhat to V3

2 participants