@agentokratia/x402-escrow 2.1.1 → 2.1.2

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.
Files changed (2) hide show
  1. package/README.md +165 -151
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -5,208 +5,219 @@ Escrow payment scheme for the x402 protocol. Supports direct USDC payments and c
5
5
  ## Features
6
6
 
7
7
  - **Direct USDC payments** - Gasless via ERC-3009 ReceiveWithAuthorization
8
- - **Multi-token support** - Pay with WETH, DAI, USDT receiver gets USDC
8
+ - **Multi-token support** - Pay with WETH, DAI, USDT. Receiver gets USDC.
9
9
  - **Automatic swap quotes** - Server fetches DEX quotes, client just signs
10
10
  - **Gzip compression** - Aggregator calldata compressed for smaller payloads
11
11
 
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- npm install @agentokratia/x402-escrow
15
+ npm install @x402/core @x402/next @x402/fetch @x402/evm @agentokratia/x402-escrow viem
16
16
  ```
17
17
 
18
- ## Client Usage
18
+ ## Server Integration
19
19
 
20
- For apps and agents paying for APIs.
21
-
22
- ### Basic Setup
20
+ ### Next.js with paymentProxy
23
21
 
24
22
  ```typescript
25
- import { EscrowScheme } from '@agentokratia/x402-escrow/client';
26
- import { x402Client } from '@x402/core/client';
27
- import { ExactEvmScheme } from '@x402/evm';
28
- import type { WalletClient } from 'viem';
29
-
30
- // Convert wagmi WalletClient to x402 signer
31
- function walletClientToSigner(walletClient: WalletClient) {
32
- return {
33
- address: walletClient.account!.address,
34
- signTypedData: async (message) =>
35
- walletClient.signTypedData({
36
- account: walletClient.account!,
37
- domain: message.domain,
38
- types: message.types,
39
- primaryType: message.primaryType,
40
- message: message.message,
41
- }),
42
- };
43
- }
44
-
45
- // Create x402 client
46
- const client = new x402Client();
47
-
48
- // Register exact scheme for direct USDC payments
49
- const exactScheme = new ExactEvmScheme(walletClientToSigner(walletClient));
50
- client.register('eip155:8453', exactScheme);
51
-
52
- // Register escrow scheme for swap payments
53
- const escrowScheme = new EscrowScheme(walletClient);
54
- client.register('eip155:8453', escrowScheme);
55
-
56
- // Create payment payload from 402 response
57
- const paymentPayload = await client.createPaymentPayload(paymentRequired);
58
- ```
59
-
60
- ### With Policies (Token Selection)
23
+ import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
24
+ import { ExactEvmScheme } from '@x402/evm/exact/server';
25
+ import { EscrowScheme } from '@agentokratia/x402-escrow/server';
26
+ import { paymentProxy } from '@x402/next';
61
27
 
62
- ```typescript
63
- import { x402Client } from '@x402/core/client';
64
- import { EscrowScheme } from '@agentokratia/x402-escrow/client';
28
+ const facilitator = new HTTPFacilitatorClient({
29
+ url: 'https://facilitator.agentokratia.com',
30
+ createAuthHeaders: async () => ({
31
+ verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
32
+ settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
33
+ }),
34
+ });
65
35
 
66
- // Selector that prefers exact scheme over escrow
67
- const preferExactSelector = (version, requirements) => {
68
- const exact = requirements.find((r) => r.scheme === 'exact');
69
- return exact || requirements[0];
70
- };
36
+ const escrow = new EscrowScheme({ facilitator });
71
37
 
72
- // Policy that filters by input token
73
- function createInputTokenPolicy(inputToken: string) {
74
- return (version, requirements) => {
75
- return requirements.filter((req) => {
76
- if (req.scheme === 'exact') {
77
- return req.asset?.toLowerCase() === inputToken.toLowerCase();
78
- }
79
- // For escrow, check swapData.inputToken
80
- const swapData = req.extra?.swapData;
81
- if (swapData?.inputToken) {
82
- return swapData.inputToken.toLowerCase() === inputToken.toLowerCase();
83
- }
84
- return req.asset?.toLowerCase() === inputToken.toLowerCase();
85
- });
86
- };
87
- }
38
+ const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
88
39
 
89
- // Create client with selector
90
- const client = new x402Client(preferExactSelector);
40
+ const server = new x402ResourceServer(facilitator)
41
+ .register('eip155:8453', new ExactEvmScheme())
42
+ .register('eip155:8453', escrow);
91
43
 
92
- // Register policy to filter by selected token
93
- client.registerPolicy(createInputTokenPolicy('0x4200000000000000000000000000000000000006')); // WETH
44
+ // Auto-discovers supported tokens (USDC, WETH, DAI...)
45
+ const escrowAccepts = await escrow.buildAccepts({
46
+ network: 'eip155:8453',
47
+ price: '$0.01',
48
+ payTo: '0xYourWallet...',
49
+ asset: USDC, // settlement token (default: auto-detect)
50
+ });
94
51
 
95
- // Register schemes
96
- client.register('eip155:8453', new ExactEvmScheme(signer));
97
- client.register('eip155:8453', new EscrowScheme(walletClient));
52
+ export const middleware = paymentProxy(
53
+ {
54
+ '/api/premium': {
55
+ accepts: [
56
+ { scheme: 'exact', network: 'eip155:8453', price: '$0.01', payTo: '0xYourWallet...' },
57
+ ...escrowAccepts,
58
+ ],
59
+ },
60
+ },
61
+ server
62
+ );
98
63
  ```
99
64
 
100
- ## Server Usage
101
-
102
- For APIs accepting payments. Config is auto-discovered from facilitator.
103
-
104
- ### Basic Setup
65
+ ### Express with paymentMiddleware
105
66
 
106
67
  ```typescript
68
+ import { paymentMiddleware } from '@x402/express';
107
69
  import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
108
70
  import { EscrowScheme } from '@agentokratia/x402-escrow/server';
109
- import { ExactEvmScheme } from '@x402/evm/exact/server';
110
71
 
111
- // 1. Create facilitator client
112
72
  const facilitator = new HTTPFacilitatorClient({
113
73
  url: 'https://facilitator.agentokratia.com',
114
74
  createAuthHeaders: async () => ({
115
75
  verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
116
76
  settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
117
- supported: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
118
77
  }),
119
78
  });
120
79
 
121
- // 2. Create escrow scheme (for swap payments)
122
- const escrow = new EscrowScheme({
123
- facilitator,
124
- apiKey: process.env.X402_API_KEY,
80
+ const escrow = new EscrowScheme({ facilitator });
81
+ const accepts = await escrow.buildAccepts({
82
+ network: 'eip155:8453',
83
+ price: '$0.01',
84
+ payTo: '0xYourWallet',
125
85
  });
126
86
 
127
- // 3. Create x402 server with both schemes
128
- const server = new x402ResourceServer(facilitator);
129
- server.register('eip155:8453', new ExactEvmScheme()); // Direct USDC
130
- server.register('eip155:8453', escrow); // Swaps
87
+ app.use(
88
+ paymentMiddleware(
89
+ {
90
+ 'GET /api/analyze': { accepts },
91
+ },
92
+ new x402ResourceServer(facilitator)
93
+ )
94
+ );
131
95
  ```
132
96
 
133
- ### Building Payment Requirements (402 Response)
97
+ ### Dynamic Pricing (Custom API Routes)
98
+
99
+ For endpoints with per-request pricing (like tip-app):
134
100
 
135
101
  ```typescript
136
- // Build accepts for both schemes in parallel
137
- const [exactResult, escrowResult] = await Promise.allSettled([
138
- // Exact scheme (USDC direct)
139
- server.buildPaymentRequirements({
140
- scheme: 'exact',
141
- network: 'eip155:8453',
142
- price: '$1.00',
143
- payTo: recipientAddress,
144
- maxTimeoutSeconds: 600,
145
- }),
146
- // Escrow scheme (swaps) - fetches DEX quotes
147
- escrow.buildAcceptsResolved({
148
- network: 'eip155:8453',
149
- price: '$1.00',
150
- payTo: recipientAddress,
151
- }),
152
- ]);
102
+ import { NextRequest, NextResponse } from 'next/server';
103
+ import { HTTPFacilitatorClient } from '@x402/core/server';
104
+ import { EscrowScheme, preprocessSwapPayload } from '@agentokratia/x402-escrow/server';
153
105
 
154
- // Combine into payment requirements array
155
- const paymentRequirements = [];
106
+ const facilitator = new HTTPFacilitatorClient({
107
+ /* config */
108
+ });
109
+ const escrow = new EscrowScheme({ facilitator, apiKey: process.env.X402_API_KEY });
110
+
111
+ export async function GET(request: NextRequest) {
112
+ const amount = request.nextUrl.searchParams.get('amount');
113
+ const recipient = request.nextUrl.searchParams.get('to');
114
+ const paymentSignature = request.headers.get('payment-signature');
115
+
116
+ if (!paymentSignature) {
117
+ // Fetch fresh DEX quotes with buildAcceptsResolved
118
+ const accepts = await escrow.buildAcceptsResolved({
119
+ network: 'eip155:8453',
120
+ price: `$${amount}`,
121
+ payTo: recipient,
122
+ });
156
123
 
157
- if (exactResult.status === 'fulfilled') {
158
- paymentRequirements.push(...exactResult.value);
159
- }
124
+ const requirements = accepts.map((a) => ({
125
+ scheme: a.scheme,
126
+ network: a.network,
127
+ asset: a.price.asset,
128
+ amount: a.price.amount,
129
+ payTo: a.payTo,
130
+ maxTimeoutSeconds: 600,
131
+ extra: a.price.extra || {},
132
+ }));
133
+
134
+ const response = NextResponse.json({ message: 'Payment required' }, { status: 402 });
135
+ response.headers.set(
136
+ 'PAYMENT-REQUIRED',
137
+ Buffer.from(JSON.stringify(requirements)).toString('base64')
138
+ );
139
+ return response;
140
+ }
160
141
 
161
- if (escrowResult.status === 'fulfilled') {
162
- for (const accept of escrowResult.value) {
163
- paymentRequirements.push({
164
- scheme: accept.scheme,
165
- network: accept.network,
166
- asset: accept.price.asset,
167
- amount: accept.price.amount,
168
- payTo: accept.payTo,
169
- maxTimeoutSeconds: accept.maxTimeoutSeconds || 600,
170
- extra: accept.price.extra || {},
171
- });
142
+ // Process payment
143
+ const payload = JSON.parse(Buffer.from(paymentSignature, 'base64').toString());
144
+ const processed = preprocessSwapPayload({ x402Version: 2, ...payload });
145
+
146
+ const verifyResult = await facilitator.verify(processed, processed.accepted);
147
+ if (!verifyResult.isValid) {
148
+ return NextResponse.json({ error: verifyResult.invalidReason }, { status: 402 });
172
149
  }
173
- }
174
150
 
175
- // Return 402 with PAYMENT-REQUIRED header
176
- const encoded = Buffer.from(JSON.stringify(paymentRequirements)).toString('base64');
177
- response.headers.set('PAYMENT-REQUIRED', encoded);
151
+ const settleResult = await facilitator.settle(processed, processed.accepted);
152
+ return NextResponse.json({ success: true, transaction: settleResult.transaction });
153
+ }
178
154
  ```
179
155
 
180
- ### Processing Payments (Verify & Settle)
156
+ ## Client Integration
157
+
158
+ ### Balance-Aware Token Selection (Recommended)
181
159
 
182
160
  ```typescript
183
- import { preprocessSwapPayload } from '@agentokratia/x402-escrow/server';
184
-
185
- // Decode payment from PAYMENT-SIGNATURE header
186
- const paymentPayload = JSON.parse(Buffer.from(paymentSignature, 'base64').toString());
187
-
188
- // Decompress swap calldata before forwarding to facilitator
189
- // (Server compresses in buildAccepts, client passes through compressed)
190
- const processedPayload = preprocessSwapPayload({
191
- x402Version: 2,
192
- resource: { url: '/api/endpoint', mimeType: 'application/json' },
193
- accepted: paymentPayload.accepted,
194
- payload: paymentPayload.payload,
161
+ import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
162
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
163
+ import { createWalletClient, createPublicClient, http } from 'viem';
164
+ import { privateKeyToAccount } from 'viem/accounts';
165
+ import { base } from 'viem/chains';
166
+ import {
167
+ EscrowScheme,
168
+ createBalanceSelector,
169
+ preferTokenPolicy,
170
+ } from '@agentokratia/x402-escrow/client';
171
+
172
+ const WETH = '0x4200000000000000000000000000000000000006';
173
+ const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
174
+
175
+ const account = privateKeyToAccount(process.env.PRIVATE_KEY);
176
+
177
+ const walletClient = createWalletClient({
178
+ account,
179
+ chain: base,
180
+ transport: http(),
195
181
  });
196
182
 
197
- // Verify and settle with facilitator
198
- const verifyResult = await facilitator.verify(processedPayload, processedPayload.accepted);
199
- if (!verifyResult.isValid) {
200
- throw new Error(verifyResult.invalidReason);
201
- }
183
+ const publicClient = createPublicClient({
184
+ chain: base,
185
+ transport: http(),
186
+ });
202
187
 
203
- const settleResult = await facilitator.settle(processedPayload, processedPayload.accepted);
204
- if (!settleResult.success) {
205
- throw new Error(settleResult.errorReason);
206
- }
188
+ // Balance-aware: auto-picks USDC (gasless) or WETH/DAI (swap)
189
+ const client = new x402Client(createBalanceSelector(publicClient, account.address))
190
+ .register('eip155:8453', new ExactEvmScheme(account))
191
+ .register('eip155:8453', new EscrowScheme(walletClient))
192
+ .registerPolicy(preferTokenPolicy([WETH, USDC]));
193
+
194
+ const paidFetch = wrapFetchWithPayment(fetch, client);
195
+
196
+ const res = await paidFetch('https://api.example.com/premium');
197
+ const data = await res.json();
198
+ ```
199
+
200
+ ### Simple Setup (Browser with wagmi)
201
+
202
+ ```typescript
203
+ import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
204
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
205
+ import { EscrowScheme } from '@agentokratia/x402-escrow/client';
206
+
207
+ // With wagmi's useWalletClient
208
+ const { data: walletClient } = useWalletClient();
209
+
210
+ const signer = {
211
+ address: walletClient.account.address,
212
+ signTypedData: (msg) => walletClient.signTypedData({ account: walletClient.account, ...msg }),
213
+ };
214
+
215
+ const client = new x402Client()
216
+ .register('eip155:8453', new ExactEvmScheme(signer))
217
+ .register('eip155:8453', new EscrowScheme(walletClient));
207
218
 
208
- // Return success with transaction hash
209
- return { success: true, transaction: settleResult.transaction };
219
+ const paidFetch = wrapFetchWithPayment(fetch, client);
220
+ const res = await paidFetch('https://api.example.com/premium');
210
221
  ```
211
222
 
212
223
  ## How It Works
@@ -250,6 +261,9 @@ Client Server Facilitator
250
261
  | Export | Description |
251
262
  | ------------------------- | ------------------------------------------------- |
252
263
  | `EscrowScheme` | Client scheme for x402Client (takes WalletClient) |
264
+ | `createBalanceSelector` | Async selector that checks on-chain balances |
265
+ | `preferTokenPolicy` | Sync policy that reorders by token preference |
266
+ | `checkBalance` | Utility for custom balance checks |
253
267
  | `signERC3009` | Sign ERC-3009 authorization |
254
268
  | `signPermit2TransferFrom` | Sign Permit2 transfer |
255
269
  | `computePaymentNonce` | Derive deterministic nonce from payment params |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentokratia/x402-escrow",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Escrow payment scheme for x402 protocol - session-based payments for high-frequency APIs",
5
5
  "license": "MIT",
6
6
  "author": "Agentokratia",