Skip to content

fix: drop viem from x402 address validation#246

Merged
jeffsmale90 merged 6 commits into
MetaMask:mainfrom
peterxing:fix/issue-243-drop-x402-viem-address-validation
May 26, 2026
Merged

fix: drop viem from x402 address validation#246
jeffsmale90 merged 6 commits into
MetaMask:mainfrom
peterxing:fix/issue-243-drop-x402-viem-address-validation

Conversation

@peterxing

@peterxing peterxing commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace the direct viem address/hex helpers in @metamask/x402 with local Ethereum validation helpers.
  • Keep EIP-55 checksum normalization for ERC-7710 delegation/facilitator addresses.
  • Remove viem from @metamask/x402 peer/dev dependencies and add focused checksum regression coverage.

Verification

  • corepack yarn workspace @metamask/x402 lint
  • corepack yarn workspace @metamask/x402 typecheck
  • corepack yarn workspace @metamask/x402 test — 3 files / 21 tests passed
  • corepack yarn workspace @metamask/x402 build
  • git diff --check

Closes #243


Note

Low Risk
Dependency swap and localized validation helpers in the x402 package; behavior is covered by existing and new tests with no auth or payment flow changes.

Overview
@metamask/x402 no longer depends on viem for address and hex handling. @metamask/utils is added as a direct dependency, and a local getAddress helper in ethereum.ts validates EIP-55 checksums (rejecting invalid mixed-case) while still accepting all-lowercase or all-uppercase inputs for normalization.

Client and server paths now import getAddress and Hex from that helper / @metamask/utils instead of viem. Delegation permissionContext checks use isStrictHexString (replacing viem’s isHex), which still rejects empty 0x. Facilitator address typing on the server uses Hex[] instead of viem’s Address.

viem is removed from peer and dev dependencies; the changelog and lockfile reflect the swap. Tests cover all-uppercase facilitator normalization and invalid mixed-case checksum errors.

Reviewed by Cursor Bugbot for commit 3d23e9d. Bugbot is set up for automated code reviews on this repo. Configure here.

@peterxing peterxing requested a review from a team as a code owner May 21, 2026 22:20

@jeffsmale90 jeffsmale90 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Hi @peterxing thanks for the contribution!

This looks good - I think we can drop @noble/hashes, and replace it with @metamask/utils (which uses @noble/hashes internally) .

The ticket does suggest implementing bespoke validation, but because we are doing checksumming, it probably makes sense to pull in the @metamask/utils dependency instead of implementing it in this package directly.

Comment thread packages/x402/src/ethereum.ts Outdated
}

return checksummed as Address;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it would make sense to add a dependency to @metamask/utils instead of @noble/hashes directly, and use the getChecksumAddress function instead of implementing it ourselves here in checksumAddress.

We could also drop the regex validation as this is implemented in getChecksumAddress in @metamask/utils - it throws Invalid hex address..

The checksum validation in getAddress looks good though.

Comment thread packages/x402/src/ethereum.ts Outdated
if (
address !== lowerAddress &&
address !== upperAddress &&
`0x${address}` !== checksummedAddress

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Instead of using string interpolation here, we can just compare value !== checksummedAddress

Suggested change
`0x${address}` !== checksummedAddress
value !== checksummedAddress

@peterxing

Copy link
Copy Markdown
Contributor Author

Addressed the review note in follow-up commit b06ec40.

Changes:

  • Replaced the direct @noble/hashes usage with @metamask/utils for checksum address normalization/validation.
  • Swapped the x402 package dependency from @noble/hashes to @metamask/utils and updated yarn.lock.
  • Kept the existing lowercase/uppercase/mixed-case checksum behavior covered by the current tests.

Verification run locally:

  • corepack yarn workspace @metamask/x402 typecheck
  • corepack yarn workspace @metamask/x402 test (21 passed)
  • corepack yarn workspace @metamask/x402 lint:eslint
  • corepack yarn workspace @metamask/x402 build
  • git diff --check

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit b06ec40. Configure here.

Comment thread packages/x402/src/ethereum.ts
@peterxing

Copy link
Copy Markdown
Contributor Author

Addressed the Cursor Bugbot follow-up in commit 81206b0.

The issue was that the all-uppercase no-checksum branch compared value against value.toUpperCase(), which uppercased the 0x prefix to 0X and rejected otherwise valid all-uppercase Ethereum addresses. I changed that comparison to preserve the lowercase 0x prefix while uppercasing only the address body, then added regression coverage for an all-uppercase facilitatorAddresses entry.

Verification run locally:

  • corepack yarn workspace @metamask/x402 typecheck
  • corepack yarn workspace @metamask/x402 test (3 files / 22 tests passed)
  • corepack yarn workspace @metamask/x402 lint:eslint
  • corepack yarn workspace @metamask/x402 build
  • git diff --check (clean; Windows line-ending warnings only)

No wallet activity, signing, payments, or live x402 calls were used.

@jeffsmale90 jeffsmale90 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good - a couple things that can be removed, and replaced with references to @metamask/utils

Comment thread packages/x402/src/ethereum.ts Outdated
Comment on lines +6 to +16
const HEX_REGEX = /^0x[0-9a-fA-F]*$/u;

/**
* Check whether a value is a 0x-prefixed hexadecimal string.
*
* @param value - Value to inspect.
* @returns True when the value is hex data.
*/
export function isHex(value: unknown): value is Hex {
return typeof value === 'string' && HEX_REGEX.test(value);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can remove the isHex util and HEX_REGEX from here, and use the isStrictHexString function exported from @metamask/utils in all cases where we presently call isHex

It ensures non-zero length '0x' prefixed hex string.

Comment thread packages/x402/src/ethereum.ts Outdated
Comment on lines +3 to +4
export type Address = `0x${string}`;
export type Hex = `0x${string}`;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We can also remove the Hex and Address types and use the Hex type exported from @metamask/utils in all places where we reference these types (Hex and Address are functionally identical, so I don't think there's value in distinguishing unless there's an actual difference in the type).

@mj-kiwi mj-kiwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, seems the isHex function isn't necessary

Comment thread packages/x402/src/x402Client.ts Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As @jeffsmale90 said, isHex isn't needed. We can use isStrictHexString from @metamask/utils and revise the code accordingly.

Suggested change
if (!isStrictHexString(payload.permissionContext)) {

@jeffsmale90

Copy link
Copy Markdown
Collaborator

@peterxing You'll also need to add a changelog line to /packages/smart-accounts-kit/CHANGELOG.md

@peterxing

Copy link
Copy Markdown
Contributor Author

Addressed the requested changelog update in commit 6a892cc.

Changes:

  • Added the requested entry to packages/smart-accounts-kit/CHANGELOG.md.
  • Mirrored the entry in packages/x402/CHANGELOG.md because the package changes are in @metamask/x402.

Verification:

  • corepack yarn prettier --check packages\\x402\\CHANGELOG.md packages\\smart-accounts-kit\\CHANGELOG.md
  • git diff --check

I could not run corepack yarn workspace @metamask/x402 changelog:validate locally because the script invokes bash, and this Windows host resolves that to a broken WSL install. No code paths changed in this commit.

@peterxing

Copy link
Copy Markdown
Contributor Author

Addressed the latest review notes in 571bd1b:

  • removed the remaining local Address/Hex aliases from the x402 code path and now use Hex from @metamask/utils
  • replaced the client-side isHex call with isStrictHexString
  • kept the uppercase-address checksum regression coverage from the previous commit

Verification on this branch:

  • corepack yarn workspace @metamask/x402 typecheck
  • corepack yarn workspace @metamask/x402 test -> 22 passed
  • corepack yarn workspace @metamask/x402 lint:eslint
  • corepack yarn prettier --check packages\x402\src\ethereum.ts packages\x402\src\x402Client.ts packages\x402\src\x402Server.ts
  • git diff --check


### Changed

- Use `@metamask/utils` for x402 address checksum validation instead of `viem`. ([#246](https://github.com/MetaMask/smart-accounts-kit/pull/246))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
- Use `@metamask/utils` for x402 address checksum validation instead of `viem`. ([#246](https://github.com/MetaMask/smart-accounts-kit/pull/246))

@peterxing

Copy link
Copy Markdown
Contributor Author

Addressed the latest changelog suggestion in commit 3d23e9d.

Change:

  • Removed the packages/smart-accounts-kit/CHANGELOG.md entry, leaving the x402 package changelog entry as the package-local release note for this PR.

Verification:

  • corepack yarn prettier --check packages\smart-accounts-kit\CHANGELOG.md packages\x402\CHANGELOG.md
  • git diff --check

No code paths changed.

@peterxing

Copy link
Copy Markdown
Contributor Author

The remaining Check changelog failure looks like a workflow checkout issue rather than a changelog-content failure.

The job runs actions/checkout against MetaMask/smart-accounts-kit with ref: fix/issue-243-drop-x402-viem-address-validation, but that branch only exists on the fork (peterxing/smart-accounts-kit). The failing log ends with:

A branch or tag with the name 'fix/issue-243-drop-x402-viem-address-validation' could not be found

Current state after the last requested change:

  • packages/x402/CHANGELOG.md still has the package-local Unreleased entry for this x402 package change.
  • packages/smart-accounts-kit/CHANGELOG.md no longer has the extra entry, per the latest review suggestion.
  • Main build/lint/test jobs and Cursor Bugbot are green on the latest head.

I cannot fix that checkout behavior from the fork branch. If the x402 changelog entry is acceptable as-is, this likely needs a maintainer-side rerun/workflow adjustment or a label/action that skips the branch checkout path for fork PRs.

@mj-kiwi mj-kiwi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@jeffsmale90 jeffsmale90 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me!

@jeffsmale90 jeffsmale90 merged commit b1db1e0 into MetaMask:main May 26, 2026
17 of 18 checks passed

Copy link
Copy Markdown
Contributor Author

Thanks for merging this.

If useful for release confidence after the x402 package update, I can do a fixed-scope A$390 package-consumer readback: install from the published/built package in a clean app, verify peer dependency warnings are gone, exercise the server/client import surfaces, and report any install/release-note gaps as a concise checklist.

Contact: info@transhumanism.com.au

Copy link
Copy Markdown
Contributor Author

Quick release-side follow-up now that the merged x402 package change appears to be on npm (@metamask/x402 latest is 0.2.0 as checked 2026-06-07 UTC): I can still do the fixed-scope A$390 package-consumer readback against the published package rather than the PR branch.

Scope would stay narrow: clean app install, peer/dependency warning check, server/client import smoke, and a concise release-readiness checklist. No wallet activity, signing, payments, live x402 calls, or private credentials involved.

Contact: info@transhumanism.com.au

Copy link
Copy Markdown
Contributor Author

MetaMask's June 8 Agent Wallet early-access launch creates a timely release-side check for this x402 dependency change: https://metamask.io/news/introducing-metamask-agent-wallet

If useful, I can do a fixed-scope A$390 agent-wallet/x402 consumer readback against the published package and current docs surface: clean install/import smoke, peer/dependency warning check, x402 server/client example sanity review, and a short checklist of any packaging or release-note gaps relevant to agent-wallet builders.

Scope stays read-only and local/simulated: no wallet activity, signing, payments, live x402 calls, funds, private credentials, or early-access account access.

Contact: info@transhumanism.com.au

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove viem dependency from @metamask/x402

3 participants