nomicfoundation/hardhat-skills · Archived

hardhat-toolbox-viem

Use alongside the `hardhat` skill when the project depends on `@nomicfoundation/hardhat-toolbox-viem`.

First seen May 19, 2026

Installation

$ npx skills add nomicfoundation/hardhat-skills --skill hardhat-toolbox-viem

Summary

  • Use alongside the `hardhat` skill when the project depends on `@nomicfoundation/hardhat-toolbox-viem`.
  • Covers the viem clients exposed on `network.create()`, contract interaction (`viem.deployContract`, `read`, `write`, `getContractAt`), and `viem.assertions` (revert / event / balance assertions).

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Also in this package

Other skills from nomicfoundation/hardhat-skills.

npx skills add nomicfoundation/hardhat-skills

Browse all from nomicfoundation/hardhat-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 2
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

More metadata
package
@nomicfoundation/hardhat-toolbox-viem

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,455 B
  • docs SUMMARY.md 326 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 39 installs

SKILL.md

Hardhat toolbox: viem

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():

import { network } from "hardhat";
import { describe, it } from "node:test";
import assert from "node:assert/strict";

describe("Counter", async function () {
  const { viem, networkHelpers } = await network.create();
  // ...
});

viem: clients and contract interaction

// Clients
const publicClient = await viem.getPublicClient();
const [owner, alice, bob] = await viem.getWalletClients(); // default accounts
const testClient = await viem.getTestClient(); // dev-only operations

// Deploy a contract, returns a fully typed instance
const counter = await viem.deployContract("Counter");

// Read state (return type inferred from ABI)
const value = await counter.read.x();

// Write transactions. Args and options are type-checked against the ABI at
// compile time; passing wrong types, wrong argument counts, or `value` on a
// non-payable function is a TypeScript error
await counter.write.inc();
await counter.write.inc({ account: alice.account }); // different sender
await counter.write.incBy([3n]); // with args
await counter.write.deposit({ value: 10n ** 18n }); // no parameters and payable

// Attach to an already-deployed contract
const existing = await viem.getContractAt("Counter", "0xabc...");

Avoid using walletClient.writeContract to interact with contracts — it has no ABI typing, so wrong args slip through. Prefer the typed instance returned by viem.deployContract or viem.getContractAt.

Inside a loadFixture setup function (see the hardhat skill for the surrounding pattern), viem.deployContract is the canonical deploy step:

async function deployCounter() {
  const counter = await viem.deployContract("Counter");
  return { counter };
}

const { counter } = await networkHelpers.loadFixture(deployCounter);

viem.assertions: Ethereum-specific assertions

Use viem.assertions for contract-specific checks. Pass the unawaited transaction promise as the first argument:

// Reverts
await viem.assertions.revert(counter.write.inc({ account: banned }));
await viem.assertions.revertWith(
  counter.write.inc({ account: banned }),
  "Not authorized",
);
await viem.assertions.revertWithCustomError(
  counter.write.inc({ account: banned }),
  counter,
  "Unauthorized",
);
await viem.assertions.revertWithCustomErrorWithArgs(
  counter.write.inc({ account: banned }),
  counter,
  "Unauthorized",
  [banned],
);

// Events
await viem.assertions.emit(counter.write.inc(), counter, "Increment");
await viem.assertions.emitWithArgs(counter.write.inc(), counter, "Increment", [
  1n,
]);

// ETH balance changes (positive = received, negative = spent, before gas)
await viem.assertions.balancesHaveChanged(game.write.claim(), {
  [winner]: PRIZE,
  [loser]: -STAKE,
});

The *WithArgs matchers (revertWithCustomErrorWithArgs and emitWithArgs) accept a (value) => boolean predicate at any arg position, alongside concrete values. The plugin also ships an anyValue helper for positions you don't care about:

import { anyValue } from "@nomicfoundation/hardhat-toolbox-viem/predicates";

// Inline predicate at any arg position. Useful for ranges or computed conditions.
await viem.assertions.revertWithCustomErrorWithArgs(
  contract.write.failing(),
  contract,
  "BadValue",
  [(n: bigint) => n > 100n, "another error arg"],
);
await viem.assertions.emitWithArgs(
  counter.write.incBy([3n]),
  counter,
  "Increment",
  [(by: bigint) => by >= 1n],
);

// `anyValue` matches anything — handy for fields you don't care about.
await viem.assertions.revertWithCustomErrorWithArgs(
  contract.write.failing(),
  contract,
  "BadValue",
  [anyValue, "another error arg"],
);

For plain TypeScript assertions (equality, arrays, types), use node:assert/strict.