@agentokratia/x402-escrow 2.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Agentokratia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # @agentokratia/x402-escrow
2
+
3
+ Escrow payment scheme for the x402 protocol. Session-based payments for high-frequency APIs.
4
+
5
+ ## Features
6
+
7
+ - **Session-based payments** - Sign once, make unlimited API calls
8
+ - **Zero per-request gas** - Facilitator handles on-chain transactions
9
+ - **100% reclaimable** - Withdraw unused funds anytime
10
+ - **ERC-3009 gasless** - Users sign off-chain, no wallet transaction needed
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @agentokratia/x402-escrow
16
+ ```
17
+
18
+ ## Client Usage
19
+
20
+ For apps and agents paying for APIs.
21
+
22
+ ### Simple (recommended)
23
+
24
+ ```typescript
25
+ import { createEscrowFetch } from '@agentokratia/x402-escrow/client';
26
+
27
+ const { fetch: escrowFetch, scheme, x402 } = createEscrowFetch(walletClient);
28
+
29
+ // Payments handled automatically
30
+ const response = await escrowFetch('https://api.example.com/premium');
31
+
32
+ // Access sessions
33
+ scheme.sessions.getAll();
34
+ scheme.sessions.hasValid(receiverAddress, '10000');
35
+ ```
36
+
37
+ ### With hooks
38
+
39
+ ```typescript
40
+ const { fetch: escrowFetch, x402 } = createEscrowFetch(walletClient);
41
+
42
+ // Add hooks for user control
43
+ x402.onBeforePaymentCreation(async (ctx) => {
44
+ console.log('About to pay:', ctx.paymentRequirements);
45
+ });
46
+
47
+ x402.onAfterPaymentCreation(async (ctx) => {
48
+ console.log('Payment created:', ctx.paymentPayload);
49
+ });
50
+ ```
51
+
52
+ ### Advanced (manual setup)
53
+
54
+ ```typescript
55
+ import { x402Client } from '@x402/core/client';
56
+ import { wrapFetchWithPayment } from '@x402/fetch';
57
+ import { EscrowScheme, withSessionExtraction } from '@agentokratia/x402-escrow/client';
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);
63
+ ```
64
+
65
+ ## Server Usage
66
+
67
+ For APIs accepting payments. Config is auto-discovered from facilitator.
68
+
69
+ ### Express
70
+
71
+ ```typescript
72
+ import { x402ResourceServer, HTTPFacilitatorClient } from '@x402/core/server';
73
+ import { paymentMiddleware } from '@x402/express';
74
+ import { EscrowScheme } from '@agentokratia/x402-escrow/server';
75
+
76
+ const facilitator = new HTTPFacilitatorClient({
77
+ url: 'https://facilitator.agentokratia.com',
78
+ createAuthHeaders: async () => ({
79
+ verify: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
80
+ settle: { Authorization: `Bearer ${process.env.X402_API_KEY}` },
81
+ supported: {},
82
+ }),
83
+ });
84
+
85
+ const server = new x402ResourceServer(facilitator).register('eip155:84532', new EscrowScheme());
86
+
87
+ app.use(
88
+ paymentMiddleware(
89
+ {
90
+ 'GET /api/premium': {
91
+ accepts: {
92
+ scheme: 'escrow',
93
+ price: '$0.01',
94
+ network: 'eip155:84532',
95
+ payTo: ownerAddress,
96
+ },
97
+ },
98
+ },
99
+ server
100
+ )
101
+ );
102
+ ```
103
+
104
+ ### Next.js
105
+
106
+ ```typescript
107
+ import { paymentProxy } from '@x402/next';
108
+ import { EscrowScheme } from '@agentokratia/x402-escrow/server';
109
+
110
+ const server = new x402ResourceServer(facilitator).register('eip155:84532', new EscrowScheme());
111
+
112
+ export const proxy = paymentProxy(
113
+ {
114
+ '/api/premium': {
115
+ accepts: { scheme: 'escrow', network: 'eip155:84532', payTo: ownerAddress, price: '$0.01' },
116
+ },
117
+ },
118
+ server
119
+ );
120
+ ```
121
+
122
+ ## How It Works
123
+
124
+ ```
125
+ 1. User signs ERC-3009 authorization (gasless)
126
+ 2. Facilitator deposits funds to escrow contract
127
+ 3. Session created with balance
128
+ 4. Each API call debits from session (no signature needed)
129
+ 5. User can reclaim unused funds anytime
130
+ ```
131
+
132
+ ## Networks
133
+
134
+ | Network | Chain ID | Escrow Contract |
135
+ | ------------ | -------- | -------------------------------------------- |
136
+ | Base Mainnet | 8453 | `0xbDEa0d1BCc5966192b070fDF62ab4eF5B4420Cff` |
137
+ | Base Sepolia | 84532 | `0xbDEa0d1BCc5966192b070fDF62ab4eF5B4420Cff` |
138
+
139
+ ## API
140
+
141
+ ### Client
142
+
143
+ | Export | Description |
144
+ | ------------------------------------------- | --------------------------------------------- |
145
+ | `createEscrowFetch(walletClient, options?)` | Creates fetch with automatic payment handling |
146
+ | `EscrowScheme` | Core scheme class for x402Client |
147
+ | `withSessionExtraction(fetch, scheme)` | Wrapper to extract sessions from responses |
148
+ | `withAxiosSessionExtraction(scheme)` | Axios interceptor for session extraction |
149
+
150
+ ### Server
151
+
152
+ | Export | Description |
153
+ | ----------------------- | ------------------------------------ |
154
+ | `EscrowScheme` | Server scheme for x402ResourceServer |
155
+ | `HTTPFacilitatorClient` | Re-export from @x402/core |
156
+
157
+ ## License
158
+
159
+ MIT
@@ -0,0 +1,524 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/client/index.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ BrowserLocalStorage: () => BrowserLocalStorage,
24
+ EscrowScheme: () => EscrowScheme,
25
+ InMemoryStorage: () => InMemoryStorage,
26
+ SessionManager: () => SessionManager,
27
+ X402_VERSION: () => X402_VERSION,
28
+ computeEscrowNonce: () => computeEscrowNonce,
29
+ createEscrowFetch: () => createEscrowFetch,
30
+ createStorage: () => createStorage,
31
+ signERC3009: () => signERC3009,
32
+ withAxiosSessionExtraction: () => withAxiosSessionExtraction,
33
+ withSessionExtraction: () => withSessionExtraction
34
+ });
35
+ module.exports = __toCommonJS(client_exports);
36
+
37
+ // src/client/session-wrapper.ts
38
+ var import_viem3 = require("viem");
39
+ var import_client = require("@x402/core/client");
40
+ var import_fetch = require("@x402/fetch");
41
+
42
+ // src/client/escrow.ts
43
+ var import_viem2 = require("viem");
44
+
45
+ // src/types.ts
46
+ var X402_VERSION = 2;
47
+ function fromBase64(str) {
48
+ try {
49
+ if (typeof atob !== "undefined") {
50
+ return decodeURIComponent(escape(atob(str)));
51
+ }
52
+ return Buffer.from(str, "base64").toString("utf-8");
53
+ } catch {
54
+ return Buffer.from(str, "base64").toString("utf-8");
55
+ }
56
+ }
57
+ function generateRequestId() {
58
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
59
+ return crypto.randomUUID();
60
+ }
61
+ return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
62
+ }
63
+ function generateRandomBytes(length) {
64
+ const bytes = new Uint8Array(length);
65
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
66
+ crypto.getRandomValues(bytes);
67
+ } else {
68
+ for (let i = 0; i < length; i++) {
69
+ bytes[i] = Math.floor(Math.random() * 256);
70
+ }
71
+ }
72
+ return bytes;
73
+ }
74
+
75
+ // src/constants.ts
76
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
77
+ var DEFAULT_SESSION_DURATION = 3600;
78
+ var DEFAULT_REFUND_WINDOW = 86400;
79
+
80
+ // src/client/storage.ts
81
+ var BaseStorage = class {
82
+ constructor() {
83
+ this.sessions = /* @__PURE__ */ new Map();
84
+ }
85
+ get(network, receiver) {
86
+ const now = Date.now() / 1e3;
87
+ for (const session of this.sessions.values()) {
88
+ if (session.network === network && session.receiver.toLowerCase() === receiver.toLowerCase() && session.authorizationExpiry > now) {
89
+ return session;
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+ findBest(network, receiver, minAmount) {
95
+ const now = Date.now() / 1e3;
96
+ let best = null;
97
+ let bestBalance = 0n;
98
+ for (const session of this.sessions.values()) {
99
+ if (session.network === network && session.receiver.toLowerCase() === receiver.toLowerCase() && session.authorizationExpiry > now) {
100
+ const balance = BigInt(session.balance);
101
+ if (balance >= minAmount && balance > bestBalance) {
102
+ best = session;
103
+ bestBalance = balance;
104
+ }
105
+ }
106
+ }
107
+ return best;
108
+ }
109
+ set(session) {
110
+ this.sessions.set(session.sessionId, session);
111
+ this.onUpdate();
112
+ }
113
+ update(sessionId, balance) {
114
+ const session = this.sessions.get(sessionId);
115
+ if (session) {
116
+ this.sessions.set(sessionId, { ...session, balance });
117
+ this.onUpdate();
118
+ }
119
+ }
120
+ list() {
121
+ return Array.from(this.sessions.values());
122
+ }
123
+ remove(sessionId) {
124
+ this.sessions.delete(sessionId);
125
+ this.onUpdate();
126
+ }
127
+ /** Override in subclasses for persistence */
128
+ onUpdate() {
129
+ }
130
+ };
131
+ var InMemoryStorage = class extends BaseStorage {
132
+ // No persistence needed
133
+ };
134
+ var BrowserLocalStorage = class extends BaseStorage {
135
+ constructor(key = "x402-sessions") {
136
+ super();
137
+ this.key = key;
138
+ this.load();
139
+ }
140
+ onUpdate() {
141
+ this.save();
142
+ }
143
+ load() {
144
+ if (typeof localStorage === "undefined") return;
145
+ try {
146
+ const data = localStorage.getItem(this.key);
147
+ if (data) {
148
+ const sessions = JSON.parse(data);
149
+ for (const s of sessions) this.sessions.set(s.sessionId, s);
150
+ }
151
+ } catch {
152
+ if (process.env.NODE_ENV !== "production") {
153
+ console.warn("[x402] Failed to load sessions from localStorage");
154
+ }
155
+ }
156
+ }
157
+ save() {
158
+ if (typeof localStorage === "undefined") return;
159
+ try {
160
+ localStorage.setItem(this.key, JSON.stringify(Array.from(this.sessions.values())));
161
+ } catch {
162
+ if (process.env.NODE_ENV !== "production") {
163
+ console.warn("[x402] Failed to save sessions to localStorage");
164
+ }
165
+ }
166
+ }
167
+ };
168
+ function createStorage(type, storageKey) {
169
+ return type === "localStorage" ? new BrowserLocalStorage(storageKey) : new InMemoryStorage();
170
+ }
171
+
172
+ // src/client/session-manager.ts
173
+ var SessionManager = class {
174
+ constructor(network, options = {}) {
175
+ this.network = network;
176
+ this.storage = createStorage(options.storage ?? "memory", options.storageKey);
177
+ }
178
+ /**
179
+ * Store a session from escrow settlement response.
180
+ */
181
+ store(session) {
182
+ this.storage.set({ ...session, createdAt: Date.now() });
183
+ }
184
+ /**
185
+ * Get session for a specific receiver.
186
+ */
187
+ getForReceiver(receiver) {
188
+ return this.storage.get(this.network, receiver);
189
+ }
190
+ /**
191
+ * Find best session for receiver with minimum balance.
192
+ */
193
+ findBest(receiver, minAmount) {
194
+ return this.storage.findBest(this.network, receiver, minAmount);
195
+ }
196
+ /**
197
+ * Check if valid session exists for receiver.
198
+ */
199
+ hasValid(receiver, minAmount) {
200
+ const session = minAmount ? this.storage.findBest(this.network, receiver, BigInt(minAmount)) : this.storage.get(this.network, receiver);
201
+ return session !== null;
202
+ }
203
+ /**
204
+ * Update session balance after debit.
205
+ */
206
+ updateBalance(sessionId, newBalance) {
207
+ this.storage.update(sessionId, newBalance);
208
+ }
209
+ /**
210
+ * Get all stored sessions.
211
+ */
212
+ getAll() {
213
+ return this.storage.list();
214
+ }
215
+ /**
216
+ * Remove a specific session.
217
+ */
218
+ remove(sessionId) {
219
+ this.storage.remove(sessionId);
220
+ }
221
+ /**
222
+ * Clear all sessions.
223
+ */
224
+ clear() {
225
+ for (const session of this.storage.list()) {
226
+ this.storage.remove(session.sessionId);
227
+ }
228
+ }
229
+ };
230
+
231
+ // src/client/eip712.ts
232
+ var import_viem = require("viem");
233
+ var PAYMENT_INFO_TYPE = "PaymentInfo(address operator,address payer,address receiver,address token,uint120 maxAmount,uint48 preApprovalExpiry,uint48 authorizationExpiry,uint48 refundExpiry,uint16 minFeeBps,uint16 maxFeeBps,address feeReceiver,uint256 salt)";
234
+ var PAYMENT_INFO_TYPEHASH = (0, import_viem.keccak256)((0, import_viem.toHex)(new TextEncoder().encode(PAYMENT_INFO_TYPE)));
235
+ var PAYMENT_INFO_ABI_PARAMS = "bytes32, address, address, address, address, uint120, uint48, uint48, uint48, uint16, uint16, address, uint256";
236
+ var NONCE_ABI_PARAMS = "uint256, address, bytes32";
237
+ var ERC3009_TYPES = {
238
+ ReceiveWithAuthorization: [
239
+ { name: "from", type: "address" },
240
+ { name: "to", type: "address" },
241
+ { name: "value", type: "uint256" },
242
+ { name: "validAfter", type: "uint256" },
243
+ { name: "validBefore", type: "uint256" },
244
+ { name: "nonce", type: "bytes32" }
245
+ ]
246
+ };
247
+ function computeEscrowNonce(chainId, escrowContract, paymentInfo) {
248
+ const paymentInfoHash = (0, import_viem.keccak256)(
249
+ (0, import_viem.encodeAbiParameters)((0, import_viem.parseAbiParameters)(PAYMENT_INFO_ABI_PARAMS), [
250
+ PAYMENT_INFO_TYPEHASH,
251
+ paymentInfo.operator,
252
+ ZERO_ADDRESS,
253
+ // payer = 0 for payer-agnostic
254
+ paymentInfo.receiver,
255
+ paymentInfo.token,
256
+ paymentInfo.maxAmount,
257
+ paymentInfo.preApprovalExpiry,
258
+ paymentInfo.authorizationExpiry,
259
+ paymentInfo.refundExpiry,
260
+ paymentInfo.minFeeBps,
261
+ paymentInfo.maxFeeBps,
262
+ paymentInfo.feeReceiver,
263
+ paymentInfo.salt
264
+ ])
265
+ );
266
+ return (0, import_viem.keccak256)(
267
+ (0, import_viem.encodeAbiParameters)((0, import_viem.parseAbiParameters)(NONCE_ABI_PARAMS), [
268
+ BigInt(chainId),
269
+ escrowContract,
270
+ paymentInfoHash
271
+ ])
272
+ );
273
+ }
274
+ async function signERC3009(wallet, authorization, domain) {
275
+ if (!wallet.account) {
276
+ throw new Error("WalletClient must have an account");
277
+ }
278
+ return wallet.signTypedData({
279
+ account: wallet.account,
280
+ domain,
281
+ types: ERC3009_TYPES,
282
+ primaryType: "ReceiveWithAuthorization",
283
+ message: {
284
+ from: authorization.from,
285
+ to: authorization.to,
286
+ value: authorization.value,
287
+ validAfter: authorization.validAfter,
288
+ validBefore: authorization.validBefore,
289
+ nonce: authorization.nonce
290
+ }
291
+ });
292
+ }
293
+
294
+ // src/client/escrow.ts
295
+ var EscrowScheme = class {
296
+ constructor(walletClient, options = {}) {
297
+ this.scheme = "escrow";
298
+ if (!walletClient.account) {
299
+ throw new Error("WalletClient must have an account");
300
+ }
301
+ if (!walletClient.chain) {
302
+ throw new Error("WalletClient must have a chain");
303
+ }
304
+ this.wallet = walletClient;
305
+ this.chainId = walletClient.chain.id;
306
+ this.network = `eip155:${walletClient.chain.id}`;
307
+ this.sessionDuration = options.sessionDuration ?? DEFAULT_SESSION_DURATION;
308
+ this.refundWindow = options.refundWindow ?? DEFAULT_REFUND_WINDOW;
309
+ this.customDepositAmount = options.depositAmount ? BigInt(options.depositAmount) : void 0;
310
+ this.sessions = new SessionManager(this.network, {
311
+ storage: options.storage,
312
+ storageKey: options.storageKey
313
+ });
314
+ }
315
+ get address() {
316
+ return this.wallet.account.address;
317
+ }
318
+ // ========== Payment Payload Creation ==========
319
+ /**
320
+ * Creates payment payload for escrow scheme.
321
+ * Auto-detects whether to create new session or use existing one.
322
+ */
323
+ async createPaymentPayload(x402Version, paymentRequirements) {
324
+ const receiver = (0, import_viem2.getAddress)(paymentRequirements.payTo);
325
+ const amount = BigInt(paymentRequirements.amount);
326
+ const existingSession = this.sessions.findBest(receiver, amount);
327
+ if (existingSession) {
328
+ return this.createUsagePayload(x402Version, existingSession, paymentRequirements.amount);
329
+ }
330
+ return this.createCreationPayload(x402Version, paymentRequirements);
331
+ }
332
+ // ========== Private: Payload Builders ==========
333
+ /**
334
+ * Session USAGE payload - uses existing session (no signature).
335
+ */
336
+ createUsagePayload(x402Version, session, amount) {
337
+ return {
338
+ x402Version,
339
+ payload: {
340
+ session: {
341
+ id: session.sessionId,
342
+ token: session.sessionToken
343
+ },
344
+ amount,
345
+ requestId: generateRequestId()
346
+ }
347
+ };
348
+ }
349
+ /**
350
+ * Session CREATION payload - requires wallet signature.
351
+ */
352
+ async createCreationPayload(x402Version, paymentRequirements) {
353
+ const extra = paymentRequirements.extra;
354
+ if (!extra.escrowContract || !extra.facilitator || !extra.tokenCollector) {
355
+ throw new Error("Missing required escrow configuration in payment requirements");
356
+ }
357
+ const escrowContract = (0, import_viem2.getAddress)(extra.escrowContract);
358
+ const facilitator = (0, import_viem2.getAddress)(extra.facilitator);
359
+ const tokenCollector = (0, import_viem2.getAddress)(extra.tokenCollector);
360
+ const receiver = (0, import_viem2.getAddress)(paymentRequirements.payTo);
361
+ const token = (0, import_viem2.getAddress)(paymentRequirements.asset);
362
+ const now = Math.floor(Date.now() / 1e3);
363
+ const salt = this.generateSalt();
364
+ const authorizationExpiry = now + this.sessionDuration;
365
+ const refundExpiry = authorizationExpiry + this.refundWindow;
366
+ const minDeposit = extra.minDeposit ? BigInt(extra.minDeposit) : BigInt(paymentRequirements.amount);
367
+ const maxDeposit = extra.maxDeposit ? BigInt(extra.maxDeposit) : minDeposit;
368
+ let amount;
369
+ if (this.customDepositAmount !== void 0) {
370
+ if (this.customDepositAmount < minDeposit) {
371
+ throw new Error(
372
+ `Deposit amount ${this.customDepositAmount} is below minimum ${minDeposit}`
373
+ );
374
+ }
375
+ if (this.customDepositAmount > maxDeposit) {
376
+ throw new Error(`Deposit amount ${this.customDepositAmount} exceeds maximum ${maxDeposit}`);
377
+ }
378
+ amount = this.customDepositAmount;
379
+ } else {
380
+ amount = maxDeposit;
381
+ }
382
+ const validAfter = 0n;
383
+ const validBefore = BigInt(authorizationExpiry);
384
+ const nonce = computeEscrowNonce(this.chainId, escrowContract, {
385
+ operator: facilitator,
386
+ payer: this.address,
387
+ receiver,
388
+ token,
389
+ maxAmount: amount,
390
+ preApprovalExpiry: authorizationExpiry,
391
+ authorizationExpiry,
392
+ refundExpiry,
393
+ minFeeBps: 0,
394
+ maxFeeBps: 0,
395
+ feeReceiver: ZERO_ADDRESS,
396
+ salt: BigInt(salt)
397
+ });
398
+ const domain = {
399
+ name: extra.name,
400
+ version: extra.version,
401
+ chainId: this.chainId,
402
+ verifyingContract: token
403
+ };
404
+ const signature = await signERC3009(
405
+ this.wallet,
406
+ { from: this.address, to: tokenCollector, value: amount, validAfter, validBefore, nonce },
407
+ domain
408
+ );
409
+ const payload = {
410
+ signature,
411
+ authorization: {
412
+ from: this.address,
413
+ to: tokenCollector,
414
+ value: amount.toString(),
415
+ validAfter: validAfter.toString(),
416
+ validBefore: validBefore.toString(),
417
+ nonce
418
+ },
419
+ sessionParams: {
420
+ salt,
421
+ authorizationExpiry,
422
+ refundExpiry
423
+ }
424
+ };
425
+ if (paymentRequirements.scheme === "escrow") {
426
+ payload.requestId = generateRequestId();
427
+ }
428
+ const accepted = {
429
+ scheme: paymentRequirements.scheme,
430
+ network: paymentRequirements.network,
431
+ asset: paymentRequirements.asset,
432
+ amount: paymentRequirements.amount,
433
+ payTo: paymentRequirements.payTo,
434
+ maxTimeoutSeconds: paymentRequirements.maxTimeoutSeconds,
435
+ extra: { ...paymentRequirements.extra, facilitator, escrowContract, tokenCollector }
436
+ };
437
+ return { x402Version, accepted, payload };
438
+ }
439
+ generateSalt() {
440
+ return (0, import_viem2.toHex)(generateRandomBytes(32));
441
+ }
442
+ };
443
+
444
+ // src/client/session-wrapper.ts
445
+ function createEscrowFetch(walletClient, options) {
446
+ const scheme = new EscrowScheme(walletClient, options);
447
+ const x402 = new import_client.x402Client().register(scheme.network, scheme);
448
+ const baseFetch = options?.fetch ?? globalThis.fetch;
449
+ const paidFetch = (0, import_fetch.wrapFetchWithPayment)(baseFetch, x402);
450
+ return {
451
+ fetch: withSessionExtraction(paidFetch, scheme),
452
+ scheme,
453
+ x402
454
+ // Expose for adding hooks
455
+ };
456
+ }
457
+ function extractSession(getHeader, escrowScheme) {
458
+ const paymentResponseHeader = getHeader("PAYMENT-RESPONSE") || getHeader("payment-response");
459
+ if (!paymentResponseHeader) return;
460
+ try {
461
+ const data = JSON.parse(fromBase64(paymentResponseHeader));
462
+ if (!data.session?.id) return;
463
+ if (!data.session.token) {
464
+ if (data.session.balance !== void 0) {
465
+ escrowScheme.sessions.updateBalance(data.session.id, data.session.balance);
466
+ }
467
+ return;
468
+ }
469
+ const receiver = data.requirements?.payTo || data.receiver;
470
+ if (!receiver) {
471
+ if (process.env.NODE_ENV !== "production") {
472
+ console.warn("[x402] Session missing receiver - cannot store");
473
+ }
474
+ return;
475
+ }
476
+ if (!(0, import_viem3.isAddress)(receiver)) {
477
+ if (process.env.NODE_ENV !== "production") {
478
+ console.warn("[x402] Invalid receiver address in session:", receiver);
479
+ }
480
+ return;
481
+ }
482
+ escrowScheme.sessions.store({
483
+ sessionId: data.session.id,
484
+ sessionToken: data.session.token,
485
+ network: escrowScheme.network,
486
+ payer: escrowScheme.address,
487
+ receiver: (0, import_viem3.getAddress)(receiver),
488
+ balance: data.session.balance || "0",
489
+ authorizationExpiry: data.session.expiresAt || 0
490
+ });
491
+ } catch (error) {
492
+ if (process.env.NODE_ENV !== "production") {
493
+ console.warn("[x402] Failed to parse PAYMENT-RESPONSE:", error);
494
+ }
495
+ }
496
+ }
497
+ function withSessionExtraction(paidFetch, escrowScheme) {
498
+ return async (input, init) => {
499
+ const response = await paidFetch(input, init);
500
+ extractSession((name) => response.headers.get(name), escrowScheme);
501
+ return response;
502
+ };
503
+ }
504
+ function withAxiosSessionExtraction(escrowScheme) {
505
+ return (response) => {
506
+ extractSession((name) => response.headers[name.toLowerCase()], escrowScheme);
507
+ return response;
508
+ };
509
+ }
510
+ // Annotate the CommonJS export names for ESM import in node:
511
+ 0 && (module.exports = {
512
+ BrowserLocalStorage,
513
+ EscrowScheme,
514
+ InMemoryStorage,
515
+ SessionManager,
516
+ X402_VERSION,
517
+ computeEscrowNonce,
518
+ createEscrowFetch,
519
+ createStorage,
520
+ signERC3009,
521
+ withAxiosSessionExtraction,
522
+ withSessionExtraction
523
+ });
524
+ //# sourceMappingURL=index.cjs.map