## File: README.md Hardhat is an Ethereum development environment for professionals. It facilitates performing frequent tasks, such as running tests, automatically checking code for mistakes or interacting with smart contracts. Built by the [Nomic Foundation](https://nomic.foundation/) for the Ethereum community. --- > 💡 This is the README for Hardhat 3, the new major version of Hardhat. For the previous version (v2), see [this branch](https://github.com/NomicFoundation/hardhat/tree/v2) instead. --- ## Getting started To install Hardhat and initialize a new project, run the following command in an empty directory: ```bash npx hardhat --init ``` This will take you through an interactive setup process to get started. ## Learn more To learn more about Hardhat, check out the [documentation](https://hardhat.org/docs/). ## Contributing Contributions are always welcome! Feel free to open any issue or send a pull request. Go to [CONTRIBUTING.md](https://github.com/NomicFoundation/hardhat/blob/main/CONTRIBUTING.md) to learn about how to set up Hardhat's development environment. --- ## File: docs/engineering-guidelines.md # Hardhat engineering guidelines # Architectural guidelines ## A1: `hardhat/src/core/` shouldn’t contain any application-level logic The intention behind the current separation of part of `hardhat` into the `core/` directory is: 1. To only include the minimal infrastructure which Hardhat is built on top of in `core`: config, tasks, hooks, global options, and user interruptions. 2. For the rest of `hardhat` to use `core` to implement any functionality related to Ethereum, Solidity, testing, etc. 3. For plugins to integrate with `hardhat`, and not `core`. ## A2: No imports nor logic in the plugins’ `index.ts` files The entry-point of the plugins are meant to only export a description of the plugin, and not implement any functionality. Please don’t import anything in those files. The only accepted imports in the `index.ts` file of plugins (both built-in and external) are their `type-extension`, types and `enums` from `hardhat`, `hardhat/config`, `hardhat/plugins` (for `definePlugin`), and potentially a simple file with constants. They can also import files that follow these same rules and restrictions. Everything else should be imported by a callback registered in the plugin object. Type extensions must be exported from the plugin entry-point as type-only exports, not imported for side effects: ```ts export type * from "./type-extensions.js"; ``` This keeps the declaration file reachable to TypeScript consumers without adding a runtime import to the compiled `index.js`. To make this safe and predictable: 1. `type-extensions.ts` must contain only type-level module augmentation code and no runtime side effects. 2. `type-extensions.ts` must not export named types. If it needs to be treated as a module, rely on `"moduleDetection": "force"` or use `export {};`. 3. Type-extensions don't need to import the modules they are augmenting. This was an incorrect way to force the .ts file to be treated as a module. Use `export {};`, or better yet ["moduleDetection": "force"](https://www.typescriptlang.org/tsconfig/#moduleDetection). 4. Avoid exporting arbitrary types from `index.ts`; otherwise downstream plugins may accidentally re-export them when they re-export your type extensions. ## A3: Always initialize the HRE using the `hre-initialization` module inside of `hardhat` If you are working on the `hardhat` package, and you need an instance of the HRE, use the `hre-initialization` module instead of using the package’s entry-point modules (e.g. `src/index.ts` and `src/hre.ts`), or the `HardhatRuntimeEnvironmentImplementation` directly. Doing this ensures that the initialization is run correctly and in a consistent way. For example, by resolving the config correctly and loading the builtin plugins. The exception to this rule are the tests of `src/core`, which must use the `HardhatRuntimeEnvironmentImplementation.create` factory directly, as they shouldn’t include the built-in plugins. ## A4: Barrel files, imports, and re-exports A barrel file in JavaScript is a single file that consolidates exports from multiple modules. We only use barrel files to export things to consumers of each package, but not within the package. This has two practical implications: 1. Re-exports should only be allowed in the modules that are included in `package.json#exports` 2. We should always import things from the file that defines them, not from files that re-export them. We should also avoid placing logic or type definitions in the barrel files, but just re-export things. The objective behind these rules is to be consistent within the codebase, spend less time thinking about how to import things, and avoid some performance issues that barrel files can cause. ## A5: Public APIs and `src/internal/` folders Each of the packages has their code in a `src/`. Anything that’s in that folder and not inside `src/internal` should be considered a public API. This means that: - It should probably be included in `package.json#exports` - It should be carefully reviewed, with a special focus on backwards compatibility (post-initial-release) - It should not be expanded (i.e. add new exports and/or files) without discussing it with the rest of the team ## A6: Don’t expose the Hook system everywhere When writing a plugin, you may find yourself in a situation where a domain object needs to execute a hook. The simplest way to do that would be to keep a reference to the `HookManager`. While it may be tempting, it leads to unnecessary coupling of the system and makes it hard to test. Instead of doing that, you accept a callback in your domain object constructor, and pass a callback that uses the `HookManager` there. For example, instead of keeping references to the `HookManager` in each `NetworkConnection` to run a chain of `HookHandler`s, we only interact with the `HookManager` in the `NetworkManager`, and pass a callback with this type to the `NetworkConnection`'s constructor: ```tsx export type JsonRpcRequestWrapperFunction = ( request: JsonRpcRequest, defaultBehavior: (r: JsonRpcRequest) => Promise, ) => Promise; ``` which we use like this: ```tsx const wrapper: JsonRpcRequestWrapperFunction = (request, defaultBehavior) => hookManager.runHandlerChain( "network", "onRequest", [networkConnection, request], async (_context, _connection, req) => defaultBehavior(req), ); ``` # General Coding guidelines ## GC1: Do not use `node:fs` directly If you need to access the file system, please use our `fs` utilities instead, as the `node:fs` module has several shortcomings that make it error-prone and hard to debug when it fails. For example, its errors don’t have stack traces (!). If you need a file system related piece of functionality that we haven’t implemented, please raise it with the team. ## GC2: Do not use object literals to construct or mock complex types In TypeScript you can always initialize an object of a certain type with an object literal. We should never do that for our main domain types nor larger ones. Instead, we should provide constructors and/or factories for each complex type, and use that everywhere. There are many reasons for this, the main ones being that (1) without using constructors/factories there’s no way to ensure that the object’s internal state is correct, and (2) the codebase becomes brittle (e.g. a small change in a type may require changing dozens of files). This is also valid for tests, where things can easily become super brittle by using object literals. ## GC3: top-level imports vs dynamic imports Hardhat 3's plugin system already handles lazy loading — plugins place business logic behind dynamic imports. Dependencies, conditional dependencies, hook handler factories, and task actions are all dynamically imported. This means most imports should be top-level imports, except for a few cases: Use `await import` only if one of these conditions is met: 1. The file with the import is part of the `hardhat` package, is always imported at startup (i.e. imported by `hardhat`'s `src/internal/cli/main.ts` or `src/index.ts`, directly or transitively), and the imported module isn't always used (e.g. `./init/init.js` in `hardhat`'s `main.ts`) 2. The import path is dynamic (e.g. the user config path) 3. The file is dynamically loaded by a wrapper that exports the same interface that loads it on first access (mostly used for HRE extensions, e.g. `src/internal/builtin-plugins/network-manager/hook-handlers/hre.ts` in `hardhat`) 4. The dynamic import is used to avoid a circular dependency (e.g. importing the `HRE` at runtime) 5. The import has to happen at a certain point in time (mostly used for import side-effects, e.g. `await import(...)` without doing anything with the imported module) 6. If there's a comment justifying it, and the imported module is cached (i.e. not running `await import(...)` every time, but instead doing something like `if (cachedModule === undefined) { cachedModule = await import(...) }`). Some code duplication in this case is acceptable if that avoids adding unnecessary async logic (e.g. avoid `const module = await getModule()` to avoid repeating just a few conditionals). Test files are free to use `await import` freely. ### Always-run hook handler factories `ConfigHooks` and `HardhatRuntimeEnvironmentHooks` factories run on every Hardhat invocation, so they should follow the same criteria as the plugin's `index.ts` file. ### Lazy wrappers for `NetworkConnection` extensions `NetworkHooks` aren't always run, but `newConnection` handlers that extend `NetworkConnection` (e.g. `connection.foo = …`) should consider lazily initializing their business logic, unless they are virtually always used. The reason for this is that otherwise the first network connection initializes too many unnecessary things. For example, the `hardhat-ethers` plugin doesn't need to be lazily initialized, as most users that install it will use it most of the time after creating a new network. On the contrary, the `hardhat-ignition-ethers` plugin isn't always used, so it should be lazily initialized. Reference pattern for lazy initialization: `packages/hardhat-ignition-ethers/src/internal/hook-handlers/network.ts`. When the wrapper caches both the imported module and an instance built from it, the getter must run `await import(...)` **before** the instance cache check — see the "Ordering in cached lazy getters" section in `docs/engineering-guidelines.md` (under GC3) for the rationale and code shape. ### Ordering in cached lazy getters This rule applies to any cached dynamic import (condition 6 in the list above) that also caches an instance built from the imported module. The `NetworkConnection` lazy wrapper above is one example, but the same shape comes up elsewhere. The getter must run `await import(...)` **before** the instance cache check, so concurrent callers share a single microtask-dedupe point — otherwise each suspended caller re-enters the branch, constructs its own implementation, and the callers end up with different instances. For the same reason, there must be no `await` between reading `this.#instance === undefined` and assigning `this.#instance`: any suspension point in that window lets a concurrent caller observe the still-`undefined` instance and construct a second one. ```ts import type { Impl as ImplT } from "./impl.js"; let Impl: typeof ImplT | undefined; async #get(): Promise { if (Impl === undefined) { ({ Impl } = await import("./impl.js")); } if (this.#instance === undefined) { this.#instance = new Impl(...); } return this.#instance; } ``` # Testing guidelines ## T1: Do not use fixture projects unless they are needed Hardhat v2 was mostly tested using fixture projects, as its initialization was tied to having a config file present in the file system. In Hardhat v3, that’s not needed, and the HRE can be manually initialized using `createHardhatRuntimeEnvironment`. There are exceptions to this rule, as parts of the functionality of Hardhat do require a certain file-system structure (e.g. compiling a project, or using mocha to run test files), but those should be the exception. ## T2: Test error codes or types, not messages Building assertions based on error messages is fragile, as error messages are part of the UI of the system, which we may change independently from the actual behavior of the system. Instead of asserting error messages, assert error codes (e.g. using the `HardhatError` assertion helpers), or error types when appropriate. The exception to this is when we want to ensure that the messages look as expected, but those tests should focus on that, and not on testing the application behavior. For example, we may want to ensure that the HTTP provider error messages are backwards compatible with v2. ## T3: Don’t use the global HRE to test plugins In Hardhat v2 we used to test plugins by importing `hardhat` and using the global instance of the Hardhat Runtime Environment that it created. In v3 we should avoid this and create the HRE explicitly, so that we can create multiple instances of it without resorting to hacky reset processes. This can be done with: ```tsx import { createHardhatRuntimeEnvironment } from "hardhat/hre"; const { default: hardhatConfig } = await import("./my/hardhat.config.js"); const hre = await createHardhatRuntimeEnvironment(hardhatConfig); ``` ## T4: Avoid mocking internal behavior via monkey-patching Most javascript test frameworks have functionality that can be used to mock the internal behavior of a module, for example, by replacing how an `import` behaves or monkey-patching a library. Instead of doing that, design your code so that it can be mocked without having to resort to those techniques. For example, by using [IoC](https://en.wikipedia.org/wiki/Inversion_of_control). ## T5: Prioritize integration tests over excessive mocking Some parts of Hardhat have deep interconnections with each other, so testing them in isolation can be challenging. For example, initializing a `TaskManager` in isolation can be challenging. In those cases, use a higher level concept to build your tests in an integration-like fashion, instead of mocking extensive parts of the system. This makes the codebase less brittle, easier to understand, and decreases the possibility of the tests giving false results due to mocking errors. Make sure to pick the lowest level concept that allows you to build the test though, instead of always using the highest possible concepts (e.g. using the HRE instead of creating an entire on-disk project). If such a higher level concept doesn’t exist, consider creating it yourself, or discuss it with the rest of the team. # Dependency Management guidelines ## DM1: Minimize the amount of dependencies The npm ecosystem is susceptible to [supply chain attacks](https://en.wikipedia.org/wiki/Supply_chain_attack), and the Ethereum ecosystem has been a target of them in the past. Adding dependencies means increasing that attack surface. If you feel you need to add a dependency, please discuss it first with the team. ## DM2: `dependencies` vs `peerDependencies` _If you are not familiar with this topic, [please take the time to read this article in detail](https://lexi-lambda.github.io/blog/2016/08/24/understanding-the-npm-dependency-model/)._ Most of our internal dependencies should be `peerDependencies`, as we want to ensure that we share the same installation/copy of each plugin/library/hardhat. Some exceptions are: - `hardhat-errors` should be used as a dependency. This is an exception to the rule, and was specially designed to work as such. - `hardhat-utils` should be a dependency, as we can live with multiple versions/copies of it. - `hardhat-zod-utils` should be a dependency, for the same reason as above. - `hardhat-test-utils` should be a devDependency, as it’s only used for testing. ## DM3: Handling `peerDependencies` in a pnpm monorepo `pnpm` and `npm` automatically install `peerDependencies`. This means that when you install a dependency, its `peerDependencies` will be installed as if they were dependencies of your project. Unfortunately, there’s an exception to this. When you install a `workspace:` dependency in pnpm, it won’t auto-install its `peerDependencies`. To workaround this, whenever you add a `workspace:` peer dependency, you should also add its `peerDependencies` as `devDependencies`. For example, if we have workspace package `A` with `peerDependencies` `p1` and `p2`, and we want to install `A` as a peer dependency in our workspace package `B`, we need to: 1. Install `"A": "workspace:..."` as a peer dependency. 2. Install `p1` and `p2` as `devDependencies`. ## DM4: Plugin dependencies Plugins should have `hardhat` as a peer dependency. If they use another plugin, it should be installed as a peer dependency. # Naming Guidelines ## N1: Interface and interface implementation We tend to name the interface as `TheInterface` while the implementation of that interface would be `TheInterfaceImplementation`. This is a change from how we approached naming in v2, where our interfaces would often be prefixed with `I` as in `ITheInterface`. ## N2: `createDebug` namespaces Pick the namespace passed to `createDebug(...)` from one of two shapes, based on which package the file lives in: | Where | Shape | Example | | --- | --- | --- | | Inside `packages/hardhat/` (built-in plugins, CLI, telemetry, …) | `hardhat:core:[:...]` | `hardhat:core:cli:main` | | Any other workspace package | `hardhat:[:...]` | `hardhat:ethers:provider` | `` is the package name with the `hardhat-` prefix stripped (`hardhat-ethers` → `ethers`, `hardhat-ignition` → `ignition`, `ignition-core` → `ignition-core`). Sub-segments mirror the file path inside the package, but compressed: drop the `internal` segment and fold a directory + basename into a single descriptor when they describe the same concept (e.g. `hardhat-ethers-provider/hardhat-ethers-provider.ts` → `:provider`). Keep segments that let users filter a related family of loggers together — in particular keep `hook-handlers` so a user can do `DEBUG='hardhat:*:hook-handlers:*'` to see every plugin's hook-handler logs at once. Two examples end-to-end: - `packages/hardhat/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts` → `hardhat:core:network-manager:edr:provider`. - `packages/hardhat-ethers/src/internal/hardhat-ethers-provider/hardhat-ethers-provider.ts` → `hardhat:ethers:provider`. Namespaces are diagnostic strings, not part of any public API. Feel free to rename them when the repo layout changes. # Error Guidelines ## E1: Use `HardhatError` in `hardhat` and Nomic plugins We are intentional when we raise errors across all our packages. New errors should be declared within `hardhat-errors` and used from there. This ensures all errors have an error code and documentation that is displayed on the website. Exceptions to this rule: 1. Using `hardhat-errors` would create a circular dependency 2. JSON-RPC specific errors, where we throw `ProviderError`. 3. Things that are too low-level, like "-utils". But naturally low level, not just shared, like in this case. 4. The encryption module of the keystore has an exception, because we want it to be self-contained and easy to read/audit. --- ## File: e2e/README.md ### Hardhat initialization tests To run the "hardhat init" tests, use the command: ``` ./test-project-initialization.sh ``` ### All other Hardhat scenario tests To run all the fixture tests, use the command: ``` ./run-fixture-projects.sh ``` You can run a single project's tests by passing the project folder name as a parameter. Example: to run only the tests inside the `vars` folder, use: ``` ./run-fixture-projects.sh vars ``` --- ## File: packages/template-package/README.md # Template package This is a template package with a base configuration that should be shared by other packages. It sets up the following things: - Typescript - eslint - prettier - npm scripts - package.json' export field --- ## File: packages/ignition-ui/README.md # hardhat-ignition-ui > ⚠️ This package is an internal Hardhat component and it's not meant to be used directly. The website used in Hardhat Ignition's `visualize` task for visualising a deployment. ## Development A development server can be run from the root of this package with: ```sh pnpm dev ``` By default in development the deployment in `./public/deployment.json` is used, to overwrite this example deployment, update the module in `./examples/ComplexModule.js` and run the regenerate command: ```sh pnpm regenerate-deployment-example ``` ## Contributing Contributions are always welcome! Feel free to open any issue or send a pull request. Go to [CONTRIBUTING.md](https://github.com/NomicFoundation/hardhat-ignition/blob/main/CONTRIBUTING.md) to learn about how to set up Hardhat Ignition's development environment. ## Feedback, help and news [Hardhat Ignition on Discord](https://hardhat.org/ignition-discord): for questions and feedback. Follow [Hardhat](https://twitter.com/HardhatHQ) and [Nomic Foundation](https://twitter.com/NomicFoundation) on Twitter. --- ## File: packages/ignition-core/README.md # hardhat-ignition-core This package contains the core logic of [Hardhat Ignition](https://hardhat.org/ignition). It's not meant to be used directly. Check [hardhat-ignition](https://github.com/NomicFoundation/hardhat/tree/main/packages/hardhat-ignition) instead. --- ## File: packages/hardhat-zod-utils/README.md # hardhat-zod-utils > ⚠️ This package is an internal Hardhat component and it's not meant to be used directly. This package contains zod utilities to validate Hardhat 3 configurations. It's used by Hardhat and its plugins. --- ## File: packages/hardhat-viem-assertions/README.md # hardhat-viem-assertions This plugin adds an Ethereum-specific assertions library that integrate with [viem](https://viem.sh/), making your smart contract tests easy to write and read. ## Installation > This plugin is part of the [Viem Hardhat Toolbox](https://hardhat.org/plugins/nomicfoundation-hardhat-toolbox-viem). If you are using that toolbox, there's nothing else you need to do. To install this plugin, run the following command: ```bash npm install --save-dev @nomicfoundation/hardhat-viem-assertions ``` In your `hardhat.config.ts` file, import the plugin and add it to the `plugins` array: ```ts import { defineConfig } from "hardhat/config"; import hardhatViemAssertions from "@nomicfoundation/hardhat-viem-assertions"; export default defineConfig({ plugins: [hardhatViemAssertions], }); ``` ## Usage You don't need to do anything else to use this plugin. The `viem` object added by the [hardhat-viem plugin](https://hardhat.org/plugins/nomicfoundation-hardhat-viem) is expanded with an `assertions` property that contains the assertions library. Here is an example of using the `balancesHaveChanged` assertion: ```ts const { viem } = await hre.network.create(); const [bobWalletClient, aliceWalletClient] = await viem.getWalletClients(); await viem.assertions.balancesHaveChanged( bobWalletClient.sendTransaction({ to: aliceWalletClient.account.address, value: 3333333333333333n, }), [ { address: aliceWalletClient.account.address, amount: 3333333333333333n, }, ], ); ``` ## Reference ### Reverted transactions Several assertions are included to check that a transaction reverted, and the reason of the revert. #### `revert` Assert that executing a contract function reverts for any reason, without checking the cause of the revert. Type: ```ts revert( contractFn: Promise, ): Promise; ``` Parameters: - `contractFn`: A promise returned by a viem read or write contract call expected to revert. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.revert(token.write.transfer([address, 0n])); ``` #### `revertWith` Assert that executing a contract function reverts with the specified reason string. Type: ```ts revertWith( contractFn: Promise, expectedRevertReason: string, ): Promise; ``` Parameters: - `contractFn`: A promise returned by a viem read or write contract call expected to revert. - `expectedRevertReason`: The expected revert reason string. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.revertWith( token.write.transfer([address, 0n]), "transfer value must be positive", ); ``` #### `revertWithCustomError` Assert that executing a contract function reverts with a specific custom error defined in the given contract. Type: ```ts revertWithCustomError>( contractFn: Promise, contract: TContract, customErrorName: ContractErrorName, ): Promise; ``` Parameters: - `contractFn`: A promise returned by a viem read or write contract call expected to revert. - `contract`: The viem contract instance whose ABI defines the expected custom error. - `customErrorName`: The expected custom error name. Autocompleted from `contract.abi`. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.revertWithCustomError( token.write.transfer([address, 0n]), token, "InvalidTransferValue", ); ``` #### `revertWithCustomErrorWithArgs` Assert that executing a contract function reverts with a specific custom error and arguments. Type: ```ts revertWithCustomErrorWithArgs< TContract extends AbiHolder, TErrorName extends ContractErrorName, >( contractFn: Promise, contract: TContract, customErrorName: TErrorName, args: ErrorArgsOf, ): Promise; ``` Parameters: - `contractFn`: A promise returned by a viem read or write contract call expected to revert. - `contract`: The viem contract instance whose ABI defines the expected custom error. - `customErrorName`: The expected custom error name. Autocompleted from `contract.abi`. - `args`: Expected custom error arguments, typed against the matching ABI input tuple. Each position can be a concrete value or a `(value) => boolean` predicate. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.revertWithCustomErrorWithArgs( token.write.transfer([address, 0n]), token, "InvalidTransferValue", [0n], ); ``` This assertion can take predicate functions to match some of the arguments: ```ts await viem.assertions.revertWithCustomErrorWithArgs( contract.read.revertWithCustomErrorWithUintAndString([1n, "test"]), contract, "CustomErrorWithUintAndString", [(arg: bigint) => arg === 1n, "test"], ); ``` ```ts import { anyValue } from "@nomicfoundation/hardhat-toolbox-viem/predicates"; await viem.assertions.revertWithCustomErrorWithArgs( contract.read.revertWithCustomErrorWithUintAndString([1n, "test"]), contract, "CustomErrorWithUintAndString", [1n, anyValue], ); ``` ### Events These assertions can be used to check that a transaction emits specific events and their arguments. Each one accepts a transaction hash or a promise resolving to one (e.g. a viem write call) and looks at the receipt of that specific transaction. #### `emit` Assert that executing a contract function emits a specific event. Type: ```ts emit>( txHash: Hash | Promise, contract: TContract, eventName: ContractEventName, ): Promise; ``` Parameters: - `txHash`: The transaction hash returned by a viem write call or `sendTransaction`, or a promise that resolves to it. - `contract`: The viem contract instance whose ABI is used to parse logs. - `eventName`: The event name to assert. Autocompleted from `contract.abi`. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.emit( rocketContract.write.launch(), rocketContract, "LaunchEvent", ); ``` The contract call can also be awaited first, which is helpful if you want to assert several events against the same transaction: ```ts const hash = await rocketContract.write.launch(); await viem.assertions.emit(hash, rocketContract, "LaunchEvent"); await viem.assertions.emit(hash, rocketContract, "FuelBurnedEvent"); ``` #### `emitWithArgs` Assert that executing a contract function emits a specific event with the given arguments. Type: ```ts emitWithArgs< TContract extends AbiHolder, TEventName extends ContractEventName, >( txHash: Hash | Promise, contract: TContract, eventName: TEventName, args: EventArgsOf, ): Promise; ``` Parameters: - `txHash`: The transaction hash returned by a viem write call or `sendTransaction`, or a promise that resolves to it. - `contract`: The viem contract instance whose ABI is used to parse logs. - `eventName`: The event name to assert. Autocompleted from `contract.abi`. - `args`: Expected event arguments, typed against the matching ABI input tuple. Each position can be a concrete value or a `(value) => boolean` predicate. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.emitWithArgs( rocketContract.write.launch(), rocketContract, "LaunchEventWithArgs", ["Apollo", "lift-off"], ); ``` This assertion can take predicate functions to match some of the arguments: ```ts await viem.assertions.emitWithArgs( contract.write.emitTwoUints([1n, 2n]), contract, "WithTwoUintArgs", [1n, (arg: bigint) => arg >= 2], ); ``` ```ts import { anyValue } from "@nomicfoundation/hardhat-toolbox-viem/predicates"; await viem.assertions.emitWithArgs( contract.write.emitTwoUints([1n, 2n]), contract, "WithTwoUintArgs", [anyValue, 2n], ); ``` ### Balance change These assertions can be used to check how a given transaction affects the ether balance of a specific address. #### `balancesHaveChanged` Assert that a transaction changes the ether balance of the given addresses by the specified amounts. The transaction can be provided as an un-awaited promise from `sendTransaction` or a viem write call, or as the already-awaited result. Type: ```ts balancesHaveChanged( txHash: Hash | Promise, changes: Array<{ address: Address; amount: bigint; }>, ): Promise; ``` Parameters: - `txHash`: The transaction hash returned by `sendTransaction` (or a viem write call), or a promise that resolves to it. - `changes`: The expected balance deltas, in wei, for each address. Negative values are allowed. Returns: - A promise that resolves if the assertion passes, or rejects if it fails. Example: ```ts await viem.assertions.balancesHaveChanged( bobWalletClient.sendTransaction({ to: aliceWalletClient.account.address, value: 3333333333333333n, }), [ { address: aliceWalletClient.account.address, amount: 3333333333333333n, }, { address: bobWalletClient.account.address, amount: -3333333333333333n, }, ], ); ``` The transaction can also be awaited first: ```ts const hash = await vault.write.deposit([], { value: 1000n }); await viem.assertions.balancesHaveChanged(hash, [ { address: vault.address, amount: 1000n }, { address: bobWalletClient.account.address, amount: -1000n }, ]); ``` --- ## File: packages/hardhat-viem/README.md # hardhat-viem This plugin integrates [viem](https://viem.sh) into Hardhat, adding a `viem` object to each Network Connection. ## Installation > This plugin is part of the [Viem Hardhat Toolbox](https://hardhat.org/plugins/nomicfoundation-hardhat-toolbox-viem). If you're using that toolbox, there's nothing else you need to do. Install the plugin with this command: ```bash npm install --save-dev @nomicfoundation/hardhat-viem ``` In your `hardhat.config.ts` file, import the plugin and add it to the `plugins` array: ```typescript import { defineConfig } from "hardhat/config"; import hardhatViem from "@nomicfoundation/hardhat-viem"; export default defineConfig({ plugins: [hardhatViem], }); ``` ## Usage This plugin adds a `viem` property to each Network Connection: ```ts import { network } from "hardhat"; const { viem } = await network.create(); const publicClient = await viem.getPublicClient(); console.log(await publicClient.getBlockNumber()); const counter = await viem.deployContract("Counter"); await counter.write.inc(); console.log(await counter.read.x()); ``` To learn more about using viem with Hardhat, read [our guide](https://hardhat.org/docs/learn-more/using-viem). ### Clients Viem provides a set of interfaces to interact with the blockchain called **clients**. There are three types: - **Public clients** fetch node information from the public JSON-RPC API, like blocks or account balances. - **Wallet clients** interact with Ethereum Accounts for tasks like transactions and message signing. - **Test clients** perform actions that are only available in development nodes. The `viem` object in the Network Connection has methods that make it easy to build clients attached to the current network. #### Public clients Get a public client using the `getPublicClient` method: ```ts const publicClient = await viem.getPublicClient(); console.log(await publicClient.getBlockNumber()); ``` Learn more about public clients in the [viem documentation](https://viem.sh/docs/clients/public). #### Wallet clients There are two methods related to wallet clients: - `getWalletClients`: returns an array of wallet clients for all the accounts configured for the network. - `getWalletClient`: receives an address and returns a wallet client for that address. ```ts const [walletClient] = await viem.getWalletClients(); await walletClient.sendTransaction(/* ... */); ``` Learn more about wallet clients in the [viem documentation](https://viem.sh/docs/clients/wallet). #### Test clients Get a test client using the `getTestClient` method: ```ts const testClient = await viem.getTestClient(); await testClient.mine({ blocks: 10 }); ``` Learn more about test clients in the [viem documentation](https://viem.sh/docs/clients/test). #### Overriding client options All the methods to get clients accept an optional parameter to override the default client options. For example, to create a public client with a different polling interval: ```ts const publicClient = await viem.getPublicClient({ pollingInterval: 5000, }); ``` These options are the same as the ones used when creating clients with viem directly. Check the [viem documentation](https://viem.sh/docs/clients/intro) to learn more about the available options in each case. ### Contracts Viem has support for [contract instances](https://viem.sh/docs/contract/getContract), type-safe interfaces for interacting with contracts. This plugin makes it easy to create instances for contracts in your project. #### Deploying contracts The `viem` object in the Network Connection has a `deployContract` method that deploys a contract by its name: ```ts const counter = await viem.deployContract("Counter"); ``` If your contract requires constructor arguments, pass them as the second parameter: ```ts const myContract = await viem.deployContract("MyContract", ["Arg1", 123]); ``` The `deployContract` method waits until the deployment transaction is mined and returns the contract instance. If you want to get the deployment transaction, or if you want to have the contract instance without waiting for the deployment to be mined, use the `sendDeploymentTransaction` method: ```ts const { contract: counter, deploymentTransaction } = await viem.sendDeploymentTransaction("Counter"); ``` #### Getting existing contracts To get an instance of an already deployed contract, use the `getContractAt` method, passing the contract name and address: ```ts const counter = await viem.getContractAt("Counter", "0x..."); ``` ## API The `viem` object added to the Network Connection has the following methods. ### `getPublicClient(publicClientConfig?)` Returns a viem public client connected to the current network. Optionally pass a configuration object to override the default client options. ### `getWalletClient(address, walletClientConfig?)` Receives an address and returns a viem wallet client for that address. Optionally pass a configuration object to override the default client options. ### `getWalletClients(walletClientConfig?)` Returns an array of viem wallet clients for all the accounts configured for the current network. Optionally pass a configuration object to override the default client options. ### `getTestClient(testClientConfig?)` Returns a viem test client connected to the current network. Optionally pass a configuration object to override the default client options. ### `deployContract(contractName, constructorArgs?, deployContractConfig?)` Deploys a contract by its name. Optionally pass an array of constructor arguments and a configuration object for the deployment. The configuration object supports the following properties: - `confirmations`: the number of confirmations to wait after the deployment transaction is mined. Default is `1`. - `libraries`: an object specifying the libraries to link in the contract. - `client`: an object with two properties, `public` and `wallet`, that specify the public and wallet clients to use with the returned contract. At least one of them must be provided. - `gas`: the gas limit for the transaction. - `gasPrice`: the gas price for the transaction. - `maxFeePerGas`: the maximum fee per gas for the transaction. - `maxPriorityFeePerGas`: the maximum priority fee per gas for the transaction. - `value`: the value to send with the transaction, in wei. ### `sendDeploymentTransaction(contractName, constructorArgs?, sendDeploymentContractConfig?)` Same as `deployContract`, but doesn't wait for the deployment to be mined, and returns an object with two properties: - `contract`: the contract instance, which is available even before the transaction is mined. - `deploymentTransaction`: the deployment transaction. The optional configuration object has the same properties as the one in `deployContract`, except for `confirmations`, which is not applicable here. ### `getContractAt(contractName, address, getContractConfig?)` Returns a contract instance for an already deployed contract. Provide the contract name and address. Optionally pass a configuration object with the following properties: - `client`: an object with two properties, `public` and `wallet`, that specify the public and wallet clients to use with the returned contract. At least one of them must be provided. --- ## File: packages/hardhat-verify/README.md # hardhat-verify [Hardhat](https://hardhat.org) plugin to verify the source of code of deployed contracts. ## Installation > This plugin is part of [Viem Hardhat Toolbox](https://hardhat.org/plugins/nomicfoundation-hardhat-toolbox-viem) and [Ethers+Mocha Hardhat Toolbox](https://hardhat.org/plugins/nomicfoundation-hardhat-toolbox-mocha-ethers). If you are using any of those toolboxes, there's nothing else you need to do. To install this plugin, run the following command: ```bash npm install --save-dev @nomicfoundation/hardhat-verify ``` In your `hardhat.config.ts` file, import the plugin and add it to the `plugins` array: ```ts import { defineConfig } from "hardhat/config"; import hardhatVerify from "@nomicfoundation/hardhat-verify"; export default defineConfig({ plugins: [hardhatVerify], }); ``` ## Usage ### Verifying on Etherscan You need to add the following Etherscan config in your `hardhat.config.ts` file ```typescript import { defineConfig } from "hardhat/config"; export default defineConfig({ verify: { etherscan: { // Your API key for Etherscan // Obtain one at https://etherscan.io/ apiKey: "", }, }, }); ``` We recommend using a [configuration variable](https://hardhat.org/docs/learn-more/configuration-variables) to set sensitive information like API keys. ```typescript import { configVariable, defineConfig } from "hardhat/config"; export default defineConfig({ verify: { etherscan: { // Your API key for Etherscan // Obtain one at https://etherscan.io/ apiKey: configVariable("ETHERSCAN_API_KEY"), }, }, }); ``` Run the `verify` task passing the network where it's deployed, the address of the contract, and the constructor arguments that were used to deploy it (if any): ```bash npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument 1" ``` ### Programmatic verification You can also verify contracts programmatically by using the `verifyContract` function from the plugin: ```typescript import hre from "hardhat"; import { verifyContract } from "@nomicfoundation/hardhat-verify/verify"; await verifyContract( { address: "DEPLOYED_CONTRACT_ADDRESS", constructorArgs: ["Constructor argument 1"], provider: "etherscan", // or "blockscout", or "sourcify" }, hre, ); ``` > Note: The `verifyContract` function is not re-exported from the Hardhat toolboxes, so you need to install the plugin and import it directly from `@nomicfoundation/hardhat-verify/verify`. ## Advanced Usage for Plugin Authors If you're building a Hardhat plugin that needs direct access to the Etherscan API (for example, to verify proxy contracts or make custom API calls), you can access the Etherscan instance through `network.create()`. ### Accessing the Etherscan Instance ```typescript import type { HardhatRuntimeEnvironment } from "hardhat/types"; export async function myCustomVerificationTask(hre: HardhatRuntimeEnvironment) { const { verification } = await hre.network.create(); // Access Etherscan instance const etherscan = verification.etherscan; // Check if a contract is already verified const isVerified = await etherscan.isVerified("0x1234..."); // Get the contract URL on the block explorer const url = await etherscan.getContractUrl("0x1234..."); // Submit a contract for verification const guid = await etherscan.verify({ contractAddress: "0x1234...", compilerInput: {/* compiler input JSON */}, contractName: "contracts/MyContract.sol:MyContract", compilerVersion: "v0.8.19+commit.7dd6d404", constructorArguments: "0x...", }); // Poll for verification status const result = await etherscan.pollVerificationStatus( guid, "0x1234...", "MyContract", ); } ``` ### Making Custom API Calls For API endpoints not covered by the standard methods, use `customApiCall()`: ```typescript const { verification } = await hre.network.create(); // Make a custom API call (apikey and chainid are added automatically) const response = await verification.etherscan.customApiCall({ module: "contract", action: "getsourcecode", address: "0x1234...", }); // Check the response if (response.status === "1") { console.log("Contract source:", response.result); } else { console.error("Error:", response.message); } ``` ### API Reference For complete type definitions and available methods, see the exported types: - `Etherscan` - The main interface for Etherscan API access - `EtherscanResponseBody` - Structure of API response bodies - `EtherscanCustomApiCallOptions` - Options for custom API calls - `EtherscanVerifyArgs` - Arguments for contract verification ### Build profiles and verification When no build profile is specified, this plugin defaults to `production`. However, tasks like `build` and `run` default to the `default` build profile. If your contracts are compiled with a different profile than the one used for verification, the compiled bytecode may not match the deployed bytecode, causing verification to fail. To avoid this, make sure to build and verify using the same profile: ```bash npx hardhat build --build-profile production npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument 1" ``` If you're using the `verifyContract` function programmatically through a script, pass the build profile when running it: ```bash npx hardhat run --build-profile production scripts/verify.ts ``` ## How it works The plugin works by fetching the bytecode in the given address and using it to check which contract in your project corresponds to it. Besides that, some sanity checks are performed locally to make sure that the verification won't fail.