|
| 1 | +# How to use viem with Hardhat |
| 2 | + |
| 3 | +[viem](https://viem.sh/) is a modern, type-safe library that lets you deploy contracts, manage accounts, read chain state, and more. It's the library we recommend for interacting with Ethereum. You can integrate viem with Hardhat by using the `hardhat-viem` plugin. |
| 4 | + |
| 5 | +## Setup |
| 6 | + |
| 7 | +If you have already initialized a viem-based project using `hardhat --init`, you don’t need to do anything else. |
| 8 | + |
| 9 | +If you want to add the plugin manually: |
| 10 | + |
| 11 | +1. Install the plugin: |
| 12 | + |
| 13 | + ```bash |
| 14 | + npm install --save-dev @nomicfoundation/hardhat-viem@next |
| 15 | + ``` |
| 16 | + |
| 17 | +2. Add it to the list of plugins in your Hardhat configuration: |
| 18 | + |
| 19 | + ```tsx |
| 20 | + import hardhatViem from "@nomicfoundation/hardhat-viem"; |
| 21 | + |
| 22 | + const config: HardhatUserConfig = { |
| 23 | + plugins: [ |
| 24 | + hardhatViem, |
| 25 | + // ...other plugins... |
| 26 | + ], |
| 27 | + // ...other config... |
| 28 | + }; |
| 29 | + |
| 30 | + export default config; |
| 31 | + ``` |
| 32 | + |
| 33 | +## Connecting to networks |
| 34 | + |
| 35 | +In Hardhat, you interact with networks using _network connections_. You can create connections with the network manager, which you can import directly from Hardhat: |
| 36 | + |
| 37 | +```tsx |
| 38 | +import { network } from "hardhat"; |
| 39 | + |
| 40 | +const connection = await network.connect("mainnet"); |
| 41 | +``` |
| 42 | + |
| 43 | +Plugins can extend the network connections with new functionality. The `hardhat-viem` plugin adds a `viem` object to each connection, which provides helpers to interact with the network you are connected to: |
| 44 | + |
| 45 | +```tsx |
| 46 | +const { viem } = await network.connect("mainnet"); |
| 47 | + |
| 48 | +const publicClient = await viem.getPublicClient(); |
| 49 | +console.log("Latest block number:", await publicClient.getBlockNumber()); |
| 50 | +``` |
| 51 | + |
| 52 | +## Using viem clients |
| 53 | + |
| 54 | +Viem groups functionality in [clients](https://viem.sh/docs/clients/intro). The `hardhat-viem` plugin helps you create them more easily. |
| 55 | + |
| 56 | +You can create a [public client](https://viem.sh/docs/clients/public) using the `getPublicClient` method: |
| 57 | + |
| 58 | +```tsx |
| 59 | +const { viem } = await network.connect(); |
| 60 | + |
| 61 | +const publicClient = await viem.getPublicClient(); |
| 62 | + |
| 63 | +console.log("Latest block number:", await publicClient.getBlockNumber()); |
| 64 | +``` |
| 65 | + |
| 66 | +Use the `getWalletClients` function to obtain [wallet clients](https://viem.sh/docs/clients/wallet). It returns an array of wallet clients, one for each account set up in the Hardhat config: |
| 67 | + |
| 68 | +```tsx |
| 69 | +const [senderClient, receiverClient] = await viem.getWalletClients(); |
| 70 | + |
| 71 | +await senderClient.sendTransaction({ |
| 72 | + to: receiverClient.account.address, |
| 73 | + value: 10n ** 18n, |
| 74 | +}); |
| 75 | +``` |
| 76 | + |
| 77 | +Finally, if you are connecting to a Hardhat network, you can call `getTestClient` to get a [test client](https://viem.sh/docs/clients/test): |
| 78 | + |
| 79 | +```tsx |
| 80 | +const testClient = await viem.getTestClient(); |
| 81 | + |
| 82 | +await testClient.mine({ |
| 83 | + blocks: 10, |
| 84 | +}); |
| 85 | +``` |
| 86 | + |
| 87 | +## Deploying and interacting with contracts |
| 88 | + |
| 89 | +`hardhat-viem` includes a `deployContract` function that lets you deploy contracts defined in the project. This function returns a viem [contract instance](https://viem.sh/docs/contract/getContract) of the deployed contract: |
| 90 | + |
| 91 | +```tsx |
| 92 | +import { network } from "hardhat"; |
| 93 | + |
| 94 | +const { viem } = await network.connect(); |
| 95 | +const counter = await viem.deployContract("Counter"); |
| 96 | + |
| 97 | +await counter.write.inc(); |
| 98 | + |
| 99 | +console.log("Counter value:", await counter.read.x()); |
| 100 | +``` |
| 101 | + |
| 102 | +If the constructor takes parameters, you can pass them as the second argument: |
| 103 | + |
| 104 | +```tsx |
| 105 | +const initialValue = 10n; |
| 106 | +const counter = await viem.deployContract("Counter", [initialValue]); |
| 107 | +``` |
| 108 | + |
| 109 | +By default, contracts are deployed from the first account defined in the Hardhat configuration, but you can specify a different one: |
| 110 | + |
| 111 | +```tsx |
| 112 | +const [wallet1, wallet2] = await viem.getWalletClients(); |
| 113 | + |
| 114 | +const counter = await viem.deployContract("Counter", [10n], { |
| 115 | + client: { |
| 116 | + wallet: wallet2, |
| 117 | + }, |
| 118 | +}); |
| 119 | +``` |
| 120 | + |
| 121 | +The `deployContract` function waits until the contract is deployed. If you just want to send the deployment without waiting until it’s mined, you can use `sendDeploymentTransaction`: |
| 122 | + |
| 123 | +```tsx |
| 124 | +const deploymentTx = await viem.sendDeploymentTransaction("Counter", [10n], { |
| 125 | + client: { |
| 126 | + wallet: wallet2, |
| 127 | + }, |
| 128 | +}); |
| 129 | +``` |
| 130 | + |
| 131 | +All the previous examples deploy a new contract instance, but sometimes you need to interact with an already deployed contract. In those cases, you can use the `getContractAt` function: |
| 132 | + |
| 133 | +```tsx |
| 134 | +const counterAddress = "0x1234567890123456789012345678901234567890"; |
| 135 | +const counter = await viem.getContractAt("Counter", counterAddress); |
| 136 | +``` |
| 137 | + |
| 138 | +### Using contracts from an npm dependency |
| 139 | + |
| 140 | +You can also use a contract defined in an npm dependency with `hardhat-viem`. |
| 141 | + |
| 142 | +To do this, you need to configure Hardhat to compile the contract and generate artifacts for it. This will allow you to use the `hardhat-viem` helpers to interact with it, just like with any other contract defined in your project. |
| 143 | + |
| 144 | +To learn how to do it, please read [this guide](./configuring-the-compiler.md#generating-artifacts-from-dependencies). |
| 145 | + |
| 146 | +## Type-safe contract interactions |
| 147 | + |
| 148 | +Viem has powerful typing capabilities, triggering compilation errors when you make mistakes like using the wrong type in a function argument or sending value to a non-payable function: |
| 149 | + |
| 150 | +```tsx |
| 151 | +// doesn't compile if getItem expects a number but receives a string: |
| 152 | +await contract.read.getItem(["3"]); |
| 153 | + |
| 154 | +// doesn't compile if setItem is not payable: |
| 155 | +await contract.write.setItem([3, "three"], { |
| 156 | + value: 1000n, |
| 157 | +}); |
| 158 | +``` |
| 159 | + |
| 160 | +When using viem on its own, you need to pass an ABI to get contract instances with properly inferred types. The `hardhat-viem` plugin handles this automatically when you use helpers like `deployContract` or `getContractAt`. |
| 161 | + |
| 162 | +### Troubleshooting contract type errors |
| 163 | + |
| 164 | +Contract types are updated when the project is compiled. If you are getting a compilation error that you don’t expect, make sure you’ve run `hardhat compile`. |
| 165 | + |
| 166 | +Note that VSCode may not always pick up the type updates automatically. If you are still getting unexpected TypeScript errors after compiling the project, open the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) and run `TypeScript: Reload Project`. |
| 167 | + |
| 168 | +## Using viem as a module |
| 169 | + |
| 170 | +The `viem` object in the connection only includes functionality added by the `hardhat-viem` plugin. To use viem’s own functionality, import it from the `viem` module: |
| 171 | + |
| 172 | +```tsx |
| 173 | +import { keccak256 } from "viem"; |
| 174 | +import { network } from "hardhat"; |
| 175 | + |
| 176 | +const { viem } = await network.connect(); |
| 177 | +``` |
| 178 | + |
| 179 | +Keep in mind that you can get a name clash if you use a namespace import: |
| 180 | + |
| 181 | +```tsx |
| 182 | +import * as viem from "viem"; |
| 183 | +import { network } from "hardhat"; |
| 184 | + |
| 185 | +// this is an error because viem is already declared |
| 186 | +const { viem } = await network.connect(); |
| 187 | +``` |
| 188 | + |
| 189 | +One way to work around this problem is to use a different name for the Hardhat viem object: |
| 190 | + |
| 191 | +```tsx |
| 192 | +const { viem: hhViem } = await network.connect(); |
| 193 | + |
| 194 | +const publicClient = await hhViem.getPublicClient(); |
| 195 | +``` |
0 commit comments