Skip to content

Viem matchers first iteration #6574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 33 commits into
base: v-next
Choose a base branch
from

Conversation

ChristopherDedominici
Copy link
Contributor

@ChristopherDedominici ChristopherDedominici commented Apr 14, 2025

Parent issue

POC for the viem matchers.

Note:

  • The tests are not exhaustive like those in the chai-matchers plugin. This is intentional, we first need to finalize the implementation approach we want to take with these matchers.
    I've included test scenarios that cover basic cases, mainly to provide a rough idea of how the matchers are intended to be used.
  • The functions’ documentation is also missing for now, it will be added once we finalize the matchers

Method implemented:

Main methods:

  • balancesHaveChanged
  • emit
  • emitWithArgs
  • revert
  • revertWith

Utils:

  • areApproximatelyEqual
  • properAddress
  • properChecksumAddress

How the matchers object is implemented (pseudocode):

connection.viem.assertions = {
    utils: {
        areApproximatelyEqual,
        properAddress,
        properChecksumAddress
    },

    balancesHaveChanged
    emit
    emitWithArgs
    revert
    revertWith
}

@ChristopherDedominici ChristopherDedominici added the v-next A Hardhat v3 development task label Apr 14, 2025
@ChristopherDedominici ChristopherDedominici self-assigned this Apr 14, 2025
@github-project-automation github-project-automation bot moved this to Backlog in Hardhat Apr 14, 2025
Copy link

changeset-bot bot commented Apr 14, 2025

⚠️ No Changeset found

Latest commit: 07b8cdc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Apr 14, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
hardhat ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 16, 2025 2:41pm

Copy link
Contributor

github-actions bot commented Apr 14, 2025

hardhat

Total size of the bundle: 208M
Total number of dependencies (including transitive): 111

List of dependencies (sorted by size)
203M	total
22M	@ignored/edr-optimism-linux-x64-musl
22M	@ignored/edr-optimism-linux-x64-gnu
20M	esbuild
20M	@ignored/edr-optimism-linux-arm64-musl
20M	@ignored/edr-optimism-linux-arm64-gnu
19M	@ignored/edr-optimism-win32-x64-msvc
16M	@ignored/edr-optimism-darwin-x64
15M	@ignored/edr-optimism-darwin-arm64
7.4M	@opentelemetry/semantic-conventions
6.2M	@sentry/core
4.0M	@sentry/node
3.1M	@opentelemetry/api
2.5M	micro-eth-signer
2.3M	@opentelemetry/core
1.9M	@opentelemetry/resources
1.9M	@noble/curves
1.7M	undici
1.6M	@opentelemetry/sdk-trace-base
1.2M	@noble/hashes
956K	@opentelemetry/instrumentation
864K	@streamparser/json
864K	@sentry/opentelemetry
860K	@nomicfoundation/hardhat-utils
856K	zod
760K	@opentelemetry/api-logs
676K	import-in-the-middle
624K	resolve
624K	micro-packed
584K	tsx
572K	acorn
544K	fast-equals
492K	@scure/bip39
452K	@nomicfoundation/hardhat-errors
408K	json-stream-stringify
368K	ethereum-cryptography
352K	@ignored/edr
332K	@streamparser/json-node
320K	pg-protocol
320K	enquirer
300K	@opentelemetry/instrumentation-http
288K	semver
288K	@ignored/edr-optimism
224K	@opentelemetry/instrumentation-pg
212K	@opentelemetry/instrumentation-graphql
200K	undici-types
196K	ws
188K	@opentelemetry/instrumentation-mongodb
180K	@opentelemetry/instrumentation-undici
176K	@opentelemetry/instrumentation-express
172K	@opentelemetry/instrumentation-amqplib
168K	@scure/base
164K	cjs-module-lexer
160K	@opentelemetry/instrumentation-fastify
156K	@opentelemetry/instrumentation-hapi
148K	@opentelemetry/instrumentation-redis-4
148K	@opentelemetry/instrumentation-mysql
144K	@opentelemetry/instrumentation-fs
136K	get-tsconfig
136K	adm-zip
136K	@opentelemetry/instrumentation-koa
132K	@opentelemetry/instrumentation-knex
124K	@opentelemetry/instrumentation-mongoose
124K	@opentelemetry/instrumentation-ioredis
120K	@opentelemetry/instrumentation-mysql2
120K	@opentelemetry/instrumentation-connect
116K	@opentelemetry/instrumentation-kafkajs
112K	@opentelemetry/instrumentation-tedious
112K	@opentelemetry/context-async-hooks
96K	@scure/bip32
96K	@opentelemetry/instrumentation-dataloader
92K	chalk
88K	@prisma/instrumentation
80K	@opentelemetry/instrumentation-generic-pool
76K	pg-types
72K	@nomicfoundation/solidity-analyzer
68K	debug
68K	@opentelemetry/sql-common
68K	@opentelemetry/instrumentation-lru-memoizer
64K	@nomicfoundation/hardhat-zod-utils
60K	acorn-import-attributes
60K	@opentelemetry/redis-common
56K	rfdc
56K	is-core-module
56K	function-bind
52K	@types/tedious
48K	shimmer
48K	ansi-colors
44K	resolve.exports
40K	resolve-pkg-maps
40K	require-in-the-middle
40K	forwarded-parse
40K	@types/mysql
36K	supports-preserve-symlinks-flag
36K	p-map
36K	@types/pg
32K	hasown
28K	xtend
24K	strip-ansi
24K	postgres-interval
24K	postgres-array
24K	env-paths
24K	ansi-regex
20K	postgres-date
20K	postgres-bytea
20K	pg-int8
20K	path-parse
20K	ms
20K	module-details-from-path
20K	@types/shimmer
20K	@types/pg-pool
20K	@types/connect

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a first iteration of viem matchers that provide custom assertions for balances, event emissions, and function reverts. It also adds utility functions for validating addresses and comparing big numbers, and integrates these matchers via Hardhat network hook handlers.

  • Introduces main matcher functions: balancesHaveChanged, revert, revertWith, emit, and emitWithArgs.
  • Implements utility methods (properAddress, properChecksumAddress, areApproximatelyEqual) to support matcher operations.
  • Adds hook handlers for initializing viem matchers during network connection setup.

Reviewed Changes

Copilot reviewed 32 out of 37 changed files in this pull request and generated no comments.

Show a summary per file
File Description
v-next/hardhat-viem-matchers/src/internal/viem-matchers-initialization.ts Initializes viem matchers by instantiating the HardhatViemMatchersImpl.
v-next/hardhat-viem-matchers/src/internal/matchers/utils/proper-checksum-address.ts Validates checksum addresses using helper functions from hardhat-utils.
v-next/hardhat-viem-matchers/src/internal/matchers/utils/proper-address.ts Uses assert to check for valid address formats.
v-next/hardhat-viem-matchers/src/internal/matchers/utils/big-number/are-approximately-equal.ts Provides a utility to check whether two bigints are approximately equal.
v-next/hardhat-viem-matchers/src/internal/matchers/revert/utils.ts Implements the core logic for testing function reverts and extracting reasons.
v-next/hardhat-viem-matchers/src/internal/matchers/revert/revert.ts Exposes a simple revert matcher wrapping around the core revert logic.
v-next/hardhat-viem-matchers/src/internal/matchers/revert/revert-with.ts Provides functionality to assert that a function reverts with a specific reason.
v-next/hardhat-viem-matchers/src/internal/matchers/emit/utils.ts Implements event emission detection by parsing logs from the viem client.
v-next/hardhat-viem-matchers/src/internal/matchers/emit/emit.ts Defines a matcher for asserting that an event is emitted.
v-next/hardhat-viem-matchers/src/internal/matchers/emit/emit-with-args.ts Enhances event emission assertions by comparing expected event arguments.
v-next/hardhat-viem-matchers/src/internal/matchers/balances-have-changed.ts Implements balance change verification based on pre/post balance snapshots.
v-next/hardhat-viem-matchers/src/internal/hook-handlers/network.ts Integrates matcher initialization into Hardhat's network connection workflow
v-next/hardhat-viem-matchers/src/index.ts Exports the Hardhat plugin and sets up dependency and hook handler configuration.
v-next/hardhat-viem-matchers/README.md Contains a placeholder for plugin documentation.
v-next/hardhat-viem-matchers/.eslintrc.cjs Configures ESLint settings based on a shared configuration.
Files not reviewed (5)
  • pnpm-lock.yaml: Language not supported
  • v-next/hardhat-viem-matchers/.gitignore: Language not supported
  • v-next/hardhat-viem-matchers/.prettierignore: Language not supported
  • v-next/hardhat-viem-matchers/LICENSE: Language not supported
  • v-next/hardhat-viem-matchers/package.json: Language not supported

);

// Convert to bigint because the values in the logs are always bigInt
argsToCheck[param.name] =
Copy link
Contributor Author

@ChristopherDedominici ChristopherDedominici Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: If the user passes an int as the expected argument, but the assertion fails because the actual value differs, the error message will display the received value as a bigint. This can be confusing.

Example: The user passes [1] as an argument, but the emitted value is 2. The error will be:

Expected: { i: 1n }
Got: { i: 2n }

This happens because, in the Viem logs, all values are apparently shown as bigint.

);

// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO
return parsedLogs as unknown as Array<{ args?: Record<string, any> }>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Viem typing is very complex, here I only need the args object that in the case of the events it should be populated

@@ -0,0 +1,130 @@
// SPDX-License-Identifier: MIT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods were copied from chai-matchers. Most are currently unused but will be utilized once more extensive testing is added.

}, [
{
address: bobWalletClient.account.address,
amount: -1000000023255859375000n, // TODO: gas fees that change the value?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that the balance variation in the sender is bigger than the amount sent. I assume it's because of gas fee?

@ChristopherDedominici ChristopherDedominici marked this pull request as ready for review April 23, 2025 15:15
Comment on lines +8 to +14
async newConnection<ChainTypeT extends ChainType | string>(
context: HookContext,
next: (
nextContext: HookContext,
) => Promise<NetworkConnection<ChainTypeT>>,
) {
const connection: NetworkConnection<ChainTypeT> = await next(context);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why all the explicit types here? If I remove them, the code continues to compile and the inferred types are correct. Is this a new convention you guys are following in HH3?

    async newConnection(
      context,
      next,
    ) {
      const connection = await next(context);

Comment on lines 25 to 27
reason = error.details
.split("reverted with reason string ")[1]
.replaceAll("'", "");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is very future-proof. Ideally we want an API-stable way of getting the return data, and then we decode it ourselves. I think this will be tricky, for fun JSON-RPC reasons, but it's important.

assert.fail("The function was expected to revert, but it did not.");
}

function extractRevertData(
Copy link
Contributor Author

@ChristopherDedominici ChristopherDedominici May 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function to extract the revert reason from a Viem error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no changeset needed This PR doesn't require a changeset v-next A Hardhat v3 development task
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

Minimal port of assertions to viem
3 participants