@x402/evm 2.6.0 → 2.7.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 +24 -0
  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
@@ -2,34 +2,98 @@ import {
2
2
  isPermit2Payload
3
3
  } from "../../chunk-TKN5V2BV.mjs";
4
4
  import {
5
+ ERC20_APPROVAL_GAS_SPONSORING_KEY,
6
+ extractEip2612GasSponsoringInfo,
7
+ extractErc20ApprovalGasSponsoringInfo,
8
+ resolveErc20ApprovalExtensionSigner,
9
+ validateEip2612GasSponsoringInfo,
10
+ validateErc20ApprovalGasSponsoringInfo
11
+ } from "../../chunk-GD4MKCN7.mjs";
12
+ import {
13
+ DEFAULT_MAX_FEE_PER_GAS,
14
+ ERC20_APPROVE_GAS_LIMIT,
15
+ ErrEip2612AssetMismatch,
16
+ ErrEip2612DeadlineExpired,
17
+ ErrEip2612FromMismatch,
18
+ ErrEip2612SpenderNotPermit2,
19
+ ErrErc20ApprovalAssetMismatch,
20
+ ErrErc20ApprovalFromMismatch,
21
+ ErrErc20ApprovalInsufficientEthForGas,
22
+ ErrErc20ApprovalInvalidFormat,
23
+ ErrErc20ApprovalSpenderNotPermit2,
24
+ ErrErc20ApprovalTxFailed,
25
+ ErrErc20ApprovalTxInvalidCalldata,
26
+ ErrErc20ApprovalTxInvalidSignature,
27
+ ErrErc20ApprovalTxParseFailed,
28
+ ErrErc20ApprovalTxSignerMismatch,
29
+ ErrErc20ApprovalTxWrongSelector,
30
+ ErrErc20ApprovalTxWrongSpender,
31
+ ErrErc20ApprovalTxWrongTarget,
32
+ ErrInvalidAuthorizationValue,
33
+ ErrInvalidEip2612ExtensionFormat,
34
+ ErrInvalidScheme,
35
+ ErrInvalidSignature,
36
+ ErrInvalidTransactionState,
37
+ ErrMissingEip712Domain,
38
+ ErrNetworkMismatch,
39
+ ErrPermit2612AmountMismatch,
40
+ ErrPermit2AllowanceRequired,
41
+ ErrPermit2AmountMismatch,
42
+ ErrPermit2DeadlineExpired,
43
+ ErrPermit2InsufficientBalance,
44
+ ErrPermit2InvalidAmount,
45
+ ErrPermit2InvalidDestination,
46
+ ErrPermit2InvalidNonce,
47
+ ErrPermit2InvalidOwner,
48
+ ErrPermit2InvalidSignature,
49
+ ErrPermit2InvalidSpender,
50
+ ErrPermit2NotYetValid,
51
+ ErrPermit2PaymentTooEarly,
52
+ ErrPermit2ProxyNotDeployed,
53
+ ErrPermit2RecipientMismatch,
54
+ ErrPermit2SimulationFailed,
55
+ ErrPermit2TokenMismatch,
56
+ ErrRecipientMismatch,
57
+ ErrTransactionFailed,
58
+ ErrUndeployedSmartWallet,
59
+ ErrUnsupportedPayloadType,
60
+ ErrValidAfterInFuture,
61
+ ErrValidBeforeExpired,
5
62
  ExactEvmSchemeV1,
63
+ MULTICALL3_ADDRESS,
6
64
  NETWORKS,
7
65
  PERMIT2_ADDRESS,
8
66
  authorizationTypes,
67
+ diagnoseEip3009SimulationFailure,
9
68
  eip3009ABI,
10
69
  erc20AllowanceAbi,
11
70
  erc20ApproveAbi,
71
+ executeTransferWithAuthorization,
12
72
  getEvmChainId,
73
+ multicall,
74
+ multicall3GetEthBalanceAbi,
13
75
  permit2WitnessTypes,
76
+ simulateEip3009Transfer,
14
77
  x402ExactPermit2ProxyABI,
15
78
  x402ExactPermit2ProxyAddress
16
- } from "../../chunk-XL6IFXCP.mjs";
79
+ } from "../../chunk-IZEI7JTG.mjs";
17
80
 
18
81
  // src/exact/facilitator/eip3009.ts
19
- import { getAddress, isAddressEqual, parseErc6492Signature, parseSignature } from "viem";
20
- async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
82
+ import { getAddress, isAddressEqual, parseErc6492Signature } from "viem";
83
+ async function verifyEIP3009(signer, payload, requirements, eip3009Payload, options) {
21
84
  const payer = eip3009Payload.authorization.from;
85
+ let eip6492Deployment;
22
86
  if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
23
87
  return {
24
88
  isValid: false,
25
- invalidReason: "unsupported_scheme",
89
+ invalidReason: ErrInvalidScheme,
26
90
  payer
27
91
  };
28
92
  }
29
93
  if (!requirements.extra?.name || !requirements.extra?.version) {
30
94
  return {
31
95
  isValid: false,
32
- invalidReason: "missing_eip712_domain",
96
+ invalidReason: ErrMissingEip712Domain,
33
97
  payer
34
98
  };
35
99
  }
@@ -38,7 +102,7 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
38
102
  if (payload.accepted.network !== requirements.network) {
39
103
  return {
40
104
  isValid: false,
41
- invalidReason: "network_mismatch",
105
+ invalidReason: ErrNetworkMismatch,
42
106
  payer
43
107
  };
44
108
  }
@@ -60,47 +124,41 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
60
124
  nonce: eip3009Payload.authorization.nonce
61
125
  }
62
126
  };
127
+ let isValid = false;
63
128
  try {
64
- const recoveredAddress = await signer.verifyTypedData({
129
+ isValid = await signer.verifyTypedData({
65
130
  address: eip3009Payload.authorization.from,
66
131
  ...permitTypedData,
67
132
  signature: eip3009Payload.signature
68
133
  });
69
- if (!recoveredAddress) {
134
+ } catch {
135
+ isValid = false;
136
+ }
137
+ const signature = eip3009Payload.signature;
138
+ const sigLen = signature.startsWith("0x") ? signature.length - 2 : signature.length;
139
+ const erc6492Data = parseErc6492Signature(signature);
140
+ const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !isAddressEqual(erc6492Data.address, "0x0000000000000000000000000000000000000000");
141
+ if (hasDeploymentInfo) {
142
+ eip6492Deployment = {
143
+ factoryAddress: erc6492Data.address,
144
+ factoryCalldata: erc6492Data.data
145
+ };
146
+ }
147
+ if (!isValid) {
148
+ const isSmartWallet = sigLen > 130;
149
+ if (!isSmartWallet) {
70
150
  return {
71
151
  isValid: false,
72
- invalidReason: "invalid_exact_evm_payload_signature",
152
+ invalidReason: ErrInvalidSignature,
73
153
  payer
74
154
  };
75
155
  }
76
- } catch {
77
- const signature = eip3009Payload.signature;
78
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
79
- const isSmartWallet = signatureLength > 130;
80
- if (isSmartWallet) {
81
- const payerAddress = eip3009Payload.authorization.from;
82
- const bytecode = await signer.getCode({ address: payerAddress });
83
- if (!bytecode || bytecode === "0x") {
84
- const erc6492Data = parseErc6492Signature(signature);
85
- const hasDeploymentInfo = erc6492Data.address && erc6492Data.data && !isAddressEqual(erc6492Data.address, "0x0000000000000000000000000000000000000000");
86
- if (!hasDeploymentInfo) {
87
- return {
88
- isValid: false,
89
- invalidReason: "invalid_exact_evm_payload_undeployed_smart_wallet",
90
- payer: payerAddress
91
- };
92
- }
93
- } else {
94
- return {
95
- isValid: false,
96
- invalidReason: "invalid_exact_evm_payload_signature",
97
- payer
98
- };
99
- }
100
- } else {
156
+ const bytecode = await signer.getCode({ address: payer });
157
+ const isDeployed = bytecode && bytecode !== "0x";
158
+ if (!isDeployed && !hasDeploymentInfo) {
101
159
  return {
102
160
  isValid: false,
103
- invalidReason: "invalid_exact_evm_payload_signature",
161
+ invalidReason: ErrUndeployedSmartWallet,
104
162
  payer
105
163
  };
106
164
  }
@@ -108,7 +166,7 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
108
166
  if (getAddress(eip3009Payload.authorization.to) !== getAddress(requirements.payTo)) {
109
167
  return {
110
168
  isValid: false,
111
- invalidReason: "invalid_exact_evm_payload_recipient_mismatch",
169
+ invalidReason: ErrRecipientMismatch,
112
170
  payer
113
171
  };
114
172
  }
@@ -116,41 +174,41 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
116
174
  if (BigInt(eip3009Payload.authorization.validBefore) < BigInt(now + 6)) {
117
175
  return {
118
176
  isValid: false,
119
- invalidReason: "invalid_exact_evm_payload_authorization_valid_before",
177
+ invalidReason: ErrValidBeforeExpired,
120
178
  payer
121
179
  };
122
180
  }
123
181
  if (BigInt(eip3009Payload.authorization.validAfter) > BigInt(now)) {
124
182
  return {
125
183
  isValid: false,
126
- invalidReason: "invalid_exact_evm_payload_authorization_valid_after",
184
+ invalidReason: ErrValidAfterInFuture,
127
185
  payer
128
186
  };
129
187
  }
130
- try {
131
- const balance = await signer.readContract({
132
- address: erc20Address,
133
- abi: eip3009ABI,
134
- functionName: "balanceOf",
135
- args: [eip3009Payload.authorization.from]
136
- });
137
- if (BigInt(balance) < BigInt(requirements.amount)) {
138
- return {
139
- isValid: false,
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.`,
142
- payer
143
- };
144
- }
145
- } catch {
146
- }
147
188
  if (BigInt(eip3009Payload.authorization.value) !== BigInt(requirements.amount)) {
148
189
  return {
149
190
  isValid: false,
150
- invalidReason: "invalid_exact_evm_payload_authorization_value_mismatch",
191
+ invalidReason: ErrInvalidAuthorizationValue,
151
192
  payer
152
193
  };
153
194
  }
195
+ if (options?.simulate !== false) {
196
+ const simulationSucceeded = await simulateEip3009Transfer(
197
+ signer,
198
+ erc20Address,
199
+ eip3009Payload,
200
+ eip6492Deployment
201
+ );
202
+ if (!simulationSucceeded) {
203
+ return diagnoseEip3009SimulationFailure(
204
+ signer,
205
+ erc20Address,
206
+ eip3009Payload,
207
+ requirements,
208
+ requirements.amount
209
+ );
210
+ }
211
+ }
154
212
  return {
155
213
  isValid: true,
156
214
  invalidReason: void 0,
@@ -159,19 +217,22 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
159
217
  }
160
218
  async function settleEIP3009(signer, payload, requirements, eip3009Payload, config) {
161
219
  const payer = eip3009Payload.authorization.from;
162
- const valid = await verifyEIP3009(signer, payload, requirements, eip3009Payload);
220
+ const valid = await verifyEIP3009(signer, payload, requirements, eip3009Payload, {
221
+ simulate: config.simulateInSettle ?? false
222
+ });
163
223
  if (!valid.isValid) {
164
224
  return {
165
225
  success: false,
166
226
  network: payload.accepted.network,
167
227
  transaction: "",
168
- errorReason: valid.invalidReason ?? "invalid_scheme",
228
+ errorReason: valid.invalidReason ?? ErrInvalidScheme,
169
229
  payer
170
230
  };
171
231
  }
172
232
  try {
173
- const parseResult = parseErc6492Signature(eip3009Payload.signature);
174
- const { signature, address: factoryAddress, data: factoryCalldata } = parseResult;
233
+ const { address: factoryAddress, data: factoryCalldata } = parseErc6492Signature(
234
+ eip3009Payload.signature
235
+ );
175
236
  if (config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !isAddressEqual(factoryAddress, "0x0000000000000000000000000000000000000000")) {
176
237
  const bytecode = await signer.getCode({ address: payer });
177
238
  if (!bytecode || bytecode === "0x") {
@@ -182,48 +243,16 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
182
243
  await signer.waitForTransactionReceipt({ hash: deployTx });
183
244
  }
184
245
  }
185
- const signatureLength = signature.startsWith("0x") ? signature.length - 2 : signature.length;
186
- const isECDSA = signatureLength === 130;
187
- let tx;
188
- if (isECDSA) {
189
- const parsedSig = parseSignature(signature);
190
- tx = await signer.writeContract({
191
- address: getAddress(requirements.asset),
192
- abi: eip3009ABI,
193
- functionName: "transferWithAuthorization",
194
- args: [
195
- getAddress(eip3009Payload.authorization.from),
196
- getAddress(eip3009Payload.authorization.to),
197
- BigInt(eip3009Payload.authorization.value),
198
- BigInt(eip3009Payload.authorization.validAfter),
199
- BigInt(eip3009Payload.authorization.validBefore),
200
- eip3009Payload.authorization.nonce,
201
- parsedSig.v || parsedSig.yParity,
202
- parsedSig.r,
203
- parsedSig.s
204
- ]
205
- });
206
- } else {
207
- tx = await signer.writeContract({
208
- address: getAddress(requirements.asset),
209
- abi: eip3009ABI,
210
- functionName: "transferWithAuthorization",
211
- args: [
212
- getAddress(eip3009Payload.authorization.from),
213
- getAddress(eip3009Payload.authorization.to),
214
- BigInt(eip3009Payload.authorization.value),
215
- BigInt(eip3009Payload.authorization.validAfter),
216
- BigInt(eip3009Payload.authorization.validBefore),
217
- eip3009Payload.authorization.nonce,
218
- signature
219
- ]
220
- });
221
- }
246
+ const tx = await executeTransferWithAuthorization(
247
+ signer,
248
+ getAddress(requirements.asset),
249
+ eip3009Payload
250
+ );
222
251
  const receipt = await signer.waitForTransactionReceipt({ hash: tx });
223
252
  if (receipt.status !== "success") {
224
253
  return {
225
254
  success: false,
226
- errorReason: "invalid_transaction_state",
255
+ errorReason: ErrTransactionFailed,
227
256
  transaction: tx,
228
257
  network: payload.accepted.network,
229
258
  payer
@@ -238,7 +267,7 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
238
267
  } catch {
239
268
  return {
240
269
  success: false,
241
- errorReason: "transaction_failed",
270
+ errorReason: ErrTransactionFailed,
242
271
  transaction: "",
243
272
  network: payload.accepted.network,
244
273
  payer
@@ -247,33 +276,7 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
247
276
  }
248
277
 
249
278
  // src/exact/facilitator/permit2.ts
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";
279
+ import { getAddress as getAddress4 } from "viem";
277
280
 
278
281
  // src/exact/facilitator/erc20approval.ts
279
282
  import {
@@ -282,9 +285,6 @@ import {
282
285
  decodeFunctionData,
283
286
  recoverTransactionAddress
284
287
  } from "viem";
285
- import {
286
- validateErc20ApprovalGasSponsoringInfo
287
- } from "@x402/extensions";
288
288
  var APPROVE_SELECTOR = "0x095ea7b3";
289
289
  async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
290
290
  if (!validateErc20ApprovalGasSponsoringInfo(info)) {
@@ -381,36 +381,285 @@ async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
381
381
  return { isValid: true };
382
382
  }
383
383
 
384
+ // src/exact/facilitator/permit2-utils.ts
385
+ import { encodeFunctionData, getAddress as getAddress3 } from "viem";
386
+ async function simulatePermit2Settle(signer, permit2Payload) {
387
+ try {
388
+ await signer.readContract({
389
+ address: x402ExactPermit2ProxyAddress,
390
+ abi: x402ExactPermit2ProxyABI,
391
+ functionName: "settle",
392
+ args: buildPermit2SettleArgs(permit2Payload)
393
+ });
394
+ return true;
395
+ } catch {
396
+ return false;
397
+ }
398
+ }
399
+ function splitEip2612Signature(signature) {
400
+ const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
401
+ if (sig.length !== 130) {
402
+ throw new Error(
403
+ `invalid EIP-2612 signature length: expected 65 bytes (130 hex chars), got ${sig.length / 2} bytes`
404
+ );
405
+ }
406
+ const r = `0x${sig.slice(0, 64)}`;
407
+ const s = `0x${sig.slice(64, 128)}`;
408
+ const v = parseInt(sig.slice(128, 130), 16);
409
+ return { v, r, s };
410
+ }
411
+ function buildPermit2SettleArgs(permit2Payload) {
412
+ return [
413
+ {
414
+ permitted: {
415
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
416
+ amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
417
+ },
418
+ nonce: BigInt(permit2Payload.permit2Authorization.nonce),
419
+ deadline: BigInt(permit2Payload.permit2Authorization.deadline)
420
+ },
421
+ getAddress3(permit2Payload.permit2Authorization.from),
422
+ {
423
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
424
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
425
+ },
426
+ permit2Payload.signature
427
+ ];
428
+ }
429
+ function encodePermit2SettleCalldata(permit2Payload) {
430
+ return encodeFunctionData({
431
+ abi: x402ExactPermit2ProxyABI,
432
+ functionName: "settle",
433
+ args: buildPermit2SettleArgs(permit2Payload)
434
+ });
435
+ }
436
+ async function simulatePermit2SettleWithPermit(signer, permit2Payload, eip2612Info) {
437
+ try {
438
+ const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
439
+ await signer.readContract({
440
+ address: x402ExactPermit2ProxyAddress,
441
+ abi: x402ExactPermit2ProxyABI,
442
+ functionName: "settleWithPermit",
443
+ args: [
444
+ {
445
+ value: BigInt(eip2612Info.amount),
446
+ deadline: BigInt(eip2612Info.deadline),
447
+ r,
448
+ s,
449
+ v
450
+ },
451
+ ...buildPermit2SettleArgs(permit2Payload)
452
+ ]
453
+ });
454
+ return true;
455
+ } catch {
456
+ return false;
457
+ }
458
+ }
459
+ async function diagnosePermit2SimulationFailure(signer, tokenAddress, permit2Payload, amountRequired) {
460
+ const payer = permit2Payload.permit2Authorization.from;
461
+ const diagnosticCalls = [
462
+ {
463
+ address: x402ExactPermit2ProxyAddress,
464
+ abi: x402ExactPermit2ProxyABI,
465
+ functionName: "PERMIT2"
466
+ },
467
+ {
468
+ address: tokenAddress,
469
+ abi: eip3009ABI,
470
+ functionName: "balanceOf",
471
+ args: [payer]
472
+ },
473
+ {
474
+ address: tokenAddress,
475
+ abi: erc20AllowanceAbi,
476
+ functionName: "allowance",
477
+ args: [payer, PERMIT2_ADDRESS]
478
+ }
479
+ ];
480
+ try {
481
+ const results = await multicall(signer.readContract.bind(signer), diagnosticCalls);
482
+ const [proxyResult, balanceResult, allowanceResult] = results;
483
+ if (proxyResult.status === "failure") {
484
+ return { isValid: false, invalidReason: ErrPermit2ProxyNotDeployed, payer };
485
+ }
486
+ if (balanceResult.status === "success") {
487
+ const balance = balanceResult.result;
488
+ if (balance < BigInt(amountRequired)) {
489
+ return { isValid: false, invalidReason: ErrPermit2InsufficientBalance, payer };
490
+ }
491
+ }
492
+ if (allowanceResult.status === "success") {
493
+ const allowance = allowanceResult.result;
494
+ if (allowance < BigInt(amountRequired)) {
495
+ return { isValid: false, invalidReason: ErrPermit2AllowanceRequired, payer };
496
+ }
497
+ }
498
+ } catch {
499
+ }
500
+ return { isValid: false, invalidReason: ErrPermit2SimulationFailed, payer };
501
+ }
502
+ async function checkPermit2Prerequisites(signer, tokenAddress, payer, amountRequired) {
503
+ const diagnosticCalls = [
504
+ {
505
+ address: x402ExactPermit2ProxyAddress,
506
+ abi: x402ExactPermit2ProxyABI,
507
+ functionName: "PERMIT2"
508
+ },
509
+ {
510
+ address: tokenAddress,
511
+ abi: eip3009ABI,
512
+ functionName: "balanceOf",
513
+ args: [payer]
514
+ },
515
+ {
516
+ address: MULTICALL3_ADDRESS,
517
+ abi: multicall3GetEthBalanceAbi,
518
+ functionName: "getEthBalance",
519
+ args: [payer]
520
+ }
521
+ ];
522
+ try {
523
+ const results = await multicall(signer.readContract.bind(signer), diagnosticCalls);
524
+ const [proxyResult, balanceResult, ethBalanceResult] = results;
525
+ if (proxyResult.status === "failure") {
526
+ return { isValid: false, invalidReason: ErrPermit2ProxyNotDeployed, payer };
527
+ }
528
+ if (balanceResult.status === "success") {
529
+ const balance = balanceResult.result;
530
+ if (balance < BigInt(amountRequired)) {
531
+ return { isValid: false, invalidReason: ErrPermit2InsufficientBalance, payer };
532
+ }
533
+ }
534
+ if (ethBalanceResult.status === "success") {
535
+ const minEthForApprovalGas = ERC20_APPROVE_GAS_LIMIT * DEFAULT_MAX_FEE_PER_GAS;
536
+ const ethBalance = ethBalanceResult.result;
537
+ if (ethBalance < minEthForApprovalGas) {
538
+ return {
539
+ isValid: false,
540
+ invalidReason: ErrErc20ApprovalInsufficientEthForGas,
541
+ payer
542
+ };
543
+ }
544
+ }
545
+ } catch {
546
+ }
547
+ return { isValid: true, invalidReason: void 0, payer };
548
+ }
549
+ async function simulatePermit2SettleWithErc20Approval(extensionSigner, permit2Payload, erc20Info) {
550
+ if (!extensionSigner.simulateTransactions) {
551
+ return false;
552
+ }
553
+ try {
554
+ const settleData = encodePermit2SettleCalldata(permit2Payload);
555
+ return await extensionSigner.simulateTransactions([
556
+ erc20Info.signedTransaction,
557
+ { to: x402ExactPermit2ProxyAddress, data: settleData, gas: BigInt(3e5) }
558
+ ]);
559
+ } catch {
560
+ return false;
561
+ }
562
+ }
563
+ async function waitAndReturn(signer, tx, payload, payer) {
564
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
565
+ if (receipt.status !== "success") {
566
+ return {
567
+ success: false,
568
+ errorReason: ErrInvalidTransactionState,
569
+ transaction: tx,
570
+ network: payload.accepted.network,
571
+ payer
572
+ };
573
+ }
574
+ return {
575
+ success: true,
576
+ transaction: tx,
577
+ network: payload.accepted.network,
578
+ payer
579
+ };
580
+ }
581
+ function mapSettleError(error, payload, payer) {
582
+ let errorReason = ErrTransactionFailed;
583
+ if (error instanceof Error) {
584
+ const message = error.message;
585
+ if (message.includes("Permit2612AmountMismatch")) {
586
+ errorReason = ErrPermit2612AmountMismatch;
587
+ } else if (message.includes("InvalidAmount")) {
588
+ errorReason = ErrPermit2InvalidAmount;
589
+ } else if (message.includes("InvalidDestination")) {
590
+ errorReason = ErrPermit2InvalidDestination;
591
+ } else if (message.includes("InvalidOwner")) {
592
+ errorReason = ErrPermit2InvalidOwner;
593
+ } else if (message.includes("PaymentTooEarly")) {
594
+ errorReason = ErrPermit2PaymentTooEarly;
595
+ } else if (message.includes("InvalidSignature") || message.includes("SignatureExpired")) {
596
+ errorReason = ErrPermit2InvalidSignature;
597
+ } else if (message.includes("InvalidNonce")) {
598
+ errorReason = ErrPermit2InvalidNonce;
599
+ } else if (message.includes("erc20_approval_tx_failed")) {
600
+ errorReason = ErrErc20ApprovalTxFailed;
601
+ } else {
602
+ errorReason = `${ErrTransactionFailed}: ${message.slice(0, 500)}`;
603
+ }
604
+ }
605
+ return {
606
+ success: false,
607
+ errorReason,
608
+ transaction: "",
609
+ network: payload.accepted.network,
610
+ payer
611
+ };
612
+ }
613
+ function validateEip2612PermitForPayment(info, payer, tokenAddress) {
614
+ if (!validateEip2612GasSponsoringInfo(info)) {
615
+ return { isValid: false, invalidReason: ErrInvalidEip2612ExtensionFormat };
616
+ }
617
+ if (getAddress3(info.from) !== getAddress3(payer)) {
618
+ return { isValid: false, invalidReason: ErrEip2612FromMismatch };
619
+ }
620
+ if (getAddress3(info.asset) !== tokenAddress) {
621
+ return { isValid: false, invalidReason: ErrEip2612AssetMismatch };
622
+ }
623
+ if (getAddress3(info.spender) !== getAddress3(PERMIT2_ADDRESS)) {
624
+ return { isValid: false, invalidReason: ErrEip2612SpenderNotPermit2 };
625
+ }
626
+ const now = Math.floor(Date.now() / 1e3);
627
+ if (BigInt(info.deadline) < BigInt(now + 6)) {
628
+ return { isValid: false, invalidReason: ErrEip2612DeadlineExpired };
629
+ }
630
+ return { isValid: true };
631
+ }
632
+
384
633
  // src/exact/facilitator/permit2.ts
385
- async function verifyPermit2(signer, payload, requirements, permit2Payload, context) {
634
+ async function verifyPermit2(signer, payload, requirements, permit2Payload, context, options) {
386
635
  const payer = permit2Payload.permit2Authorization.from;
387
636
  if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
388
637
  return {
389
638
  isValid: false,
390
- invalidReason: "unsupported_scheme",
639
+ invalidReason: ErrUnsupportedPayloadType,
391
640
  payer
392
641
  };
393
642
  }
394
643
  if (payload.accepted.network !== requirements.network) {
395
644
  return {
396
645
  isValid: false,
397
- invalidReason: "network_mismatch",
646
+ invalidReason: ErrNetworkMismatch,
398
647
  payer
399
648
  };
400
649
  }
401
650
  const chainId = getEvmChainId(requirements.network);
402
- const tokenAddress = getAddress3(requirements.asset);
403
- if (getAddress3(permit2Payload.permit2Authorization.spender) !== getAddress3(x402ExactPermit2ProxyAddress)) {
651
+ const tokenAddress = getAddress4(requirements.asset);
652
+ if (getAddress4(permit2Payload.permit2Authorization.spender) !== getAddress4(x402ExactPermit2ProxyAddress)) {
404
653
  return {
405
654
  isValid: false,
406
- invalidReason: "invalid_permit2_spender",
655
+ invalidReason: ErrPermit2InvalidSpender,
407
656
  payer
408
657
  };
409
658
  }
410
- if (getAddress3(permit2Payload.permit2Authorization.witness.to) !== getAddress3(requirements.payTo)) {
659
+ if (getAddress4(permit2Payload.permit2Authorization.witness.to) !== getAddress4(requirements.payTo)) {
411
660
  return {
412
661
  isValid: false,
413
- invalidReason: "invalid_permit2_recipient_mismatch",
662
+ invalidReason: ErrPermit2RecipientMismatch,
414
663
  payer
415
664
  };
416
665
  }
@@ -418,28 +667,28 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload, cont
418
667
  if (BigInt(permit2Payload.permit2Authorization.deadline) < BigInt(now + 6)) {
419
668
  return {
420
669
  isValid: false,
421
- invalidReason: "permit2_deadline_expired",
670
+ invalidReason: ErrPermit2DeadlineExpired,
422
671
  payer
423
672
  };
424
673
  }
425
674
  if (BigInt(permit2Payload.permit2Authorization.witness.validAfter) > BigInt(now)) {
426
675
  return {
427
676
  isValid: false,
428
- invalidReason: "permit2_not_yet_valid",
677
+ invalidReason: ErrPermit2NotYetValid,
429
678
  payer
430
679
  };
431
680
  }
432
681
  if (BigInt(permit2Payload.permit2Authorization.permitted.amount) !== BigInt(requirements.amount)) {
433
682
  return {
434
683
  isValid: false,
435
- invalidReason: "permit2_amount_mismatch",
684
+ invalidReason: ErrPermit2AmountMismatch,
436
685
  payer
437
686
  };
438
687
  }
439
- if (getAddress3(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
688
+ if (getAddress4(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
440
689
  return {
441
690
  isValid: false,
442
- invalidReason: "permit2_token_mismatch",
691
+ invalidReason: ErrPermit2TokenMismatch,
443
692
  payer
444
693
  };
445
694
  }
@@ -453,125 +702,114 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload, cont
453
702
  },
454
703
  message: {
455
704
  permitted: {
456
- token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
705
+ token: getAddress4(permit2Payload.permit2Authorization.permitted.token),
457
706
  amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
458
707
  },
459
- spender: getAddress3(permit2Payload.permit2Authorization.spender),
708
+ spender: getAddress4(permit2Payload.permit2Authorization.spender),
460
709
  nonce: BigInt(permit2Payload.permit2Authorization.nonce),
461
710
  deadline: BigInt(permit2Payload.permit2Authorization.deadline),
462
711
  witness: {
463
- to: getAddress3(permit2Payload.permit2Authorization.witness.to),
712
+ to: getAddress4(permit2Payload.permit2Authorization.witness.to),
464
713
  validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
465
714
  }
466
715
  }
467
716
  };
717
+ let signatureValid = false;
468
718
  try {
469
- const isValid = await signer.verifyTypedData({
719
+ signatureValid = await signer.verifyTypedData({
470
720
  address: payer,
471
721
  ...permit2TypedData,
472
722
  signature: permit2Payload.signature
473
723
  });
474
- if (!isValid) {
475
- return {
476
- isValid: false,
477
- invalidReason: "invalid_permit2_signature",
478
- payer
479
- };
480
- }
481
724
  } catch {
482
- return {
483
- isValid: false,
484
- invalidReason: "invalid_permit2_signature",
485
- payer
486
- };
725
+ signatureValid = false;
487
726
  }
488
- const allowanceResult = await _verifyPermit2Allowance(
489
- signer,
490
- payload,
491
- requirements,
492
- payer,
493
- tokenAddress,
494
- context
495
- );
496
- if (allowanceResult) {
497
- return allowanceResult;
498
- }
499
- try {
500
- const balance = await signer.readContract({
501
- address: tokenAddress,
502
- abi: eip3009ABI,
503
- functionName: "balanceOf",
504
- args: [payer]
505
- });
506
- if (balance < BigInt(requirements.amount)) {
727
+ if (!signatureValid) {
728
+ const bytecode = await signer.getCode({ address: payer });
729
+ const isDeployedContract = bytecode && bytecode !== "0x";
730
+ if (!isDeployedContract) {
507
731
  return {
508
732
  isValid: false,
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.`,
733
+ invalidReason: ErrPermit2InvalidSignature,
511
734
  payer
512
735
  };
513
736
  }
514
- } catch {
515
737
  }
516
- return {
517
- isValid: true,
518
- invalidReason: void 0,
519
- payer
520
- };
521
- }
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;
738
+ if (options?.simulate === false) {
739
+ return { isValid: true, invalidReason: void 0, payer };
740
+ }
741
+ const eip2612Info = extractEip2612GasSponsoringInfo(payload);
742
+ if (eip2612Info) {
743
+ const fieldResult = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
744
+ if (!fieldResult.isValid) {
745
+ return { isValid: false, invalidReason: fieldResult.invalidReason, payer };
532
746
  }
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;
747
+ const simOk2 = await simulatePermit2SettleWithPermit(signer, permit2Payload, eip2612Info);
748
+ if (!simOk2) {
749
+ return diagnosePermit2SimulationFailure(
750
+ signer,
751
+ tokenAddress,
752
+ permit2Payload,
753
+ requirements.amount
754
+ );
540
755
  }
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;
756
+ return { isValid: true, invalidReason: void 0, payer };
757
+ }
758
+ const erc20GasSponsorshipExtension = context?.getExtension(
759
+ ERC20_APPROVAL_GAS_SPONSORING_KEY
760
+ );
761
+ if (erc20GasSponsorshipExtension) {
762
+ const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
763
+ if (erc20Info) {
764
+ const fieldResult = await validateErc20ApprovalForPayment(erc20Info, payer, tokenAddress);
765
+ if (!fieldResult.isValid) {
766
+ return { isValid: false, invalidReason: fieldResult.invalidReason, payer };
552
767
  }
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 };
768
+ const extensionSigner = resolveErc20ApprovalExtensionSigner(
769
+ erc20GasSponsorshipExtension,
770
+ requirements.network
771
+ );
772
+ if (extensionSigner?.simulateTransactions) {
773
+ const simOk2 = await simulatePermit2SettleWithErc20Approval(
774
+ extensionSigner,
775
+ permit2Payload,
776
+ erc20Info
777
+ );
778
+ if (!simOk2) {
779
+ return diagnosePermit2SimulationFailure(
780
+ signer,
781
+ tokenAddress,
782
+ permit2Payload,
783
+ requirements.amount
784
+ );
785
+ }
786
+ return { isValid: true, invalidReason: void 0, payer };
561
787
  }
788
+ return checkPermit2Prerequisites(signer, tokenAddress, payer, requirements.amount);
562
789
  }
563
- return null;
564
790
  }
791
+ const simOk = await simulatePermit2Settle(signer, permit2Payload);
792
+ if (!simOk) {
793
+ return diagnosePermit2SimulationFailure(
794
+ signer,
795
+ tokenAddress,
796
+ permit2Payload,
797
+ requirements.amount
798
+ );
799
+ }
800
+ return { isValid: true, invalidReason: void 0, payer };
565
801
  }
566
- async function settlePermit2(signer, payload, requirements, permit2Payload, context) {
802
+ async function settlePermit2(signer, payload, requirements, permit2Payload, context, config) {
567
803
  const payer = permit2Payload.permit2Authorization.from;
568
- const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
804
+ const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context, {
805
+ simulate: config?.simulateInSettle ?? false
806
+ });
569
807
  if (!valid.isValid) {
570
808
  return {
571
809
  success: false,
572
810
  network: payload.accepted.network,
573
811
  transaction: "",
574
- errorReason: valid.invalidReason ?? "invalid_scheme",
812
+ errorReason: valid.invalidReason ?? ErrInvalidScheme,
575
813
  payer
576
814
  };
577
815
  }
@@ -582,15 +820,14 @@ async function settlePermit2(signer, payload, requirements, permit2Payload, cont
582
820
  const erc20Info = extractErc20ApprovalGasSponsoringInfo(payload);
583
821
  if (erc20Info) {
584
822
  const erc20GasSponsorshipExtension = context?.getExtension(
585
- ERC20_APPROVAL_GAS_SPONSORING.key
823
+ ERC20_APPROVAL_GAS_SPONSORING_KEY
586
824
  );
587
- if (erc20GasSponsorshipExtension?.signer) {
588
- return _settlePermit2WithERC20Approval(
589
- erc20GasSponsorshipExtension.signer,
590
- payload,
591
- permit2Payload,
592
- erc20Info
593
- );
825
+ const extensionSigner = resolveErc20ApprovalExtensionSigner(
826
+ erc20GasSponsorshipExtension,
827
+ payload.accepted.network
828
+ );
829
+ if (extensionSigner) {
830
+ return _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info);
594
831
  }
595
832
  }
596
833
  return _settlePermit2Direct(signer, payload, permit2Payload);
@@ -611,69 +848,26 @@ async function _settlePermit2WithEIP2612(signer, payload, permit2Payload, eip261
611
848
  s,
612
849
  v
613
850
  },
614
- {
615
- permitted: {
616
- token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
617
- amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
618
- },
619
- nonce: BigInt(permit2Payload.permit2Authorization.nonce),
620
- deadline: BigInt(permit2Payload.permit2Authorization.deadline)
621
- },
622
- getAddress3(payer),
623
- {
624
- to: getAddress3(permit2Payload.permit2Authorization.witness.to),
625
- validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
626
- },
627
- permit2Payload.signature
851
+ ...buildPermit2SettleArgs(permit2Payload)
628
852
  ]
629
853
  });
630
- return _waitAndReturn(signer, tx, payload, payer);
854
+ return waitAndReturn(signer, tx, payload, payer);
631
855
  } catch (error) {
632
- return _mapSettleError(error, payload, payer);
856
+ return mapSettleError(error, payload, payer);
633
857
  }
634
858
  }
635
859
  async function _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info) {
636
860
  const payer = permit2Payload.permit2Authorization.from;
637
861
  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") {
645
- return {
646
- success: false,
647
- errorReason: "erc20_approval_tx_failed",
648
- transaction: approvalTxHash,
649
- network: payload.accepted.network,
650
- payer
651
- };
652
- }
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);
862
+ const settleData = encodePermit2SettleCalldata(permit2Payload);
863
+ const txHashes = await extensionSigner.sendTransactions([
864
+ erc20Info.signedTransaction,
865
+ { to: x402ExactPermit2ProxyAddress, data: settleData, gas: BigInt(3e5) }
866
+ ]);
867
+ const settleTxHash = txHashes[txHashes.length - 1];
868
+ return waitAndReturn(extensionSigner, settleTxHash, payload, payer);
675
869
  } catch (error) {
676
- return _mapSettleError(error, payload, payer);
870
+ return mapSettleError(error, payload, payer);
677
871
  }
678
872
  }
679
873
  async function _settlePermit2Direct(signer, payload, permit2Payload) {
@@ -683,106 +877,12 @@ async function _settlePermit2Direct(signer, payload, permit2Payload) {
683
877
  address: x402ExactPermit2ProxyAddress,
684
878
  abi: x402ExactPermit2ProxyABI,
685
879
  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
- ]
880
+ args: buildPermit2SettleArgs(permit2Payload)
702
881
  });
703
- return _waitAndReturn(signer, tx, payload, payer);
882
+ return waitAndReturn(signer, tx, payload, payer);
704
883
  } 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") {
711
- return {
712
- success: false,
713
- errorReason: "invalid_transaction_state",
714
- transaction: tx,
715
- network: payload.accepted.network,
716
- payer
717
- };
884
+ return mapSettleError(error, payload, payer);
718
885
  }
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 };
786
886
  }
787
887
 
788
888
  // src/exact/facilitator/scheme.ts
@@ -798,7 +898,8 @@ var ExactEvmScheme = class {
798
898
  this.scheme = "exact";
799
899
  this.caipFamily = "eip155:*";
800
900
  this.config = {
801
- deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false
901
+ deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false,
902
+ simulateInSettle: config?.simulateInSettle ?? false
802
903
  };
803
904
  }
804
905
  /**
@@ -829,7 +930,8 @@ var ExactEvmScheme = class {
829
930
  */
830
931
  async verify(payload, requirements, context) {
831
932
  const rawPayload = payload.payload;
832
- if (isPermit2Payload(rawPayload)) {
933
+ const isPermit2 = isPermit2Payload(rawPayload);
934
+ if (isPermit2) {
833
935
  return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
834
936
  }
835
937
  const eip3009Payload = rawPayload;
@@ -845,8 +947,11 @@ var ExactEvmScheme = class {
845
947
  */
846
948
  async settle(payload, requirements, context) {
847
949
  const rawPayload = payload.payload;
848
- if (isPermit2Payload(rawPayload)) {
849
- return settlePermit2(this.signer, payload, requirements, rawPayload, context);
950
+ const isPermit2 = isPermit2Payload(rawPayload);
951
+ if (isPermit2) {
952
+ return settlePermit2(this.signer, payload, requirements, rawPayload, context, {
953
+ simulateInSettle: this.config.simulateInSettle
954
+ });
850
955
  }
851
956
  const eip3009Payload = rawPayload;
852
957
  return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);
@@ -858,13 +963,15 @@ function registerExactEvmScheme(facilitator, config) {
858
963
  facilitator.register(
859
964
  config.networks,
860
965
  new ExactEvmScheme(config.signer, {
861
- deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
966
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492,
967
+ simulateInSettle: config.simulateInSettle
862
968
  })
863
969
  );
864
970
  facilitator.registerV1(
865
971
  NETWORKS,
866
972
  new ExactEvmSchemeV1(config.signer, {
867
- deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
973
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492,
974
+ simulateInSettle: config.simulateInSettle
868
975
  })
869
976
  );
870
977
  return facilitator;