@dexterai/x402 2.0.0 → 3.0.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.
@@ -222,16 +222,40 @@ function createSolanaAdapter(config) {
222
222
  }
223
223
 
224
224
  // src/adapters/evm.ts
225
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
226
+ var X402_EXACT_PERMIT2_PROXY = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001";
227
+ var PERMIT2_WITNESS_TYPES = {
228
+ PermitWitnessTransferFrom: [
229
+ { name: "permitted", type: "TokenPermissions" },
230
+ { name: "spender", type: "address" },
231
+ { name: "nonce", type: "uint256" },
232
+ { name: "deadline", type: "uint256" },
233
+ { name: "witness", type: "Witness" }
234
+ ],
235
+ TokenPermissions: [
236
+ { name: "token", type: "address" },
237
+ { name: "amount", type: "uint256" }
238
+ ],
239
+ Witness: [
240
+ { name: "to", type: "address" },
241
+ { name: "validAfter", type: "uint256" }
242
+ ]
243
+ };
244
+ var MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
225
245
  var BASE_MAINNET = "eip155:8453";
226
246
  var BASE_SEPOLIA = "eip155:84532";
227
247
  var ARBITRUM_ONE = "eip155:42161";
228
248
  var POLYGON = "eip155:137";
229
249
  var OPTIMISM = "eip155:10";
230
250
  var AVALANCHE = "eip155:43114";
251
+ var BSC_MAINNET = "eip155:56";
231
252
  var SKALE_BASE = "eip155:1187947933";
232
253
  var SKALE_BASE_SEPOLIA = "eip155:324705682";
233
254
  var ETHEREUM_MAINNET = "eip155:1";
255
+ var BSC_USDT = "0x55d398326f99059fF775485246999027B3197955";
256
+ var BSC_USDC = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
234
257
  var CHAIN_IDS = {
258
+ [BSC_MAINNET]: 56,
235
259
  [BASE_MAINNET]: 8453,
236
260
  [BASE_SEPOLIA]: 84532,
237
261
  [ARBITRUM_ONE]: 42161,
@@ -243,6 +267,7 @@ var CHAIN_IDS = {
243
267
  [ETHEREUM_MAINNET]: 1
244
268
  };
245
269
  var DEFAULT_RPC_URLS2 = {
270
+ [BSC_MAINNET]: "https://bsc-dataseed1.binance.org",
246
271
  [BASE_MAINNET]: "https://api.dexter.cash/api/base/rpc",
247
272
  [BASE_SEPOLIA]: "https://sepolia.base.org",
248
273
  [ARBITRUM_ONE]: "https://arb1.arbitrum.io/rpc",
@@ -254,6 +279,7 @@ var DEFAULT_RPC_URLS2 = {
254
279
  [ETHEREUM_MAINNET]: "https://eth.llamarpc.com"
255
280
  };
256
281
  var USDC_ADDRESSES = {
282
+ [BSC_MAINNET]: BSC_USDC,
257
283
  [BASE_MAINNET]: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
258
284
  [BASE_SEPOLIA]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
259
285
  [ARBITRUM_ONE]: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
@@ -264,6 +290,10 @@ var USDC_ADDRESSES = {
264
290
  [SKALE_BASE_SEPOLIA]: "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
265
291
  [ETHEREUM_MAINNET]: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
266
292
  };
293
+ var BSC_STABLECOIN_ADDRESSES = {
294
+ [BSC_USDT]: { symbol: "USDT", decimals: 18 },
295
+ [BSC_USDC]: { symbol: "USDC", decimals: 18 }
296
+ };
267
297
  function isEvmWallet(wallet) {
268
298
  if (!wallet || typeof wallet !== "object") return false;
269
299
  const w = wallet;
@@ -271,7 +301,7 @@ function isEvmWallet(wallet) {
271
301
  }
272
302
  var EvmAdapter = class {
273
303
  name = "EVM";
274
- networks = [BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
304
+ networks = [BSC_MAINNET, BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
275
305
  config;
276
306
  log;
277
307
  constructor(config = {}) {
@@ -282,6 +312,7 @@ var EvmAdapter = class {
282
312
  canHandle(network) {
283
313
  if (this.networks.includes(network)) return true;
284
314
  if (network === "base") return true;
315
+ if (network === "bsc") return true;
285
316
  if (network === "ethereum") return true;
286
317
  if (network === "arbitrum") return true;
287
318
  if (network.startsWith("eip155:")) return true;
@@ -295,6 +326,7 @@ var EvmAdapter = class {
295
326
  return DEFAULT_RPC_URLS2[network];
296
327
  }
297
328
  if (network === "base") return DEFAULT_RPC_URLS2[BASE_MAINNET];
329
+ if (network === "bsc") return DEFAULT_RPC_URLS2[BSC_MAINNET];
298
330
  if (network === "ethereum") return DEFAULT_RPC_URLS2[ETHEREUM_MAINNET];
299
331
  if (network === "arbitrum") return DEFAULT_RPC_URLS2[ARBITRUM_ONE];
300
332
  return DEFAULT_RPC_URLS2[BASE_MAINNET];
@@ -314,6 +346,7 @@ var EvmAdapter = class {
314
346
  return parseInt(chainIdStr, 10);
315
347
  }
316
348
  if (network === "base") return 8453;
349
+ if (network === "bsc") return 56;
317
350
  if (network === "ethereum") return 1;
318
351
  if (network === "arbitrum") return 42161;
319
352
  return 8453;
@@ -363,13 +396,19 @@ var EvmAdapter = class {
363
396
  const paddedAddress = address.slice(2).toLowerCase().padStart(64, "0");
364
397
  return selector + paddedAddress;
365
398
  }
366
- async buildTransaction(accept, wallet, _rpcUrl) {
399
+ async buildTransaction(accept, wallet, rpcUrl) {
367
400
  if (!isEvmWallet(wallet)) {
368
401
  throw new Error("Invalid EVM wallet");
369
402
  }
370
403
  if (!wallet.address) {
371
404
  throw new Error("Wallet not connected");
372
405
  }
406
+ if (accept.scheme === "exact-approval") {
407
+ return this.buildApprovalTransaction(accept, wallet, rpcUrl);
408
+ }
409
+ if (accept.extra?.assetTransferMethod === "permit2") {
410
+ return this.buildPermit2Transaction(accept, wallet, rpcUrl);
411
+ }
373
412
  const { payTo, asset, extra } = accept;
374
413
  const amount = accept.amount ?? accept.maxAmountRequired;
375
414
  if (!amount) {
@@ -440,6 +479,393 @@ var EvmAdapter = class {
440
479
  signature
441
480
  };
442
481
  }
482
+ // ===========================================================================
483
+ // exact-approval: BSC and other chains without EIP-3009
484
+ // ===========================================================================
485
+ /**
486
+ * Build a payment transaction for chains that use the approval-based scheme.
487
+ * The facilitator's /supported response provides the EIP-712 domain and types
488
+ * in accept.extra, so the client doesn't hardcode any contract addresses.
489
+ */
490
+ async buildApprovalTransaction(accept, wallet, rpcUrl) {
491
+ const { payTo, asset, extra } = accept;
492
+ const amount = accept.amount ?? accept.maxAmountRequired;
493
+ if (!amount) {
494
+ throw new Error("Missing amount in payment requirements");
495
+ }
496
+ const facilitatorContract = extra?.facilitatorContract;
497
+ if (!facilitatorContract) {
498
+ throw new Error(
499
+ "exact-approval scheme requires extra.facilitatorContract from the facilitator. The /supported endpoint should provide this."
500
+ );
501
+ }
502
+ if (!wallet.signTypedData) {
503
+ throw new Error("Wallet does not support signTypedData (EIP-712)");
504
+ }
505
+ this.log("Building approval-based transaction:", {
506
+ from: wallet.address,
507
+ to: payTo,
508
+ amount,
509
+ asset,
510
+ network: accept.network,
511
+ facilitatorContract
512
+ });
513
+ const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
514
+ const fee = extra?.fee ?? "0";
515
+ const totalNeeded = BigInt(amount) + BigInt(fee);
516
+ const currentAllowance = await this.readAllowance(url, asset, wallet.address, facilitatorContract);
517
+ if (currentAllowance < totalNeeded) {
518
+ if (!wallet.sendTransaction) {
519
+ throw new Error(
520
+ "BSC payments require a wallet that supports sendTransaction for the one-time token approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
521
+ );
522
+ }
523
+ const approvalAmount = this.calculateApprovalAmount(amount, fee, extra?.approvalStrategy);
524
+ this.log(`Approving ${approvalAmount} for ${facilitatorContract} (current allowance: ${currentAllowance})`);
525
+ const approveTxHash = await wallet.sendTransaction({
526
+ to: asset,
527
+ data: this.encodeApprove(facilitatorContract, approvalAmount),
528
+ value: 0n
529
+ });
530
+ this.log(`Approval tx sent: ${approveTxHash}`);
531
+ await this.waitForReceipt(url, approveTxHash);
532
+ this.log("Approval confirmed");
533
+ } else {
534
+ this.log("Sufficient allowance, skipping approval");
535
+ }
536
+ const nonceBytes = new Uint8Array(16);
537
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
538
+ const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n).toString();
539
+ const paymentIdBytes = new Uint8Array(32);
540
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(paymentIdBytes);
541
+ const paymentId = "0x" + [...paymentIdBytes].map((b) => b.toString(16).padStart(2, "0")).join("");
542
+ const now = Math.floor(Date.now() / 1e3);
543
+ const deadline = now + (accept.maxTimeoutSeconds || 300);
544
+ const eip712Domain = extra?.eip712Domain;
545
+ const domain = eip712Domain ? {
546
+ name: eip712Domain.name,
547
+ version: eip712Domain.version,
548
+ chainId: BigInt(eip712Domain.chainId),
549
+ verifyingContract: eip712Domain.verifyingContract
550
+ } : {
551
+ name: "DexterBSCFacilitator",
552
+ version: "1",
553
+ chainId: BigInt(this.getChainId(accept.network)),
554
+ verifyingContract: facilitatorContract
555
+ };
556
+ const types = extra?.eip712Types ?? {
557
+ Payment: [
558
+ { name: "from", type: "address" },
559
+ { name: "to", type: "address" },
560
+ { name: "token", type: "address" },
561
+ { name: "amount", type: "uint256" },
562
+ { name: "fee", type: "uint256" },
563
+ { name: "nonce", type: "uint256" },
564
+ { name: "deadline", type: "uint256" },
565
+ { name: "paymentId", type: "bytes32" }
566
+ ]
567
+ };
568
+ const message = {
569
+ from: wallet.address,
570
+ to: payTo,
571
+ token: asset,
572
+ amount: BigInt(amount),
573
+ fee: BigInt(fee),
574
+ nonce: BigInt(nonce),
575
+ deadline: BigInt(deadline),
576
+ paymentId
577
+ };
578
+ const signature = await wallet.signTypedData({
579
+ domain,
580
+ types,
581
+ primaryType: "Payment",
582
+ message
583
+ });
584
+ this.log("EIP-712 Payment signature obtained");
585
+ const payload = {
586
+ from: wallet.address,
587
+ to: payTo,
588
+ token: asset,
589
+ amount,
590
+ fee,
591
+ nonce,
592
+ deadline,
593
+ paymentId,
594
+ signature
595
+ };
596
+ return {
597
+ serialized: JSON.stringify(payload),
598
+ signature
599
+ };
600
+ }
601
+ // ===========================================================================
602
+ // Permit2: Universal ERC-20 payments via Uniswap's Permit2 contract
603
+ // ===========================================================================
604
+ /**
605
+ * Build a Permit2 payment transaction. Used when the facilitator signals
606
+ * assetTransferMethod: "permit2" in extra (e.g., BSC where EIP-3009 is unavailable).
607
+ *
608
+ * Flow:
609
+ * 1. Check if token has approved the Permit2 contract. If not, approve(Permit2, maxUint256).
610
+ * 2. Sign EIP-712 PermitWitnessTransferFrom against the Permit2 contract.
611
+ * 3. Return { permit2Authorization, signature } payload for the facilitator.
612
+ */
613
+ async buildPermit2Transaction(accept, wallet, rpcUrl) {
614
+ const { payTo, asset } = accept;
615
+ const amount = accept.amount ?? accept.maxAmountRequired;
616
+ if (!amount) {
617
+ throw new Error("Missing amount in payment requirements");
618
+ }
619
+ if (!wallet.signTypedData) {
620
+ throw new Error("Wallet does not support signTypedData (EIP-712)");
621
+ }
622
+ this.log("Building Permit2 transaction:", {
623
+ from: wallet.address,
624
+ to: payTo,
625
+ amount,
626
+ asset,
627
+ network: accept.network
628
+ });
629
+ const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
630
+ const currentAllowance = await this.readAllowance(url, asset, wallet.address, PERMIT2_ADDRESS);
631
+ let approvalExtension;
632
+ if (currentAllowance < BigInt(amount)) {
633
+ const approveData = this.encodeApprove(PERMIT2_ADDRESS, MAX_UINT256);
634
+ if (wallet.signTransaction) {
635
+ this.log(`Signing Permit2 approval for relay (current allowance: ${currentAllowance})`);
636
+ const chainId2 = this.getChainId(accept.network);
637
+ const gasPrice = await this.readGasPrice(url);
638
+ const nonce2 = await this.readNonce(url, wallet.address);
639
+ const signedTx = await wallet.signTransaction({
640
+ to: asset,
641
+ data: approveData,
642
+ chainId: chainId2,
643
+ gas: 50000n,
644
+ // standard ERC-20 approve
645
+ gasPrice,
646
+ nonce: nonce2
647
+ });
648
+ approvalExtension = {
649
+ erc20ApprovalGasSponsoring: {
650
+ info: {
651
+ from: wallet.address,
652
+ asset,
653
+ spender: PERMIT2_ADDRESS,
654
+ amount: MAX_UINT256.toString(),
655
+ signedTransaction: signedTx,
656
+ version: "1"
657
+ }
658
+ }
659
+ };
660
+ this.log("Permit2 approval signed for facilitator relay");
661
+ } else if (wallet.sendTransaction) {
662
+ this.log(`Approving Permit2 directly (current allowance: ${currentAllowance})`);
663
+ const approveTxHash = await wallet.sendTransaction({
664
+ to: asset,
665
+ data: approveData,
666
+ value: 0n
667
+ });
668
+ this.log(`Permit2 approval tx sent: ${approveTxHash}`);
669
+ await this.waitForReceipt(url, approveTxHash);
670
+ this.log("Permit2 approval confirmed");
671
+ } else {
672
+ throw new Error(
673
+ "Permit2 payments require a wallet that supports signTransaction or sendTransaction for the one-time Permit2 approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
674
+ );
675
+ }
676
+ } else {
677
+ this.log("Sufficient Permit2 allowance, skipping approval");
678
+ }
679
+ const nonceBytes = new Uint8Array(32);
680
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
681
+ const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n);
682
+ const now = Math.floor(Date.now() / 1e3);
683
+ const validAfter = now - 600;
684
+ const deadline = now + (accept.maxTimeoutSeconds || 300);
685
+ const chainId = this.getChainId(accept.network);
686
+ const domain = {
687
+ name: "Permit2",
688
+ chainId: BigInt(chainId),
689
+ verifyingContract: PERMIT2_ADDRESS
690
+ };
691
+ const message = {
692
+ permitted: {
693
+ token: asset,
694
+ amount: BigInt(amount)
695
+ },
696
+ spender: X402_EXACT_PERMIT2_PROXY,
697
+ nonce,
698
+ deadline: BigInt(deadline),
699
+ witness: {
700
+ to: payTo,
701
+ validAfter: BigInt(validAfter)
702
+ }
703
+ };
704
+ const signature = await wallet.signTypedData({
705
+ domain,
706
+ types: PERMIT2_WITNESS_TYPES,
707
+ primaryType: "PermitWitnessTransferFrom",
708
+ message
709
+ });
710
+ this.log("Permit2 PermitWitnessTransferFrom signature obtained");
711
+ const payload = {
712
+ signature,
713
+ permit2Authorization: {
714
+ from: wallet.address,
715
+ permitted: {
716
+ token: asset,
717
+ amount
718
+ },
719
+ spender: X402_EXACT_PERMIT2_PROXY,
720
+ nonce: nonce.toString(),
721
+ deadline: String(deadline),
722
+ witness: {
723
+ to: payTo,
724
+ validAfter: String(validAfter)
725
+ }
726
+ }
727
+ };
728
+ return {
729
+ serialized: JSON.stringify(payload),
730
+ signature,
731
+ extensions: approvalExtension
732
+ };
733
+ }
734
+ /**
735
+ * Read ERC-20 allowance via raw eth_call (no viem dependency needed).
736
+ */
737
+ async readAllowance(rpcUrl, token, owner, spender) {
738
+ const selector = "0xdd62ed3e";
739
+ const paddedOwner = owner.slice(2).toLowerCase().padStart(64, "0");
740
+ const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
741
+ const data = selector + paddedOwner + paddedSpender;
742
+ try {
743
+ const response = await fetch(rpcUrl, {
744
+ method: "POST",
745
+ headers: { "Content-Type": "application/json" },
746
+ body: JSON.stringify({
747
+ jsonrpc: "2.0",
748
+ id: 1,
749
+ method: "eth_call",
750
+ params: [{ to: token, data }, "latest"]
751
+ })
752
+ });
753
+ const result = await response.json();
754
+ if (result.error || !result.result || result.result === "0x") return 0n;
755
+ return BigInt(result.result);
756
+ } catch {
757
+ return 0n;
758
+ }
759
+ }
760
+ /**
761
+ * Encode ERC-20 approve(address,uint256) calldata.
762
+ */
763
+ encodeApprove(spender, amount) {
764
+ const selector = "0x095ea7b3";
765
+ const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
766
+ const paddedAmount = amount.toString(16).padStart(64, "0");
767
+ return selector + paddedSpender + paddedAmount;
768
+ }
769
+ /**
770
+ * Wait for a transaction receipt by polling eth_getTransactionReceipt.
771
+ */
772
+ async waitForReceipt(rpcUrl, txHash, timeoutMs = 3e4) {
773
+ const start = Date.now();
774
+ while (Date.now() - start < timeoutMs) {
775
+ try {
776
+ const response = await fetch(rpcUrl, {
777
+ method: "POST",
778
+ headers: { "Content-Type": "application/json" },
779
+ body: JSON.stringify({
780
+ jsonrpc: "2.0",
781
+ id: 1,
782
+ method: "eth_getTransactionReceipt",
783
+ params: [txHash]
784
+ })
785
+ });
786
+ const result = await response.json();
787
+ if (result.result) {
788
+ if (result.result.status === "0x0") {
789
+ throw new Error(`Approval transaction reverted: ${txHash}`);
790
+ }
791
+ return;
792
+ }
793
+ } catch (err) {
794
+ if (err instanceof Error && err.message.includes("reverted")) throw err;
795
+ }
796
+ await new Promise((r) => setTimeout(r, 2e3));
797
+ }
798
+ throw new Error(`Approval transaction receipt timeout after ${timeoutMs}ms: ${txHash}`);
799
+ }
800
+ /**
801
+ * Read gas price via eth_gasPrice RPC call.
802
+ */
803
+ async readGasPrice(rpcUrl) {
804
+ try {
805
+ const response = await fetch(rpcUrl, {
806
+ method: "POST",
807
+ headers: { "Content-Type": "application/json" },
808
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_gasPrice", params: [] })
809
+ });
810
+ const result = await response.json();
811
+ return result.result ? BigInt(result.result) : 50000000n;
812
+ } catch {
813
+ return 50000000n;
814
+ }
815
+ }
816
+ /**
817
+ * Read transaction count (nonce) via eth_getTransactionCount RPC call.
818
+ */
819
+ async readNonce(rpcUrl, address) {
820
+ try {
821
+ const response = await fetch(rpcUrl, {
822
+ method: "POST",
823
+ headers: { "Content-Type": "application/json" },
824
+ body: JSON.stringify({
825
+ jsonrpc: "2.0",
826
+ id: 1,
827
+ method: "eth_getTransactionCount",
828
+ params: [address, "latest"]
829
+ })
830
+ });
831
+ const result = await response.json();
832
+ return result.result ? parseInt(result.result, 16) : 0;
833
+ } catch {
834
+ return 0;
835
+ }
836
+ }
837
+ /**
838
+ * Calculate how much to approve based on the facilitator's approval strategy.
839
+ * Buffered approvals reduce the number of on-chain approval txs for micropayments.
840
+ */
841
+ calculateApprovalAmount(paymentAmount, fee, strategy) {
842
+ const total = BigInt(paymentAmount) + BigInt(fee);
843
+ if (!strategy || strategy.mode === "exact") {
844
+ return total;
845
+ }
846
+ const multiple = BigInt(strategy.defaultMultiple ?? 10);
847
+ const buffered = total * multiple;
848
+ if (strategy.maxCapUsd) {
849
+ const decimals = this.inferDecimals(paymentAmount);
850
+ const maxCap = BigInt(Math.floor(strategy.maxCapUsd * Math.pow(10, decimals)));
851
+ if (buffered > maxCap) return maxCap;
852
+ }
853
+ if (strategy.exactAboveUsd) {
854
+ const decimals = this.inferDecimals(paymentAmount);
855
+ const threshold = BigInt(Math.floor(strategy.exactAboveUsd * Math.pow(10, decimals)));
856
+ if (BigInt(paymentAmount) > threshold) return total;
857
+ }
858
+ return buffered;
859
+ }
860
+ /**
861
+ * Infer token decimals from payment amount magnitude.
862
+ * BSC stablecoins use 18 decimals, all others use 6.
863
+ * A $1 payment is 1000000 (6 dec) or 1000000000000000000 (18 dec).
864
+ * If the amount has > 12 digits, it's almost certainly 18 decimals.
865
+ */
866
+ inferDecimals(amount) {
867
+ return amount.length > 12 ? 18 : 6;
868
+ }
443
869
  };
444
870
  function createEvmAdapter(config) {
445
871
  return new EvmAdapter(config);
@@ -453,6 +879,9 @@ function isKnownUSDC(asset) {
453
879
  for (const addr of Object.values(USDC_ADDRESSES)) {
454
880
  if (addr.toLowerCase() === lc) return true;
455
881
  }
882
+ for (const addr of Object.keys(BSC_STABLECOIN_ADDRESSES)) {
883
+ if (addr.toLowerCase() === lc) return true;
884
+ }
456
885
  return false;
457
886
  }
458
887
 
@@ -561,6 +990,19 @@ function createX402Client(config) {
561
990
  }
562
991
  return candidates[0];
563
992
  }
993
+ function getChainDisplayName(network, adapterName) {
994
+ const names = {
995
+ "eip155:56": "BSC",
996
+ "eip155:8453": "Base",
997
+ "eip155:84532": "Base Sepolia",
998
+ "eip155:42161": "Arbitrum",
999
+ "eip155:137": "Polygon",
1000
+ "eip155:10": "Optimism",
1001
+ "eip155:43114": "Avalanche",
1002
+ "eip155:1": "Ethereum"
1003
+ };
1004
+ return names[network] || adapterName;
1005
+ }
564
1006
  function getRpcUrl(network, adapter) {
565
1007
  return rpcUrls[network] || adapter.getDefaultRpcUrl(network);
566
1008
  }
@@ -653,6 +1095,9 @@ function createX402Client(config) {
653
1095
  accepted: accept,
654
1096
  payload
655
1097
  };
1098
+ if (signedTx.extensions) {
1099
+ paymentSignature.extensions = signedTx.extensions;
1100
+ }
656
1101
  const paymentSignatureHeader = btoa(JSON.stringify(paymentSignature));
657
1102
  const passResponse = await customFetch(passUrl, {
658
1103
  ...init,
@@ -775,10 +1220,10 @@ function createX402Client(config) {
775
1220
  const balance = await adapter.getBalance(accept, wallet, rpcUrl);
776
1221
  const requiredAmount = Number(paymentAmount) / Math.pow(10, decimals);
777
1222
  if (balance < requiredAmount) {
778
- const network = adapter.name === "EVM" ? "Base" : "Solana";
1223
+ const chainName = getChainDisplayName(accept.network, adapter.name);
779
1224
  throw new X402Error(
780
1225
  "insufficient_balance",
781
- `Insufficient USDC balance on ${network}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
1226
+ `Insufficient balance on ${chainName}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
782
1227
  );
783
1228
  }
784
1229
  log(`Balance OK: $${balance.toFixed(4)} >= $${requiredAmount.toFixed(4)}`);
@@ -831,6 +1276,9 @@ function createX402Client(config) {
831
1276
  accepted: accept,
832
1277
  payload
833
1278
  };
1279
+ if (signedTx.extensions) {
1280
+ paymentSignature.extensions = signedTx.extensions;
1281
+ }
834
1282
  const paymentSignatureHeader = btoa(JSON.stringify(paymentSignature));
835
1283
  log("Retrying request with payment...");
836
1284
  const retryResponse = await fetchWithRetry(input, {
@@ -196,11 +196,11 @@ var FacilitatorClient = class {
196
196
  async getFeePayer(network) {
197
197
  const supported = await this.getSupported();
198
198
  const kind = supported.kinds.find(
199
- (k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
199
+ (k) => k.x402Version === 2 && (k.scheme === "exact" || k.scheme === "exact-approval") && k.network === network
200
200
  );
201
201
  if (!kind) {
202
202
  throw new Error(
203
- `Facilitator does not support network "${network}" with scheme "exact"`
203
+ `Facilitator does not support network "${network}" with a recognized scheme`
204
204
  );
205
205
  }
206
206
  return kind.extra?.feePayer;
@@ -211,7 +211,7 @@ var FacilitatorClient = class {
211
211
  async getNetworkExtra(network) {
212
212
  const supported = await this.getSupported();
213
213
  const kind = supported.kinds.find(
214
- (k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
214
+ (k) => k.x402Version === 2 && (k.scheme === "exact" || k.scheme === "exact-approval") && k.network === network
215
215
  );
216
216
  return kind?.extra;
217
217
  }
@@ -1,5 +1,5 @@
1
- import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PayToProvider, d as PaymentRequired } from '../types-CjLMR7qs.cjs';
2
- export { g as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as PayToContext, f as PayToProviderDefaults, h as SOLANA_MAINNET_NETWORK, i as USDC_BASE, U as USDC_MINT } from '../types-CjLMR7qs.cjs';
1
+ import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PayToProvider, d as PaymentRequired } from '../types-_iT11DL0.cjs';
2
+ export { g as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as PayToContext, f as PayToProviderDefaults, h as SOLANA_MAINNET_NETWORK, i as USDC_BASE, U as USDC_MINT } from '../types-_iT11DL0.cjs';
3
3
  import { Request, RequestHandler } from 'express';
4
4
  import { SponsoredRecommendation } from '@dexterai/x402-ads-types';
5
5
  export { SPONSORED_ACCESS_EXTENSION_KEY, SponsoredAccessClientConsent, SponsoredAccessPaymentRequiredInfo, SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
@@ -1,5 +1,5 @@
1
- import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PayToProvider, d as PaymentRequired } from '../types-CjLMR7qs.js';
2
- export { g as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as PayToContext, f as PayToProviderDefaults, h as SOLANA_MAINNET_NETWORK, i as USDC_BASE, U as USDC_MINT } from '../types-CjLMR7qs.js';
1
+ import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, c as PayToProvider, d as PaymentRequired } from '../types-_iT11DL0.js';
2
+ export { g as AccessPassClaims, A as AccessPassClientConfig, b as AccessPassInfo, a as AccessPassTier, B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, e as PayToContext, f as PayToProviderDefaults, h as SOLANA_MAINNET_NETWORK, i as USDC_BASE, U as USDC_MINT } from '../types-_iT11DL0.js';
3
3
  import { Request, RequestHandler } from 'express';
4
4
  import { SponsoredRecommendation } from '@dexterai/x402-ads-types';
5
5
  export { SPONSORED_ACCESS_EXTENSION_KEY, SponsoredAccessClientConsent, SponsoredAccessPaymentRequiredInfo, SponsoredAccessSettlementInfo, SponsoredRecommendation } from '@dexterai/x402-ads-types';
@@ -127,11 +127,11 @@ var FacilitatorClient = class {
127
127
  async getFeePayer(network) {
128
128
  const supported = await this.getSupported();
129
129
  const kind = supported.kinds.find(
130
- (k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
130
+ (k) => k.x402Version === 2 && (k.scheme === "exact" || k.scheme === "exact-approval") && k.network === network
131
131
  );
132
132
  if (!kind) {
133
133
  throw new Error(
134
- `Facilitator does not support network "${network}" with scheme "exact"`
134
+ `Facilitator does not support network "${network}" with a recognized scheme`
135
135
  );
136
136
  }
137
137
  return kind.extra?.feePayer;
@@ -142,7 +142,7 @@ var FacilitatorClient = class {
142
142
  async getNetworkExtra(network) {
143
143
  const supported = await this.getSupported();
144
144
  const kind = supported.kinds.find(
145
- (k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
145
+ (k) => k.x402Version === 2 && (k.scheme === "exact" || k.scheme === "exact-approval") && k.network === network
146
146
  );
147
147
  return kind?.extra;
148
148
  }
@@ -1,5 +1,5 @@
1
- import { C as ChainAdapter, W as WalletSet } from './types-D1TGACsL.js';
2
- import { A as AccessPassClientConfig, P as PaymentAccept } from './types-CjLMR7qs.js';
1
+ import { C as ChainAdapter, W as WalletSet } from './types-DBS0XOsH.js';
2
+ import { A as AccessPassClientConfig, P as PaymentAccept } from './types-_iT11DL0.js';
3
3
  import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
4
4
 
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { C as ChainAdapter, W as WalletSet } from './types-DWhpiOBD.cjs';
2
- import { A as AccessPassClientConfig, P as PaymentAccept } from './types-CjLMR7qs.cjs';
1
+ import { C as ChainAdapter, W as WalletSet } from './types-C_aQh02s.cjs';
2
+ import { A as AccessPassClientConfig, P as PaymentAccept } from './types-_iT11DL0.cjs';
3
3
  import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
4
4
 
5
5
  /**