@rash2x/bridge-widget 0.1.13 → 0.1.15

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,16 +903,16 @@ 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
913
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
914
914
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.gasOnDestination") }),
915
- /* @__PURE__ */ jsx(Tip, { text: t2("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
915
+ /* @__PURE__ */ jsx(Tip, { text: t2("settings.gasOnDestinationTip"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
916
916
  ] }),
917
917
  /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-3.5", children: formatUsd(gasUsdValue) })
918
918
  ] }),
@@ -931,12 +931,11 @@ const SettingModal = ({ isOpen, onClose }) => {
931
931
  ) }) })
932
932
  ] }),
933
933
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: gasPresets.map((g) => /* @__PURE__ */ jsx(
934
- Button,
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}`)
@@ -952,22 +951,21 @@ const SettingModal = ({ isOpen, onClose }) => {
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
@@ -983,12 +981,11 @@ const SettingModal = ({ isOpen, onClose }) => {
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-2.5 px-5 py-3.5 bg-input rounded-md h-12.5 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-2.5 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-7.5 max-w-7.5 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;
1331
+ return myTokens.map((token) => ({
1332
+ token,
1333
+ willChangeSrc: false
1334
+ }));
1551
1335
  }
1552
1336
  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}`,
1337
+ ...groupedTokens.withBalance.map((token) => ({
1560
1338
  token,
1561
1339
  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
- }
1578
- }
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: [
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-3.5", 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-1.5 font-semibold text-muted-foreground uppercase mt-7.5", 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
  };
@@ -1779,7 +1574,7 @@ 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: [
1577
+ return /* @__PURE__ */ jsxs(CardHeader, { className: "gap-y-0 flex flex-row justify-between items-center", children: [
1783
1578
  /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2.5", children: [
1784
1579
  /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t2("bridge.selectToken") }),
1785
1580
  /* @__PURE__ */ jsx(SelectTokenButton, { token: current, onClick: onOpen })
@@ -1810,6 +1605,19 @@ const FormHeaderComponent = () => {
1810
1605
  ] });
1811
1606
  };
1812
1607
  const FormHeader = memo(FormHeaderComponent);
1608
+ function toLD(human, decimals) {
1609
+ const [i = "0", f = ""] = human.replace(",", ".").split(".");
1610
+ const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1611
+ return BigInt(i + frac).toString();
1612
+ }
1613
+ function fromLD(ld, decimals) {
1614
+ const bi = BigInt(ld || "0");
1615
+ if (decimals === 0) return Number(bi);
1616
+ const base = BigInt(10) ** BigInt(decimals);
1617
+ const int = bi / base;
1618
+ const frac = Number(bi % base) / Number(base);
1619
+ return Number(int) + frac;
1620
+ }
1813
1621
  async function fetchQuotes(req) {
1814
1622
  const params = {
1815
1623
  srcChainKey: req.srcChainKey,
@@ -1868,6 +1676,131 @@ async function getQuotesByPriority(req) {
1868
1676
  const priority = req.routePriority || RoutePriority.RECOMMENDED;
1869
1677
  return selectQuoteByPriority(routes, priority);
1870
1678
  }
1679
+ function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1680
+ if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1681
+ const byChain = assetMatrix[assetSymbol.toUpperCase()];
1682
+ return byChain?.[chainKey];
1683
+ }
1684
+ const DEFAULT_SLIPPAGE_BPS = 50;
1685
+ const lower = (s) => (s ?? "").toLowerCase();
1686
+ const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1687
+ function tonNorm(addr) {
1688
+ if (!addr) return null;
1689
+ try {
1690
+ if (addr.includes(":")) {
1691
+ return Address.parseRaw(addr).toString({
1692
+ bounceable: false,
1693
+ urlSafe: true
1694
+ });
1695
+ }
1696
+ return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1697
+ } catch {
1698
+ return null;
1699
+ }
1700
+ }
1701
+ const isZeroAddr = (a) => (
1702
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1703
+ !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1704
+ );
1705
+ function addrForApi(chainKey, addr) {
1706
+ if (chainKey === "ton") return tonNorm(addr);
1707
+ return addr;
1708
+ }
1709
+ function isNativeAddrEqual(chain, tokenAddr) {
1710
+ if (!chain) return false;
1711
+ if (!tokenAddr) return false;
1712
+ if (isZeroAddr(tokenAddr)) return true;
1713
+ const nativeAddr = chain.nativeCurrency?.address;
1714
+ if (!nativeAddr) return false;
1715
+ if (chain.chainKey === "ton") {
1716
+ const a = tonNorm(tokenAddr);
1717
+ const b = tonNorm(nativeAddr);
1718
+ return !!a && !!b && a === b;
1719
+ }
1720
+ return lower(nativeAddr) === lower(tokenAddr);
1721
+ }
1722
+ function findNativeMeta(tokens, chain) {
1723
+ if (!chain) return { decimals: 18, priceUsd: void 0 };
1724
+ const sym = normSym(chain.nativeCurrency?.symbol);
1725
+ if (!sym) {
1726
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1727
+ }
1728
+ const sameChain = tokens?.find(
1729
+ (t2) => t2.chainKey === chain.chainKey && normSym(t2.symbol) === sym
1730
+ ) ?? void 0;
1731
+ if (sameChain)
1732
+ return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1733
+ const anyChain = tokens?.find((t2) => normSym(t2.symbol) === sym);
1734
+ if (anyChain)
1735
+ return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1736
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1737
+ }
1738
+ function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1739
+ if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1740
+ const chain = chains?.find((c) => c.chainKey === chainKey);
1741
+ const hit = tokens?.find((t2) => {
1742
+ if (t2.chainKey !== chainKey) return false;
1743
+ if (chainKey === "ton") {
1744
+ const a = tonNorm(t2.address);
1745
+ const b = tonNorm(tokenAddr);
1746
+ return !!a && !!b && a === b;
1747
+ }
1748
+ return lower(t2.address) === lower(tokenAddr);
1749
+ }) ?? void 0;
1750
+ if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1751
+ if (isNativeAddrEqual(chain, tokenAddr)) {
1752
+ return findNativeMeta(tokens, chain);
1753
+ }
1754
+ return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1755
+ }
1756
+ function computeFeesUsdFromArray(fees, tokens, chains) {
1757
+ if (!fees?.length) return { totalUsd: 0 };
1758
+ let total = 0;
1759
+ let protocolFeeUsd = 0;
1760
+ let messageFeeUsd = 0;
1761
+ const byType = /* @__PURE__ */ new Map();
1762
+ for (const f of fees) {
1763
+ let usd = Number(f.usd ?? 0);
1764
+ if (!usd) {
1765
+ const { decimals, priceUsd } = lookupTokenMeta(
1766
+ tokens,
1767
+ chains,
1768
+ f.chainKey,
1769
+ f.token
1770
+ );
1771
+ const human = fromLD(String(f.amount ?? "0"), decimals);
1772
+ usd = (priceUsd ?? 0) * human;
1773
+ }
1774
+ total += usd;
1775
+ const type = (f.type ?? "other").toLowerCase();
1776
+ byType.set(type, (byType.get(type) ?? 0) + usd);
1777
+ if (type === "protocol" || type === "service") {
1778
+ protocolFeeUsd += usd;
1779
+ } else if (type === "message" || type === "gas" || type === "network") {
1780
+ messageFeeUsd += usd;
1781
+ }
1782
+ }
1783
+ const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1784
+ const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1785
+ return {
1786
+ totalUsd: total,
1787
+ protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1788
+ messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1789
+ serviceUsd,
1790
+ blockchainUsd
1791
+ };
1792
+ }
1793
+ function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1794
+ if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1795
+ let acc = 0n;
1796
+ for (const f of fees) {
1797
+ if (f.chainKey !== dstChainKey) continue;
1798
+ const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1799
+ if (!same) continue;
1800
+ acc += BigInt(f.amount ?? "0");
1801
+ }
1802
+ return acc.toString();
1803
+ }
1871
1804
  function useBridgeQuote() {
1872
1805
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
1873
1806
  const { fromChain, toChain } = useChainsStore();
@@ -2368,12 +2301,10 @@ const ChainSelectModal = ({
2368
2301
  }) => {
2369
2302
  const { t: t2 } = useBridgeTranslation();
2370
2303
  const [query, setQuery] = useState("");
2371
- const [isFocused, setIsFocused] = useState(false);
2372
2304
  const { setFromChain, chains, fromChain, toChain } = useChainsStore();
2373
2305
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2374
2306
  const handleClose = useCallback(() => {
2375
2307
  setQuery("");
2376
- setIsFocused(false);
2377
2308
  onClose();
2378
2309
  }, [onClose]);
2379
2310
  const findCompatibleSrcChain = useCallback(
@@ -2433,7 +2364,7 @@ const ChainSelectModal = ({
2433
2364
  Button,
2434
2365
  {
2435
2366
  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" : ""}`,
2367
+ 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-md transition-[300] ${isSelected ? "border border-ring" : ""}`,
2437
2368
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
2438
2369
  /* @__PURE__ */ jsx(
2439
2370
  NetworkSymbol,
@@ -2449,26 +2380,16 @@ const ChainSelectModal = ({
2449
2380
  chain.chainKey
2450
2381
  );
2451
2382
  };
2452
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!max-h-[80dvh] overflow-hidden", children: [
2383
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "!h-[90dvh] flex flex-col", children: [
2453
2384
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("bridge.selectNetwork") }) }),
2454
- /* @__PURE__ */ jsxs(
2455
- "div",
2385
+ /* @__PURE__ */ jsx(
2386
+ SearchInput,
2456
2387
  {
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
- ]
2388
+ placeholder: t2("bridge.searchDestinationChain"),
2389
+ value: query,
2390
+ onChange: setQuery,
2391
+ containerClassName: "rounded-md",
2392
+ className: "text-foreground placeholder:text-muted-foreground"
2472
2393
  }
2473
2394
  ),
2474
2395
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
@@ -2683,63 +2604,30 @@ const useCustomAddressStore = create$1((set) => ({
2683
2604
  setCustomDstAddress: (address) => set({ customDstAddress: address }),
2684
2605
  clearCustomDstAddress: () => set({ customDstAddress: void 0 })
2685
2606
  }));
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
- }
2607
+ const useIsAddressValid = (address, chainKey) => {
2608
+ const isValid = useMemo(() => {
2609
+ return isAddressValidForChain(chainKey, address);
2610
+ }, [address, chainKey]);
2611
+ return { isValid };
2699
2612
  };
2700
- const ToggleRow = ({ enabled, onToggle }) => {
2613
+ const AnotherAddress = () => {
2614
+ const [enabled, setEnabled] = useState(false);
2615
+ const [isFocused, setIsFocused] = useState(false);
2701
2616
  const { t: t2 } = useBridgeTranslation();
2702
2617
  const { toChain } = useChainsStore();
2703
- const { dstAddress } = useAddresses();
2704
- const { setCustomDstAddress, clearCustomDstAddress } = useCustomAddressStore();
2618
+ const { setCustomDstAddress } = useCustomAddressStore();
2705
2619
  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]);
2620
+ const { isValid } = useIsAddressValid(value.trim(), toChain?.chainKey);
2621
+ const invalid = value.trim() && !isValid;
2722
2622
  const pushToStoreIfValid = (raw) => {
2723
2623
  if (!enabled) return;
2724
2624
  const v = raw.trim();
2725
2625
  if (!v) {
2726
2626
  setCustomDstAddress(void 0);
2727
- setInvalid(false);
2728
- return;
2729
- }
2730
- const ck = toChain?.chainKey;
2731
- if (!ck) {
2732
- setInvalid(true);
2733
2627
  return;
2734
2628
  }
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);
2629
+ if (isValid) {
2630
+ setCustomDstAddress(v);
2743
2631
  }
2744
2632
  };
2745
2633
  const onChange = (text) => {
@@ -2754,7 +2642,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2754
2642
  } catch {
2755
2643
  }
2756
2644
  };
2757
- return /* @__PURE__ */ jsxs("div", { className: "p-4 flex flex-col rounded-b-lg gap-2 bg-muted", children: [
2645
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 flex flex-col rounded-b-lg bg-muted", children: [
2758
2646
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2759
2647
  /* @__PURE__ */ jsx("p", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: t2("bridge.sendToAnotherAddress") }),
2760
2648
  /* @__PURE__ */ jsx(
@@ -2763,7 +2651,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2763
2651
  className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2764
2652
  "aria-pressed": enabled,
2765
2653
  checked: enabled,
2766
- onClick: onToggle
2654
+ onClick: () => setEnabled((v) => !v)
2767
2655
  }
2768
2656
  )
2769
2657
  ] }),
@@ -2778,16 +2666,27 @@ const ToggleRow = ({ enabled, onToggle }) => {
2778
2666
  children: /* @__PURE__ */ jsxs(
2779
2667
  "div",
2780
2668
  {
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"}`,
2669
+ className: cn(
2670
+ "bg-input py-2 px-4 mt-2 w-full flex items-center gap-4 rounded-md justify-between border border-transparent transition-all",
2671
+ {
2672
+ "py-4": value,
2673
+ "border border-ring": isFocused,
2674
+ "border-destructive": invalid
2675
+ }
2676
+ ),
2782
2677
  children: [
2783
2678
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
2784
2679
  /* @__PURE__ */ jsx(
2785
2680
  Input,
2786
2681
  {
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"),
2682
+ className: cn(
2683
+ "p-0 h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none"
2684
+ ),
2685
+ placeholder: t2("bridge.anotherAddressPlaceholder"),
2789
2686
  type: "text",
2790
2687
  value,
2688
+ onFocus: () => setIsFocused(true),
2689
+ onBlur: () => setIsFocused(false),
2791
2690
  onChange: (e) => onChange(e.target.value)
2792
2691
  }
2793
2692
  ),
@@ -2797,21 +2696,14 @@ const ToggleRow = ({ enabled, onToggle }) => {
2797
2696
  defaultValue: "Check correctness before transfer"
2798
2697
  }) }) })
2799
2698
  ] }),
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(
2699
+ !value ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: onPaste, children: t2("common.paste") }) : /* @__PURE__ */ jsx(
2809
2700
  Button,
2810
2701
  {
2811
2702
  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",
2703
+ size: "sm",
2704
+ className: "rounded-full p-0 size-5 self-start",
2813
2705
  onClick: () => setValue(""),
2814
- children: /* @__PURE__ */ jsx(X, { className: "size-3 text-input-x" })
2706
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
2815
2707
  }
2816
2708
  )
2817
2709
  ]
@@ -3789,7 +3681,7 @@ const WalletSelectModal = () => {
3789
3681
  /* @__PURE__ */ jsx("div", { className: "py-2 font-semibold text-muted-foreground uppercase", children: t2("wallets.connected") }),
3790
3682
  /* @__PURE__ */ jsx("div", { className: "", children: connectedWallets.map((wallet) => {
3791
3683
  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: [
3684
+ 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-md transition-[300]", children: [
3793
3685
  /* @__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
3686
  /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3795
3687
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -3830,7 +3722,7 @@ const WalletSelectModal = () => {
3830
3722
  }
3831
3723
  },
3832
3724
  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",
3725
+ 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-md transition-[300] disabled:opacity-50 disabled:cursor-not-allowed",
3834
3726
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3835
3727
  /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3836
3728
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -5881,7 +5773,7 @@ class ChainStrategyRegistry {
5881
5773
  async getBalances(chainKey, address, tokens, priorityToken) {
5882
5774
  const strategy = this.getStrategy(chainKey);
5883
5775
  if (!strategy) return {};
5884
- return await strategy.getBalances(address, tokens, priorityToken);
5776
+ return await strategy.getBalances(address, tokens, chainKey, priorityToken);
5885
5777
  }
5886
5778
  isAddressValid(chainKey, address) {
5887
5779
  const strategy = this.getStrategy(chainKey);
@@ -5992,14 +5884,9 @@ function parseTonAddress(address) {
5992
5884
  }
5993
5885
  return Address$1.parse(address);
5994
5886
  }
5995
- async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5887
+ async function getEvmBalances(publicClient, address, tokens, chainKey, priorityToken) {
5996
5888
  const balances = {};
5997
5889
  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
5890
  if (!address || !isAddress(address)) {
6004
5891
  console.warn(`Invalid EVM address provided: ${address}`);
6005
5892
  return balances;
@@ -6008,40 +5895,55 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6008
5895
  throw new Error("No public client provided");
6009
5896
  }
6010
5897
  const nativeTokens = tokens.filter((t2) => isNativeAddress(t2.address));
6011
- const erc20Tokens = tokens.filter((t2) => !isNativeAddress(t2.address) && isAddress(t2.address));
5898
+ const erc20Tokens = tokens.filter(
5899
+ (t2) => !isNativeAddress(t2.address) && isAddress(t2.address)
5900
+ );
6012
5901
  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 };
5902
+ if (priorityToken.chainKey !== chainKey) {
5903
+ console.debug(
5904
+ `Skipping priority token ${priorityToken.symbol}: chain mismatch (expected ${chainKey}, got ${priorityToken.chainKey})`
5905
+ );
5906
+ } else {
5907
+ try {
5908
+ const isPriorityNative = isNativeAddress(priorityToken.address);
5909
+ if (isPriorityNative) {
5910
+ const ethBalance = await publicClient.getBalance({
5911
+ address
5912
+ });
5913
+ const balance = parseFloat(
5914
+ formatUnits(ethBalance, priorityToken.decimals)
5915
+ );
5916
+ if (balance > 0) {
5917
+ balances[priorityToken.symbol] = { balance, address };
5918
+ }
5919
+ } else if (isAddress(priorityToken.address)) {
5920
+ const tokenBalance = await publicClient.readContract({
5921
+ address: priorityToken.address,
5922
+ abi: [
5923
+ {
5924
+ name: "balanceOf",
5925
+ type: "function",
5926
+ stateMutability: "view",
5927
+ inputs: [{ name: "owner", type: "address" }],
5928
+ outputs: [{ name: "balance", type: "uint256" }]
5929
+ }
5930
+ ],
5931
+ functionName: "balanceOf",
5932
+ args: [address]
5933
+ });
5934
+ const balance = parseFloat(
5935
+ formatUnits(tokenBalance, priorityToken.decimals)
5936
+ );
5937
+ if (balance > 0) {
5938
+ balances[priorityToken.symbol] = { balance, address };
5939
+ }
6041
5940
  }
5941
+ } catch (error) {
5942
+ console.debug(
5943
+ `Failed to get priority token balance for ${priorityToken.symbol}:`,
5944
+ error
5945
+ );
6042
5946
  }
6043
- } catch (error) {
6044
- console.debug(`Failed to get priority token balance for ${priorityToken.symbol}:`, error);
6045
5947
  }
6046
5948
  }
6047
5949
  for (const token of nativeTokens) {
@@ -6054,7 +5956,10 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6054
5956
  balances[token.symbol] = { balance, address };
6055
5957
  }
6056
5958
  } catch (error) {
6057
- console.debug(`Failed to get native balance for ${token.symbol}:`, error);
5959
+ console.debug(
5960
+ `Failed to get native balance for ${token.symbol}:`,
5961
+ error
5962
+ );
6058
5963
  }
6059
5964
  }
6060
5965
  if (erc20Tokens.length > 0) {
@@ -6082,17 +5987,25 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6082
5987
  if (!token) return;
6083
5988
  if (result.status === "success" && result.result !== void 0) {
6084
5989
  try {
6085
- const balance = parseFloat(formatUnits(result.result, token.decimals));
5990
+ const balance = parseFloat(
5991
+ formatUnits(result.result, token.decimals)
5992
+ );
6086
5993
  if (balance > 0) {
6087
5994
  balances[token.symbol] = { balance, address };
6088
5995
  }
6089
5996
  } catch (error) {
6090
- console.debug(`Failed to parse balance for ${token.symbol}:`, error);
5997
+ console.debug(
5998
+ `Failed to parse balance for ${token.symbol}:`,
5999
+ error
6000
+ );
6091
6001
  }
6092
6002
  }
6093
6003
  });
6094
6004
  } catch (error) {
6095
- console.warn("Multicall failed, falling back to individual calls:", error);
6005
+ console.warn(
6006
+ "Multicall failed, falling back to individual calls:",
6007
+ error
6008
+ );
6096
6009
  for (const token of erc20Tokens) {
6097
6010
  try {
6098
6011
  const tokenBalance = await publicClient.readContract({
@@ -6126,7 +6039,7 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6126
6039
  }
6127
6040
  return balances;
6128
6041
  }
6129
- async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
6042
+ async function getTonBalances(address, tokens, chainKey, customTonClient, tonApiKey) {
6130
6043
  const balances = {};
6131
6044
  try {
6132
6045
  if (!isTonFriendlyAddress(address)) {
@@ -6285,13 +6198,18 @@ class EvmChainStrategy {
6285
6198
  getConnectLabel(t2) {
6286
6199
  return t2("wallets.connectEvmWallet");
6287
6200
  }
6288
- async getBalances(address, tokens, priorityToken) {
6201
+ async getBalances(address, tokens, chainKey, priorityToken) {
6289
6202
  if (!this.publicClient) {
6290
6203
  console.warn("No publicClient available for balance query");
6291
6204
  return {};
6292
6205
  }
6293
- console.log("publicClient", this.publicClient);
6294
- return await getEvmBalances(this.publicClient, address, tokens, priorityToken);
6206
+ return await getEvmBalances(
6207
+ this.publicClient,
6208
+ address,
6209
+ tokens,
6210
+ chainKey,
6211
+ priorityToken
6212
+ );
6295
6213
  }
6296
6214
  isAddressValid(address) {
6297
6215
  if (!address) return false;
@@ -6647,10 +6565,11 @@ class TonChainStrategy {
6647
6565
  getConnectLabel(t2) {
6648
6566
  return t2("wallets.connectTonWallet");
6649
6567
  }
6650
- async getBalances(address, tokens) {
6568
+ async getBalances(address, tokens, chainKey) {
6651
6569
  return await getTonBalances(
6652
6570
  address,
6653
6571
  tokens,
6572
+ chainKey,
6654
6573
  this.config.tonClient,
6655
6574
  this.config.tonApiKey
6656
6575
  );
@@ -6859,7 +6778,6 @@ class TronChainStrategy {
6859
6778
  __publicField(this, "config");
6860
6779
  this.config = config;
6861
6780
  }
6862
- // ========== Identity ==========
6863
6781
  canHandle(chainKey) {
6864
6782
  return chainKey.toLowerCase() === "tron";
6865
6783
  }
@@ -6869,7 +6787,6 @@ class TronChainStrategy {
6869
6787
  getName() {
6870
6788
  return "TRON Chain Strategy";
6871
6789
  }
6872
- // ========== Wallet Management ==========
6873
6790
  async connect() {
6874
6791
  const tronWeb = this.getTronWeb();
6875
6792
  if (!tronWeb && (typeof window === "undefined" || !window.tronLink)) {
@@ -6900,7 +6817,6 @@ class TronChainStrategy {
6900
6817
  getConnectLabel(t2) {
6901
6818
  return t2("wallets.connectTronWallet");
6902
6819
  }
6903
- // ========== Balance & Validation ==========
6904
6820
  async getBalances(address, tokens) {
6905
6821
  const tronWeb = this.getTronWeb();
6906
6822
  if (!tronWeb) return {};
@@ -6910,7 +6826,6 @@ class TronChainStrategy {
6910
6826
  if (!address) return false;
6911
6827
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
6912
6828
  }
6913
- // ========== Gas Estimation ==========
6914
6829
  async estimateGasRequirement(params) {
6915
6830
  const {
6916
6831
  selectedToken,
@@ -7593,7 +7508,6 @@ const EvaaBridgeContent = ({
7593
7508
  const { t: t2 } = useBridgeTranslation();
7594
7509
  useTokensRequest();
7595
7510
  useChainsRequest();
7596
- const [sendToAnother, setSendToAnother] = useState(false);
7597
7511
  const swap = useSwapModel();
7598
7512
  const { fromChain, toChain } = swap;
7599
7513
  const { selectedAssetSymbol, assetMatrix } = useTokensStore();
@@ -7721,13 +7635,7 @@ const EvaaBridgeContent = ({
7721
7635
  onSelect: handleToChainChange
7722
7636
  }
7723
7637
  ),
7724
- /* @__PURE__ */ jsx(
7725
- ToggleRow,
7726
- {
7727
- enabled: sendToAnother,
7728
- onToggle: () => setSendToAnother((v) => !v)
7729
- }
7730
- ),
7638
+ /* @__PURE__ */ jsx(AnotherAddress, {}),
7731
7639
  /* @__PURE__ */ jsx(SubmitButton, {})
7732
7640
  ] }),
7733
7641
  /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(Details, {}) })
@@ -7803,7 +7711,6 @@ export {
7803
7711
  buildAssetMatrix,
7804
7712
  calculateMinReceived,
7805
7713
  computeFeesUsdFromArray,
7806
- dollarsFromNativeFees,
7807
7714
  findNativeMeta,
7808
7715
  formatBalance,
7809
7716
  formatHash,
@@ -7822,10 +7729,7 @@ export {
7822
7729
  getTokens,
7823
7730
  getTonBalances,
7824
7731
  getTronBalances,
7825
- isAddressValidForChain,
7826
- isEvmAddress,
7827
7732
  isNativeAddrEqual,
7828
- isTronAddress,
7829
7733
  isZeroAddr,
7830
7734
  listAssetsForSelect,
7831
7735
  lookupTokenMeta,