@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.
@@ -0,0 +1,492 @@
1
+ // src/client/session-wrapper.ts
2
+ import { isAddress, getAddress as getAddress2 } from "viem";
3
+ import { x402Client } from "@x402/core/client";
4
+ import { wrapFetchWithPayment } from "@x402/fetch";
5
+
6
+ // src/client/escrow.ts
7
+ import { toHex as toHex2, getAddress } from "viem";
8
+
9
+ // src/types.ts
10
+ var X402_VERSION = 2;
11
+ function fromBase64(str) {
12
+ try {
13
+ if (typeof atob !== "undefined") {
14
+ return decodeURIComponent(escape(atob(str)));
15
+ }
16
+ return Buffer.from(str, "base64").toString("utf-8");
17
+ } catch {
18
+ return Buffer.from(str, "base64").toString("utf-8");
19
+ }
20
+ }
21
+ function generateRequestId() {
22
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
23
+ return crypto.randomUUID();
24
+ }
25
+ return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;
26
+ }
27
+ function generateRandomBytes(length) {
28
+ const bytes = new Uint8Array(length);
29
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
30
+ crypto.getRandomValues(bytes);
31
+ } else {
32
+ for (let i = 0; i < length; i++) {
33
+ bytes[i] = Math.floor(Math.random() * 256);
34
+ }
35
+ }
36
+ return bytes;
37
+ }
38
+
39
+ // src/constants.ts
40
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
41
+ var DEFAULT_SESSION_DURATION = 3600;
42
+ var DEFAULT_REFUND_WINDOW = 86400;
43
+
44
+ // src/client/storage.ts
45
+ var BaseStorage = class {
46
+ constructor() {
47
+ this.sessions = /* @__PURE__ */ new Map();
48
+ }
49
+ get(network, receiver) {
50
+ const now = Date.now() / 1e3;
51
+ for (const session of this.sessions.values()) {
52
+ if (session.network === network && session.receiver.toLowerCase() === receiver.toLowerCase() && session.authorizationExpiry > now) {
53
+ return session;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ findBest(network, receiver, minAmount) {
59
+ const now = Date.now() / 1e3;
60
+ let best = null;
61
+ let bestBalance = 0n;
62
+ for (const session of this.sessions.values()) {
63
+ if (session.network === network && session.receiver.toLowerCase() === receiver.toLowerCase() && session.authorizationExpiry > now) {
64
+ const balance = BigInt(session.balance);
65
+ if (balance >= minAmount && balance > bestBalance) {
66
+ best = session;
67
+ bestBalance = balance;
68
+ }
69
+ }
70
+ }
71
+ return best;
72
+ }
73
+ set(session) {
74
+ this.sessions.set(session.sessionId, session);
75
+ this.onUpdate();
76
+ }
77
+ update(sessionId, balance) {
78
+ const session = this.sessions.get(sessionId);
79
+ if (session) {
80
+ this.sessions.set(sessionId, { ...session, balance });
81
+ this.onUpdate();
82
+ }
83
+ }
84
+ list() {
85
+ return Array.from(this.sessions.values());
86
+ }
87
+ remove(sessionId) {
88
+ this.sessions.delete(sessionId);
89
+ this.onUpdate();
90
+ }
91
+ /** Override in subclasses for persistence */
92
+ onUpdate() {
93
+ }
94
+ };
95
+ var InMemoryStorage = class extends BaseStorage {
96
+ // No persistence needed
97
+ };
98
+ var BrowserLocalStorage = class extends BaseStorage {
99
+ constructor(key = "x402-sessions") {
100
+ super();
101
+ this.key = key;
102
+ this.load();
103
+ }
104
+ onUpdate() {
105
+ this.save();
106
+ }
107
+ load() {
108
+ if (typeof localStorage === "undefined") return;
109
+ try {
110
+ const data = localStorage.getItem(this.key);
111
+ if (data) {
112
+ const sessions = JSON.parse(data);
113
+ for (const s of sessions) this.sessions.set(s.sessionId, s);
114
+ }
115
+ } catch {
116
+ if (process.env.NODE_ENV !== "production") {
117
+ console.warn("[x402] Failed to load sessions from localStorage");
118
+ }
119
+ }
120
+ }
121
+ save() {
122
+ if (typeof localStorage === "undefined") return;
123
+ try {
124
+ localStorage.setItem(this.key, JSON.stringify(Array.from(this.sessions.values())));
125
+ } catch {
126
+ if (process.env.NODE_ENV !== "production") {
127
+ console.warn("[x402] Failed to save sessions to localStorage");
128
+ }
129
+ }
130
+ }
131
+ };
132
+ function createStorage(type, storageKey) {
133
+ return type === "localStorage" ? new BrowserLocalStorage(storageKey) : new InMemoryStorage();
134
+ }
135
+
136
+ // src/client/session-manager.ts
137
+ var SessionManager = class {
138
+ constructor(network, options = {}) {
139
+ this.network = network;
140
+ this.storage = createStorage(options.storage ?? "memory", options.storageKey);
141
+ }
142
+ /**
143
+ * Store a session from escrow settlement response.
144
+ */
145
+ store(session) {
146
+ this.storage.set({ ...session, createdAt: Date.now() });
147
+ }
148
+ /**
149
+ * Get session for a specific receiver.
150
+ */
151
+ getForReceiver(receiver) {
152
+ return this.storage.get(this.network, receiver);
153
+ }
154
+ /**
155
+ * Find best session for receiver with minimum balance.
156
+ */
157
+ findBest(receiver, minAmount) {
158
+ return this.storage.findBest(this.network, receiver, minAmount);
159
+ }
160
+ /**
161
+ * Check if valid session exists for receiver.
162
+ */
163
+ hasValid(receiver, minAmount) {
164
+ const session = minAmount ? this.storage.findBest(this.network, receiver, BigInt(minAmount)) : this.storage.get(this.network, receiver);
165
+ return session !== null;
166
+ }
167
+ /**
168
+ * Update session balance after debit.
169
+ */
170
+ updateBalance(sessionId, newBalance) {
171
+ this.storage.update(sessionId, newBalance);
172
+ }
173
+ /**
174
+ * Get all stored sessions.
175
+ */
176
+ getAll() {
177
+ return this.storage.list();
178
+ }
179
+ /**
180
+ * Remove a specific session.
181
+ */
182
+ remove(sessionId) {
183
+ this.storage.remove(sessionId);
184
+ }
185
+ /**
186
+ * Clear all sessions.
187
+ */
188
+ clear() {
189
+ for (const session of this.storage.list()) {
190
+ this.storage.remove(session.sessionId);
191
+ }
192
+ }
193
+ };
194
+
195
+ // src/client/eip712.ts
196
+ import {
197
+ keccak256,
198
+ encodeAbiParameters,
199
+ parseAbiParameters,
200
+ toHex
201
+ } from "viem";
202
+ 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)";
203
+ var PAYMENT_INFO_TYPEHASH = keccak256(toHex(new TextEncoder().encode(PAYMENT_INFO_TYPE)));
204
+ var PAYMENT_INFO_ABI_PARAMS = "bytes32, address, address, address, address, uint120, uint48, uint48, uint48, uint16, uint16, address, uint256";
205
+ var NONCE_ABI_PARAMS = "uint256, address, bytes32";
206
+ var ERC3009_TYPES = {
207
+ ReceiveWithAuthorization: [
208
+ { name: "from", type: "address" },
209
+ { name: "to", type: "address" },
210
+ { name: "value", type: "uint256" },
211
+ { name: "validAfter", type: "uint256" },
212
+ { name: "validBefore", type: "uint256" },
213
+ { name: "nonce", type: "bytes32" }
214
+ ]
215
+ };
216
+ function computeEscrowNonce(chainId, escrowContract, paymentInfo) {
217
+ const paymentInfoHash = keccak256(
218
+ encodeAbiParameters(parseAbiParameters(PAYMENT_INFO_ABI_PARAMS), [
219
+ PAYMENT_INFO_TYPEHASH,
220
+ paymentInfo.operator,
221
+ ZERO_ADDRESS,
222
+ // payer = 0 for payer-agnostic
223
+ paymentInfo.receiver,
224
+ paymentInfo.token,
225
+ paymentInfo.maxAmount,
226
+ paymentInfo.preApprovalExpiry,
227
+ paymentInfo.authorizationExpiry,
228
+ paymentInfo.refundExpiry,
229
+ paymentInfo.minFeeBps,
230
+ paymentInfo.maxFeeBps,
231
+ paymentInfo.feeReceiver,
232
+ paymentInfo.salt
233
+ ])
234
+ );
235
+ return keccak256(
236
+ encodeAbiParameters(parseAbiParameters(NONCE_ABI_PARAMS), [
237
+ BigInt(chainId),
238
+ escrowContract,
239
+ paymentInfoHash
240
+ ])
241
+ );
242
+ }
243
+ async function signERC3009(wallet, authorization, domain) {
244
+ if (!wallet.account) {
245
+ throw new Error("WalletClient must have an account");
246
+ }
247
+ return wallet.signTypedData({
248
+ account: wallet.account,
249
+ domain,
250
+ types: ERC3009_TYPES,
251
+ primaryType: "ReceiveWithAuthorization",
252
+ message: {
253
+ from: authorization.from,
254
+ to: authorization.to,
255
+ value: authorization.value,
256
+ validAfter: authorization.validAfter,
257
+ validBefore: authorization.validBefore,
258
+ nonce: authorization.nonce
259
+ }
260
+ });
261
+ }
262
+
263
+ // src/client/escrow.ts
264
+ var EscrowScheme = class {
265
+ constructor(walletClient, options = {}) {
266
+ this.scheme = "escrow";
267
+ if (!walletClient.account) {
268
+ throw new Error("WalletClient must have an account");
269
+ }
270
+ if (!walletClient.chain) {
271
+ throw new Error("WalletClient must have a chain");
272
+ }
273
+ this.wallet = walletClient;
274
+ this.chainId = walletClient.chain.id;
275
+ this.network = `eip155:${walletClient.chain.id}`;
276
+ this.sessionDuration = options.sessionDuration ?? DEFAULT_SESSION_DURATION;
277
+ this.refundWindow = options.refundWindow ?? DEFAULT_REFUND_WINDOW;
278
+ this.customDepositAmount = options.depositAmount ? BigInt(options.depositAmount) : void 0;
279
+ this.sessions = new SessionManager(this.network, {
280
+ storage: options.storage,
281
+ storageKey: options.storageKey
282
+ });
283
+ }
284
+ get address() {
285
+ return this.wallet.account.address;
286
+ }
287
+ // ========== Payment Payload Creation ==========
288
+ /**
289
+ * Creates payment payload for escrow scheme.
290
+ * Auto-detects whether to create new session or use existing one.
291
+ */
292
+ async createPaymentPayload(x402Version, paymentRequirements) {
293
+ const receiver = getAddress(paymentRequirements.payTo);
294
+ const amount = BigInt(paymentRequirements.amount);
295
+ const existingSession = this.sessions.findBest(receiver, amount);
296
+ if (existingSession) {
297
+ return this.createUsagePayload(x402Version, existingSession, paymentRequirements.amount);
298
+ }
299
+ return this.createCreationPayload(x402Version, paymentRequirements);
300
+ }
301
+ // ========== Private: Payload Builders ==========
302
+ /**
303
+ * Session USAGE payload - uses existing session (no signature).
304
+ */
305
+ createUsagePayload(x402Version, session, amount) {
306
+ return {
307
+ x402Version,
308
+ payload: {
309
+ session: {
310
+ id: session.sessionId,
311
+ token: session.sessionToken
312
+ },
313
+ amount,
314
+ requestId: generateRequestId()
315
+ }
316
+ };
317
+ }
318
+ /**
319
+ * Session CREATION payload - requires wallet signature.
320
+ */
321
+ async createCreationPayload(x402Version, paymentRequirements) {
322
+ const extra = paymentRequirements.extra;
323
+ if (!extra.escrowContract || !extra.facilitator || !extra.tokenCollector) {
324
+ throw new Error("Missing required escrow configuration in payment requirements");
325
+ }
326
+ const escrowContract = getAddress(extra.escrowContract);
327
+ const facilitator = getAddress(extra.facilitator);
328
+ const tokenCollector = getAddress(extra.tokenCollector);
329
+ const receiver = getAddress(paymentRequirements.payTo);
330
+ const token = getAddress(paymentRequirements.asset);
331
+ const now = Math.floor(Date.now() / 1e3);
332
+ const salt = this.generateSalt();
333
+ const authorizationExpiry = now + this.sessionDuration;
334
+ const refundExpiry = authorizationExpiry + this.refundWindow;
335
+ const minDeposit = extra.minDeposit ? BigInt(extra.minDeposit) : BigInt(paymentRequirements.amount);
336
+ const maxDeposit = extra.maxDeposit ? BigInt(extra.maxDeposit) : minDeposit;
337
+ let amount;
338
+ if (this.customDepositAmount !== void 0) {
339
+ if (this.customDepositAmount < minDeposit) {
340
+ throw new Error(
341
+ `Deposit amount ${this.customDepositAmount} is below minimum ${minDeposit}`
342
+ );
343
+ }
344
+ if (this.customDepositAmount > maxDeposit) {
345
+ throw new Error(`Deposit amount ${this.customDepositAmount} exceeds maximum ${maxDeposit}`);
346
+ }
347
+ amount = this.customDepositAmount;
348
+ } else {
349
+ amount = maxDeposit;
350
+ }
351
+ const validAfter = 0n;
352
+ const validBefore = BigInt(authorizationExpiry);
353
+ const nonce = computeEscrowNonce(this.chainId, escrowContract, {
354
+ operator: facilitator,
355
+ payer: this.address,
356
+ receiver,
357
+ token,
358
+ maxAmount: amount,
359
+ preApprovalExpiry: authorizationExpiry,
360
+ authorizationExpiry,
361
+ refundExpiry,
362
+ minFeeBps: 0,
363
+ maxFeeBps: 0,
364
+ feeReceiver: ZERO_ADDRESS,
365
+ salt: BigInt(salt)
366
+ });
367
+ const domain = {
368
+ name: extra.name,
369
+ version: extra.version,
370
+ chainId: this.chainId,
371
+ verifyingContract: token
372
+ };
373
+ const signature = await signERC3009(
374
+ this.wallet,
375
+ { from: this.address, to: tokenCollector, value: amount, validAfter, validBefore, nonce },
376
+ domain
377
+ );
378
+ const payload = {
379
+ signature,
380
+ authorization: {
381
+ from: this.address,
382
+ to: tokenCollector,
383
+ value: amount.toString(),
384
+ validAfter: validAfter.toString(),
385
+ validBefore: validBefore.toString(),
386
+ nonce
387
+ },
388
+ sessionParams: {
389
+ salt,
390
+ authorizationExpiry,
391
+ refundExpiry
392
+ }
393
+ };
394
+ if (paymentRequirements.scheme === "escrow") {
395
+ payload.requestId = generateRequestId();
396
+ }
397
+ const accepted = {
398
+ scheme: paymentRequirements.scheme,
399
+ network: paymentRequirements.network,
400
+ asset: paymentRequirements.asset,
401
+ amount: paymentRequirements.amount,
402
+ payTo: paymentRequirements.payTo,
403
+ maxTimeoutSeconds: paymentRequirements.maxTimeoutSeconds,
404
+ extra: { ...paymentRequirements.extra, facilitator, escrowContract, tokenCollector }
405
+ };
406
+ return { x402Version, accepted, payload };
407
+ }
408
+ generateSalt() {
409
+ return toHex2(generateRandomBytes(32));
410
+ }
411
+ };
412
+
413
+ // src/client/session-wrapper.ts
414
+ function createEscrowFetch(walletClient, options) {
415
+ const scheme = new EscrowScheme(walletClient, options);
416
+ const x402 = new x402Client().register(scheme.network, scheme);
417
+ const baseFetch = options?.fetch ?? globalThis.fetch;
418
+ const paidFetch = wrapFetchWithPayment(baseFetch, x402);
419
+ return {
420
+ fetch: withSessionExtraction(paidFetch, scheme),
421
+ scheme,
422
+ x402
423
+ // Expose for adding hooks
424
+ };
425
+ }
426
+ function extractSession(getHeader, escrowScheme) {
427
+ const paymentResponseHeader = getHeader("PAYMENT-RESPONSE") || getHeader("payment-response");
428
+ if (!paymentResponseHeader) return;
429
+ try {
430
+ const data = JSON.parse(fromBase64(paymentResponseHeader));
431
+ if (!data.session?.id) return;
432
+ if (!data.session.token) {
433
+ if (data.session.balance !== void 0) {
434
+ escrowScheme.sessions.updateBalance(data.session.id, data.session.balance);
435
+ }
436
+ return;
437
+ }
438
+ const receiver = data.requirements?.payTo || data.receiver;
439
+ if (!receiver) {
440
+ if (process.env.NODE_ENV !== "production") {
441
+ console.warn("[x402] Session missing receiver - cannot store");
442
+ }
443
+ return;
444
+ }
445
+ if (!isAddress(receiver)) {
446
+ if (process.env.NODE_ENV !== "production") {
447
+ console.warn("[x402] Invalid receiver address in session:", receiver);
448
+ }
449
+ return;
450
+ }
451
+ escrowScheme.sessions.store({
452
+ sessionId: data.session.id,
453
+ sessionToken: data.session.token,
454
+ network: escrowScheme.network,
455
+ payer: escrowScheme.address,
456
+ receiver: getAddress2(receiver),
457
+ balance: data.session.balance || "0",
458
+ authorizationExpiry: data.session.expiresAt || 0
459
+ });
460
+ } catch (error) {
461
+ if (process.env.NODE_ENV !== "production") {
462
+ console.warn("[x402] Failed to parse PAYMENT-RESPONSE:", error);
463
+ }
464
+ }
465
+ }
466
+ function withSessionExtraction(paidFetch, escrowScheme) {
467
+ return async (input, init) => {
468
+ const response = await paidFetch(input, init);
469
+ extractSession((name) => response.headers.get(name), escrowScheme);
470
+ return response;
471
+ };
472
+ }
473
+ function withAxiosSessionExtraction(escrowScheme) {
474
+ return (response) => {
475
+ extractSession((name) => response.headers[name.toLowerCase()], escrowScheme);
476
+ return response;
477
+ };
478
+ }
479
+ export {
480
+ BrowserLocalStorage,
481
+ EscrowScheme,
482
+ InMemoryStorage,
483
+ SessionManager,
484
+ X402_VERSION,
485
+ computeEscrowNonce,
486
+ createEscrowFetch,
487
+ createStorage,
488
+ signERC3009,
489
+ withAxiosSessionExtraction,
490
+ withSessionExtraction
491
+ };
492
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/client/session-wrapper.ts","../../src/client/escrow.ts","../../src/types.ts","../../src/constants.ts","../../src/client/storage.ts","../../src/client/session-manager.ts","../../src/client/eip712.ts"],"sourcesContent":["/**\n * Session Extraction Wrappers\n *\n * Thin wrappers that extract sessions from x402 responses and store them.\n *\n * @example Simple (recommended)\n * ```typescript\n * const { fetch: escrowFetch, scheme, x402 } = createEscrowFetch(walletClient);\n * const response = await escrowFetch('https://api.example.com/premium');\n *\n * // Add hooks (user has control)\n * x402.onAfterPaymentCreation(async (ctx) => {\n * console.log('Payment created:', ctx.paymentPayload);\n * });\n * ```\n *\n * @example With custom fetch\n * ```typescript\n * const { fetch: escrowFetch } = createEscrowFetch(walletClient, {\n * fetch: ky, // Use ky, undici, node-fetch, etc.\n * });\n * ```\n *\n * @example Fully composable (manual setup)\n * ```typescript\n * const escrowScheme = new EscrowScheme(walletClient);\n * const x402 = new x402Client().register('eip155:84532', escrowScheme);\n * const paidFetch = wrapFetchWithPayment(fetch, x402);\n * const escrowFetch = withSessionExtraction(paidFetch, escrowScheme);\n * ```\n */\n\nimport { isAddress, getAddress, type WalletClient } from 'viem';\nimport { x402Client } from '@x402/core/client';\nimport { wrapFetchWithPayment } from '@x402/fetch';\nimport { EscrowScheme, type EscrowSchemeOptions } from './escrow';\nimport { fromBase64 } from '../types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\ntype FetchLike = typeof globalThis.fetch;\n\n/** Options for createEscrowFetch */\nexport interface CreateEscrowFetchOptions extends EscrowSchemeOptions {\n /** Custom fetch implementation (default: globalThis.fetch) */\n fetch?: FetchLike;\n}\n\n/** Result from createEscrowFetch */\nexport interface EscrowFetchResult {\n /** Fetch function with automatic payment + session handling */\n fetch: FetchLike;\n /** Access to underlying scheme for session management */\n scheme: EscrowScheme;\n /** Access to x402Client for adding hooks (onBeforePaymentCreation, etc.) */\n x402: x402Client;\n}\n\n// =============================================================================\n// Convenience wrapper (Simple API)\n// =============================================================================\n\n/**\n * Creates a fetch function with automatic escrow payment and session handling.\n * This is the simplest way to integrate x402 escrow payments.\n *\n * @example Simple usage\n * ```typescript\n * const { fetch: escrowFetch, scheme, x402 } = createEscrowFetch(walletClient);\n * const response = await escrowFetch('https://api.example.com/premium');\n *\n * // Access sessions\n * scheme.sessions.getAll();\n * scheme.sessions.hasValid(receiverAddress, '10000');\n * ```\n *\n * @example With custom fetch and hooks\n * ```typescript\n * const { fetch: escrowFetch, x402 } = createEscrowFetch(walletClient, {\n * fetch: customFetch, // Use ky, undici, node-fetch, etc.\n * });\n *\n * // Add hooks for user control\n * x402.onBeforePaymentCreation(async (ctx) => {\n * console.log('About to pay:', ctx.paymentRequirements);\n * });\n * ```\n */\nexport function createEscrowFetch(\n walletClient: WalletClient,\n options?: CreateEscrowFetchOptions\n): EscrowFetchResult {\n const scheme = new EscrowScheme(walletClient, options);\n const x402 = new x402Client().register(scheme.network, scheme);\n\n // Use custom fetch or default to globalThis.fetch\n const baseFetch = options?.fetch ?? globalThis.fetch;\n const paidFetch = wrapFetchWithPayment(baseFetch, x402) as FetchLike;\n\n return {\n fetch: withSessionExtraction(paidFetch, scheme),\n scheme,\n x402, // Expose for adding hooks\n };\n}\n\n// =============================================================================\n// Core extraction logic (header-agnostic)\n// =============================================================================\n\ntype HeaderGetter = (name: string) => string | null | undefined;\n\n/**\n * Core session extraction - works with any header accessor.\n * Extracts session from PAYMENT-RESPONSE header (x402 standard).\n *\n * The facilitator returns session data in SettleResponse, which x402 encodes\n * into the PAYMENT-RESPONSE header as: { ...settleResponse, requirements }\n */\nfunction extractSession(getHeader: HeaderGetter, escrowScheme: EscrowScheme): void {\n const paymentResponseHeader = getHeader('PAYMENT-RESPONSE') || getHeader('payment-response');\n if (!paymentResponseHeader) return;\n\n try {\n const data = JSON.parse(fromBase64(paymentResponseHeader));\n\n // Must have session id at minimum\n if (!data.session?.id) return;\n\n // Check for SESSION USAGE first (no token, just balance update)\n if (!data.session.token) {\n // SESSION USAGE: Update balance only (token already stored from creation)\n if (data.session.balance !== undefined) {\n escrowScheme.sessions.updateBalance(data.session.id, data.session.balance);\n }\n return;\n }\n\n // SESSION CREATION: Has token → Store new session\n // Receiver is in data.requirements.payTo or data.receiver\n const receiver = data.requirements?.payTo || data.receiver;\n if (!receiver) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[x402] Session missing receiver - cannot store');\n }\n return;\n }\n\n // Validate receiver address\n if (!isAddress(receiver)) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[x402] Invalid receiver address in session:', receiver);\n }\n return;\n }\n\n escrowScheme.sessions.store({\n sessionId: data.session.id,\n sessionToken: data.session.token,\n network: escrowScheme.network,\n payer: escrowScheme.address,\n receiver: getAddress(receiver),\n balance: data.session.balance || '0',\n authorizationExpiry: data.session.expiresAt || 0,\n });\n } catch (error) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[x402] Failed to parse PAYMENT-RESPONSE:', error);\n }\n }\n}\n\n// =============================================================================\n// Fetch wrapper (Advanced API)\n// =============================================================================\n\n/**\n * Wraps a paid fetch to automatically extract and store sessions.\n * Use with wrapFetchWithPayment from @x402/fetch.\n *\n * @example\n * ```typescript\n * const escrowScheme = new EscrowScheme(walletClient);\n * const x402 = new x402Client().register('eip155:84532', escrowScheme);\n * const paidFetch = wrapFetchWithPayment(fetch, x402);\n * const escrowFetch = withSessionExtraction(paidFetch, escrowScheme);\n *\n * const response = await escrowFetch('https://api.example.com/premium');\n * // Session automatically stored if present in response\n * ```\n */\nexport function withSessionExtraction(paidFetch: FetchLike, escrowScheme: EscrowScheme): FetchLike {\n return async (input, init) => {\n const response = await paidFetch(input, init);\n extractSession((name) => response.headers.get(name), escrowScheme);\n return response;\n };\n}\n\n// =============================================================================\n// Axios wrapper\n// =============================================================================\n\ninterface AxiosResponseLike {\n headers: Record<string, string | undefined>;\n [key: string]: unknown;\n}\n\n/**\n * Returns an Axios response interceptor that extracts and stores sessions.\n * Use with wrapAxiosWithPayment from @x402/axios.\n *\n * @example\n * ```typescript\n * const escrowScheme = new EscrowScheme(walletClient);\n * const x402 = new x402Client().register('eip155:84532', escrowScheme);\n * const paidAxios = wrapAxiosWithPayment(axios.create(), x402);\n * paidAxios.interceptors.response.use(withAxiosSessionExtraction(escrowScheme));\n *\n * const response = await paidAxios.get('https://api.example.com/premium');\n * // Session automatically stored if present in response\n * ```\n */\nexport function withAxiosSessionExtraction(escrowScheme: EscrowScheme) {\n return <T extends AxiosResponseLike>(response: T): T => {\n extractSession((name) => response.headers[name.toLowerCase()], escrowScheme);\n return response;\n };\n}\n","/**\n * Escrow Client Scheme (Unified)\n *\n * Thin orchestrator that delegates to:\n * - SessionManager: Session lifecycle management\n * - Signer (eip712.ts): EIP-712 signing for session creation\n *\n * Handles both session CREATION and session USAGE in a single scheme.\n * - First call: Creates session with wallet signature (EIP-712)\n * - Subsequent calls: Uses stored session token (no signature needed)\n */\n\nimport { toHex, getAddress, type WalletClient, type Address, type Hex } from 'viem';\nimport {\n generateRequestId,\n generateRandomBytes,\n type PaymentRequirements,\n type Network,\n} from '../types';\nimport { ZERO_ADDRESS, DEFAULT_SESSION_DURATION, DEFAULT_REFUND_WINDOW } from '../constants';\nimport { SessionManager } from './session-manager';\nimport { signERC3009, computeEscrowNonce, type EIP712Domain } from './eip712';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface PaymentPayload {\n x402Version: number;\n resource?: { url: string; description?: string; mimeType?: string };\n accepted?: PaymentRequirements;\n payload: Record<string, unknown>;\n extensions?: Record<string, unknown>;\n}\n\ninterface SchemeNetworkClient {\n readonly scheme: string;\n createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements\n ): Promise<Pick<PaymentPayload, 'x402Version' | 'payload'>>;\n}\n\ninterface EscrowExtra {\n escrowContract: Address;\n facilitator: Address;\n tokenCollector: Address;\n name: string;\n version: string;\n minDeposit?: string;\n maxDeposit?: string;\n}\n\n// ============================================================================\n// Options\n// ============================================================================\n\nexport interface EscrowSchemeOptions {\n /** Session duration in seconds (default: 1 hour) */\n sessionDuration?: number;\n /** Refund window after session expires (default: 24 hours) */\n refundWindow?: number;\n /** Storage type: 'memory' (default) or 'localStorage' */\n storage?: 'memory' | 'localStorage';\n /** localStorage key (default: 'x402-sessions') */\n storageKey?: string;\n /**\n * Custom deposit amount in atomic units (e.g., \"10000000\" for $10 USDC).\n * Must be between minDeposit and maxDeposit from the 402 response.\n * If not specified, defaults to maxDeposit for maximum flexibility.\n */\n depositAmount?: string;\n}\n\n// ============================================================================\n// Escrow Scheme\n// ============================================================================\n\n/**\n * Escrow payment scheme for x402 (Unified).\n *\n * Orchestrates:\n * - SessionManager for session lookup and storage\n * - EIP-712 signer for session creation signatures\n *\n * Auto-preference: If a valid session exists for the receiver,\n * it will be used automatically (no wallet interaction required).\n */\nexport class EscrowScheme implements SchemeNetworkClient {\n readonly scheme = 'escrow';\n\n /** Session manager - use directly for session operations */\n readonly sessions: SessionManager;\n\n private readonly wallet: WalletClient;\n private readonly chainId: number;\n readonly network: Network;\n private readonly sessionDuration: number;\n private readonly refundWindow: number;\n private readonly customDepositAmount?: bigint;\n\n constructor(walletClient: WalletClient, options: EscrowSchemeOptions = {}) {\n if (!walletClient.account) {\n throw new Error('WalletClient must have an account');\n }\n if (!walletClient.chain) {\n throw new Error('WalletClient must have a chain');\n }\n\n this.wallet = walletClient;\n this.chainId = walletClient.chain.id;\n this.network = `eip155:${walletClient.chain.id}`;\n this.sessionDuration = options.sessionDuration ?? DEFAULT_SESSION_DURATION;\n this.refundWindow = options.refundWindow ?? DEFAULT_REFUND_WINDOW;\n this.customDepositAmount = options.depositAmount ? BigInt(options.depositAmount) : undefined;\n this.sessions = new SessionManager(this.network, {\n storage: options.storage,\n storageKey: options.storageKey,\n });\n }\n\n get address(): Address {\n return this.wallet.account!.address;\n }\n\n // ========== Payment Payload Creation ==========\n\n /**\n * Creates payment payload for escrow scheme.\n * Auto-detects whether to create new session or use existing one.\n */\n async createPaymentPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements\n ): Promise<Pick<PaymentPayload, 'x402Version' | 'payload'>> {\n const receiver = getAddress(paymentRequirements.payTo);\n const amount = BigInt(paymentRequirements.amount);\n\n // Check for existing session (auto-preference)\n const existingSession = this.sessions.findBest(receiver, amount);\n\n if (existingSession) {\n return this.createUsagePayload(x402Version, existingSession, paymentRequirements.amount);\n }\n\n return this.createCreationPayload(x402Version, paymentRequirements);\n }\n\n // ========== Private: Payload Builders ==========\n\n /**\n * Session USAGE payload - uses existing session (no signature).\n */\n private createUsagePayload(\n x402Version: number,\n session: { sessionId: string; sessionToken: string },\n amount: string\n ): Pick<PaymentPayload, 'x402Version' | 'payload'> {\n return {\n x402Version,\n payload: {\n session: {\n id: session.sessionId,\n token: session.sessionToken,\n },\n amount,\n requestId: generateRequestId(),\n },\n };\n }\n\n /**\n * Session CREATION payload - requires wallet signature.\n */\n private async createCreationPayload(\n x402Version: number,\n paymentRequirements: PaymentRequirements\n ): Promise<Pick<PaymentPayload, 'x402Version' | 'payload'>> {\n const extra = paymentRequirements.extra as unknown as EscrowExtra;\n\n if (!extra.escrowContract || !extra.facilitator || !extra.tokenCollector) {\n throw new Error('Missing required escrow configuration in payment requirements');\n }\n\n // Normalize addresses\n const escrowContract = getAddress(extra.escrowContract);\n const facilitator = getAddress(extra.facilitator);\n const tokenCollector = getAddress(extra.tokenCollector);\n const receiver = getAddress(paymentRequirements.payTo);\n const token = getAddress(paymentRequirements.asset);\n\n // Compute session parameters\n const now = Math.floor(Date.now() / 1000);\n const salt = this.generateSalt();\n const authorizationExpiry = now + this.sessionDuration;\n const refundExpiry = authorizationExpiry + this.refundWindow;\n\n // Determine deposit amount with validation\n const minDeposit = extra.minDeposit\n ? BigInt(extra.minDeposit)\n : BigInt(paymentRequirements.amount);\n const maxDeposit = extra.maxDeposit ? BigInt(extra.maxDeposit) : minDeposit;\n\n let amount: bigint;\n if (this.customDepositAmount !== undefined) {\n // User specified custom deposit - validate against bounds\n if (this.customDepositAmount < minDeposit) {\n throw new Error(\n `Deposit amount ${this.customDepositAmount} is below minimum ${minDeposit}`\n );\n }\n if (this.customDepositAmount > maxDeposit) {\n throw new Error(`Deposit amount ${this.customDepositAmount} exceeds maximum ${maxDeposit}`);\n }\n amount = this.customDepositAmount;\n } else {\n // Default to maxDeposit for maximum flexibility\n amount = maxDeposit;\n }\n\n const validAfter = 0n;\n const validBefore = BigInt(authorizationExpiry);\n\n // Compute nonce (payer-agnostic)\n const nonce = computeEscrowNonce(this.chainId, escrowContract, {\n operator: facilitator,\n payer: this.address,\n receiver,\n token,\n maxAmount: amount,\n preApprovalExpiry: authorizationExpiry,\n authorizationExpiry,\n refundExpiry,\n minFeeBps: 0,\n maxFeeBps: 0,\n feeReceiver: ZERO_ADDRESS,\n salt: BigInt(salt),\n });\n\n // Sign ERC-3009 authorization\n const domain: EIP712Domain = {\n name: extra.name,\n version: extra.version,\n chainId: this.chainId,\n verifyingContract: token,\n };\n\n const signature = await signERC3009(\n this.wallet,\n { from: this.address, to: tokenCollector, value: amount, validAfter, validBefore, nonce },\n domain\n );\n\n // Build payload\n const payload: Record<string, unknown> = {\n signature,\n authorization: {\n from: this.address,\n to: tokenCollector,\n value: amount.toString(),\n validAfter: validAfter.toString(),\n validBefore: validBefore.toString(),\n nonce,\n },\n sessionParams: {\n salt,\n authorizationExpiry,\n refundExpiry,\n },\n };\n\n // Only add requestId for escrow scheme (not exact-escrow)\n if (paymentRequirements.scheme === 'escrow') {\n payload.requestId = generateRequestId();\n }\n\n // Build accepted block for server routing\n const accepted = {\n scheme: paymentRequirements.scheme,\n network: paymentRequirements.network,\n asset: paymentRequirements.asset,\n amount: paymentRequirements.amount,\n payTo: paymentRequirements.payTo,\n maxTimeoutSeconds: paymentRequirements.maxTimeoutSeconds,\n extra: { ...paymentRequirements.extra, facilitator, escrowContract, tokenCollector },\n };\n\n return { x402Version, accepted, payload } as Pick<PaymentPayload, 'x402Version' | 'payload'>;\n }\n\n private generateSalt(): Hex {\n return toHex(generateRandomBytes(32));\n }\n}\n","/**\n * x402 Protocol Types for @agentokratia/x402-escrow\n *\n * Subset of types needed for the escrow scheme.\n */\n\n/** Current x402 protocol version */\nexport const X402_VERSION = 2;\n\n/** Network identifier in CAIP-2 format (e.g., \"eip155:8453\" for Base) */\nexport type Network = `${string}:${string}`;\n\n/**\n * PaymentRequirements - defines a single payment option\n * Base interface that each scheme extends\n */\nexport interface PaymentRequirements {\n scheme: string;\n network: Network;\n amount: string;\n asset: string;\n payTo: string;\n maxTimeoutSeconds: number;\n extra: Record<string, unknown>;\n}\n\n/**\n * x402 Facilitator settle response (JSON body)\n */\nexport interface SettleResponse {\n success: boolean;\n errorReason?: string;\n payer?: string;\n transaction: string;\n network: string;\n session?: {\n id: string;\n balance: string;\n token?: string;\n expiresAt?: number;\n };\n}\n\n/** Header name constants (x402 v2) */\nexport const X402_HEADERS = {\n /** Server → Client: Payment options (402 response) - base64 JSON */\n PAYMENT_REQUIRED: 'PAYMENT-REQUIRED',\n /** Client → Server: Payment payload - base64 JSON */\n PAYMENT_SIGNATURE: 'PAYMENT-SIGNATURE',\n /** Server → Client: Settlement result - base64 JSON (includes session data) */\n PAYMENT_RESPONSE: 'PAYMENT-RESPONSE',\n} as const;\n\n/** PAYMENT-RESPONSE header data (all schemes) */\nexport interface PaymentResponseData {\n success: boolean;\n transaction: string;\n network: string;\n payer?: string;\n receiver?: string;\n errorReason?: string;\n session?: {\n id: string;\n balance: string;\n token?: string;\n expiresAt?: number;\n };\n requirements?: PaymentRequirements;\n}\n\n/**\n * Base64 encode (works in both browser and Node.js)\n */\nexport function toBase64(str: string): string {\n try {\n if (typeof btoa !== 'undefined') {\n return btoa(unescape(encodeURIComponent(str)));\n }\n return Buffer.from(str, 'utf-8').toString('base64');\n } catch {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n}\n\n/**\n * Base64 decode (works in both browser and Node.js)\n */\nexport function fromBase64(str: string): string {\n try {\n if (typeof atob !== 'undefined') {\n return decodeURIComponent(escape(atob(str)));\n }\n return Buffer.from(str, 'base64').toString('utf-8');\n } catch {\n return Buffer.from(str, 'base64').toString('utf-8');\n }\n}\n\n/**\n * Generate a unique request ID\n */\nexport function generateRequestId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;\n}\n\n/**\n * Generate random bytes\n */\nexport function generateRandomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return bytes;\n}\n\n/**\n * Parse PAYMENT-RESPONSE header.\n */\nexport function parsePaymentResponseHeader(header: string): PaymentResponseData | null {\n try {\n return JSON.parse(fromBase64(header));\n } catch {\n return null;\n }\n}\n","/**\n * Shared Constants for @agentokratia/x402-escrow\n */\n\n// Zero address constant\nexport const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const;\n\n// Session defaults (in seconds)\nexport const DEFAULT_SESSION_DURATION = 3600; // 1 hour\nexport const DEFAULT_REFUND_WINDOW = 86400; // 24 hours\n","/**\n * Session Storage for x402 Escrow Scheme\n *\n * Provides session persistence with two implementations:\n * - InMemoryStorage: For server-side or ephemeral usage\n * - LocalStorage: For browser persistence across page loads\n */\n\nimport type { Address } from 'viem';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface StoredSession {\n sessionId: string;\n sessionToken: string;\n network: string;\n payer: Address;\n receiver: Address;\n balance: string;\n authorizationExpiry: number;\n createdAt: number;\n}\n\nexport interface SessionStorage {\n get(network: string, receiver: Address): StoredSession | null;\n findBest(network: string, receiver: Address, minAmount: bigint): StoredSession | null;\n set(session: StoredSession): void;\n update(sessionId: string, balance: string): void;\n list(): StoredSession[];\n remove(sessionId: string): void;\n}\n\n// ============================================================================\n// Base Storage (shared logic)\n// ============================================================================\n\nabstract class BaseStorage implements SessionStorage {\n protected sessions = new Map<string, StoredSession>();\n\n get(network: string, receiver: Address): StoredSession | null {\n const now = Date.now() / 1000;\n for (const session of this.sessions.values()) {\n if (\n session.network === network &&\n session.receiver.toLowerCase() === receiver.toLowerCase() &&\n session.authorizationExpiry > now\n ) {\n return session;\n }\n }\n return null;\n }\n\n findBest(network: string, receiver: Address, minAmount: bigint): StoredSession | null {\n const now = Date.now() / 1000;\n let best: StoredSession | null = null;\n let bestBalance = 0n;\n\n for (const session of this.sessions.values()) {\n if (\n session.network === network &&\n session.receiver.toLowerCase() === receiver.toLowerCase() &&\n session.authorizationExpiry > now\n ) {\n const balance = BigInt(session.balance);\n if (balance >= minAmount && balance > bestBalance) {\n best = session;\n bestBalance = balance;\n }\n }\n }\n return best;\n }\n\n set(session: StoredSession): void {\n this.sessions.set(session.sessionId, session);\n this.onUpdate();\n }\n\n update(sessionId: string, balance: string): void {\n const session = this.sessions.get(sessionId);\n if (session) {\n this.sessions.set(sessionId, { ...session, balance });\n this.onUpdate();\n }\n }\n\n list(): StoredSession[] {\n return Array.from(this.sessions.values());\n }\n\n remove(sessionId: string): void {\n this.sessions.delete(sessionId);\n this.onUpdate();\n }\n\n /** Override in subclasses for persistence */\n protected onUpdate(): void {}\n}\n\n// ============================================================================\n// In-Memory Storage\n// ============================================================================\n\nexport class InMemoryStorage extends BaseStorage {\n // No persistence needed\n}\n\n// ============================================================================\n// LocalStorage (Browser)\n// ============================================================================\n\nexport class BrowserLocalStorage extends BaseStorage {\n private key: string;\n\n constructor(key = 'x402-sessions') {\n super();\n this.key = key;\n this.load();\n }\n\n protected override onUpdate(): void {\n this.save();\n }\n\n private load(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const data = localStorage.getItem(this.key);\n if (data) {\n const sessions: StoredSession[] = JSON.parse(data);\n for (const s of sessions) this.sessions.set(s.sessionId, s);\n }\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[x402] Failed to load sessions from localStorage');\n }\n }\n }\n\n private save(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n localStorage.setItem(this.key, JSON.stringify(Array.from(this.sessions.values())));\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn('[x402] Failed to save sessions to localStorage');\n }\n }\n }\n}\n\n// ============================================================================\n// Factory\n// ============================================================================\n\nexport function createStorage(\n type: 'memory' | 'localStorage',\n storageKey?: string\n): SessionStorage {\n return type === 'localStorage' ? new BrowserLocalStorage(storageKey) : new InMemoryStorage();\n}\n","/**\n * Session Manager for x402 Escrow Scheme\n *\n * Handles session lifecycle: lookup, storage, and validation.\n * Uses the SessionStorage interface for persistence.\n */\n\nimport type { Address } from 'viem';\nimport type { Network } from '../types';\nimport { createStorage, type SessionStorage, type StoredSession } from './storage';\n\n// Re-export for convenience\nexport type { StoredSession } from './storage';\n\nexport interface SessionManagerOptions {\n /** Storage type: 'memory' (default) or 'localStorage' */\n storage?: 'memory' | 'localStorage';\n /** localStorage key (default: 'x402-sessions') */\n storageKey?: string;\n}\n\n/**\n * Manages session lifecycle for the escrow scheme.\n *\n * Responsibilities:\n * - Store sessions from escrow settlement responses\n * - Find best session for a receiver/amount\n * - Update session balances after debits\n * - Validate session expiry\n */\nexport class SessionManager {\n private readonly storage: SessionStorage;\n private readonly network: Network;\n\n constructor(network: Network, options: SessionManagerOptions = {}) {\n this.network = network;\n this.storage = createStorage(options.storage ?? 'memory', options.storageKey);\n }\n\n /**\n * Store a session from escrow settlement response.\n */\n store(session: Omit<StoredSession, 'createdAt'>): void {\n this.storage.set({ ...session, createdAt: Date.now() });\n }\n\n /**\n * Get session for a specific receiver.\n */\n getForReceiver(receiver: Address): StoredSession | null {\n return this.storage.get(this.network, receiver);\n }\n\n /**\n * Find best session for receiver with minimum balance.\n */\n findBest(receiver: Address, minAmount: bigint): StoredSession | null {\n return this.storage.findBest(this.network, receiver, minAmount);\n }\n\n /**\n * Check if valid session exists for receiver.\n */\n hasValid(receiver: Address, minAmount?: string): boolean {\n const session = minAmount\n ? this.storage.findBest(this.network, receiver, BigInt(minAmount))\n : this.storage.get(this.network, receiver);\n return session !== null;\n }\n\n /**\n * Update session balance after debit.\n */\n updateBalance(sessionId: string, newBalance: string): void {\n this.storage.update(sessionId, newBalance);\n }\n\n /**\n * Get all stored sessions.\n */\n getAll(): StoredSession[] {\n return this.storage.list();\n }\n\n /**\n * Remove a specific session.\n */\n remove(sessionId: string): void {\n this.storage.remove(sessionId);\n }\n\n /**\n * Clear all sessions.\n */\n clear(): void {\n for (const session of this.storage.list()) {\n this.storage.remove(session.sessionId);\n }\n }\n}\n","/**\n * EIP-712 Signing Utilities for x402 Escrow\n *\n * Handles ERC-3009 ReceiveWithAuthorization signing and nonce computation\n * for the escrow contract.\n */\n\nimport {\n keccak256,\n encodeAbiParameters,\n parseAbiParameters,\n toHex,\n type WalletClient,\n type Address,\n type Hex,\n} from 'viem';\nimport { ZERO_ADDRESS } from '../constants';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EIP712Domain {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Address;\n}\n\nexport interface ERC3009Authorization {\n from: Address;\n to: Address;\n value: bigint;\n validAfter: bigint;\n validBefore: bigint;\n nonce: Hex;\n}\n\nexport interface PaymentInfoParams {\n operator: Address;\n payer: Address;\n receiver: Address;\n token: Address;\n maxAmount: bigint;\n preApprovalExpiry: number;\n authorizationExpiry: number;\n refundExpiry: number;\n minFeeBps: number;\n maxFeeBps: number;\n feeReceiver: Address;\n salt: bigint;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** EIP-712 type string for PaymentInfo struct */\nconst PAYMENT_INFO_TYPE =\n '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)';\n\n/** Pre-computed typehash for PaymentInfo */\nconst PAYMENT_INFO_TYPEHASH = keccak256(toHex(new TextEncoder().encode(PAYMENT_INFO_TYPE)));\n\n/** ABI parameter encoding for PaymentInfo struct */\nconst PAYMENT_INFO_ABI_PARAMS =\n 'bytes32, address, address, address, address, uint120, uint48, uint48, uint48, uint16, uint16, address, uint256';\n\n/** ABI parameter encoding for nonce computation */\nconst NONCE_ABI_PARAMS = 'uint256, address, bytes32';\n\n/** ERC-3009 ReceiveWithAuthorization type definition */\nconst ERC3009_TYPES = {\n ReceiveWithAuthorization: [\n { name: 'from', type: 'address' },\n { name: 'to', type: 'address' },\n { name: 'value', type: 'uint256' },\n { name: 'validAfter', type: 'uint256' },\n { name: 'validBefore', type: 'uint256' },\n { name: 'nonce', type: 'bytes32' },\n ],\n} as const;\n\n// ============================================================================\n// Functions\n// ============================================================================\n\n/**\n * Compute the nonce for ERC-3009 authorization.\n * Uses payer=0 for payer-agnostic hash (allows any payer to use this session).\n */\nexport function computeEscrowNonce(\n chainId: number,\n escrowContract: Address,\n paymentInfo: PaymentInfoParams\n): Hex {\n // Use payer=0 for payer-agnostic hash\n const paymentInfoHash = keccak256(\n encodeAbiParameters(parseAbiParameters(PAYMENT_INFO_ABI_PARAMS), [\n PAYMENT_INFO_TYPEHASH,\n paymentInfo.operator,\n ZERO_ADDRESS, // payer = 0 for payer-agnostic\n paymentInfo.receiver,\n paymentInfo.token,\n paymentInfo.maxAmount,\n paymentInfo.preApprovalExpiry,\n paymentInfo.authorizationExpiry,\n paymentInfo.refundExpiry,\n paymentInfo.minFeeBps,\n paymentInfo.maxFeeBps,\n paymentInfo.feeReceiver,\n paymentInfo.salt,\n ])\n );\n\n return keccak256(\n encodeAbiParameters(parseAbiParameters(NONCE_ABI_PARAMS), [\n BigInt(chainId),\n escrowContract,\n paymentInfoHash,\n ])\n );\n}\n\n/**\n * Sign an ERC-3009 ReceiveWithAuthorization message.\n * This is required by USDC FiatTokenV2 for gasless transfers.\n */\nexport async function signERC3009(\n wallet: WalletClient,\n authorization: ERC3009Authorization,\n domain: EIP712Domain\n): Promise<Hex> {\n if (!wallet.account) {\n throw new Error('WalletClient must have an account');\n }\n\n return wallet.signTypedData({\n account: wallet.account,\n domain,\n types: ERC3009_TYPES,\n primaryType: 'ReceiveWithAuthorization',\n message: {\n from: authorization.from,\n to: authorization.to,\n value: authorization.value,\n validAfter: authorization.validAfter,\n validBefore: authorization.validBefore,\n nonce: authorization.nonce,\n },\n });\n}\n"],"mappings":"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","names":["getAddress","toHex","toHex","getAddress"]}