@bankofai/x402-evm 2.6.0-beta.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 (65) hide show
  1. package/README.md +184 -0
  2. package/dist/cjs/exact/client/index.d.ts +53 -0
  3. package/dist/cjs/exact/client/index.js +661 -0
  4. package/dist/cjs/exact/client/index.js.map +1 -0
  5. package/dist/cjs/exact/facilitator/index.d.ts +121 -0
  6. package/dist/cjs/exact/facilitator/index.js +1458 -0
  7. package/dist/cjs/exact/facilitator/index.js.map +1 -0
  8. package/dist/cjs/exact/server/index.d.ts +125 -0
  9. package/dist/cjs/exact/server/index.js +229 -0
  10. package/dist/cjs/exact/server/index.js.map +1 -0
  11. package/dist/cjs/exact/v1/client/index.d.ts +37 -0
  12. package/dist/cjs/exact/v1/client/index.js +175 -0
  13. package/dist/cjs/exact/v1/client/index.js.map +1 -0
  14. package/dist/cjs/exact/v1/facilitator/index.d.ts +62 -0
  15. package/dist/cjs/exact/v1/facilitator/index.js +436 -0
  16. package/dist/cjs/exact/v1/facilitator/index.js.map +1 -0
  17. package/dist/cjs/exact_permit/client/index.d.ts +57 -0
  18. package/dist/cjs/exact_permit/client/index.js +223 -0
  19. package/dist/cjs/exact_permit/client/index.js.map +1 -0
  20. package/dist/cjs/exact_permit/facilitator/index.d.ts +82 -0
  21. package/dist/cjs/exact_permit/facilitator/index.js +437 -0
  22. package/dist/cjs/exact_permit/facilitator/index.js.map +1 -0
  23. package/dist/cjs/exact_permit/server/index.d.ts +71 -0
  24. package/dist/cjs/exact_permit/server/index.js +186 -0
  25. package/dist/cjs/exact_permit/server/index.js.map +1 -0
  26. package/dist/cjs/index.d.ts +76 -0
  27. package/dist/cjs/index.js +779 -0
  28. package/dist/cjs/index.js.map +1 -0
  29. package/dist/cjs/permit2-CpuKY2-F.d.ts +509 -0
  30. package/dist/cjs/permit2-D65OQ5yU.d.ts +517 -0
  31. package/dist/cjs/permit2-DvsTlAyj.d.ts +531 -0
  32. package/dist/cjs/signer-DC81R8wQ.d.ts +161 -0
  33. package/dist/cjs/v1/index.d.ts +39 -0
  34. package/dist/cjs/v1/index.js +181 -0
  35. package/dist/cjs/v1/index.js.map +1 -0
  36. package/dist/esm/chunk-4JGB4GDD.mjs +425 -0
  37. package/dist/esm/chunk-4JGB4GDD.mjs.map +1 -0
  38. package/dist/esm/chunk-TKN5V2BV.mjs +13 -0
  39. package/dist/esm/chunk-TKN5V2BV.mjs.map +1 -0
  40. package/dist/esm/chunk-UDKSVWSE.mjs +716 -0
  41. package/dist/esm/chunk-UDKSVWSE.mjs.map +1 -0
  42. package/dist/esm/exact/client/index.d.mts +53 -0
  43. package/dist/esm/exact/client/index.mjs +17 -0
  44. package/dist/esm/exact/client/index.mjs.map +1 -0
  45. package/dist/esm/exact/facilitator/index.d.mts +121 -0
  46. package/dist/esm/exact/facilitator/index.mjs +876 -0
  47. package/dist/esm/exact/facilitator/index.mjs.map +1 -0
  48. package/dist/esm/exact/server/index.d.mts +125 -0
  49. package/dist/esm/exact/server/index.mjs +201 -0
  50. package/dist/esm/exact/server/index.mjs.map +1 -0
  51. package/dist/esm/exact/v1/client/index.d.mts +37 -0
  52. package/dist/esm/exact/v1/client/index.mjs +7 -0
  53. package/dist/esm/exact/v1/client/index.mjs.map +1 -0
  54. package/dist/esm/exact/v1/facilitator/index.d.mts +62 -0
  55. package/dist/esm/exact/v1/facilitator/index.mjs +7 -0
  56. package/dist/esm/exact/v1/facilitator/index.mjs.map +1 -0
  57. package/dist/esm/index.d.mts +76 -0
  58. package/dist/esm/index.mjs +71 -0
  59. package/dist/esm/index.mjs.map +1 -0
  60. package/dist/esm/permit2-D7ni3g6s.d.mts +509 -0
  61. package/dist/esm/signer-DC81R8wQ.d.mts +161 -0
  62. package/dist/esm/v1/index.d.mts +39 -0
  63. package/dist/esm/v1/index.mjs +13 -0
  64. package/dist/esm/v1/index.mjs.map +1 -0
  65. package/package.json +128 -0
@@ -0,0 +1,876 @@
1
+ import {
2
+ isPermit2Payload
3
+ } from "../../chunk-TKN5V2BV.mjs";
4
+ import {
5
+ ExactEvmSchemeV1,
6
+ NETWORKS,
7
+ PERMIT2_ADDRESS,
8
+ authorizationTypes,
9
+ eip3009ABI,
10
+ erc20AllowanceAbi,
11
+ erc20ApproveAbi,
12
+ getEvmChainId,
13
+ permit2WitnessTypes,
14
+ x402ExactPermit2ProxyABI,
15
+ x402ExactPermit2ProxyAddress
16
+ } from "../../chunk-UDKSVWSE.mjs";
17
+
18
+ // src/exact/facilitator/eip3009.ts
19
+ import { getAddress, isAddressEqual, parseErc6492Signature, parseSignature } from "viem";
20
+ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
21
+ const payer = eip3009Payload.authorization.from;
22
+ if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
23
+ return {
24
+ isValid: false,
25
+ invalidReason: "unsupported_scheme",
26
+ payer
27
+ };
28
+ }
29
+ if (!requirements.extra?.name || !requirements.extra?.version) {
30
+ return {
31
+ isValid: false,
32
+ invalidReason: "missing_eip712_domain",
33
+ payer
34
+ };
35
+ }
36
+ const { name, version } = requirements.extra;
37
+ const erc20Address = getAddress(requirements.asset);
38
+ if (payload.accepted.network !== requirements.network) {
39
+ return {
40
+ isValid: false,
41
+ invalidReason: "network_mismatch",
42
+ payer
43
+ };
44
+ }
45
+ const permitTypedData = {
46
+ types: authorizationTypes,
47
+ primaryType: "TransferWithAuthorization",
48
+ domain: {
49
+ name,
50
+ version,
51
+ chainId: getEvmChainId(requirements.network),
52
+ verifyingContract: erc20Address
53
+ },
54
+ message: {
55
+ from: eip3009Payload.authorization.from,
56
+ to: eip3009Payload.authorization.to,
57
+ value: BigInt(eip3009Payload.authorization.value),
58
+ validAfter: BigInt(eip3009Payload.authorization.validAfter),
59
+ validBefore: BigInt(eip3009Payload.authorization.validBefore),
60
+ nonce: eip3009Payload.authorization.nonce
61
+ }
62
+ };
63
+ try {
64
+ const recoveredAddress = await signer.verifyTypedData({
65
+ address: eip3009Payload.authorization.from,
66
+ ...permitTypedData,
67
+ signature: eip3009Payload.signature
68
+ });
69
+ if (!recoveredAddress) {
70
+ return {
71
+ isValid: false,
72
+ invalidReason: "invalid_exact_evm_payload_signature",
73
+ payer
74
+ };
75
+ }
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 {
101
+ return {
102
+ isValid: false,
103
+ invalidReason: "invalid_exact_evm_payload_signature",
104
+ payer
105
+ };
106
+ }
107
+ }
108
+ if (getAddress(eip3009Payload.authorization.to) !== getAddress(requirements.payTo)) {
109
+ return {
110
+ isValid: false,
111
+ invalidReason: "invalid_exact_evm_payload_recipient_mismatch",
112
+ payer
113
+ };
114
+ }
115
+ const now = Math.floor(Date.now() / 1e3);
116
+ if (BigInt(eip3009Payload.authorization.validBefore) < BigInt(now + 6)) {
117
+ return {
118
+ isValid: false,
119
+ invalidReason: "invalid_exact_evm_payload_authorization_valid_before",
120
+ payer
121
+ };
122
+ }
123
+ if (BigInt(eip3009Payload.authorization.validAfter) > BigInt(now)) {
124
+ return {
125
+ isValid: false,
126
+ invalidReason: "invalid_exact_evm_payload_authorization_valid_after",
127
+ payer
128
+ };
129
+ }
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
+ if (BigInt(eip3009Payload.authorization.value) !== BigInt(requirements.amount)) {
148
+ return {
149
+ isValid: false,
150
+ invalidReason: "invalid_exact_evm_payload_authorization_value_mismatch",
151
+ payer
152
+ };
153
+ }
154
+ return {
155
+ isValid: true,
156
+ invalidReason: void 0,
157
+ payer
158
+ };
159
+ }
160
+ async function settleEIP3009(signer, payload, requirements, eip3009Payload, config) {
161
+ const payer = eip3009Payload.authorization.from;
162
+ const valid = await verifyEIP3009(signer, payload, requirements, eip3009Payload);
163
+ if (!valid.isValid) {
164
+ return {
165
+ success: false,
166
+ network: payload.accepted.network,
167
+ transaction: "",
168
+ errorReason: valid.invalidReason ?? "invalid_scheme",
169
+ payer
170
+ };
171
+ }
172
+ try {
173
+ const parseResult = parseErc6492Signature(eip3009Payload.signature);
174
+ const { signature, address: factoryAddress, data: factoryCalldata } = parseResult;
175
+ if (config.deployERC4337WithEIP6492 && factoryAddress && factoryCalldata && !isAddressEqual(factoryAddress, "0x0000000000000000000000000000000000000000")) {
176
+ const bytecode = await signer.getCode({ address: payer });
177
+ if (!bytecode || bytecode === "0x") {
178
+ const deployTx = await signer.sendTransaction({
179
+ to: factoryAddress,
180
+ data: factoryCalldata
181
+ });
182
+ await signer.waitForTransactionReceipt({ hash: deployTx });
183
+ }
184
+ }
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
+ }
222
+ const receipt = await signer.waitForTransactionReceipt({ hash: tx });
223
+ if (receipt.status !== "success") {
224
+ return {
225
+ success: false,
226
+ errorReason: "invalid_transaction_state",
227
+ transaction: tx,
228
+ network: payload.accepted.network,
229
+ payer
230
+ };
231
+ }
232
+ return {
233
+ success: true,
234
+ transaction: tx,
235
+ network: payload.accepted.network,
236
+ payer
237
+ };
238
+ } catch {
239
+ return {
240
+ success: false,
241
+ errorReason: "transaction_failed",
242
+ transaction: "",
243
+ network: payload.accepted.network,
244
+ payer
245
+ };
246
+ }
247
+ }
248
+
249
+ // src/exact/facilitator/permit2.ts
250
+ import {
251
+ extractEip2612GasSponsoringInfo,
252
+ validateEip2612GasSponsoringInfo,
253
+ extractErc20ApprovalGasSponsoringInfo,
254
+ ERC20_APPROVAL_GAS_SPONSORING
255
+ } from "@bankofai/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 "@bankofai/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) {
386
+ const payer = permit2Payload.permit2Authorization.from;
387
+ if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
388
+ return {
389
+ isValid: false,
390
+ invalidReason: "unsupported_scheme",
391
+ payer
392
+ };
393
+ }
394
+ if (payload.accepted.network !== requirements.network) {
395
+ return {
396
+ isValid: false,
397
+ invalidReason: "network_mismatch",
398
+ payer
399
+ };
400
+ }
401
+ const chainId = getEvmChainId(requirements.network);
402
+ const tokenAddress = getAddress3(requirements.asset);
403
+ if (getAddress3(permit2Payload.permit2Authorization.spender) !== getAddress3(x402ExactPermit2ProxyAddress)) {
404
+ return {
405
+ isValid: false,
406
+ invalidReason: "invalid_permit2_spender",
407
+ payer
408
+ };
409
+ }
410
+ if (getAddress3(permit2Payload.permit2Authorization.witness.to) !== getAddress3(requirements.payTo)) {
411
+ return {
412
+ isValid: false,
413
+ invalidReason: "invalid_permit2_recipient_mismatch",
414
+ payer
415
+ };
416
+ }
417
+ const now = Math.floor(Date.now() / 1e3);
418
+ if (BigInt(permit2Payload.permit2Authorization.deadline) < BigInt(now + 6)) {
419
+ return {
420
+ isValid: false,
421
+ invalidReason: "permit2_deadline_expired",
422
+ payer
423
+ };
424
+ }
425
+ if (BigInt(permit2Payload.permit2Authorization.witness.validAfter) > BigInt(now)) {
426
+ return {
427
+ isValid: false,
428
+ invalidReason: "permit2_not_yet_valid",
429
+ payer
430
+ };
431
+ }
432
+ if (BigInt(permit2Payload.permit2Authorization.permitted.amount) !== BigInt(requirements.amount)) {
433
+ return {
434
+ isValid: false,
435
+ invalidReason: "permit2_amount_mismatch",
436
+ payer
437
+ };
438
+ }
439
+ if (getAddress3(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
440
+ return {
441
+ isValid: false,
442
+ invalidReason: "permit2_token_mismatch",
443
+ payer
444
+ };
445
+ }
446
+ const permit2TypedData = {
447
+ types: permit2WitnessTypes,
448
+ primaryType: "PermitWitnessTransferFrom",
449
+ domain: {
450
+ name: "Permit2",
451
+ chainId,
452
+ verifyingContract: PERMIT2_ADDRESS
453
+ },
454
+ message: {
455
+ permitted: {
456
+ token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
457
+ amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
458
+ },
459
+ spender: getAddress3(permit2Payload.permit2Authorization.spender),
460
+ nonce: BigInt(permit2Payload.permit2Authorization.nonce),
461
+ deadline: BigInt(permit2Payload.permit2Authorization.deadline),
462
+ witness: {
463
+ to: getAddress3(permit2Payload.permit2Authorization.witness.to),
464
+ validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
465
+ }
466
+ }
467
+ };
468
+ try {
469
+ const isValid = await signer.verifyTypedData({
470
+ address: payer,
471
+ ...permit2TypedData,
472
+ signature: permit2Payload.signature
473
+ });
474
+ if (!isValid) {
475
+ return {
476
+ isValid: false,
477
+ invalidReason: "invalid_permit2_signature",
478
+ payer
479
+ };
480
+ }
481
+ } catch {
482
+ return {
483
+ isValid: false,
484
+ invalidReason: "invalid_permit2_signature",
485
+ payer
486
+ };
487
+ }
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)) {
507
+ return {
508
+ 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.`,
511
+ payer
512
+ };
513
+ }
514
+ } catch {
515
+ }
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;
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) {
567
+ const payer = permit2Payload.permit2Authorization.from;
568
+ const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
569
+ if (!valid.isValid) {
570
+ return {
571
+ success: false,
572
+ network: payload.accepted.network,
573
+ transaction: "",
574
+ errorReason: valid.invalidReason ?? "invalid_scheme",
575
+ payer
576
+ };
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;
600
+ try {
601
+ const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
602
+ const tx = await signer.writeContract({
603
+ address: x402ExactPermit2ProxyAddress,
604
+ abi: x402ExactPermit2ProxyABI,
605
+ functionName: "settleWithPermit",
606
+ args: [
607
+ {
608
+ value: BigInt(eip2612Info.amount),
609
+ deadline: BigInt(eip2612Info.deadline),
610
+ r,
611
+ s,
612
+ v
613
+ },
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
628
+ ]
629
+ });
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") {
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);
675
+ } catch (error) {
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") {
711
+ return {
712
+ success: false,
713
+ errorReason: "invalid_transaction_state",
714
+ transaction: tx,
715
+ network: payload.accepted.network,
716
+ payer
717
+ };
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 };
786
+ }
787
+
788
+ // src/exact/facilitator/scheme.ts
789
+ var ExactEvmScheme = class {
790
+ /**
791
+ * Creates a new ExactEvmScheme facilitator instance.
792
+ *
793
+ * @param signer - The EVM signer for facilitator operations
794
+ * @param config - Optional configuration
795
+ */
796
+ constructor(signer, config) {
797
+ this.signer = signer;
798
+ this.scheme = "exact";
799
+ this.caipFamily = "eip155:*";
800
+ this.config = {
801
+ deployERC4337WithEIP6492: config?.deployERC4337WithEIP6492 ?? false
802
+ };
803
+ }
804
+ /**
805
+ * Returns undefined — EVM has no mechanism-specific extra data.
806
+ *
807
+ * @param _ - The network identifier (unused)
808
+ * @returns undefined
809
+ */
810
+ getExtra(_) {
811
+ return void 0;
812
+ }
813
+ /**
814
+ * Returns facilitator wallet addresses for the supported response.
815
+ *
816
+ * @param _ - The network identifier (unused, addresses are network-agnostic)
817
+ * @returns Array of facilitator wallet addresses
818
+ */
819
+ getSigners(_) {
820
+ return [...this.signer.getAddresses()];
821
+ }
822
+ /**
823
+ * Verifies a payment payload. Routes to Permit2 or EIP-3009 based on payload type.
824
+ *
825
+ * @param payload - The payment payload to verify
826
+ * @param requirements - The payment requirements
827
+ * @param context - Optional facilitator context for extension capabilities
828
+ * @returns Promise resolving to verification response
829
+ */
830
+ async verify(payload, requirements, context) {
831
+ const rawPayload = payload.payload;
832
+ if (isPermit2Payload(rawPayload)) {
833
+ return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
834
+ }
835
+ const eip3009Payload = rawPayload;
836
+ return verifyEIP3009(this.signer, payload, requirements, eip3009Payload);
837
+ }
838
+ /**
839
+ * Settles a payment. Routes to Permit2 or EIP-3009 based on payload type.
840
+ *
841
+ * @param payload - The payment payload to settle
842
+ * @param requirements - The payment requirements
843
+ * @param context - Optional facilitator context for extension capabilities
844
+ * @returns Promise resolving to settlement response
845
+ */
846
+ async settle(payload, requirements, context) {
847
+ const rawPayload = payload.payload;
848
+ if (isPermit2Payload(rawPayload)) {
849
+ return settlePermit2(this.signer, payload, requirements, rawPayload, context);
850
+ }
851
+ const eip3009Payload = rawPayload;
852
+ return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);
853
+ }
854
+ };
855
+
856
+ // src/exact/facilitator/register.ts
857
+ function registerExactEvmScheme(facilitator, config) {
858
+ facilitator.register(
859
+ config.networks,
860
+ new ExactEvmScheme(config.signer, {
861
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
862
+ })
863
+ );
864
+ facilitator.registerV1(
865
+ NETWORKS,
866
+ new ExactEvmSchemeV1(config.signer, {
867
+ deployERC4337WithEIP6492: config.deployERC4337WithEIP6492
868
+ })
869
+ );
870
+ return facilitator;
871
+ }
872
+ export {
873
+ ExactEvmScheme,
874
+ registerExactEvmScheme
875
+ };
876
+ //# sourceMappingURL=index.mjs.map