@unifold/ui-react 0.1.69 → 0.1.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3,11 +3,11 @@ import {
3
3
  useState as useState40,
4
4
  useEffect as useEffect34,
5
5
  useLayoutEffect as useLayoutEffect2,
6
- useCallback as useCallback10,
6
+ useCallback as useCallback11,
7
7
  useRef as useRef13,
8
8
  useMemo as useMemo14
9
9
  } from "react";
10
- import { ChevronRight as ChevronRight18, MapPinOff as MapPinOff2, AlertTriangle as AlertTriangle3, Bitcoin, DollarSign as DollarSign3 } from "lucide-react";
10
+ import { ChevronRight as ChevronRight18, MapPinOff as MapPinOff2, AlertTriangle as AlertTriangle4, Bitcoin, DollarSign as DollarSign3 } from "lucide-react";
11
11
 
12
12
  // src/components/shared/dialog.tsx
13
13
  import * as React3 from "react";
@@ -600,7 +600,8 @@ import {
600
600
  // src/hooks/use-deposit-address.ts
601
601
  import { useQuery } from "@tanstack/react-query";
602
602
  import {
603
- createDepositAddress
603
+ createDepositAddress,
604
+ isDepositAddressValidationError
604
605
  } from "@unifold/core";
605
606
  function useDepositAddress(params) {
606
607
  const {
@@ -646,7 +647,13 @@ function useDepositAddress(params) {
646
647
  // 24 hours in cache
647
648
  refetchOnMount: false,
648
649
  refetchOnWindowFocus: false,
649
- retry: 3,
650
+ // Don't retry recipient-address validation errors — they're deterministic
651
+ // (a 400 won't succeed on retry) and we want to surface the invalid-address
652
+ // screen immediately rather than after 3 backoff attempts.
653
+ retry: (failureCount, error) => {
654
+ if (isDepositAddressValidationError(error)) return false;
655
+ return failureCount < 3;
656
+ },
650
657
  retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 1e4)
651
658
  // 1s, 2s, 4s (max 10s)
652
659
  });
@@ -667,7 +674,7 @@ function useDebounce(value, delay) {
667
674
  import { useState as useState5 } from "react";
668
675
 
669
676
  // src/components/deposits/DepositHeader.tsx
670
- import { ArrowLeft, X as X2 } from "lucide-react";
677
+ import { AlertTriangle, ArrowLeft, Info, X as X2 } from "lucide-react";
671
678
  import { useEffect as useEffect3, useLayoutEffect, useState as useState3 } from "react";
672
679
  import { getAddressBalance } from "@unifold/core";
673
680
 
@@ -873,7 +880,8 @@ function DepositHeader({
873
880
  balanceChainId,
874
881
  balanceTokenAddress,
875
882
  projectName,
876
- publishableKey
883
+ publishableKey,
884
+ incident
877
885
  }) {
878
886
  const { colors: colors2, fonts, components } = useTheme();
879
887
  const [balance, setBalance] = useState3(null);
@@ -971,19 +979,64 @@ function DepositHeader({
971
979
  balanceTokenAddress,
972
980
  publishableKey
973
981
  ]);
974
- return /* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
975
- showBack ? /* @__PURE__ */ jsx4(
976
- "button",
977
- {
978
- onClick: onBack,
979
- className: "hover:uf-bg-secondary uf-rounded-lg uf-p-1 uf-transition-colors",
980
- style: { color: components.header.buttonColor },
981
- children: /* @__PURE__ */ jsx4(ArrowLeft, { className: "uf-w-5 uf-h-5" })
982
- }
983
- ) : /* @__PURE__ */ jsx4("div", { className: "uf-w-5 uf-h-5 uf-invisible" }),
984
- /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-flex-col uf-items-center", children: [
985
- badge ? /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
986
- /* @__PURE__ */ jsx4(
982
+ const incidentMessages = incident?.messages ?? [];
983
+ const showIncident = incident?.enabled && incidentMessages.length > 0;
984
+ const incidentSeverity = incident?.severity ?? "degraded";
985
+ const incidentSeverityLabel = incidentSeverity === "outage" ? "Outage" : incidentSeverity === "info" ? "Info" : "Degraded service";
986
+ const incidentStyles = incidentSeverity === "outage" ? {
987
+ bg: "rgba(239, 68, 68, 0.12)",
988
+ border: "rgba(239, 68, 68, 0.35)",
989
+ text: "#fca5a5",
990
+ link: "#fca5a5"
991
+ } : incidentSeverity === "info" ? {
992
+ bg: "rgba(59, 130, 246, 0.12)",
993
+ border: "rgba(59, 130, 246, 0.35)",
994
+ text: "#93c5fd",
995
+ link: "#93c5fd"
996
+ } : {
997
+ bg: "rgba(245, 158, 11, 0.12)",
998
+ border: "rgba(245, 158, 11, 0.35)",
999
+ text: "#fcd34d",
1000
+ link: "#fcd34d"
1001
+ };
1002
+ const IncidentIcon = incidentSeverity === "info" ? Info : AlertTriangle;
1003
+ return /* @__PURE__ */ jsxs3("div", { children: [
1004
+ /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
1005
+ showBack ? /* @__PURE__ */ jsx4(
1006
+ "button",
1007
+ {
1008
+ onClick: onBack,
1009
+ className: "hover:uf-bg-secondary uf-rounded-lg uf-p-1 uf-transition-colors",
1010
+ style: { color: components.header.buttonColor },
1011
+ children: /* @__PURE__ */ jsx4(ArrowLeft, { className: "uf-w-5 uf-h-5" })
1012
+ }
1013
+ ) : /* @__PURE__ */ jsx4("div", { className: "uf-w-5 uf-h-5 uf-invisible" }),
1014
+ /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-flex-col uf-items-center", children: [
1015
+ badge ? /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
1016
+ /* @__PURE__ */ jsx4(
1017
+ DialogTitle,
1018
+ {
1019
+ className: "uf-text-center uf-text-base",
1020
+ style: {
1021
+ color: components.header.titleColor,
1022
+ fontFamily: fonts.medium
1023
+ },
1024
+ children: title
1025
+ }
1026
+ ),
1027
+ /* @__PURE__ */ jsx4(
1028
+ "div",
1029
+ {
1030
+ className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
1031
+ style: {
1032
+ backgroundColor: colors2.card,
1033
+ color: colors2.foregroundMuted,
1034
+ fontFamily: fonts.regular
1035
+ },
1036
+ children: badge.count
1037
+ }
1038
+ )
1039
+ ] }) : /* @__PURE__ */ jsx4(
987
1040
  DialogTitle,
988
1041
  {
989
1042
  className: "uf-text-center uf-text-base",
@@ -994,61 +1047,91 @@ function DepositHeader({
994
1047
  children: title
995
1048
  }
996
1049
  ),
997
- /* @__PURE__ */ jsx4(
1050
+ subtitle ? /* @__PURE__ */ jsx4(
998
1051
  "div",
999
1052
  {
1000
- className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
1053
+ className: "uf-text-xs uf-mt-1",
1001
1054
  style: {
1002
- backgroundColor: colors2.card,
1003
1055
  color: colors2.foregroundMuted,
1004
1056
  fontFamily: fonts.regular
1005
1057
  },
1006
- children: badge.count
1058
+ children: subtitle
1007
1059
  }
1008
- )
1009
- ] }) : /* @__PURE__ */ jsx4(
1010
- DialogTitle,
1011
- {
1012
- className: "uf-text-center uf-text-base",
1013
- style: {
1014
- color: components.header.titleColor,
1015
- fontFamily: fonts.medium
1016
- },
1017
- children: title
1018
- }
1019
- ),
1020
- subtitle ? /* @__PURE__ */ jsx4(
1021
- "div",
1022
- {
1023
- className: "uf-text-xs uf-mt-1",
1024
- style: {
1025
- color: colors2.foregroundMuted,
1026
- fontFamily: fonts.regular
1027
- },
1028
- children: subtitle
1029
- }
1030
- ) : showBalanceBlock ? isLoadingBalance && showBalanceSkeleton ? /* @__PURE__ */ jsx4("div", { className: "uf-h-3 uf-w-32 uf-bg-muted uf-rounded uf-animate-pulse uf-mt-1" }) : balance ? /* @__PURE__ */ jsx4(
1031
- "div",
1060
+ ) : showBalanceBlock ? isLoadingBalance && showBalanceSkeleton ? /* @__PURE__ */ jsx4("div", { className: "uf-h-3 uf-w-32 uf-bg-muted uf-rounded uf-animate-pulse uf-mt-1" }) : balance ? /* @__PURE__ */ jsx4(
1061
+ "div",
1062
+ {
1063
+ className: "uf-text-xs uf-mt-1",
1064
+ style: {
1065
+ color: colors2.foregroundMuted,
1066
+ fontFamily: fonts.regular
1067
+ },
1068
+ children: formatBalanceDisplay(balance, projectName)
1069
+ }
1070
+ ) : null : null
1071
+ ] }),
1072
+ showClose ? /* @__PURE__ */ jsx4(
1073
+ "button",
1032
1074
  {
1033
- className: "uf-text-xs uf-mt-1",
1034
- style: {
1035
- color: colors2.foregroundMuted,
1036
- fontFamily: fonts.regular
1037
- },
1038
- children: formatBalanceDisplay(balance, projectName)
1075
+ onClick: onClose,
1076
+ className: "hover:uf-bg-secondary uf-rounded-lg uf-p-1 uf-transition-colors",
1077
+ style: { color: components.header.buttonColor },
1078
+ children: /* @__PURE__ */ jsx4(X2, { className: "uf-w-5 uf-h-5" })
1039
1079
  }
1040
- ) : null : null
1080
+ ) : /* @__PURE__ */ jsx4("div", { className: "uf-w-5 uf-h-5 uf-invisible" })
1041
1081
  ] }),
1042
- showClose ? /* @__PURE__ */ jsx4(
1043
- "button",
1082
+ showIncident && /* @__PURE__ */ jsx4(
1083
+ "div",
1044
1084
  {
1045
- onClick: onClose,
1046
- className: "hover:uf-bg-secondary uf-rounded-lg uf-p-1 uf-transition-colors",
1047
- style: { color: components.header.buttonColor },
1048
- children: /* @__PURE__ */ jsx4(X2, { className: "uf-w-5 uf-h-5" })
1085
+ className: "uf-rounded-lg uf-px-3 uf-py-2.5 uf-mb-4",
1086
+ style: {
1087
+ backgroundColor: incidentStyles.bg,
1088
+ border: `1px solid ${incidentStyles.border}`
1089
+ },
1090
+ children: /* @__PURE__ */ jsxs3("div", { className: "uf-flex uf-items-start uf-gap-2.5", children: [
1091
+ /* @__PURE__ */ jsx4(
1092
+ IncidentIcon,
1093
+ {
1094
+ className: "uf-w-4 uf-h-4 uf-mt-0.5 uf-shrink-0",
1095
+ style: { color: incidentStyles.text }
1096
+ }
1097
+ ),
1098
+ /* @__PURE__ */ jsxs3("div", { className: "uf-min-w-0 uf-flex-1", children: [
1099
+ /* @__PURE__ */ jsx4("div", { className: "uf-flex uf-items-center uf-gap-2 uf-mb-1.5", children: /* @__PURE__ */ jsx4(
1100
+ "span",
1101
+ {
1102
+ className: "uf-text-[11px] uf-leading-none uf-px-1.5 uf-py-1 uf-rounded-md",
1103
+ style: {
1104
+ color: incidentStyles.text,
1105
+ border: `1px solid ${incidentStyles.border}`,
1106
+ fontFamily: fonts.medium
1107
+ },
1108
+ children: incidentSeverityLabel
1109
+ }
1110
+ ) }),
1111
+ /* @__PURE__ */ jsx4(
1112
+ "div",
1113
+ {
1114
+ className: "uf-space-y-1",
1115
+ style: { color: incidentStyles.text, fontFamily: fonts.regular },
1116
+ children: incidentMessages.map((message, index) => /* @__PURE__ */ jsx4("p", { className: "uf-text-xs uf-leading-relaxed", children: message }, `${message}-${index}`))
1117
+ }
1118
+ ),
1119
+ incident.statusPageUrl && /* @__PURE__ */ jsx4(
1120
+ "a",
1121
+ {
1122
+ href: incident.statusPageUrl,
1123
+ target: "_blank",
1124
+ rel: "noreferrer",
1125
+ className: "uf-inline-block uf-mt-1.5 uf-text-xs uf-underline uf-underline-offset-2",
1126
+ style: { color: incidentStyles.link, fontFamily: fonts.medium },
1127
+ children: "View status"
1128
+ }
1129
+ )
1130
+ ] })
1131
+ ] })
1049
1132
  }
1050
- ) : /* @__PURE__ */ jsx4("div", { className: "uf-w-5 uf-h-5 uf-invisible" })
1051
- ] }) });
1133
+ )
1134
+ ] });
1052
1135
  }
1053
1136
 
1054
1137
  // src/components/currency/CurrencyListItem.tsx
@@ -1386,7 +1469,8 @@ var en_default = {
1386
1469
  },
1387
1470
  stripeLink: {
1388
1471
  title: "Pay with Link",
1389
- subtitle: "Buy with card or bank"
1472
+ subtitle: "Buy with card or bank",
1473
+ unavailableInRegionMessage: "Pay with Link is currently unavailable in your region."
1390
1474
  },
1391
1475
  browserWallet: {
1392
1476
  title: "Connect Wallet",
@@ -6554,7 +6638,7 @@ function BankTransfer({
6554
6638
  subdivision_code: userIpInfo?.subdivisionCode || void 0,
6555
6639
  // A provider may advertise multiple rails; default to the first.
6556
6640
  // UIs that want per-rail rows would need to render one entry per element.
6557
- payment_method: provider.payment_methods[0]
6641
+ payment_method_type: provider.payment_methods[0]
6558
6642
  });
6559
6643
  setActiveProvider(provider);
6560
6644
  const convertedPrefilled = await resolvePrefilledSourceAmount(provider.source_currency);
@@ -7885,7 +7969,7 @@ function AppleLogo({ className, style }) {
7885
7969
  }
7886
7970
  );
7887
7971
  }
7888
- function ApplePayButton({ onClick, title, subtitle }) {
7972
+ function ApplePayButton({ onClick, title, subtitle, iconUrl }) {
7889
7973
  const { colors: colors2, fonts, components } = useTheme();
7890
7974
  const [isHovered, setIsHovered] = React14.useState(false);
7891
7975
  const [isTouchDevice, setIsTouchDevice] = React14.useState(false);
@@ -7907,7 +7991,14 @@ function ApplePayButton({ onClick, title, subtitle }) {
7907
7991
  },
7908
7992
  children: [
7909
7993
  /* @__PURE__ */ jsxs23("div", { className: "uf-flex uf-items-center uf-gap-3", children: [
7910
- /* @__PURE__ */ jsx26("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ jsx26(AppleLogo, { className: "uf-w-5 uf-h-5", style: { color: components.card.iconColor } }) }),
7994
+ /* @__PURE__ */ jsx26("div", { className: "uf-rounded-lg uf-overflow-hidden uf-w-9 uf-h-9 uf-flex uf-items-center uf-justify-center", children: iconUrl ? /* @__PURE__ */ jsx26("img", { src: iconUrl, alt: "Apple Pay", width: 36, height: 36, className: "uf-rounded-lg" }) : /* @__PURE__ */ jsx26(
7995
+ "div",
7996
+ {
7997
+ className: "uf-w-9 uf-h-9 uf-rounded-lg uf-flex uf-items-center uf-justify-center",
7998
+ style: { backgroundColor: "#000" },
7999
+ children: /* @__PURE__ */ jsx26(AppleLogo, { className: "uf-w-5 uf-h-5", style: { color: "#fff" } })
8000
+ }
8001
+ ) }),
7911
8002
  /* @__PURE__ */ jsxs23("div", { className: "uf-text-left", children: [
7912
8003
  /* @__PURE__ */ jsx26(
7913
8004
  "div",
@@ -8141,13 +8232,6 @@ function solanaCandidate(provider, type, name, icon) {
8141
8232
  if (provider.isConnected && provider.publicKey) {
8142
8233
  return { type, name, address: provider.publicKey.toString(), icon };
8143
8234
  }
8144
- try {
8145
- const resp = await provider.connect({ onlyIfTrusted: true });
8146
- if (resp.publicKey) {
8147
- return { type, name, address: resp.publicKey.toString(), icon };
8148
- }
8149
- } catch {
8150
- }
8151
8235
  return null;
8152
8236
  }
8153
8237
  };
@@ -8376,6 +8460,180 @@ async function disconnectInjectedBrowserWallet(wallet) {
8376
8460
  );
8377
8461
  }
8378
8462
 
8463
+ // src/components/deposits/browser-wallets/providerResolvers.ts
8464
+ var STORED_TYPE_TO_EIP6963_WALLET_ID = {
8465
+ metamask: "metamask",
8466
+ "phantom-ethereum": "phantom",
8467
+ coinbase: "coinbase",
8468
+ trust: "trust",
8469
+ rainbow: "rainbow",
8470
+ rabby: "rabby",
8471
+ okx: "okx"
8472
+ };
8473
+ var EIP6963_WALLET_ID_TO_INFO = {
8474
+ metamask: { walletType: "metamask", name: "MetaMask", icon: "metamask" },
8475
+ phantom: { walletType: "phantom-ethereum", name: "Phantom", icon: "phantom" },
8476
+ coinbase: { walletType: "coinbase", name: "Coinbase Wallet", icon: "coinbase" },
8477
+ trust: { walletType: "trust", name: "Trust Wallet", icon: "trust" },
8478
+ rainbow: { walletType: "rainbow", name: "Rainbow", icon: "rainbow" },
8479
+ rabby: { walletType: "rabby", name: "Rabby", icon: "rabby" },
8480
+ okx: { walletType: "okx", name: "OKX Wallet", icon: "okx" }
8481
+ };
8482
+ var WALLET_ID_TO_WALLET_TYPE = {
8483
+ phantom: "phantom-ethereum",
8484
+ coinbase: "coinbase",
8485
+ trust: "trust",
8486
+ rainbow: "rainbow",
8487
+ rabby: "rabby",
8488
+ okx: "okx",
8489
+ metamask: "metamask"
8490
+ };
8491
+ var WALLET_TYPE_TO_WALLET_ID = {
8492
+ "phantom-ethereum": "phantom",
8493
+ coinbase: "coinbase",
8494
+ trust: "trust",
8495
+ okx: "okx",
8496
+ rainbow: "rainbow",
8497
+ rabby: "rabby",
8498
+ metamask: "metamask"
8499
+ };
8500
+ function isWalletType(value) {
8501
+ return value === "phantom-solana" || value === "phantom-ethereum" || value === "metamask" || value === "coinbase" || value === "solflare" || value === "backpack" || value === "glow" || value === "trust" || value === "rainbow" || value === "rabby" || value === "okx";
8502
+ }
8503
+ function walletIdToWalletType(walletId) {
8504
+ return WALLET_ID_TO_WALLET_TYPE[walletId] || "metamask";
8505
+ }
8506
+ function walletTypeToWalletId(walletType) {
8507
+ return WALLET_TYPE_TO_WALLET_ID[walletType] || walletType;
8508
+ }
8509
+ function getLegacyEvmProviders(win) {
8510
+ if (!win) return {};
8511
+ const anyWin = win;
8512
+ return {
8513
+ ethereum: anyWin.ethereum,
8514
+ phantomEthereum: anyWin.phantom?.ethereum,
8515
+ coinbaseEthereum: anyWin.coinbaseWalletExtension,
8516
+ trustEthereum: anyWin.trustwallet?.ethereum,
8517
+ okxEthereum: anyWin.okxwallet
8518
+ };
8519
+ }
8520
+ function getInjectedSolanaProviders(win) {
8521
+ if (!win) return {};
8522
+ const anyWin = win;
8523
+ return {
8524
+ phantomSolana: anyWin.phantom?.solana,
8525
+ solflare: anyWin.solflare,
8526
+ backpack: anyWin.backpack,
8527
+ glow: anyWin.glow,
8528
+ coinbaseSolana: anyWin.coinbaseSolana || anyWin.coinbaseWalletExtension?.solana,
8529
+ trustSolana: anyWin.trustwallet?.solana
8530
+ };
8531
+ }
8532
+ function describeEip6963Provider(wp) {
8533
+ const mapped = EIP6963_WALLET_ID_TO_INFO[wp.walletId];
8534
+ return {
8535
+ provider: wp.provider,
8536
+ walletType: mapped?.walletType ?? "metamask",
8537
+ name: mapped?.name ?? wp.info.name,
8538
+ icon: mapped?.icon ?? wp.info.icon
8539
+ };
8540
+ }
8541
+ function resolveQuickConnectEvmProvider(win) {
8542
+ const eip6963Providers = getEip6963Providers();
8543
+ if (eip6963Providers.length > 0) {
8544
+ const stored = getStoredWalletState();
8545
+ const preferredWalletId = stored?.walletType && isWalletType(stored.walletType) ? STORED_TYPE_TO_EIP6963_WALLET_ID[stored.walletType] : void 0;
8546
+ if (preferredWalletId) {
8547
+ const preferred = findProviderByWalletId(preferredWalletId);
8548
+ if (preferred) return describeEip6963Provider(preferred);
8549
+ }
8550
+ if (eip6963Providers.length === 1) {
8551
+ return describeEip6963Provider(eip6963Providers[0]);
8552
+ }
8553
+ return void 0;
8554
+ }
8555
+ const anyWin = win;
8556
+ const legacy = anyWin.phantom?.ethereum || anyWin.ethereum;
8557
+ if (!legacy) return void 0;
8558
+ const isPhantom = legacy.isPhantom;
8559
+ return {
8560
+ provider: legacy,
8561
+ walletType: isPhantom ? "phantom-ethereum" : "metamask",
8562
+ name: isPhantom ? "Phantom" : "MetaMask",
8563
+ icon: isPhantom ? "phantom" : "metamask"
8564
+ };
8565
+ }
8566
+ function resolveSolanaPublicKey(provider, response) {
8567
+ if (response?.publicKey) return { publicKey: response.publicKey };
8568
+ if (provider.publicKey) return { publicKey: provider.publicKey };
8569
+ return null;
8570
+ }
8571
+ function isUserRejectedSolanaConnectError(error) {
8572
+ if (!error || typeof error !== "object") return false;
8573
+ const maybeCode = "code" in error ? error.code : void 0;
8574
+ if (maybeCode === 4001) return true;
8575
+ const msg = "message" in error && typeof error.message === "string" ? error.message.toLowerCase() : "";
8576
+ return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("rejected the request") || msg.includes("declined");
8577
+ }
8578
+ function isSolanaConnectTimeoutError(error) {
8579
+ return error instanceof Error && error.message.toLowerCase().includes("did not respond to the connection request");
8580
+ }
8581
+ async function connectSolanaProviderWithRecovery(provider, walletId, walletName) {
8582
+ if (provider.isConnected && provider.publicKey) {
8583
+ return { publicKey: provider.publicKey };
8584
+ }
8585
+ const connectOnce = () => provider.connect(walletId === "solflare" ? { onlyIfTrusted: false } : void 0);
8586
+ const withTimeout = async (ms = 2e4) => await Promise.race([
8587
+ connectOnce(),
8588
+ new Promise(
8589
+ (resolve, reject) => setTimeout(() => {
8590
+ const connected = resolveSolanaPublicKey(provider);
8591
+ if (connected) {
8592
+ resolve(connected);
8593
+ return;
8594
+ }
8595
+ reject(
8596
+ new Error(
8597
+ `${walletName} did not respond to the connection request. Please unlock the wallet and try again.`
8598
+ )
8599
+ );
8600
+ }, ms)
8601
+ )
8602
+ ]);
8603
+ if (walletId === "solflare") {
8604
+ await provider.disconnect?.().catch(() => {
8605
+ });
8606
+ }
8607
+ const connectAndResolve = async () => {
8608
+ try {
8609
+ const response = await withTimeout();
8610
+ const resolved = resolveSolanaPublicKey(provider, response);
8611
+ if (resolved) return resolved;
8612
+ await new Promise((resolve) => setTimeout(resolve, 120));
8613
+ const delayedResolved = resolveSolanaPublicKey(provider);
8614
+ if (delayedResolved) return delayedResolved;
8615
+ throw new Error(`${walletName} connected but did not expose a public key.`);
8616
+ } catch (error) {
8617
+ const connected = resolveSolanaPublicKey(provider);
8618
+ if (connected) return connected;
8619
+ throw error;
8620
+ }
8621
+ };
8622
+ try {
8623
+ return await connectAndResolve();
8624
+ } catch (err) {
8625
+ if (isUserRejectedSolanaConnectError(err)) throw err;
8626
+ if (isSolanaConnectTimeoutError(err)) throw err;
8627
+ if (walletId === "solflare") {
8628
+ await provider.disconnect?.().catch(() => {
8629
+ });
8630
+ await new Promise((resolve) => setTimeout(resolve, 150));
8631
+ return await connectAndResolve();
8632
+ }
8633
+ throw err;
8634
+ }
8635
+ }
8636
+
8379
8637
  // src/resources/icons/MetamaskIcon.tsx
8380
8638
  import * as React17 from "react";
8381
8639
  import { jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
@@ -10288,21 +10546,19 @@ function BrowserWalletButton({
10288
10546
  }
10289
10547
  }
10290
10548
  if (!chainType || chainType === "ethereum") {
10291
- const ethProvider = window.phantom?.ethereum || window.ethereum;
10292
- if (ethProvider) {
10293
- const accounts = await ethProvider.request({
10549
+ const resolved = resolveQuickConnectEvmProvider(window);
10550
+ if (resolved) {
10551
+ const accounts = await resolved.provider.request({
10294
10552
  method: "eth_requestAccounts"
10295
10553
  });
10296
10554
  if (accounts && accounts.length > 0) {
10297
10555
  setUserDisconnectedWallet(false);
10298
- const isPhantom = ethProvider.isPhantom;
10299
- const walletType = isPhantom ? "phantom-ethereum" : "metamask";
10300
- setStoredWalletState(walletType);
10556
+ setStoredWalletState(resolved.walletType);
10301
10557
  setWallet({
10302
- type: walletType,
10303
- name: isPhantom ? "Phantom" : "MetaMask",
10558
+ type: resolved.walletType,
10559
+ name: resolved.name,
10304
10560
  address: accounts[0],
10305
- icon: isPhantom ? "phantom" : "metamask"
10561
+ icon: resolved.icon
10306
10562
  });
10307
10563
  }
10308
10564
  }
@@ -10336,7 +10592,10 @@ function BrowserWalletButton({
10336
10592
  if (isLoading) {
10337
10593
  return null;
10338
10594
  }
10339
- const hasWalletExtension = (!chainType || chainType === "ethereum") && getEip6963Providers().length > 0 || (!chainType || chainType === "solana") && (window.phantom?.solana?.isPhantom || window.solana?.isPhantom) || (!chainType || chainType === "ethereum") && (window.phantom?.ethereum || window.ethereum);
10595
+ const eip6963EvmProviderCount = getEip6963Providers().length;
10596
+ const legacyEvmProviders = getLegacyEvmProviders(window);
10597
+ const hasLegacyEvmProvider = eip6963EvmProviderCount === 0 && !!(legacyEvmProviders.ethereum || legacyEvmProviders.phantomEthereum || legacyEvmProviders.coinbaseEthereum || legacyEvmProviders.trustEthereum || legacyEvmProviders.okxEthereum);
10598
+ const hasWalletExtension = (!chainType || chainType === "ethereum") && eip6963EvmProviderCount > 0 || (!chainType || chainType === "solana") && (window.phantom?.solana?.isPhantom || window.solana?.isPhantom) || (!chainType || chainType === "ethereum") && hasLegacyEvmProvider;
10340
10599
  if (!onConnectClick && !wallet && !hasWalletExtension) {
10341
10600
  return null;
10342
10601
  }
@@ -10346,11 +10605,25 @@ function BrowserWalletButton({
10346
10605
  border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
10347
10606
  };
10348
10607
  const sortedWallets = featuredWallets ? [...featuredWallets].sort((a, b) => a.position - b.position) : [];
10608
+ const isImageIcon = !!wallet && (wallet.icon.startsWith("data:") || wallet.icon.startsWith("http"));
10349
10609
  const walletIconBlock = wallet ? WALLET_ICON_COMPONENTS[wallet.icon] ? React29.createElement(WALLET_ICON_COMPONENTS[wallet.icon], {
10350
10610
  size: 36,
10351
10611
  className: "uf-rounded-lg",
10352
10612
  variant: "color"
10353
- }) : /* @__PURE__ */ jsx41("div", { className: "uf-w-9 uf-h-9 uf-rounded-lg uf-bg-gray-500" }) : /* @__PURE__ */ jsx41("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ jsx41(Wallet, { className: "uf-w-5 uf-h-5", style: { color: components.card.iconColor } }) });
10613
+ }) : isImageIcon ? (
10614
+ // Wallet announced via EIP-6963 with no internal icon component: render its
10615
+ // own advertised icon (`info.icon`) rather than a generic placeholder.
10616
+ /* @__PURE__ */ jsx41(
10617
+ "img",
10618
+ {
10619
+ src: wallet.icon,
10620
+ alt: wallet.name,
10621
+ width: 36,
10622
+ height: 36,
10623
+ className: "uf-rounded-lg uf-w-9 uf-h-9"
10624
+ }
10625
+ )
10626
+ ) : /* @__PURE__ */ jsx41("div", { className: "uf-w-9 uf-h-9 uf-rounded-lg uf-bg-gray-500" }) : /* @__PURE__ */ jsx41("div", { className: "uf-rounded-lg uf-p-2", children: /* @__PURE__ */ jsx41(Wallet, { className: "uf-w-5 uf-h-5", style: { color: components.card.iconColor } }) });
10354
10627
  const titleSubtitleBlock = /* @__PURE__ */ jsxs38("div", { className: "uf-text-left uf-min-w-0", children: [
10355
10628
  /* @__PURE__ */ jsx41(
10356
10629
  "div",
@@ -10579,7 +10852,7 @@ import {
10579
10852
  Loader2 as Loader25,
10580
10853
  CreditCard as CreditCard3,
10581
10854
  CheckCircle2 as CheckCircle22,
10582
- AlertTriangle,
10855
+ AlertTriangle as AlertTriangle2,
10583
10856
  ArrowRight,
10584
10857
  Zap as Zap2,
10585
10858
  ChevronDown as ChevronDown3
@@ -10945,6 +11218,13 @@ function setStoredSelectedToken(customerId, token) {
10945
11218
  } catch {
10946
11219
  }
10947
11220
  }
11221
+ function clearStoredSelectedToken(customerId) {
11222
+ try {
11223
+ if (typeof window === "undefined" || !customerId) return;
11224
+ localStorage.removeItem(selectedTokenKey(customerId));
11225
+ } catch {
11226
+ }
11227
+ }
10948
11228
 
10949
11229
  // src/components/deposits/stripe-link/PayWithStripeLink.tsx
10950
11230
  import { Fragment as Fragment6, jsx as jsx44, jsxs as jsxs41 } from "react/jsx-runtime";
@@ -10962,6 +11242,31 @@ var STRIPE_SESSION_STATUS = {
10962
11242
  EXPIRED: "expired"
10963
11243
  };
10964
11244
  var STRIPE_SESSION_POLL_INTERVAL_MS = 3e3;
11245
+ var STRIPE_CONSUMED_PAYMENT_METHOD_CODE = "payment_method_unexpected_state";
11246
+ function isConsumedPaymentMethodError(err) {
11247
+ return err instanceof StripeApiResponseError && (err.errorType === "stripe_onramp_payment_method_consumed" || err.stripeCode === STRIPE_CONSUMED_PAYMENT_METHOD_CODE);
11248
+ }
11249
+ var STRIPE_PAYMENT_FAILURE_MESSAGES = {
11250
+ card_declined: "Your card was declined. Try again or use a different payment method.",
11251
+ insufficient_funds: "Your card has insufficient funds. Try a different payment method.",
11252
+ expired_card: "Your card has expired. Use a different payment method.",
11253
+ incorrect_cvc: "Your card\u2019s security code is incorrect. Try again or use a different card.",
11254
+ incorrect_number: "Your card number is incorrect. Try again or use a different card.",
11255
+ card_not_supported: "This card isn\u2019t supported for this purchase. Try a different payment method.",
11256
+ processing_error: "We couldn\u2019t process your card. Please try again in a moment.",
11257
+ authentication_required: "Your bank requires authentication for this payment. Please try again.",
11258
+ payment_intent_authentication_failure: "Card authentication failed. Try again or use a different card.",
11259
+ payment_method_provider_decline: "Your payment provider declined the charge. Try a different payment method.",
11260
+ payment_failed: "Your payment didn\u2019t go through. Try again or use a different payment method."
11261
+ };
11262
+ function describeStripePaymentFailure(reason) {
11263
+ if (!reason) return null;
11264
+ return STRIPE_PAYMENT_FAILURE_MESSAGES[reason] ?? null;
11265
+ }
11266
+ var PAYMENT_FAILED_LEAD = "We couldn\u2019t complete your payment.";
11267
+ function unmappedPaymentFailureMessage(stripeMessage) {
11268
+ return stripeMessage ? `${PAYMENT_FAILED_LEAD} ${stripeMessage}` : `${PAYMENT_FAILED_LEAD} Please try again.`;
11269
+ }
10965
11270
  var NO_AUTOFILL = {
10966
11271
  autoComplete: "off",
10967
11272
  "data-lpignore": "true",
@@ -11063,7 +11368,11 @@ function PayWithStripeLink({
11063
11368
  }, [controlledStep]);
11064
11369
  useEffect24(() => {
11065
11370
  setLoading(false);
11066
- setError(null);
11371
+ if (preserveErrorOnNextStepRef.current) {
11372
+ preserveErrorOnNextStepRef.current = false;
11373
+ } else {
11374
+ setError(null);
11375
+ }
11067
11376
  setKycPendingMessage(null);
11068
11377
  setQuoteLoading(false);
11069
11378
  if (step !== "checkout") {
@@ -11152,7 +11461,10 @@ function PayWithStripeLink({
11152
11461
  const pendingAddPaymentRef = useRef9(false);
11153
11462
  const autoOpenedAddPaymentRef = useRef9(false);
11154
11463
  const checkoutGenRef = useRef9(0);
11464
+ const preserveErrorOnNextStepRef = useRef9(false);
11465
+ const spentSessionIdsRef = useRef9(/* @__PURE__ */ new Set());
11155
11466
  const [stripePaymentUIReady, setStripePaymentUIReady] = useState29(false);
11467
+ const [addPaymentHovered, setAddPaymentHovered] = useState29(false);
11156
11468
  const [oauthToken, setOauthToken] = useState29(null);
11157
11469
  const [refreshTokenValue, setRefreshTokenValue] = useState29(null);
11158
11470
  const [customerId, setCustomerId] = useState29(null);
@@ -11301,7 +11613,7 @@ function PayWithStripeLink({
11301
11613
  const merged = [
11302
11614
  ...pmResult.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
11303
11615
  ...persistedNotInApi ? [persisted] : []
11304
- ];
11616
+ ].filter((t13) => !deadPaymentTokenIdsRef.current.has(t13.id));
11305
11617
  if (merged.length > 0) {
11306
11618
  const preferred = persisted && merged.find((m) => m.id === persisted.id) || merged[0];
11307
11619
  selectPaymentToken(preferred);
@@ -11366,6 +11678,7 @@ function PayWithStripeLink({
11366
11678
  const [selectedPaymentToken, setSelectedPaymentToken] = useState29(null);
11367
11679
  const [paymentDisplay, setPaymentDisplay] = useState29(null);
11368
11680
  const selectedTokenSingleUseRef = useRef9(false);
11681
+ const deadPaymentTokenIdsRef = useRef9(/* @__PURE__ */ new Set());
11369
11682
  const selectPaymentToken = useCallback6(
11370
11683
  (token) => {
11371
11684
  selectedTokenSingleUseRef.current = !!token.singleUse;
@@ -11393,9 +11706,25 @@ function PayWithStripeLink({
11393
11706
  setStoredSelectedTokenState(null);
11394
11707
  }
11395
11708
  }, [customerId]);
11709
+ const forgetSelectedPaymentToken = useCallback6(
11710
+ (tokenId) => {
11711
+ selectedTokenSingleUseRef.current = false;
11712
+ if (customerId) {
11713
+ const persisted = getStoredSelectedToken(customerId);
11714
+ if (!tokenId || persisted?.id === tokenId) {
11715
+ clearStoredSelectedToken(customerId);
11716
+ }
11717
+ }
11718
+ setSelectedPaymentToken((prev) => !tokenId || prev === tokenId ? null : prev);
11719
+ setPaymentDisplay((prev) => !tokenId || !prev ? null : prev);
11720
+ setStoredSelectedTokenState((prev) => !tokenId || prev?.id === tokenId ? null : prev);
11721
+ },
11722
+ [customerId]
11723
+ );
11396
11724
  const displayPaymentTokens = (() => {
11397
- const apiDisplay = paymentTokens.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) }));
11398
- if (storedSelectedToken && !paymentTokens.some((t13) => t13.id === storedSelectedToken.id)) {
11725
+ const isDead = (id) => deadPaymentTokenIdsRef.current.has(id);
11726
+ const apiDisplay = paymentTokens.filter((t13) => !isDead(t13.id)).map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) }));
11727
+ if (storedSelectedToken && !isDead(storedSelectedToken.id) && !paymentTokens.some((t13) => t13.id === storedSelectedToken.id)) {
11399
11728
  return [...apiDisplay, storedSelectedToken];
11400
11729
  }
11401
11730
  return apiDisplay;
@@ -12044,7 +12373,7 @@ function PayWithStripeLink({
12044
12373
  const merged = [
12045
12374
  ...existing.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
12046
12375
  ...storedNotInApi ? [stored] : []
12047
- ];
12376
+ ].filter((t13) => !deadPaymentTokenIdsRef.current.has(t13.id));
12048
12377
  if (merged.length === 0) {
12049
12378
  if (!autoOpenedAddPaymentRef.current) {
12050
12379
  autoOpenedAddPaymentRef.current = true;
@@ -12086,6 +12415,9 @@ function PayWithStripeLink({
12086
12415
  paymentInnerRef.current.replaceChildren();
12087
12416
  };
12088
12417
  }, [step, oauthToken, customerId, publishableKey, coordinator]);
12418
+ useEffect24(() => {
12419
+ if (step !== "payment") setAddPaymentHovered(false);
12420
+ }, [step]);
12089
12421
  useEffect24(() => {
12090
12422
  if (step !== "add_payment") return;
12091
12423
  if (!isCustomerVerified(customer)) {
@@ -12236,6 +12568,17 @@ function PayWithStripeLink({
12236
12568
  }
12237
12569
  if (message) onDepositError?.({ message, error: err });
12238
12570
  };
12571
+ const isRetriablePaymentToken = (id) => !!id && paymentTokens.some((t13) => t13.id === id);
12572
+ const dropConsumedToken = (message, cause) => {
12573
+ if (selectedPaymentToken) deadPaymentTokenIdsRef.current.add(selectedPaymentToken);
12574
+ setSessionForCheckout(null);
12575
+ forgetSelectedPaymentToken(selectedPaymentToken);
12576
+ preserveErrorOnNextStepRef.current = true;
12577
+ setError(message);
12578
+ setStep("amount");
12579
+ onDepositError?.({ message, error: cause });
12580
+ };
12581
+ const CONSUMED_TOKEN_MESSAGE = "That payment method can\u2019t be used. Please choose another.";
12239
12582
  if (!sessionForCheckout || sessionForCheckout.amount !== amount || sessionForCheckout.paymentToken !== selectedPaymentToken) {
12240
12583
  return;
12241
12584
  }
@@ -12243,19 +12586,29 @@ function PayWithStripeLink({
12243
12586
  setLoading(true);
12244
12587
  setError(null);
12245
12588
  setStep("checkout");
12589
+ let lastConfirmError = null;
12246
12590
  try {
12247
- const runCheckout = (sid) => coordinator.performCheckout(sid, async (onrampSessionId) => {
12248
- try {
12249
- await withTokenRefresh(
12250
- (tok) => stripeRefreshQuote(onrampSessionId, tok, publishableKey)
12251
- );
12252
- } catch {
12253
- }
12254
- const confirmResponse = await withTokenRefresh(
12255
- (tok) => stripeConfirmSession(onrampSessionId, tok, {}, publishableKey)
12256
- );
12257
- return confirmResponse.client_secret ?? "";
12258
- });
12591
+ const runCheckout = (sid) => {
12592
+ spentSessionIdsRef.current.add(sid);
12593
+ return coordinator.performCheckout(sid, async (onrampSessionId) => {
12594
+ try {
12595
+ await withTokenRefresh(
12596
+ (tok) => stripeRefreshQuote(onrampSessionId, tok, publishableKey)
12597
+ );
12598
+ } catch {
12599
+ }
12600
+ try {
12601
+ const confirmResponse = await withTokenRefresh(
12602
+ (tok) => stripeConfirmSession(onrampSessionId, tok, {}, publishableKey)
12603
+ );
12604
+ lastConfirmError = null;
12605
+ return confirmResponse.client_secret ?? "";
12606
+ } catch (confirmErr) {
12607
+ lastConfirmError = confirmErr;
12608
+ throw confirmErr;
12609
+ }
12610
+ });
12611
+ };
12259
12612
  const lastErrorFor = async (sid) => {
12260
12613
  const sessionState = await withTokenRefresh(
12261
12614
  (tok) => stripeGetSession(sid, tok, publishableKey)
@@ -12315,8 +12668,9 @@ function PayWithStripeLink({
12315
12668
  setStep("success");
12316
12669
  } else if (lastError === "quote_rate_drifted" || lastError === "charged_with_expired_quote") {
12317
12670
  setSessionForCheckout(null);
12318
- setStep("amount");
12671
+ preserveErrorOnNextStepRef.current = true;
12319
12672
  setError("Pricing updated. Please try again.");
12673
+ setStep("amount");
12320
12674
  } else if (lastError === "missing_document_verification") {
12321
12675
  surfaceStepUpOnAmount(void 0, void 0, "l2");
12322
12676
  } else if (lastError === "transaction_limit_reached") {
@@ -12327,28 +12681,52 @@ function PayWithStripeLink({
12327
12681
  } else if (lastError === "missing_consumer_wallet") {
12328
12682
  setStep("wallet");
12329
12683
  setError(null);
12684
+ } else if (isConsumedPaymentMethodError(lastConfirmError)) {
12685
+ dropConsumedToken(CONSUMED_TOKEN_MESSAGE, lastConfirmError);
12330
12686
  } else {
12331
- setSessionForCheckout(null);
12332
- setError(lastError ?? "Checkout failed. Please try again.");
12333
- setStep("amount");
12687
+ const confirmErr = lastConfirmError instanceof StripeApiResponseError ? lastConfirmError : void 0;
12688
+ const reason = describeStripePaymentFailure(lastError) ?? describeStripePaymentFailure(confirmErr?.stripeCode) ?? unmappedPaymentFailureMessage(confirmErr?.stripeMessage);
12689
+ if (!isRetriablePaymentToken(selectedPaymentToken)) {
12690
+ dropConsumedToken(reason, lastConfirmError ?? void 0);
12691
+ } else {
12692
+ setSessionForCheckout(null);
12693
+ preserveErrorOnNextStepRef.current = true;
12694
+ setError(reason);
12695
+ setStep("amount");
12696
+ onDepositError?.({
12697
+ message: lastError ?? confirmErr?.stripeCode ?? "checkout_failed",
12698
+ error: lastConfirmError ?? void 0
12699
+ });
12700
+ }
12334
12701
  }
12335
12702
  } catch (err) {
12336
12703
  if (isStale()) return;
12337
- const msg = err instanceof Error ? err.message : "Checkout failed";
12338
- const errorType = err instanceof StripeApiResponseError ? err.errorType : void 0;
12339
- if (errorType === "stripe_onramp_missing_minimum_identity_verification") {
12704
+ const stripeErr = lastConfirmError instanceof StripeApiResponseError ? lastConfirmError : err instanceof StripeApiResponseError ? err : void 0;
12705
+ const effectiveError = stripeErr ?? err;
12706
+ const msg = stripeErr?.message ?? (err instanceof Error ? err.message : "Checkout failed");
12707
+ const errorType = stripeErr?.errorType;
12708
+ if (isConsumedPaymentMethodError(effectiveError)) {
12709
+ dropConsumedToken(CONSUMED_TOKEN_MESSAGE, effectiveError);
12710
+ } else if (errorType === "stripe_onramp_missing_minimum_identity_verification") {
12340
12711
  setStep("kyc");
12341
12712
  setError(null);
12342
12713
  } else if (errorType === "stripe_onramp_missing_identity_verification") {
12343
- surfaceStepUpOnAmount(err, msg, "l1");
12714
+ surfaceStepUpOnAmount(effectiveError, msg, "l1");
12344
12715
  } else if (errorType === "stripe_onramp_missing_document_verification") {
12345
- surfaceStepUpOnAmount(err, msg, "l2");
12716
+ surfaceStepUpOnAmount(effectiveError, msg, "l2");
12346
12717
  } else if (errorType === "stripe_onramp_purchase_limit_reached") {
12347
- surfaceStepUpOnAmount(err, msg, null);
12718
+ surfaceStepUpOnAmount(effectiveError, msg, null);
12348
12719
  } else {
12349
- setError(msg);
12350
- setStep("amount");
12351
- onDepositError?.({ message: msg, error: err });
12720
+ const reason = describeStripePaymentFailure(stripeErr?.stripeCode) ?? (stripeErr?.stripeMessage ? unmappedPaymentFailureMessage(stripeErr.stripeMessage) : msg || unmappedPaymentFailureMessage());
12721
+ if (!isRetriablePaymentToken(selectedPaymentToken)) {
12722
+ dropConsumedToken(reason, effectiveError);
12723
+ } else {
12724
+ setSessionForCheckout(null);
12725
+ preserveErrorOnNextStepRef.current = true;
12726
+ setError(reason);
12727
+ setStep("amount");
12728
+ onDepositError?.({ message: msg || "checkout_failed", error: effectiveError });
12729
+ }
12352
12730
  }
12353
12731
  } finally {
12354
12732
  setLoading(false);
@@ -12374,7 +12752,7 @@ function PayWithStripeLink({
12374
12752
  const merged = [
12375
12753
  ...existing.data.map((t13) => ({ id: t13.id, ...formatPaymentToken(t13) })),
12376
12754
  ...stored && !existing.data.some((t13) => t13.id === stored.id) ? [stored] : []
12377
- ];
12755
+ ].filter((t13) => !deadPaymentTokenIdsRef.current.has(t13.id));
12378
12756
  if (merged.length > 0) {
12379
12757
  const preferred = stored && merged.find((m) => m.id === stored.id) || merged[0];
12380
12758
  selectPaymentToken(preferred);
@@ -12453,7 +12831,7 @@ function PayWithStripeLink({
12453
12831
  useEffect24(() => {
12454
12832
  if (step === "review") {
12455
12833
  const existing = sessionForCheckoutRef.current;
12456
- const hasFreshSession = !!(existing && existing.amount === amount && existing.paymentToken === selectedPaymentToken);
12834
+ const hasFreshSession = !!(existing && existing.amount === amount && existing.paymentToken === selectedPaymentToken && !spentSessionIdsRef.current.has(existing.id));
12457
12835
  if (quoteNonce === quoteNonceAtReviewRef.current && hasFreshSession) return;
12458
12836
  quoteNonceAtReviewRef.current = quoteNonce;
12459
12837
  } else if (step !== "amount") {
@@ -12477,7 +12855,9 @@ function PayWithStripeLink({
12477
12855
  const hasPaymentMethod = !!(isAuthenticated && selectedPaymentToken && onrampWalletAddress && isOnrampWalletRegistered);
12478
12856
  if (hasPaymentMethod) {
12479
12857
  const existing = sessionForCheckoutRef.current;
12480
- const canRefreshExisting = step === "review" && existing != null && existing.amount === amount && existing.paymentToken === selectedPaymentToken;
12858
+ const canRefreshExisting = step === "review" && existing != null && existing.amount === amount && existing.paymentToken === selectedPaymentToken && // Never refresh (and thereby reuse) a session already checked out —
12859
+ // its PaymentIntent is spent. Build a fresh one instead.
12860
+ !spentSessionIdsRef.current.has(existing.id);
12481
12861
  const session = canRefreshExisting ? await withTokenRefresh((tok) => stripeRefreshQuote(existing.id, tok, publishableKey)) : await withTokenRefresh(
12482
12862
  (tok) => stripeCreateSession(
12483
12863
  {
@@ -12605,7 +12985,7 @@ function PayWithStripeLink({
12605
12985
  const exceedsReachedLimit = limitReachedAmount !== null && hasInput && amountNum >= limitReachedAmount;
12606
12986
  const exceedsLimit = exceedsKnownLimit || exceedsReachedLimit || requiredStepUp !== null;
12607
12987
  const isValidAmount = hasInput && amountNum >= 5 && !exceedsLimit;
12608
- const hasValidSession = !!(sessionForCheckout && sessionForCheckout.amount === amount && sessionForCheckout.paymentToken === selectedPaymentToken);
12988
+ const hasValidSession = !!(sessionForCheckout && sessionForCheckout.amount === amount && sessionForCheckout.paymentToken === selectedPaymentToken && !spentSessionIdsRef.current.has(sessionForCheckout.id));
12609
12989
  const canCheckoutDirectly = !!(oauthToken && customerId && selectedPaymentToken && isOnrampWalletRegistered);
12610
12990
  const isPreparingSession = canCheckoutDirectly && isValidAmount && !quoteError && (quoteLoading || !hasValidSession);
12611
12991
  const custTiers = customer?.kyc_tiers ?? [];
@@ -12625,7 +13005,7 @@ function PayWithStripeLink({
12625
13005
  };
12626
13006
  if (configError) {
12627
13007
  return /* @__PURE__ */ jsxs41("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-12 uf-px-4", children: [
12628
- /* @__PURE__ */ jsx44(AlertTriangle, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
13008
+ /* @__PURE__ */ jsx44(AlertTriangle2, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
12629
13009
  /* @__PURE__ */ jsx44(
12630
13010
  "p",
12631
13011
  {
@@ -12639,7 +13019,7 @@ function PayWithStripeLink({
12639
13019
  if (step === "kyc" || step === "wallet") {
12640
13020
  if (error && !loading) {
12641
13021
  return /* @__PURE__ */ jsxs41("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-12 uf-px-4", children: [
12642
- /* @__PURE__ */ jsx44(AlertTriangle, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
13022
+ /* @__PURE__ */ jsx44(AlertTriangle2, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
12643
13023
  /* @__PURE__ */ jsx44(
12644
13024
  "p",
12645
13025
  {
@@ -12778,7 +13158,7 @@ function PayWithStripeLink({
12778
13158
  },
12779
13159
  children: [
12780
13160
  /* @__PURE__ */ jsx44(
12781
- AlertTriangle,
13161
+ AlertTriangle2,
12782
13162
  {
12783
13163
  className: "uf-w-4 uf-h-4 uf-mt-0.5 uf-shrink-0",
12784
13164
  style: { color: colors2.error }
@@ -13122,7 +13502,7 @@ function PayWithStripeLink({
13122
13502
  {
13123
13503
  onClick: kycFormMode === "name" ? handleNameNext : handleKycSubmit,
13124
13504
  disabled: loading || (kycFormMode === "name" ? !kycForm.givenName || !kycForm.surname : kycFormMode === "address" ? !kycForm.addressLine1 || !kycForm.addressCity || !kycForm.addressState || !kycForm.addressPostalCode : !kycForm.ssn || !kycForm.dob.day || !kycForm.dob.month || !kycForm.dob.year),
13125
- className: "uf-flex-1 uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
13505
+ className: "uf-flex-1 uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
13126
13506
  style: {
13127
13507
  backgroundColor: components.button.primaryBackground,
13128
13508
  fontFamily: fonts.medium,
@@ -13181,7 +13561,17 @@ function PayWithStripeLink({
13181
13561
  className: "uf-w-full uf-flex-1 uf-overflow-y-auto uf-overflow-x-hidden"
13182
13562
  }
13183
13563
  ),
13184
- !stripePaymentUIReady && /* @__PURE__ */ jsx44("div", { className: "uf-flex uf-items-center uf-justify-center uf-py-8", children: /* @__PURE__ */ jsx44(Loader25, { className: "uf-w-8 uf-h-8 uf-animate-spin", style: { color: colors2.primary } }) }),
13564
+ !stripePaymentUIReady && /* @__PURE__ */ jsxs41("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-gap-3 uf-py-10", children: [
13565
+ /* @__PURE__ */ jsx44(Loader25, { className: "uf-w-8 uf-h-8 uf-animate-spin", style: { color: colors2.primary } }),
13566
+ /* @__PURE__ */ jsx44(
13567
+ "p",
13568
+ {
13569
+ className: "uf-text-sm uf-text-center",
13570
+ style: { color: colors2.foregroundMuted, fontFamily: fonts.regular },
13571
+ children: "Setting up secure payment\u2026"
13572
+ }
13573
+ )
13574
+ ] }),
13185
13575
  error && /* @__PURE__ */ jsx44(
13186
13576
  "div",
13187
13577
  {
@@ -13288,35 +13678,14 @@ function PayWithStripeLink({
13288
13678
  isCustomerVerified(customer) && /* @__PURE__ */ jsx44("div", { className: "uf-px-1", children: /* @__PURE__ */ jsxs41(
13289
13679
  "button",
13290
13680
  {
13291
- onClick: async () => {
13292
- if (coordinator && sdkAuthenticatedRef.current) {
13293
- setStep("add_payment");
13294
- return;
13295
- }
13296
- pendingAddPaymentRef.current = true;
13297
- if (accessTokenRef.current && email) {
13298
- setLoading(true);
13299
- try {
13300
- await withTokenRefresh(
13301
- (tok) => stripeGetCustomer(customerId, tok, publishableKey)
13302
- );
13303
- const sdk = await initialize();
13304
- if (!sdk) throw new Error("SDK failed to load");
13305
- const authResult = await stripeCreateAuthIntent(email, publishableKey);
13306
- setAuthIntentId(authResult.auth_intent_id);
13307
- setStep("auth");
13308
- } catch {
13309
- setStep("email");
13310
- } finally {
13311
- setLoading(false);
13312
- }
13313
- return;
13314
- }
13315
- setStep("email");
13681
+ onClick: () => {
13682
+ setStep("add_payment");
13316
13683
  },
13684
+ onMouseEnter: () => setAddPaymentHovered(true),
13685
+ onMouseLeave: () => setAddPaymentHovered(false),
13317
13686
  className: "uf-w-full uf-p-3 uf-flex uf-items-center uf-gap-3 uf-transition-colors",
13318
13687
  style: {
13319
- backgroundColor: components.card.backgroundColor,
13688
+ backgroundColor: addPaymentHovered ? colors2.cardHover : components.card.backgroundColor,
13320
13689
  borderRadius: components.card.borderRadius,
13321
13690
  border: `${components.card.borderWidth}px solid ${components.card.borderColor}`
13322
13691
  },
@@ -13431,7 +13800,7 @@ function PayWithStripeLink({
13431
13800
  {
13432
13801
  onClick: () => handleEmailSubmit(),
13433
13802
  disabled: loading || !email.trim(),
13434
- className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
13803
+ className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
13435
13804
  style: {
13436
13805
  backgroundColor: components.button.primaryBackground,
13437
13806
  fontFamily: fonts.medium,
@@ -13660,7 +14029,7 @@ function PayWithStripeLink({
13660
14029
  {
13661
14030
  onClick: handleRegisterSubmit,
13662
14031
  disabled: loading || phone.length !== 10,
13663
- className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
14032
+ className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
13664
14033
  style: {
13665
14034
  backgroundColor: components.button.primaryBackground,
13666
14035
  fontFamily: fonts.medium,
@@ -13683,7 +14052,7 @@ function PayWithStripeLink({
13683
14052
  const fatalError = sdkError || configError;
13684
14053
  if (fatalError) {
13685
14054
  return /* @__PURE__ */ jsxs41("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-12 uf-px-4", children: [
13686
- /* @__PURE__ */ jsx44(AlertTriangle, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
14055
+ /* @__PURE__ */ jsx44(AlertTriangle2, { className: "uf-w-10 uf-h-10 uf-mb-3", style: { color: colors2.error } }),
13687
14056
  /* @__PURE__ */ jsx44(
13688
14057
  "p",
13689
14058
  {
@@ -13794,7 +14163,9 @@ function PayWithStripeLink({
13794
14163
  );
13795
14164
  }
13796
14165
  if (step === "amount") {
13797
- const paymentSelector = oauthToken && customerId && isCustomerVerified(customer) ? /* @__PURE__ */ jsxs41(
14166
+ const canSelectPaymentMethod = !!(oauthToken && customerId && isCustomerVerified(customer));
14167
+ const needsPaymentMethodSelection = canSelectPaymentMethod && !selectedPaymentToken;
14168
+ const paymentSelector = canSelectPaymentMethod ? /* @__PURE__ */ jsxs41(
13798
14169
  "button",
13799
14170
  {
13800
14171
  onClick: () => setStep("payment"),
@@ -13868,6 +14239,35 @@ function PayWithStripeLink({
13868
14239
  style: { backgroundColor: colors2.background },
13869
14240
  children: [
13870
14241
  /* @__PURE__ */ jsxs41("div", { className: "uf-mb-6 uf-pt-2", children: [
14242
+ error && /* @__PURE__ */ jsxs41(
14243
+ "div",
14244
+ {
14245
+ className: "uf-flex uf-items-start uf-gap-2 uf-mx-1 uf-mb-4 uf-px-3 uf-py-2.5",
14246
+ role: "alert",
14247
+ style: {
14248
+ backgroundColor: `${colors2.error}14`,
14249
+ border: `1px solid ${colors2.error}40`,
14250
+ borderRadius: components.card.borderRadius
14251
+ },
14252
+ children: [
14253
+ /* @__PURE__ */ jsx44(
14254
+ AlertTriangle2,
14255
+ {
14256
+ className: "uf-w-4 uf-h-4 uf-mt-0.5 uf-shrink-0",
14257
+ style: { color: colors2.error }
14258
+ }
14259
+ ),
14260
+ /* @__PURE__ */ jsx44(
14261
+ "span",
14262
+ {
14263
+ className: "uf-text-xs uf-text-left",
14264
+ style: { color: colors2.error, fontFamily: fonts.regular },
14265
+ children: error
14266
+ }
14267
+ )
14268
+ ]
14269
+ }
14270
+ ),
13871
14271
  paymentSelector,
13872
14272
  /* @__PURE__ */ jsxs41("div", { className: "uf-text-center uf-mb-4", children: [
13873
14273
  /* @__PURE__ */ jsx44(
@@ -13904,7 +14304,7 @@ function PayWithStripeLink({
13904
14304
  children: amount && amountNum < 5 && !error ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: "Minimum amount is $5" }) : exceedsLimit && canRaiseLimit && !error ? /* @__PURE__ */ jsxs41("span", { style: { color: colors2.foregroundMuted }, children: [
13905
14305
  purchaseLimit !== null ? `Limit $${purchaseLimit.toLocaleString()} \xB7 ` : "Limit reached \xB7 ",
13906
14306
  "Verify to raise"
13907
- ] }) : exceedsLimit && !error ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: purchaseLimit !== null ? `Limit $${purchaseLimit.toLocaleString()} \xB7 Try less` : "Limit reached \xB7 Try less" }) : error ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: error }) : quoteError ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: quoteError }) : quoteLoading ? /* @__PURE__ */ jsxs41(Fragment6, { children: [
14307
+ ] }) : exceedsLimit && !error ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: purchaseLimit !== null ? `Limit $${purchaseLimit.toLocaleString()} \xB7 Try less` : "Limit reached \xB7 Try less" }) : quoteError ? /* @__PURE__ */ jsx44("span", { style: { color: colors2.error }, children: quoteError }) : quoteLoading ? /* @__PURE__ */ jsxs41(Fragment6, { children: [
13908
14308
  /* @__PURE__ */ jsx44(
13909
14309
  Loader25,
13910
14310
  {
@@ -13994,7 +14394,7 @@ function PayWithStripeLink({
13994
14394
  {
13995
14395
  onClick: startLimitStepUp,
13996
14396
  disabled: loading,
13997
- className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
14397
+ className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
13998
14398
  style: {
13999
14399
  backgroundColor: components.button.primaryBackground,
14000
14400
  fontFamily: fonts.medium,
@@ -14006,8 +14406,8 @@ function PayWithStripeLink({
14006
14406
  "button",
14007
14407
  {
14008
14408
  onClick: handleAmountContinue,
14009
- disabled: loading || !isValidAmount || !quote || isPreparingSession,
14010
- className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
14409
+ disabled: loading || !isValidAmount || !quote || isPreparingSession || needsPaymentMethodSelection,
14410
+ className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
14011
14411
  style: {
14012
14412
  backgroundColor: components.button.primaryBackground,
14013
14413
  fontFamily: fonts.medium,
@@ -14137,7 +14537,7 @@ function PayWithStripeLink({
14137
14537
  {
14138
14538
  onClick: isBuyNow ? handleCheckout : handleReviewContinue,
14139
14539
  disabled: loading || !quote || isBuyNow && !hasValidSession,
14140
- className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-colors disabled:uf-opacity-40",
14540
+ className: "uf-w-full uf-py-3.5 uf-text-white uf-font-medium uf-transition-all hover:uf-opacity-90 disabled:uf-opacity-40",
14141
14541
  style: {
14142
14542
  backgroundColor: components.button.primaryBackground,
14143
14543
  fontFamily: fonts.medium,
@@ -14238,7 +14638,7 @@ function PayWithStripeLink({
14238
14638
  backgroundColor: isSessionFulfilled ? colors2.success : isSessionFailed ? colors2.error : "#f59e0b",
14239
14639
  border: `2px solid ${colors2.background}`
14240
14640
  },
14241
- children: isSessionFulfilled ? /* @__PURE__ */ jsx44(CheckCircle22, { className: "uf-w-4 uf-h-4 uf-text-white" }) : isSessionFailed ? /* @__PURE__ */ jsx44(AlertTriangle, { className: "uf-w-3.5 uf-h-3.5 uf-text-white" }) : /* @__PURE__ */ jsx44(Loader25, { className: "uf-w-3.5 uf-h-3.5 uf-text-white uf-animate-spin" })
14641
+ children: isSessionFulfilled ? /* @__PURE__ */ jsx44(CheckCircle22, { className: "uf-w-4 uf-h-4 uf-text-white" }) : isSessionFailed ? /* @__PURE__ */ jsx44(AlertTriangle2, { className: "uf-w-3.5 uf-h-3.5 uf-text-white" }) : /* @__PURE__ */ jsx44(Loader25, { className: "uf-w-3.5 uf-h-3.5 uf-text-white uf-animate-spin" })
14242
14642
  }
14243
14643
  )
14244
14644
  ] }),
@@ -14267,7 +14667,7 @@ function PayWithStripeLink({
14267
14667
  {
14268
14668
  className: "uf-w-20 uf-h-20 uf-rounded-full uf-flex uf-items-center uf-justify-center uf-mb-6",
14269
14669
  style: { backgroundColor: `${colors2.error}20` },
14270
- children: /* @__PURE__ */ jsx44(AlertTriangle, { className: "uf-w-10 uf-h-10", style: { color: colors2.error } })
14670
+ children: /* @__PURE__ */ jsx44(AlertTriangle2, { className: "uf-w-10 uf-h-10", style: { color: colors2.error } })
14271
14671
  }
14272
14672
  ),
14273
14673
  /* @__PURE__ */ jsx44(
@@ -16406,14 +16806,36 @@ function useExchanges({
16406
16806
  return { exchanges, isLoading };
16407
16807
  }
16408
16808
 
16409
- // src/hooks/use-apple-pay-providers.ts
16809
+ // src/hooks/use-public-incident.ts
16410
16810
  import { useQuery as useQuery13 } from "@tanstack/react-query";
16811
+ import { getPublicIncident } from "@unifold/core";
16812
+ function usePublicIncident({
16813
+ publishableKey,
16814
+ enabled = true
16815
+ }) {
16816
+ const {
16817
+ data: incident,
16818
+ isLoading,
16819
+ error
16820
+ } = useQuery13({
16821
+ queryKey: ["unifold", "publicIncident", publishableKey],
16822
+ queryFn: () => getPublicIncident(publishableKey),
16823
+ enabled,
16824
+ staleTime: 1e3 * 30,
16825
+ refetchInterval: 1e3 * 30,
16826
+ refetchOnWindowFocus: true
16827
+ });
16828
+ return { incident, isLoading, error: error ?? null };
16829
+ }
16830
+
16831
+ // src/hooks/use-apple-pay-providers.ts
16832
+ import { useQuery as useQuery14 } from "@tanstack/react-query";
16411
16833
  import { getApplePayProviders } from "@unifold/core";
16412
16834
  function useApplePayProviders({
16413
16835
  publishableKey,
16414
16836
  enabled = true
16415
16837
  }) {
16416
- const { data: providers, isLoading } = useQuery13({
16838
+ const { data: providers, isLoading } = useQuery14({
16417
16839
  queryKey: ["unifold", "applePayProviders", publishableKey],
16418
16840
  queryFn: () => getApplePayProviders(publishableKey),
16419
16841
  enabled,
@@ -16432,7 +16854,8 @@ import {
16432
16854
  refreshIntegrationToken as refreshIntegrationToken2,
16433
16855
  revokeIntegrationToken,
16434
16856
  IntegrationProvider as IntegrationProvider2,
16435
- ActionType as ActionType3
16857
+ ActionType as ActionType3,
16858
+ isDepositAddressValidationError as isDepositAddressValidationError2
16436
16859
  } from "@unifold/core";
16437
16860
 
16438
16861
  // src/hooks/use-allowed-country.ts
@@ -16478,7 +16901,7 @@ function useAllowedCountry(publishableKey) {
16478
16901
  }
16479
16902
 
16480
16903
  // src/hooks/use-address-validation.ts
16481
- import { useQuery as useQuery14 } from "@tanstack/react-query";
16904
+ import { useQuery as useQuery15 } from "@tanstack/react-query";
16482
16905
  import {
16483
16906
  verifyRecipientAddress
16484
16907
  } from "@unifold/core";
@@ -16492,7 +16915,7 @@ function useAddressValidation({
16492
16915
  refetchOnMount = false
16493
16916
  }) {
16494
16917
  const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
16495
- const { data, isLoading, error } = useQuery14({
16918
+ const { data, isLoading, error } = useQuery15({
16496
16919
  queryKey: [
16497
16920
  "unifold",
16498
16921
  "addressValidation",
@@ -16523,6 +16946,7 @@ function useAddressValidation({
16523
16946
  return {
16524
16947
  isValid: null,
16525
16948
  failureCode: null,
16949
+ message: null,
16526
16950
  metadata: null,
16527
16951
  isLoading: false,
16528
16952
  error: null
@@ -16531,6 +16955,7 @@ function useAddressValidation({
16531
16955
  return {
16532
16956
  isValid: data?.valid ?? null,
16533
16957
  failureCode: data?.failure_code ?? null,
16958
+ message: data?.message ?? null,
16534
16959
  metadata: data?.metadata ?? null,
16535
16960
  isLoading,
16536
16961
  error: error ?? null
@@ -16542,7 +16967,7 @@ import { useState as useState36, useEffect as useEffect30, useMemo as useMemo11
16542
16967
  import {
16543
16968
  ChevronDown as ChevronDown5,
16544
16969
  ChevronUp as ChevronUp3,
16545
- Info,
16970
+ Info as Info2,
16546
16971
  Check as Check4,
16547
16972
  DollarSign,
16548
16973
  ShieldCheck,
@@ -17390,10 +17815,41 @@ function TokenSelectorSheet({
17390
17815
  }
17391
17816
 
17392
17817
  // src/hooks/use-default-token.ts
17393
- import { useState as useState33, useEffect as useEffect29, useRef as useRef11 } from "react";
17818
+ import { useState as useState33, useEffect as useEffect29, useRef as useRef11, useCallback as useCallback8 } from "react";
17394
17819
  var getChainKey = (chainId, chainType) => {
17395
17820
  return `${chainType}:${chainId}`;
17396
17821
  };
17822
+ function getStoredSelection(key) {
17823
+ if (typeof window === "undefined") return null;
17824
+ try {
17825
+ const raw = localStorage.getItem(key);
17826
+ if (!raw) return null;
17827
+ const parsed = JSON.parse(raw);
17828
+ if (parsed && typeof parsed.symbol === "string" && typeof parsed.chainType === "string" && typeof parsed.chainId === "string") {
17829
+ return parsed;
17830
+ }
17831
+ } catch {
17832
+ }
17833
+ return null;
17834
+ }
17835
+ function saveStoredSelection(key, symbol, chainType, chainId) {
17836
+ if (typeof window === "undefined") return;
17837
+ try {
17838
+ localStorage.setItem(key, JSON.stringify({ symbol, chainType, chainId }));
17839
+ } catch {
17840
+ }
17841
+ }
17842
+ function resolveFromStorage(tokens, stored) {
17843
+ for (const t13 of tokens) {
17844
+ if (t13.symbol !== stored.symbol) continue;
17845
+ const matchedChain = t13.chains.find(
17846
+ (c) => c.chain_type === stored.chainType && c.chain_id === stored.chainId
17847
+ );
17848
+ if (matchedChain) return { token: t13, chain: matchedChain };
17849
+ if (t13.chains.length > 0) return { token: t13, chain: t13.chains[0] };
17850
+ }
17851
+ return null;
17852
+ }
17397
17853
  function resolveToken(tokens, defaultChainType, defaultChainId, defaultTokenAddress, defaultSymbol) {
17398
17854
  if (!tokens.length) return null;
17399
17855
  let selectedToken;
@@ -17443,27 +17899,73 @@ function useDefaultToken({
17443
17899
  defaultChainType,
17444
17900
  defaultChainId,
17445
17901
  defaultTokenAddress,
17446
- defaultSymbol
17902
+ defaultSymbol,
17903
+ storageKey: storageKey2
17447
17904
  }) {
17448
- const [token, setToken] = useState33(null);
17449
- const [chain, setChain] = useState33(null);
17905
+ const [token, setTokenState] = useState33(null);
17906
+ const [chain, setChainState] = useState33(null);
17450
17907
  const [initialSelectionDone, setInitialSelectionDone] = useState33(false);
17451
17908
  const appliedDefaultsRef = useRef11("");
17909
+ const tokenRef = useRef11(null);
17910
+ const chainRef = useRef11(null);
17911
+ tokenRef.current = token;
17912
+ chainRef.current = chain;
17913
+ const setToken = useCallback8(
17914
+ (newToken) => {
17915
+ tokenRef.current = newToken;
17916
+ setTokenState(newToken);
17917
+ if (storageKey2 && chainRef.current) {
17918
+ const [chainType, chainId] = chainRef.current.split(":");
17919
+ saveStoredSelection(storageKey2, newToken, chainType, chainId);
17920
+ }
17921
+ },
17922
+ [storageKey2]
17923
+ );
17924
+ const setChain = useCallback8(
17925
+ (newChain) => {
17926
+ chainRef.current = newChain;
17927
+ setChainState(newChain);
17928
+ if (storageKey2 && tokenRef.current) {
17929
+ const [chainType, chainId] = newChain.split(":");
17930
+ saveStoredSelection(storageKey2, tokenRef.current, chainType, chainId);
17931
+ }
17932
+ },
17933
+ [storageKey2]
17934
+ );
17452
17935
  useEffect29(() => {
17453
17936
  if (!tokens.length) return;
17454
17937
  const defaultsKey = `${defaultTokenAddress ?? ""}|${defaultSymbol ?? ""}|${defaultChainType ?? ""}|${defaultChainId ?? ""}`;
17455
17938
  const defaultsChanged = appliedDefaultsRef.current !== defaultsKey;
17456
17939
  if (initialSelectionDone && !defaultsChanged) return;
17457
- const result = resolveToken(
17458
- tokens,
17459
- defaultChainType,
17460
- defaultChainId,
17461
- defaultTokenAddress,
17462
- defaultSymbol
17463
- );
17940
+ const hasExplicitDefaults = defaultTokenAddress && defaultChainType && defaultChainId || defaultSymbol && defaultChainType && defaultChainId;
17941
+ let result = null;
17942
+ if (hasExplicitDefaults) {
17943
+ result = resolveToken(
17944
+ tokens,
17945
+ defaultChainType,
17946
+ defaultChainId,
17947
+ defaultTokenAddress,
17948
+ defaultSymbol
17949
+ );
17950
+ if (result) {
17951
+ const matched = defaultTokenAddress && result.chain.token_address.toLowerCase() === defaultTokenAddress.toLowerCase() && result.chain.chain_type === defaultChainType && result.chain.chain_id === defaultChainId || defaultSymbol && result.token.symbol === defaultSymbol && result.chain.chain_type === defaultChainType && result.chain.chain_id === defaultChainId;
17952
+ if (!matched) {
17953
+ result = null;
17954
+ }
17955
+ }
17956
+ }
17957
+ if (!result && storageKey2) {
17958
+ const stored = getStoredSelection(storageKey2);
17959
+ if (stored) {
17960
+ result = resolveFromStorage(tokens, stored);
17961
+ }
17962
+ }
17963
+ if (!result) {
17964
+ result = resolveToken(tokens);
17965
+ }
17464
17966
  if (result) {
17465
- setToken(result.token.symbol);
17466
- setChain(getChainKey(result.chain.chain_id, result.chain.chain_type));
17967
+ setTokenState(result.token.symbol);
17968
+ setChainState(getChainKey(result.chain.chain_id, result.chain.chain_type));
17467
17969
  appliedDefaultsRef.current = defaultsKey;
17468
17970
  setInitialSelectionDone(true);
17469
17971
  }
@@ -17473,7 +17975,8 @@ function useDefaultToken({
17473
17975
  defaultSymbol,
17474
17976
  defaultChainType,
17475
17977
  defaultChainId,
17476
- initialSelectionDone
17978
+ initialSelectionDone,
17979
+ storageKey2
17477
17980
  ]);
17478
17981
  useEffect29(() => {
17479
17982
  if (!tokens.length || !token) return;
@@ -17484,13 +17987,14 @@ function useDefaultToken({
17484
17987
  });
17485
17988
  if (!isChainAvailable) {
17486
17989
  const firstChain = currentToken.chains[0];
17487
- setChain(getChainKey(firstChain.chain_id, firstChain.chain_type));
17990
+ setChainState(getChainKey(firstChain.chain_id, firstChain.chain_type));
17488
17991
  }
17489
17992
  }, [token, tokens, chain]);
17490
17993
  return { token, chain, setToken, setChain, initialSelectionDone };
17491
17994
  }
17492
17995
 
17493
17996
  // src/hooks/use-default-source-token.ts
17997
+ var STORAGE_KEY2 = "unifold_last_deposit_from_token";
17494
17998
  function useDefaultSourceToken({
17495
17999
  supportedTokens,
17496
18000
  defaultSourceChainType,
@@ -17503,7 +18007,8 @@ function useDefaultSourceToken({
17503
18007
  defaultChainType: defaultSourceChainType,
17504
18008
  defaultChainId: defaultSourceChainId,
17505
18009
  defaultTokenAddress: defaultSourceTokenAddress,
17506
- defaultSymbol: defaultSourceSymbol
18010
+ defaultSymbol: defaultSourceSymbol,
18011
+ storageKey: STORAGE_KEY2
17507
18012
  });
17508
18013
  }
17509
18014
 
@@ -17854,7 +18359,7 @@ import {
17854
18359
  } from "@unifold/core";
17855
18360
 
17856
18361
  // src/hooks/use-hypercore-activation.ts
17857
- import { useQuery as useQuery15 } from "@tanstack/react-query";
18362
+ import { useQuery as useQuery16 } from "@tanstack/react-query";
17858
18363
  import { checkHypercoreActivation } from "@unifold/core";
17859
18364
 
17860
18365
  // src/lib/constants.ts
@@ -17873,7 +18378,7 @@ function useHypercoreActivation(params) {
17873
18378
  const recipient = recipientAddress?.trim() ?? "";
17874
18379
  const source = sourceAddress?.trim() ?? "";
17875
18380
  const hasAddresses = !!recipient && !!source;
17876
- const { data, isLoading } = useQuery15({
18381
+ const { data, isLoading } = useQuery16({
17877
18382
  queryKey: ["unifold", "hypercoreActivation", source, recipient, publishableKey],
17878
18383
  queryFn: () => checkHypercoreActivation(
17879
18384
  {
@@ -17901,7 +18406,7 @@ function useHypercoreActivation(params) {
17901
18406
  }
17902
18407
 
17903
18408
  // src/components/shared/HypercoreActivationWarning.tsx
17904
- import { AlertTriangle as AlertTriangle2 } from "lucide-react";
18409
+ import { AlertTriangle as AlertTriangle3 } from "lucide-react";
17905
18410
  import { jsx as jsx53, jsxs as jsxs48 } from "react/jsx-runtime";
17906
18411
  function HypercoreActivationWarning({
17907
18412
  activationFee,
@@ -17919,7 +18424,7 @@ function HypercoreActivationWarning({
17919
18424
  },
17920
18425
  children: [
17921
18426
  /* @__PURE__ */ jsx53(
17922
- AlertTriangle2,
18427
+ AlertTriangle3,
17923
18428
  {
17924
18429
  className: "uf-w-4 uf-h-4 uf-flex-shrink-0 uf-mt-0.5",
17925
18430
  style: { color: colors2.warning }
@@ -18234,7 +18739,7 @@ function TransferCryptoSingleInput({
18234
18739
  ),
18235
18740
  error && !loading && /* @__PURE__ */ jsxs49("div", { className: "uf-bg-destructive/10 uf-border uf-border-destructive/20 uf-rounded-xl uf-p-3 uf-space-y-2", children: [
18236
18741
  /* @__PURE__ */ jsxs49("div", { className: "uf-flex uf-items-start uf-gap-2", children: [
18237
- /* @__PURE__ */ jsx54(Info, { className: "uf-w-4 uf-h-4 uf-text-destructive uf-flex-shrink-0 uf-mt-0.5" }),
18742
+ /* @__PURE__ */ jsx54(Info2, { className: "uf-w-4 uf-h-4 uf-text-destructive uf-flex-shrink-0 uf-mt-0.5" }),
18238
18743
  /* @__PURE__ */ jsxs49("div", { className: "uf-flex-1 uf-min-w-0", children: [
18239
18744
  /* @__PURE__ */ jsx54("div", { className: "uf-text-xs uf-font-medium uf-text-destructive uf-mb-1", children: "Failed to create deposit address" }),
18240
18745
  /* @__PURE__ */ jsx54("div", { className: "uf-text-xs uf-text-muted-foreground", children: error })
@@ -18629,7 +19134,7 @@ import { useState as useState37, useEffect as useEffect31, useMemo as useMemo12
18629
19134
  import {
18630
19135
  ChevronDown as ChevronDown7,
18631
19136
  ChevronUp as ChevronUp5,
18632
- Info as Info2,
19137
+ Info as Info3,
18633
19138
  Check as Check6,
18634
19139
  DollarSign as DollarSign2,
18635
19140
  ShieldCheck as ShieldCheck2,
@@ -19098,7 +19603,7 @@ function TransferCryptoDoubleInput({
19098
19603
  ] }),
19099
19604
  error && !loading && /* @__PURE__ */ jsxs51("div", { className: "uf-bg-destructive/10 uf-border uf-border-destructive/20 uf-rounded-xl uf-p-3 uf-space-y-2", children: [
19100
19605
  /* @__PURE__ */ jsxs51("div", { className: "uf-flex uf-items-start uf-gap-2", children: [
19101
- /* @__PURE__ */ jsx56(Info2, { className: "uf-w-4 uf-h-4 uf-text-destructive uf-flex-shrink-0 uf-mt-0.5" }),
19606
+ /* @__PURE__ */ jsx56(Info3, { className: "uf-w-4 uf-h-4 uf-text-destructive uf-flex-shrink-0 uf-mt-0.5" }),
19102
19607
  /* @__PURE__ */ jsxs51("div", { className: "uf-flex-1 uf-min-w-0", children: [
19103
19608
  /* @__PURE__ */ jsx56("div", { className: "uf-text-xs uf-font-medium uf-text-destructive uf-mb-1", children: "Failed to create deposit address" }),
19104
19609
  /* @__PURE__ */ jsx56("div", { className: "uf-text-xs uf-text-muted-foreground", children: error })
@@ -19472,7 +19977,7 @@ async function sendHypercoreEvmTransfer(params) {
19472
19977
  }
19473
19978
 
19474
19979
  // src/hooks/use-deposit-quote.ts
19475
- import { useQuery as useQuery16 } from "@tanstack/react-query";
19980
+ import { useQuery as useQuery17 } from "@tanstack/react-query";
19476
19981
  import { getDepositQuote } from "@unifold/core";
19477
19982
  function useDepositQuote(params) {
19478
19983
  const {
@@ -19499,7 +20004,7 @@ function useDepositQuote(params) {
19499
20004
  ...adjustForSlippage ? { adjust_for_slippage: true } : {},
19500
20005
  ...stablecoinParity ? { stablecoin_parity: true } : {}
19501
20006
  };
19502
- return useQuery16({
20007
+ return useQuery17({
19503
20008
  queryKey: [
19504
20009
  "unifold",
19505
20010
  "depositQuote",
@@ -19527,13 +20032,13 @@ function useDepositQuote(params) {
19527
20032
  }
19528
20033
 
19529
20034
  // src/hooks/use-external-wallets.ts
19530
- import { useQuery as useQuery17 } from "@tanstack/react-query";
20035
+ import { useQuery as useQuery18 } from "@tanstack/react-query";
19531
20036
  import { getExternalWallets } from "@unifold/core";
19532
20037
  function useExternalWallets({
19533
20038
  publishableKey,
19534
20039
  enabled = true
19535
20040
  }) {
19536
- const { data: wallets = [], isLoading } = useQuery17({
20041
+ const { data: wallets = [], isLoading } = useQuery18({
19537
20042
  queryKey: ["unifold", "external-wallets", publishableKey],
19538
20043
  queryFn: () => getExternalWallets(publishableKey).then((res) => res.data),
19539
20044
  enabled: enabled && !!publishableKey,
@@ -20644,33 +21149,11 @@ function balancesRepresentSameToken(a, b) {
20644
21149
  if (!tokenA || !tokenB) return false;
20645
21150
  return tokenA.chain_type === tokenB.chain_type && tokenA.chain_id === tokenB.chain_id && normalizeTokenAddress(tokenA.token_address) === normalizeTokenAddress(tokenB.token_address);
20646
21151
  }
20647
- function getSolanaProviders() {
20648
- if (typeof window === "undefined") return {};
20649
- const win = window;
20650
- return {
20651
- phantomSolana: win.phantom?.solana,
20652
- solflare: win.solflare,
20653
- backpack: win.backpack,
20654
- glow: win.glow,
20655
- coinbaseSolana: win.coinbaseSolana || win.coinbaseWalletExtension?.solana
20656
- };
20657
- }
20658
- function getLegacyEvmProviders() {
20659
- if (typeof window === "undefined") return {};
20660
- const win = window;
20661
- return {
20662
- ethereum: win.ethereum,
20663
- phantomEthereum: win.phantom?.ethereum,
20664
- coinbaseEthereum: win.coinbaseWalletExtension,
20665
- trustEthereum: win.trustwallet?.ethereum,
20666
- okxEthereum: win.okxwallet
20667
- };
20668
- }
20669
21152
  function detectAvailableWallets(definitions, recentWalletId, filterChainType) {
20670
- const solProviders = getSolanaProviders();
20671
- const legacyEvm = getLegacyEvmProviders();
20672
- const eip6963List = getEip6963Providers();
20673
21153
  const win = typeof window !== "undefined" ? window : null;
21154
+ const solProviders = getInjectedSolanaProviders(win);
21155
+ const legacyEvm = getLegacyEvmProviders(win);
21156
+ const eip6963List = getEip6963Providers();
20674
21157
  const hasEip6963 = (walletId) => eip6963List.some((d) => {
20675
21158
  const rdns = d.info?.rdns || "";
20676
21159
  switch (walletId) {
@@ -20922,7 +21405,7 @@ function WalletConnect({
20922
21405
  const chainType = activeDepositWallet?.chain_type ?? "ethereum";
20923
21406
  const recipientAddress = activeDepositWallet?.address ?? "";
20924
21407
  const isCheckoutMode = !!checkoutAmountUsd;
20925
- const supportedChainType = chainType === "algorand" || chainType === "xrpl" || chainType === "cardano" || chainType === "n1" ? "ethereum" : chainType;
21408
+ const supportedChainType = chainType === "algorand" || chainType === "xrpl" || chainType === "cardano" || chainType === "n1" || chainType === "tron" ? "ethereum" : chainType;
20926
21409
  const transitionTo = React35.useCallback((nextView) => {
20927
21410
  if (nextView === viewRef.current) return;
20928
21411
  setIsTransitioning(true);
@@ -20939,10 +21422,13 @@ function WalletConnect({
20939
21422
  };
20940
21423
  const openMobileWalletBrowse = async (wallet, depositAddresses) => {
20941
21424
  try {
21425
+ const cleanedAmountUsd = amountUsd?.replace(/[^0-9.]/g, "") ?? "";
21426
+ const forwardedAmountUsd = parseFloat(cleanedAmountUsd) > 0 ? cleanedAmountUsd : void 0;
20942
21427
  const res = await getWalletMobileDeepLink(
20943
21428
  wallet.id,
20944
21429
  depositAddresses,
20945
- publishableKey
21430
+ publishableKey,
21431
+ forwardedAmountUsd
20946
21432
  );
20947
21433
  if (res.deeplink) {
20948
21434
  setMobileRedirect({ walletId: wallet.id, walletName: wallet.name, deeplink: res.deeplink });
@@ -21031,7 +21517,7 @@ function WalletConnect({
21031
21517
  const eip6963Match = findProviderByWalletId(wallet.id);
21032
21518
  let provider = eip6963Match?.provider;
21033
21519
  if (!provider) {
21034
- const legacyEvm = getLegacyEvmProviders();
21520
+ const legacyEvm = getLegacyEvmProviders(win);
21035
21521
  switch (wallet.id) {
21036
21522
  case "metamask":
21037
21523
  if (legacyEvm.ethereum?.isMetaMask && !legacyEvm.ethereum?.isPhantom)
@@ -21063,16 +21549,7 @@ function WalletConnect({
21063
21549
  const accounts = await provider.request({ method: "eth_requestAccounts" });
21064
21550
  if (!accounts?.length) throw new Error("No accounts returned from wallet");
21065
21551
  setUserDisconnectedWallet(false);
21066
- const walletIdToType = {
21067
- phantom: "phantom-ethereum",
21068
- coinbase: "coinbase",
21069
- trust: "trust",
21070
- rainbow: "rainbow",
21071
- rabby: "rabby",
21072
- okx: "okx",
21073
- metamask: "metamask"
21074
- };
21075
- const walletType = walletIdToType[wallet.id] || "metamask";
21552
+ const walletType = walletIdToWalletType(wallet.id);
21076
21553
  setStoredWalletState(walletType);
21077
21554
  connectedInfo = {
21078
21555
  type: walletType,
@@ -21081,7 +21558,7 @@ function WalletConnect({
21081
21558
  icon: wallet.id
21082
21559
  };
21083
21560
  } else {
21084
- const solProviders = getSolanaProviders();
21561
+ const solProviders = getInjectedSolanaProviders(win);
21085
21562
  let provider;
21086
21563
  switch (wallet.id) {
21087
21564
  case "phantom":
@@ -21100,11 +21577,11 @@ function WalletConnect({
21100
21577
  provider = solProviders.coinbaseSolana || win?.coinbaseWalletExtension?.solana;
21101
21578
  break;
21102
21579
  case "trust":
21103
- provider = win?.trustwallet?.solana;
21580
+ provider = solProviders.trustSolana;
21104
21581
  break;
21105
21582
  }
21106
21583
  if (!provider) throw new Error(`${wallet.name} Solana wallet not found.`);
21107
- const response = await provider.connect();
21584
+ const response = await connectSolanaProviderWithRecovery(provider, wallet.id, wallet.name);
21108
21585
  setUserDisconnectedWallet(false);
21109
21586
  const walletType = wallet.id === "solflare" ? "solflare" : wallet.id === "backpack" ? "backpack" : wallet.id === "glow" ? "glow" : "phantom-solana";
21110
21587
  setStoredWalletState(walletType);
@@ -21277,7 +21754,7 @@ function WalletConnect({
21277
21754
  let cancelled = false;
21278
21755
  setIsLoading(true);
21279
21756
  setError(null);
21280
- const sct = activeDepositWallet.chain_type === "algorand" || activeDepositWallet.chain_type === "xrpl" || activeDepositWallet.chain_type === "cardano" || activeDepositWallet.chain_type === "n1" ? "ethereum" : activeDepositWallet.chain_type;
21757
+ const sct = activeDepositWallet.chain_type === "algorand" || activeDepositWallet.chain_type === "xrpl" || activeDepositWallet.chain_type === "cardano" || activeDepositWallet.chain_type === "n1" || activeDepositWallet.chain_type === "tron" ? "ethereum" : activeDepositWallet.chain_type;
21281
21758
  getAddressBalances2(activeWalletInfo.address, sct, publishableKey).then((response) => {
21282
21759
  if (cancelled) return;
21283
21760
  const nonZero = response.balances.filter((b) => b.amount !== "0");
@@ -21455,16 +21932,7 @@ function WalletConnect({
21455
21932
  return (integerPart + decimalPart.padEnd(decimals, "0").slice(0, decimals)).replace(/^0+/, "") || "0";
21456
21933
  };
21457
21934
  const resolveEvmProvider = () => {
21458
- const walletIdMap = {
21459
- "phantom-ethereum": "phantom",
21460
- coinbase: "coinbase",
21461
- trust: "trust",
21462
- okx: "okx",
21463
- rainbow: "rainbow",
21464
- rabby: "rabby",
21465
- metamask: "metamask"
21466
- };
21467
- const lookupId = walletIdMap[walletInfo.type] || walletInfo.type;
21935
+ const lookupId = walletTypeToWalletId(walletInfo.type);
21468
21936
  const eip6963Match = findProviderByWalletId(lookupId);
21469
21937
  let provider = eip6963Match?.provider;
21470
21938
  if (!provider) {
@@ -22196,6 +22664,7 @@ function DepositModal({
22196
22664
  applePayTitle = "Pay with Apple Pay",
22197
22665
  applePaySubTitle = "Instant",
22198
22666
  enableBankTransfer,
22667
+ enableIncidentBanner = false,
22199
22668
  // No default: left undefined so the backend `stripe_link.enabled` can govern
22200
22669
  // (via the `??` chain in showStripeLink) once a dashboard toggle exists.
22201
22670
  enableStripeLink,
@@ -22220,7 +22689,7 @@ function DepositModal({
22220
22689
  () => normalizePrefilledUsdAmount(prefilledAmountUsd),
22221
22690
  [prefilledAmountUsd]
22222
22691
  );
22223
- const onDepositSuccessFor = useCallback10(
22692
+ const onDepositSuccessFor = useCallback11(
22224
22693
  (method) => onDepositSuccess || onEvent ? (data) => {
22225
22694
  const payload = { ...data, method };
22226
22695
  onDepositSuccess?.(payload);
@@ -22233,7 +22702,7 @@ function DepositModal({
22233
22702
  } : void 0,
22234
22703
  [onDepositSuccess, onEvent]
22235
22704
  );
22236
- const onDepositErrorFor = useCallback10(
22705
+ const onDepositErrorFor = useCallback11(
22237
22706
  (method) => onDepositError ? (error) => onDepositError({ ...error, method }) : void 0,
22238
22707
  [onDepositError]
22239
22708
  );
@@ -22241,7 +22710,7 @@ function DepositModal({
22241
22710
  const s = initialScreen ?? "main";
22242
22711
  if (s === "tracker" && hideDepositTracker === true) return "main";
22243
22712
  if (s === "cashapp" && enableCashApp === false) return "main";
22244
- if (s === "stripe_link" && !enableStripeLink) return "main";
22713
+ if (s === "stripe_link" && enableStripeLink === false) return "main";
22245
22714
  if (s === "apple_pay" && enableApplePay === false) return "main";
22246
22715
  if (s === "card" && enableFiatOnramp === false) return "main";
22247
22716
  if (s === "pay_with_exchange") return enablePayWithExchange === false ? "main" : "exchange";
@@ -22263,7 +22732,7 @@ function DepositModal({
22263
22732
  enableStripeLink
22264
22733
  ]);
22265
22734
  const [containerEl, setContainerEl] = useState40(null);
22266
- const containerCallbackRef = useCallback10((el) => {
22735
+ const containerCallbackRef = useCallback11((el) => {
22267
22736
  setContainerEl(el);
22268
22737
  }, []);
22269
22738
  const [view, setView] = useState40(effectiveInitialScreen);
@@ -22300,6 +22769,16 @@ function DepositModal({
22300
22769
  const showApplePay = enableApplePay ?? projectConfig?.apple_pay?.enabled ?? true;
22301
22770
  const showBankTransfer = enableBankTransfer ?? projectConfig?.bank_transfer?.enabled ?? true;
22302
22771
  const showDepositTracker = hideDepositTracker ? false : projectConfig?.deposit_tracker?.enabled ?? true;
22772
+ const { incident: publicIncident } = usePublicIncident({
22773
+ publishableKey,
22774
+ enabled: open && enableIncidentBanner
22775
+ });
22776
+ const activeIncident = enableIncidentBanner && publicIncident?.enabled && (publicIncident.messages?.length ?? 0) > 0 ? {
22777
+ enabled: true,
22778
+ messages: publicIncident.messages,
22779
+ severity: publicIncident.severity,
22780
+ statusPageUrl: publicIncident.status_page_url
22781
+ } : void 0;
22303
22782
  const [integrationExchanges, setIntegrationExchanges] = useState40([]);
22304
22783
  useEffect34(() => {
22305
22784
  if (!showConnectExchange || !open) return;
@@ -22366,7 +22845,11 @@ function DepositModal({
22366
22845
  setConnectedExchange((prev) => prev ? { ...prev, iconUrl } : prev);
22367
22846
  }
22368
22847
  }, [integrationExchanges, connectedExchange]);
22369
- const { data: depositAddressResponse, isLoading: walletsLoading } = useDepositAddress({
22848
+ const {
22849
+ data: depositAddressResponse,
22850
+ isLoading: walletsLoading,
22851
+ error: walletsError
22852
+ } = useDepositAddress({
22370
22853
  userId,
22371
22854
  publishableKey,
22372
22855
  recipientAddress,
@@ -22499,6 +22982,7 @@ function DepositModal({
22499
22982
  const {
22500
22983
  isValid: isAddressValid,
22501
22984
  failureCode: addressFailureCode,
22985
+ message: addressFailureMessage,
22502
22986
  metadata: addressFailureMetadata,
22503
22987
  isLoading: isAddressValidationLoading
22504
22988
  } = useAddressValidation({
@@ -22512,17 +22996,31 @@ function DepositModal({
22512
22996
  refetchOnMount: "always"
22513
22997
  });
22514
22998
  const addressValidationMessages = i18n.transferCrypto.addressValidation;
22515
- const getAddressValidationErrorMessage = (code, metadata) => {
22999
+ const getAddressValidationErrorMessage = (message, code, metadata) => {
23000
+ if (message && message.trim().length > 0) return message;
22516
23001
  if (!code) return addressValidationMessages.defaultError;
22517
23002
  const errors = addressValidationMessages.errors;
22518
23003
  const template = errors[code] ?? addressValidationMessages.defaultError;
22519
23004
  return interpolate(template, metadata);
22520
23005
  };
23006
+ const walletsRecipientError = isDepositAddressValidationError2(walletsError) ? walletsError.message : null;
23007
+ const isRecipientAddressInvalid = isAddressValid === false || walletsRecipientError !== null;
23008
+ const recipientInvalidMessage = getAddressValidationErrorMessage(
23009
+ addressFailureMessage ?? walletsRecipientError,
23010
+ addressFailureCode,
23011
+ addressFailureMetadata
23012
+ );
22521
23013
  const openingScreen = effectiveInitialScreen;
22522
23014
  const sessionOpenedFromMenu = openingScreen === "main";
22523
23015
  const standaloneNeedsDepositPrereq = openingScreen !== "main" && (view === "transfer" || view === "card");
22524
23016
  let depositPrerequisiteBody;
22525
- if (isCountryLoading || isAddressValidationLoading || tokensLoading || walletsLoading || !projectConfig || // Bank-transfer row visibility depends on the country-gated providers
23017
+ if (isRecipientAddressInvalid) {
23018
+ depositPrerequisiteBody = /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
23019
+ /* @__PURE__ */ jsx63("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx63(AlertTriangle4, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
23020
+ /* @__PURE__ */ jsx63("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: addressValidationMessages.unableToReceiveFunds }),
23021
+ /* @__PURE__ */ jsx63("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: recipientInvalidMessage })
23022
+ ] });
23023
+ } else if (isCountryLoading || isAddressValidationLoading || tokensLoading || walletsLoading || !projectConfig || // Bank-transfer row visibility depends on the country-gated providers
22526
23024
  // fetch — block the menu on it so the row never flashes in or out.
22527
23025
  showBankTransfer && bankTransferProvidersLoading || // Same for Apple Pay: row visibility depends on the geo/platform-gated
22528
23026
  // providers fetch — block the menu so the row doesn't pop in or out.
@@ -22534,7 +23032,7 @@ function DepositModal({
22534
23032
  ] });
22535
23033
  } else if (countryError) {
22536
23034
  depositPrerequisiteBody = /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
22537
- /* @__PURE__ */ jsx63("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx63(AlertTriangle3, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
23035
+ /* @__PURE__ */ jsx63("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx63(AlertTriangle4, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
22538
23036
  /* @__PURE__ */ jsx63("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: "Unable to Verify Location" }),
22539
23037
  /* @__PURE__ */ jsx63("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: "We couldn't verify your location. Please check your connection and try again." })
22540
23038
  ] });
@@ -22544,12 +23042,6 @@ function DepositModal({
22544
23042
  /* @__PURE__ */ jsx63("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: "No Tokens Available" }),
22545
23043
  /* @__PURE__ */ jsx63("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: "There are no supported tokens available from your current location." })
22546
23044
  ] });
22547
- } else if (isAddressValid === false) {
22548
- depositPrerequisiteBody = /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
22549
- /* @__PURE__ */ jsx63("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx63(AlertTriangle3, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
22550
- /* @__PURE__ */ jsx63("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: addressValidationMessages.unableToReceiveFunds }),
22551
- /* @__PURE__ */ jsx63("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: getAddressValidationErrorMessage(addressFailureCode, addressFailureMetadata) })
22552
- ] });
22553
23045
  } else {
22554
23046
  depositPrerequisiteBody = null;
22555
23047
  }
@@ -23043,6 +23535,7 @@ function DepositModal({
23043
23535
  title: modalTitle || "Deposit",
23044
23536
  showClose: !hideOverlay,
23045
23537
  onClose: handleClose,
23538
+ incident: activeIncident,
23046
23539
  showBalance: showBalanceHeader,
23047
23540
  balanceAddress: recipientAddress,
23048
23541
  balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
@@ -23062,6 +23555,7 @@ function DepositModal({
23062
23555
  showBack: showBackTransfer,
23063
23556
  onBack: handleBack,
23064
23557
  onClose: handleClose,
23558
+ incident: activeIncident,
23065
23559
  showBalance: showBalanceHeader,
23066
23560
  balanceAddress: recipientAddress,
23067
23561
  balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" || destinationChainType === "n1" ? destinationChainType : void 0,
@@ -23122,7 +23616,8 @@ function DepositModal({
23122
23616
  title: selectedExecution ? "Deposit Details" : depositTrackerTitle,
23123
23617
  showBack: showBackTracker,
23124
23618
  onBack: handleBack,
23125
- onClose: handleClose
23619
+ onClose: handleClose,
23620
+ incident: activeIncident
23126
23621
  }
23127
23622
  ),
23128
23623
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -23154,6 +23649,7 @@ function DepositModal({
23154
23649
  showBack: showBackCard,
23155
23650
  onBack: handleBack,
23156
23651
  onClose: handleClose,
23652
+ incident: activeIncident,
23157
23653
  badge: cardView === "quotes" ? { count: quotesCount } : void 0,
23158
23654
  showBalance: showBalanceHeader,
23159
23655
  balanceAddress: recipientAddress,
@@ -23203,7 +23699,8 @@ function DepositModal({
23203
23699
  title: payWithExchangeTitle,
23204
23700
  showBack: exchangeView === "pending" || sessionOpenedFromMenu,
23205
23701
  onBack: handleBack,
23206
- onClose: handleClose
23702
+ onClose: handleClose,
23703
+ incident: activeIncident
23207
23704
  }
23208
23705
  ),
23209
23706
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -23317,7 +23814,8 @@ function DepositModal({
23317
23814
  title: t8.bankTransfer.title,
23318
23815
  showBack: bankTransferView !== "providers" || sessionOpenedFromMenu,
23319
23816
  onBack: handleBack,
23320
- onClose: handleClose
23817
+ onClose: handleClose,
23818
+ incident: activeIncident
23321
23819
  }
23322
23820
  ),
23323
23821
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -23351,26 +23849,24 @@ function DepositModal({
23351
23849
  title: "Deposit with Link",
23352
23850
  showBack: stripeLinkStep !== "checkout" && stripeLinkStep !== "success",
23353
23851
  onBack: handleBack,
23852
+ incident: activeIncident,
23354
23853
  showClose: stripeLinkStep !== "checkout" && stripeLinkStep !== "auth",
23355
23854
  onClose: handleClose
23356
23855
  }
23357
23856
  ),
23358
23857
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23359
23858
  isLoadingIp ? (
23360
- // Hold the geo decision until IP resolves so we don't mount
23361
- // PayWithStripeLink (which kicks off config/OAuth work) for a
23362
- // deep-link user who turns out to be outside the US.
23859
+ // Wait for location so the first config fetch is region-aware.
23363
23860
  /* @__PURE__ */ jsx63(SkeletonButton, { variant: "with-icons" })
23364
23861
  ) : !showStripeLink ? (
23365
- // Stripe Link's crypto on-ramp is US-only. On a direct open
23366
- // (initialScreen="stripe_link") the row isn't in a menu to
23367
- // fall back to, so show a geo-restriction screen rather than
23368
- // the Link UI.
23862
+ // Direct opens (initialScreen="stripe_link") have no menu row
23863
+ // to fall back to, so render an unavailable state when backend
23864
+ // config resolves Stripe Link disabled/hidden.
23369
23865
  /* @__PURE__ */ jsx63(
23370
23866
  GeoRestrictionScreen,
23371
23867
  {
23372
23868
  methodName: t8.stripeLink.title,
23373
- message: "Pay with Link is only available in the US."
23869
+ message: t8.stripeLink.unavailableInRegionMessage
23374
23870
  }
23375
23871
  )
23376
23872
  ) : /* @__PURE__ */ jsx63(
@@ -23403,7 +23899,8 @@ function DepositModal({
23403
23899
  title: cashAppView !== "amount" && cashAppAmount ? `Pay $${cashAppAmount} via Cash App` : "Pay with Cash App",
23404
23900
  showBack: cashAppView !== "amount" || sessionOpenedFromMenu,
23405
23901
  onBack: handleBack,
23406
- onClose: handleClose
23902
+ onClose: handleClose,
23903
+ incident: activeIncident
23407
23904
  }
23408
23905
  ),
23409
23906
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -23439,7 +23936,8 @@ function DepositModal({
23439
23936
  const handled = applePayHandleRef.current?.requestBack() ?? false;
23440
23937
  if (!handled) handleBack();
23441
23938
  },
23442
- onClose: handleClose
23939
+ onClose: handleClose,
23940
+ incident: activeIncident
23443
23941
  }
23444
23942
  ),
23445
23943
  /* @__PURE__ */ jsxs57("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -23481,16 +23979,16 @@ function DepositModal({
23481
23979
  }
23482
23980
 
23483
23981
  // src/components/checkout/CheckoutModal.tsx
23484
- import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect3, useCallback as useCallback11, useRef as useRef14, useMemo as useMemo15 } from "react";
23485
- import { AlertTriangle as AlertTriangle4, ChevronRight as ChevronRight19 } from "lucide-react";
23982
+ import { useState as useState41, useEffect as useEffect35, useLayoutEffect as useLayoutEffect3, useCallback as useCallback12, useRef as useRef14, useMemo as useMemo15 } from "react";
23983
+ import { AlertTriangle as AlertTriangle5, ChevronRight as ChevronRight19 } from "lucide-react";
23486
23984
 
23487
23985
  // src/hooks/use-payment-intent.ts
23488
- import { useQuery as useQuery18 } from "@tanstack/react-query";
23986
+ import { useQuery as useQuery19 } from "@tanstack/react-query";
23489
23987
  import { retrievePaymentIntent } from "@unifold/core";
23490
23988
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "expired", "refunded", "canceled"]);
23491
23989
  function usePaymentIntent(params) {
23492
23990
  const { clientSecret, publishableKey, enabled = true, pollingInterval = 3e3 } = params;
23493
- return useQuery18({
23991
+ return useQuery19({
23494
23992
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
23495
23993
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
23496
23994
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -23569,6 +24067,7 @@ function CheckoutModal({
23569
24067
  modalTitle,
23570
24068
  enableTransferCrypto,
23571
24069
  enableConnectWallet,
24070
+ enableIncidentBanner = false,
23572
24071
  defaultSourceChainType,
23573
24072
  defaultSourceChainId,
23574
24073
  defaultSourceTokenAddress,
@@ -23584,7 +24083,7 @@ function CheckoutModal({
23584
24083
  const [browserWalletInfo, setBrowserWalletInfo] = useState41(null);
23585
24084
  const [browserWalletChainType, setBrowserWalletChainType] = useState41(() => getStoredWalletState()?.chainType);
23586
24085
  const lastCheckoutMethodRef = useRef14(void 0);
23587
- const emitCheckoutSuccess = useCallback11(
24086
+ const emitCheckoutSuccess = useCallback12(
23588
24087
  (data, method) => {
23589
24088
  const isSucceeded = data.status === "succeeded";
23590
24089
  const richIntent = isSucceeded && data.paymentIntent ? mapToCheckoutPaymentIntent(data.paymentIntent) : void 0;
@@ -23640,6 +24139,16 @@ function CheckoutModal({
23640
24139
  });
23641
24140
  const showTransferCrypto = enableTransferCrypto ?? projectConfig?.transfer_crypto?.enabled ?? true;
23642
24141
  const showConnectWallet = enableConnectWallet ?? projectConfig?.connect_wallet?.enabled ?? true;
24142
+ const { incident: publicIncident } = usePublicIncident({
24143
+ publishableKey,
24144
+ enabled: open && enableIncidentBanner
24145
+ });
24146
+ const activeIncident = enableIncidentBanner && publicIncident?.enabled && (publicIncident.messages?.length ?? 0) > 0 ? {
24147
+ enabled: true,
24148
+ messages: publicIncident.messages,
24149
+ severity: publicIncident.severity,
24150
+ statusPageUrl: publicIncident.status_page_url
24151
+ } : void 0;
23643
24152
  useEffect35(() => {
23644
24153
  if (view === "transfer" && !showTransferCrypto) {
23645
24154
  setView("main");
@@ -23735,7 +24244,7 @@ function CheckoutModal({
23735
24244
  sourceAmountUsd: minUsd.toFixed(2)
23736
24245
  };
23737
24246
  }, [sourceQuote, selectedSource]);
23738
- const handleBrowserWalletClick = useCallback11(
24247
+ const handleBrowserWalletClick = useCallback12(
23739
24248
  (walletInfo) => {
23740
24249
  const walletChainType = walletInfo.type === "phantom-solana" || walletInfo.type === "solflare" || walletInfo.type === "backpack" || walletInfo.type === "glow" ? "solana" : "ethereum";
23741
24250
  setStoredWalletState(walletInfo.type);
@@ -23758,19 +24267,19 @@ function CheckoutModal({
23758
24267
  },
23759
24268
  [wallets, onCheckoutError]
23760
24269
  );
23761
- const handleWalletConnectClick = useCallback11(() => {
24270
+ const handleWalletConnectClick = useCallback12(() => {
23762
24271
  setBrowserWalletInfo(null);
23763
24272
  lastCheckoutMethodRef.current = "wallet_connect";
23764
24273
  setView("wallet_connect");
23765
24274
  }, []);
23766
- const handleWalletDisconnect = useCallback11(() => {
24275
+ const handleWalletDisconnect = useCallback12(() => {
23767
24276
  setUserDisconnectedWallet(true);
23768
24277
  clearStoredWalletState();
23769
24278
  setBrowserWalletChainType(void 0);
23770
24279
  setBrowserWalletInfo(null);
23771
24280
  setView("main");
23772
24281
  }, []);
23773
- const handleClose = useCallback11(() => {
24282
+ const handleClose = useCallback12(() => {
23774
24283
  onOpenChange(false);
23775
24284
  if (resetViewTimeoutRef.current) {
23776
24285
  clearTimeout(resetViewTimeoutRef.current);
@@ -23800,7 +24309,7 @@ function CheckoutModal({
23800
24309
  },
23801
24310
  []
23802
24311
  );
23803
- const handleBack = useCallback11(() => {
24312
+ const handleBack = useCallback12(() => {
23804
24313
  setView("main");
23805
24314
  }, []);
23806
24315
  const poweredByFooter = /* @__PURE__ */ jsx64("div", { className: "uf-pt-3", children: /* @__PURE__ */ jsx64(
@@ -23941,7 +24450,15 @@ function CheckoutModal({
23941
24450
  {
23942
24451
  className: view === "wallet_connect" ? "uf-flex uf-min-h-0 uf-flex-col" : void 0,
23943
24452
  children: view === "main" ? /* @__PURE__ */ jsxs58(Fragment15, { children: [
23944
- /* @__PURE__ */ jsx64(DepositHeader, { title: modalTitle || "Checkout", showClose: true, onClose: handleClose }),
24453
+ /* @__PURE__ */ jsx64(
24454
+ DepositHeader,
24455
+ {
24456
+ title: modalTitle || "Checkout",
24457
+ showClose: true,
24458
+ onClose: handleClose,
24459
+ incident: activeIncident
24460
+ }
24461
+ ),
23945
24462
  /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
23946
24463
  piLoading ? /* @__PURE__ */ jsxs58("div", { className: "uf-space-y-3", children: [
23947
24464
  /* @__PURE__ */ jsx64(
@@ -23978,7 +24495,7 @@ function CheckoutModal({
23978
24495
  /* @__PURE__ */ jsx64(SkeletonButton2, {}),
23979
24496
  /* @__PURE__ */ jsx64(SkeletonButton2, {})
23980
24497
  ] }) : piError ? /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
23981
- /* @__PURE__ */ jsx64("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx64(AlertTriangle4, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
24498
+ /* @__PURE__ */ jsx64("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx64(AlertTriangle5, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
23982
24499
  /* @__PURE__ */ jsx64(
23983
24500
  "h3",
23984
24501
  {
@@ -24039,7 +24556,8 @@ function CheckoutModal({
24039
24556
  title: modalTitle || "Checkout",
24040
24557
  showBack: true,
24041
24558
  onBack: handleBack,
24042
- onClose: handleClose
24559
+ onClose: handleClose,
24560
+ incident: activeIncident
24043
24561
  }
24044
24562
  ),
24045
24563
  /* @__PURE__ */ jsxs58("div", { className: "uf-flex uf-flex-col uf-gap-1.5", children: [
@@ -24200,16 +24718,16 @@ function CheckoutModal({
24200
24718
  }
24201
24719
 
24202
24720
  // src/components/withdrawals/WithdrawModal.tsx
24203
- import { useState as useState45, useEffect as useEffect39, useLayoutEffect as useLayoutEffect4, useCallback as useCallback13, useRef as useRef16 } from "react";
24204
- import { AlertTriangle as AlertTriangle6, ChevronRight as ChevronRight21, Clock as Clock6 } from "lucide-react";
24721
+ import { useState as useState45, useEffect as useEffect39, useLayoutEffect as useLayoutEffect4, useCallback as useCallback14, useRef as useRef16 } from "react";
24722
+ import { AlertTriangle as AlertTriangle7, ChevronRight as ChevronRight21, Clock as Clock6 } from "lucide-react";
24205
24723
 
24206
24724
  // src/hooks/use-supported-destination-tokens.ts
24207
- import { useQuery as useQuery19 } from "@tanstack/react-query";
24725
+ import { useQuery as useQuery20 } from "@tanstack/react-query";
24208
24726
  import {
24209
24727
  getSupportedDestinationTokens
24210
24728
  } from "@unifold/core";
24211
24729
  function useSupportedDestinationTokens(publishableKey, enabled = true) {
24212
- return useQuery19({
24730
+ return useQuery20({
24213
24731
  queryKey: ["unifold", "supportedDestinationTokens", publishableKey],
24214
24732
  queryFn: () => getSupportedDestinationTokens(publishableKey),
24215
24733
  staleTime: 1e3 * 60 * 5,
@@ -24221,6 +24739,7 @@ function useSupportedDestinationTokens(publishableKey, enabled = true) {
24221
24739
  }
24222
24740
 
24223
24741
  // src/hooks/use-default-destination-token.ts
24742
+ var STORAGE_KEY3 = "unifold_last_withdraw_to_token";
24224
24743
  function useDefaultDestinationToken({
24225
24744
  destinationTokens,
24226
24745
  defaultDestinationChainType,
@@ -24233,12 +24752,13 @@ function useDefaultDestinationToken({
24233
24752
  defaultChainType: defaultDestinationChainType,
24234
24753
  defaultChainId: defaultDestinationChainId,
24235
24754
  defaultTokenAddress: defaultDestinationTokenAddress,
24236
- defaultSymbol: defaultDestinationSymbol
24755
+ defaultSymbol: defaultDestinationSymbol,
24756
+ storageKey: STORAGE_KEY3
24237
24757
  });
24238
24758
  }
24239
24759
 
24240
24760
  // src/hooks/use-source-token-validation.ts
24241
- import { useQuery as useQuery20 } from "@tanstack/react-query";
24761
+ import { useQuery as useQuery21 } from "@tanstack/react-query";
24242
24762
  import { getSupportedDepositTokens as getSupportedDepositTokens3 } from "@unifold/core";
24243
24763
  function useSourceTokenValidation(params) {
24244
24764
  const {
@@ -24250,7 +24770,7 @@ function useSourceTokenValidation(params) {
24250
24770
  enabled = true
24251
24771
  } = params;
24252
24772
  const hasParams = !!sourceChainType && !!sourceChainId && !!sourceTokenAddress;
24253
- return useQuery20({
24773
+ return useQuery21({
24254
24774
  queryKey: [
24255
24775
  "unifold",
24256
24776
  "sourceTokenValidation",
@@ -24298,12 +24818,12 @@ function useSourceTokenValidation(params) {
24298
24818
  }
24299
24819
 
24300
24820
  // src/hooks/use-address-balance.ts
24301
- import { useQuery as useQuery21 } from "@tanstack/react-query";
24821
+ import { useQuery as useQuery22 } from "@tanstack/react-query";
24302
24822
  import { getAddressBalance as getAddressBalance2 } from "@unifold/core";
24303
24823
  function useAddressBalance(params) {
24304
24824
  const { address, chainType, chainId, tokenAddress, publishableKey, enabled = true } = params;
24305
24825
  const hasParams = !!address && !!chainType && !!chainId && !!tokenAddress;
24306
- return useQuery21({
24826
+ return useQuery22({
24307
24827
  queryKey: [
24308
24828
  "unifold",
24309
24829
  "addressBalance",
@@ -24359,11 +24879,11 @@ function useAddressBalance(params) {
24359
24879
  }
24360
24880
 
24361
24881
  // src/hooks/use-executions.ts
24362
- import { useQuery as useQuery22 } from "@tanstack/react-query";
24882
+ import { useQuery as useQuery23 } from "@tanstack/react-query";
24363
24883
  import { queryExecutions as queryExecutions4, ActionType as ActionType4 } from "@unifold/core";
24364
24884
  function useExecutions(userId, publishableKey, options) {
24365
24885
  const actionType = options?.actionType ?? ActionType4.Deposit;
24366
- return useQuery22({
24886
+ return useQuery23({
24367
24887
  queryKey: ["unifold", "executions", actionType, userId, publishableKey],
24368
24888
  queryFn: () => queryExecutions4(userId, publishableKey, actionType),
24369
24889
  enabled: (options?.enabled ?? true) && !!userId,
@@ -24694,9 +25214,9 @@ function WithdrawDoubleInput({
24694
25214
  }
24695
25215
 
24696
25216
  // src/components/withdrawals/WithdrawForm.tsx
24697
- import { useState as useState43, useCallback as useCallback12, useMemo as useMemo17, useEffect as useEffect37 } from "react";
25217
+ import { useState as useState43, useCallback as useCallback13, useMemo as useMemo17, useEffect as useEffect37 } from "react";
24698
25218
  import {
24699
- AlertTriangle as AlertTriangle5,
25219
+ AlertTriangle as AlertTriangle6,
24700
25220
  ArrowUpDown,
24701
25221
  ChevronDown as ChevronDown9,
24702
25222
  ChevronUp as ChevronUp7,
@@ -24712,7 +25232,7 @@ import {
24712
25232
  } from "@unifold/core";
24713
25233
 
24714
25234
  // src/hooks/use-verify-recipient-address.ts
24715
- import { useQuery as useQuery23 } from "@tanstack/react-query";
25235
+ import { useQuery as useQuery24 } from "@tanstack/react-query";
24716
25236
  import { verifyRecipientAddress as verifyRecipientAddress2 } from "@unifold/core";
24717
25237
  function useVerifyRecipientAddress(params) {
24718
25238
  const {
@@ -24725,7 +25245,7 @@ function useVerifyRecipientAddress(params) {
24725
25245
  } = params;
24726
25246
  const trimmedAddress = recipientAddress?.trim() || "";
24727
25247
  const hasAllParams = !!chainType && !!chainId && !!tokenAddress && trimmedAddress.length > 0;
24728
- return useQuery23({
25248
+ return useQuery24({
24729
25249
  queryKey: [
24730
25250
  "unifold",
24731
25251
  "verifyRecipientAddress",
@@ -24976,7 +25496,7 @@ import { useMemo as useMemo16 } from "react";
24976
25496
  import { ActionType as ActionType6 } from "@unifold/core";
24977
25497
 
24978
25498
  // src/hooks/use-get-deposit-address.ts
24979
- import { useQuery as useQuery24 } from "@tanstack/react-query";
25499
+ import { useQuery as useQuery25 } from "@tanstack/react-query";
24980
25500
  import { getDepositAddress } from "@unifold/core";
24981
25501
  function useGetDepositAddress(params) {
24982
25502
  const {
@@ -24990,7 +25510,7 @@ function useGetDepositAddress(params) {
24990
25510
  enabled = true
24991
25511
  } = params;
24992
25512
  const canFire = !!userId && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
24993
- return useQuery24({
25513
+ return useQuery25({
24994
25514
  queryKey: [
24995
25515
  "unifold",
24996
25516
  "getDepositAddress",
@@ -25186,6 +25706,9 @@ function WithdrawForm({
25186
25706
  if (isDebouncing || isVerifyingAddress) return null;
25187
25707
  if (verifyError) return t10.invalidAddress;
25188
25708
  if (addressVerification && !addressVerification.valid) {
25709
+ if (addressVerification.message && addressVerification.message.trim().length > 0) {
25710
+ return addressVerification.message;
25711
+ }
25189
25712
  if (addressVerification.failure_code === "account_not_found")
25190
25713
  return `Account not found on ${selectedChain?.chain_name}`;
25191
25714
  if (addressVerification.failure_code === "not_opted_in")
@@ -25283,7 +25806,7 @@ function WithdrawForm({
25283
25806
  tokenSymbol,
25284
25807
  isStablecoin
25285
25808
  ]);
25286
- const handleSwitchUnit = useCallback12(() => {
25809
+ const handleSwitchUnit = useCallback13(() => {
25287
25810
  if (isMaxed && balanceData) {
25288
25811
  if (inputUnit === "crypto") {
25289
25812
  setAmount((Math.round(balanceUsdNum * 100) / 100).toFixed(2));
@@ -25310,7 +25833,7 @@ function WithdrawForm({
25310
25833
  setInputUnit("crypto");
25311
25834
  }
25312
25835
  }, [amount, inputUnit, exchangeRate, sourceDecimals, isMaxed, balanceData, balanceUsdNum]);
25313
- const handleMaxClick = useCallback12(() => {
25836
+ const handleMaxClick = useCallback13(() => {
25314
25837
  if (inputUnit === "crypto") {
25315
25838
  if (balanceCrypto <= 0) return;
25316
25839
  setAmount(balanceData?.balanceHuman ?? "0");
@@ -25324,7 +25847,7 @@ function WithdrawForm({
25324
25847
  const isBelowMinimum = minimumWithdrawAmountUsd !== null && fiatAmountFromInput > 0 && Math.round(fiatAmountFromInput * 100) / 100 < minimumWithdrawAmountUsd;
25325
25848
  const isOverBalance = inputUnit === "crypto" ? cryptoAmountFromInput > 0 && balanceCrypto > 0 && cryptoAmountFromInput > balanceCrypto : fiatAmountFromInput > 0 && balanceUsdNum > 0 && Math.round(fiatAmountFromInput * 100) / 100 > Math.round(balanceUsdNum * 100) / 100;
25326
25849
  const isFormValid = trimmedAddress.length > 0 && amount.trim().length > 0 && cryptoAmountFromInput > 0 && isAddressValid && !isBelowMinimum && !isOverBalance && !isBalanceBelowMinimum && !!balanceData;
25327
- const handleWithdraw = useCallback12(async () => {
25850
+ const handleWithdraw = useCallback13(async () => {
25328
25851
  if (!selectedToken || !selectedChain) return;
25329
25852
  if (!isFormValid) return;
25330
25853
  setIsSubmitting(true);
@@ -25521,7 +26044,7 @@ function WithdrawForm({
25521
26044
  )
25522
26045
  ] }),
25523
26046
  addressError && /* @__PURE__ */ jsxs60("div", { className: "uf-flex uf-items-center uf-gap-1.5 uf-mt-1.5", children: [
25524
- /* @__PURE__ */ jsx66(AlertTriangle5, { className: "uf-w-3 uf-h-3", style: { color: colors2.error } }),
26047
+ /* @__PURE__ */ jsx66(AlertTriangle6, { className: "uf-w-3 uf-h-3", style: { color: colors2.error } }),
25525
26048
  /* @__PURE__ */ jsx66("span", { className: "uf-text-xs", style: { color: colors2.error, fontFamily: fonts.regular }, children: addressError })
25526
26049
  ] })
25527
26050
  ] }),
@@ -26110,7 +26633,7 @@ function WithdrawModal({
26110
26633
  theme = "dark",
26111
26634
  hideOverlay = false
26112
26635
  }) {
26113
- const onWithdrawSuccessFor = useCallback13(
26636
+ const onWithdrawSuccessFor = useCallback14(
26114
26637
  (data) => {
26115
26638
  onWithdrawSuccess?.(data);
26116
26639
  if (data.execution) {
@@ -26121,7 +26644,7 @@ function WithdrawModal({
26121
26644
  );
26122
26645
  const { colors: colors2, fonts, components } = useTheme();
26123
26646
  const [containerEl, setContainerEl] = useState45(null);
26124
- const containerCallbackRef = useCallback13((el) => {
26647
+ const containerCallbackRef = useCallback14((el) => {
26125
26648
  setContainerEl(el);
26126
26649
  }, []);
26127
26650
  const [resolvedTheme, setResolvedTheme] = useState45(
@@ -26194,7 +26717,7 @@ function WithdrawModal({
26194
26717
  refetchInterval: view === "tracker" || view === "detail" ? 5e3 : 15e3
26195
26718
  });
26196
26719
  const allWithdrawals = allWithdrawalsData?.data ?? [];
26197
- const handleDepositWalletCreation = useCallback13(
26720
+ const handleDepositWalletCreation = useCallback14(
26198
26721
  async (params) => {
26199
26722
  const { data: wallets } = await createDepositAddress2(
26200
26723
  {
@@ -26217,12 +26740,12 @@ function WithdrawModal({
26217
26740
  },
26218
26741
  [externalUserId, publishableKey, sourceChainType]
26219
26742
  );
26220
- const handleWithdrawSubmitted = useCallback13((txInfo) => {
26743
+ const handleWithdrawSubmitted = useCallback14((txInfo) => {
26221
26744
  setSubmittedTxInfo(txInfo);
26222
26745
  setView("confirming");
26223
26746
  }, []);
26224
26747
  const resetViewTimeoutRef = useRef16(null);
26225
- const handleClose = useCallback13(() => {
26748
+ const handleClose = useCallback14(() => {
26226
26749
  onOpenChange(false);
26227
26750
  if (resetViewTimeoutRef.current) clearTimeout(resetViewTimeoutRef.current);
26228
26751
  resetViewTimeoutRef.current = setTimeout(() => {
@@ -26250,13 +26773,13 @@ function WithdrawModal({
26250
26773
  },
26251
26774
  []
26252
26775
  );
26253
- const handleTokenSymbolChange = useCallback13(
26776
+ const handleTokenSymbolChange = useCallback14(
26254
26777
  (symbol) => {
26255
26778
  setSelectedTokenSymbol(symbol);
26256
26779
  },
26257
26780
  [setSelectedTokenSymbol]
26258
26781
  );
26259
- const handleChainKeyChange = useCallback13(
26782
+ const handleChainKeyChange = useCallback14(
26260
26783
  (chainKey) => {
26261
26784
  setSelectedChainKey(chainKey);
26262
26785
  },
@@ -26363,7 +26886,7 @@ function WithdrawModal({
26363
26886
  },
26364
26887
  i
26365
26888
  )) }) : isSourceSupported === false ? /* @__PURE__ */ jsxs63("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
26366
- /* @__PURE__ */ jsx69("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx69(AlertTriangle6, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
26889
+ /* @__PURE__ */ jsx69("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ jsx69(AlertTriangle7, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
26367
26890
  /* @__PURE__ */ jsx69(
26368
26891
  "h3",
26369
26892
  {
@@ -26732,6 +27255,7 @@ export {
26732
27255
  useDepositPolling,
26733
27256
  useDepositQuote,
26734
27257
  usePaymentIntent,
27258
+ usePublicIncident,
26735
27259
  useSourceTokenValidation,
26736
27260
  useSupportedDepositTokens,
26737
27261
  useSupportedDestinationTokens,