@dexterai/x402 2.0.0 → 2.1.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/README.md +27 -0
- package/dist/adapters/index.cjs +375 -2
- package/dist/adapters/index.d.cts +4 -3
- package/dist/adapters/index.d.ts +4 -3
- package/dist/adapters/index.js +369 -2
- package/dist/client/index.cjs +378 -4
- package/dist/client/index.d.cts +5 -5
- package/dist/client/index.d.ts +5 -5
- package/dist/client/index.js +378 -4
- package/dist/react/index.cjs +378 -4
- package/dist/react/index.d.cts +5 -5
- package/dist/react/index.d.ts +5 -5
- package/dist/react/index.js +378 -4
- package/dist/server/index.cjs +3 -3
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +3 -3
- package/dist/{sponsored-access-BgEDLg_H.d.cts → sponsored-access-DAVzu4x6.d.cts} +2 -2
- package/dist/{sponsored-access-DjLEKhOV.d.ts → sponsored-access-Lxa11w_X.d.ts} +2 -2
- package/dist/{types-DWhpiOBD.d.cts → types-D1u7iu8n.d.cts} +62 -3
- package/dist/{types-D1TGACsL.d.ts → types-YQlJI5E3.d.ts} +62 -3
- package/dist/{types-CjLMR7qs.d.cts → types-_iT11DL0.d.cts} +2 -2
- package/dist/{types-CjLMR7qs.d.ts → types-_iT11DL0.d.ts} +2 -2
- package/package.json +1 -1
package/dist/client/index.cjs
CHANGED
|
@@ -414,16 +414,40 @@ function createSolanaAdapter(config) {
|
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
// src/adapters/evm.ts
|
|
417
|
+
var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
418
|
+
var X402_EXACT_PERMIT2_PROXY = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001";
|
|
419
|
+
var PERMIT2_WITNESS_TYPES = {
|
|
420
|
+
PermitWitnessTransferFrom: [
|
|
421
|
+
{ name: "permitted", type: "TokenPermissions" },
|
|
422
|
+
{ name: "spender", type: "address" },
|
|
423
|
+
{ name: "nonce", type: "uint256" },
|
|
424
|
+
{ name: "deadline", type: "uint256" },
|
|
425
|
+
{ name: "witness", type: "Witness" }
|
|
426
|
+
],
|
|
427
|
+
TokenPermissions: [
|
|
428
|
+
{ name: "token", type: "address" },
|
|
429
|
+
{ name: "amount", type: "uint256" }
|
|
430
|
+
],
|
|
431
|
+
Witness: [
|
|
432
|
+
{ name: "to", type: "address" },
|
|
433
|
+
{ name: "validAfter", type: "uint256" }
|
|
434
|
+
]
|
|
435
|
+
};
|
|
436
|
+
var MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
|
417
437
|
var BASE_MAINNET = "eip155:8453";
|
|
418
438
|
var BASE_SEPOLIA = "eip155:84532";
|
|
419
439
|
var ARBITRUM_ONE = "eip155:42161";
|
|
420
440
|
var POLYGON = "eip155:137";
|
|
421
441
|
var OPTIMISM = "eip155:10";
|
|
422
442
|
var AVALANCHE = "eip155:43114";
|
|
443
|
+
var BSC_MAINNET = "eip155:56";
|
|
423
444
|
var SKALE_BASE = "eip155:1187947933";
|
|
424
445
|
var SKALE_BASE_SEPOLIA = "eip155:324705682";
|
|
425
446
|
var ETHEREUM_MAINNET = "eip155:1";
|
|
447
|
+
var BSC_USDT = "0x55d398326f99059fF775485246999027B3197955";
|
|
448
|
+
var BSC_USDC = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
|
|
426
449
|
var CHAIN_IDS = {
|
|
450
|
+
[BSC_MAINNET]: 56,
|
|
427
451
|
[BASE_MAINNET]: 8453,
|
|
428
452
|
[BASE_SEPOLIA]: 84532,
|
|
429
453
|
[ARBITRUM_ONE]: 42161,
|
|
@@ -435,6 +459,7 @@ var CHAIN_IDS = {
|
|
|
435
459
|
[ETHEREUM_MAINNET]: 1
|
|
436
460
|
};
|
|
437
461
|
var DEFAULT_RPC_URLS2 = {
|
|
462
|
+
[BSC_MAINNET]: "https://bsc-dataseed1.binance.org",
|
|
438
463
|
[BASE_MAINNET]: "https://api.dexter.cash/api/base/rpc",
|
|
439
464
|
[BASE_SEPOLIA]: "https://sepolia.base.org",
|
|
440
465
|
[ARBITRUM_ONE]: "https://arb1.arbitrum.io/rpc",
|
|
@@ -446,6 +471,7 @@ var DEFAULT_RPC_URLS2 = {
|
|
|
446
471
|
[ETHEREUM_MAINNET]: "https://eth.llamarpc.com"
|
|
447
472
|
};
|
|
448
473
|
var USDC_ADDRESSES = {
|
|
474
|
+
[BSC_MAINNET]: BSC_USDC,
|
|
449
475
|
[BASE_MAINNET]: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
450
476
|
[BASE_SEPOLIA]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
451
477
|
[ARBITRUM_ONE]: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
@@ -456,6 +482,10 @@ var USDC_ADDRESSES = {
|
|
|
456
482
|
[SKALE_BASE_SEPOLIA]: "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
|
|
457
483
|
[ETHEREUM_MAINNET]: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
|
458
484
|
};
|
|
485
|
+
var BSC_STABLECOIN_ADDRESSES = {
|
|
486
|
+
[BSC_USDT]: { symbol: "USDT", decimals: 18 },
|
|
487
|
+
[BSC_USDC]: { symbol: "USDC", decimals: 18 }
|
|
488
|
+
};
|
|
459
489
|
function isEvmWallet(wallet) {
|
|
460
490
|
if (!wallet || typeof wallet !== "object") return false;
|
|
461
491
|
const w = wallet;
|
|
@@ -463,7 +493,7 @@ function isEvmWallet(wallet) {
|
|
|
463
493
|
}
|
|
464
494
|
var EvmAdapter = class {
|
|
465
495
|
name = "EVM";
|
|
466
|
-
networks = [BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
|
|
496
|
+
networks = [BSC_MAINNET, BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
|
|
467
497
|
config;
|
|
468
498
|
log;
|
|
469
499
|
constructor(config = {}) {
|
|
@@ -474,6 +504,7 @@ var EvmAdapter = class {
|
|
|
474
504
|
canHandle(network) {
|
|
475
505
|
if (this.networks.includes(network)) return true;
|
|
476
506
|
if (network === "base") return true;
|
|
507
|
+
if (network === "bsc") return true;
|
|
477
508
|
if (network === "ethereum") return true;
|
|
478
509
|
if (network === "arbitrum") return true;
|
|
479
510
|
if (network.startsWith("eip155:")) return true;
|
|
@@ -487,6 +518,7 @@ var EvmAdapter = class {
|
|
|
487
518
|
return DEFAULT_RPC_URLS2[network];
|
|
488
519
|
}
|
|
489
520
|
if (network === "base") return DEFAULT_RPC_URLS2[BASE_MAINNET];
|
|
521
|
+
if (network === "bsc") return DEFAULT_RPC_URLS2[BSC_MAINNET];
|
|
490
522
|
if (network === "ethereum") return DEFAULT_RPC_URLS2[ETHEREUM_MAINNET];
|
|
491
523
|
if (network === "arbitrum") return DEFAULT_RPC_URLS2[ARBITRUM_ONE];
|
|
492
524
|
return DEFAULT_RPC_URLS2[BASE_MAINNET];
|
|
@@ -506,6 +538,7 @@ var EvmAdapter = class {
|
|
|
506
538
|
return parseInt(chainIdStr, 10);
|
|
507
539
|
}
|
|
508
540
|
if (network === "base") return 8453;
|
|
541
|
+
if (network === "bsc") return 56;
|
|
509
542
|
if (network === "ethereum") return 1;
|
|
510
543
|
if (network === "arbitrum") return 42161;
|
|
511
544
|
return 8453;
|
|
@@ -555,13 +588,19 @@ var EvmAdapter = class {
|
|
|
555
588
|
const paddedAddress = address.slice(2).toLowerCase().padStart(64, "0");
|
|
556
589
|
return selector + paddedAddress;
|
|
557
590
|
}
|
|
558
|
-
async buildTransaction(accept, wallet,
|
|
591
|
+
async buildTransaction(accept, wallet, rpcUrl) {
|
|
559
592
|
if (!isEvmWallet(wallet)) {
|
|
560
593
|
throw new Error("Invalid EVM wallet");
|
|
561
594
|
}
|
|
562
595
|
if (!wallet.address) {
|
|
563
596
|
throw new Error("Wallet not connected");
|
|
564
597
|
}
|
|
598
|
+
if (accept.scheme === "exact-approval") {
|
|
599
|
+
return this.buildApprovalTransaction(accept, wallet, rpcUrl);
|
|
600
|
+
}
|
|
601
|
+
if (accept.extra?.assetTransferMethod === "permit2") {
|
|
602
|
+
return this.buildPermit2Transaction(accept, wallet, rpcUrl);
|
|
603
|
+
}
|
|
565
604
|
const { payTo, asset, extra } = accept;
|
|
566
605
|
const amount = accept.amount ?? accept.maxAmountRequired;
|
|
567
606
|
if (!amount) {
|
|
@@ -632,6 +671,325 @@ var EvmAdapter = class {
|
|
|
632
671
|
signature
|
|
633
672
|
};
|
|
634
673
|
}
|
|
674
|
+
// ===========================================================================
|
|
675
|
+
// exact-approval: BSC and other chains without EIP-3009
|
|
676
|
+
// ===========================================================================
|
|
677
|
+
/**
|
|
678
|
+
* Build a payment transaction for chains that use the approval-based scheme.
|
|
679
|
+
* The facilitator's /supported response provides the EIP-712 domain and types
|
|
680
|
+
* in accept.extra, so the client doesn't hardcode any contract addresses.
|
|
681
|
+
*/
|
|
682
|
+
async buildApprovalTransaction(accept, wallet, rpcUrl) {
|
|
683
|
+
const { payTo, asset, extra } = accept;
|
|
684
|
+
const amount = accept.amount ?? accept.maxAmountRequired;
|
|
685
|
+
if (!amount) {
|
|
686
|
+
throw new Error("Missing amount in payment requirements");
|
|
687
|
+
}
|
|
688
|
+
const facilitatorContract = extra?.facilitatorContract;
|
|
689
|
+
if (!facilitatorContract) {
|
|
690
|
+
throw new Error(
|
|
691
|
+
"exact-approval scheme requires extra.facilitatorContract from the facilitator. The /supported endpoint should provide this."
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (!wallet.signTypedData) {
|
|
695
|
+
throw new Error("Wallet does not support signTypedData (EIP-712)");
|
|
696
|
+
}
|
|
697
|
+
this.log("Building approval-based transaction:", {
|
|
698
|
+
from: wallet.address,
|
|
699
|
+
to: payTo,
|
|
700
|
+
amount,
|
|
701
|
+
asset,
|
|
702
|
+
network: accept.network,
|
|
703
|
+
facilitatorContract
|
|
704
|
+
});
|
|
705
|
+
const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
|
|
706
|
+
const fee = extra?.fee ?? "0";
|
|
707
|
+
const totalNeeded = BigInt(amount) + BigInt(fee);
|
|
708
|
+
const currentAllowance = await this.readAllowance(url, asset, wallet.address, facilitatorContract);
|
|
709
|
+
if (currentAllowance < totalNeeded) {
|
|
710
|
+
if (!wallet.sendTransaction) {
|
|
711
|
+
throw new Error(
|
|
712
|
+
"BSC payments require a wallet that supports sendTransaction for the one-time token approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
const approvalAmount = this.calculateApprovalAmount(amount, fee, extra?.approvalStrategy);
|
|
716
|
+
this.log(`Approving ${approvalAmount} for ${facilitatorContract} (current allowance: ${currentAllowance})`);
|
|
717
|
+
const approveTxHash = await wallet.sendTransaction({
|
|
718
|
+
to: asset,
|
|
719
|
+
data: this.encodeApprove(facilitatorContract, approvalAmount),
|
|
720
|
+
value: 0n
|
|
721
|
+
});
|
|
722
|
+
this.log(`Approval tx sent: ${approveTxHash}`);
|
|
723
|
+
await this.waitForReceipt(url, approveTxHash);
|
|
724
|
+
this.log("Approval confirmed");
|
|
725
|
+
} else {
|
|
726
|
+
this.log("Sufficient allowance, skipping approval");
|
|
727
|
+
}
|
|
728
|
+
const nonceBytes = new Uint8Array(16);
|
|
729
|
+
(globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
|
|
730
|
+
const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n).toString();
|
|
731
|
+
const paymentIdBytes = new Uint8Array(32);
|
|
732
|
+
(globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(paymentIdBytes);
|
|
733
|
+
const paymentId = "0x" + [...paymentIdBytes].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
734
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
735
|
+
const deadline = now + (accept.maxTimeoutSeconds || 300);
|
|
736
|
+
const eip712Domain = extra?.eip712Domain;
|
|
737
|
+
const domain = eip712Domain ? {
|
|
738
|
+
name: eip712Domain.name,
|
|
739
|
+
version: eip712Domain.version,
|
|
740
|
+
chainId: BigInt(eip712Domain.chainId),
|
|
741
|
+
verifyingContract: eip712Domain.verifyingContract
|
|
742
|
+
} : {
|
|
743
|
+
name: "DexterBSCFacilitator",
|
|
744
|
+
version: "1",
|
|
745
|
+
chainId: BigInt(this.getChainId(accept.network)),
|
|
746
|
+
verifyingContract: facilitatorContract
|
|
747
|
+
};
|
|
748
|
+
const types = extra?.eip712Types ?? {
|
|
749
|
+
Payment: [
|
|
750
|
+
{ name: "from", type: "address" },
|
|
751
|
+
{ name: "to", type: "address" },
|
|
752
|
+
{ name: "token", type: "address" },
|
|
753
|
+
{ name: "amount", type: "uint256" },
|
|
754
|
+
{ name: "fee", type: "uint256" },
|
|
755
|
+
{ name: "nonce", type: "uint256" },
|
|
756
|
+
{ name: "deadline", type: "uint256" },
|
|
757
|
+
{ name: "paymentId", type: "bytes32" }
|
|
758
|
+
]
|
|
759
|
+
};
|
|
760
|
+
const message = {
|
|
761
|
+
from: wallet.address,
|
|
762
|
+
to: payTo,
|
|
763
|
+
token: asset,
|
|
764
|
+
amount: BigInt(amount),
|
|
765
|
+
fee: BigInt(fee),
|
|
766
|
+
nonce: BigInt(nonce),
|
|
767
|
+
deadline: BigInt(deadline),
|
|
768
|
+
paymentId
|
|
769
|
+
};
|
|
770
|
+
const signature = await wallet.signTypedData({
|
|
771
|
+
domain,
|
|
772
|
+
types,
|
|
773
|
+
primaryType: "Payment",
|
|
774
|
+
message
|
|
775
|
+
});
|
|
776
|
+
this.log("EIP-712 Payment signature obtained");
|
|
777
|
+
const payload = {
|
|
778
|
+
from: wallet.address,
|
|
779
|
+
to: payTo,
|
|
780
|
+
token: asset,
|
|
781
|
+
amount,
|
|
782
|
+
fee,
|
|
783
|
+
nonce,
|
|
784
|
+
deadline,
|
|
785
|
+
paymentId,
|
|
786
|
+
signature
|
|
787
|
+
};
|
|
788
|
+
return {
|
|
789
|
+
serialized: JSON.stringify(payload),
|
|
790
|
+
signature
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
// ===========================================================================
|
|
794
|
+
// Permit2: Universal ERC-20 payments via Uniswap's Permit2 contract
|
|
795
|
+
// ===========================================================================
|
|
796
|
+
/**
|
|
797
|
+
* Build a Permit2 payment transaction. Used when the facilitator signals
|
|
798
|
+
* assetTransferMethod: "permit2" in extra (e.g., BSC where EIP-3009 is unavailable).
|
|
799
|
+
*
|
|
800
|
+
* Flow:
|
|
801
|
+
* 1. Check if token has approved the Permit2 contract. If not, approve(Permit2, maxUint256).
|
|
802
|
+
* 2. Sign EIP-712 PermitWitnessTransferFrom against the Permit2 contract.
|
|
803
|
+
* 3. Return { permit2Authorization, signature } payload for the facilitator.
|
|
804
|
+
*/
|
|
805
|
+
async buildPermit2Transaction(accept, wallet, rpcUrl) {
|
|
806
|
+
const { payTo, asset } = accept;
|
|
807
|
+
const amount = accept.amount ?? accept.maxAmountRequired;
|
|
808
|
+
if (!amount) {
|
|
809
|
+
throw new Error("Missing amount in payment requirements");
|
|
810
|
+
}
|
|
811
|
+
if (!wallet.signTypedData) {
|
|
812
|
+
throw new Error("Wallet does not support signTypedData (EIP-712)");
|
|
813
|
+
}
|
|
814
|
+
this.log("Building Permit2 transaction:", {
|
|
815
|
+
from: wallet.address,
|
|
816
|
+
to: payTo,
|
|
817
|
+
amount,
|
|
818
|
+
asset,
|
|
819
|
+
network: accept.network
|
|
820
|
+
});
|
|
821
|
+
const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
|
|
822
|
+
const currentAllowance = await this.readAllowance(url, asset, wallet.address, PERMIT2_ADDRESS);
|
|
823
|
+
if (currentAllowance < BigInt(amount)) {
|
|
824
|
+
if (!wallet.sendTransaction) {
|
|
825
|
+
throw new Error(
|
|
826
|
+
"Permit2 payments require a wallet that supports sendTransaction for the one-time Permit2 approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
this.log(`Approving Permit2 for ${asset} (current allowance: ${currentAllowance})`);
|
|
830
|
+
const approveTxHash = await wallet.sendTransaction({
|
|
831
|
+
to: asset,
|
|
832
|
+
data: this.encodeApprove(PERMIT2_ADDRESS, MAX_UINT256),
|
|
833
|
+
value: 0n
|
|
834
|
+
});
|
|
835
|
+
this.log(`Permit2 approval tx sent: ${approveTxHash}`);
|
|
836
|
+
await this.waitForReceipt(url, approveTxHash);
|
|
837
|
+
this.log("Permit2 approval confirmed");
|
|
838
|
+
} else {
|
|
839
|
+
this.log("Sufficient Permit2 allowance, skipping approval");
|
|
840
|
+
}
|
|
841
|
+
const nonceBytes = new Uint8Array(32);
|
|
842
|
+
(globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
|
|
843
|
+
const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n);
|
|
844
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
845
|
+
const validAfter = now - 600;
|
|
846
|
+
const deadline = now + (accept.maxTimeoutSeconds || 300);
|
|
847
|
+
const chainId = this.getChainId(accept.network);
|
|
848
|
+
const domain = {
|
|
849
|
+
name: "Permit2",
|
|
850
|
+
chainId: BigInt(chainId),
|
|
851
|
+
verifyingContract: PERMIT2_ADDRESS
|
|
852
|
+
};
|
|
853
|
+
const message = {
|
|
854
|
+
permitted: {
|
|
855
|
+
token: asset,
|
|
856
|
+
amount: BigInt(amount)
|
|
857
|
+
},
|
|
858
|
+
spender: X402_EXACT_PERMIT2_PROXY,
|
|
859
|
+
nonce,
|
|
860
|
+
deadline: BigInt(deadline),
|
|
861
|
+
witness: {
|
|
862
|
+
to: payTo,
|
|
863
|
+
validAfter: BigInt(validAfter)
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
const signature = await wallet.signTypedData({
|
|
867
|
+
domain,
|
|
868
|
+
types: PERMIT2_WITNESS_TYPES,
|
|
869
|
+
primaryType: "PermitWitnessTransferFrom",
|
|
870
|
+
message
|
|
871
|
+
});
|
|
872
|
+
this.log("Permit2 PermitWitnessTransferFrom signature obtained");
|
|
873
|
+
const payload = {
|
|
874
|
+
signature,
|
|
875
|
+
permit2Authorization: {
|
|
876
|
+
from: wallet.address,
|
|
877
|
+
permitted: {
|
|
878
|
+
token: asset,
|
|
879
|
+
amount
|
|
880
|
+
},
|
|
881
|
+
spender: X402_EXACT_PERMIT2_PROXY,
|
|
882
|
+
nonce: nonce.toString(),
|
|
883
|
+
deadline: String(deadline),
|
|
884
|
+
witness: {
|
|
885
|
+
to: payTo,
|
|
886
|
+
validAfter: String(validAfter)
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
return {
|
|
891
|
+
serialized: JSON.stringify(payload),
|
|
892
|
+
signature
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Read ERC-20 allowance via raw eth_call (no viem dependency needed).
|
|
897
|
+
*/
|
|
898
|
+
async readAllowance(rpcUrl, token, owner, spender) {
|
|
899
|
+
const selector = "0xdd62ed3e";
|
|
900
|
+
const paddedOwner = owner.slice(2).toLowerCase().padStart(64, "0");
|
|
901
|
+
const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
|
|
902
|
+
const data = selector + paddedOwner + paddedSpender;
|
|
903
|
+
try {
|
|
904
|
+
const response = await fetch(rpcUrl, {
|
|
905
|
+
method: "POST",
|
|
906
|
+
headers: { "Content-Type": "application/json" },
|
|
907
|
+
body: JSON.stringify({
|
|
908
|
+
jsonrpc: "2.0",
|
|
909
|
+
id: 1,
|
|
910
|
+
method: "eth_call",
|
|
911
|
+
params: [{ to: token, data }, "latest"]
|
|
912
|
+
})
|
|
913
|
+
});
|
|
914
|
+
const result = await response.json();
|
|
915
|
+
if (result.error || !result.result || result.result === "0x") return 0n;
|
|
916
|
+
return BigInt(result.result);
|
|
917
|
+
} catch {
|
|
918
|
+
return 0n;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Encode ERC-20 approve(address,uint256) calldata.
|
|
923
|
+
*/
|
|
924
|
+
encodeApprove(spender, amount) {
|
|
925
|
+
const selector = "0x095ea7b3";
|
|
926
|
+
const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
|
|
927
|
+
const paddedAmount = amount.toString(16).padStart(64, "0");
|
|
928
|
+
return selector + paddedSpender + paddedAmount;
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Wait for a transaction receipt by polling eth_getTransactionReceipt.
|
|
932
|
+
*/
|
|
933
|
+
async waitForReceipt(rpcUrl, txHash, timeoutMs = 3e4) {
|
|
934
|
+
const start = Date.now();
|
|
935
|
+
while (Date.now() - start < timeoutMs) {
|
|
936
|
+
try {
|
|
937
|
+
const response = await fetch(rpcUrl, {
|
|
938
|
+
method: "POST",
|
|
939
|
+
headers: { "Content-Type": "application/json" },
|
|
940
|
+
body: JSON.stringify({
|
|
941
|
+
jsonrpc: "2.0",
|
|
942
|
+
id: 1,
|
|
943
|
+
method: "eth_getTransactionReceipt",
|
|
944
|
+
params: [txHash]
|
|
945
|
+
})
|
|
946
|
+
});
|
|
947
|
+
const result = await response.json();
|
|
948
|
+
if (result.result) {
|
|
949
|
+
if (result.result.status === "0x0") {
|
|
950
|
+
throw new Error(`Approval transaction reverted: ${txHash}`);
|
|
951
|
+
}
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
} catch (err) {
|
|
955
|
+
if (err instanceof Error && err.message.includes("reverted")) throw err;
|
|
956
|
+
}
|
|
957
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
958
|
+
}
|
|
959
|
+
throw new Error(`Approval transaction receipt timeout after ${timeoutMs}ms: ${txHash}`);
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Calculate how much to approve based on the facilitator's approval strategy.
|
|
963
|
+
* Buffered approvals reduce the number of on-chain approval txs for micropayments.
|
|
964
|
+
*/
|
|
965
|
+
calculateApprovalAmount(paymentAmount, fee, strategy) {
|
|
966
|
+
const total = BigInt(paymentAmount) + BigInt(fee);
|
|
967
|
+
if (!strategy || strategy.mode === "exact") {
|
|
968
|
+
return total;
|
|
969
|
+
}
|
|
970
|
+
const multiple = BigInt(strategy.defaultMultiple ?? 10);
|
|
971
|
+
const buffered = total * multiple;
|
|
972
|
+
if (strategy.maxCapUsd) {
|
|
973
|
+
const decimals = this.inferDecimals(paymentAmount);
|
|
974
|
+
const maxCap = BigInt(Math.floor(strategy.maxCapUsd * Math.pow(10, decimals)));
|
|
975
|
+
if (buffered > maxCap) return maxCap;
|
|
976
|
+
}
|
|
977
|
+
if (strategy.exactAboveUsd) {
|
|
978
|
+
const decimals = this.inferDecimals(paymentAmount);
|
|
979
|
+
const threshold = BigInt(Math.floor(strategy.exactAboveUsd * Math.pow(10, decimals)));
|
|
980
|
+
if (BigInt(paymentAmount) > threshold) return total;
|
|
981
|
+
}
|
|
982
|
+
return buffered;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Infer token decimals from payment amount magnitude.
|
|
986
|
+
* BSC stablecoins use 18 decimals, all others use 6.
|
|
987
|
+
* A $1 payment is 1000000 (6 dec) or 1000000000000000000 (18 dec).
|
|
988
|
+
* If the amount has > 12 digits, it's almost certainly 18 decimals.
|
|
989
|
+
*/
|
|
990
|
+
inferDecimals(amount) {
|
|
991
|
+
return amount.length > 12 ? 18 : 6;
|
|
992
|
+
}
|
|
635
993
|
};
|
|
636
994
|
function createEvmAdapter(config) {
|
|
637
995
|
return new EvmAdapter(config);
|
|
@@ -645,6 +1003,9 @@ function isKnownUSDC(asset) {
|
|
|
645
1003
|
for (const addr of Object.values(USDC_ADDRESSES)) {
|
|
646
1004
|
if (addr.toLowerCase() === lc) return true;
|
|
647
1005
|
}
|
|
1006
|
+
for (const addr of Object.keys(BSC_STABLECOIN_ADDRESSES)) {
|
|
1007
|
+
if (addr.toLowerCase() === lc) return true;
|
|
1008
|
+
}
|
|
648
1009
|
return false;
|
|
649
1010
|
}
|
|
650
1011
|
|
|
@@ -753,6 +1114,19 @@ function createX402Client(config) {
|
|
|
753
1114
|
}
|
|
754
1115
|
return candidates[0];
|
|
755
1116
|
}
|
|
1117
|
+
function getChainDisplayName(network, adapterName) {
|
|
1118
|
+
const names = {
|
|
1119
|
+
"eip155:56": "BSC",
|
|
1120
|
+
"eip155:8453": "Base",
|
|
1121
|
+
"eip155:84532": "Base Sepolia",
|
|
1122
|
+
"eip155:42161": "Arbitrum",
|
|
1123
|
+
"eip155:137": "Polygon",
|
|
1124
|
+
"eip155:10": "Optimism",
|
|
1125
|
+
"eip155:43114": "Avalanche",
|
|
1126
|
+
"eip155:1": "Ethereum"
|
|
1127
|
+
};
|
|
1128
|
+
return names[network] || adapterName;
|
|
1129
|
+
}
|
|
756
1130
|
function getRpcUrl(network, adapter) {
|
|
757
1131
|
return rpcUrls[network] || adapter.getDefaultRpcUrl(network);
|
|
758
1132
|
}
|
|
@@ -967,10 +1341,10 @@ function createX402Client(config) {
|
|
|
967
1341
|
const balance = await adapter.getBalance(accept, wallet, rpcUrl);
|
|
968
1342
|
const requiredAmount = Number(paymentAmount) / Math.pow(10, decimals);
|
|
969
1343
|
if (balance < requiredAmount) {
|
|
970
|
-
const
|
|
1344
|
+
const chainName = getChainDisplayName(accept.network, adapter.name);
|
|
971
1345
|
throw new X402Error(
|
|
972
1346
|
"insufficient_balance",
|
|
973
|
-
`Insufficient
|
|
1347
|
+
`Insufficient balance on ${chainName}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
|
|
974
1348
|
);
|
|
975
1349
|
}
|
|
976
1350
|
log(`Balance OK: $${balance.toFixed(4)} >= $${requiredAmount.toFixed(4)}`);
|
package/dist/client/index.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { P as PaymentReceipt, a as X402Client, X as X402ClientConfig, c as createX402Client, f as fireImpressionBeacon, g as getPaymentReceipt, d as getSponsoredAccessInfo, b as getSponsoredRecommendations } from '../sponsored-access-
|
|
2
|
-
import { A as AccessPassClientConfig, P as PaymentAccept } from '../types-
|
|
3
|
-
export { b as AccessPassInfo, a as AccessPassTier, D as DEXTER_FACILITATOR_URL, U as USDC_MINT, X as X402Error } from '../types-
|
|
1
|
+
export { P as PaymentReceipt, a as X402Client, X as X402ClientConfig, c as createX402Client, f as fireImpressionBeacon, g as getPaymentReceipt, d as getSponsoredAccessInfo, b as getSponsoredRecommendations } from '../sponsored-access-DAVzu4x6.cjs';
|
|
2
|
+
import { A as AccessPassClientConfig, P as PaymentAccept } from '../types-_iT11DL0.cjs';
|
|
3
|
+
export { b as AccessPassInfo, a as AccessPassTier, D as DEXTER_FACILITATOR_URL, U as USDC_MINT, X as X402Error } from '../types-_iT11DL0.cjs';
|
|
4
4
|
import { Keypair } from '@solana/web3.js';
|
|
5
|
-
import { S as SolanaWallet, E as EvmWallet } from '../types-
|
|
6
|
-
export { B as BASE_MAINNET, C as ChainAdapter, b as SOLANA_MAINNET, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter } from '../types-
|
|
5
|
+
import { S as SolanaWallet, E as EvmWallet } from '../types-D1u7iu8n.cjs';
|
|
6
|
+
export { B as BASE_MAINNET, C as ChainAdapter, b as SOLANA_MAINNET, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter } from '../types-D1u7iu8n.cjs';
|
|
7
7
|
export { SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
|
|
8
8
|
|
|
9
9
|
/**
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { P as PaymentReceipt, a as X402Client, X as X402ClientConfig, c as createX402Client, f as fireImpressionBeacon, g as getPaymentReceipt, d as getSponsoredAccessInfo, b as getSponsoredRecommendations } from '../sponsored-access-
|
|
2
|
-
import { A as AccessPassClientConfig, P as PaymentAccept } from '../types-
|
|
3
|
-
export { b as AccessPassInfo, a as AccessPassTier, D as DEXTER_FACILITATOR_URL, U as USDC_MINT, X as X402Error } from '../types-
|
|
1
|
+
export { P as PaymentReceipt, a as X402Client, X as X402ClientConfig, c as createX402Client, f as fireImpressionBeacon, g as getPaymentReceipt, d as getSponsoredAccessInfo, b as getSponsoredRecommendations } from '../sponsored-access-Lxa11w_X.js';
|
|
2
|
+
import { A as AccessPassClientConfig, P as PaymentAccept } from '../types-_iT11DL0.js';
|
|
3
|
+
export { b as AccessPassInfo, a as AccessPassTier, D as DEXTER_FACILITATOR_URL, U as USDC_MINT, X as X402Error } from '../types-_iT11DL0.js';
|
|
4
4
|
import { Keypair } from '@solana/web3.js';
|
|
5
|
-
import { S as SolanaWallet, E as EvmWallet } from '../types-
|
|
6
|
-
export { B as BASE_MAINNET, C as ChainAdapter, b as SOLANA_MAINNET, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter } from '../types-
|
|
5
|
+
import { S as SolanaWallet, E as EvmWallet } from '../types-YQlJI5E3.js';
|
|
6
|
+
export { B as BASE_MAINNET, C as ChainAdapter, b as SOLANA_MAINNET, W as WalletSet, a as createEvmAdapter, c as createSolanaAdapter } from '../types-YQlJI5E3.js';
|
|
7
7
|
export { SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
|
|
8
8
|
|
|
9
9
|
/**
|