@rash2x/bridge-widget 0.1.12 → 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,32 +2,97 @@ 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 { useTranslation } from "react-i18next";
6
- import require$$0, { useState, useCallback, useMemo, createContext, useContext, useEffect, memo, forwardRef, useRef } from "react";
5
+ import require$$0, { useEffect, useState, useCallback, useMemo, createContext, useContext, memo, forwardRef } from "react";
6
+ import { initReactI18next, useTranslation, I18nextProvider } from "react-i18next";
7
+ import i18n, { t } from "i18next";
7
8
  import { create as create$1 } from "zustand";
8
9
  import { Button } from "@/components/ui/button";
9
10
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
11
+ import { Badge } from "@/components/ui/badge";
10
12
  import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
11
13
  import { cn } from "@/lib/utils";
12
- import { Input } from "@/components/ui/input";
14
+ import { Skeleton } from "@/components/ui/skeleton";
13
15
  import { useAccount, useConnect, useDisconnect, useWalletClient, usePublicClient } from "wagmi";
14
16
  import { useWallet } from "@tronweb3/tronwallet-adapter-react-hooks";
15
17
  import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
16
18
  import { useQuery, useQueryClient } from "@tanstack/react-query";
17
19
  import { Address, beginCell as beginCell$1, storeMessage, loadMessage, Cell } from "@ton/core";
18
- import { VariableSizeList } from "react-window";
19
- import { Skeleton } from "@/components/ui/skeleton";
20
+ import { Input } from "@/components/ui/input";
20
21
  import { CardHeader, CardTitle, CardAction, Card, CardContent, CardFooter } from "@/components/ui/card";
21
22
  import { Switch } from "@/components/ui/switch";
22
23
  import { X } from "lucide-react";
23
24
  import { AnimatePresence, motion } from "framer-motion";
24
25
  import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
25
- import { t } from "i18next";
26
26
  import { toast, Toaster } from "sonner";
27
27
  import { BrowserProvider, Contract, parseUnits } from "ethers";
28
28
  import { isAddress, formatUnits } from "viem";
29
29
  import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
30
30
  import { TronLinkAdapterName } from "@tronweb3/tronwallet-adapters";
31
+ const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
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", "anotherAddressPlaceholder": "Address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
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
+ const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
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" } };
37
+ const en = {
38
+ common: common$1,
39
+ wallets: wallets$1,
40
+ bridge: bridge$1,
41
+ transaction: transaction$1,
42
+ app: app$1,
43
+ settings: settings$1
44
+ };
45
+ const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
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": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
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
+ const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
50
+ const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
51
+ const ru = {
52
+ common,
53
+ wallets,
54
+ bridge,
55
+ transaction,
56
+ app,
57
+ settings
58
+ };
59
+ const bridgeI18n = i18n.createInstance();
60
+ const resources = {
61
+ en: {
62
+ "evaa-bridge": en
63
+ },
64
+ ru: {
65
+ "evaa-bridge": ru
66
+ }
67
+ };
68
+ bridgeI18n.use(initReactI18next).init({
69
+ resources,
70
+ lng: "en",
71
+ // Will be overridden by defaultLanguage prop
72
+ fallbackLng: "en",
73
+ debug: false,
74
+ // Use a dedicated namespace to avoid conflicts
75
+ defaultNS: "evaa-bridge",
76
+ ns: ["evaa-bridge"],
77
+ interpolation: {
78
+ escapeValue: false
79
+ // react already does escaping
80
+ }
81
+ });
82
+ function BridgeI18nProvider({
83
+ children,
84
+ defaultLanguage = "en"
85
+ }) {
86
+ useEffect(() => {
87
+ if (bridgeI18n.language !== defaultLanguage) {
88
+ bridgeI18n.changeLanguage(defaultLanguage);
89
+ }
90
+ }, [defaultLanguage]);
91
+ return /* @__PURE__ */ jsx(I18nextProvider, { i18n: bridgeI18n, children });
92
+ }
93
+ function useBridgeTranslation() {
94
+ return useTranslation("evaa-bridge", { i18n: bridgeI18n });
95
+ }
31
96
  const norm = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
32
97
  const POPULAR_ORDER = [
33
98
  "USDT",
@@ -808,7 +873,7 @@ const routePresets = [
808
873
  RoutePriority.RECOMMENDED
809
874
  ];
810
875
  const SettingModal = ({ isOpen, onClose }) => {
811
- const { t: t2 } = useTranslation();
876
+ const { t: t2 } = useBridgeTranslation();
812
877
  const { toChain } = useChainsStore();
813
878
  const { tokens } = useTokensStore();
814
879
  const {
@@ -838,16 +903,16 @@ const SettingModal = ({ isOpen, onClose }) => {
838
903
  toChain?.chainKey,
839
904
  dstNativeToken?.decimals || 18
840
905
  );
841
- const activeBtn = "bg-settings-active hover:bg-settings-active/80 text-settings-active-foreground";
842
- 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";
843
908
  return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
844
- /* @__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") }) }),
845
910
  /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
846
911
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
847
912
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
848
913
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
849
914
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.gasOnDestination") }),
850
- /* @__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" }) })
851
916
  ] }),
852
917
  /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-3.5", children: formatUsd(gasUsdValue) })
853
918
  ] }),
@@ -866,12 +931,11 @@ const SettingModal = ({ isOpen, onClose }) => {
866
931
  ) }) })
867
932
  ] }),
868
933
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: gasPresets.map((g) => /* @__PURE__ */ jsx(
869
- Button,
934
+ Badge,
870
935
  {
871
- type: "button",
872
936
  onClick: () => setGasPreset(g),
873
937
  className: cn(
874
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
938
+ "cursor-pointer",
875
939
  gasPreset === g ? activeBtn : notActiveBtn
876
940
  ),
877
941
  children: t2(`settings.gasPresets.${g}`)
@@ -887,22 +951,21 @@ const SettingModal = ({ isOpen, onClose }) => {
887
951
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t2("settings.slippageTolerance") }),
888
952
  /* @__PURE__ */ jsx(Tip, { text: t2("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
889
953
  ] }),
890
- 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", {
891
955
  defaultValue: "High slippage warning"
892
956
  }) })
893
957
  ] }),
894
958
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-6", children: [
895
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 }) }),
896
960
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsx(
897
- Button,
961
+ Badge,
898
962
  {
899
- type: "button",
900
963
  onClick: () => {
901
964
  const bps = parseFloat(p.replace("%", "")) * 100;
902
965
  setSlippageBps(bps);
903
966
  },
904
967
  className: cn(
905
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
968
+ "cursor-pointer",
906
969
  activeSlippagePreset === p ? activeBtn : notActiveBtn
907
970
  ),
908
971
  children: p
@@ -918,12 +981,11 @@ const SettingModal = ({ isOpen, onClose }) => {
918
981
  /* @__PURE__ */ jsx(Tip, { text: t2("settings.routePriority"), children: /* @__PURE__ */ jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
919
982
  ] }) }),
920
983
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsx(
921
- Button,
984
+ Badge,
922
985
  {
923
- type: "button",
924
986
  onClick: () => setRoutePriority(r),
925
987
  className: cn(
926
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
988
+ "cursor-pointer",
927
989
  routePriority === r ? activeBtn : notActiveBtn
928
990
  ),
929
991
  children: t2(`settings.routePresets.${r}`)
@@ -991,168 +1053,31 @@ function useChainStrategies() {
991
1053
  }
992
1054
  return context;
993
1055
  }
994
- function toLD(human, decimals) {
995
- const [i = "0", f = ""] = human.replace(",", ".").split(".");
996
- const frac = (f + "0".repeat(decimals)).slice(0, decimals);
997
- return BigInt(i + frac).toString();
998
- }
999
- function fromLD(ld, decimals) {
1000
- const bi = BigInt(ld || "0");
1001
- if (decimals === 0) return Number(bi);
1002
- const base = BigInt(10) ** BigInt(decimals);
1003
- const int = bi / base;
1004
- const frac = Number(bi % base) / Number(base);
1005
- return Number(int) + frac;
1006
- }
1007
- function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1008
- if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1009
- const byChain = assetMatrix[assetSymbol.toUpperCase()];
1010
- return byChain?.[chainKey];
1011
- }
1012
- const DEFAULT_SLIPPAGE_BPS = 50;
1013
- const lower = (s) => (s ?? "").toLowerCase();
1014
- const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1015
- function tonNorm(addr) {
1016
- 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;
1017
1064
  try {
1018
1065
  if (addr.includes(":")) {
1019
- return Address.parseRaw(addr).toString({
1020
- bounceable: false,
1021
- urlSafe: true
1022
- });
1066
+ Address.parseRaw(addr);
1067
+ return true;
1023
1068
  }
1024
- return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1069
+ Address.parse(addr);
1070
+ return true;
1025
1071
  } catch {
1026
- return null;
1072
+ return false;
1027
1073
  }
1028
- }
1029
- const isEvmAddress = (a) => /^0x[0-9a-fA-F]{40}$/.test(a ?? "");
1030
- const isTronAddress = (a) => /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(a ?? "");
1031
- const isZeroAddr = (a) => (
1032
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1033
- !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1034
- );
1074
+ };
1035
1075
  function isAddressValidForChain(chainKey, addr) {
1036
1076
  if (!chainKey || !addr) return false;
1037
- if (chainKey === "ton") return tonNorm(addr) !== null;
1077
+ if (chainKey === "ton") return isTonAddress(addr);
1038
1078
  if (chainKey === "tron") return isTronAddress(addr);
1039
1079
  return isEvmAddress(addr);
1040
1080
  }
1041
- function addrForApi(chainKey, addr) {
1042
- if (chainKey === "ton") return tonNorm(addr);
1043
- return addr;
1044
- }
1045
- function isNativeAddrEqual(chain, tokenAddr) {
1046
- if (!chain) return false;
1047
- if (!tokenAddr) return false;
1048
- if (isZeroAddr(tokenAddr)) return true;
1049
- const nativeAddr = chain.nativeCurrency?.address;
1050
- if (!nativeAddr) return false;
1051
- if (chain.chainKey === "ton") {
1052
- const a = tonNorm(tokenAddr);
1053
- const b = tonNorm(nativeAddr);
1054
- return !!a && !!b && a === b;
1055
- }
1056
- return lower(nativeAddr) === lower(tokenAddr);
1057
- }
1058
- function findNativeMeta(tokens, chain) {
1059
- if (!chain) return { decimals: 18, priceUsd: void 0 };
1060
- const sym = normSym(chain.nativeCurrency?.symbol);
1061
- if (!sym) {
1062
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1063
- }
1064
- const sameChain = tokens?.find(
1065
- (t2) => t2.chainKey === chain.chainKey && normSym(t2.symbol) === sym
1066
- ) ?? void 0;
1067
- if (sameChain)
1068
- return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1069
- const anyChain = tokens?.find((t2) => normSym(t2.symbol) === sym);
1070
- if (anyChain)
1071
- return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1072
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1073
- }
1074
- function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1075
- if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1076
- const chain = chains?.find((c) => c.chainKey === chainKey);
1077
- const hit = tokens?.find((t2) => {
1078
- if (t2.chainKey !== chainKey) return false;
1079
- if (chainKey === "ton") {
1080
- const a = tonNorm(t2.address);
1081
- const b = tonNorm(tokenAddr);
1082
- return !!a && !!b && a === b;
1083
- }
1084
- return lower(t2.address) === lower(tokenAddr);
1085
- }) ?? void 0;
1086
- if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1087
- if (isNativeAddrEqual(chain, tokenAddr)) {
1088
- return findNativeMeta(tokens, chain);
1089
- }
1090
- return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1091
- }
1092
- function computeFeesUsdFromArray(fees, tokens, chains) {
1093
- if (!fees?.length) return { totalUsd: 0 };
1094
- let total = 0;
1095
- let protocolFeeUsd = 0;
1096
- let messageFeeUsd = 0;
1097
- const byType = /* @__PURE__ */ new Map();
1098
- for (const f of fees) {
1099
- let usd = Number(f.usd ?? 0);
1100
- if (!usd) {
1101
- const { decimals, priceUsd } = lookupTokenMeta(
1102
- tokens,
1103
- chains,
1104
- f.chainKey,
1105
- f.token
1106
- );
1107
- const human = fromLD(String(f.amount ?? "0"), decimals);
1108
- usd = (priceUsd ?? 0) * human;
1109
- }
1110
- total += usd;
1111
- const type = (f.type ?? "other").toLowerCase();
1112
- byType.set(type, (byType.get(type) ?? 0) + usd);
1113
- if (type === "protocol" || type === "service") {
1114
- protocolFeeUsd += usd;
1115
- } else if (type === "message" || type === "gas" || type === "network") {
1116
- messageFeeUsd += usd;
1117
- }
1118
- }
1119
- const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1120
- const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1121
- return {
1122
- totalUsd: total,
1123
- protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1124
- messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1125
- serviceUsd,
1126
- blockchainUsd
1127
- };
1128
- }
1129
- function dollarsFromNativeFees(feeNative, feeLzToken, chain, tokens) {
1130
- let total = 0;
1131
- if (feeNative && chain) {
1132
- const { decimals, priceUsd } = findNativeMeta(tokens, chain);
1133
- const human = fromLD(String(feeNative), decimals);
1134
- total += (priceUsd ?? 0) * human;
1135
- }
1136
- if (feeLzToken) {
1137
- const lz = tokens?.find((t2) => normSym(t2.symbol) === "LZ");
1138
- if (lz?.price?.usd && lz.decimals != null) {
1139
- const human = fromLD(String(feeLzToken), lz.decimals);
1140
- total += lz.price.usd * human;
1141
- }
1142
- }
1143
- return total;
1144
- }
1145
- function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1146
- if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1147
- let acc = 0n;
1148
- for (const f of fees) {
1149
- if (f.chainKey !== dstChainKey) continue;
1150
- const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1151
- if (!same) continue;
1152
- acc += BigInt(f.amount ?? "0");
1153
- }
1154
- return acc.toString();
1155
- }
1156
1081
  function useBalances(chainKey, address, priorityTokenSymbol) {
1157
1082
  const { chainRegistry } = useChainStrategies();
1158
1083
  const { assetMatrix } = useTokensStore();
@@ -1214,10 +1139,92 @@ function useBalances(chainKey, address, priorityTokenSymbol) {
1214
1139
  query
1215
1140
  };
1216
1141
  }
1217
- 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();
1218
1226
  const [query, setQuery] = useState("");
1219
1227
  const [tab, setTab] = useState("my");
1220
- const [isFocused, setIsFocused] = useState(false);
1221
1228
  const { srcAddress } = useAddresses();
1222
1229
  const { fromChain, setFromChain, chains } = useChainsStore();
1223
1230
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
@@ -1302,159 +1309,7 @@ function useTokenSelectData(items) {
1302
1309
  const resetState = useCallback(() => {
1303
1310
  setQuery("");
1304
1311
  setTab("my");
1305
- setIsFocused(false);
1306
1312
  }, []);
1307
- return {
1308
- query,
1309
- setQuery,
1310
- tab,
1311
- setTab,
1312
- isFocused,
1313
- setIsFocused,
1314
- effectiveTab,
1315
- resetState,
1316
- fromChain,
1317
- setFromChain,
1318
- selectedAssetSymbol,
1319
- balancesQuery,
1320
- groupedTokens,
1321
- myTokens,
1322
- getBalance,
1323
- getTokenUsdValue,
1324
- findFirstAvailableChain,
1325
- hasAnyWallet
1326
- };
1327
- }
1328
- const TokenRow = ({
1329
- symbol,
1330
- name,
1331
- isSelected,
1332
- hasAnyWallet,
1333
- balance,
1334
- usdValue,
1335
- isBalanceLoading,
1336
- onPick
1337
- }) => {
1338
- return /* @__PURE__ */ jsxs(
1339
- Button,
1340
- {
1341
- onClick: onPick,
1342
- 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" : ""}`,
1343
- children: [
1344
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1345
- /* @__PURE__ */ jsx(
1346
- TokenSymbol,
1347
- {
1348
- symbol,
1349
- className: "size-7.5 max-w-7.5 rounded-full",
1350
- alt: symbol
1351
- }
1352
- ),
1353
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1354
- /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
1355
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
1356
- ] })
1357
- ] }),
1358
- /* @__PURE__ */ jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
1359
- /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-16 rounded-md" }),
1360
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
1361
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1362
- /* @__PURE__ */ jsx("div", { className: "font-extrabold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
1363
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
1364
- ] }) })
1365
- ]
1366
- }
1367
- );
1368
- };
1369
- const HEADER_H = 30;
1370
- const ROW_H = 50;
1371
- const HEADER_TOP_GAP = 30;
1372
- const VirtualizedTokenList = ({
1373
- nodes,
1374
- balancesLoading,
1375
- hasAnyWallet,
1376
- selectedAssetSymbol,
1377
- getBalance,
1378
- getTokenUsdValue,
1379
- onPick
1380
- }) => {
1381
- const Row = ({ index, style }) => {
1382
- const node = nodes[index];
1383
- if (node.kind === "header") {
1384
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1385
- return /* @__PURE__ */ jsx(
1386
- "div",
1387
- {
1388
- style: { ...style, paddingTop: gap },
1389
- className: "px-5 flex leading-4 text-base py-1.5 font-semibold text-muted-foreground uppercase",
1390
- children: /* @__PURE__ */ jsx("p", { children: node.title })
1391
- }
1392
- );
1393
- }
1394
- const bal = getBalance(node.token.symbol);
1395
- const usd = getTokenUsdValue(node.token.symbol, node.token.price?.usd);
1396
- const isSelected = selectedAssetSymbol?.toUpperCase() === node.token.symbol.toUpperCase();
1397
- return /* @__PURE__ */ jsx(
1398
- TokenRow,
1399
- {
1400
- symbol: node.token.symbol,
1401
- name: node.token.name,
1402
- isSelected: !!isSelected,
1403
- hasAnyWallet,
1404
- balance: bal,
1405
- usdValue: usd,
1406
- isBalanceLoading: balancesLoading,
1407
- onPick: () => onPick(node.token.symbol, node.willChangeSrc)
1408
- }
1409
- );
1410
- };
1411
- const getItemSize = (index) => {
1412
- const node = nodes[index];
1413
- if (node.kind === "header") {
1414
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1415
- return HEADER_H + gap;
1416
- }
1417
- return ROW_H;
1418
- };
1419
- return /* @__PURE__ */ jsx(
1420
- VariableSizeList,
1421
- {
1422
- height: 480,
1423
- width: "100%",
1424
- itemCount: nodes.length,
1425
- itemSize: getItemSize,
1426
- overscanCount: 8,
1427
- style: { willChange: "transform", paddingBottom: "40px" },
1428
- children: Row
1429
- }
1430
- );
1431
- };
1432
- const TokenSelectModal = ({
1433
- isOpen,
1434
- onClose,
1435
- items,
1436
- onChangeAsset
1437
- }) => {
1438
- const { t: t2 } = useTranslation();
1439
- const {
1440
- query,
1441
- setQuery,
1442
- tab,
1443
- setTab,
1444
- isFocused,
1445
- setIsFocused,
1446
- effectiveTab,
1447
- resetState,
1448
- setFromChain,
1449
- selectedAssetSymbol,
1450
- balancesQuery,
1451
- groupedTokens,
1452
- myTokens,
1453
- getBalance,
1454
- getTokenUsdValue,
1455
- findFirstAvailableChain,
1456
- hasAnyWallet
1457
- } = useTokenSelectData(items);
1458
1313
  const handleClose = useCallback(() => {
1459
1314
  resetState();
1460
1315
  onClose();
@@ -1471,68 +1326,41 @@ const TokenSelectModal = ({
1471
1326
  onChangeAsset(sym);
1472
1327
  handleClose();
1473
1328
  };
1474
- const virtualNodes = useMemo(() => {
1475
- const out = [];
1329
+ const tokensToRender = useMemo(() => {
1476
1330
  if (effectiveTab === "my") {
1477
- for (const token of myTokens) {
1478
- out.push({
1479
- kind: "token",
1480
- key: `my:${token.symbol}`,
1481
- token,
1482
- willChangeSrc: false
1483
- });
1484
- }
1485
- return out;
1331
+ return myTokens.map((token) => ({
1332
+ token,
1333
+ willChangeSrc: false
1334
+ }));
1486
1335
  }
1487
1336
  const mainTokens = [
1488
- ...groupedTokens.withBalance,
1489
- ...groupedTokens.onSrcChain
1490
- ];
1491
- for (const token of mainTokens) {
1492
- out.push({
1493
- kind: "token",
1494
- key: `main:${token.symbol}`,
1337
+ ...groupedTokens.withBalance.map((token) => ({
1495
1338
  token,
1496
1339
  willChangeSrc: false
1497
- });
1498
- }
1499
- if (groupedTokens.willChangeSrcChain.length > 0) {
1500
- out.push({
1501
- kind: "header",
1502
- key: "hdr:willChange",
1503
- title: t2("bridge.willChangeSourceChain")
1504
- });
1505
- for (const token of groupedTokens.willChangeSrcChain) {
1506
- out.push({
1507
- kind: "token",
1508
- key: `will:${token.symbol}`,
1509
- token,
1510
- willChangeSrc: true
1511
- });
1512
- }
1513
- }
1514
- return out;
1515
- }, [effectiveTab, myTokens, groupedTokens, t2]);
1516
- 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: [
1517
1357
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("bridge.selectToken") }) }),
1518
- /* @__PURE__ */ jsxs(
1519
- "div",
1358
+ /* @__PURE__ */ jsx(
1359
+ SearchInput,
1520
1360
  {
1521
- 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"}`,
1522
- children: [
1523
- /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 text-input-icon" }),
1524
- /* @__PURE__ */ jsx(
1525
- Input,
1526
- {
1527
- placeholder: t2("bridge.searchToken"),
1528
- 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",
1529
- value: query,
1530
- onChange: (e) => setQuery(e.target.value),
1531
- onFocus: () => setIsFocused(true),
1532
- onBlur: () => setIsFocused(false)
1533
- }
1534
- )
1535
- ]
1361
+ placeholder: t2("bridge.searchToken"),
1362
+ value: query,
1363
+ onChange: setQuery
1536
1364
  }
1537
1365
  ),
1538
1366
  hasAnyWallet() && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
@@ -1555,20 +1383,52 @@ const TokenSelectModal = ({
1555
1383
  }
1556
1384
  )
1557
1385
  ] }),
1558
- /* @__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: [
1559
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") }),
1560
- /* @__PURE__ */ jsx(
1561
- VirtualizedTokenList,
1562
- {
1563
- nodes: virtualNodes,
1564
- balancesLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1565
- hasAnyWallet: hasAnyWallet(),
1566
- selectedAssetSymbol,
1567
- getBalance,
1568
- getTokenUsdValue,
1569
- onPick
1570
- }
1571
- )
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
+ ] })
1572
1432
  ] }) })
1573
1433
  ] }) });
1574
1434
  };
@@ -1669,7 +1529,7 @@ const SelectTokenButton = ({
1669
1529
  onClick,
1670
1530
  token
1671
1531
  }) => {
1672
- const { t: t2 } = useTranslation();
1532
+ const { t: t2 } = useBridgeTranslation();
1673
1533
  const label = useMemo(() => {
1674
1534
  return token?.symbol ?? t2("bridge.selectToken");
1675
1535
  }, [token, t2]);
@@ -1699,7 +1559,7 @@ const SelectTokenButton = ({
1699
1559
  );
1700
1560
  };
1701
1561
  const FormHeaderComponent = () => {
1702
- const { t: t2 } = useTranslation();
1562
+ const { t: t2 } = useBridgeTranslation();
1703
1563
  const { isOpen, onClose, onOpen } = useModal();
1704
1564
  const {
1705
1565
  isOpen: isOpenSettings,
@@ -1714,7 +1574,7 @@ const FormHeaderComponent = () => {
1714
1574
  const sum = selectedAssetSymbol.toUpperCase();
1715
1575
  return assets.find((a) => a.symbol.toUpperCase() === sum) ?? assets[0];
1716
1576
  }, [assets, selectedAssetSymbol]);
1717
- 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: [
1718
1578
  /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2.5", children: [
1719
1579
  /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t2("bridge.selectToken") }),
1720
1580
  /* @__PURE__ */ jsx(SelectTokenButton, { token: current, onClick: onOpen })
@@ -1745,6 +1605,19 @@ const FormHeaderComponent = () => {
1745
1605
  ] });
1746
1606
  };
1747
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
+ }
1748
1621
  async function fetchQuotes(req) {
1749
1622
  const params = {
1750
1623
  srcChainKey: req.srcChainKey,
@@ -1803,6 +1676,131 @@ async function getQuotesByPriority(req) {
1803
1676
  const priority = req.routePriority || RoutePriority.RECOMMENDED;
1804
1677
  return selectQuoteByPriority(routes, priority);
1805
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
+ }
1806
1804
  function useBridgeQuote() {
1807
1805
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
1808
1806
  const { fromChain, toChain } = useChainsStore();
@@ -2204,7 +2202,7 @@ const SelectNetworkButton = ({
2204
2202
  onClick,
2205
2203
  network
2206
2204
  }) => {
2207
- const { t: t2 } = useTranslation();
2205
+ const { t: t2 } = useBridgeTranslation();
2208
2206
  const label = useMemo(() => {
2209
2207
  return network?.name ?? t2("bridge.selectNetwork");
2210
2208
  }, [network, t2]);
@@ -2301,14 +2299,12 @@ const ChainSelectModal = ({
2301
2299
  allowedItems,
2302
2300
  onChangeChain
2303
2301
  }) => {
2304
- const { t: t2 } = useTranslation();
2302
+ const { t: t2 } = useBridgeTranslation();
2305
2303
  const [query, setQuery] = useState("");
2306
- const [isFocused, setIsFocused] = useState(false);
2307
2304
  const { setFromChain, chains, fromChain, toChain } = useChainsStore();
2308
2305
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2309
2306
  const handleClose = useCallback(() => {
2310
2307
  setQuery("");
2311
- setIsFocused(false);
2312
2308
  onClose();
2313
2309
  }, [onClose]);
2314
2310
  const findCompatibleSrcChain = useCallback(
@@ -2368,7 +2364,7 @@ const ChainSelectModal = ({
2368
2364
  Button,
2369
2365
  {
2370
2366
  onClick: () => onChainPick(chain, willChangeSrc),
2371
- 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" : ""}`,
2372
2368
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
2373
2369
  /* @__PURE__ */ jsx(
2374
2370
  NetworkSymbol,
@@ -2384,26 +2380,16 @@ const ChainSelectModal = ({
2384
2380
  chain.chainKey
2385
2381
  );
2386
2382
  };
2387
- 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: [
2388
2384
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t2("bridge.selectNetwork") }) }),
2389
- /* @__PURE__ */ jsxs(
2390
- "div",
2385
+ /* @__PURE__ */ jsx(
2386
+ SearchInput,
2391
2387
  {
2392
- 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"}`,
2393
- children: [
2394
- /* @__PURE__ */ jsx(SearchIcon, { className: "size-6 text-muted-foreground" }),
2395
- /* @__PURE__ */ jsx(
2396
- Input,
2397
- {
2398
- placeholder: t2("bridge.searchDestinationChain"),
2399
- className: "w-full outline-none leading-0 p-0 h-6 text-base text-foreground placeholder:text-muted-foreground bg-none dark:bg-transparent",
2400
- value: query,
2401
- onChange: (e) => setQuery(e.target.value),
2402
- onFocus: () => setIsFocused(true),
2403
- onBlur: () => setIsFocused(false)
2404
- }
2405
- )
2406
- ]
2388
+ placeholder: t2("bridge.searchDestinationChain"),
2389
+ value: query,
2390
+ onChange: setQuery,
2391
+ containerClassName: "rounded-md",
2392
+ className: "text-foreground placeholder:text-muted-foreground"
2407
2393
  }
2408
2394
  ),
2409
2395
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
@@ -2446,7 +2432,7 @@ const WalletButton = ({
2446
2432
  wallet,
2447
2433
  addressType
2448
2434
  }) => {
2449
- const { t: t2 } = useTranslation();
2435
+ const { t: t2 } = useBridgeTranslation();
2450
2436
  const { onOpen } = useWalletSelectModal();
2451
2437
  const { chainRegistry } = useChainStrategies();
2452
2438
  const walletType = mapWalletToType(wallet);
@@ -2618,63 +2604,30 @@ const useCustomAddressStore = create$1((set) => ({
2618
2604
  setCustomDstAddress: (address) => set({ customDstAddress: address }),
2619
2605
  clearCustomDstAddress: () => set({ customDstAddress: void 0 })
2620
2606
  }));
2621
- const tonNormalize = (addr) => {
2622
- if (!addr) return null;
2623
- try {
2624
- if (addr.includes(":")) {
2625
- return Address.parseRaw(addr).toString({
2626
- bounceable: false,
2627
- urlSafe: true
2628
- });
2629
- }
2630
- return Address.parse(addr).toString({ bounceable: false, urlSafe: true });
2631
- } catch {
2632
- return null;
2633
- }
2607
+ const useIsAddressValid = (address, chainKey) => {
2608
+ const isValid = useMemo(() => {
2609
+ return isAddressValidForChain(chainKey, address);
2610
+ }, [address, chainKey]);
2611
+ return { isValid };
2634
2612
  };
2635
- const ToggleRow = ({ enabled, onToggle }) => {
2636
- const { t: t2 } = useTranslation();
2613
+ const AnotherAddress = () => {
2614
+ const [enabled, setEnabled] = useState(false);
2615
+ const [isFocused, setIsFocused] = useState(false);
2616
+ const { t: t2 } = useBridgeTranslation();
2637
2617
  const { toChain } = useChainsStore();
2638
- const { dstAddress } = useAddresses();
2639
- const { setCustomDstAddress, clearCustomDstAddress } = useCustomAddressStore();
2618
+ const { setCustomDstAddress } = useCustomAddressStore();
2640
2619
  const [value, setValue] = useState("");
2641
- const [invalid, setInvalid] = useState(false);
2642
- const prevDstRef = useRef(void 0);
2643
- const prevEnabledRef = useRef(enabled);
2644
- useEffect(() => {
2645
- const wasEnabled = prevEnabledRef.current;
2646
- if (enabled && !wasEnabled) {
2647
- prevDstRef.current = dstAddress;
2648
- setValue(dstAddress ?? "");
2649
- setInvalid(false);
2650
- }
2651
- if (!enabled && wasEnabled) {
2652
- clearCustomDstAddress();
2653
- setInvalid(false);
2654
- }
2655
- prevEnabledRef.current = enabled;
2656
- }, [enabled]);
2620
+ const { isValid } = useIsAddressValid(value.trim(), toChain?.chainKey);
2621
+ const invalid = value.trim() && !isValid;
2657
2622
  const pushToStoreIfValid = (raw) => {
2658
2623
  if (!enabled) return;
2659
2624
  const v = raw.trim();
2660
2625
  if (!v) {
2661
2626
  setCustomDstAddress(void 0);
2662
- setInvalid(false);
2663
- return;
2664
- }
2665
- const ck = toChain?.chainKey;
2666
- if (!ck) {
2667
- setInvalid(true);
2668
2627
  return;
2669
2628
  }
2670
- const valueForStore = ck === "ton" ? tonNormalize(v) ?? void 0 : v;
2671
- const valid = isAddressValidForChain(ck, valueForStore ?? "");
2672
- if (valid && valueForStore) {
2673
- setInvalid(false);
2674
- setCustomDstAddress(valueForStore);
2675
- if (ck === "ton") setValue(valueForStore);
2676
- } else {
2677
- setInvalid(true);
2629
+ if (isValid) {
2630
+ setCustomDstAddress(v);
2678
2631
  }
2679
2632
  };
2680
2633
  const onChange = (text) => {
@@ -2689,7 +2642,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2689
2642
  } catch {
2690
2643
  }
2691
2644
  };
2692
- 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: [
2693
2646
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2694
2647
  /* @__PURE__ */ jsx("p", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: t2("bridge.sendToAnotherAddress") }),
2695
2648
  /* @__PURE__ */ jsx(
@@ -2698,7 +2651,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2698
2651
  className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2699
2652
  "aria-pressed": enabled,
2700
2653
  checked: enabled,
2701
- onClick: onToggle
2654
+ onClick: () => setEnabled((v) => !v)
2702
2655
  }
2703
2656
  )
2704
2657
  ] }),
@@ -2713,16 +2666,27 @@ const ToggleRow = ({ enabled, onToggle }) => {
2713
2666
  children: /* @__PURE__ */ jsxs(
2714
2667
  "div",
2715
2668
  {
2716
- 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
+ ),
2717
2677
  children: [
2718
2678
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
2719
2679
  /* @__PURE__ */ jsx(
2720
2680
  Input,
2721
2681
  {
2722
- 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",
2723
- 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"),
2724
2686
  type: "text",
2725
2687
  value,
2688
+ onFocus: () => setIsFocused(true),
2689
+ onBlur: () => setIsFocused(false),
2726
2690
  onChange: (e) => onChange(e.target.value)
2727
2691
  }
2728
2692
  ),
@@ -2732,21 +2696,14 @@ const ToggleRow = ({ enabled, onToggle }) => {
2732
2696
  defaultValue: "Check correctness before transfer"
2733
2697
  }) }) })
2734
2698
  ] }),
2735
- !value ? /* @__PURE__ */ jsx(
2736
- Button,
2737
- {
2738
- variant: "default",
2739
- 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",
2740
- onClick: onPaste,
2741
- children: t2("common.paste")
2742
- }
2743
- ) : /* @__PURE__ */ jsx(
2699
+ !value ? /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: onPaste, children: t2("common.paste") }) : /* @__PURE__ */ jsx(
2744
2700
  Button,
2745
2701
  {
2746
2702
  variant: "ghost",
2747
- 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",
2748
2705
  onClick: () => setValue(""),
2749
- children: /* @__PURE__ */ jsx(X, { className: "size-3 text-input-x" })
2706
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
2750
2707
  }
2751
2708
  )
2752
2709
  ]
@@ -2859,7 +2816,7 @@ function getRouteDisplayName(route) {
2859
2816
  return route.split(/[/\-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
2860
2817
  }
2861
2818
  const Details = () => {
2862
- const { t: t2 } = useTranslation();
2819
+ const { t: t2 } = useBridgeTranslation();
2863
2820
  const { selectedAssetSymbol, assetMatrix, tokens } = useTokensStore();
2864
2821
  const { toChain, fromChain, chains } = useChainsStore();
2865
2822
  const { quote, status } = useBridgeQuoteStore();
@@ -3505,7 +3462,7 @@ function useSilentValidations(amountString) {
3505
3462
  return validationResult;
3506
3463
  }
3507
3464
  const SubmitButton = () => {
3508
- const { t: t2 } = useTranslation();
3465
+ const { t: t2 } = useBridgeTranslation();
3509
3466
  const { chainRegistry } = useChainStrategies();
3510
3467
  const { srcAddress, dstAddress } = useAddresses();
3511
3468
  const { quote, status, inputAmount, noRoute } = useBridgeQuoteStore();
@@ -3600,13 +3557,22 @@ const SubmitButton = () => {
3600
3557
  }
3601
3558
  };
3602
3559
  const disabled = isBusy || amountNum <= 0 || status === "loading" || isBalanceLoading || hasInsufficientBalance || hasAmountTooLarge || !gas.hasEnoughGas || noRoute || !isValidForTransfer;
3603
- return /* @__PURE__ */ jsx(Button, { onClick: handleClick, disabled, className: "w-full mt-4", children: label });
3560
+ return /* @__PURE__ */ jsx(
3561
+ Button,
3562
+ {
3563
+ onClick: handleClick,
3564
+ disabled,
3565
+ size: "lg",
3566
+ className: "w-full mt-4",
3567
+ children: label
3568
+ }
3569
+ );
3604
3570
  };
3605
3571
  function short(addr) {
3606
3572
  return addr.slice(0, 4) + "…" + addr.slice(-4);
3607
3573
  }
3608
3574
  const WalletSelectModal = () => {
3609
- const { t: t2 } = useTranslation();
3575
+ const { t: t2 } = useBridgeTranslation();
3610
3576
  const { isOpen, onClose } = useWalletSelectModal();
3611
3577
  const { connect, connectors, isPending } = useConnect();
3612
3578
  const { chainRegistry } = useChainStrategies();
@@ -3715,7 +3681,7 @@ const WalletSelectModal = () => {
3715
3681
  /* @__PURE__ */ jsx("div", { className: "py-2 font-semibold text-muted-foreground uppercase", children: t2("wallets.connected") }),
3716
3682
  /* @__PURE__ */ jsx("div", { className: "", children: connectedWallets.map((wallet) => {
3717
3683
  const IconComponent = wallet.icon;
3718
- 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: [
3719
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: [
3720
3686
  /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3721
3687
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -3756,7 +3722,7 @@ const WalletSelectModal = () => {
3756
3722
  }
3757
3723
  },
3758
3724
  disabled: isEvmConnector ? isPending : !wallet.enabled,
3759
- 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",
3760
3726
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3761
3727
  /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
3762
3728
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -5465,7 +5431,7 @@ var fireworksExports = requireFireworks();
5465
5431
  const Fireworks = /* @__PURE__ */ getDefaultExportFromCjs(fireworksExports);
5466
5432
  const SuccessStep = () => {
5467
5433
  const { current, reset } = useTransactionStore();
5468
- const { t: t2 } = useTranslation();
5434
+ const { t: t2 } = useBridgeTranslation();
5469
5435
  const metadata = current?.metadata;
5470
5436
  const srcTxHash = current?.srcTxHash;
5471
5437
  const handleCopyHash = () => {
@@ -5684,7 +5650,7 @@ const useCountdown = (initialSeconds) => {
5684
5650
  };
5685
5651
  };
5686
5652
  const ConfirmStep = () => {
5687
- const { t: t2 } = useTranslation();
5653
+ const { t: t2 } = useBridgeTranslation();
5688
5654
  const { formatTime } = useCountdown(90);
5689
5655
  return /* @__PURE__ */ jsx(Card, { className: "flex flex-col border-none h-full bg-background overflow-hidden rounded-none md:rounded-lg", children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col relative gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise", children: [
5690
5656
  /* @__PURE__ */ jsx(TransactionConfirmVector, {}),
@@ -5807,7 +5773,7 @@ class ChainStrategyRegistry {
5807
5773
  async getBalances(chainKey, address, tokens, priorityToken) {
5808
5774
  const strategy = this.getStrategy(chainKey);
5809
5775
  if (!strategy) return {};
5810
- return await strategy.getBalances(address, tokens, priorityToken);
5776
+ return await strategy.getBalances(address, tokens, chainKey, priorityToken);
5811
5777
  }
5812
5778
  isAddressValid(chainKey, address) {
5813
5779
  const strategy = this.getStrategy(chainKey);
@@ -5918,14 +5884,9 @@ function parseTonAddress(address) {
5918
5884
  }
5919
5885
  return Address$1.parse(address);
5920
5886
  }
5921
- async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5887
+ async function getEvmBalances(publicClient, address, tokens, chainKey, priorityToken) {
5922
5888
  const balances = {};
5923
5889
  try {
5924
- console.log("start getEvmBalances");
5925
- console.log("publicClient:", publicClient);
5926
- console.log("isAddress:", isAddress(address));
5927
- console.log("tokens:", tokens);
5928
- console.log("priorityToken:", priorityToken);
5929
5890
  if (!address || !isAddress(address)) {
5930
5891
  console.warn(`Invalid EVM address provided: ${address}`);
5931
5892
  return balances;
@@ -5934,40 +5895,55 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5934
5895
  throw new Error("No public client provided");
5935
5896
  }
5936
5897
  const nativeTokens = tokens.filter((t2) => isNativeAddress(t2.address));
5937
- 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
+ );
5938
5901
  if (priorityToken) {
5939
- try {
5940
- const isPriorityNative = isNativeAddress(priorityToken.address);
5941
- if (isPriorityNative) {
5942
- const ethBalance = await publicClient.getBalance({
5943
- address
5944
- });
5945
- const balance = parseFloat(formatUnits(ethBalance, priorityToken.decimals));
5946
- if (balance > 0) {
5947
- balances[priorityToken.symbol] = { balance, address };
5948
- }
5949
- } else if (isAddress(priorityToken.address)) {
5950
- const tokenBalance = await publicClient.readContract({
5951
- address: priorityToken.address,
5952
- abi: [
5953
- {
5954
- name: "balanceOf",
5955
- type: "function",
5956
- stateMutability: "view",
5957
- inputs: [{ name: "owner", type: "address" }],
5958
- outputs: [{ name: "balance", type: "uint256" }]
5959
- }
5960
- ],
5961
- functionName: "balanceOf",
5962
- args: [address]
5963
- });
5964
- const balance = parseFloat(formatUnits(tokenBalance, priorityToken.decimals));
5965
- if (balance > 0) {
5966
- 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
+ }
5967
5940
  }
5941
+ } catch (error) {
5942
+ console.debug(
5943
+ `Failed to get priority token balance for ${priorityToken.symbol}:`,
5944
+ error
5945
+ );
5968
5946
  }
5969
- } catch (error) {
5970
- console.debug(`Failed to get priority token balance for ${priorityToken.symbol}:`, error);
5971
5947
  }
5972
5948
  }
5973
5949
  for (const token of nativeTokens) {
@@ -5980,7 +5956,10 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5980
5956
  balances[token.symbol] = { balance, address };
5981
5957
  }
5982
5958
  } catch (error) {
5983
- 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
+ );
5984
5963
  }
5985
5964
  }
5986
5965
  if (erc20Tokens.length > 0) {
@@ -6008,17 +5987,25 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6008
5987
  if (!token) return;
6009
5988
  if (result.status === "success" && result.result !== void 0) {
6010
5989
  try {
6011
- const balance = parseFloat(formatUnits(result.result, token.decimals));
5990
+ const balance = parseFloat(
5991
+ formatUnits(result.result, token.decimals)
5992
+ );
6012
5993
  if (balance > 0) {
6013
5994
  balances[token.symbol] = { balance, address };
6014
5995
  }
6015
5996
  } catch (error) {
6016
- console.debug(`Failed to parse balance for ${token.symbol}:`, error);
5997
+ console.debug(
5998
+ `Failed to parse balance for ${token.symbol}:`,
5999
+ error
6000
+ );
6017
6001
  }
6018
6002
  }
6019
6003
  });
6020
6004
  } catch (error) {
6021
- console.warn("Multicall failed, falling back to individual calls:", error);
6005
+ console.warn(
6006
+ "Multicall failed, falling back to individual calls:",
6007
+ error
6008
+ );
6022
6009
  for (const token of erc20Tokens) {
6023
6010
  try {
6024
6011
  const tokenBalance = await publicClient.readContract({
@@ -6052,7 +6039,7 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6052
6039
  }
6053
6040
  return balances;
6054
6041
  }
6055
- async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
6042
+ async function getTonBalances(address, tokens, chainKey, customTonClient, tonApiKey) {
6056
6043
  const balances = {};
6057
6044
  try {
6058
6045
  if (!isTonFriendlyAddress(address)) {
@@ -6211,13 +6198,18 @@ class EvmChainStrategy {
6211
6198
  getConnectLabel(t2) {
6212
6199
  return t2("wallets.connectEvmWallet");
6213
6200
  }
6214
- async getBalances(address, tokens, priorityToken) {
6201
+ async getBalances(address, tokens, chainKey, priorityToken) {
6215
6202
  if (!this.publicClient) {
6216
6203
  console.warn("No publicClient available for balance query");
6217
6204
  return {};
6218
6205
  }
6219
- console.log("publicClient", this.publicClient);
6220
- return await getEvmBalances(this.publicClient, address, tokens, priorityToken);
6206
+ return await getEvmBalances(
6207
+ this.publicClient,
6208
+ address,
6209
+ tokens,
6210
+ chainKey,
6211
+ priorityToken
6212
+ );
6221
6213
  }
6222
6214
  isAddressValid(address) {
6223
6215
  if (!address) return false;
@@ -6573,10 +6565,11 @@ class TonChainStrategy {
6573
6565
  getConnectLabel(t2) {
6574
6566
  return t2("wallets.connectTonWallet");
6575
6567
  }
6576
- async getBalances(address, tokens) {
6568
+ async getBalances(address, tokens, chainKey) {
6577
6569
  return await getTonBalances(
6578
6570
  address,
6579
6571
  tokens,
6572
+ chainKey,
6580
6573
  this.config.tonClient,
6581
6574
  this.config.tonApiKey
6582
6575
  );
@@ -6659,12 +6652,12 @@ class TonChainStrategy {
6659
6652
  payload: msg.payload
6660
6653
  })
6661
6654
  );
6662
- const transaction = {
6655
+ const transaction2 = {
6663
6656
  validUntil: Math.floor(Date.now() / 1e3) + TON_CONFIG.validUntil,
6664
6657
  messages: tonMessages
6665
6658
  };
6666
6659
  const result = await this.config.tonConnectUI.sendTransaction(
6667
- transaction
6660
+ transaction2
6668
6661
  );
6669
6662
  return {
6670
6663
  chainKey: "ton",
@@ -6785,7 +6778,6 @@ class TronChainStrategy {
6785
6778
  __publicField(this, "config");
6786
6779
  this.config = config;
6787
6780
  }
6788
- // ========== Identity ==========
6789
6781
  canHandle(chainKey) {
6790
6782
  return chainKey.toLowerCase() === "tron";
6791
6783
  }
@@ -6795,7 +6787,6 @@ class TronChainStrategy {
6795
6787
  getName() {
6796
6788
  return "TRON Chain Strategy";
6797
6789
  }
6798
- // ========== Wallet Management ==========
6799
6790
  async connect() {
6800
6791
  const tronWeb = this.getTronWeb();
6801
6792
  if (!tronWeb && (typeof window === "undefined" || !window.tronLink)) {
@@ -6826,7 +6817,6 @@ class TronChainStrategy {
6826
6817
  getConnectLabel(t2) {
6827
6818
  return t2("wallets.connectTronWallet");
6828
6819
  }
6829
- // ========== Balance & Validation ==========
6830
6820
  async getBalances(address, tokens) {
6831
6821
  const tronWeb = this.getTronWeb();
6832
6822
  if (!tronWeb) return {};
@@ -6836,7 +6826,6 @@ class TronChainStrategy {
6836
6826
  if (!address) return false;
6837
6827
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
6838
6828
  }
6839
- // ========== Gas Estimation ==========
6840
6829
  async estimateGasRequirement(params) {
6841
6830
  const {
6842
6831
  selectedToken,
@@ -7483,7 +7472,7 @@ const EvaaBridgeWithProviders = (props) => {
7483
7472
  useEffect(() => {
7484
7473
  setTronConnected(!!tronConnected);
7485
7474
  }, [tronConnected, setTronConnected]);
7486
- return /* @__PURE__ */ jsx(
7475
+ return /* @__PURE__ */ jsx(BridgeI18nProvider, { defaultLanguage: props.defaultLanguage, children: /* @__PURE__ */ jsx(
7487
7476
  ChainStrategyProvider,
7488
7477
  {
7489
7478
  evmWallet: {
@@ -7508,7 +7497,7 @@ const EvaaBridgeWithProviders = (props) => {
7508
7497
  tonApiKey: props.tonApiKey,
7509
7498
  children: /* @__PURE__ */ jsx(EvaaBridgeContent, { ...props })
7510
7499
  }
7511
- );
7500
+ ) });
7512
7501
  };
7513
7502
  const EvaaBridgeContent = ({
7514
7503
  className,
@@ -7516,10 +7505,9 @@ const EvaaBridgeContent = ({
7516
7505
  onAmountChange,
7517
7506
  onChainChange
7518
7507
  } = {}) => {
7519
- const { t: t2 } = useTranslation();
7508
+ const { t: t2 } = useBridgeTranslation();
7520
7509
  useTokensRequest();
7521
7510
  useChainsRequest();
7522
- const [sendToAnother, setSendToAnother] = useState(false);
7523
7511
  const swap = useSwapModel();
7524
7512
  const { fromChain, toChain } = swap;
7525
7513
  const { selectedAssetSymbol, assetMatrix } = useTokensStore();
@@ -7647,13 +7635,7 @@ const EvaaBridgeContent = ({
7647
7635
  onSelect: handleToChainChange
7648
7636
  }
7649
7637
  ),
7650
- /* @__PURE__ */ jsx(
7651
- ToggleRow,
7652
- {
7653
- enabled: sendToAnother,
7654
- onToggle: () => setSendToAnother((v) => !v)
7655
- }
7656
- ),
7638
+ /* @__PURE__ */ jsx(AnotherAddress, {}),
7657
7639
  /* @__PURE__ */ jsx(SubmitButton, {})
7658
7640
  ] }),
7659
7641
  /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(Details, {}) })
@@ -7729,7 +7711,6 @@ export {
7729
7711
  buildAssetMatrix,
7730
7712
  calculateMinReceived,
7731
7713
  computeFeesUsdFromArray,
7732
- dollarsFromNativeFees,
7733
7714
  findNativeMeta,
7734
7715
  formatBalance,
7735
7716
  formatHash,
@@ -7748,10 +7729,7 @@ export {
7748
7729
  getTokens,
7749
7730
  getTonBalances,
7750
7731
  getTronBalances,
7751
- isAddressValidForChain,
7752
- isEvmAddress,
7753
7732
  isNativeAddrEqual,
7754
- isTronAddress,
7755
7733
  isZeroAddr,
7756
7734
  listAssetsForSelect,
7757
7735
  lookupTokenMeta,