@payai/x402-evm 2.2.4 → 2.3.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 (53) hide show
  1. package/dist/cjs/exact/client/index.d.ts +3 -3
  2. package/dist/cjs/exact/client/index.js +412 -168
  3. package/dist/cjs/exact/client/index.js.map +1 -1
  4. package/dist/cjs/exact/facilitator/index.d.ts +18 -18
  5. package/dist/cjs/exact/facilitator/index.js +477 -163
  6. package/dist/cjs/exact/facilitator/index.js.map +1 -1
  7. package/dist/cjs/exact/server/index.js +8 -1
  8. package/dist/cjs/exact/server/index.js.map +1 -1
  9. package/dist/cjs/exact/v1/client/index.d.ts +1 -1
  10. package/dist/cjs/exact/v1/client/index.js +18 -18
  11. package/dist/cjs/exact/v1/client/index.js.map +1 -1
  12. package/dist/cjs/exact/v1/facilitator/index.d.ts +1 -1
  13. package/dist/cjs/exact/v1/facilitator/index.js +11 -10
  14. package/dist/cjs/exact/v1/facilitator/index.js.map +1 -1
  15. package/dist/cjs/index.d.ts +3 -390
  16. package/dist/cjs/index.js +363 -109
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/permit2-CQbXqCMC.d.ts +517 -0
  19. package/dist/cjs/signer-DC81R8wQ.d.ts +161 -0
  20. package/dist/cjs/v1/index.d.ts +12 -2
  21. package/dist/cjs/v1/index.js +15 -11
  22. package/dist/cjs/v1/index.js.map +1 -1
  23. package/dist/esm/chunk-LBIJBD7Q.mjs +425 -0
  24. package/dist/esm/chunk-LBIJBD7Q.mjs.map +1 -0
  25. package/dist/esm/{chunk-PFULIQAE.mjs → chunk-TKN5V2BV.mjs} +1 -1
  26. package/dist/esm/chunk-TKN5V2BV.mjs.map +1 -0
  27. package/dist/esm/{chunk-MVERYV34.mjs → chunk-XL6IFXCP.mjs} +96 -39
  28. package/dist/esm/chunk-XL6IFXCP.mjs.map +1 -0
  29. package/dist/esm/exact/client/index.d.mts +3 -3
  30. package/dist/esm/exact/client/index.mjs +4 -3
  31. package/dist/esm/exact/facilitator/index.d.mts +18 -18
  32. package/dist/esm/exact/facilitator/index.mjs +407 -103
  33. package/dist/esm/exact/facilitator/index.mjs.map +1 -1
  34. package/dist/esm/exact/server/index.mjs +8 -1
  35. package/dist/esm/exact/server/index.mjs.map +1 -1
  36. package/dist/esm/exact/v1/client/index.d.mts +1 -1
  37. package/dist/esm/exact/v1/client/index.mjs +2 -2
  38. package/dist/esm/exact/v1/facilitator/index.d.mts +1 -1
  39. package/dist/esm/exact/v1/facilitator/index.mjs +2 -2
  40. package/dist/esm/index.d.mts +3 -390
  41. package/dist/esm/index.mjs +29 -6
  42. package/dist/esm/index.mjs.map +1 -1
  43. package/dist/esm/permit2-CGOcN7Et.d.mts +517 -0
  44. package/dist/esm/signer-DC81R8wQ.d.mts +161 -0
  45. package/dist/esm/v1/index.d.mts +12 -2
  46. package/dist/esm/v1/index.mjs +6 -4
  47. package/package.json +5 -5
  48. package/dist/esm/chunk-MVERYV34.mjs.map +0 -1
  49. package/dist/esm/chunk-PFULIQAE.mjs.map +0 -1
  50. package/dist/esm/chunk-W556LH23.mjs +0 -229
  51. package/dist/esm/chunk-W556LH23.mjs.map +0 -1
  52. package/dist/esm/permit2-BsAoJiWD.d.mts +0 -103
  53. package/dist/esm/signer-5OVDxViv.d.mts +0 -79
@@ -1,16 +1,19 @@
1
1
  import {
2
2
  isPermit2Payload
3
- } from "../../chunk-PFULIQAE.mjs";
3
+ } from "../../chunk-TKN5V2BV.mjs";
4
4
  import {
5
- ExactEvmSchemeV12 as ExactEvmSchemeV1,
5
+ ExactEvmSchemeV1,
6
6
  NETWORKS,
7
7
  PERMIT2_ADDRESS,
8
8
  authorizationTypes,
9
9
  eip3009ABI,
10
+ erc20AllowanceAbi,
11
+ erc20ApproveAbi,
12
+ getEvmChainId,
10
13
  permit2WitnessTypes,
11
14
  x402ExactPermit2ProxyABI,
12
15
  x402ExactPermit2ProxyAddress
13
- } from "../../chunk-MVERYV34.mjs";
16
+ } from "../../chunk-XL6IFXCP.mjs";
14
17
 
15
18
  // src/exact/facilitator/eip3009.ts
16
19
  import { getAddress, isAddressEqual, parseErc6492Signature, parseSignature } from "viem";
@@ -45,7 +48,7 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
45
48
  domain: {
46
49
  name,
47
50
  version,
48
- chainId: parseInt(requirements.network.split(":")[1]),
51
+ chainId: getEvmChainId(requirements.network),
49
52
  verifyingContract: erc20Address
50
53
  },
51
54
  message: {
@@ -135,15 +138,16 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
135
138
  return {
136
139
  isValid: false,
137
140
  invalidReason: "insufficient_funds",
141
+ 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.`,
138
142
  payer
139
143
  };
140
144
  }
141
145
  } catch {
142
146
  }
143
- if (BigInt(eip3009Payload.authorization.value) < BigInt(requirements.amount)) {
147
+ if (BigInt(eip3009Payload.authorization.value) !== BigInt(requirements.amount)) {
144
148
  return {
145
149
  isValid: false,
146
- invalidReason: "invalid_exact_evm_payload_authorization_value",
150
+ invalidReason: "invalid_exact_evm_payload_authorization_value_mismatch",
147
151
  payer
148
152
  };
149
153
  }
@@ -243,20 +247,142 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
243
247
  }
244
248
 
245
249
  // src/exact/facilitator/permit2.ts
246
- import { getAddress as getAddress2 } from "viem";
247
- var erc20AllowanceABI = [
248
- {
249
- type: "function",
250
- name: "allowance",
251
- inputs: [
252
- { name: "owner", type: "address" },
253
- { name: "spender", type: "address" }
254
- ],
255
- outputs: [{ type: "uint256" }],
256
- stateMutability: "view"
257
- }
258
- ];
259
- async function verifyPermit2(signer, payload, requirements, permit2Payload) {
250
+ import {
251
+ extractEip2612GasSponsoringInfo,
252
+ validateEip2612GasSponsoringInfo,
253
+ extractErc20ApprovalGasSponsoringInfo,
254
+ ERC20_APPROVAL_GAS_SPONSORING
255
+ } from "@x402/extensions";
256
+ import { getAddress as getAddress3 } from "viem";
257
+
258
+ // src/exact/facilitator/errors.ts
259
+ var ErrPermit2InvalidSignature = "invalid_permit2_signature";
260
+ var ErrPermit2InvalidAmount = "permit2_invalid_amount";
261
+ var ErrPermit2InvalidDestination = "permit2_invalid_destination";
262
+ var ErrPermit2InvalidOwner = "permit2_invalid_owner";
263
+ var ErrPermit2PaymentTooEarly = "permit2_payment_too_early";
264
+ var ErrPermit2InvalidNonce = "permit2_invalid_nonce";
265
+ var ErrPermit2612AmountMismatch = "permit2_2612_amount_mismatch";
266
+ var ErrErc20ApprovalInvalidFormat = "invalid_erc20_approval_extension_format";
267
+ var ErrErc20ApprovalFromMismatch = "erc20_approval_from_mismatch";
268
+ var ErrErc20ApprovalAssetMismatch = "erc20_approval_asset_mismatch";
269
+ var ErrErc20ApprovalSpenderNotPermit2 = "erc20_approval_spender_not_permit2";
270
+ var ErrErc20ApprovalTxWrongTarget = "erc20_approval_tx_wrong_target";
271
+ var ErrErc20ApprovalTxWrongSelector = "erc20_approval_tx_wrong_selector";
272
+ var ErrErc20ApprovalTxWrongSpender = "erc20_approval_tx_wrong_spender";
273
+ var ErrErc20ApprovalTxInvalidCalldata = "erc20_approval_tx_invalid_calldata";
274
+ var ErrErc20ApprovalTxSignerMismatch = "erc20_approval_tx_signer_mismatch";
275
+ var ErrErc20ApprovalTxInvalidSignature = "erc20_approval_tx_invalid_signature";
276
+ var ErrErc20ApprovalTxParseFailed = "erc20_approval_tx_parse_failed";
277
+
278
+ // src/exact/facilitator/erc20approval.ts
279
+ import {
280
+ getAddress as getAddress2,
281
+ parseTransaction,
282
+ decodeFunctionData,
283
+ recoverTransactionAddress
284
+ } from "viem";
285
+ import {
286
+ validateErc20ApprovalGasSponsoringInfo
287
+ } from "@x402/extensions";
288
+ var APPROVE_SELECTOR = "0x095ea7b3";
289
+ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
290
+ if (!validateErc20ApprovalGasSponsoringInfo(info)) {
291
+ return {
292
+ isValid: false,
293
+ invalidReason: ErrErc20ApprovalInvalidFormat,
294
+ invalidMessage: "ERC-20 approval extension info failed schema validation"
295
+ };
296
+ }
297
+ if (getAddress2(info.from) !== getAddress2(payer)) {
298
+ return {
299
+ isValid: false,
300
+ invalidReason: ErrErc20ApprovalFromMismatch,
301
+ invalidMessage: `Expected from=${payer}, got ${info.from}`
302
+ };
303
+ }
304
+ if (getAddress2(info.asset) !== tokenAddress) {
305
+ return {
306
+ isValid: false,
307
+ invalidReason: ErrErc20ApprovalAssetMismatch,
308
+ invalidMessage: `Expected asset=${tokenAddress}, got ${info.asset}`
309
+ };
310
+ }
311
+ if (getAddress2(info.spender) !== getAddress2(PERMIT2_ADDRESS)) {
312
+ return {
313
+ isValid: false,
314
+ invalidReason: ErrErc20ApprovalSpenderNotPermit2,
315
+ invalidMessage: `Expected spender=${PERMIT2_ADDRESS}, got ${info.spender}`
316
+ };
317
+ }
318
+ try {
319
+ const serializedTx = info.signedTransaction;
320
+ const tx = parseTransaction(serializedTx);
321
+ if (!tx.to || getAddress2(tx.to) !== tokenAddress) {
322
+ return {
323
+ isValid: false,
324
+ invalidReason: ErrErc20ApprovalTxWrongTarget,
325
+ invalidMessage: `Transaction targets ${tx.to ?? "null"}, expected ${tokenAddress}`
326
+ };
327
+ }
328
+ const data = tx.data ?? "0x";
329
+ if (!data.startsWith(APPROVE_SELECTOR)) {
330
+ return {
331
+ isValid: false,
332
+ invalidReason: ErrErc20ApprovalTxWrongSelector,
333
+ invalidMessage: `Transaction calldata does not start with approve() selector ${APPROVE_SELECTOR}`
334
+ };
335
+ }
336
+ try {
337
+ const decoded = decodeFunctionData({
338
+ abi: erc20ApproveAbi,
339
+ data
340
+ });
341
+ const calldataSpender = getAddress2(decoded.args[0]);
342
+ if (calldataSpender !== getAddress2(PERMIT2_ADDRESS)) {
343
+ return {
344
+ isValid: false,
345
+ invalidReason: ErrErc20ApprovalTxWrongSpender,
346
+ invalidMessage: `approve() spender is ${calldataSpender}, expected Permit2 ${PERMIT2_ADDRESS}`
347
+ };
348
+ }
349
+ } catch {
350
+ return {
351
+ isValid: false,
352
+ invalidReason: ErrErc20ApprovalTxInvalidCalldata,
353
+ invalidMessage: "Failed to decode approve() calldata from the signed transaction"
354
+ };
355
+ }
356
+ try {
357
+ const recoveredAddress = await recoverTransactionAddress({
358
+ serializedTransaction: serializedTx
359
+ });
360
+ if (getAddress2(recoveredAddress) !== getAddress2(payer)) {
361
+ return {
362
+ isValid: false,
363
+ invalidReason: ErrErc20ApprovalTxSignerMismatch,
364
+ invalidMessage: `Transaction signed by ${recoveredAddress}, expected payer ${payer}`
365
+ };
366
+ }
367
+ } catch {
368
+ return {
369
+ isValid: false,
370
+ invalidReason: ErrErc20ApprovalTxInvalidSignature,
371
+ invalidMessage: "Failed to recover signer from the signed transaction"
372
+ };
373
+ }
374
+ } catch {
375
+ return {
376
+ isValid: false,
377
+ invalidReason: ErrErc20ApprovalTxParseFailed,
378
+ invalidMessage: "Failed to parse the signed transaction"
379
+ };
380
+ }
381
+ return { isValid: true };
382
+ }
383
+
384
+ // src/exact/facilitator/permit2.ts
385
+ async function verifyPermit2(signer, payload, requirements, permit2Payload, context) {
260
386
  const payer = permit2Payload.permit2Authorization.from;
261
387
  if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
262
388
  return {
@@ -272,16 +398,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
272
398
  payer
273
399
  };
274
400
  }
275
- const chainId = parseInt(requirements.network.split(":")[1]);
276
- const tokenAddress = getAddress2(requirements.asset);
277
- if (getAddress2(permit2Payload.permit2Authorization.spender) !== getAddress2(x402ExactPermit2ProxyAddress)) {
401
+ const chainId = getEvmChainId(requirements.network);
402
+ const tokenAddress = getAddress3(requirements.asset);
403
+ if (getAddress3(permit2Payload.permit2Authorization.spender) !== getAddress3(x402ExactPermit2ProxyAddress)) {
278
404
  return {
279
405
  isValid: false,
280
406
  invalidReason: "invalid_permit2_spender",
281
407
  payer
282
408
  };
283
409
  }
284
- if (getAddress2(permit2Payload.permit2Authorization.witness.to) !== getAddress2(requirements.payTo)) {
410
+ if (getAddress3(permit2Payload.permit2Authorization.witness.to) !== getAddress3(requirements.payTo)) {
285
411
  return {
286
412
  isValid: false,
287
413
  invalidReason: "invalid_permit2_recipient_mismatch",
@@ -303,14 +429,14 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
303
429
  payer
304
430
  };
305
431
  }
306
- if (BigInt(permit2Payload.permit2Authorization.permitted.amount) < BigInt(requirements.amount)) {
432
+ if (BigInt(permit2Payload.permit2Authorization.permitted.amount) !== BigInt(requirements.amount)) {
307
433
  return {
308
434
  isValid: false,
309
- invalidReason: "permit2_insufficient_amount",
435
+ invalidReason: "permit2_amount_mismatch",
310
436
  payer
311
437
  };
312
438
  }
313
- if (getAddress2(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
439
+ if (getAddress3(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
314
440
  return {
315
441
  isValid: false,
316
442
  invalidReason: "permit2_token_mismatch",
@@ -327,16 +453,15 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
327
453
  },
328
454
  message: {
329
455
  permitted: {
330
- token: getAddress2(permit2Payload.permit2Authorization.permitted.token),
456
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
331
457
  amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
332
458
  },
333
- spender: getAddress2(permit2Payload.permit2Authorization.spender),
459
+ spender: getAddress3(permit2Payload.permit2Authorization.spender),
334
460
  nonce: BigInt(permit2Payload.permit2Authorization.nonce),
335
461
  deadline: BigInt(permit2Payload.permit2Authorization.deadline),
336
462
  witness: {
337
- to: getAddress2(permit2Payload.permit2Authorization.witness.to),
338
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter),
339
- extra: permit2Payload.permit2Authorization.witness.extra
463
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
464
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
340
465
  }
341
466
  }
342
467
  };
@@ -360,21 +485,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
360
485
  payer
361
486
  };
362
487
  }
363
- try {
364
- const allowance = await signer.readContract({
365
- address: tokenAddress,
366
- abi: erc20AllowanceABI,
367
- functionName: "allowance",
368
- args: [payer, PERMIT2_ADDRESS]
369
- });
370
- if (allowance < BigInt(requirements.amount)) {
371
- return {
372
- isValid: false,
373
- invalidReason: "permit2_allowance_required",
374
- payer
375
- };
376
- }
377
- } catch {
488
+ const allowanceResult = await _verifyPermit2Allowance(
489
+ signer,
490
+ payload,
491
+ requirements,
492
+ payer,
493
+ tokenAddress,
494
+ context
495
+ );
496
+ if (allowanceResult) {
497
+ return allowanceResult;
378
498
  }
379
499
  try {
380
500
  const balance = await signer.readContract({
@@ -387,6 +507,7 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
387
507
  return {
388
508
  isValid: false,
389
509
  invalidReason: "insufficient_funds",
510
+ 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.`,
390
511
  payer
391
512
  };
392
513
  }
@@ -398,9 +519,53 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
398
519
  payer
399
520
  };
400
521
  }
401
- async function settlePermit2(signer, payload, requirements, permit2Payload) {
522
+ async function _verifyPermit2Allowance(signer, payload, requirements, payer, tokenAddress, context) {
523
+ try {
524
+ const allowance = await signer.readContract({
525
+ address: tokenAddress,
526
+ abi: erc20AllowanceAbi,
527
+ functionName: "allowance",
528
+ args: [payer, PERMIT2_ADDRESS]
529
+ });
530
+ if (allowance >= BigInt(requirements.amount)) {
531
+ return null;
532
+ }
533
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
534
+ if (eip2612Info) {
535
+ const result = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
536
+ if (!result.isValid) {
537
+ return { isValid: false, invalidReason: result.invalidReason, payer };
538
+ }
539
+ return null;
540
+ }
541
+ const erc20GasSponsorshipExtension = context?.getExtension(
542
+ ERC20_APPROVAL_GAS_SPONSORING.key
543
+ );
544
+ if (erc20GasSponsorshipExtension) {
545
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
546
+ if (erc20Info) {
547
+ const result = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
548
+ if (!result.isValid) {
549
+ return { isValid: false, invalidReason: result.invalidReason, payer };
550
+ }
551
+ return null;
552
+ }
553
+ }
554
+ return { isValid: false, invalidReason: "permit2_allowance_required", payer };
555
+ } catch {
556
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
557
+ if (eip2612Info) {
558
+ const result = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
559
+ if (!result.isValid) {
560
+ return { isValid: false, invalidReason: result.invalidReason, payer };
561
+ }
562
+ }
563
+ return null;
564
+ }
565
+ }
566
+ async function settlePermit2(signer, payload, requirements, permit2Payload, context) {
402
567
  const payer = permit2Payload.permit2Authorization.from;
403
- const valid = await verifyPermit2(signer, payload, requirements, permit2Payload);
568
+ const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
404
569
  if (!valid.isValid) {
405
570
  return {
406
571
  success: false,
@@ -410,82 +575,223 @@ async function settlePermit2(signer, payload, requirements, permit2Payload) {
410
575
  payer
411
576
  };
412
577
  }
578
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
579
+ if (eip2612Info) {
580
+ return _settlePermit2WithEIP2612(signer, payload, permit2Payload, eip2612Info);
581
+ }
582
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
583
+ if (erc20Info) {
584
+ const erc20GasSponsorshipExtension = context?.getExtension(
585
+ ERC20_APPROVAL_GAS_SPONSORING.key
586
+ );
587
+ if (erc20GasSponsorshipExtension?.signer) {
588
+ return _settlePermit2WithERC20Approval(
589
+ erc20GasSponsorshipExtension.signer,
590
+ payload,
591
+ permit2Payload,
592
+ erc20Info
593
+ );
594
+ }
595
+ }
596
+ return _settlePermit2Direct(signer, payload, permit2Payload);
597
+ }
598
+ async function _settlePermit2WithEIP2612(signer, payload, permit2Payload, eip2612Info) {
599
+ const payer = permit2Payload.permit2Authorization.from;
413
600
  try {
601
+ const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
414
602
  const tx = await signer.writeContract({
415
603
  address: x402ExactPermit2ProxyAddress,
416
604
  abi: x402ExactPermit2ProxyABI,
417
- functionName: "settle",
605
+ functionName: "settleWithPermit",
418
606
  args: [
607
+ {
608
+ value: BigInt(eip2612Info.amount),
609
+ deadline: BigInt(eip2612Info.deadline),
610
+ r,
611
+ s,
612
+ v
613
+ },
419
614
  {
420
615
  permitted: {
421
- token: getAddress2(permit2Payload.permit2Authorization.permitted.token),
616
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
422
617
  amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
423
618
  },
424
619
  nonce: BigInt(permit2Payload.permit2Authorization.nonce),
425
620
  deadline: BigInt(permit2Payload.permit2Authorization.deadline)
426
621
  },
427
- getAddress2(payer),
622
+ getAddress3(payer),
428
623
  {
429
- to: getAddress2(permit2Payload.permit2Authorization.witness.to),
430
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter),
431
- extra: permit2Payload.permit2Authorization.witness.extra
624
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
625
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
432
626
  },
433
627
  permit2Payload.signature
434
628
  ]
435
629
  });
436
- const receipt = await signer.waitForTransactionReceipt({ hash: tx });
437
- if (receipt.status !== "success") {
630
+ return _waitAndReturn(signer, tx, payload, payer);
631
+ } catch (error) {
632
+ return _mapSettleError(error, payload, payer);
633
+ }
634
+ }
635
+ async function _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info) {
636
+ const payer = permit2Payload.permit2Authorization.from;
637
+ try {
638
+ const approvalTxHash = await extensionSigner.sendRawTransaction({
639
+ serializedTransaction: erc20Info.signedTransaction
640
+ });
641
+ const approvalReceipt = await extensionSigner.waitForTransactionReceipt({
642
+ hash: approvalTxHash
643
+ });
644
+ if (approvalReceipt.status !== "success") {
438
645
  return {
439
646
  success: false,
440
- errorReason: "invalid_transaction_state",
441
- transaction: tx,
647
+ errorReason: "erc20_approval_tx_failed",
648
+ transaction: approvalTxHash,
442
649
  network: payload.accepted.network,
443
650
  payer
444
651
  };
445
652
  }
446
- return {
447
- success: true,
448
- transaction: tx,
449
- network: payload.accepted.network,
450
- payer
451
- };
653
+ const tx = await extensionSigner.writeContract({
654
+ address: x402ExactPermit2ProxyAddress,
655
+ abi: x402ExactPermit2ProxyABI,
656
+ functionName: "settle",
657
+ args: [
658
+ {
659
+ permitted: {
660
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
661
+ amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
662
+ },
663
+ nonce: BigInt(permit2Payload.permit2Authorization.nonce),
664
+ deadline: BigInt(permit2Payload.permit2Authorization.deadline)
665
+ },
666
+ getAddress3(payer),
667
+ {
668
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
669
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
670
+ },
671
+ permit2Payload.signature
672
+ ]
673
+ });
674
+ return _waitAndReturn(extensionSigner, tx, payload, payer);
452
675
  } catch (error) {
453
- let errorReason = "transaction_failed";
454
- if (error instanceof Error) {
455
- const message = error.message;
456
- if (message.includes("AmountExceedsPermitted")) {
457
- errorReason = "permit2_amount_exceeds_permitted";
458
- } else if (message.includes("InvalidDestination")) {
459
- errorReason = "permit2_invalid_destination";
460
- } else if (message.includes("InvalidOwner")) {
461
- errorReason = "permit2_invalid_owner";
462
- } else if (message.includes("PaymentTooEarly")) {
463
- errorReason = "permit2_payment_too_early";
464
- } else if (message.includes("InvalidSignature") || message.includes("SignatureExpired")) {
465
- errorReason = "permit2_invalid_signature";
466
- } else if (message.includes("InvalidNonce")) {
467
- errorReason = "permit2_invalid_nonce";
468
- } else {
469
- errorReason = `transaction_failed: ${message.slice(0, 500)}`;
470
- }
471
- }
676
+ return _mapSettleError(error, payload, payer);
677
+ }
678
+ }
679
+ async function _settlePermit2Direct(signer, payload, permit2Payload) {
680
+ const payer = permit2Payload.permit2Authorization.from;
681
+ try {
682
+ const tx = await signer.writeContract({
683
+ address: x402ExactPermit2ProxyAddress,
684
+ abi: x402ExactPermit2ProxyABI,
685
+ functionName: "settle",
686
+ args: [
687
+ {
688
+ permitted: {
689
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
690
+ amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
691
+ },
692
+ nonce: BigInt(permit2Payload.permit2Authorization.nonce),
693
+ deadline: BigInt(permit2Payload.permit2Authorization.deadline)
694
+ },
695
+ getAddress3(payer),
696
+ {
697
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
698
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
699
+ },
700
+ permit2Payload.signature
701
+ ]
702
+ });
703
+ return _waitAndReturn(signer, tx, payload, payer);
704
+ } catch (error) {
705
+ return _mapSettleError(error, payload, payer);
706
+ }
707
+ }
708
+ async function _waitAndReturn(signer, tx, payload, payer) {
709
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
710
+ if (receipt.status !== "success") {
472
711
  return {
473
712
  success: false,
474
- errorReason,
475
- transaction: "",
713
+ errorReason: "invalid_transaction_state",
714
+ transaction: tx,
476
715
  network: payload.accepted.network,
477
716
  payer
478
717
  };
479
718
  }
719
+ return {
720
+ success: true,
721
+ transaction: tx,
722
+ network: payload.accepted.network,
723
+ payer
724
+ };
725
+ }
726
+ function _mapSettleError(error, payload, payer) {
727
+ let errorReason = "transaction_failed";
728
+ if (error instanceof Error) {
729
+ const message = error.message;
730
+ if (message.includes("Permit2612AmountMismatch")) {
731
+ errorReason = ErrPermit2612AmountMismatch;
732
+ } else if (message.includes("InvalidAmount")) {
733
+ errorReason = ErrPermit2InvalidAmount;
734
+ } else if (message.includes("InvalidDestination")) {
735
+ errorReason = ErrPermit2InvalidDestination;
736
+ } else if (message.includes("InvalidOwner")) {
737
+ errorReason = ErrPermit2InvalidOwner;
738
+ } else if (message.includes("PaymentTooEarly")) {
739
+ errorReason = ErrPermit2PaymentTooEarly;
740
+ } else if (message.includes("InvalidSignature") || message.includes("SignatureExpired")) {
741
+ errorReason = ErrPermit2InvalidSignature;
742
+ } else if (message.includes("InvalidNonce")) {
743
+ errorReason = ErrPermit2InvalidNonce;
744
+ } else {
745
+ errorReason = `transaction_failed: ${message.slice(0, 500)}`;
746
+ }
747
+ }
748
+ return {
749
+ success: false,
750
+ errorReason,
751
+ transaction: "",
752
+ network: payload.accepted.network,
753
+ payer
754
+ };
755
+ }
756
+ function validateEip2612PermitForPayment(info, payer, tokenAddress) {
757
+ if (!validateEip2612GasSponsoringInfo(info)) {
758
+ return { isValid: false, invalidReason: "invalid_eip2612_extension_format" };
759
+ }
760
+ if (getAddress3(info.from) !== getAddress3(payer)) {
761
+ return { isValid: false, invalidReason: "eip2612_from_mismatch" };
762
+ }
763
+ if (getAddress3(info.asset) !== tokenAddress) {
764
+ return { isValid: false, invalidReason: "eip2612_asset_mismatch" };
765
+ }
766
+ if (getAddress3(info.spender) !== getAddress3(PERMIT2_ADDRESS)) {
767
+ return { isValid: false, invalidReason: "eip2612_spender_not_permit2" };
768
+ }
769
+ const now = Math.floor(Date.now() / 1e3);
770
+ if (BigInt(info.deadline) < BigInt(now + 6)) {
771
+ return { isValid: false, invalidReason: "eip2612_deadline_expired" };
772
+ }
773
+ return { isValid: true };
774
+ }
775
+ function splitEip2612Signature(signature) {
776
+ const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
777
+ if (sig.length !== 130) {
778
+ throw new Error(
779
+ `invalid EIP-2612 signature length: expected 65 bytes (130 hex chars), got ${sig.length / 2} bytes`
780
+ );
781
+ }
782
+ const r = `0x${sig.slice(0, 64)}`;
783
+ const s = `0x${sig.slice(64, 128)}`;
784
+ const v = parseInt(sig.slice(128, 130), 16);
785
+ return { v, r, s };
480
786
  }
481
787
 
482
788
  // src/exact/facilitator/scheme.ts
483
789
  var ExactEvmScheme = class {
484
790
  /**
485
- * Creates a new ExactEvmFacilitator instance.
791
+ * Creates a new ExactEvmScheme facilitator instance.
486
792
  *
487
793
  * @param signer - The EVM signer for facilitator operations
488
- * @param config - Optional configuration for the facilitator
794
+ * @param config - Optional configuration
489
795
  */
490
796
  constructor(signer, config) {
491
797
  this.signer = signer;
@@ -496,53 +802,51 @@ var ExactEvmScheme = class {
496
802
  };
497
803
  }
498
804
  /**
499
- * Get mechanism-specific extra data for the supported kinds endpoint.
500
- * For EVM, no extra data is needed.
805
+ * Returns undefined — EVM has no mechanism-specific extra data.
501
806
  *
502
- * @param _ - The network identifier (unused for EVM)
503
- * @returns undefined (EVM has no extra data)
807
+ * @param _ - The network identifier (unused)
808
+ * @returns undefined
504
809
  */
505
810
  getExtra(_) {
506
811
  return void 0;
507
812
  }
508
813
  /**
509
- * Get signer addresses used by this facilitator.
510
- * Returns all addresses this facilitator can use for signing/settling transactions.
814
+ * Returns facilitator wallet addresses for the supported response.
511
815
  *
512
- * @param _ - The network identifier (unused for EVM, addresses are network-agnostic)
816
+ * @param _ - The network identifier (unused, addresses are network-agnostic)
513
817
  * @returns Array of facilitator wallet addresses
514
818
  */
515
819
  getSigners(_) {
516
820
  return [...this.signer.getAddresses()];
517
821
  }
518
822
  /**
519
- * Verifies a payment payload.
520
- * Routes to the appropriate verification logic based on payload type.
823
+ * Verifies a payment payload. Routes to Permit2 or EIP-3009 based on payload type.
521
824
  *
522
825
  * @param payload - The payment payload to verify
523
826
  * @param requirements - The payment requirements
827
+ * @param context - Optional facilitator context for extension capabilities
524
828
  * @returns Promise resolving to verification response
525
829
  */
526
- async verify(payload, requirements) {
830
+ async verify(payload, requirements, context) {
527
831
  const rawPayload = payload.payload;
528
832
  if (isPermit2Payload(rawPayload)) {
529
- return verifyPermit2(this.signer, payload, requirements, rawPayload);
833
+ return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
530
834
  }
531
835
  const eip3009Payload = rawPayload;
532
836
  return verifyEIP3009(this.signer, payload, requirements, eip3009Payload);
533
837
  }
534
838
  /**
535
- * Settles a payment by executing the transfer.
536
- * Routes to the appropriate settlement logic based on payload type.
839
+ * Settles a payment. Routes to Permit2 or EIP-3009 based on payload type.
537
840
  *
538
841
  * @param payload - The payment payload to settle
539
842
  * @param requirements - The payment requirements
843
+ * @param context - Optional facilitator context for extension capabilities
540
844
  * @returns Promise resolving to settlement response
541
845
  */
542
- async settle(payload, requirements) {
846
+ async settle(payload, requirements, context) {
543
847
  const rawPayload = payload.payload;
544
848
  if (isPermit2Payload(rawPayload)) {
545
- return settlePermit2(this.signer, payload, requirements, rawPayload);
849
+ return settlePermit2(this.signer, payload, requirements, rawPayload, context);
546
850
  }
547
851
  const eip3009Payload = rawPayload;
548
852
  return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);