@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.
@@ -25,32 +25,97 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
26
26
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
27
27
  const jsxRuntime = require("react/jsx-runtime");
28
- const reactI18next = require("react-i18next");
29
28
  const require$$0 = require("react");
29
+ const reactI18next = require("react-i18next");
30
+ const i18n = require("i18next");
30
31
  const zustand = require("zustand");
31
32
  const button = require("@/components/ui/button");
32
33
  const dialog = require("@/components/ui/dialog");
34
+ const badge = require("@/components/ui/badge");
33
35
  const tooltip = require("@/components/ui/tooltip");
34
36
  const utils = require("@/lib/utils");
35
- const input = require("@/components/ui/input");
37
+ const skeleton = require("@/components/ui/skeleton");
36
38
  const wagmi = require("wagmi");
37
39
  const tronwalletAdapterReactHooks = require("@tronweb3/tronwallet-adapter-react-hooks");
38
40
  const uiReact = require("@tonconnect/ui-react");
39
41
  const reactQuery = require("@tanstack/react-query");
40
42
  const core = require("@ton/core");
41
- const reactWindow = require("react-window");
42
- const skeleton = require("@/components/ui/skeleton");
43
+ const input = require("@/components/ui/input");
43
44
  const card = require("@/components/ui/card");
44
45
  const _switch = require("@/components/ui/switch");
45
46
  const lucideReact = require("lucide-react");
46
47
  const framerMotion = require("framer-motion");
47
48
  const accordion = require("@/components/ui/accordion");
48
- const i18next = require("i18next");
49
49
  const sonner = require("sonner");
50
50
  const ethers = require("ethers");
51
51
  const viem = require("viem");
52
52
  const ton = require("@ton/ton");
53
53
  const tronwalletAdapters = require("@tronweb3/tronwallet-adapters");
54
+ const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
55
+ 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" };
56
+ 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" };
57
+ 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" };
58
+ const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
59
+ 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" } };
60
+ const en = {
61
+ common: common$1,
62
+ wallets: wallets$1,
63
+ bridge: bridge$1,
64
+ transaction: transaction$1,
65
+ app: app$1,
66
+ settings: settings$1
67
+ };
68
+ const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
69
+ 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": "Подключить кошелёк" };
70
+ const bridge = { "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
71
+ 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": "Ошибка котировки" };
72
+ const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
73
+ const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
74
+ const ru = {
75
+ common,
76
+ wallets,
77
+ bridge,
78
+ transaction,
79
+ app,
80
+ settings
81
+ };
82
+ const bridgeI18n = i18n.createInstance();
83
+ const resources = {
84
+ en: {
85
+ "evaa-bridge": en
86
+ },
87
+ ru: {
88
+ "evaa-bridge": ru
89
+ }
90
+ };
91
+ bridgeI18n.use(reactI18next.initReactI18next).init({
92
+ resources,
93
+ lng: "en",
94
+ // Will be overridden by defaultLanguage prop
95
+ fallbackLng: "en",
96
+ debug: false,
97
+ // Use a dedicated namespace to avoid conflicts
98
+ defaultNS: "evaa-bridge",
99
+ ns: ["evaa-bridge"],
100
+ interpolation: {
101
+ escapeValue: false
102
+ // react already does escaping
103
+ }
104
+ });
105
+ function BridgeI18nProvider({
106
+ children,
107
+ defaultLanguage = "en"
108
+ }) {
109
+ require$$0.useEffect(() => {
110
+ if (bridgeI18n.language !== defaultLanguage) {
111
+ bridgeI18n.changeLanguage(defaultLanguage);
112
+ }
113
+ }, [defaultLanguage]);
114
+ return /* @__PURE__ */ jsxRuntime.jsx(reactI18next.I18nextProvider, { i18n: bridgeI18n, children });
115
+ }
116
+ function useBridgeTranslation() {
117
+ return reactI18next.useTranslation("evaa-bridge", { i18n: bridgeI18n });
118
+ }
54
119
  const norm = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
55
120
  const POPULAR_ORDER = [
56
121
  "USDT",
@@ -831,7 +896,7 @@ const routePresets = [
831
896
  RoutePriority.RECOMMENDED
832
897
  ];
833
898
  const SettingModal = ({ isOpen, onClose }) => {
834
- const { t } = reactI18next.useTranslation();
899
+ const { t } = useBridgeTranslation();
835
900
  const { toChain } = useChainsStore();
836
901
  const { tokens } = useTokensStore();
837
902
  const {
@@ -861,16 +926,16 @@ const SettingModal = ({ isOpen, onClose }) => {
861
926
  toChain?.chainKey,
862
927
  dstNativeToken?.decimals || 18
863
928
  );
864
- const activeBtn = "bg-settings-active hover:bg-settings-active/80 text-settings-active-foreground";
865
- const notActiveBtn = "bg-settings-button hover:bg-settings-button/80 text-settings-button-foreground";
929
+ const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
930
+ const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
866
931
  return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { children: [
867
- /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("settings.title", { defaultValue: "Settings" }) }) }),
932
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("settings.title") }) }),
868
933
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5", children: [
869
934
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5", children: [
870
935
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
871
936
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
872
937
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t("settings.gasOnDestination") }),
873
- /* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsxRuntime.jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
938
+ /* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestinationTip"), children: /* @__PURE__ */ jsxRuntime.jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
874
939
  ] }),
875
940
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground text-sm font-medium leading-3.5", children: formatUsd(gasUsdValue) })
876
941
  ] }),
@@ -889,12 +954,11 @@ const SettingModal = ({ isOpen, onClose }) => {
889
954
  ) }) })
890
955
  ] }),
891
956
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: gasPresets.map((g) => /* @__PURE__ */ jsxRuntime.jsx(
892
- button.Button,
957
+ badge.Badge,
893
958
  {
894
- type: "button",
895
959
  onClick: () => setGasPreset(g),
896
960
  className: utils.cn(
897
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
961
+ "cursor-pointer",
898
962
  gasPreset === g ? activeBtn : notActiveBtn
899
963
  ),
900
964
  children: t(`settings.gasPresets.${g}`)
@@ -910,22 +974,21 @@ const SettingModal = ({ isOpen, onClose }) => {
910
974
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-3.5", children: t("settings.slippageTolerance") }),
911
975
  /* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsxRuntime.jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
912
976
  ] }),
913
- slippageBps >= 500 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-orange-500 text-xs font-medium", children: t("settings.highSlippageWarning", {
977
+ slippageBps >= 500 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
914
978
  defaultValue: "High slippage warning"
915
979
  }) })
916
980
  ] }),
917
981
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-6", children: [
918
982
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-foreground leading-4.5 font-semibold h-4.5", children: slippagePercent }) }),
919
983
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
920
- button.Button,
984
+ badge.Badge,
921
985
  {
922
- type: "button",
923
986
  onClick: () => {
924
987
  const bps = parseFloat(p.replace("%", "")) * 100;
925
988
  setSlippageBps(bps);
926
989
  },
927
990
  className: utils.cn(
928
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
991
+ "cursor-pointer",
929
992
  activeSlippagePreset === p ? activeBtn : notActiveBtn
930
993
  ),
931
994
  children: p
@@ -941,12 +1004,11 @@ const SettingModal = ({ isOpen, onClose }) => {
941
1004
  /* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsxRuntime.jsx(TipIcon, { className: "size-4 text-muted-foreground" }) })
942
1005
  ] }) }),
943
1006
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
944
- button.Button,
1007
+ badge.Badge,
945
1008
  {
946
- type: "button",
947
1009
  onClick: () => setRoutePriority(r),
948
1010
  className: utils.cn(
949
- `cursor-pointer rounded-6 px-2 py-2.5 h-7 text-xs font-semibold leading-2 transition`,
1011
+ "cursor-pointer",
950
1012
  routePriority === r ? activeBtn : notActiveBtn
951
1013
  ),
952
1014
  children: t(`settings.routePresets.${r}`)
@@ -1014,168 +1076,31 @@ function useChainStrategies() {
1014
1076
  }
1015
1077
  return context;
1016
1078
  }
1017
- function toLD(human, decimals) {
1018
- const [i = "0", f = ""] = human.replace(",", ".").split(".");
1019
- const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1020
- return BigInt(i + frac).toString();
1021
- }
1022
- function fromLD(ld, decimals) {
1023
- const bi = BigInt(ld || "0");
1024
- if (decimals === 0) return Number(bi);
1025
- const base = BigInt(10) ** BigInt(decimals);
1026
- const int = bi / base;
1027
- const frac = Number(bi % base) / Number(base);
1028
- return Number(int) + frac;
1029
- }
1030
- function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1031
- if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1032
- const byChain = assetMatrix[assetSymbol.toUpperCase()];
1033
- return byChain?.[chainKey];
1034
- }
1035
- const DEFAULT_SLIPPAGE_BPS = 50;
1036
- const lower = (s) => (s ?? "").toLowerCase();
1037
- const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1038
- function tonNorm(addr) {
1039
- if (!addr) return null;
1079
+ const isEvmAddress = (addr) => {
1080
+ return /^0x[0-9a-fA-F]{40}$/.test(addr ?? "");
1081
+ };
1082
+ const isTronAddress = (addr) => {
1083
+ return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(addr ?? "");
1084
+ };
1085
+ const isTonAddress = (addr) => {
1086
+ if (!addr) return false;
1040
1087
  try {
1041
1088
  if (addr.includes(":")) {
1042
- return core.Address.parseRaw(addr).toString({
1043
- bounceable: false,
1044
- urlSafe: true
1045
- });
1089
+ core.Address.parseRaw(addr);
1090
+ return true;
1046
1091
  }
1047
- return core.Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1092
+ core.Address.parse(addr);
1093
+ return true;
1048
1094
  } catch {
1049
- return null;
1095
+ return false;
1050
1096
  }
1051
- }
1052
- const isEvmAddress = (a) => /^0x[0-9a-fA-F]{40}$/.test(a ?? "");
1053
- const isTronAddress = (a) => /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(a ?? "");
1054
- const isZeroAddr = (a) => (
1055
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1056
- !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1057
- );
1097
+ };
1058
1098
  function isAddressValidForChain(chainKey, addr) {
1059
1099
  if (!chainKey || !addr) return false;
1060
- if (chainKey === "ton") return tonNorm(addr) !== null;
1100
+ if (chainKey === "ton") return isTonAddress(addr);
1061
1101
  if (chainKey === "tron") return isTronAddress(addr);
1062
1102
  return isEvmAddress(addr);
1063
1103
  }
1064
- function addrForApi(chainKey, addr) {
1065
- if (chainKey === "ton") return tonNorm(addr);
1066
- return addr;
1067
- }
1068
- function isNativeAddrEqual(chain, tokenAddr) {
1069
- if (!chain) return false;
1070
- if (!tokenAddr) return false;
1071
- if (isZeroAddr(tokenAddr)) return true;
1072
- const nativeAddr = chain.nativeCurrency?.address;
1073
- if (!nativeAddr) return false;
1074
- if (chain.chainKey === "ton") {
1075
- const a = tonNorm(tokenAddr);
1076
- const b = tonNorm(nativeAddr);
1077
- return !!a && !!b && a === b;
1078
- }
1079
- return lower(nativeAddr) === lower(tokenAddr);
1080
- }
1081
- function findNativeMeta(tokens, chain) {
1082
- if (!chain) return { decimals: 18, priceUsd: void 0 };
1083
- const sym = normSym(chain.nativeCurrency?.symbol);
1084
- if (!sym) {
1085
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1086
- }
1087
- const sameChain = tokens?.find(
1088
- (t) => t.chainKey === chain.chainKey && normSym(t.symbol) === sym
1089
- ) ?? void 0;
1090
- if (sameChain)
1091
- return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1092
- const anyChain = tokens?.find((t) => normSym(t.symbol) === sym);
1093
- if (anyChain)
1094
- return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1095
- return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1096
- }
1097
- function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1098
- if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1099
- const chain = chains?.find((c) => c.chainKey === chainKey);
1100
- const hit = tokens?.find((t) => {
1101
- if (t.chainKey !== chainKey) return false;
1102
- if (chainKey === "ton") {
1103
- const a = tonNorm(t.address);
1104
- const b = tonNorm(tokenAddr);
1105
- return !!a && !!b && a === b;
1106
- }
1107
- return lower(t.address) === lower(tokenAddr);
1108
- }) ?? void 0;
1109
- if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1110
- if (isNativeAddrEqual(chain, tokenAddr)) {
1111
- return findNativeMeta(tokens, chain);
1112
- }
1113
- return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1114
- }
1115
- function computeFeesUsdFromArray(fees, tokens, chains) {
1116
- if (!fees?.length) return { totalUsd: 0 };
1117
- let total = 0;
1118
- let protocolFeeUsd = 0;
1119
- let messageFeeUsd = 0;
1120
- const byType = /* @__PURE__ */ new Map();
1121
- for (const f of fees) {
1122
- let usd = Number(f.usd ?? 0);
1123
- if (!usd) {
1124
- const { decimals, priceUsd } = lookupTokenMeta(
1125
- tokens,
1126
- chains,
1127
- f.chainKey,
1128
- f.token
1129
- );
1130
- const human = fromLD(String(f.amount ?? "0"), decimals);
1131
- usd = (priceUsd ?? 0) * human;
1132
- }
1133
- total += usd;
1134
- const type = (f.type ?? "other").toLowerCase();
1135
- byType.set(type, (byType.get(type) ?? 0) + usd);
1136
- if (type === "protocol" || type === "service") {
1137
- protocolFeeUsd += usd;
1138
- } else if (type === "message" || type === "gas" || type === "network") {
1139
- messageFeeUsd += usd;
1140
- }
1141
- }
1142
- const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1143
- const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1144
- return {
1145
- totalUsd: total,
1146
- protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1147
- messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1148
- serviceUsd,
1149
- blockchainUsd
1150
- };
1151
- }
1152
- function dollarsFromNativeFees(feeNative, feeLzToken, chain, tokens) {
1153
- let total = 0;
1154
- if (feeNative && chain) {
1155
- const { decimals, priceUsd } = findNativeMeta(tokens, chain);
1156
- const human = fromLD(String(feeNative), decimals);
1157
- total += (priceUsd ?? 0) * human;
1158
- }
1159
- if (feeLzToken) {
1160
- const lz = tokens?.find((t) => normSym(t.symbol) === "LZ");
1161
- if (lz?.price?.usd && lz.decimals != null) {
1162
- const human = fromLD(String(feeLzToken), lz.decimals);
1163
- total += lz.price.usd * human;
1164
- }
1165
- }
1166
- return total;
1167
- }
1168
- function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1169
- if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1170
- let acc = 0n;
1171
- for (const f of fees) {
1172
- if (f.chainKey !== dstChainKey) continue;
1173
- const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1174
- if (!same) continue;
1175
- acc += BigInt(f.amount ?? "0");
1176
- }
1177
- return acc.toString();
1178
- }
1179
1104
  function useBalances(chainKey, address, priorityTokenSymbol) {
1180
1105
  const { chainRegistry } = useChainStrategies();
1181
1106
  const { assetMatrix } = useTokensStore();
@@ -1237,10 +1162,92 @@ function useBalances(chainKey, address, priorityTokenSymbol) {
1237
1162
  query
1238
1163
  };
1239
1164
  }
1240
- function useTokenSelectData(items) {
1165
+ const SearchInput = ({
1166
+ placeholder,
1167
+ value,
1168
+ onChange,
1169
+ className,
1170
+ containerClassName
1171
+ }) => {
1172
+ const [isFocused, setIsFocused] = require$$0.useState(false);
1173
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1174
+ "div",
1175
+ {
1176
+ className: utils.cn(
1177
+ "flex items-center gap-2.5 px-5 py-3.5 bg-input rounded-md h-12.5 transition-all duration-200",
1178
+ isFocused ? "border border-ring" : "border border-transparent",
1179
+ containerClassName
1180
+ ),
1181
+ children: [
1182
+ /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "size-6 text-input-icon" }),
1183
+ /* @__PURE__ */ jsxRuntime.jsx(
1184
+ input.Input,
1185
+ {
1186
+ placeholder,
1187
+ className: utils.cn(
1188
+ "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",
1189
+ className
1190
+ ),
1191
+ value,
1192
+ onChange: (e) => onChange(e.target.value),
1193
+ onFocus: () => setIsFocused(true),
1194
+ onBlur: () => setIsFocused(false)
1195
+ }
1196
+ )
1197
+ ]
1198
+ }
1199
+ );
1200
+ };
1201
+ const TokenRow = ({
1202
+ symbol,
1203
+ name,
1204
+ isSelected,
1205
+ hasAnyWallet,
1206
+ balance,
1207
+ usdValue,
1208
+ isBalanceLoading,
1209
+ onPick
1210
+ }) => {
1211
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1212
+ button.Button,
1213
+ {
1214
+ onClick: onPick,
1215
+ 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" : ""}`,
1216
+ children: [
1217
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1218
+ /* @__PURE__ */ jsxRuntime.jsx(
1219
+ TokenSymbol,
1220
+ {
1221
+ symbol,
1222
+ className: "size-7.5 max-w-7.5 rounded-full",
1223
+ alt: symbol
1224
+ }
1225
+ ),
1226
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1227
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-extrabold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
1228
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
1229
+ ] })
1230
+ ] }),
1231
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
1232
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-5 w-16 rounded-md" }),
1233
+ /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-3 w-12 rounded-md" })
1234
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1235
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-extrabold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
1236
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
1237
+ ] }) })
1238
+ ]
1239
+ }
1240
+ );
1241
+ };
1242
+ const TokenSelectModal = ({
1243
+ isOpen,
1244
+ onClose,
1245
+ items,
1246
+ onChangeAsset
1247
+ }) => {
1248
+ const { t } = useBridgeTranslation();
1241
1249
  const [query, setQuery] = require$$0.useState("");
1242
1250
  const [tab, setTab] = require$$0.useState("my");
1243
- const [isFocused, setIsFocused] = require$$0.useState(false);
1244
1251
  const { srcAddress } = useAddresses();
1245
1252
  const { fromChain, setFromChain, chains } = useChainsStore();
1246
1253
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
@@ -1325,159 +1332,7 @@ function useTokenSelectData(items) {
1325
1332
  const resetState = require$$0.useCallback(() => {
1326
1333
  setQuery("");
1327
1334
  setTab("my");
1328
- setIsFocused(false);
1329
1335
  }, []);
1330
- return {
1331
- query,
1332
- setQuery,
1333
- tab,
1334
- setTab,
1335
- isFocused,
1336
- setIsFocused,
1337
- effectiveTab,
1338
- resetState,
1339
- fromChain,
1340
- setFromChain,
1341
- selectedAssetSymbol,
1342
- balancesQuery,
1343
- groupedTokens,
1344
- myTokens,
1345
- getBalance,
1346
- getTokenUsdValue,
1347
- findFirstAvailableChain,
1348
- hasAnyWallet
1349
- };
1350
- }
1351
- const TokenRow = ({
1352
- symbol,
1353
- name,
1354
- isSelected,
1355
- hasAnyWallet,
1356
- balance,
1357
- usdValue,
1358
- isBalanceLoading,
1359
- onPick
1360
- }) => {
1361
- return /* @__PURE__ */ jsxRuntime.jsxs(
1362
- button.Button,
1363
- {
1364
- onClick: onPick,
1365
- 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" : ""}`,
1366
- children: [
1367
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1368
- /* @__PURE__ */ jsxRuntime.jsx(
1369
- TokenSymbol,
1370
- {
1371
- symbol,
1372
- className: "size-7.5 max-w-7.5 rounded-full",
1373
- alt: symbol
1374
- }
1375
- ),
1376
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1377
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-extrabold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
1378
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
1379
- ] })
1380
- ] }),
1381
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
1382
- /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-5 w-16 rounded-md" }),
1383
- /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-3 w-12 rounded-md" })
1384
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1385
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-extrabold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
1386
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
1387
- ] }) })
1388
- ]
1389
- }
1390
- );
1391
- };
1392
- const HEADER_H = 30;
1393
- const ROW_H = 50;
1394
- const HEADER_TOP_GAP = 30;
1395
- const VirtualizedTokenList = ({
1396
- nodes,
1397
- balancesLoading,
1398
- hasAnyWallet,
1399
- selectedAssetSymbol,
1400
- getBalance,
1401
- getTokenUsdValue,
1402
- onPick
1403
- }) => {
1404
- const Row = ({ index, style }) => {
1405
- const node = nodes[index];
1406
- if (node.kind === "header") {
1407
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1408
- return /* @__PURE__ */ jsxRuntime.jsx(
1409
- "div",
1410
- {
1411
- style: { ...style, paddingTop: gap },
1412
- className: "px-5 flex leading-4 text-base py-1.5 font-semibold text-muted-foreground uppercase",
1413
- children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: node.title })
1414
- }
1415
- );
1416
- }
1417
- const bal = getBalance(node.token.symbol);
1418
- const usd = getTokenUsdValue(node.token.symbol, node.token.price?.usd);
1419
- const isSelected = selectedAssetSymbol?.toUpperCase() === node.token.symbol.toUpperCase();
1420
- return /* @__PURE__ */ jsxRuntime.jsx(
1421
- TokenRow,
1422
- {
1423
- symbol: node.token.symbol,
1424
- name: node.token.name,
1425
- isSelected: !!isSelected,
1426
- hasAnyWallet,
1427
- balance: bal,
1428
- usdValue: usd,
1429
- isBalanceLoading: balancesLoading,
1430
- onPick: () => onPick(node.token.symbol, node.willChangeSrc)
1431
- }
1432
- );
1433
- };
1434
- const getItemSize = (index) => {
1435
- const node = nodes[index];
1436
- if (node.kind === "header") {
1437
- const gap = index === 0 ? 0 : HEADER_TOP_GAP;
1438
- return HEADER_H + gap;
1439
- }
1440
- return ROW_H;
1441
- };
1442
- return /* @__PURE__ */ jsxRuntime.jsx(
1443
- reactWindow.VariableSizeList,
1444
- {
1445
- height: 480,
1446
- width: "100%",
1447
- itemCount: nodes.length,
1448
- itemSize: getItemSize,
1449
- overscanCount: 8,
1450
- style: { willChange: "transform", paddingBottom: "40px" },
1451
- children: Row
1452
- }
1453
- );
1454
- };
1455
- const TokenSelectModal = ({
1456
- isOpen,
1457
- onClose,
1458
- items,
1459
- onChangeAsset
1460
- }) => {
1461
- const { t } = reactI18next.useTranslation();
1462
- const {
1463
- query,
1464
- setQuery,
1465
- tab,
1466
- setTab,
1467
- isFocused,
1468
- setIsFocused,
1469
- effectiveTab,
1470
- resetState,
1471
- setFromChain,
1472
- selectedAssetSymbol,
1473
- balancesQuery,
1474
- groupedTokens,
1475
- myTokens,
1476
- getBalance,
1477
- getTokenUsdValue,
1478
- findFirstAvailableChain,
1479
- hasAnyWallet
1480
- } = useTokenSelectData(items);
1481
1336
  const handleClose = require$$0.useCallback(() => {
1482
1337
  resetState();
1483
1338
  onClose();
@@ -1494,68 +1349,41 @@ const TokenSelectModal = ({
1494
1349
  onChangeAsset(sym);
1495
1350
  handleClose();
1496
1351
  };
1497
- const virtualNodes = require$$0.useMemo(() => {
1498
- const out = [];
1352
+ const tokensToRender = require$$0.useMemo(() => {
1499
1353
  if (effectiveTab === "my") {
1500
- for (const token of myTokens) {
1501
- out.push({
1502
- kind: "token",
1503
- key: `my:${token.symbol}`,
1504
- token,
1505
- willChangeSrc: false
1506
- });
1507
- }
1508
- return out;
1354
+ return myTokens.map((token) => ({
1355
+ token,
1356
+ willChangeSrc: false
1357
+ }));
1509
1358
  }
1510
1359
  const mainTokens = [
1511
- ...groupedTokens.withBalance,
1512
- ...groupedTokens.onSrcChain
1513
- ];
1514
- for (const token of mainTokens) {
1515
- out.push({
1516
- kind: "token",
1517
- key: `main:${token.symbol}`,
1360
+ ...groupedTokens.withBalance.map((token) => ({
1518
1361
  token,
1519
1362
  willChangeSrc: false
1520
- });
1521
- }
1522
- if (groupedTokens.willChangeSrcChain.length > 0) {
1523
- out.push({
1524
- kind: "header",
1525
- key: "hdr:willChange",
1526
- title: t("bridge.willChangeSourceChain")
1527
- });
1528
- for (const token of groupedTokens.willChangeSrcChain) {
1529
- out.push({
1530
- kind: "token",
1531
- key: `will:${token.symbol}`,
1532
- token,
1533
- willChangeSrc: true
1534
- });
1535
- }
1536
- }
1537
- return out;
1538
- }, [effectiveTab, myTokens, groupedTokens, t]);
1539
- return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "!max-h-[80dvh] overflow-auto", children: [
1363
+ })),
1364
+ ...groupedTokens.onSrcChain.map((token) => ({
1365
+ token,
1366
+ willChangeSrc: false
1367
+ }))
1368
+ ];
1369
+ return mainTokens;
1370
+ }, [effectiveTab, myTokens, groupedTokens]);
1371
+ const willChangeSrcTokens = require$$0.useMemo(
1372
+ () => groupedTokens.willChangeSrcChain.map((token) => ({
1373
+ token,
1374
+ willChangeSrc: true
1375
+ })),
1376
+ [groupedTokens.willChangeSrcChain]
1377
+ );
1378
+ const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
1379
+ return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "!h-[90dvh] overflow-hidden flex flex-col", children: [
1540
1380
  /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectToken") }) }),
1541
- /* @__PURE__ */ jsxRuntime.jsxs(
1542
- "div",
1381
+ /* @__PURE__ */ jsxRuntime.jsx(
1382
+ SearchInput,
1543
1383
  {
1544
- 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"}`,
1545
- children: [
1546
- /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "size-6 text-input-icon" }),
1547
- /* @__PURE__ */ jsxRuntime.jsx(
1548
- input.Input,
1549
- {
1550
- placeholder: t("bridge.searchToken"),
1551
- 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",
1552
- value: query,
1553
- onChange: (e) => setQuery(e.target.value),
1554
- onFocus: () => setIsFocused(true),
1555
- onBlur: () => setIsFocused(false)
1556
- }
1557
- )
1558
- ]
1384
+ placeholder: t("bridge.searchToken"),
1385
+ value: query,
1386
+ onChange: setQuery
1559
1387
  }
1560
1388
  ),
1561
1389
  hasAnyWallet() && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
@@ -1578,20 +1406,52 @@ const TokenSelectModal = ({
1578
1406
  }
1579
1407
  )
1580
1408
  ] }),
1581
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-hidden -mx-5", children: virtualNodes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground px-5 py-3.5", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1409
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto -mx-5", children: hasNoResults ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground px-5 py-3.5", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1582
1410
  effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase px-5 py-2", children: t("bridge.noBalancesFound") }),
1583
- /* @__PURE__ */ jsxRuntime.jsx(
1584
- VirtualizedTokenList,
1585
- {
1586
- nodes: virtualNodes,
1587
- balancesLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1588
- hasAnyWallet: hasAnyWallet(),
1589
- selectedAssetSymbol,
1590
- getBalance,
1591
- getTokenUsdValue,
1592
- onPick
1593
- }
1594
- )
1411
+ tokensToRender.map(({ token, willChangeSrc }) => {
1412
+ const bal = getBalance(token.symbol);
1413
+ const usd = getTokenUsdValue(token.symbol, token.price?.usd);
1414
+ const isSelected = selectedAssetSymbol?.toUpperCase() === token.symbol.toUpperCase();
1415
+ return /* @__PURE__ */ jsxRuntime.jsx(
1416
+ TokenRow,
1417
+ {
1418
+ symbol: token.symbol,
1419
+ name: token.name,
1420
+ isSelected: !!isSelected,
1421
+ hasAnyWallet: hasAnyWallet(),
1422
+ balance: bal,
1423
+ usdValue: usd,
1424
+ isBalanceLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1425
+ onPick: () => onPick(token.symbol, willChangeSrc)
1426
+ },
1427
+ `${effectiveTab}:${token.symbol}`
1428
+ );
1429
+ }),
1430
+ effectiveTab === "all" && willChangeSrcTokens.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1431
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 flex leading-4 text-base py-1.5 font-semibold text-muted-foreground uppercase mt-7.5", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: t("bridge.willChangeSourceChain") }) }),
1432
+ willChangeSrcTokens.map(({ token, willChangeSrc }) => {
1433
+ const bal = getBalance(token.symbol);
1434
+ const usd = getTokenUsdValue(
1435
+ token.symbol,
1436
+ token.price?.usd
1437
+ );
1438
+ const isSelected = selectedAssetSymbol?.toUpperCase() === token.symbol.toUpperCase();
1439
+ return /* @__PURE__ */ jsxRuntime.jsx(
1440
+ TokenRow,
1441
+ {
1442
+ symbol: token.symbol,
1443
+ name: token.name,
1444
+ isSelected: !!isSelected,
1445
+ hasAnyWallet: hasAnyWallet(),
1446
+ balance: bal,
1447
+ usdValue: usd,
1448
+ isBalanceLoading: balancesQuery.isLoading || balancesQuery.isFetching,
1449
+ onPick: () => onPick(token.symbol, willChangeSrc)
1450
+ },
1451
+ `will:${token.symbol}`
1452
+ );
1453
+ })
1454
+ ] })
1595
1455
  ] }) })
1596
1456
  ] }) });
1597
1457
  };
@@ -1692,7 +1552,7 @@ const SelectTokenButton = ({
1692
1552
  onClick,
1693
1553
  token
1694
1554
  }) => {
1695
- const { t } = reactI18next.useTranslation();
1555
+ const { t } = useBridgeTranslation();
1696
1556
  const label = require$$0.useMemo(() => {
1697
1557
  return token?.symbol ?? t("bridge.selectToken");
1698
1558
  }, [token, t]);
@@ -1722,7 +1582,7 @@ const SelectTokenButton = ({
1722
1582
  );
1723
1583
  };
1724
1584
  const FormHeaderComponent = () => {
1725
- const { t } = reactI18next.useTranslation();
1585
+ const { t } = useBridgeTranslation();
1726
1586
  const { isOpen, onClose, onOpen } = useModal();
1727
1587
  const {
1728
1588
  isOpen: isOpenSettings,
@@ -1737,7 +1597,7 @@ const FormHeaderComponent = () => {
1737
1597
  const sum = selectedAssetSymbol.toUpperCase();
1738
1598
  return assets.find((a) => a.symbol.toUpperCase() === sum) ?? assets[0];
1739
1599
  }, [assets, selectedAssetSymbol]);
1740
- return /* @__PURE__ */ jsxRuntime.jsxs(card.CardHeader, { className: "gap-y-0 flex justify-between items-center", children: [
1600
+ return /* @__PURE__ */ jsxRuntime.jsxs(card.CardHeader, { className: "gap-y-0 flex flex-row justify-between items-center", children: [
1741
1601
  /* @__PURE__ */ jsxRuntime.jsxs(card.CardTitle, { className: "flex items-center gap-2.5", children: [
1742
1602
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t("bridge.selectToken") }),
1743
1603
  /* @__PURE__ */ jsxRuntime.jsx(SelectTokenButton, { token: current, onClick: onOpen })
@@ -1768,6 +1628,19 @@ const FormHeaderComponent = () => {
1768
1628
  ] });
1769
1629
  };
1770
1630
  const FormHeader = require$$0.memo(FormHeaderComponent);
1631
+ function toLD(human, decimals) {
1632
+ const [i = "0", f = ""] = human.replace(",", ".").split(".");
1633
+ const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1634
+ return BigInt(i + frac).toString();
1635
+ }
1636
+ function fromLD(ld, decimals) {
1637
+ const bi = BigInt(ld || "0");
1638
+ if (decimals === 0) return Number(bi);
1639
+ const base = BigInt(10) ** BigInt(decimals);
1640
+ const int = bi / base;
1641
+ const frac = Number(bi % base) / Number(base);
1642
+ return Number(int) + frac;
1643
+ }
1771
1644
  async function fetchQuotes(req) {
1772
1645
  const params = {
1773
1646
  srcChainKey: req.srcChainKey,
@@ -1826,6 +1699,131 @@ async function getQuotesByPriority(req) {
1826
1699
  const priority = req.routePriority || RoutePriority.RECOMMENDED;
1827
1700
  return selectQuoteByPriority(routes, priority);
1828
1701
  }
1702
+ function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1703
+ if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1704
+ const byChain = assetMatrix[assetSymbol.toUpperCase()];
1705
+ return byChain?.[chainKey];
1706
+ }
1707
+ const DEFAULT_SLIPPAGE_BPS = 50;
1708
+ const lower = (s) => (s ?? "").toLowerCase();
1709
+ const normSym = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1710
+ function tonNorm(addr) {
1711
+ if (!addr) return null;
1712
+ try {
1713
+ if (addr.includes(":")) {
1714
+ return core.Address.parseRaw(addr).toString({
1715
+ bounceable: false,
1716
+ urlSafe: true
1717
+ });
1718
+ }
1719
+ return core.Address.parse(addr).toString({ bounceable: false, urlSafe: true });
1720
+ } catch {
1721
+ return null;
1722
+ }
1723
+ }
1724
+ const isZeroAddr = (a) => (
1725
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1726
+ !!a && /^0x0{40}$/i.test(a) || /^0x[eE]{4}e{36}$/i.test(a)
1727
+ );
1728
+ function addrForApi(chainKey, addr) {
1729
+ if (chainKey === "ton") return tonNorm(addr);
1730
+ return addr;
1731
+ }
1732
+ function isNativeAddrEqual(chain, tokenAddr) {
1733
+ if (!chain) return false;
1734
+ if (!tokenAddr) return false;
1735
+ if (isZeroAddr(tokenAddr)) return true;
1736
+ const nativeAddr = chain.nativeCurrency?.address;
1737
+ if (!nativeAddr) return false;
1738
+ if (chain.chainKey === "ton") {
1739
+ const a = tonNorm(tokenAddr);
1740
+ const b = tonNorm(nativeAddr);
1741
+ return !!a && !!b && a === b;
1742
+ }
1743
+ return lower(nativeAddr) === lower(tokenAddr);
1744
+ }
1745
+ function findNativeMeta(tokens, chain) {
1746
+ if (!chain) return { decimals: 18, priceUsd: void 0 };
1747
+ const sym = normSym(chain.nativeCurrency?.symbol);
1748
+ if (!sym) {
1749
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1750
+ }
1751
+ const sameChain = tokens?.find(
1752
+ (t) => t.chainKey === chain.chainKey && normSym(t.symbol) === sym
1753
+ ) ?? void 0;
1754
+ if (sameChain)
1755
+ return { decimals: sameChain.decimals, priceUsd: sameChain.price?.usd };
1756
+ const anyChain = tokens?.find((t) => normSym(t.symbol) === sym);
1757
+ if (anyChain)
1758
+ return { decimals: anyChain.decimals, priceUsd: anyChain.price?.usd };
1759
+ return { decimals: chain.chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1760
+ }
1761
+ function lookupTokenMeta(tokens, chains, chainKey, tokenAddr) {
1762
+ if (!chainKey) return { decimals: 18, priceUsd: void 0 };
1763
+ const chain = chains?.find((c) => c.chainKey === chainKey);
1764
+ const hit = tokens?.find((t) => {
1765
+ if (t.chainKey !== chainKey) return false;
1766
+ if (chainKey === "ton") {
1767
+ const a = tonNorm(t.address);
1768
+ const b = tonNorm(tokenAddr);
1769
+ return !!a && !!b && a === b;
1770
+ }
1771
+ return lower(t.address) === lower(tokenAddr);
1772
+ }) ?? void 0;
1773
+ if (hit) return { decimals: hit.decimals, priceUsd: hit.price?.usd };
1774
+ if (isNativeAddrEqual(chain, tokenAddr)) {
1775
+ return findNativeMeta(tokens, chain);
1776
+ }
1777
+ return { decimals: chainKey === "ton" ? 9 : 18, priceUsd: void 0 };
1778
+ }
1779
+ function computeFeesUsdFromArray(fees, tokens, chains) {
1780
+ if (!fees?.length) return { totalUsd: 0 };
1781
+ let total = 0;
1782
+ let protocolFeeUsd = 0;
1783
+ let messageFeeUsd = 0;
1784
+ const byType = /* @__PURE__ */ new Map();
1785
+ for (const f of fees) {
1786
+ let usd = Number(f.usd ?? 0);
1787
+ if (!usd) {
1788
+ const { decimals, priceUsd } = lookupTokenMeta(
1789
+ tokens,
1790
+ chains,
1791
+ f.chainKey,
1792
+ f.token
1793
+ );
1794
+ const human = fromLD(String(f.amount ?? "0"), decimals);
1795
+ usd = (priceUsd ?? 0) * human;
1796
+ }
1797
+ total += usd;
1798
+ const type = (f.type ?? "other").toLowerCase();
1799
+ byType.set(type, (byType.get(type) ?? 0) + usd);
1800
+ if (type === "protocol" || type === "service") {
1801
+ protocolFeeUsd += usd;
1802
+ } else if (type === "message" || type === "gas" || type === "network") {
1803
+ messageFeeUsd += usd;
1804
+ }
1805
+ }
1806
+ const serviceUsd = byType.get("protocol") ?? byType.get("service") ?? void 0;
1807
+ const blockchainUsd = byType.get("gas") ?? byType.get("network") ?? byType.get("message") ?? void 0;
1808
+ return {
1809
+ totalUsd: total,
1810
+ protocolFeeUsd: protocolFeeUsd > 0 ? protocolFeeUsd : void 0,
1811
+ messageFeeUsd: messageFeeUsd > 0 ? messageFeeUsd : void 0,
1812
+ serviceUsd,
1813
+ blockchainUsd
1814
+ };
1815
+ }
1816
+ function sumFeeByTokenLD(fees, dstTokenAddr, dstChainKey) {
1817
+ if (!fees?.length || !dstTokenAddr || !dstChainKey) return "0";
1818
+ let acc = 0n;
1819
+ for (const f of fees) {
1820
+ if (f.chainKey !== dstChainKey) continue;
1821
+ const same = dstChainKey === "ton" ? tonNorm(f.token) === tonNorm(dstTokenAddr) : lower(f.token) === lower(dstTokenAddr);
1822
+ if (!same) continue;
1823
+ acc += BigInt(f.amount ?? "0");
1824
+ }
1825
+ return acc.toString();
1826
+ }
1829
1827
  function useBridgeQuote() {
1830
1828
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
1831
1829
  const { fromChain, toChain } = useChainsStore();
@@ -2227,7 +2225,7 @@ const SelectNetworkButton = ({
2227
2225
  onClick,
2228
2226
  network
2229
2227
  }) => {
2230
- const { t } = reactI18next.useTranslation();
2228
+ const { t } = useBridgeTranslation();
2231
2229
  const label = require$$0.useMemo(() => {
2232
2230
  return network?.name ?? t("bridge.selectNetwork");
2233
2231
  }, [network, t]);
@@ -2324,14 +2322,12 @@ const ChainSelectModal = ({
2324
2322
  allowedItems,
2325
2323
  onChangeChain
2326
2324
  }) => {
2327
- const { t } = reactI18next.useTranslation();
2325
+ const { t } = useBridgeTranslation();
2328
2326
  const [query, setQuery] = require$$0.useState("");
2329
- const [isFocused, setIsFocused] = require$$0.useState(false);
2330
2327
  const { setFromChain, chains, fromChain, toChain } = useChainsStore();
2331
2328
  const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2332
2329
  const handleClose = require$$0.useCallback(() => {
2333
2330
  setQuery("");
2334
- setIsFocused(false);
2335
2331
  onClose();
2336
2332
  }, [onClose]);
2337
2333
  const findCompatibleSrcChain = require$$0.useCallback(
@@ -2391,7 +2387,7 @@ const ChainSelectModal = ({
2391
2387
  button.Button,
2392
2388
  {
2393
2389
  onClick: () => onChainPick(chain, willChangeSrc),
2394
- 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" : ""}`,
2390
+ 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" : ""}`,
2395
2391
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
2396
2392
  /* @__PURE__ */ jsxRuntime.jsx(
2397
2393
  NetworkSymbol,
@@ -2407,26 +2403,16 @@ const ChainSelectModal = ({
2407
2403
  chain.chainKey
2408
2404
  );
2409
2405
  };
2410
- return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "!max-h-[80dvh] overflow-hidden", children: [
2406
+ return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "!h-[90dvh] flex flex-col", children: [
2411
2407
  /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectNetwork") }) }),
2412
- /* @__PURE__ */ jsxRuntime.jsxs(
2413
- "div",
2408
+ /* @__PURE__ */ jsxRuntime.jsx(
2409
+ SearchInput,
2414
2410
  {
2415
- 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"}`,
2416
- children: [
2417
- /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "size-6 text-muted-foreground" }),
2418
- /* @__PURE__ */ jsxRuntime.jsx(
2419
- input.Input,
2420
- {
2421
- placeholder: t("bridge.searchDestinationChain"),
2422
- className: "w-full outline-none leading-0 p-0 h-6 text-base text-foreground placeholder:text-muted-foreground bg-none dark:bg-transparent",
2423
- value: query,
2424
- onChange: (e) => setQuery(e.target.value),
2425
- onFocus: () => setIsFocused(true),
2426
- onBlur: () => setIsFocused(false)
2427
- }
2428
- )
2429
- ]
2411
+ placeholder: t("bridge.searchDestinationChain"),
2412
+ value: query,
2413
+ onChange: setQuery,
2414
+ containerClassName: "rounded-md",
2415
+ className: "text-foreground placeholder:text-muted-foreground"
2430
2416
  }
2431
2417
  ),
2432
2418
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
@@ -2469,7 +2455,7 @@ const WalletButton = ({
2469
2455
  wallet,
2470
2456
  addressType
2471
2457
  }) => {
2472
- const { t } = reactI18next.useTranslation();
2458
+ const { t } = useBridgeTranslation();
2473
2459
  const { onOpen } = useWalletSelectModal();
2474
2460
  const { chainRegistry } = useChainStrategies();
2475
2461
  const walletType = mapWalletToType(wallet);
@@ -2641,63 +2627,30 @@ const useCustomAddressStore = zustand.create((set) => ({
2641
2627
  setCustomDstAddress: (address) => set({ customDstAddress: address }),
2642
2628
  clearCustomDstAddress: () => set({ customDstAddress: void 0 })
2643
2629
  }));
2644
- const tonNormalize = (addr) => {
2645
- if (!addr) return null;
2646
- try {
2647
- if (addr.includes(":")) {
2648
- return core.Address.parseRaw(addr).toString({
2649
- bounceable: false,
2650
- urlSafe: true
2651
- });
2652
- }
2653
- return core.Address.parse(addr).toString({ bounceable: false, urlSafe: true });
2654
- } catch {
2655
- return null;
2656
- }
2630
+ const useIsAddressValid = (address, chainKey) => {
2631
+ const isValid = require$$0.useMemo(() => {
2632
+ return isAddressValidForChain(chainKey, address);
2633
+ }, [address, chainKey]);
2634
+ return { isValid };
2657
2635
  };
2658
- const ToggleRow = ({ enabled, onToggle }) => {
2659
- const { t } = reactI18next.useTranslation();
2636
+ const AnotherAddress = () => {
2637
+ const [enabled, setEnabled] = require$$0.useState(false);
2638
+ const [isFocused, setIsFocused] = require$$0.useState(false);
2639
+ const { t } = useBridgeTranslation();
2660
2640
  const { toChain } = useChainsStore();
2661
- const { dstAddress } = useAddresses();
2662
- const { setCustomDstAddress, clearCustomDstAddress } = useCustomAddressStore();
2641
+ const { setCustomDstAddress } = useCustomAddressStore();
2663
2642
  const [value, setValue] = require$$0.useState("");
2664
- const [invalid, setInvalid] = require$$0.useState(false);
2665
- const prevDstRef = require$$0.useRef(void 0);
2666
- const prevEnabledRef = require$$0.useRef(enabled);
2667
- require$$0.useEffect(() => {
2668
- const wasEnabled = prevEnabledRef.current;
2669
- if (enabled && !wasEnabled) {
2670
- prevDstRef.current = dstAddress;
2671
- setValue(dstAddress ?? "");
2672
- setInvalid(false);
2673
- }
2674
- if (!enabled && wasEnabled) {
2675
- clearCustomDstAddress();
2676
- setInvalid(false);
2677
- }
2678
- prevEnabledRef.current = enabled;
2679
- }, [enabled]);
2643
+ const { isValid } = useIsAddressValid(value.trim(), toChain?.chainKey);
2644
+ const invalid = value.trim() && !isValid;
2680
2645
  const pushToStoreIfValid = (raw) => {
2681
2646
  if (!enabled) return;
2682
2647
  const v = raw.trim();
2683
2648
  if (!v) {
2684
2649
  setCustomDstAddress(void 0);
2685
- setInvalid(false);
2686
- return;
2687
- }
2688
- const ck = toChain?.chainKey;
2689
- if (!ck) {
2690
- setInvalid(true);
2691
2650
  return;
2692
2651
  }
2693
- const valueForStore = ck === "ton" ? tonNormalize(v) ?? void 0 : v;
2694
- const valid = isAddressValidForChain(ck, valueForStore ?? "");
2695
- if (valid && valueForStore) {
2696
- setInvalid(false);
2697
- setCustomDstAddress(valueForStore);
2698
- if (ck === "ton") setValue(valueForStore);
2699
- } else {
2700
- setInvalid(true);
2652
+ if (isValid) {
2653
+ setCustomDstAddress(v);
2701
2654
  }
2702
2655
  };
2703
2656
  const onChange = (text) => {
@@ -2712,7 +2665,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2712
2665
  } catch {
2713
2666
  }
2714
2667
  };
2715
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 flex flex-col rounded-b-lg gap-2 bg-muted", children: [
2668
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 flex flex-col rounded-b-lg bg-muted", children: [
2716
2669
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2717
2670
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-4.5 font-medium text-muted-foreground", children: t("bridge.sendToAnotherAddress") }),
2718
2671
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2721,7 +2674,7 @@ const ToggleRow = ({ enabled, onToggle }) => {
2721
2674
  className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2722
2675
  "aria-pressed": enabled,
2723
2676
  checked: enabled,
2724
- onClick: onToggle
2677
+ onClick: () => setEnabled((v) => !v)
2725
2678
  }
2726
2679
  )
2727
2680
  ] }),
@@ -2736,16 +2689,27 @@ const ToggleRow = ({ enabled, onToggle }) => {
2736
2689
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2737
2690
  "div",
2738
2691
  {
2739
- className: `bg-input py-2 px-4 w-full flex items-center gap-4 rounded-12 justify-between border ${invalid ? "border-destructive" : "border-transparent"}`,
2692
+ className: utils.cn(
2693
+ "bg-input py-2 px-4 mt-2 w-full flex items-center gap-4 rounded-md justify-between border border-transparent transition-all",
2694
+ {
2695
+ "py-4": value,
2696
+ "border border-ring": isFocused,
2697
+ "border-destructive": invalid
2698
+ }
2699
+ ),
2740
2700
  children: [
2741
2701
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5 w-full", children: [
2742
2702
  /* @__PURE__ */ jsxRuntime.jsx(
2743
2703
  input.Input,
2744
2704
  {
2745
- 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",
2746
- placeholder: toChain?.chainKey === "ton" ? t("bridge.tonAddressPlaceholder") : t("bridge.evmAddressPlaceholder"),
2705
+ className: utils.cn(
2706
+ "p-0 h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none"
2707
+ ),
2708
+ placeholder: t("bridge.anotherAddressPlaceholder"),
2747
2709
  type: "text",
2748
2710
  value,
2711
+ onFocus: () => setIsFocused(true),
2712
+ onBlur: () => setIsFocused(false),
2749
2713
  onChange: (e) => onChange(e.target.value)
2750
2714
  }
2751
2715
  ),
@@ -2755,21 +2719,14 @@ const ToggleRow = ({ enabled, onToggle }) => {
2755
2719
  defaultValue: "Check correctness before transfer"
2756
2720
  }) }) })
2757
2721
  ] }),
2758
- !value ? /* @__PURE__ */ jsxRuntime.jsx(
2759
- button.Button,
2760
- {
2761
- variant: "default",
2762
- 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",
2763
- onClick: onPaste,
2764
- children: t("common.paste")
2765
- }
2766
- ) : /* @__PURE__ */ jsxRuntime.jsx(
2722
+ !value ? /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "secondary", size: "sm", onClick: onPaste, children: t("common.paste") }) : /* @__PURE__ */ jsxRuntime.jsx(
2767
2723
  button.Button,
2768
2724
  {
2769
2725
  variant: "ghost",
2770
- 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",
2726
+ size: "sm",
2727
+ className: "rounded-full p-0 size-5 self-start",
2771
2728
  onClick: () => setValue(""),
2772
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3 text-input-x" })
2729
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" })
2773
2730
  }
2774
2731
  )
2775
2732
  ]
@@ -2882,7 +2839,7 @@ function getRouteDisplayName(route) {
2882
2839
  return route.split(/[/\-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
2883
2840
  }
2884
2841
  const Details = () => {
2885
- const { t } = reactI18next.useTranslation();
2842
+ const { t } = useBridgeTranslation();
2886
2843
  const { selectedAssetSymbol, assetMatrix, tokens } = useTokensStore();
2887
2844
  const { toChain, fromChain, chains } = useChainsStore();
2888
2845
  const { quote, status } = useBridgeQuoteStore();
@@ -3528,7 +3485,7 @@ function useSilentValidations(amountString) {
3528
3485
  return validationResult;
3529
3486
  }
3530
3487
  const SubmitButton = () => {
3531
- const { t } = reactI18next.useTranslation();
3488
+ const { t } = useBridgeTranslation();
3532
3489
  const { chainRegistry } = useChainStrategies();
3533
3490
  const { srcAddress, dstAddress } = useAddresses();
3534
3491
  const { quote, status, inputAmount, noRoute } = useBridgeQuoteStore();
@@ -3623,13 +3580,22 @@ const SubmitButton = () => {
3623
3580
  }
3624
3581
  };
3625
3582
  const disabled = isBusy || amountNum <= 0 || status === "loading" || isBalanceLoading || hasInsufficientBalance || hasAmountTooLarge || !gas.hasEnoughGas || noRoute || !isValidForTransfer;
3626
- return /* @__PURE__ */ jsxRuntime.jsx(button.Button, { onClick: handleClick, disabled, className: "w-full mt-4", children: label });
3583
+ return /* @__PURE__ */ jsxRuntime.jsx(
3584
+ button.Button,
3585
+ {
3586
+ onClick: handleClick,
3587
+ disabled,
3588
+ size: "lg",
3589
+ className: "w-full mt-4",
3590
+ children: label
3591
+ }
3592
+ );
3627
3593
  };
3628
3594
  function short(addr) {
3629
3595
  return addr.slice(0, 4) + "…" + addr.slice(-4);
3630
3596
  }
3631
3597
  const WalletSelectModal = () => {
3632
- const { t } = reactI18next.useTranslation();
3598
+ const { t } = useBridgeTranslation();
3633
3599
  const { isOpen, onClose } = useWalletSelectModal();
3634
3600
  const { connect, connectors, isPending } = wagmi.useConnect();
3635
3601
  const { chainRegistry } = useChainStrategies();
@@ -3738,7 +3704,7 @@ const WalletSelectModal = () => {
3738
3704
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2 font-semibold text-muted-foreground uppercase", children: t("wallets.connected") }),
3739
3705
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: connectedWallets.map((wallet) => {
3740
3706
  const IconComponent = wallet.icon;
3741
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-5", children: /* @__PURE__ */ jsxRuntime.jsxs(button.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: [
3707
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-5", children: /* @__PURE__ */ jsxRuntime.jsxs(button.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: [
3742
3708
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
3743
3709
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: "size-7.5" }) }),
3744
3710
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -3779,7 +3745,7 @@ const WalletSelectModal = () => {
3779
3745
  }
3780
3746
  },
3781
3747
  disabled: isEvmConnector ? isPending : !wallet.enabled,
3782
- 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",
3748
+ 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",
3783
3749
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3784
3750
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: "size-7.5" }) }),
3785
3751
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
@@ -3866,7 +3832,7 @@ const ProgressStep = () => {
3866
3832
  /* @__PURE__ */ jsxRuntime.jsx("span", {}),
3867
3833
  /* @__PURE__ */ jsxRuntime.jsx("span", {})
3868
3834
  ] }),
3869
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-display font-black relative z-10", children: i18next.t("transaction.inProgress") }),
3835
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-display font-black relative z-10", children: i18n.t("transaction.inProgress") }),
3870
3836
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative mt-5 z-10", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute dot-vertical h-full left-5 top-0 bottom-0 z-0" }) })
3871
3837
  ] }) });
3872
3838
  };
@@ -4123,10 +4089,10 @@ const FailedStep = () => {
4123
4089
  /* @__PURE__ */ jsxRuntime.jsx(TransactionFailedVector, {}),
4124
4090
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "on-circle on-circle-failed-small under-noise" }),
4125
4091
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "on-circle on-circle-failed-big under-noise" }),
4126
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-display font-black relative z-10", children: i18next.t("transaction.failed") }),
4092
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-display font-black relative z-10", children: i18n.t("transaction.failed") }),
4127
4093
  current?.error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full space-y-2 mt-6 relative z-10", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: current.error }) })
4128
4094
  ] }),
4129
- /* @__PURE__ */ jsxRuntime.jsx(card.CardFooter, { className: "flex-col gap-3 pb-10 px-8", children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "outline", className: "w-full", onClick: reset, children: i18next.t("common.close") }) })
4095
+ /* @__PURE__ */ jsxRuntime.jsx(card.CardFooter, { className: "flex-col gap-3 pb-10 px-8", children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "outline", className: "w-full", onClick: reset, children: i18n.t("common.close") }) })
4130
4096
  ] });
4131
4097
  };
4132
4098
  const TransactionSuccessVector = (props) => {
@@ -5488,7 +5454,7 @@ var fireworksExports = requireFireworks();
5488
5454
  const Fireworks = /* @__PURE__ */ getDefaultExportFromCjs(fireworksExports);
5489
5455
  const SuccessStep = () => {
5490
5456
  const { current, reset } = useTransactionStore();
5491
- const { t } = reactI18next.useTranslation();
5457
+ const { t } = useBridgeTranslation();
5492
5458
  const metadata = current?.metadata;
5493
5459
  const srcTxHash = current?.srcTxHash;
5494
5460
  const handleCopyHash = () => {
@@ -5707,7 +5673,7 @@ const useCountdown = (initialSeconds) => {
5707
5673
  };
5708
5674
  };
5709
5675
  const ConfirmStep = () => {
5710
- const { t } = reactI18next.useTranslation();
5676
+ const { t } = useBridgeTranslation();
5711
5677
  const { formatTime } = useCountdown(90);
5712
5678
  return /* @__PURE__ */ jsxRuntime.jsx(card.Card, { className: "flex flex-col border-none h-full bg-background overflow-hidden rounded-none md:rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs(card.CardContent, { className: "flex flex-col relative gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise", children: [
5713
5679
  /* @__PURE__ */ jsxRuntime.jsx(TransactionConfirmVector, {}),
@@ -5830,7 +5796,7 @@ class ChainStrategyRegistry {
5830
5796
  async getBalances(chainKey, address, tokens, priorityToken) {
5831
5797
  const strategy = this.getStrategy(chainKey);
5832
5798
  if (!strategy) return {};
5833
- return await strategy.getBalances(address, tokens, priorityToken);
5799
+ return await strategy.getBalances(address, tokens, chainKey, priorityToken);
5834
5800
  }
5835
5801
  isAddressValid(chainKey, address) {
5836
5802
  const strategy = this.getStrategy(chainKey);
@@ -5941,14 +5907,9 @@ function parseTonAddress(address) {
5941
5907
  }
5942
5908
  return ton.Address.parse(address);
5943
5909
  }
5944
- async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5910
+ async function getEvmBalances(publicClient, address, tokens, chainKey, priorityToken) {
5945
5911
  const balances = {};
5946
5912
  try {
5947
- console.log("start getEvmBalances");
5948
- console.log("publicClient:", publicClient);
5949
- console.log("isAddress:", viem.isAddress(address));
5950
- console.log("tokens:", tokens);
5951
- console.log("priorityToken:", priorityToken);
5952
5913
  if (!address || !viem.isAddress(address)) {
5953
5914
  console.warn(`Invalid EVM address provided: ${address}`);
5954
5915
  return balances;
@@ -5957,40 +5918,55 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
5957
5918
  throw new Error("No public client provided");
5958
5919
  }
5959
5920
  const nativeTokens = tokens.filter((t) => isNativeAddress(t.address));
5960
- const erc20Tokens = tokens.filter((t) => !isNativeAddress(t.address) && viem.isAddress(t.address));
5921
+ const erc20Tokens = tokens.filter(
5922
+ (t) => !isNativeAddress(t.address) && viem.isAddress(t.address)
5923
+ );
5961
5924
  if (priorityToken) {
5962
- try {
5963
- const isPriorityNative = isNativeAddress(priorityToken.address);
5964
- if (isPriorityNative) {
5965
- const ethBalance = await publicClient.getBalance({
5966
- address
5967
- });
5968
- const balance = parseFloat(viem.formatUnits(ethBalance, priorityToken.decimals));
5969
- if (balance > 0) {
5970
- balances[priorityToken.symbol] = { balance, address };
5971
- }
5972
- } else if (viem.isAddress(priorityToken.address)) {
5973
- const tokenBalance = await publicClient.readContract({
5974
- address: priorityToken.address,
5975
- abi: [
5976
- {
5977
- name: "balanceOf",
5978
- type: "function",
5979
- stateMutability: "view",
5980
- inputs: [{ name: "owner", type: "address" }],
5981
- outputs: [{ name: "balance", type: "uint256" }]
5982
- }
5983
- ],
5984
- functionName: "balanceOf",
5985
- args: [address]
5986
- });
5987
- const balance = parseFloat(viem.formatUnits(tokenBalance, priorityToken.decimals));
5988
- if (balance > 0) {
5989
- balances[priorityToken.symbol] = { balance, address };
5925
+ if (priorityToken.chainKey !== chainKey) {
5926
+ console.debug(
5927
+ `Skipping priority token ${priorityToken.symbol}: chain mismatch (expected ${chainKey}, got ${priorityToken.chainKey})`
5928
+ );
5929
+ } else {
5930
+ try {
5931
+ const isPriorityNative = isNativeAddress(priorityToken.address);
5932
+ if (isPriorityNative) {
5933
+ const ethBalance = await publicClient.getBalance({
5934
+ address
5935
+ });
5936
+ const balance = parseFloat(
5937
+ viem.formatUnits(ethBalance, priorityToken.decimals)
5938
+ );
5939
+ if (balance > 0) {
5940
+ balances[priorityToken.symbol] = { balance, address };
5941
+ }
5942
+ } else if (viem.isAddress(priorityToken.address)) {
5943
+ const tokenBalance = await publicClient.readContract({
5944
+ address: priorityToken.address,
5945
+ abi: [
5946
+ {
5947
+ name: "balanceOf",
5948
+ type: "function",
5949
+ stateMutability: "view",
5950
+ inputs: [{ name: "owner", type: "address" }],
5951
+ outputs: [{ name: "balance", type: "uint256" }]
5952
+ }
5953
+ ],
5954
+ functionName: "balanceOf",
5955
+ args: [address]
5956
+ });
5957
+ const balance = parseFloat(
5958
+ viem.formatUnits(tokenBalance, priorityToken.decimals)
5959
+ );
5960
+ if (balance > 0) {
5961
+ balances[priorityToken.symbol] = { balance, address };
5962
+ }
5990
5963
  }
5964
+ } catch (error) {
5965
+ console.debug(
5966
+ `Failed to get priority token balance for ${priorityToken.symbol}:`,
5967
+ error
5968
+ );
5991
5969
  }
5992
- } catch (error) {
5993
- console.debug(`Failed to get priority token balance for ${priorityToken.symbol}:`, error);
5994
5970
  }
5995
5971
  }
5996
5972
  for (const token of nativeTokens) {
@@ -6003,7 +5979,10 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6003
5979
  balances[token.symbol] = { balance, address };
6004
5980
  }
6005
5981
  } catch (error) {
6006
- console.debug(`Failed to get native balance for ${token.symbol}:`, error);
5982
+ console.debug(
5983
+ `Failed to get native balance for ${token.symbol}:`,
5984
+ error
5985
+ );
6007
5986
  }
6008
5987
  }
6009
5988
  if (erc20Tokens.length > 0) {
@@ -6031,17 +6010,25 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6031
6010
  if (!token) return;
6032
6011
  if (result.status === "success" && result.result !== void 0) {
6033
6012
  try {
6034
- const balance = parseFloat(viem.formatUnits(result.result, token.decimals));
6013
+ const balance = parseFloat(
6014
+ viem.formatUnits(result.result, token.decimals)
6015
+ );
6035
6016
  if (balance > 0) {
6036
6017
  balances[token.symbol] = { balance, address };
6037
6018
  }
6038
6019
  } catch (error) {
6039
- console.debug(`Failed to parse balance for ${token.symbol}:`, error);
6020
+ console.debug(
6021
+ `Failed to parse balance for ${token.symbol}:`,
6022
+ error
6023
+ );
6040
6024
  }
6041
6025
  }
6042
6026
  });
6043
6027
  } catch (error) {
6044
- console.warn("Multicall failed, falling back to individual calls:", error);
6028
+ console.warn(
6029
+ "Multicall failed, falling back to individual calls:",
6030
+ error
6031
+ );
6045
6032
  for (const token of erc20Tokens) {
6046
6033
  try {
6047
6034
  const tokenBalance = await publicClient.readContract({
@@ -6075,7 +6062,7 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
6075
6062
  }
6076
6063
  return balances;
6077
6064
  }
6078
- async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
6065
+ async function getTonBalances(address, tokens, chainKey, customTonClient, tonApiKey) {
6079
6066
  const balances = {};
6080
6067
  try {
6081
6068
  if (!isTonFriendlyAddress(address)) {
@@ -6234,13 +6221,18 @@ class EvmChainStrategy {
6234
6221
  getConnectLabel(t) {
6235
6222
  return t("wallets.connectEvmWallet");
6236
6223
  }
6237
- async getBalances(address, tokens, priorityToken) {
6224
+ async getBalances(address, tokens, chainKey, priorityToken) {
6238
6225
  if (!this.publicClient) {
6239
6226
  console.warn("No publicClient available for balance query");
6240
6227
  return {};
6241
6228
  }
6242
- console.log("publicClient", this.publicClient);
6243
- return await getEvmBalances(this.publicClient, address, tokens, priorityToken);
6229
+ return await getEvmBalances(
6230
+ this.publicClient,
6231
+ address,
6232
+ tokens,
6233
+ chainKey,
6234
+ priorityToken
6235
+ );
6244
6236
  }
6245
6237
  isAddressValid(address) {
6246
6238
  if (!address) return false;
@@ -6596,10 +6588,11 @@ class TonChainStrategy {
6596
6588
  getConnectLabel(t) {
6597
6589
  return t("wallets.connectTonWallet");
6598
6590
  }
6599
- async getBalances(address, tokens) {
6591
+ async getBalances(address, tokens, chainKey) {
6600
6592
  return await getTonBalances(
6601
6593
  address,
6602
6594
  tokens,
6595
+ chainKey,
6603
6596
  this.config.tonClient,
6604
6597
  this.config.tonApiKey
6605
6598
  );
@@ -6682,12 +6675,12 @@ class TonChainStrategy {
6682
6675
  payload: msg.payload
6683
6676
  })
6684
6677
  );
6685
- const transaction = {
6678
+ const transaction2 = {
6686
6679
  validUntil: Math.floor(Date.now() / 1e3) + TON_CONFIG.validUntil,
6687
6680
  messages: tonMessages
6688
6681
  };
6689
6682
  const result = await this.config.tonConnectUI.sendTransaction(
6690
- transaction
6683
+ transaction2
6691
6684
  );
6692
6685
  return {
6693
6686
  chainKey: "ton",
@@ -6808,7 +6801,6 @@ class TronChainStrategy {
6808
6801
  __publicField(this, "config");
6809
6802
  this.config = config;
6810
6803
  }
6811
- // ========== Identity ==========
6812
6804
  canHandle(chainKey) {
6813
6805
  return chainKey.toLowerCase() === "tron";
6814
6806
  }
@@ -6818,7 +6810,6 @@ class TronChainStrategy {
6818
6810
  getName() {
6819
6811
  return "TRON Chain Strategy";
6820
6812
  }
6821
- // ========== Wallet Management ==========
6822
6813
  async connect() {
6823
6814
  const tronWeb = this.getTronWeb();
6824
6815
  if (!tronWeb && (typeof window === "undefined" || !window.tronLink)) {
@@ -6849,7 +6840,6 @@ class TronChainStrategy {
6849
6840
  getConnectLabel(t) {
6850
6841
  return t("wallets.connectTronWallet");
6851
6842
  }
6852
- // ========== Balance & Validation ==========
6853
6843
  async getBalances(address, tokens) {
6854
6844
  const tronWeb = this.getTronWeb();
6855
6845
  if (!tronWeb) return {};
@@ -6859,7 +6849,6 @@ class TronChainStrategy {
6859
6849
  if (!address) return false;
6860
6850
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
6861
6851
  }
6862
- // ========== Gas Estimation ==========
6863
6852
  async estimateGasRequirement(params) {
6864
6853
  const {
6865
6854
  selectedToken,
@@ -7506,7 +7495,7 @@ const EvaaBridgeWithProviders = (props) => {
7506
7495
  require$$0.useEffect(() => {
7507
7496
  setTronConnected(!!tronConnected);
7508
7497
  }, [tronConnected, setTronConnected]);
7509
- return /* @__PURE__ */ jsxRuntime.jsx(
7498
+ return /* @__PURE__ */ jsxRuntime.jsx(BridgeI18nProvider, { defaultLanguage: props.defaultLanguage, children: /* @__PURE__ */ jsxRuntime.jsx(
7510
7499
  ChainStrategyProvider,
7511
7500
  {
7512
7501
  evmWallet: {
@@ -7531,7 +7520,7 @@ const EvaaBridgeWithProviders = (props) => {
7531
7520
  tonApiKey: props.tonApiKey,
7532
7521
  children: /* @__PURE__ */ jsxRuntime.jsx(EvaaBridgeContent, { ...props })
7533
7522
  }
7534
- );
7523
+ ) });
7535
7524
  };
7536
7525
  const EvaaBridgeContent = ({
7537
7526
  className,
@@ -7539,10 +7528,9 @@ const EvaaBridgeContent = ({
7539
7528
  onAmountChange,
7540
7529
  onChainChange
7541
7530
  } = {}) => {
7542
- const { t } = reactI18next.useTranslation();
7531
+ const { t } = useBridgeTranslation();
7543
7532
  useTokensRequest();
7544
7533
  useChainsRequest();
7545
- const [sendToAnother, setSendToAnother] = require$$0.useState(false);
7546
7534
  const swap = useSwapModel();
7547
7535
  const { fromChain, toChain } = swap;
7548
7536
  const { selectedAssetSymbol, assetMatrix } = useTokensStore();
@@ -7670,13 +7658,7 @@ const EvaaBridgeContent = ({
7670
7658
  onSelect: handleToChainChange
7671
7659
  }
7672
7660
  ),
7673
- /* @__PURE__ */ jsxRuntime.jsx(
7674
- ToggleRow,
7675
- {
7676
- enabled: sendToAnother,
7677
- onToggle: () => setSendToAnother((v) => !v)
7678
- }
7679
- ),
7661
+ /* @__PURE__ */ jsxRuntime.jsx(AnotherAddress, {}),
7680
7662
  /* @__PURE__ */ jsxRuntime.jsx(SubmitButton, {})
7681
7663
  ] }),
7682
7664
  /* @__PURE__ */ jsxRuntime.jsx(card.CardFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(Details, {}) })
@@ -7751,7 +7733,6 @@ exports.addrForApi = addrForApi;
7751
7733
  exports.buildAssetMatrix = buildAssetMatrix;
7752
7734
  exports.calculateMinReceived = calculateMinReceived;
7753
7735
  exports.computeFeesUsdFromArray = computeFeesUsdFromArray;
7754
- exports.dollarsFromNativeFees = dollarsFromNativeFees;
7755
7736
  exports.findNativeMeta = findNativeMeta;
7756
7737
  exports.formatBalance = formatBalance;
7757
7738
  exports.formatHash = formatHash;
@@ -7770,10 +7751,7 @@ exports.getQuotesByPriority = getQuotesByPriority;
7770
7751
  exports.getTokens = getTokens;
7771
7752
  exports.getTonBalances = getTonBalances;
7772
7753
  exports.getTronBalances = getTronBalances;
7773
- exports.isAddressValidForChain = isAddressValidForChain;
7774
- exports.isEvmAddress = isEvmAddress;
7775
7754
  exports.isNativeAddrEqual = isNativeAddrEqual;
7776
- exports.isTronAddress = isTronAddress;
7777
7755
  exports.isZeroAddr = isZeroAddr;
7778
7756
  exports.listAssetsForSelect = listAssetsForSelect;
7779
7757
  exports.lookupTokenMeta = lookupTokenMeta;