@x402/evm 2.6.0 → 2.8.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.
Files changed (46) hide show
  1. package/README.md +29 -4
  2. package/dist/cjs/exact/client/index.d.ts +12 -5
  3. package/dist/cjs/exact/client/index.js +127 -28
  4. package/dist/cjs/exact/client/index.js.map +1 -1
  5. package/dist/cjs/exact/facilitator/index.d.ts +13 -1
  6. package/dist/cjs/exact/facilitator/index.js +987 -606
  7. package/dist/cjs/exact/facilitator/index.js.map +1 -1
  8. package/dist/cjs/exact/v1/client/index.d.ts +1 -1
  9. package/dist/cjs/exact/v1/client/index.js +11 -5
  10. package/dist/cjs/exact/v1/client/index.js.map +1 -1
  11. package/dist/cjs/exact/v1/facilitator/index.d.ts +16 -1
  12. package/dist/cjs/exact/v1/facilitator/index.js +414 -177
  13. package/dist/cjs/exact/v1/facilitator/index.js.map +1 -1
  14. package/dist/cjs/index.d.ts +2 -2
  15. package/dist/cjs/index.js +143 -30
  16. package/dist/cjs/index.js.map +1 -1
  17. package/dist/cjs/{permit2-DHAq6FTe.d.ts → permit2-U9Zolx3O.d.ts} +38 -5
  18. package/dist/{esm/signer-DC81R8wQ.d.mts → cjs/signer-D912R4mq.d.ts} +9 -3
  19. package/dist/cjs/v1/index.d.ts +1 -1
  20. package/dist/cjs/v1/index.js +6 -0
  21. package/dist/cjs/v1/index.js.map +1 -1
  22. package/dist/esm/chunk-GD4MKCN7.mjs +57 -0
  23. package/dist/esm/chunk-GD4MKCN7.mjs.map +1 -0
  24. package/dist/esm/{chunk-XL6IFXCP.mjs → chunk-IZEI7JTG.mjs} +516 -178
  25. package/dist/esm/chunk-IZEI7JTG.mjs.map +1 -0
  26. package/dist/esm/{chunk-LBIJBD7Q.mjs → chunk-WJWNS4G4.mjs} +113 -20
  27. package/dist/esm/chunk-WJWNS4G4.mjs.map +1 -0
  28. package/dist/esm/exact/client/index.d.mts +12 -5
  29. package/dist/esm/exact/client/index.mjs +3 -2
  30. package/dist/esm/exact/facilitator/index.d.mts +13 -1
  31. package/dist/esm/exact/facilitator/index.mjs +498 -391
  32. package/dist/esm/exact/facilitator/index.mjs.map +1 -1
  33. package/dist/esm/exact/v1/client/index.d.mts +1 -1
  34. package/dist/esm/exact/v1/client/index.mjs +1 -1
  35. package/dist/esm/exact/v1/facilitator/index.d.mts +16 -1
  36. package/dist/esm/exact/v1/facilitator/index.mjs +1 -1
  37. package/dist/esm/index.d.mts +2 -2
  38. package/dist/esm/index.mjs +7 -9
  39. package/dist/esm/index.mjs.map +1 -1
  40. package/dist/esm/{permit2-BuAhWvNC.d.mts → permit2-Bbh3a8_h.d.mts} +38 -5
  41. package/dist/{cjs/signer-DC81R8wQ.d.ts → esm/signer-D912R4mq.d.mts} +9 -3
  42. package/dist/esm/v1/index.d.mts +1 -1
  43. package/dist/esm/v1/index.mjs +1 -1
  44. package/package.json +2 -3
  45. package/dist/esm/chunk-LBIJBD7Q.mjs.map +0 -1
  46. package/dist/esm/chunk-XL6IFXCP.mjs.map +0 -1
@@ -31,7 +31,7 @@ function isPermit2Payload(payload) {
31
31
  }
32
32
 
33
33
  // src/exact/facilitator/eip3009.ts
34
- var import_viem2 = require("viem");
34
+ var import_viem4 = require("viem");
35
35
 
36
36
  // src/constants.ts
37
37
  var authorizationTypes = {
@@ -107,6 +107,23 @@ var eip3009ABI = [
107
107
  outputs: [{ name: "", type: "string" }],
108
108
  stateMutability: "view",
109
109
  type: "function"
110
+ },
111
+ {
112
+ inputs: [],
113
+ name: "name",
114
+ outputs: [{ name: "", type: "string" }],
115
+ stateMutability: "view",
116
+ type: "function"
117
+ },
118
+ {
119
+ inputs: [
120
+ { name: "authorizer", type: "address" },
121
+ { name: "nonce", type: "bytes32" }
122
+ ],
123
+ name: "authorizationState",
124
+ outputs: [{ name: "", type: "bool" }],
125
+ stateMutability: "view",
126
+ type: "function"
110
127
  }
111
128
  ];
112
129
  var erc20ApproveAbi = [
@@ -133,6 +150,8 @@ var erc20AllowanceAbi = [
133
150
  stateMutability: "view"
134
151
  }
135
152
  ];
153
+ var ERC20_APPROVE_GAS_LIMIT = 70000n;
154
+ var DEFAULT_MAX_FEE_PER_GAS = 1000000000n;
136
155
  var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
137
156
  var x402ExactPermit2ProxyAddress = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001";
138
157
  var permit2WitnessABIComponents = [
@@ -266,29 +285,327 @@ function getEvmChainId(network) {
266
285
  throw new Error(`Unsupported network format: ${network} (expected eip155:CHAIN_ID)`);
267
286
  }
268
287
 
288
+ // src/exact/facilitator/errors.ts
289
+ var ErrInvalidScheme = "invalid_exact_evm_scheme";
290
+ var ErrNetworkMismatch = "invalid_exact_evm_network_mismatch";
291
+ var ErrMissingEip712Domain = "invalid_exact_evm_missing_eip712_domain";
292
+ var ErrRecipientMismatch = "invalid_exact_evm_recipient_mismatch";
293
+ var ErrInvalidSignature = "invalid_exact_evm_signature";
294
+ var ErrValidBeforeExpired = "invalid_exact_evm_payload_authorization_valid_before";
295
+ var ErrValidAfterInFuture = "invalid_exact_evm_payload_authorization_valid_after";
296
+ var ErrInvalidAuthorizationValue = "invalid_exact_evm_authorization_value";
297
+ var ErrUndeployedSmartWallet = "invalid_exact_evm_payload_undeployed_smart_wallet";
298
+ var ErrTransactionFailed = "invalid_exact_evm_transaction_failed";
299
+ var ErrEip3009TokenNameMismatch = "invalid_exact_evm_token_name_mismatch";
300
+ var ErrEip3009TokenVersionMismatch = "invalid_exact_evm_token_version_mismatch";
301
+ var ErrEip3009NotSupported = "invalid_exact_evm_eip3009_not_supported";
302
+ var ErrEip3009NonceAlreadyUsed = "invalid_exact_evm_nonce_already_used";
303
+ var ErrEip3009InsufficientBalance = "invalid_exact_evm_insufficient_balance";
304
+ var ErrEip3009SimulationFailed = "invalid_exact_evm_transaction_simulation_failed";
305
+ var ErrPermit2InvalidSpender = "invalid_permit2_spender";
306
+ var ErrPermit2RecipientMismatch = "invalid_permit2_recipient_mismatch";
307
+ var ErrPermit2DeadlineExpired = "permit2_deadline_expired";
308
+ var ErrPermit2NotYetValid = "permit2_not_yet_valid";
309
+ var ErrPermit2AmountMismatch = "permit2_amount_mismatch";
310
+ var ErrPermit2TokenMismatch = "permit2_token_mismatch";
311
+ var ErrPermit2InvalidSignature = "invalid_permit2_signature";
312
+ var ErrPermit2AllowanceRequired = "permit2_allowance_required";
313
+ var ErrPermit2SimulationFailed = "permit2_simulation_failed";
314
+ var ErrPermit2InsufficientBalance = "permit2_insufficient_balance";
315
+ var ErrPermit2ProxyNotDeployed = "permit2_proxy_not_deployed";
316
+ var ErrPermit2InvalidAmount = "permit2_invalid_amount";
317
+ var ErrPermit2InvalidDestination = "permit2_invalid_destination";
318
+ var ErrPermit2InvalidOwner = "permit2_invalid_owner";
319
+ var ErrPermit2PaymentTooEarly = "permit2_payment_too_early";
320
+ var ErrPermit2InvalidNonce = "permit2_invalid_nonce";
321
+ var ErrPermit2612AmountMismatch = "permit2_2612_amount_mismatch";
322
+ var ErrErc20ApprovalInsufficientEthForGas = "erc20_approval_insufficient_eth_for_gas";
323
+ var ErrErc20ApprovalInvalidFormat = "invalid_erc20_approval_extension_format";
324
+ var ErrErc20ApprovalFromMismatch = "erc20_approval_from_mismatch";
325
+ var ErrErc20ApprovalAssetMismatch = "erc20_approval_asset_mismatch";
326
+ var ErrErc20ApprovalSpenderNotPermit2 = "erc20_approval_spender_not_permit2";
327
+ var ErrErc20ApprovalTxWrongTarget = "erc20_approval_tx_wrong_target";
328
+ var ErrErc20ApprovalTxWrongSelector = "erc20_approval_tx_wrong_selector";
329
+ var ErrErc20ApprovalTxWrongSpender = "erc20_approval_tx_wrong_spender";
330
+ var ErrErc20ApprovalTxInvalidCalldata = "erc20_approval_tx_invalid_calldata";
331
+ var ErrErc20ApprovalTxSignerMismatch = "erc20_approval_tx_signer_mismatch";
332
+ var ErrErc20ApprovalTxInvalidSignature = "erc20_approval_tx_invalid_signature";
333
+ var ErrErc20ApprovalTxParseFailed = "erc20_approval_tx_parse_failed";
334
+ var ErrErc20ApprovalTxFailed = "erc20_approval_tx_failed";
335
+ var ErrInvalidEip2612ExtensionFormat = "invalid_eip2612_extension_format";
336
+ var ErrEip2612FromMismatch = "eip2612_from_mismatch";
337
+ var ErrEip2612AssetMismatch = "eip2612_asset_mismatch";
338
+ var ErrEip2612SpenderNotPermit2 = "eip2612_spender_not_permit2";
339
+ var ErrEip2612DeadlineExpired = "eip2612_deadline_expired";
340
+ var ErrUnsupportedPayloadType = "unsupported_payload_type";
341
+ var ErrInvalidTransactionState = "invalid_transaction_state";
342
+
343
+ // src/exact/facilitator/eip3009-utils.ts
344
+ var import_viem3 = require("viem");
345
+
346
+ // src/multicall.ts
347
+ var import_viem2 = require("viem");
348
+ var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
349
+ var multicall3GetEthBalanceAbi = [
350
+ {
351
+ name: "getEthBalance",
352
+ inputs: [{ name: "addr", type: "address" }],
353
+ outputs: [{ name: "balance", type: "uint256" }],
354
+ stateMutability: "view",
355
+ type: "function"
356
+ }
357
+ ];
358
+ var multicall3ABI = [
359
+ {
360
+ inputs: [
361
+ { name: "requireSuccess", type: "bool" },
362
+ {
363
+ name: "calls",
364
+ type: "tuple[]",
365
+ components: [
366
+ { name: "target", type: "address" },
367
+ { name: "callData", type: "bytes" }
368
+ ]
369
+ }
370
+ ],
371
+ name: "tryAggregate",
372
+ outputs: [
373
+ {
374
+ name: "returnData",
375
+ type: "tuple[]",
376
+ components: [
377
+ { name: "success", type: "bool" },
378
+ { name: "returnData", type: "bytes" }
379
+ ]
380
+ }
381
+ ],
382
+ stateMutability: "payable",
383
+ type: "function"
384
+ }
385
+ ];
386
+ async function multicall(readContract, calls) {
387
+ const aggregateCalls = calls.map((call) => {
388
+ if ("callData" in call) {
389
+ return { target: call.address, callData: call.callData };
390
+ }
391
+ const callData = (0, import_viem2.encodeFunctionData)({
392
+ abi: call.abi,
393
+ functionName: call.functionName,
394
+ args: call.args
395
+ });
396
+ return { target: call.address, callData };
397
+ });
398
+ const rawResults = await readContract({
399
+ address: MULTICALL3_ADDRESS,
400
+ abi: multicall3ABI,
401
+ functionName: "tryAggregate",
402
+ args: [false, aggregateCalls]
403
+ });
404
+ return rawResults.map((raw, i) => {
405
+ if (!raw.success) {
406
+ return {
407
+ status: "failure",
408
+ error: new Error(`multicall: call reverted (returnData: ${raw.returnData})`)
409
+ };
410
+ }
411
+ const call = calls[i];
412
+ if ("callData" in call) {
413
+ return { status: "success", result: void 0 };
414
+ }
415
+ try {
416
+ const decoded = (0, import_viem2.decodeFunctionResult)({
417
+ abi: call.abi,
418
+ functionName: call.functionName,
419
+ data: raw.returnData
420
+ });
421
+ return { status: "success", result: decoded };
422
+ } catch (err) {
423
+ return {
424
+ status: "failure",
425
+ error: err instanceof Error ? err : new Error(String(err))
426
+ };
427
+ }
428
+ });
429
+ }
430
+
431
+ // src/exact/facilitator/eip3009-utils.ts
432
+ async function simulateEip3009Transfer(signer, erc20Address, payload, eip6492Deployment) {
433
+ const auth = payload.authorization;
434
+ const transferArgs = [
435
+ (0, import_viem3.getAddress)(auth.from),
436
+ (0, import_viem3.getAddress)(auth.to),
437
+ BigInt(auth.value),
438
+ BigInt(auth.validAfter),
439
+ BigInt(auth.validBefore),
440
+ auth.nonce
441
+ ];
442
+ if (eip6492Deployment) {
443
+ const { signature: innerSignature } = (0, import_viem3.parseErc6492Signature)(payload.signature);
444
+ const transferCalldata = (0, import_viem3.encodeFunctionData)({
445
+ abi: eip3009ABI,
446
+ functionName: "transferWithAuthorization",
447
+ args: [...transferArgs, innerSignature]
448
+ });
449
+ try {
450
+ const results = await multicall(signer.readContract.bind(signer), [
451
+ {
452
+ address: (0, import_viem3.getAddress)(eip6492Deployment.factoryAddress),
453
+ callData: eip6492Deployment.factoryCalldata
454
+ },
455
+ {
456
+ address: erc20Address,
457
+ callData: transferCalldata
458
+ }
459
+ ]);
460
+ return results[1]?.status === "success";
461
+ } catch {
462
+ return false;
463
+ }
464
+ }
465
+ const sig = payload.signature;
466
+ const sigLength = sig.startsWith("0x") ? sig.length - 2 : sig.length;
467
+ const isECDSA = sigLength === 130;
468
+ try {
469
+ if (isECDSA) {
470
+ const parsedSig = (0, import_viem3.parseSignature)(sig);
471
+ await signer.readContract({
472
+ address: erc20Address,
473
+ abi: eip3009ABI,
474
+ functionName: "transferWithAuthorization",
475
+ args: [
476
+ ...transferArgs,
477
+ parsedSig.v ?? parsedSig.yParity,
478
+ parsedSig.r,
479
+ parsedSig.s
480
+ ]
481
+ });
482
+ } else {
483
+ await signer.readContract({
484
+ address: erc20Address,
485
+ abi: eip3009ABI,
486
+ functionName: "transferWithAuthorization",
487
+ args: [...transferArgs, sig]
488
+ });
489
+ }
490
+ return true;
491
+ } catch {
492
+ return false;
493
+ }
494
+ }
495
+ async function diagnoseEip3009SimulationFailure(signer, erc20Address, payload, requirements, amountRequired) {
496
+ const payer = payload.authorization.from;
497
+ const diagnosticCalls = [
498
+ {
499
+ address: erc20Address,
500
+ abi: eip3009ABI,
501
+ functionName: "balanceOf",
502
+ args: [payload.authorization.from]
503
+ },
504
+ {
505
+ address: erc20Address,
506
+ abi: eip3009ABI,
507
+ functionName: "name"
508
+ },
509
+ {
510
+ address: erc20Address,
511
+ abi: eip3009ABI,
512
+ functionName: "version"
513
+ },
514
+ {
515
+ address: erc20Address,
516
+ abi: eip3009ABI,
517
+ functionName: "authorizationState",
518
+ args: [payload.authorization.from, payload.authorization.nonce]
519
+ }
520
+ ];
521
+ try {
522
+ const results = await multicall(signer.readContract.bind(signer), diagnosticCalls);
523
+ const [balanceResult, nameResult, versionResult, authStateResult] = results;
524
+ if (authStateResult.status === "failure") {
525
+ return { isValid: false, invalidReason: ErrEip3009NotSupported, payer };
526
+ }
527
+ if (authStateResult.status === "success" && authStateResult.result === true) {
528
+ return { isValid: false, invalidReason: ErrEip3009NonceAlreadyUsed, payer };
529
+ }
530
+ if (nameResult.status === "success" && requirements.extra?.name && nameResult.result !== requirements.extra.name) {
531
+ return { isValid: false, invalidReason: ErrEip3009TokenNameMismatch, payer };
532
+ }
533
+ if (versionResult.status === "success" && requirements.extra?.version && versionResult.result !== requirements.extra.version) {
534
+ return { isValid: false, invalidReason: ErrEip3009TokenVersionMismatch, payer };
535
+ }
536
+ if (balanceResult.status === "success") {
537
+ const balance = balanceResult.result;
538
+ if (balance < BigInt(amountRequired)) {
539
+ return {
540
+ isValid: false,
541
+ invalidReason: ErrEip3009InsufficientBalance,
542
+ payer
543
+ };
544
+ }
545
+ }
546
+ } catch {
547
+ }
548
+ return { isValid: false, invalidReason: ErrEip3009SimulationFailed, payer };
549
+ }
550
+ async function executeTransferWithAuthorization(signer, erc20Address, payload) {
551
+ const { signature } = (0, import_viem3.parseErc6492Signature)(payload.signature);
552
+ const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
553
+ const isECDSA = signatureLength === 130;
554
+ const auth = payload.authorization;
555
+ const baseArgs = [
556
+ (0, import_viem3.getAddress)(auth.from),
557
+ (0, import_viem3.getAddress)(auth.to),
558
+ BigInt(auth.value),
559
+ BigInt(auth.validAfter),
560
+ BigInt(auth.validBefore),
561
+ auth.nonce
562
+ ];
563
+ if (isECDSA) {
564
+ const parsedSig = (0, import_viem3.parseSignature)(signature);
565
+ return signer.writeContract({
566
+ address: erc20Address,
567
+ abi: eip3009ABI,
568
+ functionName: "transferWithAuthorization",
569
+ args: [
570
+ ...baseArgs,
571
+ parsedSig.v || parsedSig.yParity,
572
+ parsedSig.r,
573
+ parsedSig.s
574
+ ]
575
+ });
576
+ }
577
+ return signer.writeContract({
578
+ address: erc20Address,
579
+ abi: eip3009ABI,
580
+ functionName: "transferWithAuthorization",
581
+ args: [...baseArgs, signature]
582
+ });
583
+ }
584
+
269
585
  // src/exact/facilitator/eip3009.ts
270
- async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
586
+ async function verifyEIP3009(signer, payload, requirements, eip3009Payload, options) {
271
587
  const payer = eip3009Payload.authorization.from;
588
+ let eip6492Deployment;
272
589
  if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
273
590
  return {
274
591
  isValid: false,
275
- invalidReason: "unsupported_scheme",
592
+ invalidReason: ErrInvalidScheme,
276
593
  payer
277
594
  };
278
595
  }
279
596
  if (!requirements.extra?.name || !requirements.extra?.version) {
280
597
  return {
281
598
  isValid: false,
282
- invalidReason: "missing_eip712_domain",
599
+ invalidReason: ErrMissingEip712Domain,
283
600
  payer
284
601
  };
285
602
  }
286
603
  const { name, version } = requirements.extra;
287
- const erc20Address = (0, import_viem2.getAddress)(requirements.asset);
604
+ const erc20Address = (0, import_viem4.getAddress)(requirements.asset);
288
605
  if (payload.accepted.network !== requirements.network) {
289
606
  return {
290
607
  isValid: false,
291
- invalidReason: "network_mismatch",
608
+ invalidReason: ErrNetworkMismatch,
292
609
  payer
293
610
  };
294
611
  }
@@ -310,55 +627,49 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
310
627
  nonce: eip3009Payload.authorization.nonce
311
628
  }
312
629
  };
630
+ let isValid = false;
313
631
  try {
314
- const recoveredAddress = await signer.verifyTypedData({
632
+ isValid = await signer.verifyTypedData({
315
633
  address: eip3009Payload.authorization.from,
316
634
  ...permitTypedData,
317
635
  signature: eip3009Payload.signature
318
636
  });
319
- if (!recoveredAddress) {
637
+ } catch {
638
+ isValid = false;
639
+ }
640
+ const signature = eip3009Payload.signature;
641
+ const sigLen = signature.startsWith("0x") ? signature.length - 2 : signature.length;
642
+ const erc6492Data = (0, import_viem4.parseErc6492Signature)(signature);
643
+ const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !(0, import_viem4.isAddressEqual)(erc6492Data.address, "0x0000000000000000000000000000000000000000");
644
+ if (hasDeploymentInfo) {
645
+ eip6492Deployment = {
646
+ factoryAddress: erc6492Data.address,
647
+ factoryCalldata: erc6492Data.data
648
+ };
649
+ }
650
+ if (!isValid) {
651
+ const isSmartWallet = sigLen > 130;
652
+ if (!isSmartWallet) {
320
653
  return {
321
654
  isValid: false,
322
- invalidReason: "invalid_exact_evm_payload_signature",
655
+ invalidReason: ErrInvalidSignature,
323
656
  payer
324
657
  };
325
658
  }
326
- } catch {
327
- const signature = eip3009Payload.signature;
328
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
329
- const isSmartWallet = signatureLength > 130;
330
- if (isSmartWallet) {
331
- const payerAddress = eip3009Payload.authorization.from;
332
- const bytecode = await signer.getCode({ address: payerAddress });
333
- if (!bytecode || bytecode === "0x") {
334
- const erc6492Data = (0, import_viem2.parseErc6492Signature)(signature);
335
- const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !(0, import_viem2.isAddressEqual)(erc6492Data.address, "0x0000000000000000000000000000000000000000");
336
- if (!hasDeploymentInfo) {
337
- return {
338
- isValid: false,
339
- invalidReason: "invalid_exact_evm_payload_undeployed_smart_wallet",
340
- payer: payerAddress
341
- };
342
- }
343
- } else {
344
- return {
345
- isValid: false,
346
- invalidReason: "invalid_exact_evm_payload_signature",
347
- payer
348
- };
349
- }
350
- } else {
659
+ const bytecode = await signer.getCode({ address: payer });
660
+ const isDeployed = bytecode && bytecode !== "0x";
661
+ if (!isDeployed && !hasDeploymentInfo) {
351
662
  return {
352
663
  isValid: false,
353
- invalidReason: "invalid_exact_evm_payload_signature",
664
+ invalidReason: ErrUndeployedSmartWallet,
354
665
  payer
355
666
  };
356
667
  }
357
668
  }
358
- if ((0, import_viem2.getAddress)(eip3009Payload.authorization.to) !== (0, import_viem2.getAddress)(requirements.payTo)) {
669
+ if ((0, import_viem4.getAddress)(eip3009Payload.authorization.to) !== (0, import_viem4.getAddress)(requirements.payTo)) {
359
670
  return {
360
671
  isValid: false,
361
- invalidReason: "invalid_exact_evm_payload_recipient_mismatch",
672
+ invalidReason: ErrRecipientMismatch,
362
673
  payer
363
674
  };
364
675
  }
@@ -366,41 +677,41 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
366
677
  if (BigInt(eip3009Payload.authorization.validBefore) < BigInt(now + 6)) {
367
678
  return {
368
679
  isValid: false,
369
- invalidReason: "invalid_exact_evm_payload_authorization_valid_before",
680
+ invalidReason: ErrValidBeforeExpired,
370
681
  payer
371
682
  };
372
683
  }
373
684
  if (BigInt(eip3009Payload.authorization.validAfter) > BigInt(now)) {
374
685
  return {
375
686
  isValid: false,
376
- invalidReason: "invalid_exact_evm_payload_authorization_valid_after",
687
+ invalidReason: ErrValidAfterInFuture,
377
688
  payer
378
689
  };
379
690
  }
380
- try {
381
- const balance = await signer.readContract({
382
- address: erc20Address,
383
- abi: eip3009ABI,
384
- functionName: "balanceOf",
385
- args: [eip3009Payload.authorization.from]
386
- });
387
- if (BigInt(balance) < BigInt(requirements.amount)) {
388
- return {
389
- isValid: false,
390
- invalidReason: "insufficient_funds",
391
- invalidMessage: `Insufficient funds to complete the payment. Required: ${requirements.amount} ${requirements.asset}, Available: ${balance.toString()} ${requirements.asset}. Please add funds to your wallet and try again.`,
392
- payer
393
- };
394
- }
395
- } catch {
396
- }
397
691
  if (BigInt(eip3009Payload.authorization.value) !== BigInt(requirements.amount)) {
398
692
  return {
399
693
  isValid: false,
400
- invalidReason: "invalid_exact_evm_payload_authorization_value_mismatch",
694
+ invalidReason: ErrInvalidAuthorizationValue,
401
695
  payer
402
696
  };
403
697
  }
698
+ if (options?.simulate !== false) {
699
+ const simulationSucceeded = await simulateEip3009Transfer(
700
+ signer,
701
+ erc20Address,
702
+ eip3009Payload,
703
+ eip6492Deployment
704
+ );
705
+ if (!simulationSucceeded) {
706
+ return diagnoseEip3009SimulationFailure(
707
+ signer,
708
+ erc20Address,
709
+ eip3009Payload,
710
+ requirements,
711
+ requirements.amount
712
+ );
713
+ }
714
+ }
404
715
  return {
405
716
  isValid: true,
406
717
  invalidReason: void 0,
@@ -409,20 +720,23 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
409
720
  }
410
721
  async function settleEIP3009(signer, payload, requirements, eip3009Payload, config) {
411
722
  const payer = eip3009Payload.authorization.from;
412
- const valid = await verifyEIP3009(signer, payload, requirements, eip3009Payload);
723
+ const valid = await verifyEIP3009(signer, payload, requirements, eip3009Payload, {
724
+ simulate: config.simulateInSettle ?? false
725
+ });
413
726
  if (!valid.isValid) {
414
727
  return {
415
728
  success: false,
416
729
  network: payload.accepted.network,
417
730
  transaction: "",
418
- errorReason: valid.invalidReason ?? "invalid_scheme",
731
+ errorReason: valid.invalidReason ?? ErrInvalidScheme,
419
732
  payer
420
733
  };
421
734
  }
422
735
  try {
423
- const parseResult = (0, import_viem2.parseErc6492Signature)(eip3009Payload.signature);
424
- const { signature, address: factoryAddress, data: factoryCalldata } = parseResult;
425
- if (config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !(0, import_viem2.isAddressEqual)(factoryAddress, "0x0000000000000000000000000000000000000000")) {
736
+ const { address: factoryAddress, data: factoryCalldata } = (0, import_viem4.parseErc6492Signature)(
737
+ eip3009Payload.signature
738
+ );
739
+ if (config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !(0, import_viem4.isAddressEqual)(factoryAddress, "0x0000000000000000000000000000000000000000")) {
426
740
  const bytecode = await signer.getCode({ address: payer });
427
741
  if (!bytecode || bytecode === "0x") {
428
742
  const deployTx = await signer.sendTransaction({
@@ -432,48 +746,16 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
432
746
  await signer.waitForTransactionReceipt({ hash: deployTx });
433
747
  }
434
748
  }
435
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
436
- const isECDSA = signatureLength === 130;
437
- let tx;
438
- if (isECDSA) {
439
- const parsedSig = (0, import_viem2.parseSignature)(signature);
440
- tx = await signer.writeContract({
441
- address: (0, import_viem2.getAddress)(requirements.asset),
442
- abi: eip3009ABI,
443
- functionName: "transferWithAuthorization",
444
- args: [
445
- (0, import_viem2.getAddress)(eip3009Payload.authorization.from),
446
- (0, import_viem2.getAddress)(eip3009Payload.authorization.to),
447
- BigInt(eip3009Payload.authorization.value),
448
- BigInt(eip3009Payload.authorization.validAfter),
449
- BigInt(eip3009Payload.authorization.validBefore),
450
- eip3009Payload.authorization.nonce,
451
- parsedSig.v || parsedSig.yParity,
452
- parsedSig.r,
453
- parsedSig.s
454
- ]
455
- });
456
- } else {
457
- tx = await signer.writeContract({
458
- address: (0, import_viem2.getAddress)(requirements.asset),
459
- abi: eip3009ABI,
460
- functionName: "transferWithAuthorization",
461
- args: [
462
- (0, import_viem2.getAddress)(eip3009Payload.authorization.from),
463
- (0, import_viem2.getAddress)(eip3009Payload.authorization.to),
464
- BigInt(eip3009Payload.authorization.value),
465
- BigInt(eip3009Payload.authorization.validAfter),
466
- BigInt(eip3009Payload.authorization.validBefore),
467
- eip3009Payload.authorization.nonce,
468
- signature
469
- ]
470
- });
471
- }
749
+ const tx = await executeTransferWithAuthorization(
750
+ signer,
751
+ (0, import_viem4.getAddress)(requirements.asset),
752
+ eip3009Payload
753
+ );
472
754
  const receipt = await signer.waitForTransactionReceipt({ hash: tx });
473
755
  if (receipt.status !== "success") {
474
756
  return {
475
757
  success: false,
476
- errorReason: "invalid_transaction_state",
758
+ errorReason: ErrTransactionFailed,
477
759
  transaction: tx,
478
760
  network: payload.accepted.network,
479
761
  payer
@@ -488,7 +770,7 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
488
770
  } catch {
489
771
  return {
490
772
  success: false,
491
- errorReason: "transaction_failed",
773
+ errorReason: ErrTransactionFailed,
492
774
  transaction: "",
493
775
  network: payload.accepted.network,
494
776
  payer
@@ -496,57 +778,80 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
496
778
  }
497
779
  }
498
780
 
499
- // src/exact/facilitator/permit2.ts
500
- var import_extensions2 = require("@x402/extensions");
501
- var import_viem4 = require("viem");
781
+ // src/exact/extensions.ts
782
+ var EIP2612_GAS_SPONSORING_KEY = "eip2612GasSponsoring";
783
+ var ERC20_APPROVAL_GAS_SPONSORING_KEY = "erc20ApprovalGasSponsoring";
784
+ function _extractInfo(payload, extensionKey) {
785
+ const extensions = payload.extensions;
786
+ if (!extensions) return null;
787
+ const extension = extensions[extensionKey];
788
+ if (!extension?.info) return null;
789
+ return extension.info;
790
+ }
791
+ function extractEip2612GasSponsoringInfo(payload) {
792
+ const info = _extractInfo(payload, EIP2612_GAS_SPONSORING_KEY);
793
+ if (!info) return null;
794
+ if (!info.from || !info.asset || !info.spender || !info.amount || !info.nonce || !info.deadline || !info.signature || !info.version) {
795
+ return null;
796
+ }
797
+ return info;
798
+ }
799
+ function validateEip2612GasSponsoringInfo(info) {
800
+ const addressPattern = /^0x[a-fA-F0-9]{40}$/;
801
+ const numericPattern = /^[0-9]+$/;
802
+ const hexPattern = /^0x[a-fA-F0-9]+$/;
803
+ const versionPattern = /^[0-9]+(\.[0-9]+)*$/;
804
+ return addressPattern.test(info.from) && addressPattern.test(info.asset) && addressPattern.test(info.spender) && numericPattern.test(info.amount) && numericPattern.test(info.nonce) && numericPattern.test(info.deadline) && hexPattern.test(info.signature) && versionPattern.test(info.version);
805
+ }
806
+ function extractErc20ApprovalGasSponsoringInfo(payload) {
807
+ const info = _extractInfo(payload, ERC20_APPROVAL_GAS_SPONSORING_KEY);
808
+ if (!info) return null;
809
+ if (!info.from || !info.asset || !info.spender || !info.amount || !info.signedTransaction || !info.version) {
810
+ return null;
811
+ }
812
+ return info;
813
+ }
814
+ function validateErc20ApprovalGasSponsoringInfo(info) {
815
+ const addressPattern = /^0x[a-fA-F0-9]{40}$/;
816
+ const numericPattern = /^[0-9]+$/;
817
+ const hexPattern = /^0x[a-fA-F0-9]+$/;
818
+ const versionPattern = /^[0-9]+(\.[0-9]+)*$/;
819
+ return addressPattern.test(info.from) && addressPattern.test(info.asset) && addressPattern.test(info.spender) && numericPattern.test(info.amount) && hexPattern.test(info.signedTransaction) && versionPattern.test(info.version);
820
+ }
821
+ function resolveErc20ApprovalExtensionSigner(extension, network) {
822
+ if (!extension) return void 0;
823
+ return extension.signerForNetwork?.(network) ?? extension.signer;
824
+ }
502
825
 
503
- // src/exact/facilitator/errors.ts
504
- var ErrPermit2InvalidSignature = "invalid_permit2_signature";
505
- var ErrPermit2InvalidAmount = "permit2_invalid_amount";
506
- var ErrPermit2InvalidDestination = "permit2_invalid_destination";
507
- var ErrPermit2InvalidOwner = "permit2_invalid_owner";
508
- var ErrPermit2PaymentTooEarly = "permit2_payment_too_early";
509
- var ErrPermit2InvalidNonce = "permit2_invalid_nonce";
510
- var ErrPermit2612AmountMismatch = "permit2_2612_amount_mismatch";
511
- var ErrErc20ApprovalInvalidFormat = "invalid_erc20_approval_extension_format";
512
- var ErrErc20ApprovalFromMismatch = "erc20_approval_from_mismatch";
513
- var ErrErc20ApprovalAssetMismatch = "erc20_approval_asset_mismatch";
514
- var ErrErc20ApprovalSpenderNotPermit2 = "erc20_approval_spender_not_permit2";
515
- var ErrErc20ApprovalTxWrongTarget = "erc20_approval_tx_wrong_target";
516
- var ErrErc20ApprovalTxWrongSelector = "erc20_approval_tx_wrong_selector";
517
- var ErrErc20ApprovalTxWrongSpender = "erc20_approval_tx_wrong_spender";
518
- var ErrErc20ApprovalTxInvalidCalldata = "erc20_approval_tx_invalid_calldata";
519
- var ErrErc20ApprovalTxSignerMismatch = "erc20_approval_tx_signer_mismatch";
520
- var ErrErc20ApprovalTxInvalidSignature = "erc20_approval_tx_invalid_signature";
521
- var ErrErc20ApprovalTxParseFailed = "erc20_approval_tx_parse_failed";
826
+ // src/exact/facilitator/permit2.ts
827
+ var import_viem7 = require("viem");
522
828
 
523
829
  // src/exact/facilitator/erc20approval.ts
524
- var import_viem3 = require("viem");
525
- var import_extensions = require("@x402/extensions");
830
+ var import_viem5 = require("viem");
526
831
  var APPROVE_SELECTOR = "0x095ea7b3";
527
832
  async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
528
- if (!(0, import_extensions.validateErc20ApprovalGasSponsoringInfo)(info)) {
833
+ if (!validateErc20ApprovalGasSponsoringInfo(info)) {
529
834
  return {
530
835
  isValid: false,
531
836
  invalidReason: ErrErc20ApprovalInvalidFormat,
532
837
  invalidMessage: "ERC-20 approval extension info failed schema validation"
533
838
  };
534
839
  }
535
- if ((0, import_viem3.getAddress)(info.from) !== (0, import_viem3.getAddress)(payer)) {
840
+ if ((0, import_viem5.getAddress)(info.from) !== (0, import_viem5.getAddress)(payer)) {
536
841
  return {
537
842
  isValid: false,
538
843
  invalidReason: ErrErc20ApprovalFromMismatch,
539
844
  invalidMessage: `Expected from=${payer}, got ${info.from}`
540
845
  };
541
846
  }
542
- if ((0, import_viem3.getAddress)(info.asset) !== tokenAddress) {
847
+ if ((0, import_viem5.getAddress)(info.asset) !== tokenAddress) {
543
848
  return {
544
849
  isValid: false,
545
850
  invalidReason: ErrErc20ApprovalAssetMismatch,
546
851
  invalidMessage: `Expected asset=${tokenAddress}, got ${info.asset}`
547
852
  };
548
853
  }
549
- if ((0, import_viem3.getAddress)(info.spender) !== (0, import_viem3.getAddress)(PERMIT2_ADDRESS)) {
854
+ if ((0, import_viem5.getAddress)(info.spender) !== (0, import_viem5.getAddress)(PERMIT2_ADDRESS)) {
550
855
  return {
551
856
  isValid: false,
552
857
  invalidReason: ErrErc20ApprovalSpenderNotPermit2,
@@ -555,8 +860,8 @@ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
555
860
  }
556
861
  try {
557
862
  const serializedTx = info.signedTransaction;
558
- const tx = (0, import_viem3.parseTransaction)(serializedTx);
559
- if (!tx.to || (0, import_viem3.getAddress)(tx.to) !== tokenAddress) {
863
+ const tx = (0, import_viem5.parseTransaction)(serializedTx);
864
+ if (!tx.to || (0, import_viem5.getAddress)(tx.to) !== tokenAddress) {
560
865
  return {
561
866
  isValid: false,
562
867
  invalidReason: ErrErc20ApprovalTxWrongTarget,
@@ -572,12 +877,12 @@ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
572
877
  };
573
878
  }
574
879
  try {
575
- const decoded = (0, import_viem3.decodeFunctionData)({
880
+ const decoded = (0, import_viem5.decodeFunctionData)({
576
881
  abi: erc20ApproveAbi,
577
882
  data
578
883
  });
579
- const calldataSpender = (0, import_viem3.getAddress)(decoded.args[0]);
580
- if (calldataSpender !== (0, import_viem3.getAddress)(PERMIT2_ADDRESS)) {
884
+ const calldataSpender = (0, import_viem5.getAddress)(decoded.args[0]);
885
+ if (calldataSpender !== (0, import_viem5.getAddress)(PERMIT2_ADDRESS)) {
581
886
  return {
582
887
  isValid: false,
583
888
  invalidReason: ErrErc20ApprovalTxWrongSpender,
@@ -592,10 +897,10 @@ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
592
897
  };
593
898
  }
594
899
  try {
595
- const recoveredAddress = await (0, import_viem3.recoverTransactionAddress)({
900
+ const recoveredAddress = await (0, import_viem5.recoverTransactionAddress)({
596
901
  serializedTransaction: serializedTx
597
902
  });
598
- if ((0, import_viem3.getAddress)(recoveredAddress) !== (0, import_viem3.getAddress)(payer)) {
903
+ if ((0, import_viem5.getAddress)(recoveredAddress) !== (0, import_viem5.getAddress)(payer)) {
599
904
  return {
600
905
  isValid: false,
601
906
  invalidReason: ErrErc20ApprovalTxSignerMismatch,
@@ -619,65 +924,314 @@ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
619
924
  return { isValid: true };
620
925
  }
621
926
 
622
- // src/exact/facilitator/permit2.ts
623
- async function verifyPermit2(signer, payload, requirements, permit2Payload, context) {
624
- const payer = permit2Payload.permit2Authorization.from;
625
- if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
626
- return {
627
- isValid: false,
628
- invalidReason: "unsupported_scheme",
629
- payer
630
- };
631
- }
632
- if (payload.accepted.network !== requirements.network) {
633
- return {
634
- isValid: false,
635
- invalidReason: "network_mismatch",
636
- payer
637
- };
638
- }
639
- const chainId = getEvmChainId(requirements.network);
640
- const tokenAddress = (0, import_viem4.getAddress)(requirements.asset);
641
- if ((0, import_viem4.getAddress)(permit2Payload.permit2Authorization.spender) !== (0, import_viem4.getAddress)(x402ExactPermit2ProxyAddress)) {
642
- return {
643
- isValid: false,
644
- invalidReason: "invalid_permit2_spender",
645
- payer
646
- };
927
+ // src/exact/facilitator/permit2-utils.ts
928
+ var import_viem6 = require("viem");
929
+ async function simulatePermit2Settle(signer, permit2Payload) {
930
+ try {
931
+ await signer.readContract({
932
+ address: x402ExactPermit2ProxyAddress,
933
+ abi: x402ExactPermit2ProxyABI,
934
+ functionName: "settle",
935
+ args: buildPermit2SettleArgs(permit2Payload)
936
+ });
937
+ return true;
938
+ } catch {
939
+ return false;
647
940
  }
648
- if ((0, import_viem4.getAddress)(permit2Payload.permit2Authorization.witness.to) !== (0, import_viem4.getAddress)(requirements.payTo)) {
649
- return {
650
- isValid: false,
651
- invalidReason: "invalid_permit2_recipient_mismatch",
652
- payer
653
- };
941
+ }
942
+ function splitEip2612Signature(signature) {
943
+ const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
944
+ if (sig.length !== 130) {
945
+ throw new Error(
946
+ `invalid EIP-2612 signature length: expected 65 bytes (130 hex chars), got ${sig.length / 2} bytes`
947
+ );
654
948
  }
655
- const now = Math.floor(Date.now() / 1e3);
656
- if (BigInt(permit2Payload.permit2Authorization.deadline) < BigInt(now + 6)) {
657
- return {
658
- isValid: false,
659
- invalidReason: "permit2_deadline_expired",
660
- payer
949
+ const r = `0x${sig.slice(0, 64)}`;
950
+ const s = `0x${sig.slice(64, 128)}`;
951
+ const v = parseInt(sig.slice(128, 130), 16);
952
+ return { v, r, s };
953
+ }
954
+ function buildPermit2SettleArgs(permit2Payload) {
955
+ return [
956
+ {
957
+ permitted: {
958
+ token: (0, import_viem6.getAddress)(permit2Payload.permit2Authorization.permitted.token),
959
+ amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
960
+ },
961
+ nonce: BigInt(permit2Payload.permit2Authorization.nonce),
962
+ deadline: BigInt(permit2Payload.permit2Authorization.deadline)
963
+ },
964
+ (0, import_viem6.getAddress)(permit2Payload.permit2Authorization.from),
965
+ {
966
+ to: (0, import_viem6.getAddress)(permit2Payload.permit2Authorization.witness.to),
967
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
968
+ },
969
+ permit2Payload.signature
970
+ ];
971
+ }
972
+ function encodePermit2SettleCalldata(permit2Payload) {
973
+ return (0, import_viem6.encodeFunctionData)({
974
+ abi: x402ExactPermit2ProxyABI,
975
+ functionName: "settle",
976
+ args: buildPermit2SettleArgs(permit2Payload)
977
+ });
978
+ }
979
+ async function simulatePermit2SettleWithPermit(signer, permit2Payload, eip2612Info) {
980
+ try {
981
+ const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
982
+ await signer.readContract({
983
+ address: x402ExactPermit2ProxyAddress,
984
+ abi: x402ExactPermit2ProxyABI,
985
+ functionName: "settleWithPermit",
986
+ args: [
987
+ {
988
+ value: BigInt(eip2612Info.amount),
989
+ deadline: BigInt(eip2612Info.deadline),
990
+ r,
991
+ s,
992
+ v
993
+ },
994
+ ...buildPermit2SettleArgs(permit2Payload)
995
+ ]
996
+ });
997
+ return true;
998
+ } catch {
999
+ return false;
1000
+ }
1001
+ }
1002
+ async function diagnosePermit2SimulationFailure(signer, tokenAddress, permit2Payload, amountRequired) {
1003
+ const payer = permit2Payload.permit2Authorization.from;
1004
+ const diagnosticCalls = [
1005
+ {
1006
+ address: x402ExactPermit2ProxyAddress,
1007
+ abi: x402ExactPermit2ProxyABI,
1008
+ functionName: "PERMIT2"
1009
+ },
1010
+ {
1011
+ address: tokenAddress,
1012
+ abi: eip3009ABI,
1013
+ functionName: "balanceOf",
1014
+ args: [payer]
1015
+ },
1016
+ {
1017
+ address: tokenAddress,
1018
+ abi: erc20AllowanceAbi,
1019
+ functionName: "allowance",
1020
+ args: [payer, PERMIT2_ADDRESS]
1021
+ }
1022
+ ];
1023
+ try {
1024
+ const results = await multicall(signer.readContract.bind(signer), diagnosticCalls);
1025
+ const [proxyResult, balanceResult, allowanceResult] = results;
1026
+ if (proxyResult.status === "failure") {
1027
+ return { isValid: false, invalidReason: ErrPermit2ProxyNotDeployed, payer };
1028
+ }
1029
+ if (balanceResult.status === "success") {
1030
+ const balance = balanceResult.result;
1031
+ if (balance < BigInt(amountRequired)) {
1032
+ return { isValid: false, invalidReason: ErrPermit2InsufficientBalance, payer };
1033
+ }
1034
+ }
1035
+ if (allowanceResult.status === "success") {
1036
+ const allowance = allowanceResult.result;
1037
+ if (allowance < BigInt(amountRequired)) {
1038
+ return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
1039
+ }
1040
+ }
1041
+ } catch {
1042
+ }
1043
+ return { isValid: false, invalidReason: ErrPermit2SimulationFailed, payer };
1044
+ }
1045
+ async function checkPermit2Prerequisites(signer, tokenAddress, payer, amountRequired) {
1046
+ const diagnosticCalls = [
1047
+ {
1048
+ address: x402ExactPermit2ProxyAddress,
1049
+ abi: x402ExactPermit2ProxyABI,
1050
+ functionName: "PERMIT2"
1051
+ },
1052
+ {
1053
+ address: tokenAddress,
1054
+ abi: eip3009ABI,
1055
+ functionName: "balanceOf",
1056
+ args: [payer]
1057
+ },
1058
+ {
1059
+ address: MULTICALL3_ADDRESS,
1060
+ abi: multicall3GetEthBalanceAbi,
1061
+ functionName: "getEthBalance",
1062
+ args: [payer]
1063
+ }
1064
+ ];
1065
+ try {
1066
+ const results = await multicall(signer.readContract.bind(signer), diagnosticCalls);
1067
+ const [proxyResult, balanceResult, ethBalanceResult] = results;
1068
+ if (proxyResult.status === "failure") {
1069
+ return { isValid: false, invalidReason: ErrPermit2ProxyNotDeployed, payer };
1070
+ }
1071
+ if (balanceResult.status === "success") {
1072
+ const balance = balanceResult.result;
1073
+ if (balance < BigInt(amountRequired)) {
1074
+ return { isValid: false, invalidReason: ErrPermit2InsufficientBalance, payer };
1075
+ }
1076
+ }
1077
+ if (ethBalanceResult.status === "success") {
1078
+ const minEthForApprovalGas = ERC20_APPROVE_GAS_LIMIT * DEFAULT_MAX_FEE_PER_GAS;
1079
+ const ethBalance = ethBalanceResult.result;
1080
+ if (ethBalance < minEthForApprovalGas) {
1081
+ return {
1082
+ isValid: false,
1083
+ invalidReason: ErrErc20ApprovalInsufficientEthForGas,
1084
+ payer
1085
+ };
1086
+ }
1087
+ }
1088
+ } catch {
1089
+ }
1090
+ return { isValid: true, invalidReason: void 0, payer };
1091
+ }
1092
+ async function simulatePermit2SettleWithErc20Approval(extensionSigner, permit2Payload, erc20Info) {
1093
+ if (!extensionSigner.simulateTransactions) {
1094
+ return false;
1095
+ }
1096
+ try {
1097
+ const settleData = encodePermit2SettleCalldata(permit2Payload);
1098
+ return await extensionSigner.simulateTransactions([
1099
+ erc20Info.signedTransaction,
1100
+ { to: x402ExactPermit2ProxyAddress, data: settleData, gas: BigInt(3e5) }
1101
+ ]);
1102
+ } catch {
1103
+ return false;
1104
+ }
1105
+ }
1106
+ async function waitAndReturn(signer, tx, payload, payer) {
1107
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
1108
+ if (receipt.status !== "success") {
1109
+ return {
1110
+ success: false,
1111
+ errorReason: ErrInvalidTransactionState,
1112
+ transaction: tx,
1113
+ network: payload.accepted.network,
1114
+ payer
1115
+ };
1116
+ }
1117
+ return {
1118
+ success: true,
1119
+ transaction: tx,
1120
+ network: payload.accepted.network,
1121
+ payer
1122
+ };
1123
+ }
1124
+ function mapSettleError(error, payload, payer) {
1125
+ let errorReason = ErrTransactionFailed;
1126
+ if (error instanceof Error) {
1127
+ const message = error.message;
1128
+ if (message.includes("Permit2612AmountMismatch")) {
1129
+ errorReason = ErrPermit2612AmountMismatch;
1130
+ } else if (message.includes("InvalidAmount")) {
1131
+ errorReason = ErrPermit2InvalidAmount;
1132
+ } else if (message.includes("InvalidDestination")) {
1133
+ errorReason = ErrPermit2InvalidDestination;
1134
+ } else if (message.includes("InvalidOwner")) {
1135
+ errorReason = ErrPermit2InvalidOwner;
1136
+ } else if (message.includes("PaymentTooEarly")) {
1137
+ errorReason = ErrPermit2PaymentTooEarly;
1138
+ } else if (message.includes("InvalidSignature") || message.includes("SignatureExpired")) {
1139
+ errorReason = ErrPermit2InvalidSignature;
1140
+ } else if (message.includes("InvalidNonce")) {
1141
+ errorReason = ErrPermit2InvalidNonce;
1142
+ } else if (message.includes("erc20_approval_tx_failed")) {
1143
+ errorReason = ErrErc20ApprovalTxFailed;
1144
+ } else {
1145
+ errorReason = `${ErrTransactionFailed}: ${message.slice(0, 500)}`;
1146
+ }
1147
+ }
1148
+ return {
1149
+ success: false,
1150
+ errorReason,
1151
+ transaction: "",
1152
+ network: payload.accepted.network,
1153
+ payer
1154
+ };
1155
+ }
1156
+ function validateEip2612PermitForPayment(info, payer, tokenAddress) {
1157
+ if (!validateEip2612GasSponsoringInfo(info)) {
1158
+ return { isValid: false, invalidReason: ErrInvalidEip2612ExtensionFormat };
1159
+ }
1160
+ if ((0, import_viem6.getAddress)(info.from) !== (0, import_viem6.getAddress)(payer)) {
1161
+ return { isValid: false, invalidReason: ErrEip2612FromMismatch };
1162
+ }
1163
+ if ((0, import_viem6.getAddress)(info.asset) !== tokenAddress) {
1164
+ return { isValid: false, invalidReason: ErrEip2612AssetMismatch };
1165
+ }
1166
+ if ((0, import_viem6.getAddress)(info.spender) !== (0, import_viem6.getAddress)(PERMIT2_ADDRESS)) {
1167
+ return { isValid: false, invalidReason: ErrEip2612SpenderNotPermit2 };
1168
+ }
1169
+ const now = Math.floor(Date.now() / 1e3);
1170
+ if (BigInt(info.deadline) < BigInt(now + 6)) {
1171
+ return { isValid: false, invalidReason: ErrEip2612DeadlineExpired };
1172
+ }
1173
+ return { isValid: true };
1174
+ }
1175
+
1176
+ // src/exact/facilitator/permit2.ts
1177
+ async function verifyPermit2(signer, payload, requirements, permit2Payload, context, options) {
1178
+ const payer = permit2Payload.permit2Authorization.from;
1179
+ if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
1180
+ return {
1181
+ isValid: false,
1182
+ invalidReason: ErrUnsupportedPayloadType,
1183
+ payer
1184
+ };
1185
+ }
1186
+ if (payload.accepted.network !== requirements.network) {
1187
+ return {
1188
+ isValid: false,
1189
+ invalidReason: ErrNetworkMismatch,
1190
+ payer
1191
+ };
1192
+ }
1193
+ const chainId = getEvmChainId(requirements.network);
1194
+ const tokenAddress = (0, import_viem7.getAddress)(requirements.asset);
1195
+ if ((0, import_viem7.getAddress)(permit2Payload.permit2Authorization.spender) !== (0, import_viem7.getAddress)(x402ExactPermit2ProxyAddress)) {
1196
+ return {
1197
+ isValid: false,
1198
+ invalidReason: ErrPermit2InvalidSpender,
1199
+ payer
1200
+ };
1201
+ }
1202
+ if ((0, import_viem7.getAddress)(permit2Payload.permit2Authorization.witness.to) !== (0, import_viem7.getAddress)(requirements.payTo)) {
1203
+ return {
1204
+ isValid: false,
1205
+ invalidReason: ErrPermit2RecipientMismatch,
1206
+ payer
1207
+ };
1208
+ }
1209
+ const now = Math.floor(Date.now() / 1e3);
1210
+ if (BigInt(permit2Payload.permit2Authorization.deadline) < BigInt(now + 6)) {
1211
+ return {
1212
+ isValid: false,
1213
+ invalidReason: ErrPermit2DeadlineExpired,
1214
+ payer
661
1215
  };
662
1216
  }
663
1217
  if (BigInt(permit2Payload.permit2Authorization.witness.validAfter) > BigInt(now)) {
664
1218
  return {
665
1219
  isValid: false,
666
- invalidReason: "permit2_not_yet_valid",
1220
+ invalidReason: ErrPermit2NotYetValid,
667
1221
  payer
668
1222
  };
669
1223
  }
670
1224
  if (BigInt(permit2Payload.permit2Authorization.permitted.amount) !== BigInt(requirements.amount)) {
671
1225
  return {
672
1226
  isValid: false,
673
- invalidReason: "permit2_amount_mismatch",
1227
+ invalidReason: ErrPermit2AmountMismatch,
674
1228
  payer
675
1229
  };
676
1230
  }
677
- if ((0, import_viem4.getAddress)(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
1231
+ if ((0, import_viem7.getAddress)(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
678
1232
  return {
679
1233
  isValid: false,
680
- invalidReason: "permit2_token_mismatch",
1234
+ invalidReason: ErrPermit2TokenMismatch,
681
1235
  payer
682
1236
  };
683
1237
  }
@@ -691,144 +1245,132 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload, cont
691
1245
  },
692
1246
  message: {
693
1247
  permitted: {
694
- token: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.permitted.token),
1248
+ token: (0, import_viem7.getAddress)(permit2Payload.permit2Authorization.permitted.token),
695
1249
  amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
696
1250
  },
697
- spender: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.spender),
1251
+ spender: (0, import_viem7.getAddress)(permit2Payload.permit2Authorization.spender),
698
1252
  nonce: BigInt(permit2Payload.permit2Authorization.nonce),
699
1253
  deadline: BigInt(permit2Payload.permit2Authorization.deadline),
700
1254
  witness: {
701
- to: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.witness.to),
1255
+ to: (0, import_viem7.getAddress)(permit2Payload.permit2Authorization.witness.to),
702
1256
  validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
703
1257
  }
704
1258
  }
705
1259
  };
1260
+ let signatureValid = false;
706
1261
  try {
707
- const isValid = await signer.verifyTypedData({
1262
+ signatureValid = await signer.verifyTypedData({
708
1263
  address: payer,
709
1264
  ...permit2TypedData,
710
1265
  signature: permit2Payload.signature
711
1266
  });
712
- if (!isValid) {
713
- return {
714
- isValid: false,
715
- invalidReason: "invalid_permit2_signature",
716
- payer
717
- };
718
- }
719
1267
  } catch {
720
- return {
721
- isValid: false,
722
- invalidReason: "invalid_permit2_signature",
723
- payer
724
- };
1268
+ signatureValid = false;
725
1269
  }
726
- const allowanceResult = await _verifyPermit2Allowance(
727
- signer,
728
- payload,
729
- requirements,
730
- payer,
731
- tokenAddress,
732
- context
733
- );
734
- if (allowanceResult) {
735
- return allowanceResult;
736
- }
737
- try {
738
- const balance = await signer.readContract({
739
- address: tokenAddress,
740
- abi: eip3009ABI,
741
- functionName: "balanceOf",
742
- args: [payer]
743
- });
744
- if (balance < BigInt(requirements.amount)) {
1270
+ if (!signatureValid) {
1271
+ const bytecode = await signer.getCode({ address: payer });
1272
+ const isDeployedContract = bytecode && bytecode !== "0x";
1273
+ if (!isDeployedContract) {
745
1274
  return {
746
1275
  isValid: false,
747
- invalidReason: "insufficient_funds",
748
- invalidMessage: `Insufficient funds to complete the payment. Required: ${requirements.amount} ${requirements.asset}, Available: ${balance.toString()} ${requirements.asset}. Please add funds to your wallet and try again.`,
1276
+ invalidReason: ErrPermit2InvalidSignature,
749
1277
  payer
750
1278
  };
751
1279
  }
752
- } catch {
753
1280
  }
754
- return {
755
- isValid: true,
756
- invalidReason: void 0,
757
- payer
758
- };
759
- }
760
- async function _verifyPermit2Allowance(signer, payload, requirements, payer, tokenAddress, context) {
761
- try {
762
- const allowance = await signer.readContract({
763
- address: tokenAddress,
764
- abi: erc20AllowanceAbi,
765
- functionName: "allowance",
766
- args: [payer, PERMIT2_ADDRESS]
767
- });
768
- if (allowance >= BigInt(requirements.amount)) {
769
- return null;
1281
+ if (options?.simulate === false) {
1282
+ return { isValid: true, invalidReason: void 0, payer };
1283
+ }
1284
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
1285
+ if (eip2612Info) {
1286
+ const fieldResult = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
1287
+ if (!fieldResult.isValid) {
1288
+ return { isValid: false, invalidReason: fieldResult.invalidReason, payer };
770
1289
  }
771
- const eip2612Info = (0, import_extensions2.extractEip2612GasSponsoringInfo)(payload);
772
- if (eip2612Info) {
773
- const result = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
774
- if (!result.isValid) {
775
- return { isValid: false, invalidReason: result.invalidReason, payer };
776
- }
777
- return null;
1290
+ const simOk2 = await simulatePermit2SettleWithPermit(signer, permit2Payload, eip2612Info);
1291
+ if (!simOk2) {
1292
+ return diagnosePermit2SimulationFailure(
1293
+ signer,
1294
+ tokenAddress,
1295
+ permit2Payload,
1296
+ requirements.amount
1297
+ );
778
1298
  }
779
- const erc20GasSponsorshipExtension = context?.getExtension(
780
- import_extensions2.ERC20_APPROVAL_GAS_SPONSORING.key
781
- );
782
- if (erc20GasSponsorshipExtension) {
783
- const erc20Info = (0, import_extensions2.extractErc20ApprovalGasSponsoringInfo)(payload);
784
- if (erc20Info) {
785
- const result = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
786
- if (!result.isValid) {
787
- return { isValid: false, invalidReason: result.invalidReason, payer };
788
- }
789
- return null;
1299
+ return { isValid: true, invalidReason: void 0, payer };
1300
+ }
1301
+ const erc20GasSponsorshipExtension = context?.getExtension(
1302
+ ERC20_APPROVAL_GAS_SPONSORING_KEY
1303
+ );
1304
+ if (erc20GasSponsorshipExtension) {
1305
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
1306
+ if (erc20Info) {
1307
+ const fieldResult = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
1308
+ if (!fieldResult.isValid) {
1309
+ return { isValid: false, invalidReason: fieldResult.invalidReason, payer };
790
1310
  }
791
- }
792
- return { isValid: false, invalidReason: "permit2_allowance_required", payer };
793
- } catch {
794
- const eip2612Info = (0, import_extensions2.extractEip2612GasSponsoringInfo)(payload);
795
- if (eip2612Info) {
796
- const result = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
797
- if (!result.isValid) {
798
- return { isValid: false, invalidReason: result.invalidReason, payer };
1311
+ const extensionSigner = resolveErc20ApprovalExtensionSigner(
1312
+ erc20GasSponsorshipExtension,
1313
+ requirements.network
1314
+ );
1315
+ if (extensionSigner?.simulateTransactions) {
1316
+ const simOk2 = await simulatePermit2SettleWithErc20Approval(
1317
+ extensionSigner,
1318
+ permit2Payload,
1319
+ erc20Info
1320
+ );
1321
+ if (!simOk2) {
1322
+ return diagnosePermit2SimulationFailure(
1323
+ signer,
1324
+ tokenAddress,
1325
+ permit2Payload,
1326
+ requirements.amount
1327
+ );
1328
+ }
1329
+ return { isValid: true, invalidReason: void 0, payer };
799
1330
  }
1331
+ return checkPermit2Prerequisites(signer, tokenAddress, payer, requirements.amount);
800
1332
  }
801
- return null;
802
1333
  }
1334
+ const simOk = await simulatePermit2Settle(signer, permit2Payload);
1335
+ if (!simOk) {
1336
+ return diagnosePermit2SimulationFailure(
1337
+ signer,
1338
+ tokenAddress,
1339
+ permit2Payload,
1340
+ requirements.amount
1341
+ );
1342
+ }
1343
+ return { isValid: true, invalidReason: void 0, payer };
803
1344
  }
804
- async function settlePermit2(signer, payload, requirements, permit2Payload, context) {
1345
+ async function settlePermit2(signer, payload, requirements, permit2Payload, context, config) {
805
1346
  const payer = permit2Payload.permit2Authorization.from;
806
- const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
1347
+ const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context, {
1348
+ simulate: config?.simulateInSettle ?? false
1349
+ });
807
1350
  if (!valid.isValid) {
808
1351
  return {
809
1352
  success: false,
810
1353
  network: payload.accepted.network,
811
1354
  transaction: "",
812
- errorReason: valid.invalidReason ?? "invalid_scheme",
1355
+ errorReason: valid.invalidReason ?? ErrInvalidScheme,
813
1356
  payer
814
1357
  };
815
1358
  }
816
- const eip2612Info = (0, import_extensions2.extractEip2612GasSponsoringInfo)(payload);
1359
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
817
1360
  if (eip2612Info) {
818
1361
  return _settlePermit2WithEIP2612(signer, payload, permit2Payload, eip2612Info);
819
1362
  }
820
- const erc20Info = (0, import_extensions2.extractErc20ApprovalGasSponsoringInfo)(payload);
1363
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
821
1364
  if (erc20Info) {
822
1365
  const erc20GasSponsorshipExtension = context?.getExtension(
823
- import_extensions2.ERC20_APPROVAL_GAS_SPONSORING.key
1366
+ ERC20_APPROVAL_GAS_SPONSORING_KEY
824
1367
  );
825
- if (erc20GasSponsorshipExtension?.signer) {
826
- return _settlePermit2WithERC20Approval(
827
- erc20GasSponsorshipExtension.signer,
828
- payload,
829
- permit2Payload,
830
- erc20Info
831
- );
1368
+ const extensionSigner = resolveErc20ApprovalExtensionSigner(
1369
+ erc20GasSponsorshipExtension,
1370
+ payload.accepted.network
1371
+ );
1372
+ if (extensionSigner) {
1373
+ return _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info);
832
1374
  }
833
1375
  }
834
1376
  return _settlePermit2Direct(signer, payload, permit2Payload);
@@ -849,69 +1391,26 @@ async function _settlePermit2WithEIP2612(signer, payload, permit2Payload, eip261
849
1391
  s,
850
1392
  v
851
1393
  },
852
- {
853
- permitted: {
854
- token: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.permitted.token),
855
- amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
856
- },
857
- nonce: BigInt(permit2Payload.permit2Authorization.nonce),
858
- deadline: BigInt(permit2Payload.permit2Authorization.deadline)
859
- },
860
- (0, import_viem4.getAddress)(payer),
861
- {
862
- to: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.witness.to),
863
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
864
- },
865
- permit2Payload.signature
1394
+ ...buildPermit2SettleArgs(permit2Payload)
866
1395
  ]
867
1396
  });
868
- return _waitAndReturn(signer, tx, payload, payer);
1397
+ return waitAndReturn(signer, tx, payload, payer);
869
1398
  } catch (error) {
870
- return _mapSettleError(error, payload, payer);
1399
+ return mapSettleError(error, payload, payer);
871
1400
  }
872
1401
  }
873
1402
  async function _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info) {
874
1403
  const payer = permit2Payload.permit2Authorization.from;
875
1404
  try {
876
- const approvalTxHash = await extensionSigner.sendRawTransaction({
877
- serializedTransaction: erc20Info.signedTransaction
878
- });
879
- const approvalReceipt = await extensionSigner.waitForTransactionReceipt({
880
- hash: approvalTxHash
881
- });
882
- if (approvalReceipt.status !== "success") {
883
- return {
884
- success: false,
885
- errorReason: "erc20_approval_tx_failed",
886
- transaction: approvalTxHash,
887
- network: payload.accepted.network,
888
- payer
889
- };
890
- }
891
- const tx = await extensionSigner.writeContract({
892
- address: x402ExactPermit2ProxyAddress,
893
- abi: x402ExactPermit2ProxyABI,
894
- functionName: "settle",
895
- args: [
896
- {
897
- permitted: {
898
- token: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.permitted.token),
899
- amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
900
- },
901
- nonce: BigInt(permit2Payload.permit2Authorization.nonce),
902
- deadline: BigInt(permit2Payload.permit2Authorization.deadline)
903
- },
904
- (0, import_viem4.getAddress)(payer),
905
- {
906
- to: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.witness.to),
907
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
908
- },
909
- permit2Payload.signature
910
- ]
911
- });
912
- return _waitAndReturn(extensionSigner, tx, payload, payer);
1405
+ const settleData = encodePermit2SettleCalldata(permit2Payload);
1406
+ const txHashes = await extensionSigner.sendTransactions([
1407
+ erc20Info.signedTransaction,
1408
+ { to: x402ExactPermit2ProxyAddress, data: settleData, gas: BigInt(3e5) }
1409
+ ]);
1410
+ const settleTxHash = txHashes[txHashes.length - 1];
1411
+ return waitAndReturn(extensionSigner, settleTxHash, payload, payer);
913
1412
  } catch (error) {
914
- return _mapSettleError(error, payload, payer);
1413
+ return mapSettleError(error, payload, payer);
915
1414
  }
916
1415
  }
917
1416
  async function _settlePermit2Direct(signer, payload, permit2Payload) {
@@ -921,106 +1420,12 @@ async function _settlePermit2Direct(signer, payload, permit2Payload) {
921
1420
  address: x402ExactPermit2ProxyAddress,
922
1421
  abi: x402ExactPermit2ProxyABI,
923
1422
  functionName: "settle",
924
- args: [
925
- {
926
- permitted: {
927
- token: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.permitted.token),
928
- amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
929
- },
930
- nonce: BigInt(permit2Payload.permit2Authorization.nonce),
931
- deadline: BigInt(permit2Payload.permit2Authorization.deadline)
932
- },
933
- (0, import_viem4.getAddress)(payer),
934
- {
935
- to: (0, import_viem4.getAddress)(permit2Payload.permit2Authorization.witness.to),
936
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
937
- },
938
- permit2Payload.signature
939
- ]
1423
+ args: buildPermit2SettleArgs(permit2Payload)
940
1424
  });
941
- return _waitAndReturn(signer, tx, payload, payer);
1425
+ return waitAndReturn(signer, tx, payload, payer);
942
1426
  } catch (error) {
943
- return _mapSettleError(error, payload, payer);
944
- }
945
- }
946
- async function _waitAndReturn(signer, tx, payload, payer) {
947
- const receipt = await signer.waitForTransactionReceipt({ hash: tx });
948
- if (receipt.status !== "success") {
949
- return {
950
- success: false,
951
- errorReason: "invalid_transaction_state",
952
- transaction: tx,
953
- network: payload.accepted.network,
954
- payer
955
- };
956
- }
957
- return {
958
- success: true,
959
- transaction: tx,
960
- network: payload.accepted.network,
961
- payer
962
- };
963
- }
964
- function _mapSettleError(error, payload, payer) {
965
- let errorReason = "transaction_failed";
966
- if (error instanceof Error) {
967
- const message = error.message;
968
- if (message.includes("Permit2612AmountMismatch")) {
969
- errorReason = ErrPermit2612AmountMismatch;
970
- } else if (message.includes("InvalidAmount")) {
971
- errorReason = ErrPermit2InvalidAmount;
972
- } else if (message.includes("InvalidDestination")) {
973
- errorReason = ErrPermit2InvalidDestination;
974
- } else if (message.includes("InvalidOwner")) {
975
- errorReason = ErrPermit2InvalidOwner;
976
- } else if (message.includes("PaymentTooEarly")) {
977
- errorReason = ErrPermit2PaymentTooEarly;
978
- } else if (message.includes("InvalidSignature") || message.includes("SignatureExpired")) {
979
- errorReason = ErrPermit2InvalidSignature;
980
- } else if (message.includes("InvalidNonce")) {
981
- errorReason = ErrPermit2InvalidNonce;
982
- } else {
983
- errorReason = `transaction_failed: ${message.slice(0, 500)}`;
984
- }
985
- }
986
- return {
987
- success: false,
988
- errorReason,
989
- transaction: "",
990
- network: payload.accepted.network,
991
- payer
992
- };
993
- }
994
- function validateEip2612PermitForPayment(info, payer, tokenAddress) {
995
- if (!(0, import_extensions2.validateEip2612GasSponsoringInfo)(info)) {
996
- return { isValid: false, invalidReason: "invalid_eip2612_extension_format" };
1427
+ return mapSettleError(error, payload, payer);
997
1428
  }
998
- if ((0, import_viem4.getAddress)(info.from) !== (0, import_viem4.getAddress)(payer)) {
999
- return { isValid: false, invalidReason: "eip2612_from_mismatch" };
1000
- }
1001
- if ((0, import_viem4.getAddress)(info.asset) !== tokenAddress) {
1002
- return { isValid: false, invalidReason: "eip2612_asset_mismatch" };
1003
- }
1004
- if ((0, import_viem4.getAddress)(info.spender) !== (0, import_viem4.getAddress)(PERMIT2_ADDRESS)) {
1005
- return { isValid: false, invalidReason: "eip2612_spender_not_permit2" };
1006
- }
1007
- const now = Math.floor(Date.now() / 1e3);
1008
- if (BigInt(info.deadline) < BigInt(now + 6)) {
1009
- return { isValid: false, invalidReason: "eip2612_deadline_expired" };
1010
- }
1011
- return { isValid: true };
1012
- }
1013
- function splitEip2612Signature(signature) {
1014
- const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
1015
- if (sig.length !== 130) {
1016
- throw new Error(
1017
- `invalid EIP-2612 signature length: expected 65 bytes (130 hex chars), got ${sig.length / 2} bytes`
1018
- );
1019
- }
1020
- const r = `0x${sig.slice(0, 64)}`;
1021
- const s = `0x${sig.slice(64, 128)}`;
1022
- const v = parseInt(sig.slice(128, 130), 16);
1023
- return { v, r, s };
1024
1429
  }
1025
1430
 
1026
1431
  // src/exact/facilitator/scheme.ts
@@ -1036,7 +1441,8 @@ var ExactEvmScheme = class {
1036
1441
  this.scheme = "exact";
1037
1442
  this.caipFamily = "eip155:*";
1038
1443
  this.config = {
1039
- deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false
1444
+ deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false,
1445
+ simulateInSettle: config?.simulateInSettle ?? false
1040
1446
  };
1041
1447
  }
1042
1448
  /**
@@ -1067,7 +1473,8 @@ var ExactEvmScheme = class {
1067
1473
  */
1068
1474
  async verify(payload, requirements, context) {
1069
1475
  const rawPayload = payload.payload;
1070
- if (isPermit2Payload(rawPayload)) {
1476
+ const isPermit2 = isPermit2Payload(rawPayload);
1477
+ if (isPermit2) {
1071
1478
  return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
1072
1479
  }
1073
1480
  const eip3009Payload = rawPayload;
@@ -1083,8 +1490,11 @@ var ExactEvmScheme = class {
1083
1490
  */
1084
1491
  async settle(payload, requirements, context) {
1085
1492
  const rawPayload = payload.payload;
1086
- if (isPermit2Payload(rawPayload)) {
1087
- return settlePermit2(this.signer, payload, requirements, rawPayload, context);
1493
+ const isPermit2 = isPermit2Payload(rawPayload);
1494
+ if (isPermit2) {
1495
+ return settlePermit2(this.signer, payload, requirements, rawPayload, context, {
1496
+ simulateInSettle: this.config.simulateInSettle
1497
+ });
1088
1498
  }
1089
1499
  const eip3009Payload = rawPayload;
1090
1500
  return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);
@@ -1092,10 +1502,10 @@ var ExactEvmScheme = class {
1092
1502
  };
1093
1503
 
1094
1504
  // src/exact/v1/facilitator/scheme.ts
1095
- var import_viem6 = require("viem");
1505
+ var import_viem9 = require("viem");
1096
1506
 
1097
1507
  // src/exact/v1/client/scheme.ts
1098
- var import_viem5 = require("viem");
1508
+ var import_viem8 = require("viem");
1099
1509
 
1100
1510
  // src/v1/index.ts
1101
1511
  var EVM_NETWORK_CHAIN_ID_MAP = {
@@ -1141,7 +1551,8 @@ var ExactEvmSchemeV12 = class {
1141
1551
  this.scheme = "exact";
1142
1552
  this.caipFamily = "eip155:*";
1143
1553
  this.config = {
1144
- deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false
1554
+ deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false,
1555
+ simulateInSettle: config?.simulateInSettle ?? false
1145
1556
  };
1146
1557
  }
1147
1558
  /**
@@ -1172,14 +1583,95 @@ var ExactEvmSchemeV12 = class {
1172
1583
  * @returns Promise resolving to verification response
1173
1584
  */
1174
1585
  async verify(payload, requirements) {
1586
+ return this._verify(payload, requirements);
1587
+ }
1588
+ /**
1589
+ * Settles a payment by executing the transfer (V1).
1590
+ *
1591
+ * @param payload - The payment payload to settle
1592
+ * @param requirements - The payment requirements
1593
+ * @returns Promise resolving to settlement response
1594
+ */
1595
+ async settle(payload, requirements) {
1596
+ const payloadV1 = payload;
1597
+ const exactEvmPayload = payload.payload;
1598
+ const valid = await this._verify(payload, requirements, {
1599
+ simulate: this.config.simulateInSettle ?? false
1600
+ });
1601
+ if (!valid.isValid) {
1602
+ return {
1603
+ success: false,
1604
+ network: payloadV1.network,
1605
+ transaction: "",
1606
+ errorReason: valid.invalidReason ?? ErrInvalidScheme,
1607
+ payer: exactEvmPayload.authorization.from
1608
+ };
1609
+ }
1610
+ try {
1611
+ const { address: factoryAddress, data: factoryCalldata } = (0, import_viem9.parseErc6492Signature)(
1612
+ exactEvmPayload.signature
1613
+ );
1614
+ if (this.config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !(0, import_viem9.isAddressEqual)(factoryAddress, "0x0000000000000000000000000000000000000000")) {
1615
+ const payerAddress = exactEvmPayload.authorization.from;
1616
+ const bytecode = await this.signer.getCode({ address: payerAddress });
1617
+ if (!bytecode || bytecode === "0x") {
1618
+ const deployTx = await this.signer.sendTransaction({
1619
+ to: factoryAddress,
1620
+ data: factoryCalldata
1621
+ });
1622
+ await this.signer.waitForTransactionReceipt({ hash: deployTx });
1623
+ }
1624
+ }
1625
+ const tx = await executeTransferWithAuthorization(
1626
+ this.signer,
1627
+ (0, import_viem9.getAddress)(requirements.asset),
1628
+ exactEvmPayload
1629
+ );
1630
+ const receipt = await this.signer.waitForTransactionReceipt({ hash: tx });
1631
+ if (receipt.status !== "success") {
1632
+ return {
1633
+ success: false,
1634
+ errorReason: ErrTransactionFailed,
1635
+ transaction: tx,
1636
+ network: payloadV1.network,
1637
+ payer: exactEvmPayload.authorization.from
1638
+ };
1639
+ }
1640
+ return {
1641
+ success: true,
1642
+ transaction: tx,
1643
+ network: payloadV1.network,
1644
+ payer: exactEvmPayload.authorization.from
1645
+ };
1646
+ } catch (error) {
1647
+ return {
1648
+ success: false,
1649
+ errorReason: error instanceof Error ? error.message : ErrTransactionFailed,
1650
+ transaction: "",
1651
+ network: payloadV1.network,
1652
+ payer: exactEvmPayload.authorization.from
1653
+ };
1654
+ }
1655
+ }
1656
+ /**
1657
+ * Internal verify with optional simulation control.
1658
+ *
1659
+ * @param payload - The payment payload to verify
1660
+ * @param requirements - The payment requirements
1661
+ * @param options - Verification options (e.g. simulate)
1662
+ * @returns Promise resolving to verification response
1663
+ */
1664
+ async _verify(payload, requirements, options) {
1175
1665
  const requirementsV1 = requirements;
1176
1666
  const payloadV1 = payload;
1177
1667
  const exactEvmPayload = payload.payload;
1668
+ const payer = exactEvmPayload.authorization.from;
1669
+ let eip6492Deployment;
1178
1670
  if (payloadV1.scheme !== "exact" || requirements.scheme !== "exact") {
1179
1671
  return {
1180
1672
  isValid: false,
1181
- invalidReason: "unsupported_scheme",
1182
- payer: exactEvmPayload.authorization.from
1673
+ invalidReason: ErrInvalidScheme,
1674
+ payer
1183
1675
  };
1184
1676
  }
1185
1677
  let chainId;
@@ -1188,24 +1680,24 @@ var ExactEvmSchemeV12 = class {
1188
1680
  } catch {
1189
1681
  return {
1190
1682
  isValid: false,
1191
- invalidReason: `invalid_network`,
1192
- payer: exactEvmPayload.authorization.from
1683
+ invalidReason: ErrNetworkMismatch,
1684
+ payer
1193
1685
  };
1194
1686
  }
1195
1687
  if (!requirements.extra?.name || !requirements.extra?.version) {
1196
1688
  return {
1197
1689
  isValid: false,
1198
- invalidReason: "missing_eip712_domain",
1199
- payer: exactEvmPayload.authorization.from
1690
+ invalidReason: ErrMissingEip712Domain,
1691
+ payer
1200
1692
  };
1201
1693
  }
1202
1694
  const { name, version } = requirements.extra;
1203
- const erc20Address = (0, import_viem6.getAddress)(requirements.asset);
1695
+ const erc20Address = (0, import_viem9.getAddress)(requirements.asset);
1204
1696
  if (payloadV1.network !== requirements.network) {
1205
1697
  return {
1206
1698
  isValid: false,
1207
- invalidReason: "network_mismatch",
1208
- payer: exactEvmPayload.authorization.from
1699
+ invalidReason: ErrNetworkMismatch,
1700
+ payer
1209
1701
  };
1210
1702
  }
1211
1703
  const permitTypedData = {
@@ -1226,210 +1718,97 @@ var ExactEvmSchemeV12 = class {
1226
1718
  nonce: exactEvmPayload.authorization.nonce
1227
1719
  }
1228
1720
  };
1721
+ let isValid = false;
1229
1722
  try {
1230
- const recoveredAddress = await this.signer.verifyTypedData({
1231
- address: exactEvmPayload.authorization.from,
1723
+ isValid = await this.signer.verifyTypedData({
1724
+ address: payer,
1232
1725
  ...permitTypedData,
1233
1726
  signature: exactEvmPayload.signature
1234
1727
  });
1235
- if (!recoveredAddress) {
1728
+ } catch {
1729
+ isValid = false;
1730
+ }
1731
+ const signature = exactEvmPayload.signature;
1732
+ const sigLen = signature.startsWith("0x") ? signature.length - 2 : signature.length;
1733
+ const erc6492Data = (0, import_viem9.parseErc6492Signature)(signature);
1734
+ const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !(0, import_viem9.isAddressEqual)(erc6492Data.address, "0x0000000000000000000000000000000000000000");
1735
+ if (hasDeploymentInfo) {
1736
+ eip6492Deployment = {
1737
+ factoryAddress: erc6492Data.address,
1738
+ factoryCalldata: erc6492Data.data
1739
+ };
1740
+ }
1741
+ if (!isValid) {
1742
+ const isSmartWallet = sigLen > 130;
1743
+ if (!isSmartWallet) {
1236
1744
  return {
1237
1745
  isValid: false,
1238
- invalidReason: "invalid_exact_evm_payload_signature",
1239
- payer: exactEvmPayload.authorization.from
1746
+ invalidReason: ErrInvalidSignature,
1747
+ payer
1240
1748
  };
1241
1749
  }
1242
- } catch {
1243
- const signature = exactEvmPayload.signature;
1244
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
1245
- const isSmartWallet = signatureLength > 130;
1246
- if (isSmartWallet) {
1247
- const payerAddress = exactEvmPayload.authorization.from;
1248
- const bytecode = await this.signer.getCode({ address: payerAddress });
1249
- if (!bytecode || bytecode === "0x") {
1250
- const erc6492Data = (0, import_viem6.parseErc6492Signature)(signature);
1251
- const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !(0, import_viem6.isAddressEqual)(erc6492Data.address, "0x0000000000000000000000000000000000000000");
1252
- if (!hasDeploymentInfo) {
1253
- return {
1254
- isValid: false,
1255
- invalidReason: "invalid_exact_evm_payload_undeployed_smart_wallet",
1256
- payer: payerAddress
1257
- };
1258
- }
1259
- } else {
1260
- return {
1261
- isValid: false,
1262
- invalidReason: "invalid_exact_evm_payload_signature",
1263
- payer: exactEvmPayload.authorization.from
1264
- };
1265
- }
1266
- } else {
1750
+ const bytecode = await this.signer.getCode({ address: payer });
1751
+ const isDeployed = bytecode && bytecode !== "0x";
1752
+ if (!isDeployed && !hasDeploymentInfo) {
1267
1753
  return {
1268
1754
  isValid: false,
1269
- invalidReason: "invalid_exact_evm_payload_signature",
1270
- payer: exactEvmPayload.authorization.from
1755
+ invalidReason: ErrUndeployedSmartWallet,
1756
+ payer
1271
1757
  };
1272
1758
  }
1273
1759
  }
1274
- if ((0, import_viem6.getAddress)(exactEvmPayload.authorization.to) !== (0, import_viem6.getAddress)(requirements.payTo)) {
1760
+ if ((0, import_viem9.getAddress)(exactEvmPayload.authorization.to) !== (0, import_viem9.getAddress)(requirements.payTo)) {
1275
1761
  return {
1276
1762
  isValid: false,
1277
- invalidReason: "invalid_exact_evm_payload_recipient_mismatch",
1278
- payer: exactEvmPayload.authorization.from
1763
+ invalidReason: ErrRecipientMismatch,
1764
+ payer
1279
1765
  };
1280
1766
  }
1281
1767
  const now = Math.floor(Date.now() / 1e3);
1282
1768
  if (BigInt(exactEvmPayload.authorization.validBefore) < BigInt(now + 6)) {
1283
1769
  return {
1284
1770
  isValid: false,
1285
- invalidReason: "invalid_exact_evm_payload_authorization_valid_before",
1286
- payer: exactEvmPayload.authorization.from
1771
+ invalidReason: ErrValidBeforeExpired,
1772
+ payer
1287
1773
  };
1288
1774
  }
1289
1775
  if (BigInt(exactEvmPayload.authorization.validAfter) > BigInt(now)) {
1290
1776
  return {
1291
1777
  isValid: false,
1292
- invalidReason: "invalid_exact_evm_payload_authorization_valid_after",
1293
- payer: exactEvmPayload.authorization.from
1778
+ invalidReason: ErrValidAfterInFuture,
1779
+ payer
1294
1780
  };
1295
1781
  }
1296
- try {
1297
- const balance = await this.signer.readContract({
1298
- address: erc20Address,
1299
- abi: eip3009ABI,
1300
- functionName: "balanceOf",
1301
- args: [exactEvmPayload.authorization.from]
1302
- });
1303
- if (BigInt(balance) < BigInt(requirementsV1.maxAmountRequired)) {
1304
- return {
1305
- isValid: false,
1306
- invalidReason: "insufficient_funds",
1307
- invalidMessage: `Insufficient funds to complete the payment. Required: ${requirementsV1.maxAmountRequired} ${requirements.asset}, Available: ${balance.toString()} ${requirements.asset}. Please add funds to your wallet and try again.`,
1308
- payer: exactEvmPayload.authorization.from
1309
- };
1310
- }
1311
- } catch {
1312
- }
1313
1782
  if (BigInt(exactEvmPayload.authorization.value) !== BigInt(requirementsV1.maxAmountRequired)) {
1314
1783
  return {
1315
1784
  isValid: false,
1316
- invalidReason: "invalid_exact_evm_payload_authorization_value_mismatch",
1317
- payer: exactEvmPayload.authorization.from
1785
+ invalidReason: ErrInvalidAuthorizationValue,
1786
+ payer
1318
1787
  };
1319
1788
  }
1789
+ if (options?.simulate !== false) {
1790
+ const simulationSucceeded = await simulateEip3009Transfer(
1791
+ this.signer,
1792
+ erc20Address,
1793
+ exactEvmPayload,
1794
+ eip6492Deployment
1795
+ );
1796
+ if (!simulationSucceeded) {
1797
+ return diagnoseEip3009SimulationFailure(
1798
+ this.signer,
1799
+ erc20Address,
1800
+ exactEvmPayload,
1801
+ requirements,
1802
+ requirementsV1.maxAmountRequired
1803
+ );
1804
+ }
1805
+ }
1320
1806
  return {
1321
1807
  isValid: true,
1322
1808
  invalidReason: void 0,
1323
- payer: exactEvmPayload.authorization.from
1809
+ payer
1324
1810
  };
1325
1811
  }
1326
- /**
1327
- * Settles a payment by executing the transfer (V1).
1328
- *
1329
- * @param payload - The payment payload to settle
1330
- * @param requirements - The payment requirements
1331
- * @returns Promise resolving to settlement response
1332
- */
1333
- async settle(payload, requirements) {
1334
- const payloadV1 = payload;
1335
- const exactEvmPayload = payload.payload;
1336
- const valid = await this.verify(payload, requirements);
1337
- if (!valid.isValid) {
1338
- return {
1339
- success: false,
1340
- network: payloadV1.network,
1341
- transaction: "",
1342
- errorReason: valid.invalidReason ?? "invalid_scheme",
1343
- payer: exactEvmPayload.authorization.from
1344
- };
1345
- }
1346
- try {
1347
- const parseResult = (0, import_viem6.parseErc6492Signature)(exactEvmPayload.signature);
1348
- const { signature, address: factoryAddress, data: factoryCalldata } = parseResult;
1349
- if (this.config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !(0, import_viem6.isAddressEqual)(factoryAddress, "0x0000000000000000000000000000000000000000")) {
1350
- const payerAddress = exactEvmPayload.authorization.from;
1351
- const bytecode = await this.signer.getCode({ address: payerAddress });
1352
- if (!bytecode || bytecode === "0x") {
1353
- try {
1354
- console.log(`Deploying ERC-4337 smart wallet for ${payerAddress} via EIP-6492`);
1355
- const deployTx = await this.signer.sendTransaction({
1356
- to: factoryAddress,
1357
- data: factoryCalldata
1358
- });
1359
- await this.signer.waitForTransactionReceipt({ hash: deployTx });
1360
- console.log(`Successfully deployed smart wallet for ${payerAddress}`);
1361
- } catch (deployError) {
1362
- console.error("Smart wallet deployment failed:", deployError);
1363
- throw deployError;
1364
- }
1365
- } else {
1366
- console.log(`Smart wallet for ${payerAddress} already deployed, skipping deployment`);
1367
- }
1368
- }
1369
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
1370
- const isECDSA = signatureLength === 130;
1371
- let tx;
1372
- if (isECDSA) {
1373
- const parsedSig = (0, import_viem6.parseSignature)(signature);
1374
- tx = await this.signer.writeContract({
1375
- address: (0, import_viem6.getAddress)(requirements.asset),
1376
- abi: eip3009ABI,
1377
- functionName: "transferWithAuthorization",
1378
- args: [
1379
- (0, import_viem6.getAddress)(exactEvmPayload.authorization.from),
1380
- (0, import_viem6.getAddress)(exactEvmPayload.authorization.to),
1381
- BigInt(exactEvmPayload.authorization.value),
1382
- BigInt(exactEvmPayload.authorization.validAfter),
1383
- BigInt(exactEvmPayload.authorization.validBefore),
1384
- exactEvmPayload.authorization.nonce,
1385
- parsedSig.v || parsedSig.yParity,
1386
- parsedSig.r,
1387
- parsedSig.s
1388
- ]
1389
- });
1390
- } else {
1391
- tx = await this.signer.writeContract({
1392
- address: (0, import_viem6.getAddress)(requirements.asset),
1393
- abi: eip3009ABI,
1394
- functionName: "transferWithAuthorization",
1395
- args: [
1396
- (0, import_viem6.getAddress)(exactEvmPayload.authorization.from),
1397
- (0, import_viem6.getAddress)(exactEvmPayload.authorization.to),
1398
- BigInt(exactEvmPayload.authorization.value),
1399
- BigInt(exactEvmPayload.authorization.validAfter),
1400
- BigInt(exactEvmPayload.authorization.validBefore),
1401
- exactEvmPayload.authorization.nonce,
1402
- signature
1403
- ]
1404
- });
1405
- }
1406
- const receipt = await this.signer.waitForTransactionReceipt({ hash: tx });
1407
- if (receipt.status !== "success") {
1408
- return {
1409
- success: false,
1410
- errorReason: "invalid_transaction_state",
1411
- transaction: tx,
1412
- network: payloadV1.network,
1413
- payer: exactEvmPayload.authorization.from
1414
- };
1415
- }
1416
- return {
1417
- success: true,
1418
- transaction: tx,
1419
- network: payloadV1.network,
1420
- payer: exactEvmPayload.authorization.from
1421
- };
1422
- } catch (error) {
1423
- console.error("Failed to settle transaction:", error);
1424
- return {
1425
- success: false,
1426
- errorReason: "transaction_failed",
1427
- transaction: "",
1428
- network: payloadV1.network,
1429
- payer: exactEvmPayload.authorization.from
1430
- };
1431
- }
1432
- }
1433
1812
  };
1434
1813
 
1435
1814
  // src/exact/facilitator/register.ts
@@ -1437,13 +1816,15 @@ function registerExactEvmScheme(facilitator, config) {
1437
1816
  facilitator.register(
1438
1817
  config.networks,
1439
1818
  new ExactEvmScheme(config.signer, {
1440
- deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
1819
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492,
1820
+ simulateInSettle: config.simulateInSettle
1441
1821
  })
1442
1822
  );
1443
1823
  facilitator.registerV1(
1444
1824
  NETWORKS,
1445
1825
  new ExactEvmSchemeV12(config.signer, {
1446
- deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
1826
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492,
1827
+ simulateInSettle: config.simulateInSettle
1447
1828
  })
1448
1829
  );
1449
1830
  return facilitator;