@x402/evm 2.4.0 → 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 +284 -192
- 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 +454 -234
- 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 +225 -132
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/permit2-CQbXqCMC.d.ts +517 -0
- package/dist/cjs/{signer-3KGwtdNT.d.ts → signer-DC81R8wQ.d.ts} +37 -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-GSU4DHTC.mjs → chunk-7KHQD5KT.mjs} +72 -37
- package/dist/esm/chunk-7KHQD5KT.mjs.map +1 -0
- package/dist/esm/{chunk-CJHYX7BQ.mjs → chunk-GY6X5A3G.mjs} +119 -56
- 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 +385 -176
- 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 +18 -5
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/permit2-CGOcN7Et.d.mts +517 -0
- package/dist/esm/{signer-3KGwtdNT.d.mts → signer-DC81R8wQ.d.mts} +37 -0
- package/dist/esm/v1/index.d.mts +11 -2
- package/dist/esm/v1/index.mjs +6 -4
- package/package.json +3 -3
- package/dist/cjs/permit2-CzKPU5By.d.ts +0 -130
- package/dist/esm/chunk-CJHYX7BQ.mjs.map +0 -1
- package/dist/esm/chunk-GSU4DHTC.mjs.map +0 -1
- package/dist/esm/chunk-PFULIQAE.mjs.map +0 -1
- package/dist/esm/permit2-C2FkNEHT.d.mts +0 -130
|
@@ -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: {
|
|
@@ -246,22 +249,140 @@ async function settleEIP3009(signer, payload, requirements, eip3009Payload, conf
|
|
|
246
249
|
// src/exact/facilitator/permit2.ts
|
|
247
250
|
import {
|
|
248
251
|
extractEip2612GasSponsoringInfo,
|
|
249
|
-
validateEip2612GasSponsoringInfo
|
|
252
|
+
validateEip2612GasSponsoringInfo,
|
|
253
|
+
extractErc20ApprovalGasSponsoringInfo,
|
|
254
|
+
ERC20_APPROVAL_GAS_SPONSORING
|
|
250
255
|
} from "@x402/extensions";
|
|
251
|
-
import { getAddress as
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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) {
|
|
265
386
|
const payer = permit2Payload.permit2Authorization.from;
|
|
266
387
|
if (payload.accepted.scheme !== "exact" || requirements.scheme !== "exact") {
|
|
267
388
|
return {
|
|
@@ -277,16 +398,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
277
398
|
payer
|
|
278
399
|
};
|
|
279
400
|
}
|
|
280
|
-
const chainId =
|
|
281
|
-
const tokenAddress =
|
|
282
|
-
if (
|
|
401
|
+
const chainId = getEvmChainId(requirements.network);
|
|
402
|
+
const tokenAddress = getAddress3(requirements.asset);
|
|
403
|
+
if (getAddress3(permit2Payload.permit2Authorization.spender) !== getAddress3(x402ExactPermit2ProxyAddress)) {
|
|
283
404
|
return {
|
|
284
405
|
isValid: false,
|
|
285
406
|
invalidReason: "invalid_permit2_spender",
|
|
286
407
|
payer
|
|
287
408
|
};
|
|
288
409
|
}
|
|
289
|
-
if (
|
|
410
|
+
if (getAddress3(permit2Payload.permit2Authorization.witness.to) !== getAddress3(requirements.payTo)) {
|
|
290
411
|
return {
|
|
291
412
|
isValid: false,
|
|
292
413
|
invalidReason: "invalid_permit2_recipient_mismatch",
|
|
@@ -315,7 +436,7 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
315
436
|
payer
|
|
316
437
|
};
|
|
317
438
|
}
|
|
318
|
-
if (
|
|
439
|
+
if (getAddress3(permit2Payload.permit2Authorization.permitted.token) !== tokenAddress) {
|
|
319
440
|
return {
|
|
320
441
|
isValid: false,
|
|
321
442
|
invalidReason: "permit2_token_mismatch",
|
|
@@ -332,16 +453,15 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
332
453
|
},
|
|
333
454
|
message: {
|
|
334
455
|
permitted: {
|
|
335
|
-
token:
|
|
456
|
+
token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
|
|
336
457
|
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
337
458
|
},
|
|
338
|
-
spender:
|
|
459
|
+
spender: getAddress3(permit2Payload.permit2Authorization.spender),
|
|
339
460
|
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
340
461
|
deadline: BigInt(permit2Payload.permit2Authorization.deadline),
|
|
341
462
|
witness: {
|
|
342
|
-
to:
|
|
343
|
-
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
344
|
-
extra: permit2Payload.permit2Authorization.witness.extra
|
|
463
|
+
to: getAddress3(permit2Payload.permit2Authorization.witness.to),
|
|
464
|
+
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
345
465
|
}
|
|
346
466
|
}
|
|
347
467
|
};
|
|
@@ -365,44 +485,16 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
365
485
|
payer
|
|
366
486
|
};
|
|
367
487
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
const validationResult = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
|
|
379
|
-
if (!validationResult.isValid) {
|
|
380
|
-
return {
|
|
381
|
-
isValid: false,
|
|
382
|
-
invalidReason: validationResult.invalidReason,
|
|
383
|
-
payer
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
} else {
|
|
387
|
-
return {
|
|
388
|
-
isValid: false,
|
|
389
|
-
invalidReason: "permit2_allowance_required",
|
|
390
|
-
payer
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
} catch {
|
|
395
|
-
const eip2612Info = extractEip2612GasSponsoringInfo(payload);
|
|
396
|
-
if (eip2612Info) {
|
|
397
|
-
const validationResult = validateEip2612PermitForPayment(eip2612Info, payer, tokenAddress);
|
|
398
|
-
if (!validationResult.isValid) {
|
|
399
|
-
return {
|
|
400
|
-
isValid: false,
|
|
401
|
-
invalidReason: validationResult.invalidReason,
|
|
402
|
-
payer
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
}
|
|
488
|
+
const allowanceResult = await _verifyPermit2Allowance(
|
|
489
|
+
signer,
|
|
490
|
+
payload,
|
|
491
|
+
requirements,
|
|
492
|
+
payer,
|
|
493
|
+
tokenAddress,
|
|
494
|
+
context
|
|
495
|
+
);
|
|
496
|
+
if (allowanceResult) {
|
|
497
|
+
return allowanceResult;
|
|
406
498
|
}
|
|
407
499
|
try {
|
|
408
500
|
const balance = await signer.readContract({
|
|
@@ -427,9 +519,53 @@ async function verifyPermit2(signer, payload, requirements, permit2Payload) {
|
|
|
427
519
|
payer
|
|
428
520
|
};
|
|
429
521
|
}
|
|
430
|
-
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) {
|
|
431
567
|
const payer = permit2Payload.permit2Authorization.from;
|
|
432
|
-
const valid = await verifyPermit2(signer, payload, requirements, permit2Payload);
|
|
568
|
+
const valid = await verifyPermit2(signer, payload, requirements, permit2Payload, context);
|
|
433
569
|
if (!valid.isValid) {
|
|
434
570
|
return {
|
|
435
571
|
success: false,
|
|
@@ -439,120 +575,195 @@ async function settlePermit2(signer, payload, requirements, permit2Payload) {
|
|
|
439
575
|
payer
|
|
440
576
|
};
|
|
441
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;
|
|
442
600
|
try {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
permitted: {
|
|
461
|
-
token: getAddress2(permit2Payload.permit2Authorization.permitted.token),
|
|
462
|
-
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
463
|
-
},
|
|
464
|
-
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
465
|
-
deadline: BigInt(permit2Payload.permit2Authorization.deadline)
|
|
466
|
-
},
|
|
467
|
-
getAddress2(payer),
|
|
468
|
-
{
|
|
469
|
-
to: getAddress2(permit2Payload.permit2Authorization.witness.to),
|
|
470
|
-
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter),
|
|
471
|
-
extra: permit2Payload.permit2Authorization.witness.extra
|
|
472
|
-
},
|
|
473
|
-
permit2Payload.signature
|
|
474
|
-
]
|
|
475
|
-
});
|
|
476
|
-
} else {
|
|
477
|
-
tx = await signer.writeContract({
|
|
478
|
-
address: x402ExactPermit2ProxyAddress,
|
|
479
|
-
abi: x402ExactPermit2ProxyABI,
|
|
480
|
-
functionName: "settle",
|
|
481
|
-
args: [
|
|
482
|
-
{
|
|
483
|
-
permitted: {
|
|
484
|
-
token: getAddress2(permit2Payload.permit2Authorization.permitted.token),
|
|
485
|
-
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
486
|
-
},
|
|
487
|
-
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
488
|
-
deadline: BigInt(permit2Payload.permit2Authorization.deadline)
|
|
489
|
-
},
|
|
490
|
-
getAddress2(payer),
|
|
491
|
-
{
|
|
492
|
-
to: getAddress2(permit2Payload.permit2Authorization.witness.to),
|
|
493
|
-
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter),
|
|
494
|
-
extra: permit2Payload.permit2Authorization.witness.extra
|
|
601
|
+
const { v, r, s } = splitEip2612Signature(eip2612Info.signature);
|
|
602
|
+
const tx = await signer.writeContract({
|
|
603
|
+
address: x402ExactPermit2ProxyAddress,
|
|
604
|
+
abi: x402ExactPermit2ProxyABI,
|
|
605
|
+
functionName: "settleWithPermit",
|
|
606
|
+
args: [
|
|
607
|
+
{
|
|
608
|
+
value: BigInt(eip2612Info.amount),
|
|
609
|
+
deadline: BigInt(eip2612Info.deadline),
|
|
610
|
+
r,
|
|
611
|
+
s,
|
|
612
|
+
v
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
permitted: {
|
|
616
|
+
token: getAddress3(permit2Payload.permit2Authorization.permitted.token),
|
|
617
|
+
amount: BigInt(permit2Payload.permit2Authorization.permitted.amount)
|
|
495
618
|
},
|
|
496
|
-
permit2Payload.
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
619
|
+
nonce: BigInt(permit2Payload.permit2Authorization.nonce),
|
|
620
|
+
deadline: BigInt(permit2Payload.permit2Authorization.deadline)
|
|
621
|
+
},
|
|
622
|
+
getAddress3(payer),
|
|
623
|
+
{
|
|
624
|
+
to: getAddress3(permit2Payload.permit2Authorization.witness.to),
|
|
625
|
+
validAfter: BigInt(permit2Payload.permit2Authorization.witness.validAfter)
|
|
626
|
+
},
|
|
627
|
+
permit2Payload.signature
|
|
628
|
+
]
|
|
629
|
+
});
|
|
630
|
+
return _waitAndReturn(signer, tx, payload, payer);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
return _mapSettleError(error, payload, payer);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async function _settlePermit2WithERC20Approval(extensionSigner, payload, permit2Payload, erc20Info) {
|
|
636
|
+
const payer = permit2Payload.permit2Authorization.from;
|
|
637
|
+
try {
|
|
638
|
+
const approvalTxHash = await extensionSigner.sendRawTransaction({
|
|
639
|
+
serializedTransaction: erc20Info.signedTransaction
|
|
640
|
+
});
|
|
641
|
+
const approvalReceipt = await extensionSigner.waitForTransactionReceipt({
|
|
642
|
+
hash: approvalTxHash
|
|
643
|
+
});
|
|
644
|
+
if (approvalReceipt.status !== "success") {
|
|
502
645
|
return {
|
|
503
646
|
success: false,
|
|
504
|
-
errorReason: "
|
|
505
|
-
transaction:
|
|
647
|
+
errorReason: "erc20_approval_tx_failed",
|
|
648
|
+
transaction: approvalTxHash,
|
|
506
649
|
network: payload.accepted.network,
|
|
507
650
|
payer
|
|
508
651
|
};
|
|
509
652
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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);
|
|
516
675
|
} catch (error) {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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") {
|
|
536
711
|
return {
|
|
537
712
|
success: false,
|
|
538
|
-
errorReason,
|
|
539
|
-
transaction:
|
|
713
|
+
errorReason: "invalid_transaction_state",
|
|
714
|
+
transaction: tx,
|
|
540
715
|
network: payload.accepted.network,
|
|
541
716
|
payer
|
|
542
717
|
};
|
|
543
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
|
+
};
|
|
544
755
|
}
|
|
545
756
|
function validateEip2612PermitForPayment(info, payer, tokenAddress) {
|
|
546
757
|
if (!validateEip2612GasSponsoringInfo(info)) {
|
|
547
758
|
return { isValid: false, invalidReason: "invalid_eip2612_extension_format" };
|
|
548
759
|
}
|
|
549
|
-
if (
|
|
760
|
+
if (getAddress3(info.from) !== getAddress3(payer)) {
|
|
550
761
|
return { isValid: false, invalidReason: "eip2612_from_mismatch" };
|
|
551
762
|
}
|
|
552
|
-
if (
|
|
763
|
+
if (getAddress3(info.asset) !== tokenAddress) {
|
|
553
764
|
return { isValid: false, invalidReason: "eip2612_asset_mismatch" };
|
|
554
765
|
}
|
|
555
|
-
if (
|
|
766
|
+
if (getAddress3(info.spender) !== getAddress3(PERMIT2_ADDRESS)) {
|
|
556
767
|
return { isValid: false, invalidReason: "eip2612_spender_not_permit2" };
|
|
557
768
|
}
|
|
558
769
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -577,10 +788,10 @@ function splitEip2612Signature(signature) {
|
|
|
577
788
|
// src/exact/facilitator/scheme.ts
|
|
578
789
|
var ExactEvmScheme = class {
|
|
579
790
|
/**
|
|
580
|
-
* Creates a new
|
|
791
|
+
* Creates a new ExactEvmScheme facilitator instance.
|
|
581
792
|
*
|
|
582
793
|
* @param signer - The EVM signer for facilitator operations
|
|
583
|
-
* @param config - Optional configuration
|
|
794
|
+
* @param config - Optional configuration
|
|
584
795
|
*/
|
|
585
796
|
constructor(signer, config) {
|
|
586
797
|
this.signer = signer;
|
|
@@ -591,53 +802,51 @@ var ExactEvmScheme = class {
|
|
|
591
802
|
};
|
|
592
803
|
}
|
|
593
804
|
/**
|
|
594
|
-
*
|
|
595
|
-
* For EVM, no extra data is needed.
|
|
805
|
+
* Returns undefined — EVM has no mechanism-specific extra data.
|
|
596
806
|
*
|
|
597
|
-
* @param _ - The network identifier (unused
|
|
598
|
-
* @returns undefined
|
|
807
|
+
* @param _ - The network identifier (unused)
|
|
808
|
+
* @returns undefined
|
|
599
809
|
*/
|
|
600
810
|
getExtra(_) {
|
|
601
811
|
return void 0;
|
|
602
812
|
}
|
|
603
813
|
/**
|
|
604
|
-
*
|
|
605
|
-
* Returns all addresses this facilitator can use for signing/settling transactions.
|
|
814
|
+
* Returns facilitator wallet addresses for the supported response.
|
|
606
815
|
*
|
|
607
|
-
* @param _ - The network identifier (unused
|
|
816
|
+
* @param _ - The network identifier (unused, addresses are network-agnostic)
|
|
608
817
|
* @returns Array of facilitator wallet addresses
|
|
609
818
|
*/
|
|
610
819
|
getSigners(_) {
|
|
611
820
|
return [...this.signer.getAddresses()];
|
|
612
821
|
}
|
|
613
822
|
/**
|
|
614
|
-
* Verifies a payment payload.
|
|
615
|
-
* 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.
|
|
616
824
|
*
|
|
617
825
|
* @param payload - The payment payload to verify
|
|
618
826
|
* @param requirements - The payment requirements
|
|
827
|
+
* @param context - Optional facilitator context for extension capabilities
|
|
619
828
|
* @returns Promise resolving to verification response
|
|
620
829
|
*/
|
|
621
|
-
async verify(payload, requirements) {
|
|
830
|
+
async verify(payload, requirements, context) {
|
|
622
831
|
const rawPayload = payload.payload;
|
|
623
832
|
if (isPermit2Payload(rawPayload)) {
|
|
624
|
-
return verifyPermit2(this.signer, payload, requirements, rawPayload);
|
|
833
|
+
return verifyPermit2(this.signer, payload, requirements, rawPayload, context);
|
|
625
834
|
}
|
|
626
835
|
const eip3009Payload = rawPayload;
|
|
627
836
|
return verifyEIP3009(this.signer, payload, requirements, eip3009Payload);
|
|
628
837
|
}
|
|
629
838
|
/**
|
|
630
|
-
* Settles a payment
|
|
631
|
-
* Routes to the appropriate settlement logic based on payload type.
|
|
839
|
+
* Settles a payment. Routes to Permit2 or EIP-3009 based on payload type.
|
|
632
840
|
*
|
|
633
841
|
* @param payload - The payment payload to settle
|
|
634
842
|
* @param requirements - The payment requirements
|
|
843
|
+
* @param context - Optional facilitator context for extension capabilities
|
|
635
844
|
* @returns Promise resolving to settlement response
|
|
636
845
|
*/
|
|
637
|
-
async settle(payload, requirements) {
|
|
846
|
+
async settle(payload, requirements, context) {
|
|
638
847
|
const rawPayload = payload.payload;
|
|
639
848
|
if (isPermit2Payload(rawPayload)) {
|
|
640
|
-
return settlePermit2(this.signer, payload, requirements, rawPayload);
|
|
849
|
+
return settlePermit2(this.signer, payload, requirements, rawPayload, context);
|
|
641
850
|
}
|
|
642
851
|
const eip3009Payload = rawPayload;
|
|
643
852
|
return settleEIP3009(this.signer, payload, requirements, eip3009Payload, this.config);
|