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