@rash2x/bridge-widget 0.1.13 → 0.1.16

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.
@@ -2,22 +2,22 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
5
- import require$$0, { useEffect, useState, useCallback, useMemo, createContext, useContext, memo, forwardRef, useRef } from "react";
5
+ import require$$0, { useEffect, useState, useCallback, useMemo, createContext, useContext, memo, forwardRef } from "react";
6
6
  import { initReactI18next, useTranslation, I18nextProvider } from "react-i18next";
7
7
  import i18n, { t } from "i18next";
8
8
  import { create as create$1 } from "zustand";
9
9
  import { Button } from "@/components/ui/button";
10
10
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
11
+ import { Badge } from "@/components/ui/badge";
11
12
  import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
12
13
  import { cn } from "@/lib/utils";
13
- import { Input } from "@/components/ui/input";
14
+ import { Skeleton } from "@/components/ui/skeleton";
14
15
  import { useAccount, useConnect, useDisconnect, useWalletClient, usePublicClient } from "wagmi";
15
16
  import { useWallet } from "@tronweb3/tronwallet-adapter-react-hooks";
16
17
  import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
17
18
  import { useQuery, useQueryClient } from "@tanstack/react-query";
18
19
  import { Address, beginCell as beginCell$1, storeMessage, loadMessage, Cell } from "@ton/core";
19
- import { VariableSizeList } from "react-window";
20
- import { Skeleton } from "@/components/ui/skeleton";
20
+ import { Input } from "@/components/ui/input";
21
21
  import { CardHeader, CardTitle, CardAction, Card, CardContent, CardFooter } from "@/components/ui/card";
22
22
  import { Switch } from "@/components/ui/switch";
23
23
  import { X } from "lucide-react";
@@ -30,7 +30,7 @@ import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
30
30
  import { TronLinkAdapterName } from "@tronweb3/tronwallet-adapters";
31
31
  const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
32
32
  const wallets$1 = { "addTonWallet": "Add TON wallet", "addEvmWallet": "Add EVM wallet", "connectTonWallet": "Connect TON wallet", "connectEvmWallet": "Connect EVM wallet", "initializingMetamask": "Initializing MetaMask SDK...", "initializingTronlink": "Initializing TronLink...", "failedToConnectTon": "Failed to connect to TON wallet", "failedToDisconnect": "Failed to disconnect", "metamaskConnectionError": "MetaMask connection error", "failedToConnectMetamask": "Failed to connect to MetaMask", "failedToDisconnectMetamask": "Failed to disconnect from MetaMask", "selectWallet": "Select Wallet", "tonWallets": "TON", "evmWallets": "EVM", "tronWallets": "TRON", "tonKeeper": "TonKeeper", "metaMask": "WalletConnect", "tronLink": "TronLink", "addTronWallet": "Add Tron wallet", "comingSoon": "Coming Soon", "connected": "CONNECTED", "disconnect": "Disconnect", "chooseWallet": "Choose wallet", "oneWalletPerEnv": "You can only connect one wallet per environment.", "connect": "Connect", "connectTronWallet": "Connect Tron wallet", "connectWallet": "Connect wallet" };
33
- const bridge$1 = { "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will Change Source Chain", "noBalancesFound": "No balances found.", "noResults": "No results", "sendToAnotherAddress": "Send to another address", "youWillReceive": "You will receive", "tonAddressPlaceholder": "TON address", "evmAddressPlaceholder": "0x… EVM address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
33
+ const bridge$1 = { "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will Change Source Chain", "noBalancesFound": "No balances found.", "noResults": "No results", "sendToAnotherAddress": "Send to another address", "youWillReceive": "You will receive", "anotherAddressPlaceholder": "Address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
34
34
  const transaction$1 = { "enterAmount": "Enter amount", "transfer": "Transfer", "getQuote": "Get quote", "failed": "Transaction Failed", "confirm": "Confirm transaction", "signTransaction": "Sign in transaction in wallet", "quoting": "Quoting...", "inProgress": "Processing...", "checkingBalance": "Checking balance...", "insufficientBalance": "Insufficient balance", "amountTooSmall": "Min {{min}}", "amountTooLarge": "Max {{max}}", "successTitle": "Success", "bridged": "Bridged", "transferTitle": "Transfer", "hash": "Hash", "route": "Route", "estTime": "Est. Time", "slippage": "Slippage", "minimumReceived": "Minimum received", "totalFee": "Total Fee", "noRouteFound": "No route found", "notEnoughGas": "Not enough gas", "noRouteFoundForSettings": "No route found for current settings.", "tryAdjustSettings": "Try disabling Gas on Destination, or adjust amount/networks.", "quoteError": "Quote error" };
35
35
  const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
36
36
  const settings$1 = { "title": "Settings", "gasOnDestination": "Gas on destination", "slippageTolerance": "Slippage tolerance", "routePriority": "Route Priority", "highSlippageWarning": "High slippage warning", "gasPresets": { "auto": "Auto", "none": "None", "medium": "Medium", "max": "Max" }, "routePresets": { "fastest": "Fastest", "cheapest": "Cheapest", "recommended": "Recommended" } };
@@ -44,7 +44,7 @@ const en = {
44
44
  };
45
45
  const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
46
46
  const wallets = { "addTonWallet": "Добавить TON кошелёк", "addEvmWallet": "Добавить EVM кошелёк", "connectTonWallet": "Подключить TON кошелёк", "connectEvmWallet": "Подключить EVM кошелёк", "initializingMetamask": "Инициализация MetaMask SDK...", "initializingTronlink": "Инициализация TronLink...", "failedToConnectTon": "Не удалось подключиться к TON кошельку", "failedToDisconnect": "Не удалось отключиться", "metamaskConnectionError": "Ошибка подключения MetaMask", "failedToConnectMetamask": "Не удалось подключиться к MetaMask", "failedToDisconnectMetamask": "Не удалось отключиться от MetaMask", "selectWallet": "Выберите кошелёк", "tonWallets": "TON", "evmWallets": "EVM", "tronWallets": "TRON", "tonKeeper": "TonKeeper", "metaMask": "WalletConnect", "tronLink": "TronLink", "addTronWallet": "Добавить Tron кошелёк", "comingSoon": "Скоро", "connected": "ПОДКЛЮЧЕНО", "disconnect": "Отключить", "chooseWallet": "Выберите кошелёк", "oneWalletPerEnv": "Можно подключить только один кошелёк на окружение.", "connect": "Подключить", "connectTronWallet": "Подключить Tron кошелёк", "connectWallet": "Подключить кошелёк" };
47
- const bridge = { "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "tonAddressPlaceholder": "TON адрес", "evmAddressPlaceholder": "0x… EVM адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
47
+ const bridge = { "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
48
48
  const transaction = { "enterAmount": "Введите сумму", "transfer": "Перевести", "getQuote": "Получить котировку", "quoting": "Расчет котировки...", "failed": "Ошибка транзакции", "confirm": "Подтвердите транзакцию", "signTransaction": "Подпишите транзакцию в кошельке", "inProgress": "Выполнение...", "checkingBalance": "Проверка баланса...", "insufficientBalance": "Недостаточно средств", "amountTooSmall": "Минимум {{min}}", "amountTooLarge": "Максимум {{max}}", "successTitle": "Успех", "bridged": "Переведено", "transferTitle": "Перевод", "hash": "Хэш", "route": "Маршрут", "estTime": "Время", "slippage": "Проскальзывание", "minimumReceived": "Минимум к получению", "totalFee": "Общая комиссия", "noRouteFound": "Маршрут не найден", "notEnoughGas": "Недостаточно газа", "noRouteFoundForSettings": "Маршрут не найден для текущих настроек.", "tryAdjustSettings": "Попробуйте отключить Gas on Destination или измените сумму/сети.", "quoteError": "Ошибка котировки" };
49
49
  const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
50
50
  const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
@@ -903,21 +903,21 @@ const SettingModal = ({ isOpen, onClose }) => {
903
903
  toChain?.chainKey,
904
904
  dstNativeToken?.decimals || 18
905
905
  );
906
- const activeBtn = "bg-settings-active hover:bg-settings-active/80 text-settings-active-foreground";
907
- const notActiveBtn = "bg-settings-button hover:bg-settings-button/80 text-settings-button-foreground";
906
+ const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
907
+ const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
908
908
  return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
909
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("settings.title", { defaultValue: "Settings" }) }) }),
909
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("settings.title") }) }),
910
910
  /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
911
911
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
912
912
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
913
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
914
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.gasOnDestination") }),
913
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
914
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t2("settings.gasOnDestination") }),
915
915
  /* @__PURE__ */ jsx(Tip, { text: t2("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
916
916
  ] }),
917
- /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-3.5", children: formatUsd(gasUsdValue) })
917
+ /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
918
918
  ] }),
919
919
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
920
- /* @__PURE__ */ jsxs("div", { className: "flex items-center w-1/3 gap-1.5 shrink-0", children: [
920
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center w-1/3 gap-2 shrink-0", children: [
921
921
  /* @__PURE__ */ jsx(
922
922
  TokenSymbol,
923
923
  {
@@ -930,13 +930,12 @@ const SettingModal = ({ isOpen, onClose }) => {
930
930
  gasDisplayAmount < 1e-3 ? 6 : 3
931
931
  ) }) })
932
932
  ] }),
933
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: gasPresets.map((g) => /* @__PURE__ */ jsx(
934
- Button,
933
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: gasPresets.map((g) => /* @__PURE__ */ jsx(
934
+ Badge,
935
935
  {
936
- type: "button",
937
936
  onClick: () => setGasPreset(g),
938
937
  className: cn(
939
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
938
+ "cursor-pointer",
940
939
  gasPreset === g ? activeBtn : notActiveBtn
941
940
  ),
942
941
  children: t2(`settings.gasPresets.${g}`)
@@ -948,26 +947,25 @@ const SettingModal = ({ isOpen, onClose }) => {
948
947
  /* @__PURE__ */ jsx("hr", {}),
949
948
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
950
949
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
951
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
950
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
952
951
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.slippageTolerance") }),
953
952
  /* @__PURE__ */ jsx(Tip, { text: t2("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
954
953
  ] }),
955
- slippageBps >= 500 && /* @__PURE__ */ jsx("p", { className: "text-orange-500 text-xs font-medium", children: t2("settings.highSlippageWarning", {
954
+ slippageBps >= 500 && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs font-medium", children: t2("settings.highSlippageWarning", {
956
955
  defaultValue: "High slippage warning"
957
956
  }) })
958
957
  ] }),
959
958
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-6", children: [
960
959
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("p", { className: "text-lg text-foreground leading-4.5 font-semibold h-4.5", children: slippagePercent }) }),
961
960
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsx(
962
- Button,
961
+ Badge,
963
962
  {
964
- type: "button",
965
963
  onClick: () => {
966
964
  const bps = parseFloat(p.replace("%", "")) * 100;
967
965
  setSlippageBps(bps);
968
966
  },
969
967
  className: cn(
970
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
968
+ "cursor-pointer",
971
969
  activeSlippagePreset === p ? activeBtn : notActiveBtn
972
970
  ),
973
971
  children: p
@@ -978,17 +976,16 @@ const SettingModal = ({ isOpen, onClose }) => {
978
976
  ] }),
979
977
  /* @__PURE__ */ jsx("hr", {}),
980
978
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
981
- /* @__PURE__ */ jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
979
+ /* @__PURE__ */ jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
982
980
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.routePriority") }),
983
981
  /* @__PURE__ */ jsx(Tip, { text: t2("settings.routePriority"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
984
982
  ] }) }),
985
983
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsx(
986
- Button,
984
+ Badge,
987
985
  {
988
- type: "button",
989
986
  onClick: () => setRoutePriority(r),
990
987
  className: cn(
991
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
988
+ "cursor-pointer",
992
989
  routePriority === r ? activeBtn : notActiveBtn
993
990
  ),
994
991
  children: t2(`settings.routePresets.${r}`)
@@ -1056,168 +1053,31 @@ function useChainStrategies() {
1056
1053
  }
1057
1054
  return context;
1058
1055
  }
1059
- function toLD(human, decimals) {
1060
- const [i = "0", f = ""] = human.replace(",", ".").split(".");
1061
- const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1062
- return BigInt(i + frac).toString();
1063
- }
1064
- function fromLD(ld, decimals) {
1065
- const bi = BigInt(ld || "0");
1066
- if (decimals === 0) return Number(bi);
1067
- const base = BigInt(10) ** BigInt(decimals);
1068
- const int = bi / base;
1069
- const frac = Number(bi % base) / Number(base);
1070
- return Number(int) + frac;
1071
- }
1072
- function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1073
- if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1074
- const byChain = assetMatrix[assetSymbol.toUpperCase()];
1075
- return byChain?.[chainKey];
1076
- }
1077
- const DEFAULT_SLIPPAGE_BPS = 50;
1078
- const lower = (s) => (s ?? "").toLowerCase();
1079
- const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1080
- function tonNorm(addr) {
1081
- if (!addr) return null;
1056
+ const isEvmAddress = (addr) => {
1057
+ return /^0x[0-9a-fA-F]{40}$/.test(addr ?? "");
1058
+ };
1059
+ const isTronAddress = (addr) => {
1060
+ return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(addr ?? "");
1061
+ };
1062
+ const isTonAddress = (addr) => {
1063
+ if (!addr) return false;
1082
1064
  try {
1083
1065
  if (addr.includes(":")) {
1084
- return Address.parseRaw(addr).toString({
1085
- bounceable: false,
1086
- urlSafe: true
1087
- });
1066
+ Address.parseRaw(addr);
1067
+ return true;
1088
1068
  }
1089
- return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1069
+ Address.parse(addr);
1070
+ return true;
1090
1071
  } catch {
1091
- return null;
1072
+ return false;
1092
1073
  }
1093
- }
1094
- const isEvmAddress = (a) => /^0x[0-9a-fA-F]{40}$/.test(a ?? "");
1095
- const isTronAddress = (a) => /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(a ?? "");
1096
- const isZeroAddr = (a) => (
1097
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1098
- !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1099
- );
1074
+ };
1100
1075
  function isAddressValidForChain(chainKey, addr) {
1101
1076
  if (!chainKey || !addr) return false;
1102
- if (chainKey === "ton") return tonNorm(addr) !== null;
1077
+ if (chainKey === "ton") return isTonAddress(addr);
1103
1078
  if (chainKey === "tron") return isTronAddress(addr);
1104
1079
  return isEvmAddress(addr);
1105
1080
  }
1106
- function addrForApi(chainKey, addr) {
1107
- if (chainKey === "ton") return tonNorm(addr);
1108
- return addr;
1109
- }
1110
- function isNativeAddrEqual(chain, tokenAddr) {
1111
- if (!chain) return false;
1112
- if (!tokenAddr) return false;
1113
- if (isZeroAddr(tokenAddr)) return true;
1114
- const nativeAddr = chain.nativeCurrency?.address;
1115
- if (!nativeAddr) return false;
1116
- if (chain.chainKey === "ton") {
1117
- const a = tonNorm(tokenAddr);
1118
- const b = tonNorm(nativeAddr);
1119
- return !!a && !!b && a === b;
1120
- }
1121
- return lower(nativeAddr) === lower(tokenAddr);
1122
- }
1123
- function findNativeMeta(tokens, chain) {
1124
- if (!chain) return { decimals: 18, priceUsd: void 0 };
1125
- const sym = normSym(chain.nativeCurrency?.symbol);
1126
- if (!sym) {
1127
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1128
- }
1129
- const sameChain = tokens?.find(
1130
- (t2) => t2.chainKey === chain.chainKey && normSym(t2.symbol) === sym
1131
- ) ?? void 0;
1132
- if (sameChain)
1133
- return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1134
- const anyChain = tokens?.find((t2) => normSym(t2.symbol) === sym);
1135
- if (anyChain)
1136
- return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1137
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1138
- }
1139
- function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1140
- if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1141
- const chain = chains?.find((c) => c.chainKey === chainKey);
1142
- const hit = tokens?.find((t2) => {
1143
- if (t2.chainKey !== chainKey) return false;
1144
- if (chainKey === "ton") {
1145
- const a = tonNorm(t2.address);
1146
- const b = tonNorm(tokenAddr);
1147
- return !!a && !!b && a === b;
1148
- }
1149
- return lower(t2.address) === lower(tokenAddr);
1150
- }) ?? void 0;
1151
- if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1152
- if (isNativeAddrEqual(chain, tokenAddr)) {
1153
- return findNativeMeta(tokens, chain);
1154
- }
1155
- return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1156
- }
1157
- function computeFeesUsdFromArray(fees, tokens, chains) {
1158
- if (!fees?.length) return { totalUsd: 0 };
1159
- let total = 0;
1160
- let protocolFeeUsd = 0;
1161
- let messageFeeUsd = 0;
1162
- const byType = /* @__PURE__ */ new Map();
1163
- for (const f of fees) {
1164
- let usd = Number(f.usd ?? 0);
1165
- if (!usd) {
1166
- const { decimals, priceUsd } = lookupTokenMeta(
1167
- tokens,
1168
- chains,
1169
- f.chainKey,
1170
- f.token
1171
- );
1172
- const human = fromLD(String(f.amount ?? "0"), decimals);
1173
- usd = (priceUsd ?? 0) * human;
1174
- }
1175
- total += usd;
1176
- const type = (f.type ?? "other").toLowerCase();
1177
- byType.set(type, (byType.get(type) ?? 0) + usd);
1178
- if (type === "protocol" || type === "service") {
1179
- protocolFeeUsd += usd;
1180
- } else if (type === "message" || type === "gas" || type === "network") {
1181
- messageFeeUsd += usd;
1182
- }
1183
- }
1184
- const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1185
- const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1186
- return {
1187
- totalUsd: total,
1188
- protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1189
- messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1190
- serviceUsd,
1191
- blockchainUsd
1192
- };
1193
- }
1194
- function dollarsFromNativeFees(feeNative, feeLzToken, chain, tokens) {
1195
- let total = 0;
1196
- if (feeNative && chain) {
1197
- const { decimals, priceUsd } = findNativeMeta(tokens, chain);
1198
- const human = fromLD(String(feeNative), decimals);
1199
- total += (priceUsd ?? 0) * human;
1200
- }
1201
- if (feeLzToken) {
1202
- const lz = tokens?.find((t2) => normSym(t2.symbol) === "LZ");
1203
- if (lz?.price?.usd && lz.decimals != null) {
1204
- const human = fromLD(String(feeLzToken), lz.decimals);
1205
- total += lz.price.usd * human;
1206
- }
1207
- }
1208
- return total;
1209
- }
1210
- function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1211
- if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1212
- let acc = 0n;
1213
- for (const f of fees) {
1214
- if (f.chainKey !== dstChainKey) continue;
1215
- const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1216
- if (!same) continue;
1217
- acc += BigInt(f.amount ?? "0");
1218
- }
1219
- return acc.toString();
1220
- }
1221
1081
  function useBalances(chainKey, address, priorityTokenSymbol) {
1222
1082
  const { chainRegistry } = useChainStrategies();
1223
1083
  const { assetMatrix } = useTokensStore();
@@ -1279,10 +1139,92 @@ function useBalances(chainKey, address, priorityTokenSymbol) {
1279
1139
  query
1280
1140
  };
1281
1141
  }
1282
- function useTokenSelectData(items) {
1142
+ const SearchInput = ({
1143
+ placeholder,
1144
+ value,
1145
+ onChange,
1146
+ className,
1147
+ containerClassName
1148
+ }) => {
1149
+ const [isFocused, setIsFocused] = useState(false);
1150
+ return /* @__PURE__ */ jsxs(
1151
+ "div",
1152
+ {
1153
+ className: cn(
1154
+ "flex items-center gap-3 px-5 py-4 bg-input rounded-md h-13 transition-all duration-200",
1155
+ isFocused ? "border border-ring" : "border border-transparent",
1156
+ containerClassName
1157
+ ),
1158
+ children: [
1159
+ /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 text-input-icon" }),
1160
+ /* @__PURE__ */ jsx(
1161
+ Input,
1162
+ {
1163
+ placeholder,
1164
+ className: cn(
1165
+ "w-full outline-none bg-transparent border-none ring-0 leading-0 p-0 h-6 text-base text-input-text placeholder:text-input-placeholder bg-none dark:bg-transparent",
1166
+ className
1167
+ ),
1168
+ value,
1169
+ onChange: (e) => onChange(e.target.value),
1170
+ onFocus: () => setIsFocused(true),
1171
+ onBlur: () => setIsFocused(false)
1172
+ }
1173
+ )
1174
+ ]
1175
+ }
1176
+ );
1177
+ };
1178
+ const TokenRow = ({
1179
+ symbol,
1180
+ name,
1181
+ isSelected,
1182
+ hasAnyWallet,
1183
+ balance,
1184
+ usdValue,
1185
+ isBalanceLoading,
1186
+ onPick
1187
+ }) => {
1188
+ return /* @__PURE__ */ jsxs(
1189
+ Button,
1190
+ {
1191
+ onClick: onPick,
1192
+ className: `w-full bg-transparent flex items-center justify-between gap-3 px-5 hover:bg-accent hover:scale-100 ${isSelected ? "border border-ring" : ""}`,
1193
+ children: [
1194
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1195
+ /* @__PURE__ */ jsx(
1196
+ TokenSymbol,
1197
+ {
1198
+ symbol,
1199
+ className: "size-8 rounded-full",
1200
+ alt: symbol
1201
+ }
1202
+ ),
1203
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1204
+ /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
1205
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
1206
+ ] })
1207
+ ] }),
1208
+ /* @__PURE__ */ jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
1209
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-16 rounded-md" }),
1210
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
1211
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1212
+ /* @__PURE__ */ jsx("div", { className: "font-extrabold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
1213
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
1214
+ ] }) })
1215
+ ]
1216
+ }
1217
+ );
1218
+ };
1219
+ const TokenSelectModal = ({
1220
+ isOpen,
1221
+ onClose,
1222
+ items,
1223
+ onChangeAsset
1224
+ }) => {
1225
+ const { t: t2 } = useBridgeTranslation();
1283
1226
  const [query, setQuery] = useState("");
1284
1227
  const [tab, setTab] = useState("my");
1285
- const [isFocused, setIsFocused] = useState(false);
1286
1228
  const { srcAddress } = useAddresses();
1287
1229
  const { fromChain, setFromChain, chains } = useChainsStore();
1288
1230
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
@@ -1367,159 +1309,7 @@ function useTokenSelectData(items) {
1367
1309
  const resetState = useCallback(() => {
1368
1310
  setQuery("");
1369
1311
  setTab("my");
1370
- setIsFocused(false);
1371
1312
  }, []);
1372
- return {
1373
- query,
1374
- setQuery,
1375
- tab,
1376
- setTab,
1377
- isFocused,
1378
- setIsFocused,
1379
- effectiveTab,
1380
- resetState,
1381
- fromChain,
1382
- setFromChain,
1383
- selectedAssetSymbol,
1384
- balancesQuery,
1385
- groupedTokens,
1386
- myTokens,
1387
- getBalance,
1388
- getTokenUsdValue,
1389
- findFirstAvailableChain,
1390
- hasAnyWallet
1391
- };
1392
- }
1393
- const TokenRow = ({
1394
- symbol,
1395
- name,
1396
- isSelected,
1397
- hasAnyWallet,
1398
- balance,
1399
- usdValue,
1400
- isBalanceLoading,
1401
- onPick
1402
- }) => {
1403
- return /* @__PURE__ */ jsxs(
1404
- Button,
1405
- {
1406
- onClick: onPick,
1407
- className: `w-full h-12.5 rounded-md cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-modal-item-hover transition-[300] ${isSelected ? "border border-ring" : ""}`,
1408
- children: [
1409
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1410
- /* @__PURE__ */ jsx(
1411
- TokenSymbol,
1412
- {
1413
- symbol,
1414
- className: "size-7.5 max-w-7.5 rounded-full",
1415
- alt: symbol
1416
- }
1417
- ),
1418
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1419
- /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
1420
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
1421
- ] })
1422
- ] }),
1423
- /* @__PURE__ */ jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
1424
- /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-16 rounded-md" }),
1425
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
1426
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1427
- /* @__PURE__ */ jsx("div", { className: "font-extrabold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
1428
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
1429
- ] }) })
1430
- ]
1431
- }
1432
- );
1433
- };
1434
- const HEADER_H = 30;
1435
- const ROW_H = 50;
1436
- const HEADER_TOP_GAP = 30;
1437
- const VirtualizedTokenList = ({
1438
- nodes,
1439
- balancesLoading,
1440
- hasAnyWallet,
1441
- selectedAssetSymbol,
1442
- getBalance,
1443
- getTokenUsdValue,
1444
- onPick
1445
- }) => {
1446
- const Row = ({ index, style }) => {
1447
- const node = nodes[index];
1448
- if (node.kind === "header") {
1449
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1450
- return /* @__PURE__ */ jsx(
1451
- "div",
1452
- {
1453
- style: { ...style, paddingTop: gap },
1454
- className: "px-5 flex leading-4 text-base py-1.5 font-semibold text-muted-foreground uppercase",
1455
- children: /* @__PURE__ */ jsx("p", { children: node.title })
1456
- }
1457
- );
1458
- }
1459
- const bal = getBalance(node.token.symbol);
1460
- const usd = getTokenUsdValue(node.token.symbol, node.token.price?.usd);
1461
- const isSelected = selectedAssetSymbol?.toUpperCase() === node.token.symbol.toUpperCase();
1462
- return /* @__PURE__ */ jsx(
1463
- TokenRow,
1464
- {
1465
- symbol: node.token.symbol,
1466
- name: node.token.name,
1467
- isSelected: !!isSelected,
1468
- hasAnyWallet,
1469
- balance: bal,
1470
- usdValue: usd,
1471
- isBalanceLoading: balancesLoading,
1472
- onPick: () => onPick(node.token.symbol, node.willChangeSrc)
1473
- }
1474
- );
1475
- };
1476
- const getItemSize = (index) => {
1477
- const node = nodes[index];
1478
- if (node.kind === "header") {
1479
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1480
- return HEADER_H + gap;
1481
- }
1482
- return ROW_H;
1483
- };
1484
- return /* @__PURE__ */ jsx(
1485
- VariableSizeList,
1486
- {
1487
- height: 480,
1488
- width: "100%",
1489
- itemCount: nodes.length,
1490
- itemSize: getItemSize,
1491
- overscanCount: 8,
1492
- style: { willChange: "transform", paddingBottom: "40px" },
1493
- children: Row
1494
- }
1495
- );
1496
- };
1497
- const TokenSelectModal = ({
1498
- isOpen,
1499
- onClose,
1500
- items,
1501
- onChangeAsset
1502
- }) => {
1503
- const { t: t2 } = useBridgeTranslation();
1504
- const {
1505
- query,
1506
- setQuery,
1507
- tab,
1508
- setTab,
1509
- isFocused,
1510
- setIsFocused,
1511
- effectiveTab,
1512
- resetState,
1513
- setFromChain,
1514
- selectedAssetSymbol,
1515
- balancesQuery,
1516
- groupedTokens,
1517
- myTokens,
1518
- getBalance,
1519
- getTokenUsdValue,
1520
- findFirstAvailableChain,
1521
- hasAnyWallet
1522
- } = useTokenSelectData(items);
1523
1313
  const handleClose = useCallback(() => {
1524
1314
  resetState();
1525
1315
  onClose();
@@ -1536,68 +1326,41 @@ const TokenSelectModal = ({
1536
1326
  onChangeAsset(sym);
1537
1327
  handleClose();
1538
1328
  };
1539
- const virtualNodes = useMemo(() => {
1540
- const out = [];
1329
+ const tokensToRender = useMemo(() => {
1541
1330
  if (effectiveTab === "my") {
1542
- for (const token of myTokens) {
1543
- out.push({
1544
- kind: "token",
1545
- key: `my:${token.symbol}`,
1546
- token,
1547
- willChangeSrc: false
1548
- });
1549
- }
1550
- return out;
1551
- }
1552
- const mainTokens = [
1553
- ...groupedTokens.withBalance,
1554
- ...groupedTokens.onSrcChain
1555
- ];
1556
- for (const token of mainTokens) {
1557
- out.push({
1558
- kind: "token",
1559
- key: `main:${token.symbol}`,
1331
+ return myTokens.map((token) => ({
1560
1332
  token,
1561
1333
  willChangeSrc: false
1562
- });
1563
- }
1564
- if (groupedTokens.willChangeSrcChain.length > 0) {
1565
- out.push({
1566
- kind: "header",
1567
- key: "hdr:willChange",
1568
- title: t2("bridge.willChangeSourceChain")
1569
- });
1570
- for (const token of groupedTokens.willChangeSrcChain) {
1571
- out.push({
1572
- kind: "token",
1573
- key: `will:${token.symbol}`,
1574
- token,
1575
- willChangeSrc: true
1576
- });
1577
- }
1334
+ }));
1578
1335
  }
1579
- return out;
1580
- }, [effectiveTab, myTokens, groupedTokens, t2]);
1581
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!max-h-[80dvh] overflow-auto", children: [
1336
+ const mainTokens = [
1337
+ ...groupedTokens.withBalance.map((token) => ({
1338
+ token,
1339
+ willChangeSrc: false
1340
+ })),
1341
+ ...groupedTokens.onSrcChain.map((token) => ({
1342
+ token,
1343
+ willChangeSrc: false
1344
+ }))
1345
+ ];
1346
+ return mainTokens;
1347
+ }, [effectiveTab, myTokens, groupedTokens]);
1348
+ const willChangeSrcTokens = useMemo(
1349
+ () => groupedTokens.willChangeSrcChain.map((token) => ({
1350
+ token,
1351
+ willChangeSrc: true
1352
+ })),
1353
+ [groupedTokens.willChangeSrcChain]
1354
+ );
1355
+ const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
1356
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!h-[90dvh] overflow-hidden flex flex-col", children: [
1582
1357
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("bridge.selectToken") }) }),
1583
- /* @__PURE__ */ jsxs(
1584
- "div",
1358
+ /* @__PURE__ */ jsx(
1359
+ SearchInput,
1585
1360
  {
1586
- className: `flex items-center gap-2.5 px-5 py-3.5 bg-input rounded-12 h-12.5 transition-all duration-200 ${isFocused ? "border border-ring" : "border border-transparent"}`,
1587
- children: [
1588
- /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 text-input-icon" }),
1589
- /* @__PURE__ */ jsx(
1590
- Input,
1591
- {
1592
- placeholder: t2("bridge.searchToken"),
1593
- className: "w-full outline-none leading-0 p-0 h-6 text-base text-input-text placeholder:text-input-placeholder bg-none dark:bg-transparent",
1594
- value: query,
1595
- onChange: (e) => setQuery(e.target.value),
1596
- onFocus: () => setIsFocused(true),
1597
- onBlur: () => setIsFocused(false)
1598
- }
1599
- )
1600
- ]
1361
+ placeholder: t2("bridge.searchToken"),
1362
+ value: query,
1363
+ onChange: setQuery
1601
1364
  }
1602
1365
  ),
1603
1366
  hasAnyWallet() && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
@@ -1620,20 +1383,52 @@ const TokenSelectModal = ({
1620
1383
  }
1621
1384
  )
1622
1385
  ] }),
1623
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden -mx-5", children: virtualNodes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-3.5", children: t2("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1386
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto -mx-5", children: hasNoResults ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t2("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1624
1387
  effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase px-5 py-2", children: t2("bridge.noBalancesFound") }),
1625
- /* @__PURE__ */ jsx(
1626
- VirtualizedTokenList,
1627
- {
1628
- nodes: virtualNodes,
1629
- balancesLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1630
- hasAnyWallet: hasAnyWallet(),
1631
- selectedAssetSymbol,
1632
- getBalance,
1633
- getTokenUsdValue,
1634
- onPick
1635
- }
1636
- )
1388
+ tokensToRender.map(({ token, willChangeSrc }) => {
1389
+ const bal = getBalance(token.symbol);
1390
+ const usd = getTokenUsdValue(token.symbol, token.price?.usd);
1391
+ const isSelected = selectedAssetSymbol?.toUpperCase() === token.symbol.toUpperCase();
1392
+ return /* @__PURE__ */ jsx(
1393
+ TokenRow,
1394
+ {
1395
+ symbol: token.symbol,
1396
+ name: token.name,
1397
+ isSelected: !!isSelected,
1398
+ hasAnyWallet: hasAnyWallet(),
1399
+ balance: bal,
1400
+ usdValue: usd,
1401
+ isBalanceLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1402
+ onPick: () => onPick(token.symbol, willChangeSrc)
1403
+ },
1404
+ `${effectiveTab}:${token.symbol}`
1405
+ );
1406
+ }),
1407
+ effectiveTab === "all" && willChangeSrcTokens.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
1408
+ /* @__PURE__ */ jsx("div", { className: "px-5 flex leading-4 text-base py-2 font-semibold text-muted-foreground uppercase mt-8", children: /* @__PURE__ */ jsx("p", { children: t2("bridge.willChangeSourceChain") }) }),
1409
+ willChangeSrcTokens.map(({ token, willChangeSrc }) => {
1410
+ const bal = getBalance(token.symbol);
1411
+ const usd = getTokenUsdValue(
1412
+ token.symbol,
1413
+ token.price?.usd
1414
+ );
1415
+ const isSelected = selectedAssetSymbol?.toUpperCase() === token.symbol.toUpperCase();
1416
+ return /* @__PURE__ */ jsx(
1417
+ TokenRow,
1418
+ {
1419
+ symbol: token.symbol,
1420
+ name: token.name,
1421
+ isSelected: !!isSelected,
1422
+ hasAnyWallet: hasAnyWallet(),
1423
+ balance: bal,
1424
+ usdValue: usd,
1425
+ isBalanceLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1426
+ onPick: () => onPick(token.symbol, willChangeSrc)
1427
+ },
1428
+ `will:${token.symbol}`
1429
+ );
1430
+ })
1431
+ ] })
1637
1432
  ] }) })
1638
1433
  ] }) });
1639
1434
  };
@@ -1747,7 +1542,7 @@ const SelectTokenButton = ({
1747
1542
  type: "button",
1748
1543
  "aria-label": label,
1749
1544
  children: [
1750
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-center", children: [
1545
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
1751
1546
  /* @__PURE__ */ jsx(
1752
1547
  TokenSymbol,
1753
1548
  {
@@ -1756,7 +1551,7 @@ const SelectTokenButton = ({
1756
1551
  alt: label
1757
1552
  }
1758
1553
  ),
1759
- /* @__PURE__ */ jsx("span", { className: "text-secondary-foreground text-sm leading-4.5 font-semibold ", children: label })
1554
+ /* @__PURE__ */ jsx("span", { className: "text-secondary-foreground text-sm font-semibold", children: label })
1760
1555
  ] }),
1761
1556
  /* @__PURE__ */ jsx(ArrowDownIcon, { className: "size-4 text-secondary-foreground" })
1762
1557
  ]
@@ -1779,12 +1574,12 @@ const FormHeaderComponent = () => {
1779
1574
  const sum = selectedAssetSymbol.toUpperCase();
1780
1575
  return assets.find((a) => a.symbol.toUpperCase() === sum) ?? assets[0];
1781
1576
  }, [assets, selectedAssetSymbol]);
1782
- return /* @__PURE__ */ jsxs(CardHeader, { className: "gap-y-0 flex justify-between items-center", children: [
1783
- /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2.5", children: [
1784
- /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t2("bridge.selectToken") }),
1577
+ return /* @__PURE__ */ jsxs(CardHeader, { className: "gap-y-0 flex flex-row justify-between items-center", children: [
1578
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-3", children: [
1579
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-4 text-muted-foreground", children: t2("bridge.selectToken") }),
1785
1580
  /* @__PURE__ */ jsx(SelectTokenButton, { token: current, onClick: onOpen })
1786
1581
  ] }),
1787
- /* @__PURE__ */ jsxs(CardAction, { className: "flex items-center gap-2.5", children: [
1582
+ /* @__PURE__ */ jsxs(CardAction, { className: "flex items-center gap-3", children: [
1788
1583
  /* @__PURE__ */ jsx(RefreshButton, {}),
1789
1584
  /* @__PURE__ */ jsx(Button, { onClick: onOpenSettings, size: "sm", variant: "secondary", children: /* @__PURE__ */ jsx(BoltIcon, { stroke: "currentColor" }) })
1790
1585
  ] }),
@@ -1800,16 +1595,23 @@ const FormHeaderComponent = () => {
1800
1595
  }
1801
1596
  }
1802
1597
  ),
1803
- /* @__PURE__ */ jsx(
1804
- SettingModal,
1805
- {
1806
- isOpen: isOpenSettings,
1807
- onClose: onCloseSettings
1808
- }
1809
- )
1598
+ /* @__PURE__ */ jsx(SettingModal, { isOpen: isOpenSettings, onClose: onCloseSettings })
1810
1599
  ] });
1811
1600
  };
1812
1601
  const FormHeader = memo(FormHeaderComponent);
1602
+ function toLD(human, decimals) {
1603
+ const [i = "0", f = ""] = human.replace(",", ".").split(".");
1604
+ const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1605
+ return BigInt(i + frac).toString();
1606
+ }
1607
+ function fromLD(ld, decimals) {
1608
+ const bi = BigInt(ld || "0");
1609
+ if (decimals === 0) return Number(bi);
1610
+ const base = BigInt(10) ** BigInt(decimals);
1611
+ const int = bi / base;
1612
+ const frac = Number(bi % base) / Number(base);
1613
+ return Number(int) + frac;
1614
+ }
1813
1615
  async function fetchQuotes(req) {
1814
1616
  const params = {
1815
1617
  srcChainKey: req.srcChainKey,
@@ -1868,6 +1670,131 @@ async function getQuotesByPriority(req) {
1868
1670
  const priority = req.routePriority || RoutePriority.RECOMMENDED;
1869
1671
  return selectQuoteByPriority(routes, priority);
1870
1672
  }
1673
+ function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1674
+ if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1675
+ const byChain = assetMatrix[assetSymbol.toUpperCase()];
1676
+ return byChain?.[chainKey];
1677
+ }
1678
+ const DEFAULT_SLIPPAGE_BPS = 50;
1679
+ const lower = (s) => (s ?? "").toLowerCase();
1680
+ const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1681
+ function tonNorm(addr) {
1682
+ if (!addr) return null;
1683
+ try {
1684
+ if (addr.includes(":")) {
1685
+ return Address.parseRaw(addr).toString({
1686
+ bounceable: false,
1687
+ urlSafe: true
1688
+ });
1689
+ }
1690
+ return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1691
+ } catch {
1692
+ return null;
1693
+ }
1694
+ }
1695
+ const isZeroAddr = (a) => (
1696
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1697
+ !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1698
+ );
1699
+ function addrForApi(chainKey, addr) {
1700
+ if (chainKey === "ton") return tonNorm(addr);
1701
+ return addr;
1702
+ }
1703
+ function isNativeAddrEqual(chain, tokenAddr) {
1704
+ if (!chain) return false;
1705
+ if (!tokenAddr) return false;
1706
+ if (isZeroAddr(tokenAddr)) return true;
1707
+ const nativeAddr = chain.nativeCurrency?.address;
1708
+ if (!nativeAddr) return false;
1709
+ if (chain.chainKey === "ton") {
1710
+ const a = tonNorm(tokenAddr);
1711
+ const b = tonNorm(nativeAddr);
1712
+ return !!a && !!b && a === b;
1713
+ }
1714
+ return lower(nativeAddr) === lower(tokenAddr);
1715
+ }
1716
+ function findNativeMeta(tokens, chain) {
1717
+ if (!chain) return { decimals: 18, priceUsd: void 0 };
1718
+ const sym = normSym(chain.nativeCurrency?.symbol);
1719
+ if (!sym) {
1720
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1721
+ }
1722
+ const sameChain = tokens?.find(
1723
+ (t2) => t2.chainKey === chain.chainKey && normSym(t2.symbol) === sym
1724
+ ) ?? void 0;
1725
+ if (sameChain)
1726
+ return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1727
+ const anyChain = tokens?.find((t2) => normSym(t2.symbol) === sym);
1728
+ if (anyChain)
1729
+ return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1730
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1731
+ }
1732
+ function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1733
+ if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1734
+ const chain = chains?.find((c) => c.chainKey === chainKey);
1735
+ const hit = tokens?.find((t2) => {
1736
+ if (t2.chainKey !== chainKey) return false;
1737
+ if (chainKey === "ton") {
1738
+ const a = tonNorm(t2.address);
1739
+ const b = tonNorm(tokenAddr);
1740
+ return !!a && !!b && a === b;
1741
+ }
1742
+ return lower(t2.address) === lower(tokenAddr);
1743
+ }) ?? void 0;
1744
+ if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1745
+ if (isNativeAddrEqual(chain, tokenAddr)) {
1746
+ return findNativeMeta(tokens, chain);
1747
+ }
1748
+ return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1749
+ }
1750
+ function computeFeesUsdFromArray(fees, tokens, chains) {
1751
+ if (!fees?.length) return { totalUsd: 0 };
1752
+ let total = 0;
1753
+ let protocolFeeUsd = 0;
1754
+ let messageFeeUsd = 0;
1755
+ const byType = /* @__PURE__ */ new Map();
1756
+ for (const f of fees) {
1757
+ let usd = Number(f.usd ?? 0);
1758
+ if (!usd) {
1759
+ const { decimals, priceUsd } = lookupTokenMeta(
1760
+ tokens,
1761
+ chains,
1762
+ f.chainKey,
1763
+ f.token
1764
+ );
1765
+ const human = fromLD(String(f.amount ?? "0"), decimals);
1766
+ usd = (priceUsd ?? 0) * human;
1767
+ }
1768
+ total += usd;
1769
+ const type = (f.type ?? "other").toLowerCase();
1770
+ byType.set(type, (byType.get(type) ?? 0) + usd);
1771
+ if (type === "protocol" || type === "service") {
1772
+ protocolFeeUsd += usd;
1773
+ } else if (type === "message" || type === "gas" || type === "network") {
1774
+ messageFeeUsd += usd;
1775
+ }
1776
+ }
1777
+ const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1778
+ const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1779
+ return {
1780
+ totalUsd: total,
1781
+ protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1782
+ messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1783
+ serviceUsd,
1784
+ blockchainUsd
1785
+ };
1786
+ }
1787
+ function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1788
+ if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1789
+ let acc = 0n;
1790
+ for (const f of fees) {
1791
+ if (f.chainKey !== dstChainKey) continue;
1792
+ const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1793
+ if (!same) continue;
1794
+ acc += BigInt(f.amount ?? "0");
1795
+ }
1796
+ return acc.toString();
1797
+ }
1871
1798
  function useBridgeQuote() {
1872
1799
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
1873
1800
  const { fromChain, toChain } = useChainsStore();
@@ -2245,14 +2172,14 @@ const SwapButton = () => {
2245
2172
  const WalletBalance = (props) => {
2246
2173
  const { value, isLoading = false } = props;
2247
2174
  if (isLoading) {
2248
- return /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-center", children: [
2175
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
2249
2176
  /* @__PURE__ */ jsx(WalletIcon, { className: "text-muted-foreground" }),
2250
2177
  /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16 rounded-md" })
2251
2178
  ] });
2252
2179
  }
2253
- return /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-center", children: [
2180
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
2254
2181
  /* @__PURE__ */ jsx(WalletIcon, { className: "text-muted-foreground" }),
2255
- /* @__PURE__ */ jsx("span", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: value })
2182
+ /* @__PURE__ */ jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: value })
2256
2183
  ] });
2257
2184
  };
2258
2185
  const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/networks";
@@ -2282,7 +2209,7 @@ const SelectNetworkButton = ({
2282
2209
  type: "button",
2283
2210
  "aria-label": label,
2284
2211
  children: [
2285
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 items-center", children: [
2212
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
2286
2213
  /* @__PURE__ */ jsx(
2287
2214
  NetworkSymbol,
2288
2215
  {
@@ -2291,7 +2218,7 @@ const SelectNetworkButton = ({
2291
2218
  alt: label
2292
2219
  }
2293
2220
  ),
2294
- /* @__PURE__ */ jsx("span", { className: "text-secondary-foreground text-sm leading-4.5 font-semibold ", children: label })
2221
+ /* @__PURE__ */ jsx("span", { className: "text-secondary-foreground text-sm leading-5 font-semibold ", children: label })
2295
2222
  ] }),
2296
2223
  /* @__PURE__ */ jsx(ArrowDownIcon, { className: "size-4 text-secondary-foreground" })
2297
2224
  ]
@@ -2349,7 +2276,7 @@ const CurrencyInput = forwardRef(
2349
2276
  pattern: "[0-9]*[.,]?[0-9]*",
2350
2277
  readOnly,
2351
2278
  className: cn(
2352
- "text-[32px] h-8.5 font-medium leading-8.5 rounded-none text-end bg-transparent dark:bg-transparent text-foreground shadow-none border-none outline-none ring-0 focus:outline-none",
2279
+ "text-[32px] h-12 font-medium leading-9 rounded-none text-end bg-transparent dark:bg-transparent text-foreground shadow-none border-none outline-none ring-0 focus:outline-none",
2353
2280
  className
2354
2281
  ),
2355
2282
  max,
@@ -2368,12 +2295,10 @@ const ChainSelectModal = ({
2368
2295
  }) => {
2369
2296
  const { t: t2 } = useBridgeTranslation();
2370
2297
  const [query, setQuery] = useState("");
2371
- const [isFocused, setIsFocused] = useState(false);
2372
2298
  const { setFromChain, chains, fromChain, toChain } = useChainsStore();
2373
2299
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2374
2300
  const handleClose = useCallback(() => {
2375
2301
  setQuery("");
2376
- setIsFocused(false);
2377
2302
  onClose();
2378
2303
  }, [onClose]);
2379
2304
  const findCompatibleSrcChain = useCallback(
@@ -2433,8 +2358,8 @@ const ChainSelectModal = ({
2433
2358
  Button,
2434
2359
  {
2435
2360
  onClick: () => onChainPick(chain, willChangeSrc),
2436
- className: `w-full cursor-pointer flex shadow-none items-center justify-between gap-2.5 px-5 py-3.5 h-12.5 font-extrabold capitalize hover:bg-muted bg-transparent rounded-12 transition-[300] ${isSelected ? "border border-ring" : ""}`,
2437
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
2361
+ className: `w-full cursor-pointer flex shadow-none items-center justify-between gap-3 px-5 py-3.5 h-12.5 font-extrabold capitalize hover:bg-muted bg-transparent rounded-md transition-[300] ${isSelected ? "border border-ring" : ""}`,
2362
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
2438
2363
  /* @__PURE__ */ jsx(
2439
2364
  NetworkSymbol,
2440
2365
  {
@@ -2449,26 +2374,16 @@ const ChainSelectModal = ({
2449
2374
  chain.chainKey
2450
2375
  );
2451
2376
  };
2452
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!max-h-[80dvh] overflow-hidden", children: [
2377
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!h-[90dvh] flex flex-col", children: [
2453
2378
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("bridge.selectNetwork") }) }),
2454
- /* @__PURE__ */ jsxs(
2455
- "div",
2379
+ /* @__PURE__ */ jsx(
2380
+ SearchInput,
2456
2381
  {
2457
- className: `flex items-center gap-2.5 px-5 py-3.5 bg-input rounded-md h-12.5 transition-all duration-200 ${isFocused ? "border border-ring" : "border border-transparent"}`,
2458
- children: [
2459
- /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 text-muted-foreground" }),
2460
- /* @__PURE__ */ jsx(
2461
- Input,
2462
- {
2463
- placeholder: t2("bridge.searchDestinationChain"),
2464
- className: "w-full outline-none leading-0 p-0 h-6 text-base text-foreground placeholder:text-muted-foreground bg-none dark:bg-transparent",
2465
- value: query,
2466
- onChange: (e) => setQuery(e.target.value),
2467
- onFocus: () => setIsFocused(true),
2468
- onBlur: () => setIsFocused(false)
2469
- }
2470
- )
2471
- ]
2382
+ placeholder: t2("bridge.searchDestinationChain"),
2383
+ value: query,
2384
+ onChange: setQuery,
2385
+ containerClassName: "rounded-md",
2386
+ className: "text-foreground placeholder:text-muted-foreground"
2472
2387
  }
2473
2388
  ),
2474
2389
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
@@ -2493,9 +2408,9 @@ function short$1(addr) {
2493
2408
  return addr.slice(0, 4) + "…" + addr.slice(-4);
2494
2409
  }
2495
2410
  const prefixIcons = {
2496
- tronlink: /* @__PURE__ */ jsx(TronLinkIcon, { className: "size-4.5" }),
2497
- metamask: /* @__PURE__ */ jsx(MetaMaskIcon, { className: "size-4.5" }),
2498
- ton: /* @__PURE__ */ jsx(TonKeeperIcon, { className: "size-4.5" })
2411
+ tronlink: /* @__PURE__ */ jsx(TronLinkIcon, { className: "size-5" }),
2412
+ metamask: /* @__PURE__ */ jsx(MetaMaskIcon, { className: "size-5" }),
2413
+ ton: /* @__PURE__ */ jsx(TonKeeperIcon, { className: "size-5" })
2499
2414
  };
2500
2415
  const mapWalletToType = (wallet) => {
2501
2416
  switch (wallet) {
@@ -2544,7 +2459,7 @@ const WalletButton = ({
2544
2459
  e.preventDefault();
2545
2460
  },
2546
2461
  disabled: isButtonDisabled,
2547
- className: "p-0 !px-0 py-0 flex gap-1 cursor-pointer shadow-none hover:bg-transparent bg-transparent rounded-none h-4.5",
2462
+ className: "p-0 !px-0 py-0 flex gap-1 cursor-pointer shadow-none hover:bg-transparent bg-transparent rounded-none h-5",
2548
2463
  children: [
2549
2464
  isConnected ? prefixIcons[wallet] : null,
2550
2465
  /* @__PURE__ */ jsx("p", { className: "leading-4 text-sm font-medium text-link border-b-2 border-dotted border-link", children: buttonText })
@@ -2620,7 +2535,7 @@ const SwapSection = ({
2620
2535
  ),
2621
2536
  children: [
2622
2537
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
2623
- /* @__PURE__ */ jsx("span", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: label }),
2538
+ /* @__PURE__ */ jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: label }),
2624
2539
  /* @__PURE__ */ jsx(
2625
2540
  WalletBalance,
2626
2541
  {
@@ -2683,63 +2598,30 @@ const useCustomAddressStore = create$1((set) => ({
2683
2598
  setCustomDstAddress: (address) => set({ customDstAddress: address }),
2684
2599
  clearCustomDstAddress: () => set({ customDstAddress: void 0 })
2685
2600
  }));
2686
- const tonNormalize = (addr) => {
2687
- if (!addr) return null;
2688
- try {
2689
- if (addr.includes(":")) {
2690
- return Address.parseRaw(addr).toString({
2691
- bounceable: false,
2692
- urlSafe: true
2693
- });
2694
- }
2695
- return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
2696
- } catch {
2697
- return null;
2698
- }
2601
+ const useIsAddressValid = (address, chainKey) => {
2602
+ const isValid = useMemo(() => {
2603
+ return isAddressValidForChain(chainKey, address);
2604
+ }, [address, chainKey]);
2605
+ return { isValid };
2699
2606
  };
2700
- const ToggleRow = ({ enabled, onToggle }) => {
2607
+ const AnotherAddress = () => {
2608
+ const [enabled, setEnabled] = useState(false);
2609
+ const [isFocused, setIsFocused] = useState(false);
2701
2610
  const { t: t2 } = useBridgeTranslation();
2702
2611
  const { toChain } = useChainsStore();
2703
- const { dstAddress } = useAddresses();
2704
- const { setCustomDstAddress, clearCustomDstAddress } = useCustomAddressStore();
2612
+ const { setCustomDstAddress } = useCustomAddressStore();
2705
2613
  const [value, setValue] = useState("");
2706
- const [invalid, setInvalid] = useState(false);
2707
- const prevDstRef = useRef(void 0);
2708
- const prevEnabledRef = useRef(enabled);
2709
- useEffect(() => {
2710
- const wasEnabled = prevEnabledRef.current;
2711
- if (enabled && !wasEnabled) {
2712
- prevDstRef.current = dstAddress;
2713
- setValue(dstAddress ?? "");
2714
- setInvalid(false);
2715
- }
2716
- if (!enabled && wasEnabled) {
2717
- clearCustomDstAddress();
2718
- setInvalid(false);
2719
- }
2720
- prevEnabledRef.current = enabled;
2721
- }, [enabled]);
2614
+ const { isValid } = useIsAddressValid(value.trim(), toChain?.chainKey);
2615
+ const invalid = value.trim() && !isValid;
2722
2616
  const pushToStoreIfValid = (raw) => {
2723
2617
  if (!enabled) return;
2724
2618
  const v = raw.trim();
2725
2619
  if (!v) {
2726
2620
  setCustomDstAddress(void 0);
2727
- setInvalid(false);
2728
- return;
2729
- }
2730
- const ck = toChain?.chainKey;
2731
- if (!ck) {
2732
- setInvalid(true);
2733
2621
  return;
2734
2622
  }
2735
- const valueForStore = ck === "ton" ? tonNormalize(v) ?? void 0 : v;
2736
- const valid = isAddressValidForChain(ck, valueForStore ?? "");
2737
- if (valid && valueForStore) {
2738
- setInvalid(false);
2739
- setCustomDstAddress(valueForStore);
2740
- if (ck === "ton") setValue(valueForStore);
2741
- } else {
2742
- setInvalid(true);
2623
+ if (isValid) {
2624
+ setCustomDstAddress(v);
2743
2625
  }
2744
2626
  };
2745
2627
  const onChange = (text) => {
@@ -2754,16 +2636,16 @@ const ToggleRow = ({ enabled, onToggle }) => {
2754
2636
  } catch {
2755
2637
  }
2756
2638
  };
2757
- return /* @__PURE__ */ jsxs("div", { className: "p-4 flex flex-col rounded-b-lg gap-2 bg-muted", children: [
2639
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 flex flex-col rounded-b-lg bg-muted", children: [
2758
2640
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2759
- /* @__PURE__ */ jsx("p", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: t2("bridge.sendToAnotherAddress") }),
2641
+ /* @__PURE__ */ jsx("p", { className: "text-sm leading-5 font-medium text-muted-foreground", children: t2("bridge.sendToAnotherAddress") }),
2760
2642
  /* @__PURE__ */ jsx(
2761
2643
  Switch,
2762
2644
  {
2763
2645
  className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2764
2646
  "aria-pressed": enabled,
2765
2647
  checked: enabled,
2766
- onClick: onToggle
2648
+ onClick: () => setEnabled((v) => !v)
2767
2649
  }
2768
2650
  )
2769
2651
  ] }),
@@ -2778,16 +2660,27 @@ const ToggleRow = ({ enabled, onToggle }) => {
2778
2660
  children: /* @__PURE__ */ jsxs(
2779
2661
  "div",
2780
2662
  {
2781
- className: `bg-input py-2 px-4 w-full flex items-center gap-4 rounded-12 justify-between border ${invalid ? "border-destructive" : "border-transparent"}`,
2663
+ className: cn(
2664
+ "bg-input py-2 px-4 mt-2 w-full flex items-center gap-4 rounded-md justify-between border border-transparent transition-all",
2665
+ {
2666
+ "py-4": value,
2667
+ "border border-ring": isFocused,
2668
+ "border-destructive": invalid
2669
+ }
2670
+ ),
2782
2671
  children: [
2783
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
2672
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
2784
2673
  /* @__PURE__ */ jsx(
2785
2674
  Input,
2786
2675
  {
2787
- className: "p-0 h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-input-placeholder text-input-text border-none",
2788
- placeholder: toChain?.chainKey === "ton" ? t2("bridge.tonAddressPlaceholder") : t2("bridge.evmAddressPlaceholder"),
2676
+ className: cn(
2677
+ "p-0 h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none"
2678
+ ),
2679
+ placeholder: t2("bridge.anotherAddressPlaceholder"),
2789
2680
  type: "text",
2790
2681
  value,
2682
+ onFocus: () => setIsFocused(true),
2683
+ onBlur: () => setIsFocused(false),
2791
2684
  onChange: (e) => onChange(e.target.value)
2792
2685
  }
2793
2686
  ),
@@ -2797,21 +2690,14 @@ const ToggleRow = ({ enabled, onToggle }) => {
2797
2690
  defaultValue: "Check correctness before transfer"
2798
2691
  }) }) })
2799
2692
  ] }),
2800
- !value ? /* @__PURE__ */ jsx(
2801
- Button,
2802
- {
2803
- variant: "default",
2804
- className: "self-center py-2 h-8.5 px-3 hover:bg-input-button bg-input-button text-input-button-foreground text-sm leading-4.5 font-semibold uppercase !rounded-40",
2805
- onClick: onPaste,
2806
- children: t2("common.paste")
2807
- }
2808
- ) : /* @__PURE__ */ jsx(
2693
+ !value ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: onPaste, children: t2("common.paste") }) : /* @__PURE__ */ jsx(
2809
2694
  Button,
2810
2695
  {
2811
2696
  variant: "ghost",
2812
- className: "h-5 w-5 self-start\n bg-input-x-bg hover:bg-input-x-bg \n p-0.5 m-0 rounded-full\n border-0 shadow-none\n focus:outline-none focus:ring-0 has-[>svg]:px-0",
2697
+ size: "sm",
2698
+ className: "rounded-full p-0 size-5 self-start",
2813
2699
  onClick: () => setValue(""),
2814
- children: /* @__PURE__ */ jsx(X, { className: "size-3 text-input-x" })
2700
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
2815
2701
  }
2816
2702
  )
2817
2703
  ]
@@ -2970,10 +2856,10 @@ const Details = () => {
2970
2856
  const routeText = quote?.route ? getRouteDisplayName(quote.route) : t2(`settings.routePresets.${routePriority}`);
2971
2857
  return /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", className: "bg-muted rounded-lg", children: [
2972
2858
  /* @__PURE__ */ jsx(AccordionTrigger, { className: "w-full gap-1 items-center py-6 px-5 rounded-b-lg data-[state=open]:pb-3", children: /* @__PURE__ */ jsxs("div", { className: "w-full flex items-center justify-between", children: [
2973
- /* @__PURE__ */ jsx("p", { className: "text-sm font-normal text-priority leading-3.5", children: t2("bridge.youWillReceive", { defaultValue: "You will receive" }) }),
2974
- /* @__PURE__ */ jsxs("div", { className: "bg-transparent hover:bg-transparent shadow-none h-4 p-0 px-0 py-0 flex items-center gap-1.5", children: [
2859
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-normal text-priority leading-4", children: t2("bridge.youWillReceive", { defaultValue: "You will receive" }) }),
2860
+ /* @__PURE__ */ jsxs("div", { className: "bg-transparent hover:bg-transparent shadow-none h-4 p-0 px-0 py-0 flex items-center gap-2", children: [
2975
2861
  /* @__PURE__ */ jsx(TokenSymbol, { symbol, className: "w-4 h-4", alt: "token" }),
2976
- isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24 rounded-md" }) : /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold leading-3.5 text-foreground", children: [
2862
+ isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24 rounded-md" }) : /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold leading-4 text-foreground", children: [
2977
2863
  receiveText,
2978
2864
  " ",
2979
2865
  symbol
@@ -2985,7 +2871,7 @@ const Details = () => {
2985
2871
  DetailsRow,
2986
2872
  {
2987
2873
  label: t2("transaction.route"),
2988
- value: /* @__PURE__ */ jsxs("strong", { className: "flex items-center gap-1.5", children: [
2874
+ value: /* @__PURE__ */ jsxs("strong", { className: "flex items-center gap-2", children: [
2989
2875
  /* @__PURE__ */ jsx(StargateIcon, { className: "size-4" }),
2990
2876
  /* @__PURE__ */ jsx("p", { className: "", children: routeText })
2991
2877
  ] })
@@ -3025,7 +2911,7 @@ const DetailsRow = ({
3025
2911
  value,
3026
2912
  isLoading = false
3027
2913
  }) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3028
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
2914
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3029
2915
  /* @__PURE__ */ jsx("p", { className: "text-sm text-priority font-normal", children: label }),
3030
2916
  /* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-receive-icon" }) })
3031
2917
  ] }),
@@ -3789,9 +3675,9 @@ const WalletSelectModal = () => {
3789
3675
  /* @__PURE__ */ jsx("div", { className: "py-2 font-semibold text-muted-foreground uppercase", children: t2("wallets.connected") }),
3790
3676
  /* @__PURE__ */ jsx("div", { className: "", children: connectedWallets.map((wallet) => {
3791
3677
  const IconComponent = wallet.icon;
3792
- return /* @__PURE__ */ jsx("div", { className: "-mx-5", children: /* @__PURE__ */ jsxs(Button, { className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300]", children: [
3793
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
3794
- /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3678
+ return /* @__PURE__ */ jsx("div", { className: "-mx-5", children: /* @__PURE__ */ jsxs(Button, { className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-3 px-5 py-3 hover:bg-muted h-auto rounded-md transition-[300]", children: [
3679
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3680
+ /* @__PURE__ */ jsx("div", { className: "size-8 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-8" }) }),
3795
3681
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
3796
3682
  /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: short(wallet.address) }),
3797
3683
  /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: wallet.name })
@@ -3830,9 +3716,9 @@ const WalletSelectModal = () => {
3830
3716
  }
3831
3717
  },
3832
3718
  disabled: isEvmConnector ? isPending : !wallet.enabled,
3833
- className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300] disabled:opacity-50 disabled:cursor-not-allowed",
3719
+ className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-3 px-5 py-3 hover:bg-muted h-auto rounded-md transition-[300] disabled:opacity-50 disabled:cursor-not-allowed",
3834
3720
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3835
- /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3721
+ /* @__PURE__ */ jsx("div", { className: "w-8 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-8" }) }),
3836
3722
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
3837
3723
  /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: wallet.name }),
3838
3724
  wallet.comingSoon ? /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t2("wallets.comingSoon") }) : /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t2("wallets.connect") })
@@ -5881,7 +5767,7 @@ class ChainStrategyRegistry {
5881
5767
  async getBalances(chainKey, address, tokens, priorityToken) {
5882
5768
  const strategy = this.getStrategy(chainKey);
5883
5769
  if (!strategy) return {};
5884
- return await strategy.getBalances(address, tokens, priorityToken);
5770
+ return await strategy.getBalances(address, tokens, chainKey, priorityToken);
5885
5771
  }
5886
5772
  isAddressValid(chainKey, address) {
5887
5773
  const strategy = this.getStrategy(chainKey);
@@ -5992,14 +5878,9 @@ function parseTonAddress(address) {
5992
5878
  }
5993
5879
  return Address$1.parse(address);
5994
5880
  }
5995
- async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5881
+ async function getEvmBalances(publicClient, address, tokens, chainKey, priorityToken) {
5996
5882
  const balances = {};
5997
5883
  try {
5998
- console.log("start getEvmBalances");
5999
- console.log("publicClient:", publicClient);
6000
- console.log("isAddress:", isAddress(address));
6001
- console.log("tokens:", tokens);
6002
- console.log("priorityToken:", priorityToken);
6003
5884
  if (!address || !isAddress(address)) {
6004
5885
  console.warn(`Invalid EVM address provided: ${address}`);
6005
5886
  return balances;
@@ -6008,40 +5889,55 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6008
5889
  throw new Error("No public client provided");
6009
5890
  }
6010
5891
  const nativeTokens = tokens.filter((t2) => isNativeAddress(t2.address));
6011
- const erc20Tokens = tokens.filter((t2) => !isNativeAddress(t2.address) && isAddress(t2.address));
5892
+ const erc20Tokens = tokens.filter(
5893
+ (t2) => !isNativeAddress(t2.address) && isAddress(t2.address)
5894
+ );
6012
5895
  if (priorityToken) {
6013
- try {
6014
- const isPriorityNative = isNativeAddress(priorityToken.address);
6015
- if (isPriorityNative) {
6016
- const ethBalance = await publicClient.getBalance({
6017
- address
6018
- });
6019
- const balance = parseFloat(formatUnits(ethBalance, priorityToken.decimals));
6020
- if (balance > 0) {
6021
- balances[priorityToken.symbol] = { balance, address };
6022
- }
6023
- } else if (isAddress(priorityToken.address)) {
6024
- const tokenBalance = await publicClient.readContract({
6025
- address: priorityToken.address,
6026
- abi: [
6027
- {
6028
- name: "balanceOf",
6029
- type: "function",
6030
- stateMutability: "view",
6031
- inputs: [{ name: "owner", type: "address" }],
6032
- outputs: [{ name: "balance", type: "uint256" }]
6033
- }
6034
- ],
6035
- functionName: "balanceOf",
6036
- args: [address]
6037
- });
6038
- const balance = parseFloat(formatUnits(tokenBalance, priorityToken.decimals));
6039
- if (balance > 0) {
6040
- balances[priorityToken.symbol] = { balance, address };
5896
+ if (priorityToken.chainKey !== chainKey) {
5897
+ console.debug(
5898
+ `Skipping priority token ${priorityToken.symbol}: chain mismatch (expected ${chainKey}, got ${priorityToken.chainKey})`
5899
+ );
5900
+ } else {
5901
+ try {
5902
+ const isPriorityNative = isNativeAddress(priorityToken.address);
5903
+ if (isPriorityNative) {
5904
+ const ethBalance = await publicClient.getBalance({
5905
+ address
5906
+ });
5907
+ const balance = parseFloat(
5908
+ formatUnits(ethBalance, priorityToken.decimals)
5909
+ );
5910
+ if (balance > 0) {
5911
+ balances[priorityToken.symbol] = { balance, address };
5912
+ }
5913
+ } else if (isAddress(priorityToken.address)) {
5914
+ const tokenBalance = await publicClient.readContract({
5915
+ address: priorityToken.address,
5916
+ abi: [
5917
+ {
5918
+ name: "balanceOf",
5919
+ type: "function",
5920
+ stateMutability: "view",
5921
+ inputs: [{ name: "owner", type: "address" }],
5922
+ outputs: [{ name: "balance", type: "uint256" }]
5923
+ }
5924
+ ],
5925
+ functionName: "balanceOf",
5926
+ args: [address]
5927
+ });
5928
+ const balance = parseFloat(
5929
+ formatUnits(tokenBalance, priorityToken.decimals)
5930
+ );
5931
+ if (balance > 0) {
5932
+ balances[priorityToken.symbol] = { balance, address };
5933
+ }
6041
5934
  }
5935
+ } catch (error) {
5936
+ console.debug(
5937
+ `Failed to get priority token balance for ${priorityToken.symbol}:`,
5938
+ error
5939
+ );
6042
5940
  }
6043
- } catch (error) {
6044
- console.debug(`Failed to get priority token balance for ${priorityToken.symbol}:`, error);
6045
5941
  }
6046
5942
  }
6047
5943
  for (const token of nativeTokens) {
@@ -6054,7 +5950,10 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6054
5950
  balances[token.symbol] = { balance, address };
6055
5951
  }
6056
5952
  } catch (error) {
6057
- console.debug(`Failed to get native balance for ${token.symbol}:`, error);
5953
+ console.debug(
5954
+ `Failed to get native balance for ${token.symbol}:`,
5955
+ error
5956
+ );
6058
5957
  }
6059
5958
  }
6060
5959
  if (erc20Tokens.length > 0) {
@@ -6082,17 +5981,25 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6082
5981
  if (!token) return;
6083
5982
  if (result.status === "success" && result.result !== void 0) {
6084
5983
  try {
6085
- const balance = parseFloat(formatUnits(result.result, token.decimals));
5984
+ const balance = parseFloat(
5985
+ formatUnits(result.result, token.decimals)
5986
+ );
6086
5987
  if (balance > 0) {
6087
5988
  balances[token.symbol] = { balance, address };
6088
5989
  }
6089
5990
  } catch (error) {
6090
- console.debug(`Failed to parse balance for ${token.symbol}:`, error);
5991
+ console.debug(
5992
+ `Failed to parse balance for ${token.symbol}:`,
5993
+ error
5994
+ );
6091
5995
  }
6092
5996
  }
6093
5997
  });
6094
5998
  } catch (error) {
6095
- console.warn("Multicall failed, falling back to individual calls:", error);
5999
+ console.warn(
6000
+ "Multicall failed, falling back to individual calls:",
6001
+ error
6002
+ );
6096
6003
  for (const token of erc20Tokens) {
6097
6004
  try {
6098
6005
  const tokenBalance = await publicClient.readContract({
@@ -6126,7 +6033,7 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6126
6033
  }
6127
6034
  return balances;
6128
6035
  }
6129
- async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
6036
+ async function getTonBalances(address, tokens, chainKey, customTonClient, tonApiKey) {
6130
6037
  const balances = {};
6131
6038
  try {
6132
6039
  if (!isTonFriendlyAddress(address)) {
@@ -6285,13 +6192,18 @@ class EvmChainStrategy {
6285
6192
  getConnectLabel(t2) {
6286
6193
  return t2("wallets.connectEvmWallet");
6287
6194
  }
6288
- async getBalances(address, tokens, priorityToken) {
6195
+ async getBalances(address, tokens, chainKey, priorityToken) {
6289
6196
  if (!this.publicClient) {
6290
6197
  console.warn("No publicClient available for balance query");
6291
6198
  return {};
6292
6199
  }
6293
- console.log("publicClient", this.publicClient);
6294
- return await getEvmBalances(this.publicClient, address, tokens, priorityToken);
6200
+ return await getEvmBalances(
6201
+ this.publicClient,
6202
+ address,
6203
+ tokens,
6204
+ chainKey,
6205
+ priorityToken
6206
+ );
6295
6207
  }
6296
6208
  isAddressValid(address) {
6297
6209
  if (!address) return false;
@@ -6647,10 +6559,11 @@ class TonChainStrategy {
6647
6559
  getConnectLabel(t2) {
6648
6560
  return t2("wallets.connectTonWallet");
6649
6561
  }
6650
- async getBalances(address, tokens) {
6562
+ async getBalances(address, tokens, chainKey) {
6651
6563
  return await getTonBalances(
6652
6564
  address,
6653
6565
  tokens,
6566
+ chainKey,
6654
6567
  this.config.tonClient,
6655
6568
  this.config.tonApiKey
6656
6569
  );
@@ -6859,7 +6772,6 @@ class TronChainStrategy {
6859
6772
  __publicField(this, "config");
6860
6773
  this.config = config;
6861
6774
  }
6862
- // ========== Identity ==========
6863
6775
  canHandle(chainKey) {
6864
6776
  return chainKey.toLowerCase() === "tron";
6865
6777
  }
@@ -6869,7 +6781,6 @@ class TronChainStrategy {
6869
6781
  getName() {
6870
6782
  return "TRON Chain Strategy";
6871
6783
  }
6872
- // ========== Wallet Management ==========
6873
6784
  async connect() {
6874
6785
  const tronWeb = this.getTronWeb();
6875
6786
  if (!tronWeb && (typeof window === "undefined" || !window.tronLink)) {
@@ -6900,7 +6811,6 @@ class TronChainStrategy {
6900
6811
  getConnectLabel(t2) {
6901
6812
  return t2("wallets.connectTronWallet");
6902
6813
  }
6903
- // ========== Balance & Validation ==========
6904
6814
  async getBalances(address, tokens) {
6905
6815
  const tronWeb = this.getTronWeb();
6906
6816
  if (!tronWeb) return {};
@@ -6910,7 +6820,6 @@ class TronChainStrategy {
6910
6820
  if (!address) return false;
6911
6821
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
6912
6822
  }
6913
- // ========== Gas Estimation ==========
6914
6823
  async estimateGasRequirement(params) {
6915
6824
  const {
6916
6825
  selectedToken,
@@ -7593,7 +7502,6 @@ const EvaaBridgeContent = ({
7593
7502
  const { t: t2 } = useBridgeTranslation();
7594
7503
  useTokensRequest();
7595
7504
  useChainsRequest();
7596
- const [sendToAnother, setSendToAnother] = useState(false);
7597
7505
  const swap = useSwapModel();
7598
7506
  const { fromChain, toChain } = swap;
7599
7507
  const { selectedAssetSymbol, assetMatrix } = useTokensStore();
@@ -7721,13 +7629,7 @@ const EvaaBridgeContent = ({
7721
7629
  onSelect: handleToChainChange
7722
7630
  }
7723
7631
  ),
7724
- /* @__PURE__ */ jsx(
7725
- ToggleRow,
7726
- {
7727
- enabled: sendToAnother,
7728
- onToggle: () => setSendToAnother((v) => !v)
7729
- }
7730
- ),
7632
+ /* @__PURE__ */ jsx(AnotherAddress, {}),
7731
7633
  /* @__PURE__ */ jsx(SubmitButton, {})
7732
7634
  ] }),
7733
7635
  /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(Details, {}) })
@@ -7803,7 +7705,6 @@ export {
7803
7705
  buildAssetMatrix,
7804
7706
  calculateMinReceived,
7805
7707
  computeFeesUsdFromArray,
7806
- dollarsFromNativeFees,
7807
7708
  findNativeMeta,
7808
7709
  formatBalance,
7809
7710
  formatHash,
@@ -7822,10 +7723,7 @@ export {
7822
7723
  getTokens,
7823
7724
  getTonBalances,
7824
7725
  getTronBalances,
7825
- isAddressValidForChain,
7826
- isEvmAddress,
7827
7726
  isNativeAddrEqual,
7828
- isTronAddress,
7829
7727
  isZeroAddr,
7830
7728
  listAssetsForSelect,
7831
7729
  lookupTokenMeta,