@x402x/facilitator-sdk 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/dist/index.js ADDED
@@ -0,0 +1,994 @@
1
+ 'use strict';
2
+
3
+ var extensions = require('@x402x/extensions');
4
+ var viem = require('viem');
5
+ var accounts = require('viem/accounts');
6
+
7
+ // src/facilitator.ts
8
+ function isValidEthereumAddress(address) {
9
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
10
+ }
11
+ function isValidHex(hex) {
12
+ return /^0x[a-fA-F0-9]*$/.test(hex) && hex.length % 2 === 0 && hex.length >= 2;
13
+ }
14
+ function isValid32ByteHex(hex) {
15
+ return /^0x[a-fA-F0-9]{64}$/.test(hex);
16
+ }
17
+ function isValid256BitHex(hex) {
18
+ return /^0x[a-fA-F0-9]{1,64}$/.test(hex);
19
+ }
20
+ function validateSettlementRouter(network, router, allowedRouters, networkConfig) {
21
+ if (!isValidEthereumAddress(router)) {
22
+ throw new extensions.FacilitatorValidationError(`Invalid SettlementRouter address: ${router}`);
23
+ }
24
+ if (allowedRouters && network in allowedRouters) {
25
+ const networkAllowedRouters = allowedRouters[network];
26
+ if (networkAllowedRouters.length > 0 && !networkAllowedRouters.includes(router)) {
27
+ throw new extensions.FacilitatorValidationError(
28
+ `SettlementRouter ${router} not allowed for network ${network}. Allowed routers: ${networkAllowedRouters.join(", ")}`
29
+ );
30
+ }
31
+ }
32
+ if (networkConfig?.settlementRouter && router !== networkConfig.settlementRouter) {
33
+ throw new extensions.FacilitatorValidationError(
34
+ `SettlementRouter ${router} does not match network config expected router ${networkConfig.settlementRouter}`
35
+ );
36
+ }
37
+ return router;
38
+ }
39
+ function validateSettlementExtra(extra) {
40
+ if (!extra || typeof extra !== "object") {
41
+ throw new extensions.FacilitatorValidationError("Missing or invalid extra field");
42
+ }
43
+ const e = extra;
44
+ if (!e.settlementRouter || typeof e.settlementRouter !== "string") {
45
+ throw new extensions.FacilitatorValidationError("Missing or invalid settlementRouter");
46
+ }
47
+ if (!isValidEthereumAddress(e.settlementRouter)) {
48
+ throw new extensions.FacilitatorValidationError("Invalid settlementRouter address format");
49
+ }
50
+ if (!e.salt || typeof e.salt !== "string") {
51
+ throw new extensions.FacilitatorValidationError("Missing or invalid salt");
52
+ }
53
+ if (!isValid32ByteHex(e.salt)) {
54
+ throw new extensions.FacilitatorValidationError("Salt must be a 32-byte hex string");
55
+ }
56
+ if (!e.payTo || typeof e.payTo !== "string") {
57
+ throw new extensions.FacilitatorValidationError("Missing or invalid payTo");
58
+ }
59
+ if (!isValidEthereumAddress(e.payTo)) {
60
+ throw new extensions.FacilitatorValidationError("Invalid payTo address format");
61
+ }
62
+ if (!e.facilitatorFee || typeof e.facilitatorFee !== "string") {
63
+ throw new extensions.FacilitatorValidationError("Missing or invalid facilitatorFee");
64
+ }
65
+ if (!isValid256BitHex(e.facilitatorFee)) {
66
+ throw new extensions.FacilitatorValidationError("Facilitator fee must be a valid hex number");
67
+ }
68
+ if (!e.hook || typeof e.hook !== "string") {
69
+ throw new extensions.FacilitatorValidationError("Missing or invalid hook");
70
+ }
71
+ if (!isValidEthereumAddress(e.hook)) {
72
+ throw new extensions.FacilitatorValidationError("Invalid hook address format");
73
+ }
74
+ if (!e.hookData || typeof e.hookData !== "string") {
75
+ throw new extensions.FacilitatorValidationError("Missing or invalid hookData");
76
+ }
77
+ if (!isValidHex(e.hookData)) {
78
+ throw new extensions.FacilitatorValidationError("Hook data must be valid hex");
79
+ }
80
+ return {
81
+ settlementRouter: e.settlementRouter,
82
+ salt: e.salt,
83
+ payTo: e.payTo,
84
+ facilitatorFee: e.facilitatorFee,
85
+ hook: e.hook,
86
+ hookData: e.hookData
87
+ };
88
+ }
89
+ function validateNetwork(network) {
90
+ if (!network || typeof network !== "string") {
91
+ throw new extensions.FacilitatorValidationError("Invalid network: must be a non-empty string");
92
+ }
93
+ if (!/^(eip155:\d+|[a-z][a-z0-9-]*[a-z0-9])$/.test(network)) {
94
+ throw new extensions.FacilitatorValidationError(`Invalid network format: ${network}`);
95
+ }
96
+ return network;
97
+ }
98
+ function validateFacilitatorConfig(config) {
99
+ if (!config.signer && !config.privateKey) {
100
+ throw new extensions.FacilitatorValidationError(
101
+ "Missing signer or privateKey in facilitator configuration"
102
+ );
103
+ }
104
+ if (config.signer) {
105
+ if (!isValidEthereumAddress(config.signer)) {
106
+ throw new extensions.FacilitatorValidationError(`Invalid signer address: ${config.signer}`);
107
+ }
108
+ }
109
+ if (config.privateKey) {
110
+ const privateKey = config.privateKey;
111
+ const hasPrefix = privateKey.startsWith("0x") || privateKey.startsWith("0X");
112
+ const hexBody = hasPrefix ? privateKey.slice(2) : privateKey;
113
+ if (!/^[a-fA-F0-9]{64}$/.test(hexBody)) {
114
+ throw new extensions.FacilitatorValidationError(
115
+ "Invalid private key format: must be 32-byte hex string (64 hex chars, with optional 0x prefix)"
116
+ );
117
+ }
118
+ }
119
+ if (config.allowedRouters) {
120
+ for (const [network, routers] of Object.entries(config.allowedRouters)) {
121
+ validateNetwork(network);
122
+ if (!Array.isArray(routers)) {
123
+ throw new extensions.FacilitatorValidationError(`Allowed routers for ${network} must be an array`);
124
+ }
125
+ for (const router of routers) {
126
+ if (!isValidEthereumAddress(router)) {
127
+ throw new extensions.FacilitatorValidationError(`Invalid router address for ${network}: ${router}`);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ if (config.rpcUrls) {
133
+ for (const [network, rpcUrl] of Object.entries(config.rpcUrls)) {
134
+ validateNetwork(network);
135
+ if (typeof rpcUrl !== "string" || !rpcUrl.startsWith("http")) {
136
+ throw new extensions.FacilitatorValidationError(`Invalid RPC URL for ${network}: ${rpcUrl}`);
137
+ }
138
+ }
139
+ }
140
+ }
141
+ function validateGasLimit(gasLimit) {
142
+ if (gasLimit <= 0n) {
143
+ throw new extensions.FacilitatorValidationError("Gas limit must be positive");
144
+ }
145
+ if (gasLimit > 10000000n) {
146
+ throw new extensions.FacilitatorValidationError("Gas limit too large (> 10M)");
147
+ }
148
+ }
149
+ function validateGasMultiplier(multiplier) {
150
+ if (multiplier <= 0) {
151
+ throw new extensions.FacilitatorValidationError("Gas multiplier must be positive");
152
+ }
153
+ if (multiplier > 5) {
154
+ throw new extensions.FacilitatorValidationError("Gas multiplier too large (> 5x)");
155
+ }
156
+ }
157
+ function validateFeeAmount(fee, minFee, maxFee) {
158
+ let feeBigInt;
159
+ try {
160
+ feeBigInt = BigInt(fee);
161
+ } catch (error) {
162
+ throw new extensions.FacilitatorValidationError(`Invalid fee amount: ${fee}. Must be a valid number.`);
163
+ }
164
+ if (feeBigInt < 0n) {
165
+ throw new extensions.FacilitatorValidationError("Fee cannot be negative");
166
+ }
167
+ if (minFee && feeBigInt < BigInt(minFee)) {
168
+ throw new extensions.FacilitatorValidationError(`Fee below minimum: ${fee} < ${minFee}`);
169
+ }
170
+ if (maxFee && feeBigInt > BigInt(maxFee)) {
171
+ throw new extensions.FacilitatorValidationError(`Fee above maximum: ${fee} > ${maxFee}`);
172
+ }
173
+ }
174
+ function networkConfigToChain(networkConfig, rpcUrl) {
175
+ return {
176
+ id: networkConfig.chainId,
177
+ name: networkConfig.name,
178
+ nativeCurrency: {
179
+ name: networkConfig.metadata?.nativeToken || "ETH",
180
+ symbol: networkConfig.metadata?.nativeToken || "ETH",
181
+ decimals: 18
182
+ },
183
+ rpcUrls: {
184
+ default: {
185
+ http: [rpcUrl]
186
+ }
187
+ },
188
+ blockExplorers: {
189
+ default: {
190
+ name: "Explorer",
191
+ url: (() => {
192
+ const addressSuffix = "/address/";
193
+ const baseUrl = networkConfig.addressExplorerBaseUrl;
194
+ return baseUrl.endsWith(addressSuffix) ? baseUrl.slice(0, -addressSuffix.length) : baseUrl;
195
+ })()
196
+ }
197
+ },
198
+ testnet: networkConfig.type === "testnet"
199
+ };
200
+ }
201
+ function createPublicClientForNetwork(network, rpcUrls) {
202
+ const canonicalNetwork = extensions.toCanonicalNetworkKey(network);
203
+ const v1NetworkName = extensions.getNetworkName(canonicalNetwork);
204
+ const networkConfig = extensions.getNetworkConfig(v1NetworkName);
205
+ if (!networkConfig) {
206
+ throw new Error(`Network configuration not found for network: ${network}`);
207
+ }
208
+ const rpcUrl = rpcUrls?.[network] || rpcUrls?.[v1NetworkName] || rpcUrls?.[canonicalNetwork];
209
+ if (!rpcUrl) {
210
+ throw new Error(`No RPC URL available for network: ${network}. Please provide RPC URL in config.`);
211
+ }
212
+ const chain = networkConfigToChain(networkConfig, rpcUrl);
213
+ return viem.createPublicClient({
214
+ chain,
215
+ transport: viem.http(rpcUrl)
216
+ });
217
+ }
218
+ function createWalletClientForNetwork(network, signer, rpcUrls, transport, privateKey) {
219
+ const canonicalNetwork = extensions.toCanonicalNetworkKey(network);
220
+ const v1NetworkName = extensions.getNetworkName(canonicalNetwork);
221
+ const networkConfig = extensions.getNetworkConfig(v1NetworkName);
222
+ const rpcUrl = rpcUrls?.[network] || rpcUrls?.[v1NetworkName] || rpcUrls?.[canonicalNetwork];
223
+ if (!rpcUrl) {
224
+ throw new Error(`No RPC URL available for network: ${network}. Please provide RPC URL in config.`);
225
+ }
226
+ if (!signer && !privateKey) {
227
+ throw new Error("Either signer or privateKey must be provided to create wallet client");
228
+ }
229
+ let account;
230
+ if (privateKey) {
231
+ account = accounts.privateKeyToAccount(privateKey);
232
+ } else if (signer) {
233
+ account = signer;
234
+ } else {
235
+ throw new Error("Failed to create account: neither signer nor privateKey provided");
236
+ }
237
+ const chain = networkConfigToChain(networkConfig, rpcUrl);
238
+ return viem.createWalletClient({
239
+ account,
240
+ chain,
241
+ transport: transport || viem.http(rpcUrl)
242
+ });
243
+ }
244
+ function calculateGasLimit(baseFee, facilitatorFee, gasMultiplier = 1.2) {
245
+ validateGasMultiplier(gasMultiplier);
246
+ const baseGas = 200000n;
247
+ const hookGas = facilitatorFee !== "0x0" ? 100000n : 0n;
248
+ const totalGas = (baseGas + hookGas) * BigInt(Math.ceil(gasMultiplier * 100)) / 100n;
249
+ validateGasLimit(totalGas);
250
+ return totalGas;
251
+ }
252
+ async function checkIfSettled(publicClient, router, contextKey) {
253
+ try {
254
+ const isSettled = await publicClient.readContract({
255
+ address: router,
256
+ abi: extensions.SETTLEMENT_ROUTER_ABI,
257
+ functionName: "isSettled",
258
+ args: [contextKey]
259
+ });
260
+ return isSettled;
261
+ } catch (error) {
262
+ throw new Error(
263
+ `Failed to check settlement status: ${error instanceof Error ? error.message : "Unknown error"}`
264
+ );
265
+ }
266
+ }
267
+ async function executeSettlementWithRouter(walletClient, params, config = {}) {
268
+ const gasLimit = config.gasLimit || calculateGasLimit("0x0", params.facilitatorFee, config.gasMultiplier);
269
+ console.log("[executeSettlementWithRouter] Settlement params:", {
270
+ token: params.token,
271
+ from: params.from,
272
+ value: params.value,
273
+ validAfter: params.validAfter,
274
+ validBefore: params.validBefore,
275
+ nonce: params.nonce,
276
+ signature: params.signature ? `${params.signature.slice(0, 10)}...` : void 0,
277
+ salt: params.salt,
278
+ payTo: params.payTo,
279
+ facilitatorFee: params.facilitatorFee,
280
+ hook: params.hook,
281
+ hookData: params.hookData,
282
+ settlementRouter: params.settlementRouter
283
+ });
284
+ try {
285
+ const txHash = await walletClient.writeContract({
286
+ address: params.settlementRouter,
287
+ abi: extensions.SETTLEMENT_ROUTER_ABI,
288
+ functionName: "settleAndExecute",
289
+ args: [
290
+ params.token,
291
+ params.from,
292
+ BigInt(params.value),
293
+ BigInt(params.validAfter),
294
+ BigInt(params.validBefore),
295
+ params.nonce,
296
+ params.signature,
297
+ params.salt,
298
+ params.payTo,
299
+ BigInt(params.facilitatorFee),
300
+ params.hook,
301
+ params.hookData
302
+ ],
303
+ gas: gasLimit,
304
+ chain: walletClient.chain,
305
+ account: walletClient.account ?? null
306
+ });
307
+ return txHash;
308
+ } catch (error) {
309
+ if (error instanceof Error) {
310
+ let errorMessage = `SettlementRouter execution failed: ${error.message}`;
311
+ if ("cause" in error && error.cause) {
312
+ errorMessage += ` (cause: ${error.cause})`;
313
+ }
314
+ throw new Error(errorMessage);
315
+ }
316
+ throw new Error("Unknown error during SettlementRouter execution");
317
+ }
318
+ }
319
+ async function waitForSettlementReceipt(publicClient, txHash, timeoutMs = 3e4) {
320
+ try {
321
+ const receipt = await publicClient.waitForTransactionReceipt({
322
+ hash: txHash,
323
+ timeout: timeoutMs
324
+ });
325
+ return {
326
+ success: receipt.status === "success",
327
+ blockNumber: receipt.blockNumber,
328
+ gasUsed: receipt.gasUsed,
329
+ effectiveGasPrice: receipt.effectiveGasPrice
330
+ };
331
+ } catch (error) {
332
+ throw new Error(
333
+ `Failed to get transaction receipt: ${error instanceof Error ? error.message : "Unknown error"}`
334
+ );
335
+ }
336
+ }
337
+ function parseEvmExactPayload(payload) {
338
+ const evmPayload = payload.payload;
339
+ if (!evmPayload || !evmPayload.signature || !evmPayload.authorization) {
340
+ throw new Error("Invalid EVM exact payload structure");
341
+ }
342
+ return evmPayload;
343
+ }
344
+ function parseSettlementRouterParams(paymentRequirements, paymentPayload) {
345
+ if (!extensions.isSettlementMode(paymentRequirements)) {
346
+ throw new Error("Payment requirements are not in SettlementRouter mode");
347
+ }
348
+ const evmPayload = parseEvmExactPayload(paymentPayload);
349
+ const extra = extensions.parseSettlementExtra(paymentRequirements.extra);
350
+ return {
351
+ token: paymentRequirements.asset,
352
+ from: evmPayload.authorization.from,
353
+ value: paymentRequirements.amount,
354
+ // V2 uses 'amount', not 'maxAmountRequired'
355
+ validAfter: evmPayload.authorization.validAfter || "0x0",
356
+ validBefore: evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF",
357
+ nonce: evmPayload.authorization.nonce,
358
+ signature: evmPayload.signature,
359
+ salt: extra.salt,
360
+ payTo: extra.payTo,
361
+ facilitatorFee: extra.facilitatorFee,
362
+ hook: extra.hook,
363
+ hookData: extra.hookData,
364
+ settlementRouter: extra.settlementRouter
365
+ };
366
+ }
367
+ async function executeSettlementWithWalletClient(walletClient, publicClient, paymentRequirements, paymentPayload, config = {}) {
368
+ try {
369
+ const canonicalNetwork = extensions.toCanonicalNetworkKey(paymentRequirements.network);
370
+ const v1NetworkName = extensions.getNetworkName(canonicalNetwork);
371
+ const networkConfig = extensions.getNetworkConfig(v1NetworkName);
372
+ const settlementRouter = paymentRequirements.extra?.settlementRouter;
373
+ if (!settlementRouter) {
374
+ throw new Error("Missing settlementRouter in payment requirements");
375
+ }
376
+ validateSettlementRouter(
377
+ paymentRequirements.network,
378
+ settlementRouter,
379
+ config.allowedRouters,
380
+ networkConfig
381
+ );
382
+ const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
383
+ const txHash = await executeSettlementWithRouter(walletClient, params, {
384
+ gasLimit: config.gasLimit,
385
+ gasMultiplier: config.gasMultiplier
386
+ });
387
+ const receipt = await waitForSettlementReceipt(publicClient, txHash, config.timeoutMs || 3e4);
388
+ return {
389
+ success: receipt.success,
390
+ transaction: txHash,
391
+ network: paymentRequirements.network,
392
+ payer: params.from,
393
+ // Use params.from for consistency
394
+ errorReason: receipt.success ? void 0 : "Transaction failed"
395
+ };
396
+ } catch (error) {
397
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
398
+ let payer;
399
+ try {
400
+ const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
401
+ payer = params.from;
402
+ } catch (parseError) {
403
+ console.error("[executeSettlementWithWalletClient] Failed to parse params:", parseError);
404
+ try {
405
+ const evmPayload = parseEvmExactPayload(paymentPayload);
406
+ payer = evmPayload.authorization.from;
407
+ } catch {
408
+ payer = void 0;
409
+ }
410
+ }
411
+ console.error("[executeSettlementWithWalletClient] Settlement failed:", {
412
+ error: errorMessage,
413
+ stack: error instanceof Error ? error.stack : void 0,
414
+ network: paymentRequirements.network,
415
+ asset: paymentRequirements.asset,
416
+ payer
417
+ });
418
+ return {
419
+ success: false,
420
+ transaction: "",
421
+ network: paymentRequirements.network,
422
+ payer,
423
+ errorReason: errorMessage
424
+ };
425
+ }
426
+ }
427
+ async function settleWithSettlementRouter(paymentRequirements, paymentPayload, config, options = {}) {
428
+ try {
429
+ const networkConfig = extensions.getNetworkConfig(paymentRequirements.network);
430
+ validateSettlementRouter(
431
+ paymentRequirements.network,
432
+ paymentRequirements.extra?.settlementRouter,
433
+ config.allowedRouters,
434
+ networkConfig
435
+ );
436
+ const params = parseSettlementRouterParams(paymentRequirements, paymentPayload);
437
+ const publicClient = createPublicClientForNetwork(paymentRequirements.network, config.rpcUrls);
438
+ const walletClient = createWalletClientForNetwork(
439
+ paymentRequirements.network,
440
+ config.signer,
441
+ config.rpcUrls,
442
+ void 0,
443
+ config.privateKey
444
+ );
445
+ const txHash = await executeSettlementWithRouter(walletClient, params, {
446
+ gasLimit: options.gasLimit,
447
+ gasMultiplier: options.gasMultiplier
448
+ });
449
+ const receipt = await waitForSettlementReceipt(
450
+ publicClient,
451
+ txHash,
452
+ options.timeoutMs || 3e4
453
+ );
454
+ return {
455
+ success: receipt.success,
456
+ transaction: txHash,
457
+ network: paymentRequirements.network,
458
+ payer: params.from,
459
+ errorReason: receipt.success ? void 0 : "Transaction failed"
460
+ };
461
+ } catch (error) {
462
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
463
+ let payer;
464
+ try {
465
+ const evmPayload = parseEvmExactPayload(paymentPayload);
466
+ payer = evmPayload.authorization.from;
467
+ } catch {
468
+ payer = void 0;
469
+ }
470
+ return {
471
+ success: false,
472
+ transaction: "",
473
+ network: paymentRequirements.network,
474
+ payer,
475
+ errorReason: errorMessage
476
+ };
477
+ }
478
+ }
479
+ var authorizationTypes = {
480
+ TransferWithAuthorization: [
481
+ { name: "from", type: "address" },
482
+ { name: "to", type: "address" },
483
+ { name: "value", type: "uint256" },
484
+ { name: "validAfter", type: "uint256" },
485
+ { name: "validBefore", type: "uint256" },
486
+ { name: "nonce", type: "bytes32" }
487
+ ]
488
+ };
489
+ function parseEvmExactPayload2(payload) {
490
+ const evmPayload = payload.payload;
491
+ if (!evmPayload.signature) {
492
+ throw new extensions.FacilitatorValidationError("Missing signature in EVM exact payload");
493
+ }
494
+ if (!evmPayload.authorization) {
495
+ throw new extensions.FacilitatorValidationError("Missing authorization in EVM exact payload");
496
+ }
497
+ const auth = evmPayload.authorization;
498
+ if (!auth.from || !auth.to || !auth.value || !auth.nonce) {
499
+ throw new extensions.FacilitatorValidationError("Invalid authorization structure in EVM exact payload");
500
+ }
501
+ return evmPayload;
502
+ }
503
+ var eip3009ABI = [
504
+ {
505
+ inputs: [
506
+ { name: "from", type: "address" },
507
+ { name: "to", type: "address" },
508
+ { name: "value", type: "uint256" },
509
+ { name: "validAfter", type: "uint256" },
510
+ { name: "validBefore", type: "uint256" },
511
+ { name: "nonce", type: "bytes32" },
512
+ { name: "signature", type: "bytes" }
513
+ ],
514
+ name: "transferWithAuthorization",
515
+ outputs: [],
516
+ stateMutability: "nonpayable",
517
+ type: "function"
518
+ },
519
+ {
520
+ inputs: [{ name: "account", type: "address" }],
521
+ name: "balanceOf",
522
+ outputs: [{ name: "", type: "uint256" }],
523
+ stateMutability: "view",
524
+ type: "function"
525
+ }
526
+ ];
527
+ var RouterSettlementFacilitator = class {
528
+ constructor(config) {
529
+ this.scheme = "exact";
530
+ this.caipFamily = "eip155:*";
531
+ validateFacilitatorConfig(config);
532
+ this.config = {
533
+ // Default values
534
+ gasConfig: {
535
+ maxGasLimit: 5000000n,
536
+ gasMultiplier: 1.2
537
+ },
538
+ feeConfig: {
539
+ minFee: "0x0",
540
+ maxFee: "0xFFFFFFFFFFFFFFFF"
541
+ },
542
+ timeouts: {
543
+ verify: 5e3,
544
+ // 5 seconds
545
+ settle: 3e4
546
+ // 30 seconds
547
+ },
548
+ // Override with user config
549
+ ...config
550
+ };
551
+ }
552
+ /**
553
+ * Get scheme-specific extra data for responses
554
+ */
555
+ getExtra(network) {
556
+ try {
557
+ if (!network || typeof network !== "string" || network.trim() === "") {
558
+ return void 0;
559
+ }
560
+ const networkConfig = extensions.getNetworkConfig(network);
561
+ if (!networkConfig) {
562
+ return void 0;
563
+ }
564
+ return {
565
+ scheme: this.scheme,
566
+ caipFamily: this.caipFamily,
567
+ settlementRouter: networkConfig?.settlementRouter,
568
+ defaultAsset: networkConfig?.defaultAsset,
569
+ supportedNetworks: [network]
570
+ // Can be expanded for multi-network support
571
+ };
572
+ } catch (error) {
573
+ return void 0;
574
+ }
575
+ }
576
+ /**
577
+ * Get signer addresses for the network
578
+ * Derives from privateKey if signer address not explicitly provided
579
+ */
580
+ getSigners(network) {
581
+ validateNetwork(network);
582
+ if (this.config.signer) {
583
+ return [this.config.signer];
584
+ }
585
+ if (this.config.privateKey) {
586
+ const account = accounts.privateKeyToAccount(this.config.privateKey);
587
+ return [account.address];
588
+ }
589
+ throw new Error("Either signer or privateKey must be provided in FacilitatorConfig");
590
+ }
591
+ /**
592
+ * Verify payment payload without executing settlement
593
+ */
594
+ async verify(payload, requirements) {
595
+ try {
596
+ this.validateBasicPayload(payload, requirements);
597
+ const isRouterSettlement = extensions.isSettlementMode(requirements);
598
+ if (isRouterSettlement) {
599
+ return await this.verifySettlementRouter(payload, requirements);
600
+ } else {
601
+ return await this.verifyStandard(payload, requirements);
602
+ }
603
+ } catch (error) {
604
+ let payer;
605
+ try {
606
+ const evmPayload = parseEvmExactPayload2(payload);
607
+ payer = evmPayload.authorization.from;
608
+ } catch {
609
+ payer = void 0;
610
+ }
611
+ if (error instanceof extensions.FacilitatorValidationError || error instanceof extensions.SettlementRouterError) {
612
+ return {
613
+ isValid: false,
614
+ invalidReason: error.message,
615
+ payer: payer || ""
616
+ };
617
+ }
618
+ return {
619
+ isValid: false,
620
+ invalidReason: `Verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
621
+ payer: payer || ""
622
+ };
623
+ }
624
+ }
625
+ /**
626
+ * Settle payment by executing blockchain transaction
627
+ */
628
+ async settle(payload, requirements) {
629
+ try {
630
+ const verification = await this.verify(payload, requirements);
631
+ if (!verification.isValid) {
632
+ return {
633
+ success: false,
634
+ transaction: "",
635
+ network: requirements.network,
636
+ payer: verification.payer,
637
+ errorReason: verification.invalidReason || "Payment verification failed"
638
+ };
639
+ }
640
+ const isRouterSettlement = extensions.isSettlementMode(requirements);
641
+ if (isRouterSettlement) {
642
+ return await this.settleWithRouter(payload, requirements);
643
+ } else {
644
+ return await this.settleStandard(payload, requirements);
645
+ }
646
+ } catch (error) {
647
+ let payer;
648
+ try {
649
+ const evmPayload = parseEvmExactPayload2(payload);
650
+ payer = evmPayload.authorization.from;
651
+ } catch {
652
+ payer = void 0;
653
+ }
654
+ return {
655
+ success: false,
656
+ transaction: "",
657
+ network: requirements.network,
658
+ payer: payer || "",
659
+ errorReason: error instanceof Error ? error.message : "Unknown settlement error"
660
+ };
661
+ }
662
+ }
663
+ /**
664
+ * Validate basic payload and requirements
665
+ */
666
+ validateBasicPayload(payload, requirements) {
667
+ validateNetwork(requirements.network);
668
+ if (requirements.scheme !== this.scheme) {
669
+ throw new extensions.FacilitatorValidationError(
670
+ `Scheme mismatch: expected ${this.scheme}, got ${requirements.scheme}`
671
+ );
672
+ }
673
+ if (!requirements.network.startsWith("eip155:")) {
674
+ throw new extensions.FacilitatorValidationError(
675
+ `Unsupported network family: ${requirements.network}. Only EVM networks (eip155:*) are supported`
676
+ );
677
+ }
678
+ parseEvmExactPayload2(payload);
679
+ if (!requirements.asset) {
680
+ throw new extensions.FacilitatorValidationError("Missing asset in payment requirements");
681
+ }
682
+ if (!requirements.payTo) {
683
+ throw new extensions.FacilitatorValidationError("Missing payTo address in payment requirements");
684
+ }
685
+ if (!requirements.amount) {
686
+ throw new extensions.FacilitatorValidationError("Missing amount in payment requirements");
687
+ }
688
+ }
689
+ /**
690
+ * Verify payment for SettlementRouter mode
691
+ */
692
+ async verifySettlementRouter(payload, requirements) {
693
+ const evmPayload = parseEvmExactPayload2(payload);
694
+ const payer = evmPayload.authorization.from;
695
+ const settlementExtra = validateSettlementExtra(requirements.extra);
696
+ const networkConfig = extensions.getNetworkConfig(requirements.network);
697
+ validateSettlementRouter(
698
+ requirements.network,
699
+ settlementExtra.settlementRouter,
700
+ this.config.allowedRouters,
701
+ networkConfig
702
+ );
703
+ validateFeeAmount(
704
+ settlementExtra.facilitatorFee,
705
+ this.config.feeConfig?.minFee,
706
+ this.config.feeConfig?.maxFee
707
+ );
708
+ const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
709
+ const extraWithDomain = requirements.extra;
710
+ const eip712Name = extraWithDomain?.name || "USD Coin";
711
+ const eip712Version = extraWithDomain?.version || "2";
712
+ try {
713
+ const parsedSignature = viem.parseErc6492Signature(evmPayload.signature);
714
+ const typedData = {
715
+ types: authorizationTypes,
716
+ primaryType: "TransferWithAuthorization",
717
+ domain: {
718
+ name: eip712Name,
719
+ version: eip712Version,
720
+ chainId: parseInt(requirements.network.split(":")[1]),
721
+ verifyingContract: requirements.asset
722
+ },
723
+ message: {
724
+ from: payer,
725
+ to: evmPayload.authorization.to,
726
+ value: BigInt(evmPayload.authorization.value),
727
+ validAfter: BigInt(evmPayload.authorization.validAfter || "0x0"),
728
+ validBefore: BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
729
+ nonce: evmPayload.authorization.nonce
730
+ }
731
+ };
732
+ const isValidSignature = await viem.verifyTypedData({
733
+ address: payer,
734
+ ...typedData,
735
+ signature: parsedSignature.signature
736
+ });
737
+ if (!isValidSignature) {
738
+ return {
739
+ isValid: false,
740
+ invalidReason: "Invalid signature",
741
+ payer
742
+ };
743
+ }
744
+ } catch (error) {
745
+ return {
746
+ isValid: false,
747
+ invalidReason: `Signature verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
748
+ payer
749
+ };
750
+ }
751
+ try {
752
+ const chainId = parseInt(requirements.network.split(":")[1]);
753
+ const calculatedCommitment = extensions.calculateCommitment({
754
+ chainId,
755
+ hub: settlementExtra.settlementRouter,
756
+ asset: requirements.asset,
757
+ from: payer,
758
+ value: evmPayload.authorization.value,
759
+ validAfter: evmPayload.authorization.validAfter || "0x0",
760
+ validBefore: evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF",
761
+ salt: settlementExtra.salt,
762
+ payTo: settlementExtra.payTo,
763
+ facilitatorFee: settlementExtra.facilitatorFee,
764
+ hook: settlementExtra.hook,
765
+ hookData: settlementExtra.hookData
766
+ });
767
+ if (evmPayload.authorization.nonce !== calculatedCommitment) {
768
+ return {
769
+ isValid: false,
770
+ invalidReason: "Commitment mismatch: nonce does not match calculated commitment",
771
+ payer
772
+ };
773
+ }
774
+ } catch (error) {
775
+ return {
776
+ isValid: false,
777
+ invalidReason: `Commitment verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
778
+ payer
779
+ };
780
+ }
781
+ try {
782
+ const balance = await publicClient.readContract({
783
+ address: requirements.asset,
784
+ abi: [
785
+ {
786
+ type: "function",
787
+ name: "balanceOf",
788
+ inputs: [{ name: "account", type: "address" }],
789
+ outputs: [{ name: "", type: "uint256" }],
790
+ stateMutability: "view"
791
+ }
792
+ ],
793
+ functionName: "balanceOf",
794
+ args: [payer]
795
+ });
796
+ const totalRequired = BigInt(requirements.amount) + BigInt(settlementExtra.facilitatorFee);
797
+ if (balance < totalRequired) {
798
+ return {
799
+ isValid: false,
800
+ invalidReason: `Insufficient balance: have ${balance}, need ${totalRequired}`,
801
+ payer
802
+ };
803
+ }
804
+ } catch (error) {
805
+ return {
806
+ isValid: false,
807
+ invalidReason: `Balance check failed: ${error instanceof Error ? error.message : "Unknown error"}`,
808
+ payer
809
+ };
810
+ }
811
+ return {
812
+ isValid: true,
813
+ payer
814
+ };
815
+ }
816
+ /**
817
+ * Verify payment for standard mode (fallback)
818
+ */
819
+ async verifyStandard(payload, requirements) {
820
+ const evmPayload = parseEvmExactPayload2(payload);
821
+ const payer = evmPayload.authorization.from;
822
+ const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
823
+ try {
824
+ const parsedSignature = viem.parseErc6492Signature(evmPayload.signature);
825
+ const extraAny = requirements.extra;
826
+ const domainName = extraAny?.name || "USD Coin";
827
+ const domainVersion = extraAny?.version || "3";
828
+ const typedData = {
829
+ types: authorizationTypes,
830
+ primaryType: "TransferWithAuthorization",
831
+ domain: {
832
+ name: domainName,
833
+ version: domainVersion,
834
+ chainId: parseInt(requirements.network.split(":")[1]),
835
+ verifyingContract: requirements.asset
836
+ },
837
+ message: {
838
+ from: payer,
839
+ to: requirements.payTo,
840
+ value: BigInt(requirements.amount),
841
+ validAfter: BigInt(evmPayload.authorization.validAfter || "0x0"),
842
+ validBefore: BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
843
+ nonce: evmPayload.authorization.nonce
844
+ }
845
+ };
846
+ const isValidSignature = await viem.verifyTypedData({
847
+ address: payer,
848
+ ...typedData,
849
+ signature: parsedSignature.signature
850
+ });
851
+ if (!isValidSignature) {
852
+ return {
853
+ isValid: false,
854
+ invalidReason: "Invalid signature",
855
+ payer
856
+ };
857
+ }
858
+ const balance = await publicClient.readContract({
859
+ address: requirements.asset,
860
+ abi: eip3009ABI,
861
+ functionName: "balanceOf",
862
+ args: [payer]
863
+ });
864
+ if (BigInt(balance) < BigInt(requirements.amount)) {
865
+ return {
866
+ isValid: false,
867
+ invalidReason: "Insufficient balance",
868
+ payer
869
+ };
870
+ }
871
+ return {
872
+ isValid: true,
873
+ payer
874
+ };
875
+ } catch (error) {
876
+ return {
877
+ isValid: false,
878
+ invalidReason: `Standard verification failed: ${error instanceof Error ? error.message : "Unknown error"}`,
879
+ payer
880
+ };
881
+ }
882
+ }
883
+ /**
884
+ * Settle payment using SettlementRouter
885
+ */
886
+ async settleWithRouter(payload, requirements) {
887
+ return await settleWithSettlementRouter(requirements, payload, this.config, {
888
+ gasMultiplier: this.config.gasConfig?.gasMultiplier,
889
+ timeoutMs: this.config.timeouts?.settle
890
+ });
891
+ }
892
+ /**
893
+ * Settle payment using standard method (fallback)
894
+ */
895
+ async settleStandard(payload, requirements) {
896
+ const evmPayload = parseEvmExactPayload2(payload);
897
+ const payer = evmPayload.authorization.from;
898
+ const walletClient = createWalletClientForNetwork(
899
+ requirements.network,
900
+ this.config.signer,
901
+ this.config.rpcUrls,
902
+ void 0,
903
+ this.config.privateKey
904
+ );
905
+ const publicClient = createPublicClientForNetwork(requirements.network, this.config.rpcUrls);
906
+ try {
907
+ const parsedSignature = viem.parseErc6492Signature(evmPayload.signature);
908
+ const txHash = await walletClient.writeContract({
909
+ address: requirements.asset,
910
+ abi: eip3009ABI,
911
+ functionName: "transferWithAuthorization",
912
+ args: [
913
+ payer,
914
+ requirements.payTo,
915
+ BigInt(requirements.amount),
916
+ BigInt(evmPayload.authorization.validAfter || "0x0"),
917
+ BigInt(evmPayload.authorization.validBefore || "0xFFFFFFFFFFFFFFFF"),
918
+ evmPayload.authorization.nonce,
919
+ parsedSignature.signature
920
+ ],
921
+ chain: walletClient.chain,
922
+ account: walletClient.account ?? null
923
+ });
924
+ const receipt = await waitForSettlementReceipt(publicClient, txHash);
925
+ return {
926
+ success: receipt.success,
927
+ transaction: txHash,
928
+ network: requirements.network,
929
+ payer,
930
+ errorReason: receipt.success ? void 0 : "Transaction failed"
931
+ };
932
+ } catch (error) {
933
+ return {
934
+ success: false,
935
+ transaction: "",
936
+ network: requirements.network,
937
+ payer,
938
+ errorReason: error instanceof Error ? error.message : "Unknown error"
939
+ };
940
+ }
941
+ }
942
+ };
943
+ function createRouterSettlementFacilitator(config) {
944
+ return new RouterSettlementFacilitator(config);
945
+ }
946
+
947
+ Object.defineProperty(exports, "FacilitatorValidationError", {
948
+ enumerable: true,
949
+ get: function () { return extensions.FacilitatorValidationError; }
950
+ });
951
+ Object.defineProperty(exports, "SETTLEMENT_ROUTER_ABI", {
952
+ enumerable: true,
953
+ get: function () { return extensions.SETTLEMENT_ROUTER_ABI; }
954
+ });
955
+ Object.defineProperty(exports, "SettlementRouterError", {
956
+ enumerable: true,
957
+ get: function () { return extensions.SettlementRouterError; }
958
+ });
959
+ Object.defineProperty(exports, "getNetworkConfig", {
960
+ enumerable: true,
961
+ get: function () { return extensions.getNetworkConfig; }
962
+ });
963
+ Object.defineProperty(exports, "isSettlementMode", {
964
+ enumerable: true,
965
+ get: function () { return extensions.isSettlementMode; }
966
+ });
967
+ Object.defineProperty(exports, "parseSettlementExtra", {
968
+ enumerable: true,
969
+ get: function () { return extensions.parseSettlementExtra; }
970
+ });
971
+ exports.RouterSettlementFacilitator = RouterSettlementFacilitator;
972
+ exports.calculateGasLimit = calculateGasLimit;
973
+ exports.checkIfSettled = checkIfSettled;
974
+ exports.createPublicClientForNetwork = createPublicClientForNetwork;
975
+ exports.createRouterSettlementFacilitator = createRouterSettlementFacilitator;
976
+ exports.createWalletClientForNetwork = createWalletClientForNetwork;
977
+ exports.executeSettlementWithRouter = executeSettlementWithRouter;
978
+ exports.executeSettlementWithWalletClient = executeSettlementWithWalletClient;
979
+ exports.isValid256BitHex = isValid256BitHex;
980
+ exports.isValid32ByteHex = isValid32ByteHex;
981
+ exports.isValidEthereumAddress = isValidEthereumAddress;
982
+ exports.isValidHex = isValidHex;
983
+ exports.parseSettlementRouterParams = parseSettlementRouterParams;
984
+ exports.settleWithSettlementRouter = settleWithSettlementRouter;
985
+ exports.validateFacilitatorConfig = validateFacilitatorConfig;
986
+ exports.validateFeeAmount = validateFeeAmount;
987
+ exports.validateGasLimit = validateGasLimit;
988
+ exports.validateGasMultiplier = validateGasMultiplier;
989
+ exports.validateNetwork = validateNetwork;
990
+ exports.validateSettlementExtra = validateSettlementExtra;
991
+ exports.validateSettlementRouter = validateSettlementRouter;
992
+ exports.waitForSettlementReceipt = waitForSettlementReceipt;
993
+ //# sourceMappingURL=index.js.map
994
+ //# sourceMappingURL=index.js.map