@dexterai/x402 1.9.4 → 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.
Files changed (40) hide show
  1. package/README.md +27 -0
  2. package/dist/adapters/index.cjs +375 -3
  3. package/dist/adapters/index.d.cts +4 -5
  4. package/dist/adapters/index.d.ts +4 -5
  5. package/dist/adapters/index.js +369 -3
  6. package/dist/client/index.cjs +570 -10
  7. package/dist/client/index.d.cts +200 -36
  8. package/dist/client/index.d.ts +200 -36
  9. package/dist/client/index.js +568 -10
  10. package/dist/react/index.cjs +404 -8
  11. package/dist/react/index.d.cts +5 -5
  12. package/dist/react/index.d.ts +5 -5
  13. package/dist/react/index.js +404 -8
  14. package/dist/server/index.cjs +3 -4
  15. package/dist/server/index.d.cts +2 -2
  16. package/dist/server/index.d.ts +2 -2
  17. package/dist/server/index.js +3 -4
  18. package/dist/{sponsored-access-D1_mINs4.d.ts → sponsored-access-DAVzu4x6.d.cts} +13 -2
  19. package/dist/{sponsored-access-Br6YPA-m.d.cts → sponsored-access-Lxa11w_X.d.ts} +13 -2
  20. package/dist/types-D1u7iu8n.d.cts +304 -0
  21. package/dist/types-YQlJI5E3.d.ts +304 -0
  22. package/dist/{types-CjLMR7qs.d.cts → types-_iT11DL0.d.cts} +2 -2
  23. package/dist/{types-CjLMR7qs.d.ts → types-_iT11DL0.d.ts} +2 -2
  24. package/dist/utils/index.cjs +0 -1
  25. package/dist/utils/index.js +0 -1
  26. package/package.json +1 -1
  27. package/dist/adapters/index.cjs.map +0 -1
  28. package/dist/adapters/index.js.map +0 -1
  29. package/dist/client/index.cjs.map +0 -1
  30. package/dist/client/index.js.map +0 -1
  31. package/dist/react/index.cjs.map +0 -1
  32. package/dist/react/index.js.map +0 -1
  33. package/dist/server/index.cjs.map +0 -1
  34. package/dist/server/index.js.map +0 -1
  35. package/dist/solana-BcOfK6Eq.d.cts +0 -132
  36. package/dist/solana-Cxr5byPa.d.ts +0 -132
  37. package/dist/types-BIHhO2-I.d.ts +0 -123
  38. package/dist/types-CfKflCZO.d.cts +0 -123
  39. package/dist/utils/index.cjs.map +0 -1
  40. package/dist/utils/index.js.map +0 -1
@@ -249,16 +249,40 @@ function createSolanaAdapter(config) {
249
249
  }
250
250
 
251
251
  // src/adapters/evm.ts
252
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
253
+ var X402_EXACT_PERMIT2_PROXY = "0x402085c248EeA27D92E8b30b2C58ed07f9E20001";
254
+ var PERMIT2_WITNESS_TYPES = {
255
+ PermitWitnessTransferFrom: [
256
+ { name: "permitted", type: "TokenPermissions" },
257
+ { name: "spender", type: "address" },
258
+ { name: "nonce", type: "uint256" },
259
+ { name: "deadline", type: "uint256" },
260
+ { name: "witness", type: "Witness" }
261
+ ],
262
+ TokenPermissions: [
263
+ { name: "token", type: "address" },
264
+ { name: "amount", type: "uint256" }
265
+ ],
266
+ Witness: [
267
+ { name: "to", type: "address" },
268
+ { name: "validAfter", type: "uint256" }
269
+ ]
270
+ };
271
+ var MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
252
272
  var BASE_MAINNET = "eip155:8453";
253
273
  var BASE_SEPOLIA = "eip155:84532";
254
274
  var ARBITRUM_ONE = "eip155:42161";
255
275
  var POLYGON = "eip155:137";
256
276
  var OPTIMISM = "eip155:10";
257
277
  var AVALANCHE = "eip155:43114";
278
+ var BSC_MAINNET = "eip155:56";
258
279
  var SKALE_BASE = "eip155:1187947933";
259
280
  var SKALE_BASE_SEPOLIA = "eip155:324705682";
260
281
  var ETHEREUM_MAINNET = "eip155:1";
282
+ var BSC_USDT = "0x55d398326f99059fF775485246999027B3197955";
283
+ var BSC_USDC = "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d";
261
284
  var CHAIN_IDS = {
285
+ [BSC_MAINNET]: 56,
262
286
  [BASE_MAINNET]: 8453,
263
287
  [BASE_SEPOLIA]: 84532,
264
288
  [ARBITRUM_ONE]: 42161,
@@ -270,6 +294,7 @@ var CHAIN_IDS = {
270
294
  [ETHEREUM_MAINNET]: 1
271
295
  };
272
296
  var DEFAULT_RPC_URLS2 = {
297
+ [BSC_MAINNET]: "https://bsc-dataseed1.binance.org",
273
298
  [BASE_MAINNET]: "https://api.dexter.cash/api/base/rpc",
274
299
  [BASE_SEPOLIA]: "https://sepolia.base.org",
275
300
  [ARBITRUM_ONE]: "https://arb1.arbitrum.io/rpc",
@@ -281,6 +306,7 @@ var DEFAULT_RPC_URLS2 = {
281
306
  [ETHEREUM_MAINNET]: "https://eth.llamarpc.com"
282
307
  };
283
308
  var USDC_ADDRESSES = {
309
+ [BSC_MAINNET]: BSC_USDC,
284
310
  [BASE_MAINNET]: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
285
311
  [BASE_SEPOLIA]: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
286
312
  [ARBITRUM_ONE]: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
@@ -291,6 +317,10 @@ var USDC_ADDRESSES = {
291
317
  [SKALE_BASE_SEPOLIA]: "0x2e08028E3C4c2356572E096d8EF835cD5C6030bD",
292
318
  [ETHEREUM_MAINNET]: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
293
319
  };
320
+ var BSC_STABLECOIN_ADDRESSES = {
321
+ [BSC_USDT]: { symbol: "USDT", decimals: 18 },
322
+ [BSC_USDC]: { symbol: "USDC", decimals: 18 }
323
+ };
294
324
  function isEvmWallet(wallet) {
295
325
  if (!wallet || typeof wallet !== "object") return false;
296
326
  const w = wallet;
@@ -298,7 +328,7 @@ function isEvmWallet(wallet) {
298
328
  }
299
329
  var EvmAdapter = class {
300
330
  name = "EVM";
301
- networks = [BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
331
+ networks = [BSC_MAINNET, BASE_MAINNET, BASE_SEPOLIA, ETHEREUM_MAINNET, ARBITRUM_ONE];
302
332
  config;
303
333
  log;
304
334
  constructor(config = {}) {
@@ -309,6 +339,7 @@ var EvmAdapter = class {
309
339
  canHandle(network) {
310
340
  if (this.networks.includes(network)) return true;
311
341
  if (network === "base") return true;
342
+ if (network === "bsc") return true;
312
343
  if (network === "ethereum") return true;
313
344
  if (network === "arbitrum") return true;
314
345
  if (network.startsWith("eip155:")) return true;
@@ -322,6 +353,7 @@ var EvmAdapter = class {
322
353
  return DEFAULT_RPC_URLS2[network];
323
354
  }
324
355
  if (network === "base") return DEFAULT_RPC_URLS2[BASE_MAINNET];
356
+ if (network === "bsc") return DEFAULT_RPC_URLS2[BSC_MAINNET];
325
357
  if (network === "ethereum") return DEFAULT_RPC_URLS2[ETHEREUM_MAINNET];
326
358
  if (network === "arbitrum") return DEFAULT_RPC_URLS2[ARBITRUM_ONE];
327
359
  return DEFAULT_RPC_URLS2[BASE_MAINNET];
@@ -341,6 +373,7 @@ var EvmAdapter = class {
341
373
  return parseInt(chainIdStr, 10);
342
374
  }
343
375
  if (network === "base") return 8453;
376
+ if (network === "bsc") return 56;
344
377
  if (network === "ethereum") return 1;
345
378
  if (network === "arbitrum") return 42161;
346
379
  return 8453;
@@ -390,13 +423,19 @@ var EvmAdapter = class {
390
423
  const paddedAddress = address.slice(2).toLowerCase().padStart(64, "0");
391
424
  return selector + paddedAddress;
392
425
  }
393
- async buildTransaction(accept, wallet, _rpcUrl) {
426
+ async buildTransaction(accept, wallet, rpcUrl) {
394
427
  if (!isEvmWallet(wallet)) {
395
428
  throw new Error("Invalid EVM wallet");
396
429
  }
397
430
  if (!wallet.address) {
398
431
  throw new Error("Wallet not connected");
399
432
  }
433
+ if (accept.scheme === "exact-approval") {
434
+ return this.buildApprovalTransaction(accept, wallet, rpcUrl);
435
+ }
436
+ if (accept.extra?.assetTransferMethod === "permit2") {
437
+ return this.buildPermit2Transaction(accept, wallet, rpcUrl);
438
+ }
400
439
  const { payTo, asset, extra } = accept;
401
440
  const amount = accept.amount ?? accept.maxAmountRequired;
402
441
  if (!amount) {
@@ -467,6 +506,325 @@ var EvmAdapter = class {
467
506
  signature
468
507
  };
469
508
  }
509
+ // ===========================================================================
510
+ // exact-approval: BSC and other chains without EIP-3009
511
+ // ===========================================================================
512
+ /**
513
+ * Build a payment transaction for chains that use the approval-based scheme.
514
+ * The facilitator's /supported response provides the EIP-712 domain and types
515
+ * in accept.extra, so the client doesn't hardcode any contract addresses.
516
+ */
517
+ async buildApprovalTransaction(accept, wallet, rpcUrl) {
518
+ const { payTo, asset, extra } = accept;
519
+ const amount = accept.amount ?? accept.maxAmountRequired;
520
+ if (!amount) {
521
+ throw new Error("Missing amount in payment requirements");
522
+ }
523
+ const facilitatorContract = extra?.facilitatorContract;
524
+ if (!facilitatorContract) {
525
+ throw new Error(
526
+ "exact-approval scheme requires extra.facilitatorContract from the facilitator. The /supported endpoint should provide this."
527
+ );
528
+ }
529
+ if (!wallet.signTypedData) {
530
+ throw new Error("Wallet does not support signTypedData (EIP-712)");
531
+ }
532
+ this.log("Building approval-based transaction:", {
533
+ from: wallet.address,
534
+ to: payTo,
535
+ amount,
536
+ asset,
537
+ network: accept.network,
538
+ facilitatorContract
539
+ });
540
+ const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
541
+ const fee = extra?.fee ?? "0";
542
+ const totalNeeded = BigInt(amount) + BigInt(fee);
543
+ const currentAllowance = await this.readAllowance(url, asset, wallet.address, facilitatorContract);
544
+ if (currentAllowance < totalNeeded) {
545
+ if (!wallet.sendTransaction) {
546
+ throw new Error(
547
+ "BSC payments require a wallet that supports sendTransaction for the one-time token approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
548
+ );
549
+ }
550
+ const approvalAmount = this.calculateApprovalAmount(amount, fee, extra?.approvalStrategy);
551
+ this.log(`Approving ${approvalAmount} for ${facilitatorContract} (current allowance: ${currentAllowance})`);
552
+ const approveTxHash = await wallet.sendTransaction({
553
+ to: asset,
554
+ data: this.encodeApprove(facilitatorContract, approvalAmount),
555
+ value: 0n
556
+ });
557
+ this.log(`Approval tx sent: ${approveTxHash}`);
558
+ await this.waitForReceipt(url, approveTxHash);
559
+ this.log("Approval confirmed");
560
+ } else {
561
+ this.log("Sufficient allowance, skipping approval");
562
+ }
563
+ const nonceBytes = new Uint8Array(16);
564
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
565
+ const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n).toString();
566
+ const paymentIdBytes = new Uint8Array(32);
567
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(paymentIdBytes);
568
+ const paymentId = "0x" + [...paymentIdBytes].map((b) => b.toString(16).padStart(2, "0")).join("");
569
+ const now = Math.floor(Date.now() / 1e3);
570
+ const deadline = now + (accept.maxTimeoutSeconds || 300);
571
+ const eip712Domain = extra?.eip712Domain;
572
+ const domain = eip712Domain ? {
573
+ name: eip712Domain.name,
574
+ version: eip712Domain.version,
575
+ chainId: BigInt(eip712Domain.chainId),
576
+ verifyingContract: eip712Domain.verifyingContract
577
+ } : {
578
+ name: "DexterBSCFacilitator",
579
+ version: "1",
580
+ chainId: BigInt(this.getChainId(accept.network)),
581
+ verifyingContract: facilitatorContract
582
+ };
583
+ const types = extra?.eip712Types ?? {
584
+ Payment: [
585
+ { name: "from", type: "address" },
586
+ { name: "to", type: "address" },
587
+ { name: "token", type: "address" },
588
+ { name: "amount", type: "uint256" },
589
+ { name: "fee", type: "uint256" },
590
+ { name: "nonce", type: "uint256" },
591
+ { name: "deadline", type: "uint256" },
592
+ { name: "paymentId", type: "bytes32" }
593
+ ]
594
+ };
595
+ const message = {
596
+ from: wallet.address,
597
+ to: payTo,
598
+ token: asset,
599
+ amount: BigInt(amount),
600
+ fee: BigInt(fee),
601
+ nonce: BigInt(nonce),
602
+ deadline: BigInt(deadline),
603
+ paymentId
604
+ };
605
+ const signature = await wallet.signTypedData({
606
+ domain,
607
+ types,
608
+ primaryType: "Payment",
609
+ message
610
+ });
611
+ this.log("EIP-712 Payment signature obtained");
612
+ const payload = {
613
+ from: wallet.address,
614
+ to: payTo,
615
+ token: asset,
616
+ amount,
617
+ fee,
618
+ nonce,
619
+ deadline,
620
+ paymentId,
621
+ signature
622
+ };
623
+ return {
624
+ serialized: JSON.stringify(payload),
625
+ signature
626
+ };
627
+ }
628
+ // ===========================================================================
629
+ // Permit2: Universal ERC-20 payments via Uniswap's Permit2 contract
630
+ // ===========================================================================
631
+ /**
632
+ * Build a Permit2 payment transaction. Used when the facilitator signals
633
+ * assetTransferMethod: "permit2" in extra (e.g., BSC where EIP-3009 is unavailable).
634
+ *
635
+ * Flow:
636
+ * 1. Check if token has approved the Permit2 contract. If not, approve(Permit2, maxUint256).
637
+ * 2. Sign EIP-712 PermitWitnessTransferFrom against the Permit2 contract.
638
+ * 3. Return { permit2Authorization, signature } payload for the facilitator.
639
+ */
640
+ async buildPermit2Transaction(accept, wallet, rpcUrl) {
641
+ const { payTo, asset } = accept;
642
+ const amount = accept.amount ?? accept.maxAmountRequired;
643
+ if (!amount) {
644
+ throw new Error("Missing amount in payment requirements");
645
+ }
646
+ if (!wallet.signTypedData) {
647
+ throw new Error("Wallet does not support signTypedData (EIP-712)");
648
+ }
649
+ this.log("Building Permit2 transaction:", {
650
+ from: wallet.address,
651
+ to: payTo,
652
+ amount,
653
+ asset,
654
+ network: accept.network
655
+ });
656
+ const url = rpcUrl || this.getDefaultRpcUrl(accept.network);
657
+ const currentAllowance = await this.readAllowance(url, asset, wallet.address, PERMIT2_ADDRESS);
658
+ if (currentAllowance < BigInt(amount)) {
659
+ if (!wallet.sendTransaction) {
660
+ throw new Error(
661
+ "Permit2 payments require a wallet that supports sendTransaction for the one-time Permit2 approval. Use createEvmKeypairWallet() or a browser wallet with transaction support."
662
+ );
663
+ }
664
+ this.log(`Approving Permit2 for ${asset} (current allowance: ${currentAllowance})`);
665
+ const approveTxHash = await wallet.sendTransaction({
666
+ to: asset,
667
+ data: this.encodeApprove(PERMIT2_ADDRESS, MAX_UINT256),
668
+ value: 0n
669
+ });
670
+ this.log(`Permit2 approval tx sent: ${approveTxHash}`);
671
+ await this.waitForReceipt(url, approveTxHash);
672
+ this.log("Permit2 approval confirmed");
673
+ } else {
674
+ this.log("Sufficient Permit2 allowance, skipping approval");
675
+ }
676
+ const nonceBytes = new Uint8Array(32);
677
+ (globalThis.crypto ?? (await import("crypto")).webcrypto).getRandomValues(nonceBytes);
678
+ const nonce = [...nonceBytes].reduce((acc, b) => acc * 256n + BigInt(b), 0n);
679
+ const now = Math.floor(Date.now() / 1e3);
680
+ const validAfter = now - 600;
681
+ const deadline = now + (accept.maxTimeoutSeconds || 300);
682
+ const chainId = this.getChainId(accept.network);
683
+ const domain = {
684
+ name: "Permit2",
685
+ chainId: BigInt(chainId),
686
+ verifyingContract: PERMIT2_ADDRESS
687
+ };
688
+ const message = {
689
+ permitted: {
690
+ token: asset,
691
+ amount: BigInt(amount)
692
+ },
693
+ spender: X402_EXACT_PERMIT2_PROXY,
694
+ nonce,
695
+ deadline: BigInt(deadline),
696
+ witness: {
697
+ to: payTo,
698
+ validAfter: BigInt(validAfter)
699
+ }
700
+ };
701
+ const signature = await wallet.signTypedData({
702
+ domain,
703
+ types: PERMIT2_WITNESS_TYPES,
704
+ primaryType: "PermitWitnessTransferFrom",
705
+ message
706
+ });
707
+ this.log("Permit2 PermitWitnessTransferFrom signature obtained");
708
+ const payload = {
709
+ signature,
710
+ permit2Authorization: {
711
+ from: wallet.address,
712
+ permitted: {
713
+ token: asset,
714
+ amount
715
+ },
716
+ spender: X402_EXACT_PERMIT2_PROXY,
717
+ nonce: nonce.toString(),
718
+ deadline: String(deadline),
719
+ witness: {
720
+ to: payTo,
721
+ validAfter: String(validAfter)
722
+ }
723
+ }
724
+ };
725
+ return {
726
+ serialized: JSON.stringify(payload),
727
+ signature
728
+ };
729
+ }
730
+ /**
731
+ * Read ERC-20 allowance via raw eth_call (no viem dependency needed).
732
+ */
733
+ async readAllowance(rpcUrl, token, owner, spender) {
734
+ const selector = "0xdd62ed3e";
735
+ const paddedOwner = owner.slice(2).toLowerCase().padStart(64, "0");
736
+ const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
737
+ const data = selector + paddedOwner + paddedSpender;
738
+ try {
739
+ const response = await fetch(rpcUrl, {
740
+ method: "POST",
741
+ headers: { "Content-Type": "application/json" },
742
+ body: JSON.stringify({
743
+ jsonrpc: "2.0",
744
+ id: 1,
745
+ method: "eth_call",
746
+ params: [{ to: token, data }, "latest"]
747
+ })
748
+ });
749
+ const result = await response.json();
750
+ if (result.error || !result.result || result.result === "0x") return 0n;
751
+ return BigInt(result.result);
752
+ } catch {
753
+ return 0n;
754
+ }
755
+ }
756
+ /**
757
+ * Encode ERC-20 approve(address,uint256) calldata.
758
+ */
759
+ encodeApprove(spender, amount) {
760
+ const selector = "0x095ea7b3";
761
+ const paddedSpender = spender.slice(2).toLowerCase().padStart(64, "0");
762
+ const paddedAmount = amount.toString(16).padStart(64, "0");
763
+ return selector + paddedSpender + paddedAmount;
764
+ }
765
+ /**
766
+ * Wait for a transaction receipt by polling eth_getTransactionReceipt.
767
+ */
768
+ async waitForReceipt(rpcUrl, txHash, timeoutMs = 3e4) {
769
+ const start = Date.now();
770
+ while (Date.now() - start < timeoutMs) {
771
+ try {
772
+ const response = await fetch(rpcUrl, {
773
+ method: "POST",
774
+ headers: { "Content-Type": "application/json" },
775
+ body: JSON.stringify({
776
+ jsonrpc: "2.0",
777
+ id: 1,
778
+ method: "eth_getTransactionReceipt",
779
+ params: [txHash]
780
+ })
781
+ });
782
+ const result = await response.json();
783
+ if (result.result) {
784
+ if (result.result.status === "0x0") {
785
+ throw new Error(`Approval transaction reverted: ${txHash}`);
786
+ }
787
+ return;
788
+ }
789
+ } catch (err) {
790
+ if (err instanceof Error && err.message.includes("reverted")) throw err;
791
+ }
792
+ await new Promise((r) => setTimeout(r, 2e3));
793
+ }
794
+ throw new Error(`Approval transaction receipt timeout after ${timeoutMs}ms: ${txHash}`);
795
+ }
796
+ /**
797
+ * Calculate how much to approve based on the facilitator's approval strategy.
798
+ * Buffered approvals reduce the number of on-chain approval txs for micropayments.
799
+ */
800
+ calculateApprovalAmount(paymentAmount, fee, strategy) {
801
+ const total = BigInt(paymentAmount) + BigInt(fee);
802
+ if (!strategy || strategy.mode === "exact") {
803
+ return total;
804
+ }
805
+ const multiple = BigInt(strategy.defaultMultiple ?? 10);
806
+ const buffered = total * multiple;
807
+ if (strategy.maxCapUsd) {
808
+ const decimals = this.inferDecimals(paymentAmount);
809
+ const maxCap = BigInt(Math.floor(strategy.maxCapUsd * Math.pow(10, decimals)));
810
+ if (buffered > maxCap) return maxCap;
811
+ }
812
+ if (strategy.exactAboveUsd) {
813
+ const decimals = this.inferDecimals(paymentAmount);
814
+ const threshold = BigInt(Math.floor(strategy.exactAboveUsd * Math.pow(10, decimals)));
815
+ if (BigInt(paymentAmount) > threshold) return total;
816
+ }
817
+ return buffered;
818
+ }
819
+ /**
820
+ * Infer token decimals from payment amount magnitude.
821
+ * BSC stablecoins use 18 decimals, all others use 6.
822
+ * A $1 payment is 1000000 (6 dec) or 1000000000000000000 (18 dec).
823
+ * If the amount has > 12 digits, it's almost certainly 18 decimals.
824
+ */
825
+ inferDecimals(amount) {
826
+ return amount.length > 12 ? 18 : 6;
827
+ }
470
828
  };
471
829
  function createEvmAdapter(config) {
472
830
  return new EvmAdapter(config);
@@ -480,6 +838,9 @@ function isKnownUSDC(asset) {
480
838
  for (const addr of Object.values(USDC_ADDRESSES)) {
481
839
  if (addr.toLowerCase() === lc) return true;
482
840
  }
841
+ for (const addr of Object.keys(BSC_STABLECOIN_ADDRESSES)) {
842
+ if (addr.toLowerCase() === lc) return true;
843
+ }
483
844
  return false;
484
845
  }
485
846
 
@@ -499,10 +860,33 @@ function createX402Client(config) {
499
860
  fetch: customFetch = globalThis.fetch,
500
861
  verbose = false,
501
862
  accessPass: accessPassConfig,
502
- onPaymentRequired
863
+ onPaymentRequired,
864
+ maxRetries = 0,
865
+ retryDelayMs = 500
503
866
  } = config;
504
867
  const log = verbose ? console.log.bind(console, "[x402]") : () => {
505
868
  };
869
+ async function fetchWithRetry(input, init) {
870
+ let lastError;
871
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
872
+ try {
873
+ const response = await customFetch(input, init);
874
+ if (response.status >= 502 && response.status <= 504 && attempt < maxRetries) {
875
+ log(`Retry ${attempt + 1}/${maxRetries}: server returned ${response.status}`);
876
+ await new Promise((r) => setTimeout(r, retryDelayMs * Math.pow(2, attempt)));
877
+ continue;
878
+ }
879
+ return response;
880
+ } catch (err) {
881
+ lastError = err;
882
+ if (attempt < maxRetries) {
883
+ log(`Retry ${attempt + 1}/${maxRetries}: ${err instanceof Error ? err.message : "network error"}`);
884
+ await new Promise((r) => setTimeout(r, retryDelayMs * Math.pow(2, attempt)));
885
+ }
886
+ }
887
+ }
888
+ throw lastError;
889
+ }
506
890
  const passCache = /* @__PURE__ */ new Map();
507
891
  function getCachedPass(url) {
508
892
  try {
@@ -565,6 +949,19 @@ function createX402Client(config) {
565
949
  }
566
950
  return candidates[0];
567
951
  }
952
+ function getChainDisplayName(network, adapterName) {
953
+ const names = {
954
+ "eip155:56": "BSC",
955
+ "eip155:8453": "Base",
956
+ "eip155:84532": "Base Sepolia",
957
+ "eip155:42161": "Arbitrum",
958
+ "eip155:137": "Polygon",
959
+ "eip155:10": "Optimism",
960
+ "eip155:43114": "Avalanche",
961
+ "eip155:1": "Ethereum"
962
+ };
963
+ return names[network] || adapterName;
964
+ }
568
965
  function getRpcUrl(network, adapter) {
569
966
  return rpcUrls[network] || adapter.getDefaultRpcUrl(network);
570
967
  }
@@ -702,7 +1099,7 @@ function createX402Client(config) {
702
1099
  }
703
1100
  }
704
1101
  }
705
- const response = await customFetch(input, init);
1102
+ const response = await fetchWithRetry(input, init);
706
1103
  if (response.status !== 402) {
707
1104
  return response;
708
1105
  }
@@ -779,10 +1176,10 @@ function createX402Client(config) {
779
1176
  const balance = await adapter.getBalance(accept, wallet, rpcUrl);
780
1177
  const requiredAmount = Number(paymentAmount) / Math.pow(10, decimals);
781
1178
  if (balance < requiredAmount) {
782
- const network = adapter.name === "EVM" ? "Base" : "Solana";
1179
+ const chainName = getChainDisplayName(accept.network, adapter.name);
783
1180
  throw new X402Error(
784
1181
  "insufficient_balance",
785
- `Insufficient USDC balance on ${network}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
1182
+ `Insufficient balance on ${chainName}. Have $${balance.toFixed(4)}, need $${requiredAmount.toFixed(4)}`
786
1183
  );
787
1184
  }
788
1185
  log(`Balance OK: $${balance.toFixed(4)} >= $${requiredAmount.toFixed(4)}`);
@@ -837,7 +1234,7 @@ function createX402Client(config) {
837
1234
  };
838
1235
  const paymentSignatureHeader = btoa(JSON.stringify(paymentSignature));
839
1236
  log("Retrying request with payment...");
840
- const retryResponse = await customFetch(input, {
1237
+ const retryResponse = await fetchWithRetry(input, {
841
1238
  ...init,
842
1239
  headers: {
843
1240
  ...init?.headers || {},
@@ -1347,4 +1744,3 @@ function useAccessPass(config) {
1347
1744
  useAccessPass,
1348
1745
  useX402Payment
1349
1746
  });
1350
- //# sourceMappingURL=index.cjs.map
@@ -1,10 +1,10 @@
1
- import { a as X402Client } from '../sponsored-access-Br6YPA-m.cjs';
2
- export { f as fireImpressionBeacon, b as getSponsoredRecommendations } from '../sponsored-access-Br6YPA-m.cjs';
3
- import { W as WalletSet, B as BalanceInfo } from '../types-CfKflCZO.cjs';
1
+ import { a as X402Client } from '../sponsored-access-DAVzu4x6.cjs';
2
+ export { f as fireImpressionBeacon, b as getSponsoredRecommendations } from '../sponsored-access-DAVzu4x6.cjs';
3
+ import { W as WalletSet, d as BalanceInfo } from '../types-D1u7iu8n.cjs';
4
4
  import { SponsoredRecommendation } from '@dexterai/x402-ads-types';
5
5
  export { SponsoredRecommendation } from '@dexterai/x402-ads-types';
6
- import { a as AccessPassTier } from '../types-CjLMR7qs.cjs';
7
- export { A as AccessPassClientConfig, b as AccessPassInfo, X as X402Error } from '../types-CjLMR7qs.cjs';
6
+ import { a as AccessPassTier } from '../types-_iT11DL0.cjs';
7
+ export { A as AccessPassClientConfig, b as AccessPassInfo, X as X402Error } from '../types-_iT11DL0.cjs';
8
8
 
9
9
  /**
10
10
  * React Hook for x402 v2 Payments
@@ -1,10 +1,10 @@
1
- import { a as X402Client } from '../sponsored-access-D1_mINs4.js';
2
- export { f as fireImpressionBeacon, b as getSponsoredRecommendations } from '../sponsored-access-D1_mINs4.js';
3
- import { W as WalletSet, B as BalanceInfo } from '../types-BIHhO2-I.js';
1
+ import { a as X402Client } from '../sponsored-access-Lxa11w_X.js';
2
+ export { f as fireImpressionBeacon, b as getSponsoredRecommendations } from '../sponsored-access-Lxa11w_X.js';
3
+ import { W as WalletSet, d as BalanceInfo } from '../types-YQlJI5E3.js';
4
4
  import { SponsoredRecommendation } from '@dexterai/x402-ads-types';
5
5
  export { SponsoredRecommendation } from '@dexterai/x402-ads-types';
6
- import { a as AccessPassTier } from '../types-CjLMR7qs.js';
7
- export { A as AccessPassClientConfig, b as AccessPassInfo, X as X402Error } from '../types-CjLMR7qs.js';
6
+ import { a as AccessPassTier } from '../types-_iT11DL0.js';
7
+ export { A as AccessPassClientConfig, b as AccessPassInfo, X as X402Error } from '../types-_iT11DL0.js';
8
8
 
9
9
  /**
10
10
  * React Hook for x402 v2 Payments