coinley-pay 0.0.76 → 0.0.78

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.
@@ -28649,7 +28649,16 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28649
28649
  }
28650
28650
  };
28651
28651
  const createPayment = async (config) => {
28652
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
28652
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
28653
+ const attemptMeta = (config == null ? void 0 : config.__coinleyAttemptMeta) || null;
28654
+ const attemptStillCurrent = () => {
28655
+ if (!(attemptMeta == null ? void 0 : attemptMeta.isStillCurrent)) return true;
28656
+ try {
28657
+ return !!attemptMeta.isStillCurrent();
28658
+ } catch {
28659
+ return false;
28660
+ }
28661
+ };
28653
28662
  try {
28654
28663
  setLoading(true);
28655
28664
  setError("");
@@ -28691,22 +28700,37 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28691
28700
  console.log(" 📈 Coinley Percentage:", (_g = payment.payment) == null ? void 0 : _g.coinleyPercentage, `(${(((_h = payment.payment) == null ? void 0 : _h.coinleyPercentage) || 0) / 100}%)`);
28692
28701
  console.log("📊 Contract Address:", (_i = payment.payment) == null ? void 0 : _i.contractAddress);
28693
28702
  console.log("📊 Split Params:", (_j = payment.payment) == null ? void 0 : _j.splitParams);
28703
+ console.log("📊 Attempt ID:", (attemptMeta == null ? void 0 : attemptMeta.attemptId) || "(none)");
28694
28704
  console.log("📊 =============================================");
28695
- setPaymentData(payment.payment);
28705
+ if (!attemptStillCurrent()) {
28706
+ console.log("[PaymentFlow] Dropping stale createPayment response", { attemptId: attemptMeta == null ? void 0 : attemptMeta.attemptId, paymentId: (_k = payment.payment) == null ? void 0 : _k.id });
28707
+ const stale = new Error("Your payment quote changed while you were switching networks or tokens. Please try again.");
28708
+ stale.code = "STALE_ATTEMPT";
28709
+ throw stale;
28710
+ }
28711
+ setPaymentData({
28712
+ ...payment.payment,
28713
+ __coinleyAttemptId: (attemptMeta == null ? void 0 : attemptMeta.attemptId) ?? null,
28714
+ __coinleySelectionSnapshot: (attemptMeta == null ? void 0 : attemptMeta.selectionSnapshot) ?? null
28715
+ });
28696
28716
  sdkAnalytics.trackPaymentCreated(
28697
- (_k = payment.payment) == null ? void 0 : _k.id,
28717
+ (_l = payment.payment) == null ? void 0 : _l.id,
28698
28718
  config.amount,
28699
28719
  selectedNetwork.shortName,
28700
28720
  selectedToken.symbol
28701
28721
  );
28702
28722
  clarityAnalytics.trackPaymentCreated(
28703
- (_l = payment.payment) == null ? void 0 : _l.id,
28723
+ (_m = payment.payment) == null ? void 0 : _m.id,
28704
28724
  config.amount,
28705
28725
  selectedNetwork.shortName,
28706
28726
  selectedToken.symbol
28707
28727
  );
28708
28728
  setCurrentStep("confirm");
28709
28729
  } catch (err) {
28730
+ if ((err == null ? void 0 : err.code) === "STALE_ATTEMPT") {
28731
+ console.log("[PaymentFlow] createPayment aborted — stale attempt");
28732
+ throw err;
28733
+ }
28710
28734
  setError(err.message);
28711
28735
  sdkAnalytics.trackError("payment_creation_failed", err.message, {
28712
28736
  amount: config.amount,
@@ -28737,7 +28761,16 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28737
28761
  return supportedChains.includes(chainId);
28738
28762
  };
28739
28763
  const createDepositPayment = async (config) => {
28740
- var _a2, _b, _c, _d, _e, _f, _g;
28764
+ var _a2, _b, _c, _d, _e, _f, _g, _h;
28765
+ const attemptMeta = (config == null ? void 0 : config.__coinleyAttemptMeta) || null;
28766
+ const attemptStillCurrent = () => {
28767
+ if (!(attemptMeta == null ? void 0 : attemptMeta.isStillCurrent)) return true;
28768
+ try {
28769
+ return !!attemptMeta.isStillCurrent();
28770
+ } catch {
28771
+ return false;
28772
+ }
28773
+ };
28741
28774
  try {
28742
28775
  setLoading(true);
28743
28776
  setError("");
@@ -28768,10 +28801,20 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28768
28801
  }
28769
28802
  };
28770
28803
  if ((paymentData == null ? void 0 : paymentData.id) && !(paymentData == null ? void 0 : paymentData.isDepositPayment)) {
28771
- depositPayload.existingPaymentId = paymentData.id;
28772
- console.log("📤 Converting existing payment to deposit:", { debugId, paymentId: paymentData.id });
28804
+ const existingAttemptId = paymentData.__coinleyAttemptId ?? null;
28805
+ const sameAttempt = !(attemptMeta == null ? void 0 : attemptMeta.attemptId) || !existingAttemptId || existingAttemptId === attemptMeta.attemptId;
28806
+ if (sameAttempt) {
28807
+ depositPayload.existingPaymentId = paymentData.id;
28808
+ console.log("📤 Converting existing payment to deposit:", { debugId, paymentId: paymentData.id, attemptId: attemptMeta == null ? void 0 : attemptMeta.attemptId });
28809
+ } else {
28810
+ console.log("📤 Not reusing existing payment — different attempt", {
28811
+ debugId,
28812
+ existingAttemptId,
28813
+ currentAttemptId: attemptMeta == null ? void 0 : attemptMeta.attemptId
28814
+ });
28815
+ }
28773
28816
  }
28774
- console.log("📤 Creating deposit payment:", { debugId, depositPayload });
28817
+ console.log("📤 Creating deposit payment:", { debugId, attemptId: attemptMeta == null ? void 0 : attemptMeta.attemptId, depositPayload });
28775
28818
  const response = await api.current.createDepositPayment(depositPayload);
28776
28819
  if (!response.success) {
28777
28820
  throw new Error(response.error || "Failed to create deposit payment");
@@ -28779,22 +28822,34 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28779
28822
  console.log("📊 ========== DEPOSIT PAYMENT CREATED ==========");
28780
28823
  console.log("📊 Payment ID:", (_b = response.payment) == null ? void 0 : _b.id);
28781
28824
  console.log("📊 Debug ID:", debugId);
28825
+ console.log("📊 Attempt ID:", (attemptMeta == null ? void 0 : attemptMeta.attemptId) || "(none)");
28782
28826
  console.log("📊 Deposit Address:", (_c = response.payment) == null ? void 0 : _c.depositAddress);
28783
28827
  console.log("📊 Amount:", (_d = response.payment) == null ? void 0 : _d.amount);
28784
28828
  console.log("📊 Expires At:", (_e = response.payment) == null ? void 0 : _e.expiresAt);
28785
28829
  console.log("📊 =============================================");
28830
+ if (!attemptStillCurrent()) {
28831
+ console.log("[PaymentFlow] Dropping stale createDepositPayment response", {
28832
+ attemptId: attemptMeta == null ? void 0 : attemptMeta.attemptId,
28833
+ paymentId: (_f = response.payment) == null ? void 0 : _f.id
28834
+ });
28835
+ const stale = new Error("Your payment quote changed while you were switching networks or tokens. Please try again.");
28836
+ stale.code = "STALE_ATTEMPT";
28837
+ throw stale;
28838
+ }
28786
28839
  setPaymentData({
28787
28840
  ...response.payment,
28788
- isDepositPayment: true
28841
+ isDepositPayment: true,
28842
+ __coinleyAttemptId: (attemptMeta == null ? void 0 : attemptMeta.attemptId) ?? null,
28843
+ __coinleySelectionSnapshot: (attemptMeta == null ? void 0 : attemptMeta.selectionSnapshot) ?? null
28789
28844
  });
28790
28845
  sdkAnalytics.trackPaymentCreated(
28791
- (_f = response.payment) == null ? void 0 : _f.id,
28846
+ (_g = response.payment) == null ? void 0 : _g.id,
28792
28847
  config.amount,
28793
28848
  selectedNetwork.shortName,
28794
28849
  selectedToken.symbol
28795
28850
  );
28796
28851
  clarityAnalytics.trackPaymentCreated(
28797
- (_g = response.payment) == null ? void 0 : _g.id,
28852
+ (_h = response.payment) == null ? void 0 : _h.id,
28798
28853
  config.amount,
28799
28854
  selectedNetwork.shortName,
28800
28855
  selectedToken.symbol
@@ -28802,6 +28857,10 @@ const usePaymentFlow = (apiUrl, apiKey, apiSecret) => {
28802
28857
  setCurrentStep("confirm");
28803
28858
  return response.payment;
28804
28859
  } catch (err) {
28860
+ if ((err == null ? void 0 : err.code) === "STALE_ATTEMPT") {
28861
+ console.log("[PaymentFlow] createDepositPayment aborted — stale attempt");
28862
+ throw err;
28863
+ }
28805
28864
  setError(err.message);
28806
28865
  sdkAnalytics.trackError("deposit_payment_creation_failed", err.message, {
28807
28866
  amount: config.amount,
@@ -46976,7 +47035,7 @@ const WalletSelector = ({
46976
47035
  try {
46977
47036
  setAppKitError(null);
46978
47037
  setWalletConnectAddress(solanaAccountState.address);
46979
- const { createWalletConnectAdapter } = await import("./appKitSolana-CGUDW_KI.js");
47038
+ const { createWalletConnectAdapter } = await import("./appKitSolana-AZtdMOls.js");
46980
47039
  const adapter = createWalletConnectAdapter(solanaAccountState.address);
46981
47040
  await solanaWallet.connectWalletConnect(adapter, solanaAccountState.address);
46982
47041
  console.log("✅ WalletConnect synced with SDK");
@@ -47081,7 +47140,7 @@ const WalletSelector = ({
47081
47140
  setAppKitLoading(true);
47082
47141
  setAppKitError(null);
47083
47142
  try {
47084
- const { initializeAppKitEVM, openAppKitModal } = await import("./appKitEVM-gWlDgC4I.js");
47143
+ const { initializeAppKitEVM, openAppKitModal } = await import("./appKitEVM-C17FPKPo.js");
47085
47144
  await initializeAppKitEVM(wagmiConfig);
47086
47145
  await openAppKitModal();
47087
47146
  } catch (error) {
@@ -47101,7 +47160,7 @@ const WalletSelector = ({
47101
47160
  setAppKitError(null);
47102
47161
  try {
47103
47162
  console.log("📦 Loading AppKit Solana module...");
47104
- const { initializeAppKitSolana } = await import("./appKitSolana-CGUDW_KI.js");
47163
+ const { initializeAppKitSolana } = await import("./appKitSolana-AZtdMOls.js");
47105
47164
  console.log("✅ Module loaded, initializing...");
47106
47165
  const modal = await initializeAppKitSolana(solanaWallet);
47107
47166
  console.log("✅ AppKit Solana initialized successfully");
@@ -47379,8 +47438,17 @@ const useBridge = () => {
47379
47438
  return [];
47380
47439
  }
47381
47440
  }, []);
47382
- const getBridgeQuote = useCallback(async (originChainId, destChainId, originTokenAddress, originTokenSymbol, originTokenDecimals, destTokenAddress, destTokenSymbol, amount, recipient, _originBridgeTokens, _destBridgeTokens, user, debugId = createDebugId()) => {
47441
+ const getBridgeQuote = useCallback(async (originChainId, destChainId, originTokenAddress, originTokenSymbol, originTokenDecimals, destTokenAddress, destTokenSymbol, amount, recipient, _originBridgeTokens, _destBridgeTokens, user, debugId = createDebugId(), attemptMeta = null) => {
47383
47442
  var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
47443
+ const attemptId = (attemptMeta == null ? void 0 : attemptMeta.attemptId) ?? null;
47444
+ const isStillCurrent = () => {
47445
+ if (!(attemptMeta == null ? void 0 : attemptMeta.isStillCurrent)) return true;
47446
+ try {
47447
+ return !!attemptMeta.isStillCurrent();
47448
+ } catch {
47449
+ return false;
47450
+ }
47451
+ };
47384
47452
  setBridgeStatus("quoting");
47385
47453
  setBridgeError(null);
47386
47454
  try {
@@ -47464,10 +47532,13 @@ const useBridge = () => {
47464
47532
  effectiveRecipient: extractQuoteRecipient(quote),
47465
47533
  // Li.Fi provides transactionRequest directly
47466
47534
  transactionRequest: quote.transactionRequest,
47467
- approvalAddress: estimate.approvalAddress
47535
+ approvalAddress: estimate.approvalAddress,
47536
+ attemptId,
47537
+ selectionSnapshot: (attemptMeta == null ? void 0 : attemptMeta.selectionSnapshot) ?? null
47468
47538
  };
47469
47539
  console.log("[LiFi] Quote response:", {
47470
47540
  debugId,
47541
+ attemptId,
47471
47542
  tool: quote.tool,
47472
47543
  requestedRecipient: parsedQuote.requestedRecipient,
47473
47544
  effectiveRecipient: parsedQuote.effectiveRecipient,
@@ -47484,11 +47555,20 @@ const useBridge = () => {
47484
47555
  hasValue: quote.transactionRequest.value !== void 0
47485
47556
  } : null
47486
47557
  });
47558
+ if (!isStillCurrent()) {
47559
+ console.log("[LiFi] Dropping stale quote result — attempt no longer current", { debugId, attemptId });
47560
+ return parsedQuote;
47561
+ }
47487
47562
  setBridgeQuote(parsedQuote);
47488
47563
  setBridgeStatus("idle");
47489
47564
  return parsedQuote;
47490
47565
  } catch (error) {
47491
- console.warn("[LiFi] Quote failed:", error.message);
47566
+ const stillCurrent = isStillCurrent();
47567
+ console.warn("[LiFi] Quote failed:", error.message, { debugId, attemptId, stillCurrent });
47568
+ if (!stillCurrent) {
47569
+ console.log("[LiFi] Ignoring stale quote error");
47570
+ return null;
47571
+ }
47492
47572
  setBridgeQuote(null);
47493
47573
  setBridgeError(error.message);
47494
47574
  setBridgeStatus("error");
@@ -47830,7 +47910,34 @@ const TOKEN_LIST_TTL = 10 * 60 * 1e3;
47830
47910
  const WALLET_TOKEN_CACHE_TTL_MS = 30 * 1e3;
47831
47911
  const WEAK_WALLET_TOKEN_CACHE_TTL_MS = 5 * 1e3;
47832
47912
  const EMPTY_WALLET_TOKEN_CACHE_TTL_MS = 3 * 1e3;
47913
+ const EMPTY_CATALOG_CACHE_TTL = 60 * 1e3;
47833
47914
  const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
47915
+ const FETCH_RETRY_DELAY_MS = 1e3;
47916
+ const fetchRpcWithRetry = async (chainSlug, method, params, { timeout = 1e4, retryDelay = FETCH_RETRY_DELAY_MS, label = method }) => {
47917
+ const doFetch = (t) => fetch(`https://${chainSlug}.g.alchemy.com/v2/${ALCHEMY_API_KEY_PRIMARY$1}`, {
47918
+ method: "POST",
47919
+ headers: { "Content-Type": "application/json" },
47920
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
47921
+ signal: AbortSignal.timeout(t)
47922
+ }).then((r) => {
47923
+ if (!r.ok) throw new Error(`HTTP ${r.status}`);
47924
+ return r.json();
47925
+ });
47926
+ try {
47927
+ return { data: await doFetch(timeout), ok: true, retried: false };
47928
+ } catch (firstErr) {
47929
+ console.warn(`[WalletTokens] ${label} attempt 1 failed: ${firstErr.message}`);
47930
+ await new Promise((r) => setTimeout(r, retryDelay));
47931
+ const retryTimeout = Math.round(timeout * 1.5);
47932
+ try {
47933
+ console.log(`[WalletTokens] ${label} retrying (timeout=${retryTimeout}ms)`);
47934
+ return { data: await doFetch(retryTimeout), ok: true, retried: true };
47935
+ } catch (retryErr) {
47936
+ console.warn(`[WalletTokens] ${label} retry failed: ${retryErr.message}`);
47937
+ return { data: null, ok: false, retried: true, error: retryErr.message };
47938
+ }
47939
+ }
47940
+ };
47834
47941
  const formatBalance = (balance) => {
47835
47942
  if (balance > 1e6) return `${(balance / 1e6).toFixed(1)}M`;
47836
47943
  if (balance > 1e3) return `${(balance / 1e3).toFixed(1)}K`;
@@ -47904,9 +48011,11 @@ const useWalletTokens = () => {
47904
48011
  return fallbackListCache.current[cacheKey];
47905
48012
  }
47906
48013
  const lookup2 = {};
48014
+ let fetchOk = false;
47907
48015
  try {
47908
48016
  const uniRes = await fetch(UNISWAP_TOKEN_LIST_URL, { signal: AbortSignal.timeout(1e4) });
47909
48017
  const uniData = await uniRes.json();
48018
+ fetchOk = true;
47910
48019
  for (const token of uniData.tokens || []) {
47911
48020
  if (token.chainId !== chainId) continue;
47912
48021
  lookup2[`${token.chainId}:${token.address.toLowerCase()}`] = {
@@ -47928,6 +48037,7 @@ const useWalletTokens = () => {
47928
48037
  signal: AbortSignal.timeout(1e4)
47929
48038
  });
47930
48039
  const cgData = await cgRes.json();
48040
+ fetchOk = true;
47931
48041
  for (const token of cgData.tokens || []) {
47932
48042
  const key = `${token.chainId}:${token.address.toLowerCase()}`;
47933
48043
  if (!lookup2[key]) {
@@ -47945,9 +48055,15 @@ const useWalletTokens = () => {
47945
48055
  } catch (error) {
47946
48056
  console.warn("[WalletTokens] CoinGecko list fetch failed:", error.message);
47947
48057
  }
47948
- fallbackListCache.current[cacheKey] = lookup2;
47949
- fallbackListCacheTime.current[cacheKey] = Date.now();
47950
- console.log(`[WalletTokens] Loaded ${Object.keys(lookup2).length} fallback tokens for chain ${chainId}`);
48058
+ const fallbackCount = Object.keys(lookup2).length;
48059
+ if (fallbackCount > 0) {
48060
+ fallbackListCache.current[cacheKey] = lookup2;
48061
+ fallbackListCacheTime.current[cacheKey] = Date.now();
48062
+ } else if (fetchOk) {
48063
+ fallbackListCache.current[cacheKey] = lookup2;
48064
+ fallbackListCacheTime.current[cacheKey] = Date.now() - TOKEN_LIST_TTL + EMPTY_CATALOG_CACHE_TTL;
48065
+ }
48066
+ console.log(`[WalletTokens] Loaded ${fallbackCount} fallback tokens for chain ${chainId} (fetchOk=${fetchOk})`);
47951
48067
  return lookup2;
47952
48068
  }, []);
47953
48069
  const getLiFiTokenCatalog = useCallback(async (chainId, chainType = "EVM") => {
@@ -47957,6 +48073,7 @@ const useWalletTokens = () => {
47957
48073
  return lifiTokenCache.current[cacheKey];
47958
48074
  }
47959
48075
  const lookup2 = {};
48076
+ let fetchOk = false;
47960
48077
  try {
47961
48078
  const params = new URLSearchParams({
47962
48079
  chains: chainId.toString(),
@@ -47974,6 +48091,7 @@ const useWalletTokens = () => {
47974
48091
  }
47975
48092
  const data = await response.json();
47976
48093
  const chainTokens = getLiFiChainTokens(data, chainId);
48094
+ fetchOk = true;
47977
48095
  for (const token of chainTokens) {
47978
48096
  const key = chainType === "SVM" ? token.address : (_a2 = token.address) == null ? void 0 : _a2.toLowerCase();
47979
48097
  if (!key) continue;
@@ -47982,9 +48100,15 @@ const useWalletTokens = () => {
47982
48100
  } catch (error) {
47983
48101
  console.warn(`[WalletTokens] Li.Fi catalog fetch failed for chain ${chainId}:`, error.message);
47984
48102
  }
47985
- lifiTokenCache.current[cacheKey] = lookup2;
47986
- lifiTokenCacheTime.current[cacheKey] = Date.now();
47987
- console.log(`[WalletTokens] Loaded ${Object.keys(lookup2).length} Li.Fi tokens for chain ${chainId}`);
48103
+ const lifiCount = Object.keys(lookup2).length;
48104
+ if (lifiCount > 0) {
48105
+ lifiTokenCache.current[cacheKey] = lookup2;
48106
+ lifiTokenCacheTime.current[cacheKey] = Date.now();
48107
+ } else if (fetchOk) {
48108
+ lifiTokenCache.current[cacheKey] = lookup2;
48109
+ lifiTokenCacheTime.current[cacheKey] = Date.now() - TOKEN_LIST_TTL + EMPTY_CATALOG_CACHE_TTL;
48110
+ }
48111
+ console.log(`[WalletTokens] Loaded ${lifiCount} Li.Fi tokens for chain ${chainId} (fetchOk=${fetchOk})`);
47988
48112
  return lookup2;
47989
48113
  }, []);
47990
48114
  const fetchWalletTokens = useCallback(async (walletAddress, chainId, paymentAmount = 0) => {
@@ -48012,32 +48136,42 @@ const useWalletTokens = () => {
48012
48136
  setLoadingTokens(true);
48013
48137
  setTokenError(null);
48014
48138
  try {
48015
- const [lifiCatalog, fallbackList, balancesRes, nativeRes] = await Promise.all([
48016
- getLiFiTokenCatalog(numericChainId, "EVM"),
48017
- getFallbackTokenList(numericChainId),
48018
- fetch(`https://${chainSlug}.g.alchemy.com/v2/${ALCHEMY_API_KEY_PRIMARY$1}`, {
48019
- method: "POST",
48020
- headers: { "Content-Type": "application/json" },
48021
- body: JSON.stringify({
48022
- jsonrpc: "2.0",
48023
- id: 1,
48024
- method: "alchemy_getTokenBalances",
48025
- params: [walletAddress, "erc20"]
48026
- }),
48027
- signal: AbortSignal.timeout(1e4)
48028
- }).then(async (r) => r.ok ? r.json() : { result: { tokenBalances: [] } }),
48029
- fetch(`https://${chainSlug}.g.alchemy.com/v2/${ALCHEMY_API_KEY_PRIMARY$1}`, {
48030
- method: "POST",
48031
- headers: { "Content-Type": "application/json" },
48032
- body: JSON.stringify({
48033
- jsonrpc: "2.0",
48034
- id: 1,
48035
- method: "eth_getBalance",
48036
- params: [walletAddress, "latest"]
48037
- }),
48038
- signal: AbortSignal.timeout(5e3)
48039
- }).then(async (r) => r.ok ? r.json() : { result: "0x0" })
48139
+ const [lifiResult, fallbackResult, balanceResult, nativeResult] = await Promise.all([
48140
+ getLiFiTokenCatalog(numericChainId, "EVM").then(
48141
+ (data) => ({ data, ok: true }),
48142
+ (err) => {
48143
+ console.warn(`[WalletTokens] Li.Fi catalog failed (chain ${numericChainId}): ${err.message}`);
48144
+ return { data: {}, ok: false };
48145
+ }
48146
+ ),
48147
+ getFallbackTokenList(numericChainId).then(
48148
+ (data) => ({ data, ok: true }),
48149
+ (err) => {
48150
+ console.warn(`[WalletTokens] Fallback list failed (chain ${numericChainId}): ${err.message}`);
48151
+ return { data: {}, ok: false };
48152
+ }
48153
+ ),
48154
+ fetchRpcWithRetry(chainSlug, "alchemy_getTokenBalances", [walletAddress, "erc20"], {
48155
+ timeout: 1e4,
48156
+ label: `ERC20 balances (${chainSlug})`
48157
+ }),
48158
+ fetchRpcWithRetry(chainSlug, "eth_getBalance", [walletAddress, "latest"], {
48159
+ timeout: 5e3,
48160
+ label: `Native balance (${chainSlug})`
48161
+ })
48040
48162
  ]);
48163
+ const lifiCatalog = lifiResult.data;
48164
+ const fallbackList = fallbackResult.data;
48165
+ const balancesRes = balanceResult.ok ? balanceResult.data : { result: { tokenBalances: [] } };
48166
+ const nativeRes = nativeResult.ok ? nativeResult.data : { result: "0x0" };
48167
+ let resultQuality = "complete";
48168
+ if (!balanceResult.ok && !nativeResult.ok) resultQuality = "failed";
48169
+ else if (!balanceResult.ok || !nativeResult.ok) resultQuality = "partial";
48170
+ if (resultQuality !== "complete") {
48171
+ console.warn(
48172
+ `[WalletTokens] Fetch quality: ${resultQuality} — balances=${balanceResult.ok ? balanceResult.retried ? "ok(retried)" : "ok" : "FAILED"}, native=${nativeResult.ok ? nativeResult.retried ? "ok(retried)" : "ok" : "FAILED"} (chain ${numericChainId})`
48173
+ );
48174
+ }
48041
48175
  const tokens = [];
48042
48176
  const tokenBalances = ((_a2 = balancesRes.result) == null ? void 0 : _a2.tokenBalances) || [];
48043
48177
  const unresolvedBalances = [];
@@ -48072,6 +48206,7 @@ const useWalletTokens = () => {
48072
48206
  }
48073
48207
  if (unresolvedBalances.length > 0) {
48074
48208
  console.log(`[WalletTokens] ${unresolvedBalances.length} tokens missing metadata, fetching from Alchemy`);
48209
+ let metadataFetchFailures = 0;
48075
48210
  const metadataResults = await Promise.all(
48076
48211
  unresolvedBalances.map(async ({ contractAddress, rawBalance }) => {
48077
48212
  try {
@@ -48103,6 +48238,7 @@ const useWalletTokens = () => {
48103
48238
  extra: { isUnverified: true }
48104
48239
  });
48105
48240
  } catch {
48241
+ metadataFetchFailures++;
48106
48242
  return null;
48107
48243
  }
48108
48244
  })
@@ -48112,6 +48248,12 @@ const useWalletTokens = () => {
48112
48248
  console.log(`[WalletTokens] Recovered ${resolved.length} tokens via alchemy_getTokenMetadata`);
48113
48249
  tokens.push(...resolved);
48114
48250
  }
48251
+ if (metadataFetchFailures > 0) {
48252
+ console.warn(
48253
+ `[WalletTokens] ${metadataFetchFailures}/${unresolvedBalances.length} metadata fetches failed (chain ${numericChainId}) — downgrading quality from ${resultQuality}`
48254
+ );
48255
+ if (resultQuality === "complete") resultQuality = "partial";
48256
+ }
48115
48257
  }
48116
48258
  const nativeBalance = Number(BigInt(nativeRes.result || "0")) / 1e18;
48117
48259
  if (nativeBalance > 1e-4) {
@@ -48250,18 +48392,33 @@ const useWalletTokens = () => {
48250
48392
  const missingUsdCount = resolvedTokens.filter(
48251
48393
  (token) => !token.isStablecoin && token.usdValue === void 0
48252
48394
  ).length;
48253
- const cacheTtl = resolvedTokens.length === 0 ? EMPTY_WALLET_TOKEN_CACHE_TTL_MS : missingUsdCount > 0 ? WEAK_WALLET_TOKEN_CACHE_TTL_MS : WALLET_TOKEN_CACHE_TTL_MS;
48254
- console.log(`[WalletTokens] ${resolvedTokens.length} tokens after Li.Fi metadata resolution (payment: $${paymentAmount})`);
48255
- console.log(`[WalletTokens] Price coverage: ${resolvedTokens.length - missingUsdCount}/${resolvedTokens.length} tokens priced`);
48395
+ if (resultQuality === "failed" && resolvedTokens.length === 0) {
48396
+ console.warn(`[WalletTokens] Result: FAILED with 0 tokens not caching (chain ${numericChainId})`);
48397
+ setTokenError("Token discovery timed out reselect network to retry");
48398
+ setWalletTokens([]);
48399
+ return [];
48400
+ }
48401
+ let cacheTtl;
48402
+ if (resolvedTokens.length === 0) {
48403
+ cacheTtl = EMPTY_WALLET_TOKEN_CACHE_TTL_MS;
48404
+ } else if (resultQuality === "partial" || missingUsdCount > 0) {
48405
+ cacheTtl = WEAK_WALLET_TOKEN_CACHE_TTL_MS;
48406
+ } else {
48407
+ cacheTtl = WALLET_TOKEN_CACHE_TTL_MS;
48408
+ }
48409
+ console.log(
48410
+ `[WalletTokens] Result: ${resolvedTokens.length} tokens, quality=${resultQuality}, priced=${resolvedTokens.length - missingUsdCount}/${resolvedTokens.length}, cacheTTL=${cacheTtl}ms (chain ${numericChainId}, payment: $${paymentAmount})`
48411
+ );
48256
48412
  balanceCache.current[cacheKey] = {
48257
48413
  tokens: resolvedTokens,
48258
48414
  time: Date.now(),
48259
48415
  ttl: cacheTtl
48260
48416
  };
48261
48417
  setWalletTokens(resolvedTokens);
48418
+ setTokenError(null);
48262
48419
  return resolvedTokens;
48263
48420
  } catch (error) {
48264
- console.warn("[WalletTokens] Failed to fetch:", error.message);
48421
+ console.error("[WalletTokens] Unexpected error in token processing:", error.message, error.stack);
48265
48422
  setTokenError(error.message);
48266
48423
  setWalletTokens([]);
48267
48424
  return [];
@@ -48290,8 +48447,9 @@ const useWalletTokens = () => {
48290
48447
  }).then((r) => r.json());
48291
48448
  const balance = BigInt(res.result || "0x0");
48292
48449
  return { chainId: numericId, hasBalance: balance > 0n };
48293
- } catch {
48294
- return { chainId: numericId, hasBalance: false };
48450
+ } catch (err) {
48451
+ console.warn(`[WalletTokens] Chain ${numericId} (${slug}) balance check failed: ${err.message}`);
48452
+ return { chainId: numericId, hasBalance: true };
48295
48453
  }
48296
48454
  })
48297
48455
  );
@@ -48659,7 +48817,7 @@ const deepLinkUtils = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defin
48659
48817
  isInDAppBrowser,
48660
48818
  openCurrentPageInWallet
48661
48819
  }, Symbol.toStringTag, { value: "Module" }));
48662
- const CHECKOUT_BUILD = "checkout-2026-04-09b-recipient-settlement-guard";
48820
+ const CHECKOUT_BUILD = "checkout-2026-04-10a-token-discovery-retry-fix";
48663
48821
  const normalizeEvmAddress = (value) => {
48664
48822
  if (typeof value !== "string") return null;
48665
48823
  const trimmed = value.trim();
@@ -48928,6 +49086,71 @@ const CoinleyPaymentInternal = ({
48928
49086
  const hasCommittedTransaction = Boolean(
48929
49087
  currentTransactionHash || (solanaTransaction == null ? void 0 : solanaTransaction.txSignature) || (paymentData == null ? void 0 : paymentData.transactionHash)
48930
49088
  );
49089
+ const activeAttemptRef = useRef(null);
49090
+ const [retryEpoch, setRetryEpoch] = useState(0);
49091
+ const buildSelectionSnapshot = (networkArg = selectedNetwork, tokenArg = selectedToken, addressArg = address, solanaPubkeyArg = solanaWallet == null ? void 0 : solanaWallet.publicKey) => ({
49092
+ networkId: (networkArg == null ? void 0 : networkArg.id) ?? null,
49093
+ chainId: (networkArg == null ? void 0 : networkArg.chainId) ?? null,
49094
+ networkShortName: (networkArg == null ? void 0 : networkArg.shortName) ?? null,
49095
+ tokenId: (tokenArg == null ? void 0 : tokenArg.id) ?? null,
49096
+ tokenSymbol: (tokenArg == null ? void 0 : tokenArg.symbol) ?? null,
49097
+ tokenContract: (tokenArg == null ? void 0 : tokenArg.contractAddress) ?? null,
49098
+ walletAddress: addressArg || solanaPubkeyArg || null,
49099
+ isSwap: !!(tokenArg == null ? void 0 : tokenArg.isSwapToken),
49100
+ isBridge: !!(networkArg == null ? void 0 : networkArg.isBridgeNetwork)
49101
+ });
49102
+ const createNewAttempt = (reason, overrides = {}) => {
49103
+ const snapshot2 = overrides.snapshot || buildSelectionSnapshot(
49104
+ overrides.network,
49105
+ overrides.token,
49106
+ overrides.address,
49107
+ overrides.solanaPubkey
49108
+ );
49109
+ const attempt = {
49110
+ id: `attempt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
49111
+ reason,
49112
+ createdAt: Date.now(),
49113
+ snapshot: snapshot2
49114
+ };
49115
+ activeAttemptRef.current = attempt;
49116
+ console.log("[Attempt] created", { id: attempt.id, reason, snapshot: snapshot2 });
49117
+ return attempt;
49118
+ };
49119
+ const invalidateCurrentAttempt = (reason) => {
49120
+ const prev = activeAttemptRef.current;
49121
+ if (prev) {
49122
+ console.log("[Attempt] invalidated", { id: prev.id, reason });
49123
+ }
49124
+ activeAttemptRef.current = null;
49125
+ };
49126
+ const normalizeWalletAddress = (value) => typeof value === "string" ? value.trim().toLowerCase() : null;
49127
+ const snapshotsMatch = (a, b) => {
49128
+ if (!a || !b) return false;
49129
+ return a.chainId === b.chainId && a.networkShortName === b.networkShortName && a.tokenSymbol === b.tokenSymbol && a.tokenContract === b.tokenContract && a.isSwap === b.isSwap && a.isBridge === b.isBridge && normalizeWalletAddress(a.walletAddress) === normalizeWalletAddress(b.walletAddress);
49130
+ };
49131
+ const buildAttemptMeta = (attempt) => {
49132
+ if (!attempt) return null;
49133
+ const attemptId = attempt.id;
49134
+ return {
49135
+ attemptId,
49136
+ selectionSnapshot: attempt.snapshot,
49137
+ isStillCurrent: () => {
49138
+ var _a3;
49139
+ return ((_a3 = activeAttemptRef.current) == null ? void 0 : _a3.id) === attemptId;
49140
+ }
49141
+ };
49142
+ };
49143
+ const STALE_ATTEMPT_MESSAGE = "Your payment quote changed while you were switching networks or tokens. Please try again.";
49144
+ const handleHardFailure = (reason, { swapBridgeFlow = false } = {}) => {
49145
+ var _a3;
49146
+ invalidateCurrentAttempt(reason);
49147
+ (_a3 = transactionHandling.resetTransaction) == null ? void 0 : _a3.call(transactionHandling);
49148
+ if (swapBridgeFlow) {
49149
+ setPaymentData(null);
49150
+ relayBridge.resetBridge();
49151
+ setRetryEpoch((epoch) => epoch + 1);
49152
+ }
49153
+ };
48931
49154
  useEffect(() => {
48932
49155
  if (paymentData && selectedToken && selectedNetwork) {
48933
49156
  generateQRCode(paymentData, selectedToken, selectedNetwork, preferredWallet);
@@ -49146,7 +49369,8 @@ const CoinleyPaymentInternal = ({
49146
49369
  const timeout = setTimeout(() => {
49147
49370
  setError("Transaction timed out. Please try again.");
49148
49371
  setCurrentStep(PAYMENT_STEPS.CONFIRM);
49149
- transactionHandling.resetTransaction();
49372
+ const wasSwapBridgeFlow = !!((selectedToken == null ? void 0 : selectedToken.isSwapToken) || (selectedNetwork == null ? void 0 : selectedNetwork.isBridgeNetwork));
49373
+ handleHardFailure("transaction-timeout", { swapBridgeFlow: wasSwapBridgeFlow });
49150
49374
  }, 12e4);
49151
49375
  return () => clearTimeout(timeout);
49152
49376
  }
@@ -49300,6 +49524,7 @@ const CoinleyPaymentInternal = ({
49300
49524
  }, [paymentData == null ? void 0 : paymentData.id, activeTab, currentStep, verifyingPayment, paymentType, selectedNetwork, selectedToken, onSuccess, paymentFlow.api]);
49301
49525
  const resetAllTransactionState = async () => {
49302
49526
  var _a3;
49527
+ invalidateCurrentAttempt("reset-all-transaction-state");
49303
49528
  transactionHandling.resetTransaction();
49304
49529
  (_a3 = solanaTransaction.resetTransaction) == null ? void 0 : _a3.call(solanaTransaction);
49305
49530
  relayBridge.resetBridge();
@@ -49488,6 +49713,13 @@ const CoinleyPaymentInternal = ({
49488
49713
  }
49489
49714
  const amount = paymentAmount || parseFloat(config == null ? void 0 : config.amount) || 0;
49490
49715
  if (amount <= 0) return;
49716
+ let attempt = activeAttemptRef.current;
49717
+ if (!attempt || !snapshotsMatch(attempt.snapshot, buildSelectionSnapshot())) {
49718
+ attempt = createNewAttempt("quote-effect", {
49719
+ snapshot: buildSelectionSnapshot()
49720
+ });
49721
+ }
49722
+ const attemptMeta = buildAttemptMeta(attempt);
49491
49723
  if (isSwap && !isBridge) {
49492
49724
  const DEPOSIT_SUPPORTED_CHAINS = [8453, 56, 42161, 137, 10, 43114, 143];
49493
49725
  const isSolanaSwap = (selectedNetwork == null ? void 0 : selectedNetwork.shortName) === "solana" || (selectedNetwork == null ? void 0 : selectedNetwork.chainType) === "solana";
@@ -49547,8 +49779,12 @@ const CoinleyPaymentInternal = ({
49547
49779
  // recipient (EVM address for cross-VM)
49548
49780
  null,
49549
49781
  null,
49550
- quoteUser
49782
+ quoteUser,
49551
49783
  // user/signer (Solana address for cross-VM)
49784
+ void 0,
49785
+ // debugId (let hook generate)
49786
+ attemptMeta
49787
+ // attempt identity for stale-result drop
49552
49788
  );
49553
49789
  } else if (isBridge) {
49554
49790
  const destNetwork = selectedNetwork.bridgeDestination;
@@ -49567,7 +49803,12 @@ const CoinleyPaymentInternal = ({
49567
49803
  amount.toString(),
49568
49804
  address,
49569
49805
  selectedNetwork.bridgeTokens,
49570
- destNetwork.bridgeTokens
49806
+ destNetwork.bridgeTokens,
49807
+ void 0,
49808
+ // user
49809
+ void 0,
49810
+ // debugId
49811
+ attemptMeta
49571
49812
  );
49572
49813
  } else {
49573
49814
  relayBridge.getBridgeQuote(
@@ -49583,11 +49824,16 @@ const CoinleyPaymentInternal = ({
49583
49824
  amount.toString(),
49584
49825
  address,
49585
49826
  selectedNetwork.bridgeTokens,
49586
- destNetwork.bridgeTokens
49827
+ destNetwork.bridgeTokens,
49828
+ void 0,
49829
+ // user
49830
+ void 0,
49831
+ // debugId
49832
+ attemptMeta
49587
49833
  );
49588
49834
  }
49589
49835
  }
49590
- }, [selectedNetwork == null ? void 0 : selectedNetwork.id, selectedToken == null ? void 0 : selectedToken.id, address, paymentAmount]);
49836
+ }, [selectedNetwork == null ? void 0 : selectedNetwork.id, selectedToken == null ? void 0 : selectedToken.id, address, solanaWallet == null ? void 0 : solanaWallet.publicKey, paymentAmount, retryEpoch]);
49591
49837
  useEffect(() => {
49592
49838
  const requiresRouteValidation = (selectedToken == null ? void 0 : selectedToken.isSwapToken) || (selectedNetwork == null ? void 0 : selectedNetwork.isBridgeNetwork);
49593
49839
  if (!requiresRouteValidation || !(selectedToken == null ? void 0 : selectedToken.id) || relayBridge.bridgeStatus === "quoting") {
@@ -49617,11 +49863,25 @@ const CoinleyPaymentInternal = ({
49617
49863
  if (!(paymentData == null ? void 0 : paymentData.depositAddress) || !relayBridge.bridgeQuote) return;
49618
49864
  const amount = paymentAmount || parseFloat(config == null ? void 0 : config.amount) || 0;
49619
49865
  if (amount <= 0) return;
49866
+ const activeAttempt = activeAttemptRef.current;
49867
+ const paymentAttemptId = paymentData.__coinleyAttemptId ?? null;
49868
+ if (!activeAttempt) {
49869
+ console.log("[Swap] Skipping re-quote — no active attempt");
49870
+ return;
49871
+ }
49872
+ if (paymentAttemptId && paymentAttemptId !== activeAttempt.id) {
49873
+ console.log("[Swap] Skipping re-quote — paymentData belongs to stale attempt", {
49874
+ paymentAttemptId,
49875
+ activeAttemptId: activeAttempt.id
49876
+ });
49877
+ return;
49878
+ }
49620
49879
  const currentQuote = relayBridge.bridgeQuote;
49621
49880
  const depositAddr = paymentData.depositAddress;
49622
- console.log("[Swap] Re-quoting with deposit address as recipient:", depositAddr);
49881
+ console.log("[Swap] Re-quoting with deposit address as recipient:", depositAddr, { attemptId: activeAttempt.id });
49623
49882
  const isSolanaOrigin = currentQuote.originChainId === 792703809;
49624
49883
  const reQuoteUser = isSolanaOrigin ? solanaWallet == null ? void 0 : solanaWallet.publicKey : void 0;
49884
+ const attemptMeta = buildAttemptMeta(activeAttempt);
49625
49885
  relayBridge.getBridgeQuote(
49626
49886
  currentQuote.originChainId,
49627
49887
  currentQuote.destChainId,
@@ -49635,8 +49895,11 @@ const CoinleyPaymentInternal = ({
49635
49895
  // Relay delivers output here (EVM address)
49636
49896
  null,
49637
49897
  null,
49638
- reQuoteUser
49898
+ reQuoteUser,
49639
49899
  // Signer on origin chain (Solana pubkey for cross-VM)
49900
+ void 0,
49901
+ // debugId
49902
+ attemptMeta
49640
49903
  );
49641
49904
  }, [paymentData == null ? void 0 : paymentData.depositAddress]);
49642
49905
  useEffect(() => {
@@ -49664,6 +49927,8 @@ const CoinleyPaymentInternal = ({
49664
49927
  await resetAllTransactionState();
49665
49928
  relayBridge.resetBridge();
49666
49929
  resetWalletTokens();
49930
+ setPaymentData(null);
49931
+ setError("");
49667
49932
  setSelectedNetwork(network);
49668
49933
  setSelectedToken(null);
49669
49934
  if (network == null ? void 0 : network.isBridgeNetwork) {
@@ -49677,7 +49942,16 @@ const CoinleyPaymentInternal = ({
49677
49942
  clarityAnalytics.trackNetworkSelected(network == null ? void 0 : network.name, config == null ? void 0 : config.merchantName);
49678
49943
  };
49679
49944
  const handleTokenSelect = (token) => {
49945
+ var _a3;
49946
+ invalidateCurrentAttempt("token-changed");
49947
+ relayBridge.resetBridge();
49948
+ (_a3 = transactionHandling.resetTransaction) == null ? void 0 : _a3.call(transactionHandling);
49949
+ setPaymentData(null);
49950
+ setError("");
49680
49951
  setSelectedToken(token);
49952
+ createNewAttempt("token-selected", {
49953
+ snapshot: buildSelectionSnapshot(selectedNetwork, token, address, solanaWallet == null ? void 0 : solanaWallet.publicKey)
49954
+ });
49681
49955
  sdkAnalytics.trackTokenSelected(token == null ? void 0 : token.symbol, selectedNetwork == null ? void 0 : selectedNetwork.name, config == null ? void 0 : config.merchantName);
49682
49956
  clarityAnalytics.trackTokenSelected(token == null ? void 0 : token.symbol, selectedNetwork == null ? void 0 : selectedNetwork.name, config == null ? void 0 : config.merchantName);
49683
49957
  };
@@ -49692,11 +49966,43 @@ const CoinleyPaymentInternal = ({
49692
49966
  setError("Invalid payment amount. Please refresh and try again.");
49693
49967
  return;
49694
49968
  }
49695
- if (!paymentData) {
49969
+ let attempt = activeAttemptRef.current;
49970
+ if (!attempt || !snapshotsMatch(attempt.snapshot, buildSelectionSnapshot())) {
49971
+ attempt = createNewAttempt("connect-wallet", {
49972
+ snapshot: buildSelectionSnapshot()
49973
+ });
49974
+ }
49975
+ const attemptMeta = buildAttemptMeta(attempt);
49976
+ let effectivePaymentData = paymentData;
49977
+ if ((effectivePaymentData == null ? void 0 : effectivePaymentData.__coinleyAttemptId) && effectivePaymentData.__coinleyAttemptId !== attempt.id) {
49978
+ const existingSnapshot = effectivePaymentData.__coinleySelectionSnapshot;
49979
+ if (existingSnapshot && snapshotsMatch(existingSnapshot, attempt.snapshot)) {
49980
+ console.log("[Attempt] re-stamping paymentData for retry", {
49981
+ paymentId: effectivePaymentData.id,
49982
+ priorAttempt: effectivePaymentData.__coinleyAttemptId,
49983
+ newAttempt: attempt.id
49984
+ });
49985
+ const restamped = {
49986
+ ...effectivePaymentData,
49987
+ __coinleyAttemptId: attempt.id
49988
+ };
49989
+ setPaymentData(restamped);
49990
+ effectivePaymentData = restamped;
49991
+ } else {
49992
+ console.log("[Attempt] discarding stale paymentData from prior attempt", {
49993
+ priorAttempt: effectivePaymentData.__coinleyAttemptId,
49994
+ currentAttempt: attempt.id
49995
+ });
49996
+ setPaymentData(null);
49997
+ effectivePaymentData = null;
49998
+ }
49999
+ }
50000
+ if (!effectivePaymentData) {
49696
50001
  const configWithConvertedAmount = {
49697
50002
  ...config,
49698
50003
  amount: paymentAmount,
49699
50004
  // Use USD amount after currency conversion
50005
+ __coinleyAttemptMeta: attemptMeta,
49700
50006
  metadata: {
49701
50007
  ...config.metadata,
49702
50008
  originalCurrency: merchantCurrency,
@@ -49711,7 +50017,15 @@ const CoinleyPaymentInternal = ({
49711
50017
  const merchantHasSolanaWallet = !!(((_a3 = config == null ? void 0 : config.merchantWalletAddresses) == null ? void 0 : _a3.solana) || ((_b = config == null ? void 0 : config.merchantWalletAddresses) == null ? void 0 : _b["solana-devnet"]));
49712
50018
  if (connectedWalletType === "solana" && merchantHasSolanaWallet && !(selectedToken == null ? void 0 : selectedToken.isSwapToken)) {
49713
50019
  console.log("[Payment] Solana direct: merchant has Solana wallet");
49714
- await createPayment(configWithConvertedAmount);
50020
+ try {
50021
+ await createPayment(configWithConvertedAmount);
50022
+ } catch (e) {
50023
+ if ((e == null ? void 0 : e.code) === "STALE_ATTEMPT") {
50024
+ setError(STALE_ATTEMPT_MESSAGE);
50025
+ return;
50026
+ }
50027
+ return;
50028
+ }
49715
50029
  } else if (connectedWalletType === "solana" && !merchantHasSolanaWallet) {
49716
50030
  const bestDest = getBestDestinationChain(networks);
49717
50031
  const effectiveNetwork = (bestDest == null ? void 0 : bestDest.shortName) || "base";
@@ -49730,6 +50044,10 @@ const CoinleyPaymentInternal = ({
49730
50044
  try {
49731
50045
  await createDepositPayment(depositConfig);
49732
50046
  } catch (e) {
50047
+ if ((e == null ? void 0 : e.code) === "STALE_ATTEMPT") {
50048
+ setError(STALE_ATTEMPT_MESSAGE);
50049
+ return;
50050
+ }
49733
50051
  return;
49734
50052
  }
49735
50053
  } else if ((selectedToken == null ? void 0 : selectedToken.isSwapToken) || (selectedNetwork == null ? void 0 : selectedNetwork.isBridgeNetwork)) {
@@ -49759,10 +50077,22 @@ const CoinleyPaymentInternal = ({
49759
50077
  try {
49760
50078
  await createDepositPayment(depositConfig);
49761
50079
  } catch (e) {
50080
+ if ((e == null ? void 0 : e.code) === "STALE_ATTEMPT") {
50081
+ setError(STALE_ATTEMPT_MESSAGE);
50082
+ return;
50083
+ }
49762
50084
  return;
49763
50085
  }
49764
50086
  } else {
49765
- await createPayment(configWithConvertedAmount);
50087
+ try {
50088
+ await createPayment(configWithConvertedAmount);
50089
+ } catch (e) {
50090
+ if ((e == null ? void 0 : e.code) === "STALE_ATTEMPT") {
50091
+ setError(STALE_ATTEMPT_MESSAGE);
50092
+ return;
50093
+ }
50094
+ return;
50095
+ }
49766
50096
  }
49767
50097
  }
49768
50098
  setShowWalletConfirm(true);
@@ -49772,11 +50102,54 @@ const CoinleyPaymentInternal = ({
49772
50102
  await resetAllTransactionState();
49773
50103
  };
49774
50104
  const executePayment = async () => {
49775
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
49776
- if (((selectedToken == null ? void 0 : selectedToken.isSwapToken) || (selectedNetwork == null ? void 0 : selectedNetwork.isBridgeNetwork)) && relayBridge.bridgeStatus === "quoting") {
50105
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
50106
+ const isSwapOrBridgeFlow = !!((selectedToken == null ? void 0 : selectedToken.isSwapToken) || (selectedNetwork == null ? void 0 : selectedNetwork.isBridgeNetwork));
50107
+ if (isSwapOrBridgeFlow && relayBridge.bridgeStatus === "quoting") {
49777
50108
  setError("Payment quote is being refreshed. Please wait a moment and try again.");
49778
50109
  return;
49779
50110
  }
50111
+ if (isSwapOrBridgeFlow) {
50112
+ const activeAttempt = activeAttemptRef.current;
50113
+ if (!activeAttempt) {
50114
+ console.warn("[Attempt] executePayment blocked — no active attempt");
50115
+ setError(STALE_ATTEMPT_MESSAGE);
50116
+ return;
50117
+ }
50118
+ const currentSnapshot = buildSelectionSnapshot();
50119
+ if (!snapshotsMatch(activeAttempt.snapshot, currentSnapshot)) {
50120
+ console.warn("[Attempt] executePayment blocked — snapshot mismatch", {
50121
+ attempt: activeAttempt.snapshot,
50122
+ current: currentSnapshot
50123
+ });
50124
+ invalidateCurrentAttempt("snapshot-mismatch-at-execute");
50125
+ setError(STALE_ATTEMPT_MESSAGE);
50126
+ return;
50127
+ }
50128
+ if (paymentData && paymentData.__coinleyAttemptId && paymentData.__coinleyAttemptId !== activeAttempt.id) {
50129
+ console.warn("[Attempt] executePayment blocked — paymentData attempt mismatch", {
50130
+ paymentAttemptId: paymentData.__coinleyAttemptId,
50131
+ activeAttemptId: activeAttempt.id
50132
+ });
50133
+ setPaymentData(null);
50134
+ setError(STALE_ATTEMPT_MESSAGE);
50135
+ return;
50136
+ }
50137
+ const quoteAttemptId = ((_a3 = relayBridge.bridgeQuote) == null ? void 0 : _a3.attemptId) ?? null;
50138
+ if (quoteAttemptId && quoteAttemptId !== activeAttempt.id) {
50139
+ console.warn("[Attempt] executePayment blocked — bridgeQuote attempt mismatch", {
50140
+ quoteAttemptId,
50141
+ activeAttemptId: activeAttempt.id
50142
+ });
50143
+ relayBridge.resetBridge();
50144
+ setError(STALE_ATTEMPT_MESSAGE);
50145
+ return;
50146
+ }
50147
+ console.log("[Attempt] executePayment proceeding", {
50148
+ attemptId: activeAttempt.id,
50149
+ hasPaymentData: !!paymentData,
50150
+ hasQuote: !!relayBridge.bridgeQuote
50151
+ });
50152
+ }
49780
50153
  if (isSolana) {
49781
50154
  if (!solanaWallet.publicKey || !solanaWallet.connectedWallet) {
49782
50155
  setError("Please connect your Solana wallet first");
@@ -49800,11 +50173,11 @@ const CoinleyPaymentInternal = ({
49800
50173
  const amount = paymentAmount || parseFloat(config == null ? void 0 : config.amount) || 0;
49801
50174
  const SOL_MINT = "So11111111111111111111111111111111111111112";
49802
50175
  const tokenMint = selectedToken == null ? void 0 : selectedToken.contractAddress;
49803
- const isNativeSOL = tokenMint === "native" || ((_a3 = selectedToken == null ? void 0 : selectedToken.symbol) == null ? void 0 : _a3.toUpperCase()) === "SOL";
50176
+ const isNativeSOL = tokenMint === "native" || ((_b = selectedToken == null ? void 0 : selectedToken.symbol) == null ? void 0 : _b.toUpperCase()) === "SOL";
49804
50177
  let quote = relayBridge.bridgeQuote;
49805
50178
  if (!quote) {
49806
50179
  console.log("[Solana Bridge] No quote cached — fetching fresh quote...");
49807
- const originTokenAddress = isNativeSOL ? SOLANA_TOKEN_ADDRESSES.SOL : SOLANA_TOKEN_ADDRESSES[(_b = selectedToken == null ? void 0 : selectedToken.symbol) == null ? void 0 : _b.toUpperCase()] || tokenMint;
50180
+ const originTokenAddress = isNativeSOL ? SOLANA_TOKEN_ADDRESSES.SOL : SOLANA_TOKEN_ADDRESSES[(_c = selectedToken == null ? void 0 : selectedToken.symbol) == null ? void 0 : _c.toUpperCase()] || tokenMint;
49808
50181
  quote = await relayBridge.getBridgeQuote(
49809
50182
  RELAY_SOLANA_CHAIN_ID,
49810
50183
  destChainId,
@@ -49841,7 +50214,7 @@ const CoinleyPaymentInternal = ({
49841
50214
  if (!solQuote) {
49842
50215
  throw new Error("Unable to find a bridge route. Try paying with SOL, USDC, or USDT.");
49843
50216
  }
49844
- const solNeededLamports = BigInt(((_e = (_d = (_c = solQuote.raw) == null ? void 0 : _c.details) == null ? void 0 : _d.currencyIn) == null ? void 0 : _e.amount) || "0");
50217
+ const solNeededLamports = BigInt(((_f = (_e = (_d = solQuote.raw) == null ? void 0 : _d.details) == null ? void 0 : _e.currencyIn) == null ? void 0 : _f.amount) || "0");
49845
50218
  const solWithBuffer = solNeededLamports * 105n / 100n;
49846
50219
  const solAmount = Number(solWithBuffer) / 1e9;
49847
50220
  console.log(`[Solana Bridge] Need ~${solAmount.toFixed(6)} SOL for bridge`);
@@ -49883,8 +50256,8 @@ const CoinleyPaymentInternal = ({
49883
50256
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
49884
50257
  try {
49885
50258
  const status = await paymentFlow.api.getDepositStatus(paymentData.id);
49886
- console.log(`[Solana Bridge] Deposit check ${attempt + 1}/${maxAttempts}:`, (_f = status == null ? void 0 : status.payment) == null ? void 0 : _f.status);
49887
- if (((_g = status == null ? void 0 : status.payment) == null ? void 0 : _g.detectedAt) || ((_h = status == null ? void 0 : status.payment) == null ? void 0 : _h.status) === "completed" || ((_i = status == null ? void 0 : status.payment) == null ? void 0 : _i.status) === "swept") {
50259
+ console.log(`[Solana Bridge] Deposit check ${attempt + 1}/${maxAttempts}:`, (_g = status == null ? void 0 : status.payment) == null ? void 0 : _g.status);
50260
+ if (((_h = status == null ? void 0 : status.payment) == null ? void 0 : _h.detectedAt) || ((_i = status == null ? void 0 : status.payment) == null ? void 0 : _i.status) === "completed" || ((_j = status == null ? void 0 : status.payment) == null ? void 0 : _j.status) === "swept") {
49888
50261
  detected = true;
49889
50262
  console.log("[Solana Bridge] Deposit detected on destination chain!");
49890
50263
  break;
@@ -49983,6 +50356,7 @@ const CoinleyPaymentInternal = ({
49983
50356
  const friendly = parseUserFriendlyError(err);
49984
50357
  setError((friendly == null ? void 0 : friendly.message) || "Transaction failed. Please try again.");
49985
50358
  setCurrentStep(PAYMENT_STEPS.CONFIRM);
50359
+ handleHardFailure("solana-execute-failed", { swapBridgeFlow: isSwapOrBridgeFlow });
49986
50360
  }
49987
50361
  } else {
49988
50362
  if (!connectedWallet || !address) {
@@ -50005,8 +50379,8 @@ const CoinleyPaymentInternal = ({
50005
50379
  const { getWalletClient: getWalletClient2 } = await import("@wagmi/core");
50006
50380
  const targetChainId = parseInt(selectedNetwork.chainId);
50007
50381
  const expectedDepositAddress = normalizeEvmAddress(paymentData.depositAddress);
50008
- const quoteRequestedRecipient = normalizeEvmAddress((_j = relayBridge.bridgeQuote) == null ? void 0 : _j.requestedRecipient);
50009
- const quoteEffectiveRecipient = normalizeEvmAddress((_k = relayBridge.bridgeQuote) == null ? void 0 : _k.effectiveRecipient);
50382
+ const quoteRequestedRecipient = normalizeEvmAddress((_k = relayBridge.bridgeQuote) == null ? void 0 : _k.requestedRecipient);
50383
+ const quoteEffectiveRecipient = normalizeEvmAddress((_l = relayBridge.bridgeQuote) == null ? void 0 : _l.effectiveRecipient);
50010
50384
  console.log(`[${action}] validating deposit recipient`, {
50011
50385
  expectedDepositAddress,
50012
50386
  quoteRequestedRecipient,
@@ -50031,10 +50405,10 @@ const CoinleyPaymentInternal = ({
50031
50405
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
50032
50406
  try {
50033
50407
  walletClient = await getWalletClient2(wagmiConfig, { chainId: targetChainId });
50034
- console.log(`[${action}] connector confirmed: chain=${(_l = walletClient.chain) == null ? void 0 : _l.id} | attempts=${attempt + 1}`);
50408
+ console.log(`[${action}] connector confirmed: chain=${(_m = walletClient.chain) == null ? void 0 : _m.id} | attempts=${attempt + 1}`);
50035
50409
  break;
50036
50410
  } catch (e) {
50037
- if (((_m = e.message) == null ? void 0 : _m.includes("does not match")) && attempt < maxAttempts - 1) {
50411
+ if (((_n = e.message) == null ? void 0 : _n.includes("does not match")) && attempt < maxAttempts - 1) {
50038
50412
  console.log(`[${action}] connector still settling, attempt ${attempt + 1}: ${e.message}`);
50039
50413
  await new Promise((resolve2) => setTimeout(resolve2, 200));
50040
50414
  continue;
@@ -50122,6 +50496,7 @@ const CoinleyPaymentInternal = ({
50122
50496
  const friendly = parseUserFriendlyError(err);
50123
50497
  setError((friendly == null ? void 0 : friendly.message) || "Transaction failed. Please try again.");
50124
50498
  setCurrentStep(PAYMENT_STEPS.CONFIRM);
50499
+ handleHardFailure("evm-execute-failed", { swapBridgeFlow: isSwapOrBridgeFlow });
50125
50500
  }
50126
50501
  }
50127
50502
  };
@@ -53383,4 +53758,4 @@ export {
53383
53758
  verifyWebhookSignature as w,
53384
53759
  coinleyWebhookMiddleware as x
53385
53760
  };
53386
- //# sourceMappingURL=index-eI8nBZJW.js.map
53761
+ //# sourceMappingURL=index-CEBT3A4T.js.map