@rash2x/bridge-widget 0.2.11 → 0.3.0

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,15 +2,16 @@ 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 { useEffect, useMemo, useState, createContext, useContext, useCallback, forwardRef } from "react";
5
+ import { useEffect, useMemo, createContext, useContext, useState, useCallback, forwardRef } from "react";
6
6
  import { initReactI18next, I18nextProvider, useTranslation } from "react-i18next";
7
7
  import i18n from "i18next";
8
8
  import { Button } from "@/components/ui/button";
9
9
  import { create } from "zustand";
10
- import { useAccount, useConnect, useDisconnect, useWalletClient, usePublicClient } from "wagmi";
10
+ import { useAccount, usePublicClient, useConnect, useDisconnect, useWalletClient } from "wagmi";
11
11
  import { useWallet } from "@tronweb3/tronwallet-adapter-react-hooks";
12
12
  import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
13
13
  import { Address, loadMessage, Cell, beginCell as beginCell$1, storeMessage } from "@ton/core";
14
+ import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
14
15
  import { useQuery, useQueryClient } from "@tanstack/react-query";
15
16
  import { cn } from "@/lib/utils";
16
17
  import { Skeleton } from "@/components/ui/skeleton";
@@ -21,7 +22,6 @@ import { X, Loader2, AlertCircleIcon, CheckCircle2, Clock } from "lucide-react";
21
22
  import { AnimatePresence, motion } from "framer-motion";
22
23
  import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
23
24
  import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
24
- import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
25
25
  import { toast, Toaster } from "sonner";
26
26
  import { DialogDescription as DialogDescription$1 } from "@radix-ui/react-dialog";
27
27
  import { BrowserProvider, Contract, parseUnits } from "ethers";
@@ -30,8 +30,8 @@ import { TronLinkAdapterName } from "@tronweb3/tronwallet-adapters";
30
30
  import { CardHeader, CardTitle, CardAction, Card, CardContent, CardFooter } from "@/components/ui/card";
31
31
  import { Badge } from "@/components/ui/badge";
32
32
  const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
33
- 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" };
34
- const bridge$1 = { "max": "Max", "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" };
33
+ 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", "tonconnect": "TonConnect", "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" };
34
+ const bridge$1 = { "max": "Max", "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 network", "willChangeSourceNetworkAndToken": "Will change source network and token", "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" };
35
35
  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}}", "success": "Success", "successTitle": "Success", "done": "Done", "hashCopied": "Hash copied to clipboard", "bridged": "Bridged", "transferTitle": "Transfer", "hash": "Hash", "finalFee": "Final Fee", "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" };
36
36
  const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
37
37
  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" } };
@@ -46,8 +46,8 @@ const en = {
46
46
  errors: errors$1
47
47
  };
48
48
  const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
49
- 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": "Подключить кошелёк" };
50
- const bridge = { "max": "Макс", "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
49
+ 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", "tonconnect": "TonConnect", "metaMask": "WalletConnect", "tronLink": "TronLink", "addTronWallet": "Добавить Tron кошелёк", "comingSoon": "Скоро", "connected": "ПОДКЛЮЧЕНО", "disconnect": "Отключить", "chooseWallet": "Выберите кошелёк", "oneWalletPerEnv": "Можно подключить только один кошелёк на окружение.", "connect": "Подключить", "connectTronWallet": "Подключить Tron кошелёк", "connectWallet": "Подключить кошелёк" };
50
+ const bridge = { "max": "Макс", "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "willChangeSourceNetworkAndToken": "Сменит исходную сеть и токен", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
51
51
  const transaction = { "enterAmount": "Введите сумму", "transfer": "Перевести", "getQuote": "Получить котировку", "quoting": "Расчет котировки...", "failed": "Ошибка транзакции", "confirm": "Подтвердите транзакцию", "signTransaction": "Подпишите транзакцию в кошельке", "inProgress": "Выполнение...", "checkingBalance": "Проверка баланса...", "insufficientBalance": "Недостаточно средств", "amountTooSmall": "Минимум {{min}}", "amountTooLarge": "Максимум {{max}}", "success": "Успех", "successTitle": "Успех", "done": "Готово", "hashCopied": "Хэш скопирован в буфер обмена", "bridged": "Переведено", "transferTitle": "Перевод", "hash": "Хэш", "finalFee": "Итоговая комиссия", "route": "Маршрут", "estTime": "Время", "slippage": "Проскальзывание", "minimumReceived": "Минимум к получению", "totalFee": "Общая комиссия", "noRouteFound": "Маршрут не найден", "notEnoughGas": "Недостаточно газа", "noRouteFoundForSettings": "Маршрут не найден для текущих настроек.", "tryAdjustSettings": "Попробуйте отключить Gas on Destination или измените сумму/сети.", "quoteError": "Ошибка котировки" };
52
52
  const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
53
53
  const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
@@ -98,72 +98,282 @@ function BridgeI18nProvider({
98
98
  function useBridgeTranslation() {
99
99
  return useTranslation("evaa-bridge", { i18n: bridgeI18n });
100
100
  }
101
- const useChainsStore = create((set, get) => ({
102
- chains: void 0,
103
- fromChain: void 0,
104
- toChain: void 0,
105
- allowedFromChains: void 0,
106
- allowedToChains: void 0,
107
- isLoadingToChains: false,
108
- setChains: async (data) => {
109
- const prev = get().chains;
110
- const same = (() => {
111
- if (!prev && !data) return true;
112
- if (!prev || !data) return false;
113
- if (prev.length !== data.length) return false;
114
- for (let i = 0; i < prev.length; i++) {
115
- if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
101
+ function createJSONStorage(getStorage, options) {
102
+ let storage;
103
+ try {
104
+ storage = getStorage();
105
+ } catch (e) {
106
+ return;
107
+ }
108
+ const persistStorage = {
109
+ getItem: (name) => {
110
+ var _a;
111
+ const parse = (str2) => {
112
+ if (str2 === null) {
113
+ return null;
114
+ }
115
+ return JSON.parse(str2, void 0);
116
+ };
117
+ const str = (_a = storage.getItem(name)) != null ? _a : null;
118
+ if (str instanceof Promise) {
119
+ return str.then(parse);
116
120
  }
117
- return true;
118
- })();
119
- if (same) return;
120
- set({ chains: data });
121
- },
122
- setFromChain: (data) => {
123
- const prev = get().fromChain;
124
- if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
125
- set({ fromChain: data });
126
- },
127
- setToChain: (data) => {
128
- const prev = get().toChain;
129
- if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
130
- set({ toChain: data });
131
- },
132
- setAllowedFromChains: (data) => {
133
- const prev = get().allowedFromChains;
134
- const same = (() => {
135
- if (!prev && !data) return true;
136
- if (!prev || !data) return false;
137
- if (prev.length !== data.length) return false;
138
- for (let i = 0; i < prev.length; i++) {
139
- if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
121
+ return parse(str);
122
+ },
123
+ setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, void 0)),
124
+ removeItem: (name) => storage.removeItem(name)
125
+ };
126
+ return persistStorage;
127
+ }
128
+ const toThenable = (fn) => (input) => {
129
+ try {
130
+ const result = fn(input);
131
+ if (result instanceof Promise) {
132
+ return result;
133
+ }
134
+ return {
135
+ then(onFulfilled) {
136
+ return toThenable(onFulfilled)(result);
137
+ },
138
+ catch(_onRejected) {
139
+ return this;
140
140
  }
141
- return true;
142
- })();
143
- if (same) return;
144
- set({ allowedFromChains: data });
145
- },
146
- setAllowedToChains: (data) => {
147
- const prev = get().allowedToChains;
148
- const same = (() => {
149
- if (!prev && !data) return true;
150
- if (!prev || !data) return false;
151
- if (prev.length !== data.length) return false;
152
- for (let i = 0; i < prev.length; i++) {
153
- if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
141
+ };
142
+ } catch (e) {
143
+ return {
144
+ then(_onFulfilled) {
145
+ return this;
146
+ },
147
+ catch(onRejected) {
148
+ return toThenable(onRejected)(e);
154
149
  }
155
- return true;
156
- })();
157
- if (same) return;
158
- set({ allowedToChains: data });
159
- },
160
- setIsLoadingToChains: (v) => set({ isLoadingToChains: v }),
161
- swapChains: () => set((state) => ({
162
- fromChain: state.toChain,
163
- toChain: state.fromChain
164
- }))
165
- }));
166
- const norm = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
150
+ };
151
+ }
152
+ };
153
+ const persistImpl = (config, baseOptions) => (set, get, api) => {
154
+ let options = {
155
+ storage: createJSONStorage(() => localStorage),
156
+ partialize: (state) => state,
157
+ version: 0,
158
+ merge: (persistedState, currentState) => ({
159
+ ...currentState,
160
+ ...persistedState
161
+ }),
162
+ ...baseOptions
163
+ };
164
+ let hasHydrated = false;
165
+ const hydrationListeners = /* @__PURE__ */ new Set();
166
+ const finishHydrationListeners = /* @__PURE__ */ new Set();
167
+ let storage = options.storage;
168
+ if (!storage) {
169
+ return config(
170
+ (...args) => {
171
+ console.warn(
172
+ `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
173
+ );
174
+ set(...args);
175
+ },
176
+ get,
177
+ api
178
+ );
179
+ }
180
+ const setItem = () => {
181
+ const state = options.partialize({ ...get() });
182
+ return storage.setItem(options.name, {
183
+ state,
184
+ version: options.version
185
+ });
186
+ };
187
+ const savedSetState = api.setState;
188
+ api.setState = (state, replace) => {
189
+ savedSetState(state, replace);
190
+ return setItem();
191
+ };
192
+ const configResult = config(
193
+ (...args) => {
194
+ set(...args);
195
+ return setItem();
196
+ },
197
+ get,
198
+ api
199
+ );
200
+ api.getInitialState = () => configResult;
201
+ let stateFromStorage;
202
+ const hydrate = () => {
203
+ var _a, _b;
204
+ if (!storage) return;
205
+ hasHydrated = false;
206
+ hydrationListeners.forEach((cb) => {
207
+ var _a2;
208
+ return cb((_a2 = get()) != null ? _a2 : configResult);
209
+ });
210
+ const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;
211
+ return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {
212
+ if (deserializedStorageValue) {
213
+ if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) {
214
+ if (options.migrate) {
215
+ const migration = options.migrate(
216
+ deserializedStorageValue.state,
217
+ deserializedStorageValue.version
218
+ );
219
+ if (migration instanceof Promise) {
220
+ return migration.then((result) => [true, result]);
221
+ }
222
+ return [true, migration];
223
+ }
224
+ console.error(
225
+ `State loaded from storage couldn't be migrated since no migrate function was provided`
226
+ );
227
+ } else {
228
+ return [false, deserializedStorageValue.state];
229
+ }
230
+ }
231
+ return [false, void 0];
232
+ }).then((migrationResult) => {
233
+ var _a2;
234
+ const [migrated, migratedState] = migrationResult;
235
+ stateFromStorage = options.merge(
236
+ migratedState,
237
+ (_a2 = get()) != null ? _a2 : configResult
238
+ );
239
+ set(stateFromStorage, true);
240
+ if (migrated) {
241
+ return setItem();
242
+ }
243
+ }).then(() => {
244
+ postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);
245
+ stateFromStorage = get();
246
+ hasHydrated = true;
247
+ finishHydrationListeners.forEach((cb) => cb(stateFromStorage));
248
+ }).catch((e) => {
249
+ postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);
250
+ });
251
+ };
252
+ api.persist = {
253
+ setOptions: (newOptions) => {
254
+ options = {
255
+ ...options,
256
+ ...newOptions
257
+ };
258
+ if (newOptions.storage) {
259
+ storage = newOptions.storage;
260
+ }
261
+ },
262
+ clearStorage: () => {
263
+ storage == null ? void 0 : storage.removeItem(options.name);
264
+ },
265
+ getOptions: () => options,
266
+ rehydrate: () => hydrate(),
267
+ hasHydrated: () => hasHydrated,
268
+ onHydrate: (cb) => {
269
+ hydrationListeners.add(cb);
270
+ return () => {
271
+ hydrationListeners.delete(cb);
272
+ };
273
+ },
274
+ onFinishHydration: (cb) => {
275
+ finishHydrationListeners.add(cb);
276
+ return () => {
277
+ finishHydrationListeners.delete(cb);
278
+ };
279
+ }
280
+ };
281
+ if (!options.skipHydration) {
282
+ hydrate();
283
+ }
284
+ return stateFromStorage || configResult;
285
+ };
286
+ const persist = persistImpl;
287
+ const useChainsStore = create()(
288
+ persist(
289
+ (set, get) => ({
290
+ chains: void 0,
291
+ fromChain: void 0,
292
+ toChain: void 0,
293
+ allowedFromChains: void 0,
294
+ allowedToChains: void 0,
295
+ isLoadingToChains: false,
296
+ setChains: async (data) => {
297
+ const prev = get().chains;
298
+ const same = (() => {
299
+ if (!prev && !data) return true;
300
+ if (!prev || !data) return false;
301
+ if (prev.length !== data.length) return false;
302
+ for (let i = 0; i < prev.length; i++) {
303
+ if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
304
+ }
305
+ return true;
306
+ })();
307
+ if (same) return;
308
+ set({ chains: data });
309
+ },
310
+ setFromChain: (data) => {
311
+ const prev = get().fromChain;
312
+ if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
313
+ set({ fromChain: data });
314
+ },
315
+ setToChain: (data) => {
316
+ const prev = get().toChain;
317
+ if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
318
+ set({ toChain: data });
319
+ },
320
+ setAllowedFromChains: (data) => {
321
+ const prev = get().allowedFromChains;
322
+ const same = (() => {
323
+ if (!prev && !data) return true;
324
+ if (!prev || !data) return false;
325
+ if (prev.length !== data.length) return false;
326
+ for (let i = 0; i < prev.length; i++) {
327
+ if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
328
+ }
329
+ return true;
330
+ })();
331
+ if (same) return;
332
+ set({ allowedFromChains: data });
333
+ },
334
+ setAllowedToChains: (data) => {
335
+ const prev = get().allowedToChains;
336
+ const same = (() => {
337
+ if (!prev && !data) return true;
338
+ if (!prev || !data) return false;
339
+ if (prev.length !== data.length) return false;
340
+ for (let i = 0; i < prev.length; i++) {
341
+ if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
342
+ }
343
+ return true;
344
+ })();
345
+ if (same) return;
346
+ set({ allowedToChains: data });
347
+ },
348
+ setIsLoadingToChains: (v) => set({ isLoadingToChains: v }),
349
+ swapChains: () => set((state) => ({
350
+ fromChain: state.toChain,
351
+ toChain: state.fromChain
352
+ }))
353
+ }),
354
+ {
355
+ name: "evaa-bridge-chains",
356
+ storage: createJSONStorage(() => sessionStorage),
357
+ partialize: (state) => ({
358
+ fromChain: state.fromChain,
359
+ toChain: state.toChain
360
+ })
361
+ }
362
+ )
363
+ );
364
+ function normalizeTickerSymbol$1(s) {
365
+ return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
366
+ }
367
+ function normalizeTokenSymbol(token) {
368
+ let normalizedSymbol = token.symbol.toUpperCase().replace(/₮/g, "T");
369
+ normalizedSymbol = normalizedSymbol.replace(/^S\*/, "");
370
+ normalizedSymbol = normalizedSymbol.replace(/0\.S$/i, "").replace(/0$/, "").replace(/\.S$/i, "");
371
+ normalizedSymbol = normalizedSymbol.replace(/\.E$/i, "").replace(/\.N$/i, "");
372
+ return {
373
+ ...token,
374
+ symbol: normalizedSymbol
375
+ };
376
+ }
167
377
  const POPULAR_ORDER = [
168
378
  "USDT",
169
379
  "USDC",
@@ -199,6 +409,16 @@ function buildAssetMatrix(tokens) {
199
409
  if (!symbol) continue;
200
410
  (m[symbol] || (m[symbol] = {}))[t.chainKey] = t;
201
411
  }
412
+ const chainStats = {};
413
+ Object.values(m).forEach((byChain) => {
414
+ Object.keys(byChain).forEach((chainKey) => {
415
+ chainStats[chainKey] = (chainStats[chainKey] || 0) + 1;
416
+ });
417
+ });
418
+ console.log(
419
+ `[DEBUG] Asset matrix built: ${Object.keys(m).length} assets across chains:`,
420
+ chainStats
421
+ );
202
422
  return m;
203
423
  }
204
424
  function listAssetsForSelect(tokens) {
@@ -208,11 +428,11 @@ function listAssetsForSelect(tokens) {
208
428
  const preferred = pickOrder.map((k) => byChain[k]).find(Boolean) ?? Object.values(byChain)[0];
209
429
  return preferred;
210
430
  });
211
- const byNorm = new Map(base.map((e) => [norm(e.symbol), e]));
431
+ const byNorm = new Map(base.map((e) => [normalizeTickerSymbol$1(e.symbol), e]));
212
432
  const used = /* @__PURE__ */ new Set();
213
433
  const popular = [];
214
434
  for (const s of POPULAR_ORDER) {
215
- const key = norm(s);
435
+ const key = normalizeTickerSymbol$1(s);
216
436
  const item = byNorm.get(key);
217
437
  if (item && !used.has(key)) {
218
438
  popular.push(item);
@@ -220,11 +440,11 @@ function listAssetsForSelect(tokens) {
220
440
  }
221
441
  }
222
442
  const stable = base.filter((e) => {
223
- const k = norm(e.symbol);
443
+ const k = normalizeTickerSymbol$1(e.symbol);
224
444
  return !used.has(k) && STABLES.has(k);
225
445
  }).sort((a, b) => a.symbol.localeCompare(b.symbol));
226
- for (const e of stable) used.add(norm(e.symbol));
227
- const other = base.filter((e) => !used.has(norm(e.symbol))).sort((a, b) => a.symbol.localeCompare(b.symbol));
446
+ for (const e of stable) used.add(normalizeTickerSymbol$1(e.symbol));
447
+ const other = base.filter((e) => !used.has(normalizeTickerSymbol$1(e.symbol))).sort((a, b) => a.symbol.localeCompare(b.symbol));
228
448
  return [...popular, ...stable, ...other];
229
449
  }
230
450
  function resolveTokenOnChain(tokens, assetSymbol, chainKey) {
@@ -629,163 +849,636 @@ function isAddressValidForChain(chainKey, addr) {
629
849
  if (chainKey === "tron") return isTronAddress(addr);
630
850
  return isEvmAddress(addr);
631
851
  }
632
- function useBridgeQuote() {
633
- const { assetMatrix, selectedAssetSymbol } = useTokensStore();
634
- const { fromChain, toChain } = useChainsStore();
635
- const { srcAddress, dstAddress } = useAddresses();
636
- const { slippageBps, routePriority, getDstNativeAmount, getSlippageDecimal } = useSettingsStore();
637
- const {
638
- inputAmount,
639
- refetchTrigger,
640
- setLoading: setQuoteLoading,
641
- setQuote,
642
- setError: setQError,
643
- setNoRoute,
644
- resetWithIdle
645
- } = useBridgeQuoteStore();
646
- const input = inputAmount;
647
- const srcTokenOnFrom = useMemo(
648
- () => resolveTokenOnChainFromMatrix$2(
649
- assetMatrix,
650
- selectedAssetSymbol,
651
- fromChain?.chainKey
652
- ),
653
- [assetMatrix, selectedAssetSymbol, fromChain?.chainKey]
654
- );
655
- const dstTokenOnTo = useMemo(
656
- () => resolveTokenOnChainFromMatrix$2(
657
- assetMatrix,
658
- selectedAssetSymbol,
659
- toChain?.chainKey
660
- ),
661
- [assetMatrix, selectedAssetSymbol, toChain?.chainKey]
662
- );
663
- const [loading, setLoading] = useState(false);
664
- useEffect(() => {
665
- if (!input || input === "") {
666
- setLoading(false);
667
- resetWithIdle();
668
- }
669
- }, [input, resetWithIdle]);
670
- useEffect(() => {
671
- const resetUi = (withError) => {
672
- setLoading(false);
673
- {
674
- resetWithIdle();
675
- }
676
- };
677
- if (!input || Number(input) <= 0 || !fromChain?.chainKey || !toChain?.chainKey || !srcTokenOnFrom || !dstTokenOnTo || !srcAddress || !dstAddress) {
678
- resetUi();
679
- return;
680
- }
681
- if (!isAddressValidForChain(fromChain.chainKey, srcAddress) || !isAddressValidForChain(toChain.chainKey, dstAddress)) {
682
- resetUi();
683
- return;
684
- }
685
- if (fromChain.chainKey === toChain.chainKey) {
686
- resetUi();
687
- return;
688
- }
689
- let cancelled = false;
690
- const run = async () => {
691
- try {
692
- setLoading(true);
693
- setQuoteLoading?.();
694
- if (assetMatrix && selectedAssetSymbol) {
695
- const tokenMatrix = assetMatrix[selectedAssetSymbol.toUpperCase()];
696
- if (!tokenMatrix || !tokenMatrix[fromChain.chainKey] || !tokenMatrix[toChain.chainKey]) {
697
- if (!cancelled) {
698
- resetUi();
699
- }
700
- return;
701
- }
702
- }
703
- const srcAmountLD = toLD(input, srcTokenOnFrom.decimals);
704
- const srcAddrApi = addrForApi(fromChain.chainKey, srcAddress);
705
- const dstAddrApi = addrForApi(toChain.chainKey, dstAddress);
706
- const dstNativeAmount = getDstNativeAmount(toChain.chainKey);
707
- const slippageDecimal = getSlippageDecimal();
708
- const approximateMinLD = BigInt(srcAmountLD) * BigInt(9500) / BigInt(1e4);
709
- const quoteRoute = await getQuotesByPriority({
710
- srcChainKey: fromChain.chainKey,
711
- dstChainKey: toChain.chainKey,
712
- srcToken: srcTokenOnFrom.address,
713
- dstToken: dstTokenOnTo.address,
714
- srcAmountLD,
715
- dstAmountMinLD: approximateMinLD.toString(),
716
- srcAddress: srcAddrApi,
717
- dstAddress: dstAddrApi,
718
- dstNativeAmount,
719
- slippage: slippageDecimal,
720
- routePriority
721
- });
722
- if (quoteRoute?.route === null) {
723
- setNoRoute(true);
724
- if (!cancelled) resetUi();
725
- return;
726
- } else {
727
- setNoRoute(false);
728
- }
729
- if (!quoteRoute || !quoteRoute.dstAmount || BigInt(quoteRoute.dstAmount) === BigInt(0)) {
730
- if (!cancelled) resetUi();
731
- return;
732
- }
733
- if (cancelled) return;
734
- setQuote(quoteRoute);
735
- } catch {
736
- if (!cancelled) {
737
- resetUi();
738
- }
739
- } finally {
740
- if (!cancelled) setLoading(false);
741
- }
742
- };
743
- run();
744
- return () => {
745
- cancelled = true;
746
- };
747
- }, [
748
- input,
749
- fromChain?.chainKey,
750
- toChain?.chainKey,
751
- srcTokenOnFrom,
752
- dstTokenOnTo,
753
- srcAddress,
754
- dstAddress,
755
- setQuoteLoading,
756
- setQuote,
757
- setQError,
758
- slippageBps,
759
- routePriority,
760
- refetchTrigger
761
- ]);
762
- return { loading };
763
- }
764
- async function getChains() {
765
- const res = await fetch("https://stargate.finance/api/v1/chains", {
766
- credentials: "same-origin"
767
- });
768
- if (!res.ok) {
769
- throw new Error(`Failed to load chains: ${res.status}`);
852
+ const ChainStrategyContext = createContext(void 0);
853
+ function useChainStrategies() {
854
+ const context = useContext(ChainStrategyContext);
855
+ if (!context) {
856
+ throw new Error(
857
+ "useChainStrategies must be used within ChainStrategyProvider"
858
+ );
770
859
  }
771
- const data = await res.json();
772
- return Array.isArray(data) ? data : data.chains ?? [];
860
+ return context;
773
861
  }
774
- async function getTokens() {
775
- const res = await fetch("https://stargate.finance/api/v1/tokens", {
776
- credentials: "same-origin"
777
- });
778
- if (!res.ok) {
779
- throw new Error(`Failed to load chains: ${res.status}`);
862
+ const truncateToDecimals = (num, decimals) => {
863
+ if (!isFinite(num) || isNaN(num)) return "0.00";
864
+ const multiplier = Math.pow(10, decimals);
865
+ const truncated = Math.floor(num * multiplier) / multiplier;
866
+ return truncated.toFixed(decimals);
867
+ };
868
+ const formatTokenAmount = (amount, symbol, options) => {
869
+ const normalizedSymbol = (symbol ?? "").toUpperCase();
870
+ if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
871
+ return `${Math.floor(amount)} ${normalizedSymbol}`;
780
872
  }
781
- const data = await res.json();
782
- return Array.isArray(data) ? data : data.tokens ?? [];
783
- }
784
- async function getDestTokens(srcChainKey, srcTokenAddr) {
785
- const url = new URL("https://stargate.finance/api/v1/tokens");
786
- url.searchParams.set("srcChainKey", srcChainKey);
787
- url.searchParams.set("srcToken", srcTokenAddr);
788
- const res = await fetch(url.toString(), { credentials: "omit" });
873
+ if (options?.decimals !== void 0) {
874
+ return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
875
+ }
876
+ if (amount >= 1) {
877
+ return `${amount.toFixed(0)} ${normalizedSymbol}`;
878
+ } else if (amount >= 1e-3) {
879
+ return `${amount.toFixed(3)} ${normalizedSymbol}`;
880
+ } else {
881
+ return `${amount.toFixed(6)} ${normalizedSymbol}`;
882
+ }
883
+ };
884
+ const formatUsd = (value) => {
885
+ if (!value || !isFinite(value)) return "$0";
886
+ if (value >= 1) return `$${value.toFixed(2)}`;
887
+ return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
888
+ };
889
+ const formatPercentage = (bps, decimals = 2) => {
890
+ return `${(bps / 100).toFixed(decimals)}%`;
891
+ };
892
+ const formatBalance = (amount, decimals = 2) => {
893
+ if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
894
+ return "0.00";
895
+ }
896
+ return amount.toFixed(decimals);
897
+ };
898
+ const formatHash = (hash, startChars = 4, endChars = 4) => {
899
+ if (!hash) return "";
900
+ if (hash.length <= startChars + endChars) return hash;
901
+ return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
902
+ };
903
+ const formatAddress = formatHash;
904
+ const EVM_CONFIG = {
905
+ usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
906
+ gasEstimates: {
907
+ approve: 65000n,
908
+ bridge: 300000n
909
+ },
910
+ gasBuffer: 1.2,
911
+ // 20% buffer
912
+ timeout: 3e5,
913
+ // 5 minutes (increased for slower networks)
914
+ requiredConfirmations: 3
915
+ // Wait for 3 confirmations for reorg protection
916
+ };
917
+ const TON_CONFIG = {
918
+ apiUrl: "https://toncenter.com/api/v2",
919
+ timeout: 36e4,
920
+ // 6 minutes
921
+ validUntil: 600,
922
+ // 10 minutes
923
+ pollingInterval: 5e3,
924
+ // 5 seconds between transaction status checks
925
+ estimatedNetworkFee: "100000000"
926
+ // 0.1 TON in nanoton (conservative estimate)
927
+ };
928
+ const TRON_CONFIG = {
929
+ timeout: 12e4,
930
+ // 2 minutes (for 19 confirmations)
931
+ feeLimit: 1e8,
932
+ // 100 TRX in sun
933
+ requiredConfirmations: 19,
934
+ // TRON standard: 19 blocks for confirmation
935
+ pollingInterval: 3e3,
936
+ // 3 seconds between checks
937
+ estimatedNetworkFee: "10000000"
938
+ // 10 TRX in SUN (fallback estimate)
939
+ };
940
+ let tonClientInstance = null;
941
+ function getTonClient(customClient, apiKey) {
942
+ if (customClient) {
943
+ return customClient;
944
+ }
945
+ if (!tonClientInstance) {
946
+ tonClientInstance = new TonClient({
947
+ endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
948
+ apiKey
949
+ });
950
+ }
951
+ return tonClientInstance;
952
+ }
953
+ function getQuoteAmounts(quote, srcToken, dstToken) {
954
+ if (!quote || !srcToken || !dstToken) {
955
+ return {
956
+ inputHuman: 0,
957
+ outputHuman: 0,
958
+ outputHumanRounded: "0",
959
+ minReceivedHuman: 0
960
+ };
961
+ }
962
+ const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
963
+ const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
964
+ const outputHumanRounded = truncateToDecimals(outputHuman, 2);
965
+ const minReceivedHuman = fromLD(
966
+ quote.dstAmountMin || "0",
967
+ dstToken.decimals
968
+ );
969
+ return {
970
+ inputHuman,
971
+ outputHuman,
972
+ outputHumanRounded,
973
+ minReceivedHuman
974
+ };
975
+ }
976
+ function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
977
+ if (!quote || !tokens || !chains) {
978
+ return {
979
+ totalUsd: 0,
980
+ protocolFeeUsd: void 0,
981
+ messageFeeUsd: void 0,
982
+ serviceUsd: void 0,
983
+ blockchainUsd: void 0,
984
+ inSrcToken: void 0,
985
+ inDstToken: void 0
986
+ };
987
+ }
988
+ const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
989
+ let inSrcToken = void 0;
990
+ let inDstToken = void 0;
991
+ if (srcToken && quote.srcChainKey) {
992
+ const feeInSrcLD = sumFeeByTokenLD(
993
+ quote.fees,
994
+ srcToken.address,
995
+ quote.srcChainKey
996
+ );
997
+ const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
998
+ if (feeInSrcHuman > 0) {
999
+ inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
1000
+ } else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
1001
+ const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
1002
+ inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
1003
+ }
1004
+ }
1005
+ if (dstToken && quote.dstChainKey) {
1006
+ const feeInDstLD = sumFeeByTokenLD(
1007
+ quote.fees,
1008
+ dstToken.address,
1009
+ quote.dstChainKey
1010
+ );
1011
+ const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
1012
+ if (feeInDstHuman > 0) {
1013
+ inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
1014
+ }
1015
+ }
1016
+ return {
1017
+ totalUsd: feeData.totalUsd,
1018
+ protocolFeeUsd: feeData.protocolFeeUsd,
1019
+ messageFeeUsd: feeData.messageFeeUsd,
1020
+ serviceUsd: feeData.serviceUsd,
1021
+ blockchainUsd: feeData.blockchainUsd,
1022
+ inSrcToken,
1023
+ inDstToken
1024
+ };
1025
+ }
1026
+ function calculateMinReceived(quote, slippageBps, dstToken) {
1027
+ if (!quote || !dstToken) return 0;
1028
+ const dstAmountLD = BigInt(quote.dstAmount);
1029
+ const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
1030
+ return fromLD(minAmountLD.toString(), dstToken.decimals);
1031
+ }
1032
+ function estimateTonNetworkFee(quote) {
1033
+ try {
1034
+ const tonStep = quote.steps?.find(
1035
+ (s) => s.chainKey.toLowerCase() === "ton" && s.type === "bridge"
1036
+ );
1037
+ if (!tonStep?.transaction?.messages) {
1038
+ console.warn("No TON messages found in quote, using fallback");
1039
+ return TON_CONFIG.estimatedNetworkFee;
1040
+ }
1041
+ const messages = tonStep.transaction.messages;
1042
+ const messageCount = messages.length;
1043
+ const baseFeePerMessage = 10000000n;
1044
+ let totalFee = 0n;
1045
+ for (const message of messages) {
1046
+ let messageFee = baseFeePerMessage;
1047
+ if (message.payload) {
1048
+ const payloadSize = message.payload.length;
1049
+ const sizeFee = BigInt(Math.floor(payloadSize / 100)) * 100000n;
1050
+ messageFee += sizeFee;
1051
+ }
1052
+ if (message.amount) {
1053
+ const amount = BigInt(message.amount);
1054
+ if (amount > 0n && message.payload) {
1055
+ messageFee += 5000000n;
1056
+ }
1057
+ }
1058
+ totalFee += messageFee;
1059
+ }
1060
+ const minBuffer = 50000000n;
1061
+ totalFee = totalFee > minBuffer ? totalFee : minBuffer;
1062
+ totalFee = totalFee * 120n / 100n;
1063
+ const maxFee = 1000000000n;
1064
+ if (totalFee > maxFee) {
1065
+ totalFee = maxFee;
1066
+ }
1067
+ console.log(
1068
+ `TON fee estimate: ${messageCount} messages = ${Number(totalFee) / 1e9} TON`
1069
+ );
1070
+ return totalFee.toString();
1071
+ } catch (error) {
1072
+ console.warn("Failed to estimate TON fee:", error);
1073
+ return TON_CONFIG.estimatedNetworkFee;
1074
+ }
1075
+ }
1076
+ function addTonNetworkFee(quote, chains, estimatedFee) {
1077
+ if (!quote || quote.srcChainKey.toLowerCase() !== "ton") {
1078
+ return quote;
1079
+ }
1080
+ const tonChain = chains?.find(
1081
+ (c) => c.chainKey.toLowerCase() === "ton"
1082
+ );
1083
+ if (!tonChain?.nativeCurrency?.address) {
1084
+ console.warn("Could not find TON native currency address");
1085
+ return quote;
1086
+ }
1087
+ const networkFee = {
1088
+ token: tonChain.nativeCurrency.address,
1089
+ chainKey: "ton",
1090
+ amount: estimatedFee || TON_CONFIG.estimatedNetworkFee,
1091
+ type: "network"
1092
+ };
1093
+ const hasNetworkFee = quote.fees?.some(
1094
+ (fee) => fee.type === "network" && fee.chainKey === "ton"
1095
+ );
1096
+ if (hasNetworkFee) {
1097
+ return quote;
1098
+ }
1099
+ return {
1100
+ ...quote,
1101
+ fees: [...quote.fees || [], networkFee]
1102
+ };
1103
+ }
1104
+ async function estimateTronNetworkFee(tronWeb, quote, userAddress) {
1105
+ try {
1106
+ const accountResources = await tronWeb.trx.getAccountResources(userAddress);
1107
+ const availableEnergy = accountResources.EnergyLimit || 0;
1108
+ const tronStep = quote.steps?.find(
1109
+ (s) => s.chainKey.toLowerCase() === "tron" && s.type === "bridge"
1110
+ );
1111
+ if (!tronStep?.transaction?.data || !tronStep.transaction.to) {
1112
+ console.warn("No TRON transaction data in quote, using fallback");
1113
+ return TRON_CONFIG.estimatedNetworkFee;
1114
+ }
1115
+ const contractAddress = tronWeb.address.fromHex(
1116
+ tronStep.transaction.to.startsWith("41") ? tronStep.transaction.to : "41" + tronStep.transaction.to.slice(2)
1117
+ );
1118
+ const simulation = await tronWeb.transactionBuilder.triggerSmartContract(
1119
+ contractAddress,
1120
+ "function signature doesn't matter for energy estimation",
1121
+ {
1122
+ feeLimit: TRON_CONFIG.feeLimit,
1123
+ callValue: tronStep.transaction.value ? Number(tronStep.transaction.value) : 0
1124
+ },
1125
+ [],
1126
+ userAddress
1127
+ );
1128
+ const requiredEnergy = simulation.energy_used || 0;
1129
+ const energyDeficit = Math.max(0, requiredEnergy - availableEnergy);
1130
+ if (energyDeficit === 0) {
1131
+ return "0";
1132
+ }
1133
+ const energyPriceInSun = 420;
1134
+ const feeSun = energyDeficit * energyPriceInSun;
1135
+ console.log(
1136
+ `TRON fee estimation: ${requiredEnergy} energy required, ${availableEnergy} available, ${feeSun / 1e6} TRX fee`
1137
+ );
1138
+ return feeSun.toString();
1139
+ } catch (error) {
1140
+ console.warn("Failed to estimate TRON network fee:", error);
1141
+ return TRON_CONFIG.estimatedNetworkFee;
1142
+ }
1143
+ }
1144
+ function addTronNetworkFee(quote, chains, estimatedFee) {
1145
+ if (!quote || quote.srcChainKey.toLowerCase() !== "tron") {
1146
+ return quote;
1147
+ }
1148
+ const tronChain = chains?.find(
1149
+ (c) => c.chainKey.toLowerCase() === "tron"
1150
+ );
1151
+ if (!tronChain?.nativeCurrency?.address) {
1152
+ console.warn("Could not find TRON native currency address");
1153
+ return quote;
1154
+ }
1155
+ const networkFee = {
1156
+ token: tronChain.nativeCurrency.address,
1157
+ chainKey: "tron",
1158
+ amount: estimatedFee || TRON_CONFIG.estimatedNetworkFee,
1159
+ type: "network"
1160
+ };
1161
+ const hasNetworkFee = quote.fees?.some(
1162
+ (fee) => fee.type === "network" && fee.chainKey === "tron"
1163
+ );
1164
+ if (hasNetworkFee) {
1165
+ return quote;
1166
+ }
1167
+ return {
1168
+ ...quote,
1169
+ fees: [...quote.fees || [], networkFee]
1170
+ };
1171
+ }
1172
+ async function estimateEvmNetworkFee(publicClient, quote, chainKey) {
1173
+ try {
1174
+ let totalGasLimit = 0n;
1175
+ for (const step of quote.steps || []) {
1176
+ if (step.chainKey.toLowerCase() !== chainKey.toLowerCase()) continue;
1177
+ if (step.type === "approve") {
1178
+ totalGasLimit += EVM_CONFIG.gasEstimates.approve;
1179
+ } else if (step.type === "bridge") {
1180
+ totalGasLimit += EVM_CONFIG.gasEstimates.bridge;
1181
+ }
1182
+ }
1183
+ if (totalGasLimit === 0n) {
1184
+ return "0";
1185
+ }
1186
+ totalGasLimit = totalGasLimit * BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100)) / 100n;
1187
+ const gasPrice = await publicClient.getGasPrice();
1188
+ const totalCostWei = totalGasLimit * gasPrice;
1189
+ console.log(
1190
+ `EVM gas estimate: ${totalGasLimit} gas × ${gasPrice} Wei/gas = ${totalCostWei} Wei`
1191
+ );
1192
+ return totalCostWei.toString();
1193
+ } catch (error) {
1194
+ console.warn("Failed to estimate EVM gas fee:", error);
1195
+ return "10000000000000000";
1196
+ }
1197
+ }
1198
+ function addEvmNetworkFee(quote, chains, estimatedFee) {
1199
+ if (!quote) return quote;
1200
+ const srcChainKey = quote.srcChainKey.toLowerCase();
1201
+ if (srcChainKey === "ton" || srcChainKey === "tron") {
1202
+ return quote;
1203
+ }
1204
+ const srcChain = chains?.find(
1205
+ (c) => c.chainKey.toLowerCase() === srcChainKey
1206
+ );
1207
+ if (!srcChain?.nativeCurrency?.address) {
1208
+ return quote;
1209
+ }
1210
+ const hasNetworkFee = quote.fees?.some(
1211
+ (fee) => fee.type === "network" && fee.chainKey.toLowerCase() === srcChainKey
1212
+ );
1213
+ if (hasNetworkFee) {
1214
+ return quote;
1215
+ }
1216
+ const networkFee = {
1217
+ token: srcChain.nativeCurrency.address,
1218
+ chainKey: quote.srcChainKey,
1219
+ amount: estimatedFee || "10000000000000000",
1220
+ // 0.01 ETH fallback
1221
+ type: "network"
1222
+ };
1223
+ return {
1224
+ ...quote,
1225
+ fees: [...quote.fees || [], networkFee]
1226
+ };
1227
+ }
1228
+ function getQuoteDetails(quote, srcToken, dstToken, tokens, chains, slippageBps) {
1229
+ const amounts = getQuoteAmounts(quote, srcToken, dstToken);
1230
+ const fees = getQuoteFees(quote, tokens, chains, srcToken, dstToken);
1231
+ const minimumReceived = calculateMinReceived(quote, slippageBps, dstToken);
1232
+ return {
1233
+ inputAmount: amounts.inputHuman,
1234
+ outputAmount: amounts.outputHuman,
1235
+ outputAmountRounded: amounts.outputHumanRounded,
1236
+ minimumReceived,
1237
+ etaSeconds: quote?.duration?.estimated,
1238
+ fees
1239
+ };
1240
+ }
1241
+ function useBridgeQuote() {
1242
+ const { assetMatrix, selectedAssetSymbol } = useTokensStore();
1243
+ const { fromChain, toChain, chains } = useChainsStore();
1244
+ const { srcAddress, dstAddress } = useAddresses();
1245
+ const { slippageBps, routePriority, getDstNativeAmount, getSlippageDecimal } = useSettingsStore();
1246
+ const {
1247
+ inputAmount,
1248
+ refetchTrigger,
1249
+ setLoading: setQuoteLoading,
1250
+ setQuote,
1251
+ setError: setQError,
1252
+ setNoRoute,
1253
+ resetWithIdle
1254
+ } = useBridgeQuoteStore();
1255
+ const { chainRegistry } = useChainStrategies();
1256
+ const publicClient = usePublicClient();
1257
+ const input = inputAmount;
1258
+ const srcTokenOnFrom = useMemo(
1259
+ () => resolveTokenOnChainFromMatrix$2(
1260
+ assetMatrix,
1261
+ selectedAssetSymbol,
1262
+ fromChain?.chainKey
1263
+ ),
1264
+ [assetMatrix, selectedAssetSymbol, fromChain?.chainKey]
1265
+ );
1266
+ const dstTokenOnTo = useMemo(
1267
+ () => resolveTokenOnChainFromMatrix$2(
1268
+ assetMatrix,
1269
+ selectedAssetSymbol,
1270
+ toChain?.chainKey
1271
+ ),
1272
+ [assetMatrix, selectedAssetSymbol, toChain?.chainKey]
1273
+ );
1274
+ const [loading, setLoading] = useState(false);
1275
+ useEffect(() => {
1276
+ if (!input || input === "") {
1277
+ setLoading(false);
1278
+ resetWithIdle();
1279
+ }
1280
+ }, [input, resetWithIdle]);
1281
+ useEffect(() => {
1282
+ const resetUi = (withError) => {
1283
+ setLoading(false);
1284
+ {
1285
+ resetWithIdle();
1286
+ }
1287
+ };
1288
+ if (!input || Number(input) <= 0 || !fromChain?.chainKey || !toChain?.chainKey || !srcTokenOnFrom || !dstTokenOnTo || !srcAddress || !dstAddress) {
1289
+ resetUi();
1290
+ return;
1291
+ }
1292
+ if (!isAddressValidForChain(fromChain.chainKey, srcAddress) || !isAddressValidForChain(toChain.chainKey, dstAddress)) {
1293
+ resetUi();
1294
+ return;
1295
+ }
1296
+ if (fromChain.chainKey === toChain.chainKey) {
1297
+ resetUi();
1298
+ return;
1299
+ }
1300
+ let cancelled = false;
1301
+ const run = async () => {
1302
+ try {
1303
+ setLoading(true);
1304
+ setQuoteLoading?.();
1305
+ if (assetMatrix && selectedAssetSymbol) {
1306
+ const tokenMatrix = assetMatrix[selectedAssetSymbol.toUpperCase()];
1307
+ if (!tokenMatrix || !tokenMatrix[fromChain.chainKey] || !tokenMatrix[toChain.chainKey]) {
1308
+ if (!cancelled) {
1309
+ resetUi();
1310
+ }
1311
+ return;
1312
+ }
1313
+ }
1314
+ const srcAmountLD = toLD(input, srcTokenOnFrom.decimals);
1315
+ const srcAddrApi = addrForApi(fromChain.chainKey, srcAddress);
1316
+ const dstAddrApi = addrForApi(toChain.chainKey, dstAddress);
1317
+ const dstNativeAmount = getDstNativeAmount(toChain.chainKey);
1318
+ const slippageDecimal = getSlippageDecimal();
1319
+ const approximateMinLD = BigInt(srcAmountLD) * BigInt(9500) / BigInt(1e4);
1320
+ const quoteRoute = await getQuotesByPriority({
1321
+ srcChainKey: fromChain.chainKey,
1322
+ dstChainKey: toChain.chainKey,
1323
+ srcToken: srcTokenOnFrom.address,
1324
+ dstToken: dstTokenOnTo.address,
1325
+ srcAmountLD,
1326
+ dstAmountMinLD: approximateMinLD.toString(),
1327
+ srcAddress: srcAddrApi,
1328
+ dstAddress: dstAddrApi,
1329
+ dstNativeAmount,
1330
+ slippage: slippageDecimal,
1331
+ routePriority
1332
+ });
1333
+ if (quoteRoute?.route === null) {
1334
+ setNoRoute(true);
1335
+ if (!cancelled) resetUi();
1336
+ return;
1337
+ } else {
1338
+ setNoRoute(false);
1339
+ }
1340
+ if (!quoteRoute || !quoteRoute.dstAmount || BigInt(quoteRoute.dstAmount) === BigInt(0)) {
1341
+ if (!cancelled) resetUi();
1342
+ return;
1343
+ }
1344
+ if (cancelled) return;
1345
+ let quoteWithFees = quoteRoute;
1346
+ if (quoteRoute.srcChainKey.toLowerCase() === "ton") {
1347
+ try {
1348
+ const estimatedFee = estimateTonNetworkFee(quoteRoute);
1349
+ quoteWithFees = addTonNetworkFee(quoteRoute, chains, estimatedFee);
1350
+ console.log("TON network fee estimated for quote:", estimatedFee);
1351
+ } catch (error) {
1352
+ console.warn("Failed to estimate TON fee, using fallback:", error);
1353
+ quoteWithFees = addTonNetworkFee(quoteRoute, chains);
1354
+ }
1355
+ }
1356
+ if (quoteRoute.srcChainKey.toLowerCase() === "tron") {
1357
+ const tronStrategy = chainRegistry.getStrategy("tron");
1358
+ if (tronStrategy?.isConnected()) {
1359
+ const tronWeb = tronStrategy.getClient();
1360
+ const tronAddress = tronStrategy.getAccount();
1361
+ if (tronWeb && tronAddress) {
1362
+ try {
1363
+ const estimatedFee = await estimateTronNetworkFee(
1364
+ tronWeb,
1365
+ quoteRoute,
1366
+ tronAddress
1367
+ );
1368
+ quoteWithFees = addTronNetworkFee(
1369
+ quoteWithFees || quoteRoute,
1370
+ chains,
1371
+ estimatedFee
1372
+ );
1373
+ console.log("TRON network fee estimated for quote:", estimatedFee);
1374
+ } catch (error) {
1375
+ console.warn("Failed to estimate TRON fee, using fallback:", error);
1376
+ quoteWithFees = addTronNetworkFee(
1377
+ quoteWithFees || quoteRoute,
1378
+ chains
1379
+ );
1380
+ }
1381
+ } else {
1382
+ quoteWithFees = addTronNetworkFee(
1383
+ quoteWithFees || quoteRoute,
1384
+ chains
1385
+ );
1386
+ }
1387
+ } else {
1388
+ quoteWithFees = addTronNetworkFee(
1389
+ quoteWithFees || quoteRoute,
1390
+ chains
1391
+ );
1392
+ }
1393
+ }
1394
+ const srcChainKey = quoteRoute.srcChainKey.toLowerCase();
1395
+ if (srcChainKey !== "ton" && srcChainKey !== "tron") {
1396
+ if (publicClient) {
1397
+ try {
1398
+ const estimatedFee = await estimateEvmNetworkFee(
1399
+ publicClient,
1400
+ quoteRoute,
1401
+ quoteRoute.srcChainKey
1402
+ );
1403
+ quoteWithFees = addEvmNetworkFee(
1404
+ quoteWithFees || quoteRoute,
1405
+ chains,
1406
+ estimatedFee
1407
+ );
1408
+ console.log("EVM network fee estimated for quote:", estimatedFee);
1409
+ } catch (error) {
1410
+ console.warn("Failed to estimate EVM fee, using fallback:", error);
1411
+ quoteWithFees = addEvmNetworkFee(
1412
+ quoteWithFees || quoteRoute,
1413
+ chains
1414
+ );
1415
+ }
1416
+ } else {
1417
+ quoteWithFees = addEvmNetworkFee(
1418
+ quoteWithFees || quoteRoute,
1419
+ chains
1420
+ );
1421
+ }
1422
+ }
1423
+ setQuote(quoteWithFees || quoteRoute);
1424
+ } catch {
1425
+ if (!cancelled) {
1426
+ resetUi();
1427
+ }
1428
+ } finally {
1429
+ if (!cancelled) setLoading(false);
1430
+ }
1431
+ };
1432
+ run();
1433
+ return () => {
1434
+ cancelled = true;
1435
+ };
1436
+ }, [
1437
+ input,
1438
+ fromChain?.chainKey,
1439
+ toChain?.chainKey,
1440
+ srcTokenOnFrom,
1441
+ dstTokenOnTo,
1442
+ srcAddress,
1443
+ dstAddress,
1444
+ setQuoteLoading,
1445
+ setQuote,
1446
+ setQError,
1447
+ slippageBps,
1448
+ routePriority,
1449
+ refetchTrigger,
1450
+ chainRegistry,
1451
+ chains,
1452
+ publicClient
1453
+ ]);
1454
+ return { loading };
1455
+ }
1456
+ async function getChains() {
1457
+ const res = await fetch("https://stargate.finance/api/v1/chains", {
1458
+ credentials: "same-origin"
1459
+ });
1460
+ if (!res.ok) {
1461
+ throw new Error(`Failed to load chains: ${res.status}`);
1462
+ }
1463
+ const data = await res.json();
1464
+ return Array.isArray(data) ? data : data.chains ?? [];
1465
+ }
1466
+ async function getTokens() {
1467
+ const res = await fetch("https://stargate.finance/api/v1/tokens", {
1468
+ credentials: "same-origin"
1469
+ });
1470
+ if (!res.ok) {
1471
+ throw new Error(`Failed to load chains: ${res.status}`);
1472
+ }
1473
+ const data = await res.json();
1474
+ const tokens = Array.isArray(data) ? data : data.tokens ?? [];
1475
+ return tokens.map(normalizeTokenSymbol);
1476
+ }
1477
+ async function getDestTokens(srcChainKey, srcTokenAddr) {
1478
+ const url = new URL("https://stargate.finance/api/v1/tokens");
1479
+ url.searchParams.set("srcChainKey", srcChainKey);
1480
+ url.searchParams.set("srcToken", srcTokenAddr);
1481
+ const res = await fetch(url.toString(), { credentials: "omit" });
789
1482
  if (!res.ok) throw new Error(`Failed to load dest tokens: ${res.status}`);
790
1483
  const data = await res.json();
791
1484
  const raw = data.tokens ?? [];
@@ -806,10 +1499,7 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
806
1499
  if (sa === sb) return (a.name ?? "").localeCompare(b.name ?? "");
807
1500
  return sa.localeCompare(sb);
808
1501
  });
809
- return unique;
810
- }
811
- function normalizeTickerSymbol$1(s) {
812
- return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
1502
+ return unique.map(normalizeTokenSymbol);
813
1503
  }
814
1504
  function resolveTokenOnChainFromMatrix$1(assetMatrix, assetSymbol, chainKey) {
815
1505
  if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
@@ -836,7 +1526,14 @@ const useChainDerivations = () => {
836
1526
  return [];
837
1527
  const byChain = assetMatrix[selectedAssetSymbol.toUpperCase()];
838
1528
  const keys = new Set(Object.keys(byChain ?? {}));
839
- return chains.filter((c) => keys.has(c.chainKey));
1529
+ const result = chains.filter((c) => keys.has(c.chainKey));
1530
+ console.log(
1531
+ `[DEBUG] supportedFrom for ${selectedAssetSymbol}:`,
1532
+ `${result.length} of ${chains.length} chains`,
1533
+ result.map((c) => c.chainKey),
1534
+ `| Has Arbitrum: ${result.some((c) => c.chainKey === "arbitrum")}`
1535
+ );
1536
+ return result;
840
1537
  }, [selectedAssetSymbol, assetMatrix, chains]);
841
1538
  useEffect(() => {
842
1539
  setAllowedFromChains(supportedFrom);
@@ -904,6 +1601,13 @@ const useChainDerivations = () => {
904
1601
  if (fromChain?.chainKey) {
905
1602
  list = list.filter((c) => c.chainKey !== fromChain.chainKey);
906
1603
  }
1604
+ console.log(
1605
+ `[DEBUG] allowedToChains from ${fromChain?.chainKey}:`,
1606
+ `${list.length} destinations`,
1607
+ list.map((c) => c.chainKey),
1608
+ `| Has Arbitrum: ${list.some((c) => c.chainKey === "arbitrum")}`,
1609
+ `| API returned ${dest.length} tokens, filtered to ${filteredDest.length}`
1610
+ );
907
1611
  setAllowedToChains(list);
908
1612
  if (!toChain || !list.some((c) => c.chainKey === toChain.chainKey)) {
909
1613
  setToChain(list[0]);
@@ -938,16 +1642,6 @@ const useChainDerivations = () => {
938
1642
  isLoadingToChains
939
1643
  };
940
1644
  };
941
- const ChainStrategyContext = createContext(void 0);
942
- function useChainStrategies() {
943
- const context = useContext(ChainStrategyContext);
944
- if (!context) {
945
- throw new Error(
946
- "useChainStrategies must be used within ChainStrategyProvider"
947
- );
948
- }
949
- return context;
950
- }
951
1645
  function useBalances(chainKey, address, priorityTokenSymbol) {
952
1646
  const { chainRegistry } = useChainStrategies();
953
1647
  const { assetMatrix } = useTokensStore();
@@ -1311,7 +2005,7 @@ const StargateIcon = (props) => {
1311
2005
  "path",
1312
2006
  {
1313
2007
  d: "M4.37473 7.21715L4.86639 7.00685C5.83193 6.59514 6.59903 5.82804 7.01074 4.86248L7.22104 4.37082C7.51425 3.68368 8.49163 3.68368 8.78489 4.37082L8.99514 4.86248C9.40685 5.82804 10.1739 6.59514 11.1395 7.00685L11.6312 7.21715C12.3183 7.51036 12.3183 8.4848 11.6312 8.78095L11.1395 8.99125C10.1739 9.40296 9.40685 10.1701 8.99514 11.1356L8.78489 11.6273C8.49163 12.3144 7.51425 12.3144 7.22104 11.6273L7.01074 11.1356C6.59903 10.1701 5.83193 9.40296 4.86639 8.99125L4.37473 8.78095C3.68759 8.48774 3.68759 7.5133 4.37473 7.21715Z",
1314
- fill: "#FAFAFA"
2008
+ fill: "currentColor"
1315
2009
  }
1316
2010
  )
1317
2011
  ] }),
@@ -1447,7 +2141,7 @@ const WalletConnectIcon = (props) => {
1447
2141
  }
1448
2142
  );
1449
2143
  };
1450
- const TonKeeperIcon = (props) => {
2144
+ const TonConnectIcon = (props) => {
1451
2145
  return /* @__PURE__ */ jsxs(
1452
2146
  "svg",
1453
2147
  {
@@ -1637,7 +2331,7 @@ const SelectNetworkButton = ({
1637
2331
  size: "sm",
1638
2332
  variant: "secondary",
1639
2333
  type: "button",
1640
- className: "shrink-0 gap-2 bg-card text-card-foreground hover:text-card-foreground h-9",
2334
+ className: "shrink-0 gap-2 h-9 !pl-2",
1641
2335
  "aria-label": label,
1642
2336
  children: [
1643
2337
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
@@ -1728,49 +2422,37 @@ const SearchInput = ({
1728
2422
  placeholder,
1729
2423
  value,
1730
2424
  onChange,
1731
- className,
1732
- containerClassName
2425
+ className
1733
2426
  }) => {
1734
- const [isFocused, setIsFocused] = useState(false);
1735
- return /* @__PURE__ */ jsxs(
1736
- "div",
1737
- {
1738
- className: cn(
1739
- "flex items-center gap-3 px-5 py-4 bg-input rounded-md h-13 transition-all duration-200",
1740
- isFocused ? "border border-ring" : "border border-transparent",
1741
- containerClassName
1742
- ),
1743
- children: [
1744
- /* @__PURE__ */ jsx(SearchIcon, { className: "w-6 h-6 text-input-icon" }),
1745
- /* @__PURE__ */ jsx(
1746
- Input,
1747
- {
1748
- placeholder,
1749
- className: cn(
1750
- "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 focus-visible:ring-0 focus-visible:ring-offset-0",
1751
- className
1752
- ),
1753
- value,
1754
- onChange: (e) => onChange(e.target.value),
1755
- onFocus: () => setIsFocused(true),
1756
- onBlur: () => setIsFocused(false)
1757
- }
1758
- )
1759
- ]
1760
- }
1761
- );
2427
+ return /* @__PURE__ */ jsxs("div", { className: cn("rounded-xs relative"), children: [
2428
+ /* @__PURE__ */ jsx(SearchIcon, { className: "w-6 h-6 absolute left-5 top-0 bottom-0 my-auto" }),
2429
+ /* @__PURE__ */ jsx(
2430
+ Input,
2431
+ {
2432
+ placeholder,
2433
+ className: cn(
2434
+ "w-full outline-none px-5 py-4 relative z-10 pl-16 bg-input border-none rounded-xs ring-0 leading-0 h-13 text-base focus-visible:border focus-visible:border-ring",
2435
+ className
2436
+ ),
2437
+ value,
2438
+ onChange: (e) => onChange(e.target.value)
2439
+ }
2440
+ )
2441
+ ] });
1762
2442
  };
1763
2443
  const ChainSelectModal = ({
1764
2444
  isOpen,
1765
2445
  onClose,
1766
2446
  items,
1767
2447
  allowedItems,
1768
- onChangeChain
2448
+ onChangeChain,
2449
+ isSource
1769
2450
  }) => {
1770
2451
  const { t } = useBridgeTranslation();
1771
2452
  const [query, setQuery] = useState("");
1772
2453
  const { setFromChain, chains, fromChain, toChain } = useChainsStore();
1773
- const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2454
+ const { assetMatrix, selectedAssetSymbol, setSelectedAssetSymbol } = useTokensStore();
2455
+ console.log("allowedItems", allowedItems);
1774
2456
  const handleClose = useCallback(() => {
1775
2457
  setQuery("");
1776
2458
  onClose();
@@ -1799,40 +2481,84 @@ const ChainSelectModal = ({
1799
2481
  },
1800
2482
  [chains, selectedAssetSymbol, assetMatrix]
1801
2483
  );
2484
+ const findCompatibleTokenAndSrcChain = useCallback(
2485
+ (dstChain) => {
2486
+ if (!chains || !assetMatrix) return void 0;
2487
+ const PRIORITY_CHAINS = [
2488
+ "ethereum",
2489
+ "arbitrum",
2490
+ "bsc",
2491
+ "polygon",
2492
+ "optimism",
2493
+ "base"
2494
+ ];
2495
+ for (const tokenSymbol of Object.keys(assetMatrix)) {
2496
+ const assetByChain = assetMatrix[tokenSymbol];
2497
+ if (!assetByChain[dstChain.chainKey]) continue;
2498
+ const availableChains = chains.filter(
2499
+ (c) => assetByChain[c.chainKey] && c.chainKey !== dstChain.chainKey
2500
+ );
2501
+ if (availableChains.length === 0) continue;
2502
+ let sourceChain;
2503
+ for (const chainKey of PRIORITY_CHAINS) {
2504
+ sourceChain = availableChains.find((c) => c.chainKey === chainKey);
2505
+ if (sourceChain) break;
2506
+ }
2507
+ if (!sourceChain) sourceChain = availableChains[0];
2508
+ return { tokenSymbol, sourceChain };
2509
+ }
2510
+ return void 0;
2511
+ },
2512
+ [chains, assetMatrix]
2513
+ );
1802
2514
  const groupedChains = useMemo(() => {
1803
2515
  const q = query.trim().toLowerCase();
1804
2516
  const filtered = q ? (items ?? []).filter(
1805
2517
  (c) => c.name.toLowerCase().includes(q) || c.chainKey.toLowerCase().includes(q)
1806
2518
  ) : items ?? [];
1807
- const groups = { available: [], willChangeSrc: [] };
2519
+ const groups = { available: [], willChangeSrc: [], willChangeTokenAndSrc: [] };
1808
2520
  for (const chain of filtered) {
1809
2521
  const isAllowed = allowedItems?.some((c) => c.chainKey === chain.chainKey) || false;
1810
2522
  if (isAllowed) {
1811
2523
  groups.available.push(chain);
1812
2524
  } else {
1813
2525
  const compatibleSrc = findCompatibleSrcChain(chain);
1814
- if (compatibleSrc) groups.willChangeSrc.push(chain);
2526
+ if (compatibleSrc) {
2527
+ groups.willChangeSrc.push(chain);
2528
+ } else {
2529
+ const compatibleTokenAndSrc = findCompatibleTokenAndSrcChain(chain);
2530
+ if (compatibleTokenAndSrc) {
2531
+ groups.willChangeTokenAndSrc.push(chain);
2532
+ }
2533
+ }
1815
2534
  }
1816
2535
  }
1817
2536
  groups.available.sort((a, b) => a.name.localeCompare(b.name));
1818
2537
  groups.willChangeSrc.sort((a, b) => a.name.localeCompare(b.name));
2538
+ groups.willChangeTokenAndSrc.sort((a, b) => a.name.localeCompare(b.name));
1819
2539
  return groups;
1820
- }, [items, query, allowedItems, findCompatibleSrcChain]);
1821
- const onChainPick = (chain, willChangeSrc = false) => {
1822
- if (willChangeSrc) {
2540
+ }, [items, query, allowedItems, findCompatibleSrcChain, findCompatibleTokenAndSrcChain]);
2541
+ const onChainPick = (chain, willChangeSrc = false, willChangeTokenAndSrc = false) => {
2542
+ if (willChangeTokenAndSrc) {
2543
+ const result = findCompatibleTokenAndSrcChain(chain);
2544
+ if (result) {
2545
+ setSelectedAssetSymbol(result.tokenSymbol);
2546
+ if (setFromChain) setFromChain(result.sourceChain);
2547
+ }
2548
+ } else if (willChangeSrc) {
1823
2549
  const newSrcChain = findCompatibleSrcChain(chain);
1824
2550
  if (newSrcChain && setFromChain) setFromChain(newSrcChain);
1825
2551
  }
1826
2552
  onChangeChain(chain);
1827
2553
  handleClose();
1828
2554
  };
1829
- const renderChainItem = (chain, willChangeSrc) => {
1830
- const isSelected = fromChain?.chainKey === chain.chainKey || toChain?.chainKey === chain.chainKey;
2555
+ const renderChainItem = (chain, willChangeSrc, willChangeTokenAndSrc = false) => {
2556
+ const isSelected = isSource ? fromChain?.chainKey === chain.chainKey : toChain?.chainKey === chain.chainKey;
1831
2557
  return /* @__PURE__ */ jsx(
1832
2558
  Button,
1833
2559
  {
1834
- onClick: () => onChainPick(chain, willChangeSrc),
1835
- className: `w-full cursor-pointer flex shadow-none rounded-sm items-center justify-between gap-3 px-5 py-4 h-13 font-semibold capitalize bg-transparent hover:scale-100 hover:bg-accent ${isSelected ? "border border-ring" : ""}`,
2560
+ onClick: () => onChainPick(chain, willChangeSrc, willChangeTokenAndSrc),
2561
+ className: `w-full cursor-pointer flex shadow-none rounded-xs items-center justify-between gap-3 px-5 py-4 h-13 font-semibold capitalize bg-transparent hover:scale-100 hover:bg-accent ${isSelected ? "border border-ring" : ""}`,
1836
2562
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1837
2563
  /* @__PURE__ */ jsx(
1838
2564
  NetworkSymbol,
@@ -1848,7 +2574,7 @@ const ChainSelectModal = ({
1848
2574
  chain.chainKey
1849
2575
  );
1850
2576
  };
1851
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90dvh] h-[90dvh] overflow-hidden flex flex-col", children: [
2577
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col", children: [
1852
2578
  /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectNetwork") }) }),
1853
2579
  /* @__PURE__ */ jsx(
1854
2580
  SearchInput,
@@ -1856,19 +2582,24 @@ const ChainSelectModal = ({
1856
2582
  placeholder: t("bridge.searchDestinationChain"),
1857
2583
  value: query,
1858
2584
  onChange: setQuery,
1859
- containerClassName: "rounded-md",
1860
2585
  className: "text-foreground placeholder:text-muted-foreground"
1861
2586
  }
1862
2587
  ),
1863
2588
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
1864
2589
  groupedChains.available.length > 0 && groupedChains.available.map((c) => renderChainItem(c, false)),
1865
- groupedChains.available.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
1866
- groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsx("p", { className: "px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase", children: t("bridge.willChangeSourceChain") }),
1867
- groupedChains.willChangeSrc.length > 0 && groupedChains.willChangeSrc.map(
1868
- (c) => renderChainItem(c, true)
2590
+ groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2591
+ /* @__PURE__ */ jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceChain") }),
2592
+ groupedChains.willChangeSrc.map(
2593
+ (c) => renderChainItem(c, true, false)
1869
2594
  )
1870
2595
  ] }),
1871
- groupedChains.available.length === 0 && groupedChains.willChangeSrc.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-5 py-4 text-sm text-muted-foreground", children: t("bridge.noResults") })
2596
+ groupedChains.willChangeTokenAndSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2597
+ /* @__PURE__ */ jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 || groupedChains.willChangeSrc.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceNetworkAndToken") }),
2598
+ groupedChains.willChangeTokenAndSrc.map(
2599
+ (c) => renderChainItem(c, false, true)
2600
+ )
2601
+ ] }),
2602
+ groupedChains.available.length === 0 && groupedChains.willChangeSrc.length === 0 && groupedChains.willChangeTokenAndSrc.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-5 py-4 text-sm text-muted-foreground", children: t("bridge.noResults") })
1872
2603
  ] })
1873
2604
  ] }) });
1874
2605
  };
@@ -1878,52 +2609,10 @@ const useWalletSelectModal = create((set) => ({
1878
2609
  onOpen: (addressType) => set({ isOpen: true, addressType }),
1879
2610
  onClose: () => set({ isOpen: false, addressType: void 0 })
1880
2611
  }));
1881
- const truncateToDecimals = (num, decimals) => {
1882
- if (!isFinite(num) || isNaN(num)) return "0.00";
1883
- const multiplier = Math.pow(10, decimals);
1884
- const truncated = Math.floor(num * multiplier) / multiplier;
1885
- return truncated.toFixed(decimals);
1886
- };
1887
- const formatTokenAmount = (amount, symbol, options) => {
1888
- const normalizedSymbol = (symbol ?? "").toUpperCase();
1889
- if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
1890
- return `${Math.floor(amount)} ${normalizedSymbol}`;
1891
- }
1892
- if (options?.decimals !== void 0) {
1893
- return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
1894
- }
1895
- if (amount >= 1) {
1896
- return `${amount.toFixed(0)} ${normalizedSymbol}`;
1897
- } else if (amount >= 1e-3) {
1898
- return `${amount.toFixed(3)} ${normalizedSymbol}`;
1899
- } else {
1900
- return `${amount.toFixed(6)} ${normalizedSymbol}`;
1901
- }
1902
- };
1903
- const formatUsd = (value) => {
1904
- if (!value || !isFinite(value)) return "$0";
1905
- if (value >= 1) return `$${value.toFixed(2)}`;
1906
- return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
1907
- };
1908
- const formatPercentage = (bps, decimals = 2) => {
1909
- return `${(bps / 100).toFixed(decimals)}%`;
1910
- };
1911
- const formatBalance = (amount, decimals = 2) => {
1912
- if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
1913
- return "0.00";
1914
- }
1915
- return amount.toFixed(decimals);
1916
- };
1917
- const formatHash = (hash, startChars = 4, endChars = 4) => {
1918
- if (!hash) return "";
1919
- if (hash.length <= startChars + endChars) return hash;
1920
- return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
1921
- };
1922
- const formatAddress = formatHash;
1923
2612
  const prefixIcons = {
1924
2613
  tronlink: /* @__PURE__ */ jsx(TronLinkIcon, { className: "w-5 h-5" }),
1925
2614
  metamask: /* @__PURE__ */ jsx(MetaMaskIcon, { className: "w-5 h-5" }),
1926
- ton: /* @__PURE__ */ jsx(TonKeeperIcon, { className: "w-5 h-5" })
2615
+ ton: /* @__PURE__ */ jsx(TonConnectIcon, { className: "w-5 h-5" })
1927
2616
  };
1928
2617
  const mapWalletToType = (wallet) => {
1929
2618
  switch (wallet) {
@@ -1974,7 +2663,7 @@ const WalletInlineButton = ({
1974
2663
  disabled: isButtonDisabled,
1975
2664
  variant: "ghost",
1976
2665
  size: "sm",
1977
- className: "flex gap-1 cursor-pointer !px-0 pr-1 h-5",
2666
+ className: "flex gap-1 cursor-pointer hover:opacity-60 hover:bg-transparent !px-0 pr-1 h-5",
1978
2667
  children: [
1979
2668
  /* @__PURE__ */ jsx("span", { children: isConnected ? prefixIcons[wallet] : null }),
1980
2669
  /* @__PURE__ */ jsx("span", { className: "leading-3 text-sm border-b border-dotted border-link text-link", children: buttonText })
@@ -2110,7 +2799,8 @@ const SwapSection = ({
2110
2799
  onClose,
2111
2800
  items: chains,
2112
2801
  allowedItems: allowedChains,
2113
- onChangeChain
2802
+ onChangeChain,
2803
+ isSource
2114
2804
  }
2115
2805
  )
2116
2806
  ] });
@@ -2164,7 +2854,6 @@ const AnotherAddress = () => {
2164
2854
  /* @__PURE__ */ jsx(
2165
2855
  Switch,
2166
2856
  {
2167
- className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2168
2857
  "aria-pressed": enabled,
2169
2858
  checked: enabled,
2170
2859
  onClick: () => setEnabled((v) => !v)
@@ -2183,7 +2872,7 @@ const AnotherAddress = () => {
2183
2872
  "div",
2184
2873
  {
2185
2874
  className: cn(
2186
- "bg-input py-2 px-4 mt-2 w-full flex items-center gap-4 rounded-md justify-between border border-transparent transition-all",
2875
+ "bg-input px-4 py-2 pr-2 mt-4 w-full flex items-center gap-3 rounded-xs justify-between border border-transparent transition-all",
2187
2876
  {
2188
2877
  "py-4": value,
2189
2878
  "border border-ring": isFocused,
@@ -2193,13 +2882,13 @@ const AnotherAddress = () => {
2193
2882
  children: [
2194
2883
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
2195
2884
  /* @__PURE__ */ jsx(
2196
- Input,
2885
+ "textarea",
2197
2886
  {
2198
2887
  className: cn(
2199
- "p-0 h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none focus:outline-none focus:ring-0"
2888
+ "h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none focus:outline-none focus:ring-0 resize-none transition-all"
2200
2889
  ),
2201
2890
  placeholder: t("bridge.anotherAddressPlaceholder"),
2202
- type: "text",
2891
+ rows: value.length >= 32 ? 2 : 1,
2203
2892
  value,
2204
2893
  onFocus: () => setIsFocused(true),
2205
2894
  onBlur: () => setIsFocused(false),
@@ -2216,7 +2905,7 @@ const AnotherAddress = () => {
2216
2905
  Button,
2217
2906
  {
2218
2907
  variant: "secondary",
2219
- className: "bg-card text-card-foreground",
2908
+ className: "bg-accent text-card-foreground uppercase text-xs",
2220
2909
  size: "sm",
2221
2910
  onClick: onPaste,
2222
2911
  children: t("common.paste")
@@ -2226,258 +2915,136 @@ const AnotherAddress = () => {
2226
2915
  {
2227
2916
  variant: "ghost",
2228
2917
  size: "sm",
2229
- className: "rounded-full p-0 w-5 h-5 self-start",
2230
- onClick: () => setValue(""),
2231
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2232
- }
2233
- )
2234
- ]
2235
- }
2236
- )
2237
- },
2238
- "custom-address-block"
2239
- ) })
2240
- ] });
2241
- };
2242
- const TipIcon = (props) => {
2243
- return /* @__PURE__ */ jsxs(
2244
- "svg",
2245
- {
2246
- width: "16",
2247
- height: "16",
2248
- viewBox: "0 0 16 16",
2249
- fill: "none",
2250
- xmlns: "http://www.w3.org/2000/svg",
2251
- ...props,
2252
- children: [
2253
- /* @__PURE__ */ jsx("foreignObject", { x: "-4.4893", y: "-4.4893", width: "24.9786", height: "24.9786", children: /* @__PURE__ */ jsx(
2254
- "div",
2255
- {
2256
- style: {
2257
- backdropFilter: "blur(2.24px)",
2258
- WebkitBackdropFilter: "blur(2.24px)",
2259
- clipPath: "url(#bgblur_0_13066_11660_clip_path)",
2260
- height: "100%",
2261
- width: "100%"
2262
- }
2263
- }
2264
- ) }),
2265
- /* @__PURE__ */ jsx(
2266
- "circle",
2267
- {
2268
- "data-figma-bg-blur-radius": "4.4893",
2269
- cx: "8",
2270
- cy: "8",
2271
- r: "8",
2272
- fill: "currentColor",
2273
- fillOpacity: "0.2"
2274
- }
2275
- ),
2276
- /* @__PURE__ */ jsx(
2277
- "path",
2278
- {
2279
- d: "M6.57031 5.85386C6.57031 5.063 7.21143 4.42188 8.00229 4.42188C8.79316 4.42188 9.43428 5.063 9.43428 5.85386C9.43428 6.13893 9.35098 6.40455 9.20739 6.62769C8.77944 7.29276 8.00229 7.92696 8.00229 8.71782V9.07582M8.00229 11.3178V11.5818",
2280
- stroke: "currentColor",
2281
- strokeOpacity: "0.5",
2282
- strokeWidth: "1.68349",
2283
- strokeLinecap: "round"
2284
- }
2285
- ),
2286
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
2287
- "clipPath",
2288
- {
2289
- id: "bgblur_0_13066_11660_clip_path",
2290
- transform: "translate(4.4893 4.4893)",
2291
- children: /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8" })
2292
- }
2293
- ) })
2294
- ]
2295
- }
2296
- );
2297
- };
2298
- const Tip = (props) => {
2299
- const { children, text } = props;
2300
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
2301
- /* @__PURE__ */ jsx(TooltipTrigger, { children }),
2302
- /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: text }) })
2303
- ] });
2304
- };
2305
- const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/tokens";
2306
- const TokenSymbol = ({
2307
- symbol,
2308
- className = "w-4 h-4",
2309
- alt
2310
- }) => {
2311
- const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
2312
- const src = `${BASE_URL}/${normalizedSymbol}.svg`;
2313
- return /* @__PURE__ */ jsx("img", { src, alt: alt ?? symbol, className });
2314
- };
2315
- const EVM_CONFIG = {
2316
- usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
2317
- gasEstimates: {
2318
- approve: 65000n,
2319
- bridge: 300000n
2320
- },
2321
- gasBuffer: 1.2,
2322
- // 20% buffer
2323
- timeout: 3e5,
2324
- // 5 minutes (increased for slower networks)
2325
- requiredConfirmations: 3
2326
- // Wait for 3 confirmations for reorg protection
2327
- };
2328
- const TON_CONFIG = {
2329
- apiUrl: "https://toncenter.com/api/v2",
2330
- timeout: 36e4,
2331
- // 6 minutes
2332
- validUntil: 600,
2333
- // 10 minutes
2334
- pollingInterval: 5e3,
2335
- // 5 seconds between transaction status checks
2336
- estimatedNetworkFee: "100000000"
2337
- // 0.1 TON in nanoton (conservative estimate)
2338
- };
2339
- const TRON_CONFIG = {
2340
- timeout: 12e4,
2341
- // 2 minutes (for 19 confirmations)
2342
- feeLimit: 1e8,
2343
- // 100 TRX in sun
2344
- requiredConfirmations: 19,
2345
- // TRON standard: 19 blocks for confirmation
2346
- pollingInterval: 3e3
2347
- // 3 seconds between checks
2348
- };
2349
- let tonClientInstance = null;
2350
- function getTonClient(customClient, apiKey) {
2351
- if (customClient) {
2352
- return customClient;
2353
- }
2354
- if (!tonClientInstance) {
2355
- tonClientInstance = new TonClient({
2356
- endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
2357
- apiKey
2358
- });
2359
- }
2360
- return tonClientInstance;
2361
- }
2362
- function getQuoteAmounts(quote, srcToken, dstToken) {
2363
- if (!quote || !srcToken || !dstToken) {
2364
- return {
2365
- inputHuman: 0,
2366
- outputHuman: 0,
2367
- outputHumanRounded: "0",
2368
- minReceivedHuman: 0
2369
- };
2370
- }
2371
- const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
2372
- const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
2373
- const outputHumanRounded = truncateToDecimals(outputHuman, 2);
2374
- const minReceivedHuman = fromLD(
2375
- quote.dstAmountMin || "0",
2376
- dstToken.decimals
2377
- );
2378
- return {
2379
- inputHuman,
2380
- outputHuman,
2381
- outputHumanRounded,
2382
- minReceivedHuman
2383
- };
2384
- }
2385
- function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
2386
- if (!quote || !tokens || !chains) {
2387
- return {
2388
- totalUsd: 0,
2389
- protocolFeeUsd: void 0,
2390
- messageFeeUsd: void 0,
2391
- serviceUsd: void 0,
2392
- blockchainUsd: void 0,
2393
- inSrcToken: void 0,
2394
- inDstToken: void 0
2395
- };
2396
- }
2397
- const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
2398
- let inSrcToken = void 0;
2399
- let inDstToken = void 0;
2400
- if (srcToken && quote.srcChainKey) {
2401
- const feeInSrcLD = sumFeeByTokenLD(
2402
- quote.fees,
2403
- srcToken.address,
2404
- quote.srcChainKey
2405
- );
2406
- const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
2407
- if (feeInSrcHuman > 0) {
2408
- inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
2409
- } else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
2410
- const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
2411
- inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
2412
- }
2413
- }
2414
- if (dstToken && quote.dstChainKey) {
2415
- const feeInDstLD = sumFeeByTokenLD(
2416
- quote.fees,
2417
- dstToken.address,
2418
- quote.dstChainKey
2419
- );
2420
- const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
2421
- if (feeInDstHuman > 0) {
2422
- inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
2918
+ className: "rounded-full p-0 w-5 h-5 self-start",
2919
+ onClick: () => setValue(""),
2920
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2921
+ }
2922
+ )
2923
+ ]
2924
+ }
2925
+ )
2926
+ },
2927
+ "custom-address-block"
2928
+ ) })
2929
+ ] });
2930
+ };
2931
+ const InfoIcon = (props) => {
2932
+ return /* @__PURE__ */ jsx(
2933
+ "svg",
2934
+ {
2935
+ width: "16",
2936
+ height: "16",
2937
+ viewBox: "0 0 16 16",
2938
+ fill: "none",
2939
+ xmlns: "http://www.w3.org/2000/svg",
2940
+ ...props,
2941
+ children: /* @__PURE__ */ jsx(
2942
+ "path",
2943
+ {
2944
+ d: "M6.56836 5.8519C6.56836 5.06104 7.20948 4.41992 8.00034 4.41992C8.7912 4.41992 9.43232 5.06104 9.43232 5.8519C9.43232 6.13698 9.34902 6.40259 9.20543 6.62574C8.77748 7.29081 8.00034 7.92501 8.00034 8.71587V9.07386M8.00034 11.3159V11.5798",
2945
+ stroke: "currentColor",
2946
+ "stroke-width": "1.68349",
2947
+ "stroke-linecap": "round"
2948
+ }
2949
+ )
2423
2950
  }
2424
- }
2425
- return {
2426
- totalUsd: feeData.totalUsd,
2427
- protocolFeeUsd: feeData.protocolFeeUsd,
2428
- messageFeeUsd: feeData.messageFeeUsd,
2429
- serviceUsd: feeData.serviceUsd,
2430
- blockchainUsd: feeData.blockchainUsd,
2431
- inSrcToken,
2432
- inDstToken
2433
- };
2434
- }
2435
- function calculateMinReceived(quote, slippageBps, dstToken) {
2436
- if (!quote || !dstToken) return 0;
2437
- const dstAmountLD = BigInt(quote.dstAmount);
2438
- const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
2439
- return fromLD(minAmountLD.toString(), dstToken.decimals);
2440
- }
2441
- function addTonNetworkFee(quote, chains) {
2442
- if (!quote || quote.srcChainKey.toLowerCase() !== "ton") {
2443
- return quote;
2444
- }
2445
- const tonChain = chains?.find(
2446
- (c) => c.chainKey.toLowerCase() === "ton"
2447
- );
2448
- if (!tonChain?.nativeCurrency?.address) {
2449
- console.warn("Could not find TON native currency address");
2450
- return quote;
2451
- }
2452
- const networkFee = {
2453
- token: tonChain.nativeCurrency.address,
2454
- chainKey: "ton",
2455
- amount: TON_CONFIG.estimatedNetworkFee,
2456
- type: "network"
2457
- };
2458
- const hasNetworkFee = quote.fees?.some(
2459
- (fee) => fee.type === "network" && fee.chainKey === "ton"
2460
2951
  );
2461
- if (hasNetworkFee) {
2462
- return quote;
2463
- }
2464
- return {
2465
- ...quote,
2466
- fees: [...quote.fees || [], networkFee]
2467
- };
2952
+ };
2953
+ const Tip = (props) => {
2954
+ const { children, text } = props;
2955
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
2956
+ /* @__PURE__ */ jsx(TooltipTrigger, { className: "w-4 h-4 rounded-full bg-muted text-muted-foreground", children }),
2957
+ /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: text }) })
2958
+ ] });
2959
+ };
2960
+ const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/tokens";
2961
+ const TokenSymbol = ({
2962
+ symbol,
2963
+ className = "w-4 h-4",
2964
+ alt
2965
+ }) => {
2966
+ const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
2967
+ const src = `${BASE_URL}/${normalizedSymbol}.svg`;
2968
+ return /* @__PURE__ */ jsx("img", { src, alt: alt ?? symbol, className });
2969
+ };
2970
+ function getSimpleFallback(chainKey) {
2971
+ const key = chainKey.toLowerCase();
2972
+ if (key === "ton") return 0.15;
2973
+ if (key === "tron") return 10;
2974
+ return 0.01;
2468
2975
  }
2469
- function getQuoteDetails(quote, srcToken, dstToken, tokens, chains, slippageBps) {
2470
- const amounts = getQuoteAmounts(quote, srcToken, dstToken);
2471
- const fees = getQuoteFees(quote, tokens, chains, srcToken, dstToken);
2472
- const minimumReceived = calculateMinReceived(quote, slippageBps, dstToken);
2473
- return {
2474
- inputAmount: amounts.inputHuman,
2475
- outputAmount: amounts.outputHuman,
2476
- outputAmountRounded: amounts.outputHumanRounded,
2477
- minimumReceived,
2478
- etaSeconds: quote?.duration?.estimated,
2479
- fees
2480
- };
2976
+ function useGasEstimate(amountNum) {
2977
+ const { fromChain } = useChainsStore();
2978
+ const { selectedAssetSymbol } = useTokensStore();
2979
+ const { srcAddress } = useAddresses();
2980
+ const { balances, isLoading: balancesLoading } = useBalances(
2981
+ fromChain?.chainKey,
2982
+ srcAddress
2983
+ );
2984
+ const { quote } = useBridgeQuoteStore();
2985
+ const balancesKnown = !balancesLoading;
2986
+ const chainKey = fromChain?.chainKey;
2987
+ const nativeCurrencySymbol = fromChain?.nativeCurrency?.symbol;
2988
+ const nativeCurrencyAddress = fromChain?.nativeCurrency?.address;
2989
+ const nativeCurrencyDecimals = fromChain?.nativeCurrency?.decimals;
2990
+ const quoteFees = quote?.fees ? JSON.stringify(quote.fees) : null;
2991
+ const quoteSrcChainKey = quote?.srcChainKey;
2992
+ const nativeBalanceValue = nativeCurrencySymbol ? Number(balances[nativeCurrencySymbol.toUpperCase()]?.balance ?? 0) : 0;
2993
+ const result = useMemo(() => {
2994
+ if (!chainKey || !nativeCurrencySymbol) {
2995
+ return {
2996
+ nativeSym: "",
2997
+ nativeBalance: 0,
2998
+ requiredNative: 0,
2999
+ balancesKnown,
3000
+ isNativeSelected: false,
3001
+ hasEnoughGas: true
3002
+ };
3003
+ }
3004
+ const nativeSym = nativeCurrencySymbol.toUpperCase();
3005
+ const nativeBalance = nativeBalanceValue;
3006
+ const isNativeSelected = nativeSym === (selectedAssetSymbol || "").toUpperCase();
3007
+ let requiredNative = 0;
3008
+ if (quoteFees && quoteSrcChainKey === chainKey) {
3009
+ const fees = JSON.parse(quoteFees);
3010
+ const feesInNative = fees.filter(
3011
+ (f) => f.chainKey === chainKey && f.token === nativeCurrencyAddress
3012
+ ).reduce(
3013
+ (sum, f) => sum + BigInt(f.amount || "0"),
3014
+ 0n
3015
+ );
3016
+ const decimals = nativeCurrencyDecimals || 18;
3017
+ requiredNative = Number(feesInNative) / Math.pow(10, decimals);
3018
+ } else {
3019
+ requiredNative = getSimpleFallback(chainKey);
3020
+ }
3021
+ let hasEnoughGas = true;
3022
+ if (isNativeSelected) {
3023
+ hasEnoughGas = nativeBalance - (amountNum ?? 0) >= requiredNative;
3024
+ } else {
3025
+ hasEnoughGas = nativeBalance >= requiredNative;
3026
+ }
3027
+ return {
3028
+ nativeSym,
3029
+ nativeBalance,
3030
+ requiredNative,
3031
+ balancesKnown,
3032
+ isNativeSelected,
3033
+ hasEnoughGas: balancesKnown ? hasEnoughGas : true
3034
+ };
3035
+ }, [
3036
+ chainKey,
3037
+ nativeCurrencySymbol,
3038
+ nativeCurrencyAddress,
3039
+ nativeCurrencyDecimals,
3040
+ selectedAssetSymbol,
3041
+ quoteFees,
3042
+ quoteSrcChainKey,
3043
+ amountNum,
3044
+ balancesKnown,
3045
+ nativeBalanceValue
3046
+ ]);
3047
+ return result;
2481
3048
  }
2482
3049
  function getRouteDisplayName(route) {
2483
3050
  if (!route) return "Stargate Bridge";
@@ -2494,6 +3061,7 @@ const Details = () => {
2494
3061
  const { toChain, fromChain, chains } = useChainsStore();
2495
3062
  const { quote, status } = useBridgeQuoteStore();
2496
3063
  const { slippageBps, routePriority } = useSettingsStore();
3064
+ const gas = useGasEstimate();
2497
3065
  const dstToken = resolveTokenOnChainFromMatrix$2(
2498
3066
  assetMatrix,
2499
3067
  selectedAssetSymbol,
@@ -2504,9 +3072,8 @@ const Details = () => {
2504
3072
  selectedAssetSymbol,
2505
3073
  fromChain?.chainKey
2506
3074
  );
2507
- const quoteWithFees = addTonNetworkFee(quote, chains);
2508
3075
  const quoteDetails = getQuoteDetails(
2509
- quoteWithFees || quote,
3076
+ quote,
2510
3077
  srcToken,
2511
3078
  dstToken,
2512
3079
  tokens,
@@ -2517,21 +3084,7 @@ const Details = () => {
2517
3084
  const isLoading = status === "loading";
2518
3085
  const receiveText = quoteDetails.outputAmount != null ? Number(quoteDetails.outputAmount).toFixed(6) : "0.00";
2519
3086
  const etaText = quoteDetails.etaSeconds != null ? `≈ ${Math.max(1, Math.round(quoteDetails.etaSeconds / 60))}m` : "—";
2520
- const feeInSrc = quoteDetails.fees?.inSrcToken;
2521
- const feeInDst = quoteDetails.fees?.inDstToken;
2522
- const totalFeeUsd = quoteDetails.fees?.totalUsd;
2523
- const totalFeeDisplay = (() => {
2524
- if (feeInSrc != null && srcToken?.symbol) {
2525
- return `${Number(feeInSrc).toFixed(6)} ${srcToken.symbol.toUpperCase()}`;
2526
- }
2527
- if (feeInDst != null && dstToken?.symbol) {
2528
- return `${Number(feeInDst).toFixed(6)} ${dstToken.symbol.toUpperCase()}`;
2529
- }
2530
- if (totalFeeUsd != null) {
2531
- return formatUsd(totalFeeUsd);
2532
- }
2533
- return "—";
2534
- })();
3087
+ const totalFeeDisplay = gas.requiredNative > 0 ? `${gas.requiredNative.toFixed(6)} ${gas.nativeSym}` : "—";
2535
3088
  const currentSlippageText = formatPercentage(slippageBps);
2536
3089
  const routeText = quote?.route ? getRouteDisplayName(quote.route) : t(`settings.routePresets.${routePriority}`);
2537
3090
  return /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", className: "bg-muted/50 rounded-sm", children: [
@@ -2551,7 +3104,7 @@ const Details = () => {
2551
3104
  DetailsRow,
2552
3105
  {
2553
3106
  label: t("transaction.route"),
2554
- value: /* @__PURE__ */ jsxs("strong", { className: "flex items-center font-semibold gap-2", children: [
3107
+ value: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
2555
3108
  /* @__PURE__ */ jsx(StargateIcon, { className: "w-4 h-4" }),
2556
3109
  /* @__PURE__ */ jsx("p", { className: "", children: routeText })
2557
3110
  ] })
@@ -2578,7 +3131,14 @@ const Details = () => {
2578
3131
  label: t("transaction.totalFee"),
2579
3132
  value: /* @__PURE__ */ jsxs("strong", { className: "inline-flex items-center gap-1", children: [
2580
3133
  /* @__PURE__ */ jsx("span", { children: totalFeeDisplay }),
2581
- /* @__PURE__ */ jsx(TokenSymbol, { symbol, className: "w-4 h-4", alt: "token" })
3134
+ /* @__PURE__ */ jsx(
3135
+ TokenSymbol,
3136
+ {
3137
+ symbol: gas.nativeSym,
3138
+ className: "w-4 h-4",
3139
+ alt: "token"
3140
+ }
3141
+ )
2582
3142
  ] }),
2583
3143
  isLoading
2584
3144
  }
@@ -2593,7 +3153,7 @@ const DetailsRow = ({
2593
3153
  }) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
2594
3154
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2595
3155
  /* @__PURE__ */ jsx("p", { className: "text-sm text-priority font-normal", children: label }),
2596
- /* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(TipIcon, { className: "w-4 h-4 text-receive-icon" }) })
3156
+ /* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(InfoIcon, {}) })
2597
3157
  ] }),
2598
3158
  isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16 rounded-md" }) : /* @__PURE__ */ jsx("div", { className: "text-foreground text-sm", children: value ?? "—" })
2599
3159
  ] });
@@ -2646,6 +3206,20 @@ const useTransactionStore = create((set, get) => ({
2646
3206
  };
2647
3207
  set({ current: next });
2648
3208
  },
3209
+ updateActualFee: (feeValue, feeSymbol) => {
3210
+ const cur = get().current;
3211
+ if (!cur) return;
3212
+ const next = {
3213
+ ...cur,
3214
+ metadata: {
3215
+ ...cur.metadata,
3216
+ actualFeeValue: feeValue,
3217
+ actualFeeSymbol: feeSymbol
3218
+ },
3219
+ updatedAt: Date.now()
3220
+ };
3221
+ set({ current: next });
3222
+ },
2649
3223
  reset: () => {
2650
3224
  set({ current: void 0 });
2651
3225
  }
@@ -2791,9 +3365,13 @@ function isUserRejection(error) {
2791
3365
  if (error instanceof TransactionRejectedError) {
2792
3366
  return true;
2793
3367
  }
3368
+ if (typeof error === "string") {
3369
+ const message = error.toLowerCase();
3370
+ return message.includes("rejected") || message.includes("denied") || message.includes("declined") || message.includes("not sent") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request") || message.includes("declined by user");
3371
+ }
2794
3372
  if (error instanceof Error) {
2795
3373
  const message = error.message.toLowerCase();
2796
- return message.includes("rejected") || message.includes("denied") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request");
3374
+ return message.includes("rejected") || message.includes("denied") || message.includes("declined") || message.includes("not sent") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request") || message.includes("declined by user");
2797
3375
  }
2798
3376
  return false;
2799
3377
  }
@@ -2812,17 +3390,13 @@ function toChainStrategyError(error, chainKey, context) {
2812
3390
  error
2813
3391
  );
2814
3392
  }
2815
- return new ChainStrategyError(
2816
- String(error),
2817
- "UNKNOWN_ERROR",
2818
- chainKey
2819
- );
3393
+ return new ChainStrategyError(String(error), "UNKNOWN_ERROR", chainKey);
2820
3394
  }
2821
3395
  function useBridgeTransaction() {
2822
3396
  const { quote } = useBridgeQuoteStore();
2823
3397
  const { chainRegistry } = useChainStrategies();
2824
3398
  const { srcAddress, dstAddress } = useAddresses();
2825
- const { assetMatrix, tokens, selectedAssetSymbol } = useTokensStore();
3399
+ const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2826
3400
  const { chains } = useChainsStore();
2827
3401
  const txStore = useTransactionStore();
2828
3402
  const [isProcessing, setIsProcessing] = useState(false);
@@ -2851,31 +3425,33 @@ function useBridgeTransaction() {
2851
3425
  }
2852
3426
  const srcChain = chains?.find((c) => c.chainKey === quote.srcChainKey);
2853
3427
  const dstChain = chains?.find((c) => c.chainKey === quote.dstChainKey);
2854
- const quoteWithFees = addTonNetworkFee(quote, chains) || quote;
2855
- const amounts = getQuoteAmounts(quoteWithFees, srcToken, dstToken);
2856
- const fees = getQuoteFees(quoteWithFees, tokens, chains, srcToken, dstToken);
3428
+ const amounts = getQuoteAmounts(quote, srcToken, dstToken);
2857
3429
  const metadata = {
2858
3430
  srcChainName: srcChain?.name || quote.srcChainKey,
2859
3431
  dstChainName: dstChain?.name || quote.dstChainKey,
2860
3432
  srcTokenSymbol: srcToken?.symbol || quote.srcToken,
2861
3433
  dstTokenSymbol: dstToken?.symbol || quote.dstToken,
2862
3434
  srcAmountHuman: amounts.inputHuman,
2863
- dstAmountHuman: amounts.outputHuman,
2864
- // TODO: IT'S NOT TRUE DAMN :)
2865
- totalFeeValue: fees.inSrcToken ?? fees.inDstToken ?? void 0,
2866
- totalFeeSymbol: fees.inSrcToken ? srcToken?.symbol : fees.inDstToken ? dstToken?.symbol : void 0
3435
+ dstAmountHuman: amounts.outputHuman
3436
+ // Actual fee will be updated when transaction completes
2867
3437
  };
2868
3438
  txStore.setTransaction(quote, "executing", metadata);
2869
3439
  setIsProcessing(true);
2870
3440
  try {
2871
3441
  const steps = quote.steps || [];
2872
3442
  if (steps.length === 0) {
2873
- throw new InvalidStepsError(quote.srcChainKey, "No transaction steps found in quote");
3443
+ throw new InvalidStepsError(
3444
+ quote.srcChainKey,
3445
+ "No transaction steps found in quote"
3446
+ );
2874
3447
  }
2875
3448
  const chainKey = steps[0].chainKey;
2876
3449
  const strategy = chainRegistry.getStrategy(chainKey);
2877
3450
  if (!strategy) {
2878
- throw new InvalidStepsError(chainKey, `No strategy available for chain: ${chainKey}`);
3451
+ throw new InvalidStepsError(
3452
+ chainKey,
3453
+ `No strategy available for chain: ${chainKey}`
3454
+ );
2879
3455
  }
2880
3456
  if (!strategy.isConnected()) {
2881
3457
  throw new WalletNotConnectedError(chainKey);
@@ -2887,7 +3463,6 @@ function useBridgeTransaction() {
2887
3463
  srcChainKey: quote.srcChainKey,
2888
3464
  dstChainKey: quote.dstChainKey
2889
3465
  };
2890
- console.log(steps);
2891
3466
  const txResult = await strategy.executeSteps(steps, context, (hash) => {
2892
3467
  txStore.setSrcHash(hash);
2893
3468
  txStore.updateStatus("processing");
@@ -2898,6 +3473,21 @@ function useBridgeTransaction() {
2898
3473
  if (result.dstTxHash) {
2899
3474
  txStore.setDstHash(result.dstTxHash);
2900
3475
  }
3476
+ if (result.actualFeeValue) {
3477
+ let feeSymbol = result.actualFeeSymbol;
3478
+ if (!feeSymbol) {
3479
+ const srcChain2 = chains?.find(
3480
+ (c) => c.chainKey === quote.srcChainKey
3481
+ );
3482
+ feeSymbol = srcChain2?.nativeCurrency?.symbol || "";
3483
+ }
3484
+ if (feeSymbol) {
3485
+ const feeValue = parseFloat(result.actualFeeValue);
3486
+ if (!isNaN(feeValue)) {
3487
+ txStore.updateActualFee(feeValue, feeSymbol);
3488
+ }
3489
+ }
3490
+ }
2901
3491
  txStore.updateStatus("completed");
2902
3492
  console.log("Transaction completed successfully");
2903
3493
  } else {
@@ -2915,19 +3505,26 @@ function useBridgeTransaction() {
2915
3505
  console.error("Error tracking completion:", err);
2916
3506
  });
2917
3507
  } else {
2918
- throw new TransactionFailedError(chainKey, "Transaction hash not received from wallet");
3508
+ throw new TransactionFailedError(
3509
+ chainKey,
3510
+ "Transaction hash not received from wallet"
3511
+ );
2919
3512
  }
2920
3513
  return txResult;
2921
3514
  } catch (err) {
2922
3515
  if (isUserRejection(err)) {
2923
3516
  txStore.setError("TRANSACTION_REJECTED");
2924
- throw new TransactionFailedError(quote.srcChainKey, "Transaction rejected by user");
3517
+ throw new TransactionFailedError(
3518
+ quote.srcChainKey,
3519
+ "Transaction rejected by user"
3520
+ );
2925
3521
  }
2926
3522
  if (ChainStrategyError.isChainStrategyError(err)) {
2927
3523
  txStore.setError(err.code, { chainKey: err.chainKey });
2928
3524
  console.error("Chain strategy error:", err.toJSON());
2929
3525
  throw err;
2930
3526
  }
3527
+ console.log(err);
2931
3528
  txStore.setError("UNKNOWN_ERROR");
2932
3529
  throw err;
2933
3530
  } finally {
@@ -2940,66 +3537,6 @@ function useBridgeTransaction() {
2940
3537
  hasQuote: !!quote
2941
3538
  };
2942
3539
  }
2943
- function useGasEstimate(amountNum) {
2944
- const { fromChain } = useChainsStore();
2945
- const { selectedAssetSymbol, tokens } = useTokensStore();
2946
- const getSourceGasReserveHuman = useSettingsStore(
2947
- (state) => state.getSourceGasReserveHuman
2948
- );
2949
- const { srcAddress } = useAddresses();
2950
- const { balances, isLoading: balancesLoading } = useBalances(
2951
- fromChain?.chainKey,
2952
- srcAddress
2953
- );
2954
- const { chainRegistry } = useChainStrategies();
2955
- const balancesKnown = !balancesLoading;
2956
- const strategy = useMemo(() => {
2957
- if (!fromChain) return null;
2958
- return chainRegistry.getStrategy(fromChain.chainKey);
2959
- }, [fromChain, chainRegistry]);
2960
- const { data: gasRequirement } = useQuery({
2961
- queryKey: [
2962
- "gas-estimate",
2963
- fromChain?.chainKey,
2964
- selectedAssetSymbol,
2965
- amountNum,
2966
- balances
2967
- ],
2968
- queryFn: async () => {
2969
- if (!fromChain || !strategy) {
2970
- return null;
2971
- }
2972
- const selectedToken = tokens?.find(
2973
- (t) => t.symbol.toUpperCase() === selectedAssetSymbol?.toUpperCase()
2974
- ) || null;
2975
- const nativeTokenSymbol = fromChain.nativeCurrency?.symbol ?? "";
2976
- const nativeDecimals = fromChain.nativeCurrency?.decimals || 18;
2977
- const reserveFallback = getSourceGasReserveHuman(fromChain.chainKey);
2978
- const result = await strategy.estimateGasRequirement({
2979
- selectedToken,
2980
- nativeTokenSymbol,
2981
- amount: amountNum,
2982
- balances,
2983
- nativeDecimals,
2984
- reserveFallback
2985
- });
2986
- return result;
2987
- },
2988
- enabled: !!fromChain && !!strategy,
2989
- staleTime: 3e4,
2990
- gcTime: 5 * 6e4,
2991
- refetchOnWindowFocus: false,
2992
- retry: 1
2993
- });
2994
- return {
2995
- nativeSym: gasRequirement?.nativeSym || "",
2996
- nativeBalance: gasRequirement?.nativeBalance || 0,
2997
- requiredNative: gasRequirement?.requiredNative || 0,
2998
- balancesKnown,
2999
- isNativeSelected: gasRequirement?.isNativeSelected || false,
3000
- hasEnoughGas: balancesKnown ? gasRequirement?.hasEnoughGas ?? true : true
3001
- };
3002
- }
3003
3540
  function useBalanceCheck(amountNum, gas) {
3004
3541
  const { fromChain } = useChainsStore();
3005
3542
  const { selectedAssetSymbol } = useTokensStore();
@@ -3330,8 +3867,8 @@ const WalletSelectModal = () => {
3330
3867
  {
3331
3868
  strategy: tonWallet,
3332
3869
  walletId: "ton",
3333
- name: t("wallets.tonKeeper"),
3334
- icon: TonKeeperIcon
3870
+ name: t("wallets.tonconnect"),
3871
+ icon: TonConnectIcon
3335
3872
  },
3336
3873
  {
3337
3874
  strategy: metaMaskWallet,
@@ -3369,8 +3906,8 @@ const WalletSelectModal = () => {
3369
3906
  const tonWallets = [
3370
3907
  {
3371
3908
  id: "ton",
3372
- name: t("wallets.tonKeeper"),
3373
- icon: TonKeeperIcon,
3909
+ name: t("wallets.tonconnect"),
3910
+ icon: TonConnectIcon,
3374
3911
  enabled: true
3375
3912
  }
3376
3913
  ];
@@ -3582,7 +4119,7 @@ const SuccessStep = ({
3582
4119
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 pt-10 px-8 flex-1 justify-start items-center text-center noise bg-background", children: [
3583
4120
  icon,
3584
4121
  /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("transaction.success") }) }),
3585
- /* @__PURE__ */ jsxs("div", { className: "w-full space-y-3 mt-4 relative z-10 text-sm", children: [
4122
+ /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2 mt-4 relative z-10 pb-14", children: [
3586
4123
  metadata?.srcAmountHuman && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3587
4124
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.bridged") }),
3588
4125
  /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
@@ -3594,15 +4131,13 @@ const SuccessStep = ({
3594
4131
  ] }),
3595
4132
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3596
4133
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.transferTitle") }),
3597
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
4134
+ /* @__PURE__ */ jsxs("span", { className: "font-medium space-x-1", children: [
3598
4135
  /* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
3599
4136
  metadata?.srcChainName,
3600
4137
  " ",
3601
4138
  /* @__PURE__ */ jsx(NetworkSymbol, { chainKey: metadata.srcChainName })
3602
4139
  ] }),
3603
- " ",
3604
- "→",
3605
- " ",
4140
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "→" }),
3606
4141
  /* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
3607
4142
  metadata?.dstChainName,
3608
4143
  " ",
@@ -3616,17 +4151,17 @@ const SuccessStep = ({
3616
4151
  "button",
3617
4152
  {
3618
4153
  onClick: handleOpenExplorer,
3619
- className: "font-medium hover:underline cursor-pointer inline-flex items-center gap-1",
4154
+ className: "font-medium cursor-pointer inline-flex items-center gap-1 underline hover:no-underline",
3620
4155
  children: formatHash(srcTxHash)
3621
4156
  }
3622
4157
  )
3623
4158
  ] }),
3624
- metadata?.totalFeeValue && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
4159
+ metadata?.actualFeeValue && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3625
4160
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.finalFee") }),
3626
4161
  /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
3627
- metadata.totalFeeValue,
4162
+ metadata.actualFeeValue,
3628
4163
  " ",
3629
- metadata.totalFeeSymbol
4164
+ metadata.actualFeeSymbol
3630
4165
  ] })
3631
4166
  ] })
3632
4167
  ] })
@@ -3723,7 +4258,7 @@ const useTokens = () => {
3723
4258
  return { ...query };
3724
4259
  };
3725
4260
  const useChains = () => {
3726
- const { setChains, setFromChain } = useChainsStore();
4261
+ const { setChains, setFromChain, fromChain } = useChainsStore();
3727
4262
  const query = useQuery({
3728
4263
  queryKey: ["chains"],
3729
4264
  queryFn: () => getChains(),
@@ -3737,10 +4272,16 @@ const useChains = () => {
3737
4272
  });
3738
4273
  useEffect(() => {
3739
4274
  if (query.isSuccess && query.data?.length) {
4275
+ console.log(
4276
+ `[DEBUG] Loaded ${query.data.length} chains from API:`,
4277
+ query.data.map((c) => c.chainKey)
4278
+ );
3740
4279
  setChains(query.data);
3741
- setFromChain(query.data[0]);
4280
+ if (!fromChain) {
4281
+ setFromChain(query.data[0]);
4282
+ }
3742
4283
  }
3743
- }, [query.isSuccess, query.data, setChains, setFromChain]);
4284
+ }, [query.isSuccess, query.data]);
3744
4285
  useEffect(() => {
3745
4286
  if (query.isError && query.error) {
3746
4287
  console.error(query.error.name, query.error.message);
@@ -3898,7 +4439,9 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
3898
4439
  }
3899
4440
  } catch (error) {
3900
4441
  const errorMessage = error instanceof Error ? error.message : String(error);
3901
- const isZeroDataError = errorMessage.includes('returned no data ("0x")');
4442
+ const isZeroDataError = errorMessage.includes(
4443
+ 'returned no data ("0x")'
4444
+ );
3902
4445
  if (!isZeroDataError) {
3903
4446
  console.debug(
3904
4447
  `Failed to get priority token balance for ${priorityToken.symbol}:`,
@@ -4009,12 +4552,9 @@ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
4009
4552
  }
4010
4553
  const client = getTonClient(customTonClient, tonApiKey);
4011
4554
  const accountAddress = Address$1.parse(address);
4012
- console.log(address);
4013
- console.log(tokens);
4014
4555
  try {
4015
4556
  const balance = await client.getBalance(accountAddress);
4016
4557
  const tonBalance = Number(balance) / 1e9;
4017
- console.log("tonBalance", tonBalance);
4018
4558
  if (tonBalance > 0) {
4019
4559
  balances.TON = { balance: tonBalance, address: "ton-native" };
4020
4560
  }
@@ -4071,8 +4611,23 @@ async function getTronBalances(tronWeb, address, tokens) {
4071
4611
  try {
4072
4612
  if (!tronWeb) throw new Error("TronWeb not available");
4073
4613
  const ownerB58 = toTronBase58(address, tronWeb);
4614
+ try {
4615
+ const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
4616
+ const sunNum = Number(trxBalanceSun);
4617
+ const trxVal = tronWeb.fromSun(sunNum);
4618
+ const trxStr = typeof trxVal === "string" ? trxVal : trxVal.toString();
4619
+ const trxBalance = parseFloat(trxStr);
4620
+ if (trxBalance > 0) {
4621
+ balances.TRX = { balance: trxBalance, address: ownerB58 };
4622
+ }
4623
+ } catch (error) {
4624
+ console.warn("Failed to get native TRX balance:", error);
4625
+ }
4074
4626
  for (const token of tokens) {
4075
4627
  try {
4628
+ if (isNativeAddress(token.address) && token.symbol.toUpperCase() === "TRX") {
4629
+ continue;
4630
+ }
4076
4631
  let balance = 0;
4077
4632
  if (isNativeAddress(token.address)) {
4078
4633
  const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
@@ -4159,6 +4714,9 @@ class EvmChainStrategy {
4159
4714
  getConnectLabel(t) {
4160
4715
  return t("wallets.connectEvmWallet");
4161
4716
  }
4717
+ getClient() {
4718
+ return this.provider;
4719
+ }
4162
4720
  async getBalances(address, tokens, priorityToken) {
4163
4721
  if (!this.publicClient) {
4164
4722
  console.warn("No publicClient available for balance query");
@@ -4175,52 +4733,6 @@ class EvmChainStrategy {
4175
4733
  if (!address) return false;
4176
4734
  return /^0x[0-9a-fA-F]{40}$/.test(address);
4177
4735
  }
4178
- async estimateGasRequirement(params) {
4179
- const provider = this.provider;
4180
- const {
4181
- selectedToken,
4182
- nativeTokenSymbol,
4183
- amount,
4184
- balances,
4185
- nativeDecimals,
4186
- reserveFallback
4187
- } = params;
4188
- const nativeSym = nativeTokenSymbol.toUpperCase();
4189
- const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
4190
- const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
4191
- let estimatedGas = null;
4192
- if (provider) {
4193
- try {
4194
- const gasPriceHex = await provider.send("eth_gasPrice", []);
4195
- const gasPrice = BigInt(gasPriceHex);
4196
- const approveGas = isNativeSelected ? 0n : EVM_CONFIG.gasEstimates.approve;
4197
- const bridgeGas = EVM_CONFIG.gasEstimates.bridge;
4198
- const totalGas = approveGas + bridgeGas;
4199
- const bufferMultiplier = BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100));
4200
- const requiredWei = gasPrice * totalGas * bufferMultiplier / 100n;
4201
- const { formatUnits: formatUnits2 } = await import("ethers");
4202
- estimatedGas = Number(formatUnits2(requiredWei, nativeDecimals));
4203
- } catch {
4204
- estimatedGas = null;
4205
- }
4206
- }
4207
- const requiredNative = estimatedGas ?? reserveFallback;
4208
- const amountNum = amount ?? 0;
4209
- let hasEnoughGas = true;
4210
- if (isNativeSelected) {
4211
- hasEnoughGas = nativeBalance - amountNum >= requiredNative;
4212
- } else {
4213
- hasEnoughGas = nativeBalance >= requiredNative;
4214
- }
4215
- return {
4216
- estimatedGas,
4217
- nativeBalance,
4218
- requiredNative,
4219
- hasEnoughGas,
4220
- nativeSym,
4221
- isNativeSelected
4222
- };
4223
- }
4224
4736
  validateSteps(steps) {
4225
4737
  if (!steps || steps.length === 0) {
4226
4738
  throw new InvalidStepsError("evm", "No transaction steps provided");
@@ -4313,8 +4825,24 @@ class EvmChainStrategy {
4313
4825
  console.log(
4314
4826
  `EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
4315
4827
  );
4828
+ let actualFeeValue;
4829
+ try {
4830
+ const gasUsed = receipt.gasUsed;
4831
+ const effectiveGasPrice = receipt.gasPrice;
4832
+ if (gasUsed && effectiveGasPrice) {
4833
+ const feeWei = gasUsed * effectiveGasPrice;
4834
+ const { formatUnits: formatUnits2 } = await import("ethers");
4835
+ const feeInNative = formatUnits2(feeWei, 18);
4836
+ actualFeeValue = feeInNative;
4837
+ console.log(`EVM transaction fee: ${feeInNative} (native token)`);
4838
+ }
4839
+ } catch (error) {
4840
+ console.warn("Failed to calculate actual fee:", error);
4841
+ }
4316
4842
  return {
4317
- completed: true
4843
+ completed: true,
4844
+ actualFeeValue
4845
+ // Symbol will be determined by the caller based on chain info
4318
4846
  };
4319
4847
  } catch (error) {
4320
4848
  if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
@@ -4327,8 +4855,26 @@ class EvmChainStrategy {
4327
4855
  console.log(
4328
4856
  `Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
4329
4857
  );
4858
+ let actualFeeValue;
4859
+ try {
4860
+ const receipt = error.receipt;
4861
+ const gasUsed = receipt.gasUsed;
4862
+ const effectiveGasPrice = receipt.gasPrice || receipt.effectiveGasPrice;
4863
+ if (gasUsed && effectiveGasPrice) {
4864
+ const feeWei = gasUsed * effectiveGasPrice;
4865
+ const { formatUnits: formatUnits2 } = await import("ethers");
4866
+ const feeInNative = formatUnits2(feeWei, 18);
4867
+ actualFeeValue = feeInNative;
4868
+ console.log(
4869
+ `Replacement transaction fee: ${feeInNative} (native token)`
4870
+ );
4871
+ }
4872
+ } catch (feeError) {
4873
+ console.warn("Failed to calculate replacement transaction fee:", feeError);
4874
+ }
4330
4875
  return {
4331
- completed: true
4876
+ completed: true,
4877
+ actualFeeValue
4332
4878
  };
4333
4879
  } else {
4334
4880
  const chainError2 = new TransactionRevertedError("evm", txHash);
@@ -4531,6 +5077,9 @@ class TonChainStrategy {
4531
5077
  getConnectLabel(t) {
4532
5078
  return t("wallets.connectTonWallet");
4533
5079
  }
5080
+ getClient() {
5081
+ return getTonClient(this.config.tonClient, this.config.tonApiKey);
5082
+ }
4534
5083
  async getBalances(address, tokens) {
4535
5084
  return await getTonBalances(
4536
5085
  address,
@@ -4543,39 +5092,6 @@ class TonChainStrategy {
4543
5092
  if (!address) return false;
4544
5093
  return true;
4545
5094
  }
4546
- async estimateGasRequirement(params) {
4547
- const {
4548
- selectedToken,
4549
- nativeTokenSymbol,
4550
- amount,
4551
- balances,
4552
- nativeDecimals = 9,
4553
- reserveFallback
4554
- } = params;
4555
- const nativeSym = nativeTokenSymbol.toUpperCase();
4556
- const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
4557
- const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
4558
- const { formatUnits: formatUnits2 } = await import("ethers");
4559
- const estimatedGas = Number(
4560
- formatUnits2(TON_CONFIG.estimatedNetworkFee, nativeDecimals)
4561
- );
4562
- const requiredNative = estimatedGas > 0 ? estimatedGas : reserveFallback;
4563
- const amountNum = amount ?? 0;
4564
- let hasEnoughGas = true;
4565
- if (isNativeSelected) {
4566
- hasEnoughGas = nativeBalance - amountNum >= requiredNative;
4567
- } else {
4568
- hasEnoughGas = nativeBalance >= requiredNative;
4569
- }
4570
- return {
4571
- estimatedGas,
4572
- nativeBalance,
4573
- requiredNative,
4574
- hasEnoughGas,
4575
- nativeSym,
4576
- isNativeSelected
4577
- };
4578
- }
4579
5095
  validateSteps(steps) {
4580
5096
  if (!steps || steps.length === 0) {
4581
5097
  throw new InvalidStepsError("ton", "No transaction steps provided");
@@ -4652,11 +5168,11 @@ class TonChainStrategy {
4652
5168
  }
4653
5169
  async waitForCompletion(txHash) {
4654
5170
  try {
4655
- const confirmed = await this.checkTonTransaction(
5171
+ const result = await this.checkTonTransaction(
4656
5172
  txHash,
4657
5173
  TON_CONFIG.timeout
4658
5174
  );
4659
- if (!confirmed) {
5175
+ if (!result.confirmed) {
4660
5176
  const error = new TransactionFailedError(
4661
5177
  "ton",
4662
5178
  "Transaction not confirmed on-chain",
@@ -4668,7 +5184,9 @@ class TonChainStrategy {
4668
5184
  };
4669
5185
  }
4670
5186
  return {
4671
- completed: true
5187
+ completed: true,
5188
+ actualFeeValue: result.fee,
5189
+ actualFeeSymbol: "TON"
4672
5190
  };
4673
5191
  } catch (error) {
4674
5192
  const chainError = toChainStrategyError(
@@ -4711,7 +5229,7 @@ class TonChainStrategy {
4711
5229
  "Expected external-in message, got:",
4712
5230
  inMessage.info.type
4713
5231
  );
4714
- return false;
5232
+ return { confirmed: false };
4715
5233
  }
4716
5234
  accountAddress = inMessage.info.dest;
4717
5235
  targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
@@ -4719,7 +5237,7 @@ class TonChainStrategy {
4719
5237
  targetMessageHash = Buffer.from(hashOrBoc, "hex");
4720
5238
  if (!this.config.tonAddress) {
4721
5239
  console.debug("No wallet address available for hex hash lookup");
4722
- return false;
5240
+ return { confirmed: false };
4723
5241
  }
4724
5242
  accountAddress = Address.parse(this.config.tonAddress);
4725
5243
  }
@@ -4746,7 +5264,13 @@ class TonChainStrategy {
4746
5264
  );
4747
5265
  if (txInMessageHash.equals(targetMessageHash)) {
4748
5266
  console.debug("Transaction found by in-message hash");
4749
- return true;
5267
+ const totalFees = tx.totalFees;
5268
+ const feeInTon = Number(totalFees) / 1e9;
5269
+ console.log(`TON transaction fee: ${feeInTon} TON`);
5270
+ return {
5271
+ confirmed: true,
5272
+ fee: feeInTon.toString()
5273
+ };
4750
5274
  }
4751
5275
  }
4752
5276
  }
@@ -4761,10 +5285,10 @@ class TonChainStrategy {
4761
5285
  hash = void 0;
4762
5286
  }
4763
5287
  }
4764
- return false;
5288
+ return { confirmed: false };
4765
5289
  } catch (error) {
4766
5290
  console.debug("Error parsing BOC or checking transaction:", error);
4767
- return false;
5291
+ return { confirmed: false };
4768
5292
  }
4769
5293
  }
4770
5294
  }
@@ -4818,7 +5342,7 @@ class TronChainStrategy {
4818
5342
  return t("wallets.connectTronWallet");
4819
5343
  }
4820
5344
  async getBalances(address, tokens) {
4821
- const tronWeb = this.getTronWeb();
5345
+ const tronWeb = this.getClient();
4822
5346
  if (!tronWeb) return {};
4823
5347
  return await getTronBalances(tronWeb, address, tokens);
4824
5348
  }
@@ -4826,35 +5350,6 @@ class TronChainStrategy {
4826
5350
  if (!address) return false;
4827
5351
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
4828
5352
  }
4829
- async estimateGasRequirement(params) {
4830
- const {
4831
- selectedToken,
4832
- nativeTokenSymbol,
4833
- amount,
4834
- balances,
4835
- reserveFallback
4836
- } = params;
4837
- const nativeSym = nativeTokenSymbol.toUpperCase();
4838
- const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
4839
- const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
4840
- const estimatedGas = null;
4841
- const requiredNative = reserveFallback;
4842
- const amountNum = amount ?? 0;
4843
- let hasEnoughGas = true;
4844
- if (isNativeSelected) {
4845
- hasEnoughGas = nativeBalance - amountNum >= requiredNative;
4846
- } else {
4847
- hasEnoughGas = nativeBalance >= requiredNative;
4848
- }
4849
- return {
4850
- estimatedGas,
4851
- nativeBalance,
4852
- requiredNative,
4853
- hasEnoughGas,
4854
- nativeSym,
4855
- isNativeSelected
4856
- };
4857
- }
4858
5353
  validateSteps(steps) {
4859
5354
  console.log("validateSteps");
4860
5355
  if (!steps?.length) {
@@ -4871,8 +5366,7 @@ class TronChainStrategy {
4871
5366
  }
4872
5367
  }
4873
5368
  async executeSteps(steps, _context, onFirstHash) {
4874
- console.log("executeSteps");
4875
- const tronWeb = this.getTronWeb();
5369
+ const tronWeb = this.getClient();
4876
5370
  if (!tronWeb) {
4877
5371
  throw new ProviderNotAvailableError("tron");
4878
5372
  }
@@ -4966,7 +5460,7 @@ class TronChainStrategy {
4966
5460
  }
4967
5461
  async waitForCompletion(txHash) {
4968
5462
  try {
4969
- const tronWeb = this.getTronWeb();
5463
+ const tronWeb = this.getClient();
4970
5464
  if (!tronWeb) {
4971
5465
  throw new ProviderNotAvailableError("tron");
4972
5466
  }
@@ -4975,6 +5469,7 @@ class TronChainStrategy {
4975
5469
  );
4976
5470
  const deadline = Date.now() + TRON_CONFIG.timeout;
4977
5471
  let txBlockNumber = null;
5472
+ let actualFeeTrx = null;
4978
5473
  while (Date.now() < deadline && !txBlockNumber) {
4979
5474
  try {
4980
5475
  const info = await tronWeb.trx.getTransactionInfo(txHash);
@@ -5009,7 +5504,9 @@ class TronChainStrategy {
5009
5504
  };
5010
5505
  }
5011
5506
  txBlockNumber = info.blockNumber;
5012
- console.log(`TRON transaction found in block ${txBlockNumber}`);
5507
+ const feeSun = info.fee || 0;
5508
+ actualFeeTrx = feeSun / 1e6;
5509
+ console.log(`TRON transaction found in block ${txBlockNumber}, fee: ${actualFeeTrx} TRX`);
5013
5510
  }
5014
5511
  } catch (e) {
5015
5512
  console.debug("TRON getTransactionInfo error:", e);
@@ -5037,7 +5534,9 @@ class TronChainStrategy {
5037
5534
  `TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
5038
5535
  );
5039
5536
  return {
5040
- completed: true
5537
+ completed: true,
5538
+ actualFeeValue: actualFeeTrx?.toString(),
5539
+ actualFeeSymbol: "TRX"
5041
5540
  };
5042
5541
  }
5043
5542
  console.log(
@@ -5066,7 +5565,7 @@ class TronChainStrategy {
5066
5565
  };
5067
5566
  }
5068
5567
  }
5069
- getTronWeb() {
5568
+ getClient() {
5070
5569
  return typeof window !== "undefined" ? window.tronWeb : void 0;
5071
5570
  }
5072
5571
  /**
@@ -5383,7 +5882,7 @@ class TronChainStrategy {
5383
5882
  */
5384
5883
  async checkSolidified(txHash) {
5385
5884
  try {
5386
- const tronWeb = this.getTronWeb();
5885
+ const tronWeb = this.getClient();
5387
5886
  if (!tronWeb) {
5388
5887
  return false;
5389
5888
  }
@@ -5529,7 +6028,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
5529
6028
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
5530
6029
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5531
6030
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.gasOnDestination") }),
5532
- /* @__PURE__ */ jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(TipIcon, { className: "w-4 h-4 text-muted-foreground" }) })
6031
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5533
6032
  ] }),
5534
6033
  /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
5535
6034
  ] }),
@@ -5567,7 +6066,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
5567
6066
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
5568
6067
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5569
6068
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.slippageTolerance") }),
5570
- /* @__PURE__ */ jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(TipIcon, { className: "w-4 h-4 text-muted-foreground" }) })
6069
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5571
6070
  ] }),
5572
6071
  slippageBps >= 500 && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
5573
6072
  defaultValue: "High slippage warning"
@@ -5597,7 +6096,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
5597
6096
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
5598
6097
  /* @__PURE__ */ jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5599
6098
  /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.routePriority") }),
5600
- /* @__PURE__ */ jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsx(TipIcon, { className: "w-4 h-4 text-muted-foreground" }) })
6099
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5601
6100
  ] }) }),
5602
6101
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsx(
5603
6102
  Badge,
@@ -5630,7 +6129,7 @@ const TokenRow = ({
5630
6129
  Button,
5631
6130
  {
5632
6131
  onClick: onPick,
5633
- className: `w-full rounded-sm bg-transparent flex items-center justify-between gap-3 px-5 hover:bg-accent hover:scale-100 ${isSelected ? "border border-ring" : ""}`,
6132
+ className: `w-full rounded-xs bg-transparent flex items-center justify-between gap-3 px-3 hover:bg-accent hover:scale-100 ${isSelected ? "bg-accent hover:bg-accent/50" : ""}`,
5634
6133
  children: [
5635
6134
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5636
6135
  /* @__PURE__ */ jsx(
@@ -5651,7 +6150,7 @@ const TokenRow = ({
5651
6150
  /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
5652
6151
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5653
6152
  /* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
5654
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
6153
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground/50", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
5655
6154
  ] }) })
5656
6155
  ]
5657
6156
  }
@@ -5794,7 +6293,7 @@ const TokenSelectModal = ({
5794
6293
  [groupedTokens.willChangeSrcChain]
5795
6294
  );
5796
6295
  const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
5797
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90dvh] h-[90dvh] overflow-hidden flex flex-col", children: [
6296
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col", children: [
5798
6297
  /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectToken") }) }),
5799
6298
  /* @__PURE__ */ jsx(
5800
6299
  SearchInput,
@@ -5808,24 +6307,26 @@ const TokenSelectModal = ({
5808
6307
  /* @__PURE__ */ jsx(
5809
6308
  Button,
5810
6309
  {
5811
- variant: tab === "my" ? "default" : "outline",
6310
+ variant: tab === "my" ? "default" : "ghost",
5812
6311
  onClick: () => setTab("my"),
5813
6312
  size: "sm",
6313
+ className: cn(tab !== "my" && "bg-muted hover:bg-accent"),
5814
6314
  children: t("bridge.myTokens")
5815
6315
  }
5816
6316
  ),
5817
6317
  /* @__PURE__ */ jsx(
5818
6318
  Button,
5819
6319
  {
5820
- variant: tab === "all" ? "default" : "outline",
6320
+ variant: tab === "all" ? "default" : "ghost",
5821
6321
  onClick: () => setTab("all"),
5822
6322
  size: "sm",
6323
+ className: cn(tab !== "all" && "bg-muted hover:bg-accent"),
5823
6324
  children: t("bridge.allTokens")
5824
6325
  }
5825
6326
  )
5826
6327
  ] }),
5827
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto -mx-3", children: hasNoResults ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5828
- effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase px-5 py-2", children: t("bridge.noBalancesFound") }),
6328
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto px-1", children: hasNoResults ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6329
+ effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase py-2", children: t("bridge.noBalancesFound") }),
5829
6330
  tokensToRender.map(({ token, willChangeSrc }) => {
5830
6331
  const bal = getBalance(token.symbol);
5831
6332
  const usd = getTokenUsdValue(token.symbol, token.price?.usd);
@@ -5876,6 +6377,7 @@ const TokenSelectModal = ({
5876
6377
  function useBridgeRefresh() {
5877
6378
  const qc = useQueryClient();
5878
6379
  const { srcAddress, dstAddress } = useAddresses();
6380
+ const { fromChain, toChain } = useChainsStore();
5879
6381
  const { setLoading: setQLoading, triggerRefetch } = useBridgeQuoteStore();
5880
6382
  const { hasAnyWallet } = useConnectedWalletsStore();
5881
6383
  const [spinning, setSpinning] = useState(false);
@@ -5888,14 +6390,30 @@ function useBridgeRefresh() {
5888
6390
  qc.invalidateQueries({ queryKey: ["chains"] })
5889
6391
  ];
5890
6392
  if (hasAnyWallet()) {
5891
- if (srcAddress)
6393
+ if (srcAddress) {
5892
6394
  queries.push(
5893
6395
  qc.invalidateQueries({ queryKey: ["srcTokens", srcAddress] })
5894
6396
  );
5895
- if (dstAddress)
6397
+ if (fromChain?.chainKey) {
6398
+ queries.push(
6399
+ qc.invalidateQueries({
6400
+ queryKey: ["balances", fromChain.chainKey, srcAddress]
6401
+ })
6402
+ );
6403
+ }
6404
+ }
6405
+ if (dstAddress) {
5896
6406
  queries.push(
5897
6407
  qc.invalidateQueries({ queryKey: ["dstTokens", dstAddress] })
5898
6408
  );
6409
+ if (toChain?.chainKey) {
6410
+ queries.push(
6411
+ qc.invalidateQueries({
6412
+ queryKey: ["balances", toChain.chainKey, dstAddress]
6413
+ })
6414
+ );
6415
+ }
6416
+ }
5899
6417
  }
5900
6418
  await Promise.all(queries);
5901
6419
  triggerRefetch();
@@ -5906,6 +6424,8 @@ function useBridgeRefresh() {
5906
6424
  qc,
5907
6425
  srcAddress,
5908
6426
  dstAddress,
6427
+ fromChain?.chainKey,
6428
+ toChain?.chainKey,
5909
6429
  hasAnyWallet,
5910
6430
  setQLoading,
5911
6431
  triggerRefetch
@@ -5925,7 +6445,7 @@ const RefreshButton = () => {
5925
6445
  onClick: handleRefresh,
5926
6446
  disabled: spinning,
5927
6447
  variant: "secondary",
5928
- className: "bg-card text-card-foreground hover:text-card-foreground h-9",
6448
+ className: "h-9 w-11",
5929
6449
  size: "sm",
5930
6450
  children: /* @__PURE__ */ jsx(
5931
6451
  ReloadIcon,
@@ -5953,7 +6473,7 @@ const SelectTokenButton = ({
5953
6473
  onClick,
5954
6474
  size: "sm",
5955
6475
  variant: "secondary",
5956
- className: "shrink-0 gap-2 bg-card text-card-foreground hover:text-card-foreground h-9",
6476
+ className: "shrink-0 gap-2 h-9 !pl-2",
5957
6477
  type: "button",
5958
6478
  "aria-label": label,
5959
6479
  children: [
@@ -6001,7 +6521,7 @@ const Toolbar = () => {
6001
6521
  {
6002
6522
  onClick: onOpenSettings,
6003
6523
  size: "sm",
6004
- className: "bg-card text-card-foreground hover:text-card-foreground h-9",
6524
+ className: "h-9 w-11",
6005
6525
  variant: "secondary",
6006
6526
  children: /* @__PURE__ */ jsx(BoltIcon, { stroke: "currentColor" })
6007
6527
  }
@@ -6282,11 +6802,16 @@ export {
6282
6802
  EvaaBridge,
6283
6803
  RoutePriority,
6284
6804
  RouteType,
6805
+ addEvmNetworkFee,
6285
6806
  addTonNetworkFee,
6807
+ addTronNetworkFee,
6286
6808
  addrForApi,
6287
6809
  buildAssetMatrix,
6288
6810
  calculateMinReceived,
6289
6811
  computeFeesUsdFromArray,
6812
+ estimateEvmNetworkFee,
6813
+ estimateTonNetworkFee,
6814
+ estimateTronNetworkFee,
6290
6815
  findNativeMeta,
6291
6816
  formatAddress,
6292
6817
  formatBalance,