@x402/evm 2.3.1 → 2.5.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.
- package/dist/cjs/exact/client/index.d.ts +2 -2
- package/dist/cjs/exact/client/index.js +412 -169
- package/dist/cjs/exact/client/index.js.map +1 -1
- package/dist/cjs/exact/facilitator/index.d.ts +18 -18
- package/dist/cjs/exact/facilitator/index.js +467 -157
- 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 +17 -18
- package/dist/cjs/exact/v1/client/index.js.map +1 -1
- package/dist/cjs/exact/v1/facilitator/index.d.ts +1 -1
- package/dist/cjs/exact/v1/facilitator/index.js +7 -8
- package/dist/cjs/exact/v1/facilitator/index.js.map +1 -1
- package/dist/cjs/index.d.ts +3 -390
- package/dist/cjs/index.js +363 -110
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/permit2-CQbXqCMC.d.ts +517 -0
- package/dist/cjs/signer-DC81R8wQ.d.ts +161 -0
- package/dist/cjs/v1/index.d.ts +11 -2
- package/dist/cjs/v1/index.js +14 -11
- package/dist/cjs/v1/index.js.map +1 -1
- package/dist/esm/{chunk-RPL6OFJL.mjs → chunk-7KHQD5KT.mjs} +92 -37
- package/dist/esm/chunk-7KHQD5KT.mjs.map +1 -0
- package/dist/esm/chunk-GY6X5A3G.mjs +425 -0
- package/dist/esm/chunk-GY6X5A3G.mjs.map +1 -0
- package/dist/esm/{chunk-PFULIQAE.mjs → chunk-TKN5V2BV.mjs} +1 -1
- package/dist/esm/chunk-TKN5V2BV.mjs.map +1 -0
- package/dist/esm/exact/client/index.d.mts +2 -2
- package/dist/esm/exact/client/index.mjs +4 -3
- package/dist/esm/exact/facilitator/index.d.mts +18 -18
- package/dist/esm/exact/facilitator/index.mjs +401 -99
- 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 +2 -2
- package/dist/esm/exact/v1/facilitator/index.d.mts +1 -1
- package/dist/esm/exact/v1/facilitator/index.mjs +2 -2
- package/dist/esm/index.d.mts +3 -390
- package/dist/esm/index.mjs +29 -6
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/permit2-CGOcN7Et.d.mts +517 -0
- package/dist/esm/signer-DC81R8wQ.d.mts +161 -0
- package/dist/esm/v1/index.d.mts +11 -2
- package/dist/esm/v1/index.mjs +6 -4
- package/package.json +3 -2
- package/dist/cjs/permit2-BYv82va2.d.ts +0 -103
- package/dist/cjs/signer-5OVDxViv.d.ts +0 -79
- package/dist/esm/chunk-E2YMUI3X.mjs +0 -229
- package/dist/esm/chunk-E2YMUI3X.mjs.map +0 -1
- package/dist/esm/chunk-PFULIQAE.mjs.map +0 -1
- package/dist/esm/chunk-RPL6OFJL.mjs.map +0 -1
- package/dist/esm/permit2-BsAoJiWD.d.mts +0 -103
- package/dist/esm/signer-5OVDxViv.d.mts +0 -79
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isPermit2Payload
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-TKN5V2BV.mjs";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
ExactEvmSchemeV1,
|
|
6
6
|
NETWORKS,
|
|
7
7
|
PERMIT2_ADDRESS,
|
|
8
8
|
authorizationTypes,
|
|
9
9
|
eip3009ABI,
|
|
10
|
+
erc20AllowanceAbi,
|
|
11
|
+
erc20ApproveAbi,
|
|
12
|
+
getEvmChainId,
|
|
10
13
|
permit2WitnessTypes,
|
|
11
14
|
x402ExactPermit2ProxyABI,
|
|
12
15
|
x402ExactPermit2ProxyAddress
|
|
13
|
-
} from "../../chunk-
|
|
16
|
+
} from "../../chunk-7KHQD5KT.mjs";
|
|
14
17
|
|
|
15
18
|
// src/exact/facilitator/eip3009.ts
|
|
16
19
|
import { getAddress, isAddressEqual, parseErc6492Signature, parseSignature } from "viem";
|
|
@@ -45,7 +48,7 @@ async function verifyEIP3009(signer, payload, requirements, eip3009Payload) {
|
|
|
45
48
|
domain: {
|
|
46
49
|
name,
|
|
47
50
|
version,
|
|
48
|
-
chainId:
|
|
51
|
+
chainId: getEvmChainId(requirements.network),
|
|
49
52
|
verifyingContract: erc20Address
|
|
50
53
|
},
|
|
51
54
|
message: {
|
|
@@ -244,20 +247,142 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
|
|
|
244
247
|
}
|
|
245
248
|
|
|
246
249
|
// src/exact/facilitator/permit2.ts
|
|
247
|
-
import {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
250
|
+
import {
|
|
251
|
+
extractEip2612GasSponsoringInfo,
|
|
252
|
+
validateEip2612GasSponsoringInfo,
|
|
253
|
+
extractErc20ApprovalGasSponsoringInfo,
|
|
254
|
+
ERC20_APPROVAL_GAS_SPONSORING
|
|
255
|
+
} from "@x402/extensions";
|
|
256
|
+
import { getAddress as getAddress3 } from "viem";
|
|
257
|
+
|
|
258
|
+
// src/exact/facilitator/errors.ts
|
|
259
|
+
var ErrPermit2InvalidSignature = "invalid_permit2_signature";
|
|
260
|
+
var ErrPermit2InvalidAmount = "permit2_invalid_amount";
|
|
261
|
+
var ErrPermit2InvalidDestination = "permit2_invalid_destination";
|
|
262
|
+
var ErrPermit2InvalidOwner = "permit2_invalid_owner";
|
|
263
|
+
var ErrPermit2PaymentTooEarly = "permit2_payment_too_early";
|
|
264
|
+
var ErrPermit2InvalidNonce = "permit2_invalid_nonce";
|
|
265
|
+
var ErrPermit2612AmountMismatch = "permit2_2612_amount_mismatch";
|
|
266
|
+
var ErrErc20ApprovalInvalidFormat = "invalid_erc20_approval_extension_format";
|
|
267
|
+
var ErrErc20ApprovalFromMismatch = "erc20_approval_from_mismatch";
|
|
268
|
+
var ErrErc20ApprovalAssetMismatch = "erc20_approval_asset_mismatch";
|
|
269
|
+
var ErrErc20ApprovalSpenderNotPermit2 = "erc20_approval_spender_not_permit2";
|
|
270
|
+
var ErrErc20ApprovalTxWrongTarget = "erc20_approval_tx_wrong_target";
|
|
271
|
+
var ErrErc20ApprovalTxWrongSelector = "erc20_approval_tx_wrong_selector";
|
|
272
|
+
var ErrErc20ApprovalTxWrongSpender = "erc20_approval_tx_wrong_spender";
|
|
273
|
+
var ErrErc20ApprovalTxInvalidCalldata = "erc20_approval_tx_invalid_calldata";
|
|
274
|
+
var ErrErc20ApprovalTxSignerMismatch = "erc20_approval_tx_signer_mismatch";
|
|
275
|
+
var ErrErc20ApprovalTxInvalidSignature = "erc20_approval_tx_invalid_signature";
|
|
276
|
+
var ErrErc20ApprovalTxParseFailed = "erc20_approval_tx_parse_failed";
|
|
277
|
+
|
|
278
|
+
// src/exact/facilitator/erc20approval.ts
|
|
279
|
+
import {
|
|
280
|
+
getAddress as getAddress2,
|
|
281
|
+
parseTransaction,
|
|
282
|
+
decodeFunctionData,
|
|
283
|
+
recoverTransactionAddress
|
|
284
|
+
} from "viem";
|
|
285
|
+
import {
|
|
286
|
+
validateErc20ApprovalGasSponsoringInfo
|
|
287
|
+
} from "@x402/extensions";
|
|
288
|
+
var APPROVE_SELECTOR = "0x095ea7b3";
|
|
289
|
+
async function validateErc20ApprovalForPayment(info, payer, tokenAddress) {
|
|
290
|
+
if (!validateErc20ApprovalGasSponsoringInfo(info)) {
|
|
291
|
+
return {
|
|
292
|
+
isValid: false,
|
|
293
|
+
invalidReason: ErrErc20ApprovalInvalidFormat,
|
|
294
|
+
invalidMessage: "ERC-20 approval extension info failed schema validation"
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (getAddress2(info.from) !== getAddress2(payer)) {
|
|
298
|
+
return {
|
|
299
|
+
isValid: false,
|
|
300
|
+
invalidReason: ErrErc20ApprovalFromMismatch,
|
|
301
|
+
invalidMessage: `Expected from=${payer}, got ${info.from}`
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
if (getAddress2(info.asset) !== tokenAddress) {
|
|
305
|
+
return {
|
|
306
|
+
isValid: false,
|
|
307
|
+
invalidReason: ErrErc20ApprovalAssetMismatch,
|
|
308
|
+
invalidMessage: `Expected asset=${tokenAddress}, got ${info.asset}`
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (getAddress2(info.spender) !== getAddress2(PERMIT2_ADDRESS)) {
|
|
312
|
+
return {
|
|
313
|
+
isValid: false,
|
|
314
|
+
invalidReason: ErrErc20ApprovalSpenderNotPermit2,
|
|
315
|
+
invalidMessage: `Expected spender=${PERMIT2_ADDRESS}, got ${info.spender}`
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const serializedTx = info.signedTransaction;
|
|
320
|
+
const tx = parseTransaction(serializedTx);
|
|
321
|
+
if (!tx.to || getAddress2(tx.to) !== tokenAddress) {
|
|
322
|
+
return {
|
|
323
|
+
isValid: false,
|
|
324
|
+
invalidReason: ErrErc20ApprovalTxWrongTarget,
|
|
325
|
+
invalidMessage: `Transaction targets ${tx.to ?? "null"}, expected ${tokenAddress}`
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
const data = tx.data ?? "0x";
|
|
329
|
+
if (!data.startsWith(APPROVE_SELECTOR)) {
|
|
330
|
+
return {
|
|
331
|
+
isValid: false,
|
|
332
|
+
invalidReason: ErrErc20ApprovalTxWrongSelector,
|
|
333
|
+
invalidMessage: `Transaction calldata does not start with approve() selector ${APPROVE_SELECTOR}`
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const decoded = decodeFunctionData({
|
|
338
|
+
abi: erc20ApproveAbi,
|
|
339
|
+
data
|
|
340
|
+
});
|
|
341
|
+
const calldataSpender = getAddress2(decoded.args[0]);
|
|
342
|
+
if (calldataSpender !== getAddress2(PERMIT2_ADDRESS)) {
|
|
343
|
+
return {
|
|
344
|
+
isValid: false,
|
|
345
|
+
invalidReason: ErrErc20ApprovalTxWrongSpender,
|
|
346
|
+
invalidMessage: `approve() spender is ${calldataSpender}, expected Permit2 ${PERMIT2_ADDRESS}`
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
return {
|
|
351
|
+
isValid: false,
|
|
352
|
+
invalidReason: ErrErc20ApprovalTxInvalidCalldata,
|
|
353
|
+
invalidMessage: "Failed to decode approve() calldata from the signed transaction"
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const recoveredAddress = await recoverTransactionAddress({
|
|
358
|
+
serializedTransaction: serializedTx
|
|
359
|
+
});
|
|
360
|
+
if (getAddress2(recoveredAddress) !== getAddress2(payer)) {
|
|
361
|
+
return {
|
|
362
|
+
isValid: false,
|
|
363
|
+
invalidReason: ErrErc20ApprovalTxSignerMismatch,
|
|
364
|
+
invalidMessage: `Transaction signed by ${recoveredAddress}, expected payer ${payer}`
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
} catch {
|
|
368
|
+
return {
|
|
369
|
+
isValid: false,
|
|
370
|
+
invalidReason: ErrErc20ApprovalTxInvalidSignature,
|
|
371
|
+
invalidMessage: "Failed to recover signer from the signed transaction"
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
} catch {
|
|
375
|
+
return {
|
|
376
|
+
isValid: false,
|
|
377
|
+
invalidReason: ErrErc20ApprovalTxParseFailed,
|
|
378
|
+
invalidMessage: "Failed to parse the signed transaction"
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return { isValid: true };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/exact/facilitator/permit2.ts
|
|
385
|
+
async function verifyPermit2(signer, payload, requirements, permit2Payload, context) {
|
|
261
386
|
const payer = permit2Payload.permit2Authorization.from;
|
|
262
387
|
if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
|
|
263
388
|
return {
|
|
@@ -273,16 +398,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
273
398
|
payer
|
|
274
399
|
};
|
|
275
400
|
}
|
|
276
|
-
const chainId =
|
|
277
|
-
const tokenAddress =
|
|
278
|
-
if (
|
|
401
|
+
const chainId = getEvmChainId(requirements.network);
|
|
402
|
+
const tokenAddress = getAddress3(requirements.asset);
|
|
403
|
+
if (getAddress3(permit2Payload.permit2Authorization.spender) !== getAddress3(x402ExactPermit2ProxyAddress)) {
|
|
279
404
|
return {
|
|
280
405
|
isValid: false,
|
|
281
406
|
invalidReason: "invalid_permit2_spender",
|
|
282
407
|
payer
|
|
283
408
|
};
|
|
284
409
|
}
|
|
285
|
-
if (
|
|
410
|
+
if (getAddress3(permit2Payload.permit2Authorization.witness.to) !== getAddress3(requirements.payTo)) {
|
|
286
411
|
return {
|
|
287
412
|
isValid: false,
|
|
288
413
|
invalidReason: "invalid_permit2_recipient_mismatch",
|
|
@@ -311,7 +436,7 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
311
436
|
payer
|
|
312
437
|
};
|
|
313
438
|
}
|
|
314
|
-
if (
|
|
439
|
+
if (getAddress3(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
|
|
315
440
|
return {
|
|
316
441
|
isValid: false,
|
|
317
442
|
invalidReason: "permit2_token_mismatch",
|
|
@@ -328,16 +453,15 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
328
453
|
},
|
|
329
454
|
message: {
|
|
330
455
|
permitted: {
|
|
331
|
-
token:
|
|
456
|
+
token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
|
|
332
457
|
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
333
458
|
},
|
|
334
|
-
spender:
|
|
459
|
+
spender: getAddress3(permit2Payload.permit2Authorization.spender),
|
|
335
460
|
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
336
461
|
deadline: BigInt(permit2Payload.permit2Authorization.deadline),
|
|
337
462
|
witness: {
|
|
338
|
-
to:
|
|
339
|
-
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
340
|
-
extra: permit2Payload.permit2Authorization.witness.extra
|
|
463
|
+
to: getAddress3(permit2Payload.permit2Authorization.witness.to),
|
|
464
|
+
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
341
465
|
}
|
|
342
466
|
}
|
|
343
467
|
};
|
|
@@ -361,21 +485,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
361
485
|
payer
|
|
362
486
|
};
|
|
363
487
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
invalidReason: "permit2_allowance_required",
|
|
375
|
-
payer
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
} catch {
|
|
488
|
+
const allowanceResult = await _verifyPermit2Allowance(
|
|
489
|
+
signer,
|
|
490
|
+
payload,
|
|
491
|
+
requirements,
|
|
492
|
+
payer,
|
|
493
|
+
tokenAddress,
|
|
494
|
+
context
|
|
495
|
+
);
|
|
496
|
+
if (allowanceResult) {
|
|
497
|
+
return allowanceResult;
|
|
379
498
|
}
|
|
380
499
|
try {
|
|
381
500
|
const balance = await signer.readContract({
|
|
@@ -400,9 +519,53 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
400
519
|
payer
|
|
401
520
|
};
|
|
402
521
|
}
|
|
403
|
-
async function
|
|
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) {
|
|
404
567
|
const payer = permit2Payload.permit2Authorization.from;
|
|
405
|
-
const valid = await verifyPermit2(signer, payload, requirements, permit2Payload);
|
|
568
|
+
const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
|
|
406
569
|
if (!valid.isValid) {
|
|
407
570
|
return {
|
|
408
571
|
success: false,
|
|
@@ -412,82 +575,223 @@ async function settlePermit2(signer, payload, requirements, permit2Payload) {
|
|
|
412
575
|
payer
|
|
413
576
|
};
|
|
414
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;
|
|
415
600
|
try {
|
|
601
|
+
const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
|
|
416
602
|
const tx = await signer.writeContract({
|
|
417
603
|
address: x402ExactPermit2ProxyAddress,
|
|
418
604
|
abi: x402ExactPermit2ProxyABI,
|
|
419
|
-
functionName: "
|
|
605
|
+
functionName: "settleWithPermit",
|
|
420
606
|
args: [
|
|
607
|
+
{
|
|
608
|
+
value: BigInt(eip2612Info.amount),
|
|
609
|
+
deadline: BigInt(eip2612Info.deadline),
|
|
610
|
+
r,
|
|
611
|
+
s,
|
|
612
|
+
v
|
|
613
|
+
},
|
|
421
614
|
{
|
|
422
615
|
permitted: {
|
|
423
|
-
token:
|
|
616
|
+
token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
|
|
424
617
|
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
425
618
|
},
|
|
426
619
|
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
427
620
|
deadline: BigInt(permit2Payload.permit2Authorization.deadline)
|
|
428
621
|
},
|
|
429
|
-
|
|
622
|
+
getAddress3(payer),
|
|
430
623
|
{
|
|
431
|
-
to:
|
|
432
|
-
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
433
|
-
extra: permit2Payload.permit2Authorization.witness.extra
|
|
624
|
+
to: getAddress3(permit2Payload.permit2Authorization.witness.to),
|
|
625
|
+
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
434
626
|
},
|
|
435
627
|
permit2Payload.signature
|
|
436
628
|
]
|
|
437
629
|
});
|
|
438
|
-
|
|
439
|
-
|
|
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") {
|
|
440
645
|
return {
|
|
441
646
|
success: false,
|
|
442
|
-
errorReason: "
|
|
443
|
-
transaction:
|
|
647
|
+
errorReason: "erc20_approval_tx_failed",
|
|
648
|
+
transaction: approvalTxHash,
|
|
444
649
|
network: payload.accepted.network,
|
|
445
650
|
payer
|
|
446
651
|
};
|
|
447
652
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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);
|
|
454
675
|
} catch (error) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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") {
|
|
474
711
|
return {
|
|
475
712
|
success: false,
|
|
476
|
-
errorReason,
|
|
477
|
-
transaction:
|
|
713
|
+
errorReason: "invalid_transaction_state",
|
|
714
|
+
transaction: tx,
|
|
478
715
|
network: payload.accepted.network,
|
|
479
716
|
payer
|
|
480
717
|
};
|
|
481
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 };
|
|
482
786
|
}
|
|
483
787
|
|
|
484
788
|
// src/exact/facilitator/scheme.ts
|
|
485
789
|
var ExactEvmScheme = class {
|
|
486
790
|
/**
|
|
487
|
-
* Creates a new
|
|
791
|
+
* Creates a new ExactEvmScheme facilitator instance.
|
|
488
792
|
*
|
|
489
793
|
* @param signer - The EVM signer for facilitator operations
|
|
490
|
-
* @param config - Optional configuration
|
|
794
|
+
* @param config - Optional configuration
|
|
491
795
|
*/
|
|
492
796
|
constructor(signer, config) {
|
|
493
797
|
this.signer = signer;
|
|
@@ -498,53 +802,51 @@ var ExactEvmScheme = class {
|
|
|
498
802
|
};
|
|
499
803
|
}
|
|
500
804
|
/**
|
|
501
|
-
*
|
|
502
|
-
* For EVM, no extra data is needed.
|
|
805
|
+
* Returns undefined — EVM has no mechanism-specific extra data.
|
|
503
806
|
*
|
|
504
|
-
* @param _ - The network identifier (unused
|
|
505
|
-
* @returns undefined
|
|
807
|
+
* @param _ - The network identifier (unused)
|
|
808
|
+
* @returns undefined
|
|
506
809
|
*/
|
|
507
810
|
getExtra(_) {
|
|
508
811
|
return void 0;
|
|
509
812
|
}
|
|
510
813
|
/**
|
|
511
|
-
*
|
|
512
|
-
* Returns all addresses this facilitator can use for signing/settling transactions.
|
|
814
|
+
* Returns facilitator wallet addresses for the supported response.
|
|
513
815
|
*
|
|
514
|
-
* @param _ - The network identifier (unused
|
|
816
|
+
* @param _ - The network identifier (unused, addresses are network-agnostic)
|
|
515
817
|
* @returns Array of facilitator wallet addresses
|
|
516
818
|
*/
|
|
517
819
|
getSigners(_) {
|
|
518
820
|
return [...this.signer.getAddresses()];
|
|
519
821
|
}
|
|
520
822
|
/**
|
|
521
|
-
* Verifies a payment payload.
|
|
522
|
-
* Routes to the appropriate verification logic based on payload type.
|
|
823
|
+
* Verifies a payment payload. Routes to Permit2 or EIP-3009 based on payload type.
|
|
523
824
|
*
|
|
524
825
|
* @param payload - The payment payload to verify
|
|
525
826
|
* @param requirements - The payment requirements
|
|
827
|
+
* @param context - Optional facilitator context for extension capabilities
|
|
526
828
|
* @returns Promise resolving to verification response
|
|
527
829
|
*/
|
|
528
|
-
async verify(payload, requirements) {
|
|
830
|
+
async verify(payload, requirements, context) {
|
|
529
831
|
const rawPayload = payload.payload;
|
|
530
832
|
if (isPermit2Payload(rawPayload)) {
|
|
531
|
-
return verifyPermit2(this.signer, payload, requirements, rawPayload);
|
|
833
|
+
return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
|
|
532
834
|
}
|
|
533
835
|
const eip3009Payload = rawPayload;
|
|
534
836
|
return verifyEIP3009(this.signer, payload, requirements, eip3009Payload);
|
|
535
837
|
}
|
|
536
838
|
/**
|
|
537
|
-
* Settles a payment
|
|
538
|
-
* Routes to the appropriate settlement logic based on payload type.
|
|
839
|
+
* Settles a payment. Routes to Permit2 or EIP-3009 based on payload type.
|
|
539
840
|
*
|
|
540
841
|
* @param payload - The payment payload to settle
|
|
541
842
|
* @param requirements - The payment requirements
|
|
843
|
+
* @param context - Optional facilitator context for extension capabilities
|
|
542
844
|
* @returns Promise resolving to settlement response
|
|
543
845
|
*/
|
|
544
|
-
async settle(payload, requirements) {
|
|
846
|
+
async settle(payload, requirements, context) {
|
|
545
847
|
const rawPayload = payload.payload;
|
|
546
848
|
if (isPermit2Payload(rawPayload)) {
|
|
547
|
-
return settlePermit2(this.signer, payload, requirements, rawPayload);
|
|
849
|
+
return settlePermit2(this.signer, payload, requirements, rawPayload, context);
|
|
548
850
|
}
|
|
549
851
|
const eip3009Payload = rawPayload;
|
|
550
852
|
return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);
|