@x402x/extensions 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.mts +183 -0
- package/dist/client/index.d.ts +183 -0
- package/dist/client/index.js +255 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +251 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.d.mts +4 -182
- package/dist/index.d.ts +4 -182
- package/dist/index.js +87 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network } from '@x402/core/types';
|
|
2
|
+
import { x402Client } from '@x402/core/client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* x402x EVM Client Scheme with Router Settlement
|
|
6
|
+
*
|
|
7
|
+
* This scheme extends the standard EVM exact scheme to support x402x router settlement.
|
|
8
|
+
* The key difference is using a commitment hash (binding all settlement parameters)
|
|
9
|
+
* as the EIP-3009 nonce instead of a random value.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Client EVM signer interface
|
|
14
|
+
* Compatible with viem WalletClient and LocalAccount
|
|
15
|
+
*/
|
|
16
|
+
type ClientEvmSigner = {
|
|
17
|
+
readonly address: `0x${string}`;
|
|
18
|
+
signTypedData(message: {
|
|
19
|
+
domain: Record<string, unknown>;
|
|
20
|
+
types: Record<string, unknown>;
|
|
21
|
+
primaryType: string;
|
|
22
|
+
message: Record<string, unknown>;
|
|
23
|
+
}): Promise<`0x${string}`>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* EVM client implementation for the Exact payment scheme with x402x router settlement.
|
|
27
|
+
*
|
|
28
|
+
* This scheme uses a commitment hash as the EIP-3009 nonce to cryptographically bind
|
|
29
|
+
* all settlement parameters (salt, hook, hookData, etc.) to the user's signature,
|
|
30
|
+
* preventing parameter tampering attacks.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import { ExactEvmSchemeWithRouterSettlement } from '@x402x/extensions/client';
|
|
35
|
+
* import { x402Client } from '@x402/core/client';
|
|
36
|
+
*
|
|
37
|
+
* const signer = { address, signTypedData }; // viem WalletClient or LocalAccount
|
|
38
|
+
* const scheme = new ExactEvmSchemeWithRouterSettlement(signer);
|
|
39
|
+
*
|
|
40
|
+
* const client = new x402Client()
|
|
41
|
+
* .register('eip155:84532', scheme);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare class ExactEvmSchemeWithRouterSettlement implements SchemeNetworkClient {
|
|
45
|
+
private readonly signer;
|
|
46
|
+
readonly scheme = "exact";
|
|
47
|
+
/**
|
|
48
|
+
* Per-request router settlement extension (typically sourced from PaymentRequired.extensions).
|
|
49
|
+
*
|
|
50
|
+
* IMPORTANT: We do NOT put this on `paymentRequirements.extra`, because for x402 v2 the
|
|
51
|
+
* server matches paid requests by deep-equality between `paymentPayload.accepted` and the
|
|
52
|
+
* server-side `accepts[]`. Mutating `accepted` will cause "No matching payment requirements".
|
|
53
|
+
*/
|
|
54
|
+
private routerSettlementFromPaymentRequired?;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new ExactEvmSchemeWithRouterSettlement instance.
|
|
57
|
+
*
|
|
58
|
+
* @param signer - The EVM signer for client operations (viem WalletClient or LocalAccount)
|
|
59
|
+
*/
|
|
60
|
+
constructor(signer: ClientEvmSigner);
|
|
61
|
+
/**
|
|
62
|
+
* Set router-settlement extension data for the next payment payload creation.
|
|
63
|
+
*
|
|
64
|
+
* Intended to be called from an `x402Client.onBeforePaymentCreation` hook, which has access
|
|
65
|
+
* to `paymentRequired.extensions`.
|
|
66
|
+
*/
|
|
67
|
+
setRouterSettlementExtensionFromPaymentRequired(ext: unknown | undefined): void;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a payment payload for the Exact scheme with router settlement.
|
|
70
|
+
*
|
|
71
|
+
* This method:
|
|
72
|
+
* 1. Extracts settlement parameters from PaymentRequired.extensions
|
|
73
|
+
* 2. Calculates a commitment hash binding all parameters
|
|
74
|
+
* 3. Uses the commitment as the EIP-3009 nonce
|
|
75
|
+
* 4. Signs with settlementRouter as the 'to' address
|
|
76
|
+
*
|
|
77
|
+
* @param x402Version - The x402 protocol version (must be 2)
|
|
78
|
+
* @param paymentRequirements - The payment requirements from the server
|
|
79
|
+
* @returns Promise resolving to a payment payload
|
|
80
|
+
*
|
|
81
|
+
* @throws Error if x402Version is not 2
|
|
82
|
+
* @throws Error if x402x-router-settlement extension is missing
|
|
83
|
+
* @throws Error if required settlement parameters are missing
|
|
84
|
+
*/
|
|
85
|
+
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
86
|
+
/**
|
|
87
|
+
* Sign the EIP-3009 authorization using EIP-712
|
|
88
|
+
*
|
|
89
|
+
* @param authorization - The authorization to sign
|
|
90
|
+
* @param requirements - The payment requirements
|
|
91
|
+
* @param chainId - The chain ID
|
|
92
|
+
* @returns Promise resolving to the signature
|
|
93
|
+
*/
|
|
94
|
+
private signAuthorization;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* x402x Extension Handler
|
|
99
|
+
*
|
|
100
|
+
* Provides utilities to integrate x402x router settlement with official x402 SDK.
|
|
101
|
+
*
|
|
102
|
+
* The challenge: x402 v2 spec places extensions at root level in PaymentRequired,
|
|
103
|
+
* but x402x scheme needs settlement parameters from PaymentRequirements.extra.
|
|
104
|
+
*
|
|
105
|
+
* Solution: Provide two-layer API:
|
|
106
|
+
* 1. High-level: registerX402xScheme() - one line setup (recommended)
|
|
107
|
+
* 2. Low-level: injectX402xExtensionHandler() - flexible configuration
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Injects x402x extension handler into x402Client (Low-level API).
|
|
112
|
+
*
|
|
113
|
+
* IMPORTANT (x402 v2 + x402x multi-network):
|
|
114
|
+
* - Server returns per-option x402x info in `accepts[i].extra["x402x-router-settlement"]`
|
|
115
|
+
* - x402Client only passes `PaymentRequirements` to scheme (no root extensions)
|
|
116
|
+
* - Facilitator v2 reads from `paymentPayload.extensions["x402x-router-settlement"]`
|
|
117
|
+
*
|
|
118
|
+
* Solution: Copy the selected option's x402x info from `selectedRequirements.extra`
|
|
119
|
+
* into `paymentRequired.extensions` so it gets included in `paymentPayload.extensions`.
|
|
120
|
+
*
|
|
121
|
+
* This does NOT mutate `selectedRequirements` (which becomes `paymentPayload.accepted`),
|
|
122
|
+
* so v2 deepEqual matching still works.
|
|
123
|
+
*
|
|
124
|
+
* @param client - x402Client instance to inject handler into
|
|
125
|
+
* @param onRouterSettlementExtension - Callback to receive the per-request extension object
|
|
126
|
+
* @returns The same client instance for chaining
|
|
127
|
+
*
|
|
128
|
+
* @example Low-level API (for advanced users)
|
|
129
|
+
* ```typescript
|
|
130
|
+
* import { x402Client } from '@x402/core/client';
|
|
131
|
+
* import {
|
|
132
|
+
* injectX402xExtensionHandler,
|
|
133
|
+
* ExactEvmSchemeWithRouterSettlement
|
|
134
|
+
* } from '@x402x/extensions';
|
|
135
|
+
*
|
|
136
|
+
* const client = new x402Client();
|
|
137
|
+
* const scheme = new ExactEvmSchemeWithRouterSettlement(signer);
|
|
138
|
+
* injectX402xExtensionHandler(client, (ext) => scheme.setRouterSettlementExtensionFromPaymentRequired(ext))
|
|
139
|
+
* .register('eip155:84532', scheme);
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function injectX402xExtensionHandler(client: x402Client, onRouterSettlementExtension?: (extension: unknown | undefined) => void): x402Client;
|
|
143
|
+
/**
|
|
144
|
+
* Register x402x router settlement scheme with automatic extension handling (High-level API).
|
|
145
|
+
*
|
|
146
|
+
* This is the recommended way to set up x402x payments. It combines:
|
|
147
|
+
* 1. Extension handler injection (injectX402xExtensionHandler)
|
|
148
|
+
* 2. Scheme registration (ExactEvmSchemeWithRouterSettlement)
|
|
149
|
+
*
|
|
150
|
+
* Use this for the simplest integration - just provide your signer and network.
|
|
151
|
+
*
|
|
152
|
+
* @param client - x402Client instance
|
|
153
|
+
* @param network - Network identifier in CAIP-2 format (e.g., "eip155:84532")
|
|
154
|
+
* @param signer - EVM signer with address and signTypedData method
|
|
155
|
+
* @returns The client instance for chaining
|
|
156
|
+
*
|
|
157
|
+
* @example High-level API (recommended)
|
|
158
|
+
* ```typescript
|
|
159
|
+
* import { x402Client } from '@x402/core/client';
|
|
160
|
+
* import { registerX402xScheme } from '@x402x/extensions';
|
|
161
|
+
* import { useWalletClient } from 'wagmi';
|
|
162
|
+
*
|
|
163
|
+
* const { data: walletClient } = useWalletClient();
|
|
164
|
+
*
|
|
165
|
+
* const client = new x402Client();
|
|
166
|
+
* registerX402xScheme(client, 'eip155:84532', {
|
|
167
|
+
* address: walletClient.account.address,
|
|
168
|
+
* signTypedData: walletClient.signTypedData,
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* // That's it! Client is ready for x402x payments
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @example Multiple networks
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const client = new x402Client();
|
|
177
|
+
* registerX402xScheme(client, 'eip155:84532', signer); // Base Sepolia
|
|
178
|
+
* registerX402xScheme(client, 'eip155:8453', signer); // Base Mainnet
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare function registerX402xScheme(client: x402Client, network: Network, signer: ClientEvmSigner): x402Client;
|
|
182
|
+
|
|
183
|
+
export { type ClientEvmSigner, ExactEvmSchemeWithRouterSettlement, injectX402xExtensionHandler, registerX402xScheme };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayload, Network } from '@x402/core/types';
|
|
2
|
+
import { x402Client } from '@x402/core/client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* x402x EVM Client Scheme with Router Settlement
|
|
6
|
+
*
|
|
7
|
+
* This scheme extends the standard EVM exact scheme to support x402x router settlement.
|
|
8
|
+
* The key difference is using a commitment hash (binding all settlement parameters)
|
|
9
|
+
* as the EIP-3009 nonce instead of a random value.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Client EVM signer interface
|
|
14
|
+
* Compatible with viem WalletClient and LocalAccount
|
|
15
|
+
*/
|
|
16
|
+
type ClientEvmSigner = {
|
|
17
|
+
readonly address: `0x${string}`;
|
|
18
|
+
signTypedData(message: {
|
|
19
|
+
domain: Record<string, unknown>;
|
|
20
|
+
types: Record<string, unknown>;
|
|
21
|
+
primaryType: string;
|
|
22
|
+
message: Record<string, unknown>;
|
|
23
|
+
}): Promise<`0x${string}`>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* EVM client implementation for the Exact payment scheme with x402x router settlement.
|
|
27
|
+
*
|
|
28
|
+
* This scheme uses a commitment hash as the EIP-3009 nonce to cryptographically bind
|
|
29
|
+
* all settlement parameters (salt, hook, hookData, etc.) to the user's signature,
|
|
30
|
+
* preventing parameter tampering attacks.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import { ExactEvmSchemeWithRouterSettlement } from '@x402x/extensions/client';
|
|
35
|
+
* import { x402Client } from '@x402/core/client';
|
|
36
|
+
*
|
|
37
|
+
* const signer = { address, signTypedData }; // viem WalletClient or LocalAccount
|
|
38
|
+
* const scheme = new ExactEvmSchemeWithRouterSettlement(signer);
|
|
39
|
+
*
|
|
40
|
+
* const client = new x402Client()
|
|
41
|
+
* .register('eip155:84532', scheme);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare class ExactEvmSchemeWithRouterSettlement implements SchemeNetworkClient {
|
|
45
|
+
private readonly signer;
|
|
46
|
+
readonly scheme = "exact";
|
|
47
|
+
/**
|
|
48
|
+
* Per-request router settlement extension (typically sourced from PaymentRequired.extensions).
|
|
49
|
+
*
|
|
50
|
+
* IMPORTANT: We do NOT put this on `paymentRequirements.extra`, because for x402 v2 the
|
|
51
|
+
* server matches paid requests by deep-equality between `paymentPayload.accepted` and the
|
|
52
|
+
* server-side `accepts[]`. Mutating `accepted` will cause "No matching payment requirements".
|
|
53
|
+
*/
|
|
54
|
+
private routerSettlementFromPaymentRequired?;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new ExactEvmSchemeWithRouterSettlement instance.
|
|
57
|
+
*
|
|
58
|
+
* @param signer - The EVM signer for client operations (viem WalletClient or LocalAccount)
|
|
59
|
+
*/
|
|
60
|
+
constructor(signer: ClientEvmSigner);
|
|
61
|
+
/**
|
|
62
|
+
* Set router-settlement extension data for the next payment payload creation.
|
|
63
|
+
*
|
|
64
|
+
* Intended to be called from an `x402Client.onBeforePaymentCreation` hook, which has access
|
|
65
|
+
* to `paymentRequired.extensions`.
|
|
66
|
+
*/
|
|
67
|
+
setRouterSettlementExtensionFromPaymentRequired(ext: unknown | undefined): void;
|
|
68
|
+
/**
|
|
69
|
+
* Creates a payment payload for the Exact scheme with router settlement.
|
|
70
|
+
*
|
|
71
|
+
* This method:
|
|
72
|
+
* 1. Extracts settlement parameters from PaymentRequired.extensions
|
|
73
|
+
* 2. Calculates a commitment hash binding all parameters
|
|
74
|
+
* 3. Uses the commitment as the EIP-3009 nonce
|
|
75
|
+
* 4. Signs with settlementRouter as the 'to' address
|
|
76
|
+
*
|
|
77
|
+
* @param x402Version - The x402 protocol version (must be 2)
|
|
78
|
+
* @param paymentRequirements - The payment requirements from the server
|
|
79
|
+
* @returns Promise resolving to a payment payload
|
|
80
|
+
*
|
|
81
|
+
* @throws Error if x402Version is not 2
|
|
82
|
+
* @throws Error if x402x-router-settlement extension is missing
|
|
83
|
+
* @throws Error if required settlement parameters are missing
|
|
84
|
+
*/
|
|
85
|
+
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, "x402Version" | "payload">>;
|
|
86
|
+
/**
|
|
87
|
+
* Sign the EIP-3009 authorization using EIP-712
|
|
88
|
+
*
|
|
89
|
+
* @param authorization - The authorization to sign
|
|
90
|
+
* @param requirements - The payment requirements
|
|
91
|
+
* @param chainId - The chain ID
|
|
92
|
+
* @returns Promise resolving to the signature
|
|
93
|
+
*/
|
|
94
|
+
private signAuthorization;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* x402x Extension Handler
|
|
99
|
+
*
|
|
100
|
+
* Provides utilities to integrate x402x router settlement with official x402 SDK.
|
|
101
|
+
*
|
|
102
|
+
* The challenge: x402 v2 spec places extensions at root level in PaymentRequired,
|
|
103
|
+
* but x402x scheme needs settlement parameters from PaymentRequirements.extra.
|
|
104
|
+
*
|
|
105
|
+
* Solution: Provide two-layer API:
|
|
106
|
+
* 1. High-level: registerX402xScheme() - one line setup (recommended)
|
|
107
|
+
* 2. Low-level: injectX402xExtensionHandler() - flexible configuration
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Injects x402x extension handler into x402Client (Low-level API).
|
|
112
|
+
*
|
|
113
|
+
* IMPORTANT (x402 v2 + x402x multi-network):
|
|
114
|
+
* - Server returns per-option x402x info in `accepts[i].extra["x402x-router-settlement"]`
|
|
115
|
+
* - x402Client only passes `PaymentRequirements` to scheme (no root extensions)
|
|
116
|
+
* - Facilitator v2 reads from `paymentPayload.extensions["x402x-router-settlement"]`
|
|
117
|
+
*
|
|
118
|
+
* Solution: Copy the selected option's x402x info from `selectedRequirements.extra`
|
|
119
|
+
* into `paymentRequired.extensions` so it gets included in `paymentPayload.extensions`.
|
|
120
|
+
*
|
|
121
|
+
* This does NOT mutate `selectedRequirements` (which becomes `paymentPayload.accepted`),
|
|
122
|
+
* so v2 deepEqual matching still works.
|
|
123
|
+
*
|
|
124
|
+
* @param client - x402Client instance to inject handler into
|
|
125
|
+
* @param onRouterSettlementExtension - Callback to receive the per-request extension object
|
|
126
|
+
* @returns The same client instance for chaining
|
|
127
|
+
*
|
|
128
|
+
* @example Low-level API (for advanced users)
|
|
129
|
+
* ```typescript
|
|
130
|
+
* import { x402Client } from '@x402/core/client';
|
|
131
|
+
* import {
|
|
132
|
+
* injectX402xExtensionHandler,
|
|
133
|
+
* ExactEvmSchemeWithRouterSettlement
|
|
134
|
+
* } from '@x402x/extensions';
|
|
135
|
+
*
|
|
136
|
+
* const client = new x402Client();
|
|
137
|
+
* const scheme = new ExactEvmSchemeWithRouterSettlement(signer);
|
|
138
|
+
* injectX402xExtensionHandler(client, (ext) => scheme.setRouterSettlementExtensionFromPaymentRequired(ext))
|
|
139
|
+
* .register('eip155:84532', scheme);
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function injectX402xExtensionHandler(client: x402Client, onRouterSettlementExtension?: (extension: unknown | undefined) => void): x402Client;
|
|
143
|
+
/**
|
|
144
|
+
* Register x402x router settlement scheme with automatic extension handling (High-level API).
|
|
145
|
+
*
|
|
146
|
+
* This is the recommended way to set up x402x payments. It combines:
|
|
147
|
+
* 1. Extension handler injection (injectX402xExtensionHandler)
|
|
148
|
+
* 2. Scheme registration (ExactEvmSchemeWithRouterSettlement)
|
|
149
|
+
*
|
|
150
|
+
* Use this for the simplest integration - just provide your signer and network.
|
|
151
|
+
*
|
|
152
|
+
* @param client - x402Client instance
|
|
153
|
+
* @param network - Network identifier in CAIP-2 format (e.g., "eip155:84532")
|
|
154
|
+
* @param signer - EVM signer with address and signTypedData method
|
|
155
|
+
* @returns The client instance for chaining
|
|
156
|
+
*
|
|
157
|
+
* @example High-level API (recommended)
|
|
158
|
+
* ```typescript
|
|
159
|
+
* import { x402Client } from '@x402/core/client';
|
|
160
|
+
* import { registerX402xScheme } from '@x402x/extensions';
|
|
161
|
+
* import { useWalletClient } from 'wagmi';
|
|
162
|
+
*
|
|
163
|
+
* const { data: walletClient } = useWalletClient();
|
|
164
|
+
*
|
|
165
|
+
* const client = new x402Client();
|
|
166
|
+
* registerX402xScheme(client, 'eip155:84532', {
|
|
167
|
+
* address: walletClient.account.address,
|
|
168
|
+
* signTypedData: walletClient.signTypedData,
|
|
169
|
+
* });
|
|
170
|
+
*
|
|
171
|
+
* // That's it! Client is ready for x402x payments
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* @example Multiple networks
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const client = new x402Client();
|
|
177
|
+
* registerX402xScheme(client, 'eip155:84532', signer); // Base Sepolia
|
|
178
|
+
* registerX402xScheme(client, 'eip155:8453', signer); // Base Mainnet
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
declare function registerX402xScheme(client: x402Client, network: Network, signer: ClientEvmSigner): x402Client;
|
|
182
|
+
|
|
183
|
+
export { type ClientEvmSigner, ExactEvmSchemeWithRouterSettlement, injectX402xExtensionHandler, registerX402xScheme };
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var viem = require('viem');
|
|
4
|
+
|
|
5
|
+
// src/client/exact-evm-scheme.ts
|
|
6
|
+
function calculateCommitment(params) {
|
|
7
|
+
return viem.keccak256(
|
|
8
|
+
viem.encodePacked(
|
|
9
|
+
[
|
|
10
|
+
"string",
|
|
11
|
+
// Protocol identifier
|
|
12
|
+
"uint256",
|
|
13
|
+
// Chain ID
|
|
14
|
+
"address",
|
|
15
|
+
// Hub address
|
|
16
|
+
"address",
|
|
17
|
+
// Token address
|
|
18
|
+
"address",
|
|
19
|
+
// From (payer)
|
|
20
|
+
"uint256",
|
|
21
|
+
// Value
|
|
22
|
+
"uint256",
|
|
23
|
+
// Valid after
|
|
24
|
+
"uint256",
|
|
25
|
+
// Valid before
|
|
26
|
+
"bytes32",
|
|
27
|
+
// Salt
|
|
28
|
+
"address",
|
|
29
|
+
// Pay to
|
|
30
|
+
"uint256",
|
|
31
|
+
// Facilitator fee
|
|
32
|
+
"address",
|
|
33
|
+
// Hook
|
|
34
|
+
"bytes32"
|
|
35
|
+
// keccak256(hookData)
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
"X402/settle/v1",
|
|
39
|
+
BigInt(params.chainId),
|
|
40
|
+
params.hub,
|
|
41
|
+
params.asset,
|
|
42
|
+
params.from,
|
|
43
|
+
BigInt(params.value),
|
|
44
|
+
BigInt(params.validAfter),
|
|
45
|
+
BigInt(params.validBefore),
|
|
46
|
+
params.salt,
|
|
47
|
+
params.payTo,
|
|
48
|
+
BigInt(params.facilitatorFee),
|
|
49
|
+
params.hook,
|
|
50
|
+
viem.keccak256(params.hookData)
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/server-extension.ts
|
|
57
|
+
var ROUTER_SETTLEMENT_KEY = "x402x-router-settlement";
|
|
58
|
+
|
|
59
|
+
// src/client/exact-evm-scheme.ts
|
|
60
|
+
var authorizationTypes = {
|
|
61
|
+
TransferWithAuthorization: [
|
|
62
|
+
{ name: "from", type: "address" },
|
|
63
|
+
{ name: "to", type: "address" },
|
|
64
|
+
{ name: "value", type: "uint256" },
|
|
65
|
+
{ name: "validAfter", type: "uint256" },
|
|
66
|
+
{ name: "validBefore", type: "uint256" },
|
|
67
|
+
{ name: "nonce", type: "bytes32" }
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
var ExactEvmSchemeWithRouterSettlement = class {
|
|
71
|
+
/**
|
|
72
|
+
* Creates a new ExactEvmSchemeWithRouterSettlement instance.
|
|
73
|
+
*
|
|
74
|
+
* @param signer - The EVM signer for client operations (viem WalletClient or LocalAccount)
|
|
75
|
+
*/
|
|
76
|
+
constructor(signer) {
|
|
77
|
+
this.signer = signer;
|
|
78
|
+
this.scheme = "exact";
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Set router-settlement extension data for the next payment payload creation.
|
|
82
|
+
*
|
|
83
|
+
* Intended to be called from an `x402Client.onBeforePaymentCreation` hook, which has access
|
|
84
|
+
* to `paymentRequired.extensions`.
|
|
85
|
+
*/
|
|
86
|
+
setRouterSettlementExtensionFromPaymentRequired(ext) {
|
|
87
|
+
if (!ext) {
|
|
88
|
+
this.routerSettlementFromPaymentRequired = void 0;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.routerSettlementFromPaymentRequired = ext;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates a payment payload for the Exact scheme with router settlement.
|
|
95
|
+
*
|
|
96
|
+
* This method:
|
|
97
|
+
* 1. Extracts settlement parameters from PaymentRequired.extensions
|
|
98
|
+
* 2. Calculates a commitment hash binding all parameters
|
|
99
|
+
* 3. Uses the commitment as the EIP-3009 nonce
|
|
100
|
+
* 4. Signs with settlementRouter as the 'to' address
|
|
101
|
+
*
|
|
102
|
+
* @param x402Version - The x402 protocol version (must be 2)
|
|
103
|
+
* @param paymentRequirements - The payment requirements from the server
|
|
104
|
+
* @returns Promise resolving to a payment payload
|
|
105
|
+
*
|
|
106
|
+
* @throws Error if x402Version is not 2
|
|
107
|
+
* @throws Error if x402x-router-settlement extension is missing
|
|
108
|
+
* @throws Error if required settlement parameters are missing
|
|
109
|
+
*/
|
|
110
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
111
|
+
if (x402Version !== 2) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`ExactEvmSchemeWithRouterSettlement only supports x402 version 2, got: ${x402Version}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const routerSettlement = paymentRequirements.extra?.[ROUTER_SETTLEMENT_KEY] ?? this.routerSettlementFromPaymentRequired;
|
|
117
|
+
if (!routerSettlement?.info) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"x402x-router-settlement extension not available for scheme signing. Ensure the resource server includes the extension in PaymentRequired.extensions and the client registered x402x via registerX402xScheme() (or injected the handler)."
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const { salt, settlementRouter, hook, hookData, finalPayTo, facilitatorFee } = routerSettlement.info;
|
|
123
|
+
this.routerSettlementFromPaymentRequired = void 0;
|
|
124
|
+
if (!salt) throw new Error("Missing required parameter: salt");
|
|
125
|
+
if (!settlementRouter) throw new Error("Missing required parameter: settlementRouter");
|
|
126
|
+
if (!hook) throw new Error("Missing required parameter: hook");
|
|
127
|
+
if (hookData === void 0) throw new Error("Missing required parameter: hookData");
|
|
128
|
+
if (!finalPayTo) throw new Error("Missing required parameter: finalPayTo");
|
|
129
|
+
const resolvedFacilitatorFee = facilitatorFee ?? "0";
|
|
130
|
+
const chainId = parseInt(paymentRequirements.network.split(":")[1]);
|
|
131
|
+
if (isNaN(chainId)) {
|
|
132
|
+
throw new Error(`Invalid network format: ${paymentRequirements.network}`);
|
|
133
|
+
}
|
|
134
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
135
|
+
const validAfter = (now - 600).toString();
|
|
136
|
+
const validBefore = (now + paymentRequirements.maxTimeoutSeconds).toString();
|
|
137
|
+
const commitmentParams = {
|
|
138
|
+
chainId,
|
|
139
|
+
hub: settlementRouter,
|
|
140
|
+
asset: paymentRequirements.asset,
|
|
141
|
+
from: this.signer.address,
|
|
142
|
+
value: paymentRequirements.amount,
|
|
143
|
+
validAfter,
|
|
144
|
+
validBefore,
|
|
145
|
+
salt,
|
|
146
|
+
payTo: finalPayTo,
|
|
147
|
+
facilitatorFee: resolvedFacilitatorFee,
|
|
148
|
+
hook,
|
|
149
|
+
hookData
|
|
150
|
+
};
|
|
151
|
+
const nonce = calculateCommitment(commitmentParams);
|
|
152
|
+
const authorization = {
|
|
153
|
+
from: this.signer.address,
|
|
154
|
+
to: viem.getAddress(settlementRouter),
|
|
155
|
+
value: paymentRequirements.amount,
|
|
156
|
+
validAfter,
|
|
157
|
+
validBefore,
|
|
158
|
+
nonce
|
|
159
|
+
};
|
|
160
|
+
const signature = await this.signAuthorization(authorization, paymentRequirements, chainId);
|
|
161
|
+
const payload = {
|
|
162
|
+
authorization,
|
|
163
|
+
signature
|
|
164
|
+
};
|
|
165
|
+
return {
|
|
166
|
+
x402Version,
|
|
167
|
+
payload
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Sign the EIP-3009 authorization using EIP-712
|
|
172
|
+
*
|
|
173
|
+
* @param authorization - The authorization to sign
|
|
174
|
+
* @param requirements - The payment requirements
|
|
175
|
+
* @param chainId - The chain ID
|
|
176
|
+
* @returns Promise resolving to the signature
|
|
177
|
+
*/
|
|
178
|
+
async signAuthorization(authorization, requirements, chainId) {
|
|
179
|
+
if (!requirements.extra?.name || !requirements.extra?.version) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`EIP-712 domain parameters (name, version) are required in payment requirements for asset ${requirements.asset}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const { name, version } = requirements.extra;
|
|
185
|
+
const domain = {
|
|
186
|
+
name,
|
|
187
|
+
version,
|
|
188
|
+
chainId,
|
|
189
|
+
verifyingContract: viem.getAddress(requirements.asset)
|
|
190
|
+
};
|
|
191
|
+
const message = {
|
|
192
|
+
from: viem.getAddress(authorization.from),
|
|
193
|
+
to: viem.getAddress(authorization.to),
|
|
194
|
+
value: BigInt(authorization.value),
|
|
195
|
+
validAfter: BigInt(authorization.validAfter),
|
|
196
|
+
validBefore: BigInt(authorization.validBefore),
|
|
197
|
+
nonce: authorization.nonce
|
|
198
|
+
};
|
|
199
|
+
return await this.signer.signTypedData({
|
|
200
|
+
domain,
|
|
201
|
+
types: authorizationTypes,
|
|
202
|
+
primaryType: "TransferWithAuthorization",
|
|
203
|
+
message
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/client/extension-handler.ts
|
|
209
|
+
function injectX402xExtensionHandler(client, onRouterSettlementExtension) {
|
|
210
|
+
return client.onBeforePaymentCreation(async (context) => {
|
|
211
|
+
const { paymentRequired, selectedRequirements } = context;
|
|
212
|
+
console.log("[x402x-handler] onBeforePaymentCreation called");
|
|
213
|
+
console.log("[x402x-handler] selectedRequirements.network:", selectedRequirements.network);
|
|
214
|
+
console.log(
|
|
215
|
+
"[x402x-handler] selectedRequirements.extra keys:",
|
|
216
|
+
Object.keys(selectedRequirements.extra || {})
|
|
217
|
+
);
|
|
218
|
+
const perOptionExtension = selectedRequirements.extra?.[ROUTER_SETTLEMENT_KEY];
|
|
219
|
+
if (perOptionExtension) {
|
|
220
|
+
if (!paymentRequired.extensions) {
|
|
221
|
+
paymentRequired.extensions = {};
|
|
222
|
+
}
|
|
223
|
+
paymentRequired.extensions[ROUTER_SETTLEMENT_KEY] = perOptionExtension;
|
|
224
|
+
console.log(
|
|
225
|
+
"[x402x-handler] \u2705 Copied per-option x402x info into PaymentRequired.extensions"
|
|
226
|
+
);
|
|
227
|
+
console.log("[x402x-handler] Extension info:", JSON.stringify(perOptionExtension, null, 2));
|
|
228
|
+
} else {
|
|
229
|
+
console.warn(
|
|
230
|
+
"[x402x-handler] \u26A0\uFE0F No per-option x402x info found in selectedRequirements.extra"
|
|
231
|
+
);
|
|
232
|
+
console.warn(
|
|
233
|
+
"[x402x-handler] This may cause facilitator errors. Check server-side createSettlementRouteConfig."
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (onRouterSettlementExtension) {
|
|
237
|
+
const extensionToUse = perOptionExtension || paymentRequired.extensions?.[ROUTER_SETTLEMENT_KEY];
|
|
238
|
+
onRouterSettlementExtension(extensionToUse);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function registerX402xScheme(client, network, signer) {
|
|
243
|
+
const scheme = new ExactEvmSchemeWithRouterSettlement(signer);
|
|
244
|
+
injectX402xExtensionHandler(client, (ext) => {
|
|
245
|
+
scheme.setRouterSettlementExtensionFromPaymentRequired(ext);
|
|
246
|
+
});
|
|
247
|
+
client.register(network, scheme);
|
|
248
|
+
return client;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
exports.ExactEvmSchemeWithRouterSettlement = ExactEvmSchemeWithRouterSettlement;
|
|
252
|
+
exports.injectX402xExtensionHandler = injectX402xExtensionHandler;
|
|
253
|
+
exports.registerX402xScheme = registerX402xScheme;
|
|
254
|
+
//# sourceMappingURL=index.js.map
|
|
255
|
+
//# sourceMappingURL=index.js.map
|