mcpField
H
hardhat-toolbox-viem

hardhat-toolbox-viem

github.com/nomicfoundation开发256 次安装2 周前更新
代码仓库

关于 hardhat-toolbox-viem

面向使用 viem 的 Hardhat 项目的技能,适用于编写 TypeScript 合约测试的开发者,涵盖类型化合约交互和以太坊专用断言。

主要功能

  1. 01类型化 viem 客户端
  2. 02类型化合约部署与查找
  3. 03基于 fixture 的部署
  4. 04以太坊回滚与事件断言

适用场景

  1. 01在 Hardhat 测试中部署合约
  2. 02检查合约回滚和事件
  3. 03编写类型安全的合约交互

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nomicfoundation/hardhat-skills --skill hardhat-toolbox-viem

安装后在 Claude Code 中输入/hardhat-toolbox-viem确认已加载。

详细介绍

Hardhat 工具箱:viem

此技能基于核心 hardhat 技能。请先加载该技能,了解测试组织方式、network.create() 的结构、networkHelpers、fixture 以及类型检查工作流。以下内容都依赖于 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
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...");

避免使用 walletClient.writeContract 与合约交互——它没有 ABI 类型支持,因此错误的参数也能通过。优先使用 viem.deployContract 或 viem.getContractAt 返回的类型化实例。

在 loadFixture 设置函数中(外围模式请参见 hardhat 技能),viem.deployContract 是标准的部署步骤:

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

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

viem.assertions:Ethereum 专用断言

使用 viem.assertions 进行合约专用检查。将未等待的交易 Promise 作为第一个参数传入:

// 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,
});

*WithArgs 匹配器(revertWithCustomErrorWithArgs 和 emitWithArgs)允许在任意参数位置传入 (value) => boolean 谓词,也可以与具体值搭配使用。该插件还提供了 anyValue 助手,用于不关心的参数位置:

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"],
);

对于普通的 TypeScript 断言(相等性、数组、类型),请使用 node:assert/strict。

评论

支持 Markdown