@agentokratia/x402-escrow 2.1.0 → 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.
- package/README.md +228 -94
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,76 +1,72 @@
|
|
|
1
1
|
# @agentokratia/x402-escrow
|
|
2
2
|
|
|
3
|
-
Escrow payment scheme for the x402 protocol.
|
|
3
|
+
Escrow payment scheme for the x402 protocol. Supports direct USDC payments and cross-token swaps.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
7
|
+
- **Direct USDC payments** - Gasless via ERC-3009 ReceiveWithAuthorization
|
|
8
|
+
- **Multi-token support** - Pay with WETH, DAI, USDT. Receiver gets USDC.
|
|
9
|
+
- **Automatic swap quotes** - Server fetches DEX quotes, client just signs
|
|
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
|
-
##
|
|
18
|
+
## Server Integration
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
### Simple (recommended)
|
|
20
|
+
### Next.js with paymentProxy
|
|
23
21
|
|
|
24
22
|
```typescript
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
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';
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
});
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
scheme.sessions.getAll();
|
|
34
|
-
scheme.sessions.hasValid(receiverAddress, '10000');
|
|
35
|
-
```
|
|
36
|
+
const escrow = new EscrowScheme({ facilitator });
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const server = new x402ResourceServer(facilitator)
|
|
41
|
+
.register('eip155:8453', new ExactEvmScheme())
|
|
42
|
+
.register('eip155:8453', escrow);
|
|
41
43
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
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)
|
|
45
50
|
});
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const escrowScheme = new EscrowScheme(walletClient);
|
|
60
|
-
const x402 = new x402Client().register('eip155:84532', escrowScheme);
|
|
61
|
-
const paidFetch = wrapFetchWithPayment(fetch, x402);
|
|
62
|
-
const escrowFetch = withSessionExtraction(paidFetch, escrowScheme);
|
|
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
|
+
);
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
For APIs accepting payments. Config is auto-discovered from facilitator.
|
|
68
|
-
|
|
69
|
-
### Express
|
|
65
|
+
### Express with paymentMiddleware
|
|
70
66
|
|
|
71
67
|
```typescript
|
|
72
|
-
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
|
|
73
68
|
import { paymentMiddleware } from '@x402/express';
|
|
69
|
+
import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
|
|
74
70
|
import { EscrowScheme } from '@agentokratia/x402-escrow/server';
|
|
75
71
|
|
|
76
72
|
const facilitator = new HTTPFacilitatorClient({
|
|
@@ -78,81 +74,219 @@ const facilitator = new HTTPFacilitatorClient({
|
|
|
78
74
|
createAuthHeaders: async () => ({
|
|
79
75
|
verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
|
|
80
76
|
settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
|
|
81
|
-
supported: {},
|
|
82
77
|
}),
|
|
83
78
|
});
|
|
84
79
|
|
|
85
|
-
const
|
|
80
|
+
const escrow = new EscrowScheme({ facilitator });
|
|
81
|
+
const accepts = await escrow.buildAccepts({
|
|
82
|
+
network: 'eip155:8453',
|
|
83
|
+
price: '$0.01',
|
|
84
|
+
payTo: '0xYourWallet',
|
|
85
|
+
});
|
|
86
86
|
|
|
87
87
|
app.use(
|
|
88
88
|
paymentMiddleware(
|
|
89
89
|
{
|
|
90
|
-
'GET /api/
|
|
91
|
-
accepts: {
|
|
92
|
-
scheme: 'escrow',
|
|
93
|
-
price: '$0.01',
|
|
94
|
-
network: 'eip155:84532',
|
|
95
|
-
payTo: ownerAddress,
|
|
96
|
-
},
|
|
97
|
-
},
|
|
90
|
+
'GET /api/analyze': { accepts },
|
|
98
91
|
},
|
|
99
|
-
|
|
92
|
+
new x402ResourceServer(facilitator)
|
|
100
93
|
)
|
|
101
94
|
);
|
|
102
95
|
```
|
|
103
96
|
|
|
104
|
-
###
|
|
97
|
+
### Dynamic Pricing (Custom API Routes)
|
|
98
|
+
|
|
99
|
+
For endpoints with per-request pricing (like tip-app):
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
103
|
+
import { HTTPFacilitatorClient } from '@x402/core/server';
|
|
104
|
+
import { EscrowScheme, preprocessSwapPayload } from '@agentokratia/x402-escrow/server';
|
|
105
|
+
|
|
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
|
+
});
|
|
123
|
+
|
|
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
|
+
}
|
|
141
|
+
|
|
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 });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const settleResult = await facilitator.settle(processed, processed.accepted);
|
|
152
|
+
return NextResponse.json({ success: true, transaction: settleResult.transaction });
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Client Integration
|
|
157
|
+
|
|
158
|
+
### Balance-Aware Token Selection (Recommended)
|
|
105
159
|
|
|
106
160
|
```typescript
|
|
107
|
-
import {
|
|
108
|
-
import {
|
|
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(),
|
|
181
|
+
});
|
|
109
182
|
|
|
110
|
-
const
|
|
183
|
+
const publicClient = createPublicClient({
|
|
184
|
+
chain: base,
|
|
185
|
+
transport: http(),
|
|
186
|
+
});
|
|
111
187
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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));
|
|
218
|
+
|
|
219
|
+
const paidFetch = wrapFetchWithPayment(fetch, client);
|
|
220
|
+
const res = await paidFetch('https://api.example.com/premium');
|
|
120
221
|
```
|
|
121
222
|
|
|
122
223
|
## How It Works
|
|
123
224
|
|
|
124
225
|
```
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
226
|
+
Client Server Facilitator
|
|
227
|
+
│ │ │
|
|
228
|
+
│ GET /api/resource │ │
|
|
229
|
+
│──────────────────────────────>│ │
|
|
230
|
+
│ │ buildAccepts() │
|
|
231
|
+
│ │─────────────────────────────>│
|
|
232
|
+
│ │ (fetches quotes for swaps) │
|
|
233
|
+
│ │<─────────────────────────────│
|
|
234
|
+
│ 402 + PAYMENT-REQUIRED │ │
|
|
235
|
+
│<──────────────────────────────│ │
|
|
236
|
+
│ │ │
|
|
237
|
+
│ User selects token (WETH) │ │
|
|
238
|
+
│ Signs EIP-712 Permit2 │ │
|
|
239
|
+
│ │ │
|
|
240
|
+
│ GET + PAYMENT-SIGNATURE │ │
|
|
241
|
+
│──────────────────────────────>│ │
|
|
242
|
+
│ │ verify() + settle() │
|
|
243
|
+
│ │─────────────────────────────>│
|
|
244
|
+
│ │ (executes swap on-chain) │
|
|
245
|
+
│ │<─────────────────────────────│
|
|
246
|
+
│ 200 + transaction hash │ │
|
|
247
|
+
│<──────────────────────────────│ │
|
|
130
248
|
```
|
|
131
249
|
|
|
132
250
|
## Networks
|
|
133
251
|
|
|
134
|
-
| Network | Chain ID |
|
|
135
|
-
| ------------ | -------- |
|
|
136
|
-
| Base Mainnet | 8453 | `
|
|
137
|
-
| Base Sepolia | 84532 | `
|
|
252
|
+
| Network | Chain ID | Facilitator |
|
|
253
|
+
| ------------ | -------- | -------------------------------------- |
|
|
254
|
+
| Base Mainnet | 8453 | `https://facilitator.agentokratia.com` |
|
|
255
|
+
| Base Sepolia | 84532 | `https://facilitator.agentokratia.com` |
|
|
138
256
|
|
|
139
257
|
## API
|
|
140
258
|
|
|
141
|
-
### Client
|
|
142
|
-
|
|
143
|
-
| Export
|
|
144
|
-
|
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
155
|
-
|
|
259
|
+
### Client Exports
|
|
260
|
+
|
|
261
|
+
| Export | Description |
|
|
262
|
+
| ------------------------- | ------------------------------------------------- |
|
|
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 |
|
|
267
|
+
| `signERC3009` | Sign ERC-3009 authorization |
|
|
268
|
+
| `signPermit2TransferFrom` | Sign Permit2 transfer |
|
|
269
|
+
| `computePaymentNonce` | Derive deterministic nonce from payment params |
|
|
270
|
+
| `PERMIT2_ADDRESS` | Universal Permit2 contract address |
|
|
271
|
+
| `decompressCalldata` | Decompress gzipped aggregator calldata |
|
|
272
|
+
|
|
273
|
+
### Server Exports
|
|
274
|
+
|
|
275
|
+
| Export | Description |
|
|
276
|
+
| ----------------------- | ------------------------------------------------- |
|
|
277
|
+
| `EscrowScheme` | Server scheme for x402ResourceServer |
|
|
278
|
+
| `HTTPFacilitatorClient` | Re-export from @x402/core/server |
|
|
279
|
+
| `preprocessSwapPayload` | Decompress swap calldata before facilitator calls |
|
|
280
|
+
| `compressCalldata` | Compress aggregator calldata (gzip) |
|
|
281
|
+
|
|
282
|
+
## Supported Input Tokens (Base Mainnet)
|
|
283
|
+
|
|
284
|
+
| Token | Address |
|
|
285
|
+
| ----- | -------------------------------------------- |
|
|
286
|
+
| USDC | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
|
|
287
|
+
| WETH | `0x4200000000000000000000000000000000000006` |
|
|
288
|
+
| DAI | `0x50c5725949a6f0c72e6c4a641f24049a917db0cb` |
|
|
289
|
+
| USDT | `0xfde4c96c8593536e31f229ea8f37b2ada2699bb2` |
|
|
156
290
|
|
|
157
291
|
## License
|
|
158
292
|
|
package/package.json
CHANGED