UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

120 lines (90 loc) 4.95 kB
--- name: hardhat-toolbox-mocha-ethers description: Use alongside the `hardhat` skill when the project depends on `@nomicfoundation/hardhat-toolbox-mocha-ethers`. Covers the ethers helpers exposed on `network.create()`, contract interaction (`ethers.deployContract`, calling functions, `connect`), TypeChain-typed contract instances, and the chai matchers from `@nomicfoundation/hardhat-ethers-chai-matchers` (`.to.emit`, `.to.be.revertedWith*`, `.to.changeEtherBalance(s)`). metadata: package: "@nomicfoundation/hardhat-toolbox-mocha-ethers" --- # Hardhat toolbox: ethers + Mocha This skill builds on the core **`hardhat`** skill. Load that first for test organization, the `network.create()` shape, `networkHelpers`, fixtures, and the typechecking workflow. Everything below hangs off the connection returned by `network.create()`. This stack runs tests under **Mocha** (global `describe` / `it`) and asserts with **chai**. Mocha doesn't await `describe` callbacks, so the connection is set up at the top of the file with top-level `await`: ```ts import { expect } from "chai"; import { network } from "hardhat"; const { ethers, networkHelpers } = await network.create(); describe("Counter", function () { // ... }); ``` One connection per file is the norm. State isolation between tests comes from `loadFixture` (see the `hardhat` skill), not from creating a fresh connection per `describe`. ## `ethers`: signers and contract interaction Contract types are generated by **TypeChain** from the compiled ABI. Run `hardhat build` before typechecking so the types reflect the latest contracts. ```ts // Provider (read-only chain data) const blockNumber = await ethers.provider.getBlockNumber(); const balance = await ethers.provider.getBalance("0xabc..."); // Signers (for sending transactions) const [owner, alice, bob] = await ethers.getSigners(); // default funded accounts // Utilities const oneEth = ethers.parseEther("1.0"); // Deploy a contract, returns a fully typed instance. // Pass constructor args as the second argument, and (optionally) a signer // other than the default as the third. const counter = await ethers.deployContract("Counter"); const counterWithArgs = await ethers.deployContract("Counter", [42n]); const counterFromAlice = await ethers.deployContract("Counter", [], alice); // Read state and call functions. Args and return types are type-checked // against the ABI at compile time; passing wrong types or wrong argument // counts is a TypeScript error. const value = await counter.x(); await counter.inc(); await counter.incBy(3n); // Send from a different signer await counter.connect(alice).inc(); // Send ETH along with a payable call await counter.deposit({ value: oneEth }); // Attach to an already-deployed contract const existing = await ethers.getContractAt("Counter", "0xabc..."); ``` Inside a `loadFixture` setup function (see the `hardhat` skill for the surrounding pattern), `ethers.deployContract` is the canonical deploy step: ```ts async function deployCounterFixture() { const counter = await ethers.deployContract("Counter"); return { counter }; } const { counter } = await networkHelpers.loadFixture(deployCounterFixture); ``` To send a transaction from an arbitrary address (not one of the default-funded accounts), fund it for gas and pull an impersonating signer with `ethers.getImpersonatedSigner` — it handles the impersonation internally, no separate `networkHelpers.impersonateAccount` call needed: ```ts const addr = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; await networkHelpers.setBalance(addr, ethers.parseEther("1.0")); const signer = await ethers.getImpersonatedSigner(addr); await counter.connect(signer).inc(); ``` ## Chai matchers: Ethereum-specific assertions The `hardhat-ethers-chai-matchers` plugin extends chai's `expect` with matchers for transactions, events, and balance changes. Pass the **transaction promise** (the result of a write call, unawaited) into `expect`: ```ts import { expect } from "chai"; // Reverts await expect(counter.connect(banned).inc()).to.revert(ethers); await expect(counter.connect(banned).inc()).to.be.revertedWith( "only the owner can increment the counter", ); await expect(counter.connect(banned).inc()).to.be.revertedWithCustomError( counter, "Unauthorized", ); await expect(counter.connect(banned).inc()) .to.be.revertedWithCustomError(counter, "Unauthorized") .withArgs(banned.address); // Events await expect(counter.inc()).to.emit(counter, "Increment"); await expect(counter.inc()).to.emit(counter, "Increment").withArgs(1n); // ETH balance changes (positive = received, negative = spent, before gas) await expect(game.claim()).to.changeEtherBalance(ethers, winner, PRIZE); await expect(game.claim()).to.changeEtherBalances( ethers, [winner, loser], [PRIZE, -STAKE], ); ``` For plain assertions (equality, arrays, types), use `expect` from chai directly without the Ethereum matchers.