-
-
Notifications
You must be signed in to change notification settings - Fork 38
Adds new @metamask/smart-accounts-kit-x402 package #236
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
jeffsmale90
wants to merge
16
commits into
main
Choose a base branch
from
experimental/x402
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
0fd3a4d
Vibe code x402 client and server, example client and server
jeffsmale90 94ffc3a
Update server - add extra.facilitatorAddress if provided by facilitat…
jeffsmale90 8ee224f
Move implementation into @metamask/smart-accounts-kit-x402 package
jeffsmale90 3f26a7d
Passthrough for non-7710 payment methods
jeffsmale90 0d69537
Math.floor the expiry
jeffsmale90 f22ef54
Server now accepts both eip-3009 and erc-7710 payments
jeffsmale90 d240346
Update yarn.lock
jeffsmale90 02e12be
Remove example project
jeffsmale90 a8ecef6
update yarn.lock
jeffsmale90 e01d71e
Kind.extras may have 'facilitatorAddresses' (plural) instead of singular
jeffsmale90 d2b2575
Add unit tests
jeffsmale90 228e120
Fix linting
jeffsmale90 d0d8131
Update changelog
jeffsmale90 1af3006
@metamask/smart-accounts-kit-x402 version changed to 0.0.0
jeffsmale90 30085fa
Revert yarn.config.cjs
jeffsmale90 66cc06a
Stringify invalid transfer method when creating error object
jeffsmale90 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to this project will be documented in this file. | ||
|
|
||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| ### Added | ||
|
|
||
| - New @metamask/smart-accounts-kit-x402 package providing plugins to @x402 packages ([#236](https://github.com/MetaMask/smart-accounts-kit/pull/236)) | ||
|
|
||
| [Unreleased]: https://github.com/metamask/smart-accounts-kit/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| Apache License | ||
| Version 2.0, January 2004 | ||
| http://www.apache.org/licenses/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| MIT No Attribution License (MIT-0) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # @metamask/smart-accounts-kit-x402 | ||
|
|
||
| x402 adapters for ERC-7710 payment requirement publishing and payload creation. | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| yarn add @metamask/smart-accounts-kit-x402 | ||
| npm install @metamask/smart-accounts-kit-x402 | ||
| ``` | ||
|
|
||
| ## Exports | ||
|
|
||
| - `x402Erc7710Client` | ||
| - `x402Erc7710Server` | ||
| - `x402ExactEvmErc7710ServerScheme` | ||
|
|
||
| ## Notes | ||
|
|
||
| This package intentionally does not depend on `@metamask/smart-accounts-kit`. | ||
| Consumers provide delegation payloads via `x402DelegationProvider`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // eslint-disable-next-line | ||
| import baseConfig from '../../shared/config/base.eslint.mjs'; | ||
|
|
||
| const withX402NamingExceptions = baseConfig.map((entry) => { | ||
| const namingConventionRule = | ||
| entry.rules?.['@typescript-eslint/naming-convention']; | ||
|
|
||
| if (!Array.isArray(namingConventionRule)) { | ||
| return entry; | ||
| } | ||
|
|
||
| const [level, ...conventions] = namingConventionRule; | ||
|
|
||
| return { | ||
| ...entry, | ||
| rules: { | ||
| ...entry.rules, | ||
| '@typescript-eslint/naming-convention': [ | ||
| level, | ||
| { | ||
| selector: ['class', 'typeAlias'], | ||
| filter: { | ||
| regex: '^x402[A-Z].*$', | ||
| match: true, | ||
| }, | ||
| format: null, | ||
| }, | ||
| ...conventions, | ||
| ], | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| const config = [ | ||
| ...withX402NamingExceptions, | ||
| { | ||
| files: ['**/*.ts', '**/*.tsx'], | ||
| rules: { | ||
| 'new-cap': [ | ||
| 'error', | ||
| { | ||
| newIsCap: true, | ||
| newIsCapExceptionPattern: '^x402[A-Z]', | ||
| capIsNew: true, | ||
| properties: true, | ||
| }, | ||
| ], | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| export default config; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| { | ||
| "name": "@metamask/smart-accounts-kit-x402", | ||
| "version": "0.0.0", | ||
| "description": "x402 adapters for MetaMask smart accounts and ERC-7710", | ||
| "license": "(MIT-0 OR Apache-2.0)", | ||
| "type": "module", | ||
| "keywords": [ | ||
| "MetaMask", | ||
| "Ethereum" | ||
| ], | ||
| "homepage": "https://github.com/metamask/smart-accounts-kit/tree/main/packages/smart-accounts-kit-x402#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/metamask/smart-accounts-kit/issues" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/metamask/smart-accounts-kit.git" | ||
| }, | ||
| "main": "./dist/index.cjs", | ||
| "module": "./dist/index.mjs", | ||
| "types": "./dist/index.d.ts", | ||
| "files": [ | ||
| "dist/**", | ||
| "dist/" | ||
| ], | ||
| "exports": { | ||
| ".": { | ||
| "require": { | ||
| "types": "./dist/index.d.cts", | ||
| "default": "./dist/index.cjs" | ||
| }, | ||
| "import": { | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.mjs" | ||
| } | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "engines": { | ||
| "node": "^18.18 || >=20" | ||
| }, | ||
| "sideEffects": false, | ||
| "scripts": { | ||
| "build": "yarn typecheck && tsup", | ||
| "typecheck": "tsc --noEmit", | ||
| "test": "vitest run --coverage", | ||
| "test:watch": "vitest watch", | ||
| "lint": "yarn lint:eslint", | ||
| "lint:eslint": "eslint . --cache --ext js,ts", | ||
| "lint:fix": "yarn lint:eslint --fix", | ||
| "changelog:update": "../../scripts/update-changelog.sh @metamask/smart-accounts-kit-x402", | ||
| "changelog:validate": "../../scripts/validate-changelog.sh @metamask/smart-accounts-kit-x402" | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "registry": "https://registry.npmjs.org/" | ||
| }, | ||
| "peerDependencies": { | ||
| "@x402/core": "^2.12.0", | ||
| "@x402/evm": "^2.12.0", | ||
| "viem": "^2.31.4" | ||
| }, | ||
| "devDependencies": { | ||
| "@metamask/auto-changelog": "^5.0.2", | ||
| "@x402/core": "^2.12.0", | ||
| "@x402/evm": "^2.12.0", | ||
| "eslint": "^9.39.2", | ||
| "prettier": "^3.5.3", | ||
| "tsup": "^8.5.0", | ||
| "typescript": "5.5.4", | ||
| "viem": "2.31.4", | ||
| "vitest": "^3.2.4" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export { | ||
| x402Erc7710Client, | ||
| type x402DelegationProvider, | ||
| type x402DelegationPaymentPayload, | ||
| type x402PaymentRequirements, | ||
| type x402PaymentPayloadResult, | ||
| type x402SchemeNetworkClientLike, | ||
| type x402Erc7710ClientConfig, | ||
| } from './x402Client'; | ||
| export { x402Erc7710Server, type x402Erc7710ServerConfig } from './x402Server'; | ||
| export { x402ExactEvmErc7710ServerScheme } from './x402ExactEvmErc7710ServerScheme'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| import { type Hex, getAddress, isHex } from 'viem'; | ||
|
|
||
| export type x402PaymentRequirements = { | ||
| scheme: string; | ||
| network: string; | ||
| asset: string; | ||
| amount: string; | ||
| payTo: string; | ||
| maxTimeoutSeconds: number; | ||
| extra?: Record<string, unknown>; | ||
| }; | ||
|
|
||
| export type x402PaymentPayloadResult = { | ||
| x402Version: number; | ||
| payload: Record<string, unknown>; | ||
| extensions?: Record<string, unknown>; | ||
| }; | ||
|
|
||
| export type x402DelegationPaymentPayload = { | ||
| delegationManager: Hex; | ||
| permissionContext: Hex; | ||
| delegator: Hex; | ||
| }; | ||
|
|
||
| export type x402DelegationProvider = ( | ||
| paymentRequirements: x402PaymentRequirements, | ||
| ) => Promise<x402DelegationPaymentPayload>; | ||
|
|
||
| export type x402SchemeNetworkClientLike = { | ||
| readonly scheme: string; | ||
| createPaymentPayload: ( | ||
| x402Version: number, | ||
| paymentRequirements: x402PaymentRequirements, | ||
| context?: Record<string, unknown>, | ||
| ) => Promise<x402PaymentPayloadResult>; | ||
| }; | ||
|
|
||
| export type x402Erc7710ClientConfig = { | ||
| delegationProvider: x402DelegationProvider; | ||
| fallbackClient?: x402SchemeNetworkClientLike; | ||
| }; | ||
|
|
||
| /** | ||
| * Normalize and validate a delegation payload before publishing it. | ||
| * | ||
| * @param payload - Delegation payload returned by the configured provider. | ||
| * @returns The normalized payload with checksum addresses. | ||
| */ | ||
| function normalizeDelegationPayload( | ||
| payload: x402DelegationPaymentPayload, | ||
| ): x402DelegationPaymentPayload { | ||
| if (!isHex(payload.permissionContext) || payload.permissionContext === '0x') { | ||
| throw new Error( | ||
| 'Invalid delegation payload: permissionContext must be non-empty hex data', | ||
| ); | ||
| } | ||
|
|
||
| return { | ||
| delegationManager: getAddress(payload.delegationManager), | ||
| permissionContext: payload.permissionContext, | ||
| delegator: getAddress(payload.delegator), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * x402 `SchemeNetworkClient`-compatible implementation for ERC-7710 payments. | ||
| * | ||
| * This class uses structural typing and intentionally does not import x402 types, | ||
| * so it can be consumed without adding a direct dependency on x402 packages. | ||
| */ | ||
| export class x402Erc7710Client { | ||
| readonly scheme = 'exact'; | ||
|
|
||
| readonly #delegationProvider: x402DelegationProvider; | ||
|
|
||
| readonly #fallbackClient?: x402SchemeNetworkClientLike; | ||
|
|
||
| constructor(config: x402Erc7710ClientConfig) { | ||
| this.#delegationProvider = config.delegationProvider; | ||
| this.#fallbackClient = config.fallbackClient; | ||
| } | ||
|
|
||
| async createPaymentPayload( | ||
| x402Version: number, | ||
| paymentRequirements: x402PaymentRequirements, | ||
| context?: Record<string, unknown>, | ||
| ): Promise<x402PaymentPayloadResult> { | ||
| const assetTransferMethod = paymentRequirements.extra?.assetTransferMethod; | ||
|
|
||
| if (assetTransferMethod !== 'erc7710') { | ||
| if (this.#fallbackClient) { | ||
| return this.#fallbackClient.createPaymentPayload( | ||
| x402Version, | ||
| paymentRequirements, | ||
| context, | ||
| ); | ||
| } | ||
|
|
||
| const invalidAssetTransferMethod = | ||
| typeof assetTransferMethod === 'string' | ||
| ? `"${assetTransferMethod}"` | ||
| : JSON.stringify(assetTransferMethod); | ||
|
|
||
| throw new Error( | ||
| `x402Erc7710Client can only process assetTransferMethod "erc7710". Received: ${invalidAssetTransferMethod}$`, | ||
| ); | ||
|
jeffsmale90 marked this conversation as resolved.
|
||
| } | ||
|
|
||
| const delegation = await this.#delegationProvider(paymentRequirements); | ||
|
|
||
| return { | ||
| x402Version, | ||
| payload: normalizeDelegationPayload(delegation), | ||
| }; | ||
| } | ||
| } | ||
40 changes: 40 additions & 0 deletions
40
packages/smart-accounts-kit-x402/src/x402ExactEvmErc7710ServerScheme.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import type { Network, PaymentRequirements } from '@x402/core/types'; | ||
| import { ExactEvmScheme } from '@x402/evm/exact/server'; | ||
|
|
||
| import { x402Erc7710Server } from './x402Server'; | ||
|
|
||
| /** | ||
| * Exact EVM server scheme that injects ERC-7710 payment requirement fields. | ||
| */ | ||
| export class x402ExactEvmErc7710ServerScheme extends ExactEvmScheme { | ||
| readonly #erc7710Server = new x402Erc7710Server(); | ||
|
|
||
| async enhancePaymentRequirements( | ||
| paymentRequirements: PaymentRequirements, | ||
| supportedKind: { | ||
| x402Version: number; | ||
| scheme: string; | ||
| network: Network; | ||
| extra?: Record<string, unknown>; | ||
| }, | ||
| facilitatorExtensions: string[], | ||
| ): Promise<PaymentRequirements> { | ||
| const baseRequirements = await super.enhancePaymentRequirements( | ||
| paymentRequirements, | ||
| supportedKind, | ||
| facilitatorExtensions, | ||
| ); | ||
|
|
||
| if (baseRequirements.extra?.assetTransferMethod !== 'erc7710') { | ||
| return baseRequirements; | ||
| } | ||
|
|
||
| const enhancedRequirements = | ||
| await this.#erc7710Server.enhancePaymentRequirements( | ||
| baseRequirements, | ||
| supportedKind, | ||
| ); | ||
|
|
||
| return enhancedRequirements as PaymentRequirements; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray
$character in error message stringLow Severity
The error message template literal has a trailing
$after the${invalidAssetTransferMethod}interpolation, producing messages likeReceived: "eip3009"$instead ofReceived: "eip3009". The tests don't catch this because Vitest's.toThrow(string)performs a substring match, so the expected string without the$still matches.Reviewed by Cursor Bugbot for commit 66cc06a. Configure here.