@rash2x/bridge-widget 0.2.11 → 0.3.1

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 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
  {
@@ -1568,7 +2262,7 @@ const SwapButton = () => {
1568
2262
  variant: "secondary",
1569
2263
  size: "sm",
1570
2264
  className: cn(
1571
- "bg-card text-card-foreground hover:text-card-foreground backdrop-blur-md h-9",
2265
+ "backdrop-blur-md h-9 w-9",
1572
2266
  "absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2",
1573
2267
  !canSwap || isSwapping ? "opacity-50 cursor-not-allowed" : "hover:scale-110"
1574
2268
  ),
@@ -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,36 @@ 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();
1774
2455
  const handleClose = useCallback(() => {
1775
2456
  setQuery("");
1776
2457
  onClose();
@@ -1799,40 +2480,98 @@ const ChainSelectModal = ({
1799
2480
  },
1800
2481
  [chains, selectedAssetSymbol, assetMatrix]
1801
2482
  );
2483
+ const findCompatibleTokenAndSrcChain = useCallback(
2484
+ (dstChain) => {
2485
+ if (!chains || !assetMatrix) return void 0;
2486
+ const PRIORITY_CHAINS = [
2487
+ "ethereum",
2488
+ "arbitrum",
2489
+ "bsc",
2490
+ "polygon",
2491
+ "optimism",
2492
+ "base"
2493
+ ];
2494
+ for (const tokenSymbol of Object.keys(assetMatrix)) {
2495
+ const assetByChain = assetMatrix[tokenSymbol];
2496
+ if (!assetByChain[dstChain.chainKey]) continue;
2497
+ const availableChains = chains.filter(
2498
+ (c) => assetByChain[c.chainKey] && c.chainKey !== dstChain.chainKey
2499
+ );
2500
+ if (availableChains.length === 0) continue;
2501
+ let sourceChain;
2502
+ for (const chainKey of PRIORITY_CHAINS) {
2503
+ sourceChain = availableChains.find((c) => c.chainKey === chainKey);
2504
+ if (sourceChain) break;
2505
+ }
2506
+ if (!sourceChain) sourceChain = availableChains[0];
2507
+ return { tokenSymbol, sourceChain };
2508
+ }
2509
+ return void 0;
2510
+ },
2511
+ [chains, assetMatrix]
2512
+ );
1802
2513
  const groupedChains = useMemo(() => {
1803
2514
  const q = query.trim().toLowerCase();
1804
2515
  const filtered = q ? (items ?? []).filter(
1805
2516
  (c) => c.name.toLowerCase().includes(q) || c.chainKey.toLowerCase().includes(q)
1806
2517
  ) : items ?? [];
1807
- const groups = { available: [], willChangeSrc: [] };
2518
+ const groups = {
2519
+ available: [],
2520
+ willChangeSrc: [],
2521
+ willChangeTokenAndSrc: []
2522
+ };
1808
2523
  for (const chain of filtered) {
1809
2524
  const isAllowed = allowedItems?.some((c) => c.chainKey === chain.chainKey) || false;
1810
2525
  if (isAllowed) {
1811
2526
  groups.available.push(chain);
1812
2527
  } else {
1813
2528
  const compatibleSrc = findCompatibleSrcChain(chain);
1814
- if (compatibleSrc) groups.willChangeSrc.push(chain);
2529
+ if (compatibleSrc) {
2530
+ groups.willChangeSrc.push(chain);
2531
+ } else {
2532
+ const compatibleTokenAndSrc = findCompatibleTokenAndSrcChain(chain);
2533
+ if (compatibleTokenAndSrc) {
2534
+ groups.willChangeTokenAndSrc.push(chain);
2535
+ }
2536
+ }
1815
2537
  }
1816
2538
  }
1817
2539
  groups.available.sort((a, b) => a.name.localeCompare(b.name));
1818
2540
  groups.willChangeSrc.sort((a, b) => a.name.localeCompare(b.name));
2541
+ groups.willChangeTokenAndSrc.sort((a, b) => a.name.localeCompare(b.name));
1819
2542
  return groups;
1820
- }, [items, query, allowedItems, findCompatibleSrcChain]);
1821
- const onChainPick = (chain, willChangeSrc = false) => {
1822
- if (willChangeSrc) {
2543
+ }, [
2544
+ items,
2545
+ query,
2546
+ allowedItems,
2547
+ findCompatibleSrcChain,
2548
+ findCompatibleTokenAndSrcChain
2549
+ ]);
2550
+ const onChainPick = (chain, willChangeSrc = false, willChangeTokenAndSrc = false) => {
2551
+ if (willChangeTokenAndSrc) {
2552
+ const result = findCompatibleTokenAndSrcChain(chain);
2553
+ if (result) {
2554
+ setSelectedAssetSymbol(result.tokenSymbol);
2555
+ if (setFromChain) setFromChain(result.sourceChain);
2556
+ }
2557
+ } else if (willChangeSrc) {
1823
2558
  const newSrcChain = findCompatibleSrcChain(chain);
1824
2559
  if (newSrcChain && setFromChain) setFromChain(newSrcChain);
1825
2560
  }
1826
2561
  onChangeChain(chain);
1827
2562
  handleClose();
1828
2563
  };
1829
- const renderChainItem = (chain, willChangeSrc) => {
1830
- const isSelected = fromChain?.chainKey === chain.chainKey || toChain?.chainKey === chain.chainKey;
2564
+ const renderChainItem = (chain, willChangeSrc, willChangeTokenAndSrc = false) => {
2565
+ const isSelected = isSource ? fromChain?.chainKey === chain.chainKey : toChain?.chainKey === chain.chainKey;
1831
2566
  return /* @__PURE__ */ jsx(
1832
2567
  Button,
1833
2568
  {
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" : ""}`,
2569
+ onClick: () => onChainPick(chain, willChangeSrc, willChangeTokenAndSrc),
2570
+ className: cn(
2571
+ "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",
2572
+ isSelected ? "bg-accent" : "",
2573
+ willChangeSrc || willChangeTokenAndSrc ? "opacity-50 hover:opacity-100" : ""
2574
+ ),
1836
2575
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1837
2576
  /* @__PURE__ */ jsx(
1838
2577
  NetworkSymbol,
@@ -1848,27 +2587,44 @@ const ChainSelectModal = ({
1848
2587
  chain.chainKey
1849
2588
  );
1850
2589
  };
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: [
1852
- /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectNetwork") }) }),
2590
+ 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 p-10", children: [
2591
+ /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl leading-8", children: t("bridge.selectNetwork") }) }),
1853
2592
  /* @__PURE__ */ jsx(
1854
2593
  SearchInput,
1855
2594
  {
1856
2595
  placeholder: t("bridge.searchDestinationChain"),
1857
2596
  value: query,
1858
2597
  onChange: setQuery,
1859
- containerClassName: "rounded-md",
1860
2598
  className: "text-foreground placeholder:text-muted-foreground"
1861
2599
  }
1862
2600
  ),
1863
2601
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
1864
2602
  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)
2603
+ groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2604
+ /* @__PURE__ */ jsx(
2605
+ "p",
2606
+ {
2607
+ className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 ? "mt-10" : ""}`,
2608
+ children: t("bridge.willChangeSourceChain")
2609
+ }
2610
+ ),
2611
+ groupedChains.willChangeSrc.map(
2612
+ (c) => renderChainItem(c, true, false)
2613
+ )
2614
+ ] }),
2615
+ groupedChains.willChangeTokenAndSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
2616
+ /* @__PURE__ */ jsx(
2617
+ "p",
2618
+ {
2619
+ 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" : ""}`,
2620
+ children: t("bridge.willChangeSourceNetworkAndToken")
2621
+ }
2622
+ ),
2623
+ groupedChains.willChangeTokenAndSrc.map(
2624
+ (c) => renderChainItem(c, false, true)
1869
2625
  )
1870
2626
  ] }),
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") })
2627
+ 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
2628
  ] })
1873
2629
  ] }) });
1874
2630
  };
@@ -1878,52 +2634,10 @@ const useWalletSelectModal = create((set) => ({
1878
2634
  onOpen: (addressType) => set({ isOpen: true, addressType }),
1879
2635
  onClose: () => set({ isOpen: false, addressType: void 0 })
1880
2636
  }));
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
2637
  const prefixIcons = {
1924
2638
  tronlink: /* @__PURE__ */ jsx(TronLinkIcon, { className: "w-5 h-5" }),
1925
2639
  metamask: /* @__PURE__ */ jsx(MetaMaskIcon, { className: "w-5 h-5" }),
1926
- ton: /* @__PURE__ */ jsx(TonKeeperIcon, { className: "w-5 h-5" })
2640
+ ton: /* @__PURE__ */ jsx(TonConnectIcon, { className: "w-5 h-5" })
1927
2641
  };
1928
2642
  const mapWalletToType = (wallet) => {
1929
2643
  switch (wallet) {
@@ -1974,7 +2688,7 @@ const WalletInlineButton = ({
1974
2688
  disabled: isButtonDisabled,
1975
2689
  variant: "ghost",
1976
2690
  size: "sm",
1977
- className: "flex gap-1 cursor-pointer !px-0 pr-1 h-5",
2691
+ className: "flex gap-1 cursor-pointer hover:opacity-60 hover:bg-transparent !px-0 pr-1 h-5",
1978
2692
  children: [
1979
2693
  /* @__PURE__ */ jsx("span", { children: isConnected ? prefixIcons[wallet] : null }),
1980
2694
  /* @__PURE__ */ jsx("span", { className: "leading-3 text-sm border-b border-dotted border-link text-link", children: buttonText })
@@ -2110,7 +2824,8 @@ const SwapSection = ({
2110
2824
  onClose,
2111
2825
  items: chains,
2112
2826
  allowedItems: allowedChains,
2113
- onChangeChain
2827
+ onChangeChain,
2828
+ isSource
2114
2829
  }
2115
2830
  )
2116
2831
  ] });
@@ -2164,7 +2879,6 @@ const AnotherAddress = () => {
2164
2879
  /* @__PURE__ */ jsx(
2165
2880
  Switch,
2166
2881
  {
2167
- className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
2168
2882
  "aria-pressed": enabled,
2169
2883
  checked: enabled,
2170
2884
  onClick: () => setEnabled((v) => !v)
@@ -2183,7 +2897,7 @@ const AnotherAddress = () => {
2183
2897
  "div",
2184
2898
  {
2185
2899
  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",
2900
+ "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
2901
  {
2188
2902
  "py-4": value,
2189
2903
  "border border-ring": isFocused,
@@ -2193,13 +2907,13 @@ const AnotherAddress = () => {
2193
2907
  children: [
2194
2908
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
2195
2909
  /* @__PURE__ */ jsx(
2196
- Input,
2910
+ "textarea",
2197
2911
  {
2198
2912
  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"
2913
+ "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
2914
  ),
2201
2915
  placeholder: t("bridge.anotherAddressPlaceholder"),
2202
- type: "text",
2916
+ rows: value.length >= 32 ? 2 : 1,
2203
2917
  value,
2204
2918
  onFocus: () => setIsFocused(true),
2205
2919
  onBlur: () => setIsFocused(false),
@@ -2216,7 +2930,7 @@ const AnotherAddress = () => {
2216
2930
  Button,
2217
2931
  {
2218
2932
  variant: "secondary",
2219
- className: "bg-card text-card-foreground",
2933
+ className: "bg-accent text-card-foreground uppercase text-xs",
2220
2934
  size: "sm",
2221
2935
  onClick: onPaste,
2222
2936
  children: t("common.paste")
@@ -2227,257 +2941,135 @@ const AnotherAddress = () => {
2227
2941
  variant: "ghost",
2228
2942
  size: "sm",
2229
2943
  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));
2944
+ onClick: () => setValue(""),
2945
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
2946
+ }
2947
+ )
2948
+ ]
2949
+ }
2950
+ )
2951
+ },
2952
+ "custom-address-block"
2953
+ ) })
2954
+ ] });
2955
+ };
2956
+ const InfoIcon = (props) => {
2957
+ return /* @__PURE__ */ jsx(
2958
+ "svg",
2959
+ {
2960
+ width: "16",
2961
+ height: "16",
2962
+ viewBox: "0 0 16 16",
2963
+ fill: "none",
2964
+ xmlns: "http://www.w3.org/2000/svg",
2965
+ ...props,
2966
+ children: /* @__PURE__ */ jsx(
2967
+ "path",
2968
+ {
2969
+ 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",
2970
+ stroke: "currentColor",
2971
+ "stroke-width": "1.68349",
2972
+ "stroke-linecap": "round"
2973
+ }
2974
+ )
2423
2975
  }
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
2976
  );
2461
- if (hasNetworkFee) {
2462
- return quote;
2463
- }
2464
- return {
2465
- ...quote,
2466
- fees: [...quote.fees || [], networkFee]
2467
- };
2977
+ };
2978
+ const Tip = (props) => {
2979
+ const { children, text } = props;
2980
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
2981
+ /* @__PURE__ */ jsx(TooltipTrigger, { className: "w-4 h-4 rounded-full bg-muted text-muted-foreground", children }),
2982
+ /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: text }) })
2983
+ ] });
2984
+ };
2985
+ const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/tokens";
2986
+ const TokenSymbol = ({
2987
+ symbol,
2988
+ className = "w-4 h-4",
2989
+ alt
2990
+ }) => {
2991
+ const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
2992
+ const src = `${BASE_URL}/${normalizedSymbol}.svg`;
2993
+ return /* @__PURE__ */ jsx("img", { src, alt: alt ?? symbol, className });
2994
+ };
2995
+ function getSimpleFallback(chainKey) {
2996
+ const key = chainKey.toLowerCase();
2997
+ if (key === "ton") return 0.15;
2998
+ if (key === "tron") return 10;
2999
+ return 0.01;
2468
3000
  }
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
- };
3001
+ function useGasEstimate(amountNum) {
3002
+ const { fromChain } = useChainsStore();
3003
+ const { selectedAssetSymbol } = useTokensStore();
3004
+ const { srcAddress } = useAddresses();
3005
+ const { balances, isLoading: balancesLoading } = useBalances(
3006
+ fromChain?.chainKey,
3007
+ srcAddress
3008
+ );
3009
+ const { quote } = useBridgeQuoteStore();
3010
+ const balancesKnown = !balancesLoading;
3011
+ const chainKey = fromChain?.chainKey;
3012
+ const nativeCurrencySymbol = fromChain?.nativeCurrency?.symbol;
3013
+ const nativeCurrencyAddress = fromChain?.nativeCurrency?.address;
3014
+ const nativeCurrencyDecimals = fromChain?.nativeCurrency?.decimals;
3015
+ const quoteFees = quote?.fees ? JSON.stringify(quote.fees) : null;
3016
+ const quoteSrcChainKey = quote?.srcChainKey;
3017
+ const nativeBalanceValue = nativeCurrencySymbol ? Number(balances[nativeCurrencySymbol.toUpperCase()]?.balance ?? 0) : 0;
3018
+ const result = useMemo(() => {
3019
+ if (!chainKey || !nativeCurrencySymbol) {
3020
+ return {
3021
+ nativeSym: "",
3022
+ nativeBalance: 0,
3023
+ requiredNative: 0,
3024
+ balancesKnown,
3025
+ isNativeSelected: false,
3026
+ hasEnoughGas: true
3027
+ };
3028
+ }
3029
+ const nativeSym = nativeCurrencySymbol.toUpperCase();
3030
+ const nativeBalance = nativeBalanceValue;
3031
+ const isNativeSelected = nativeSym === (selectedAssetSymbol || "").toUpperCase();
3032
+ let requiredNative = 0;
3033
+ if (quoteFees && quoteSrcChainKey === chainKey) {
3034
+ const fees = JSON.parse(quoteFees);
3035
+ const feesInNative = fees.filter(
3036
+ (f) => f.chainKey === chainKey && f.token === nativeCurrencyAddress
3037
+ ).reduce(
3038
+ (sum, f) => sum + BigInt(f.amount || "0"),
3039
+ 0n
3040
+ );
3041
+ const decimals = nativeCurrencyDecimals || 18;
3042
+ requiredNative = Number(feesInNative) / Math.pow(10, decimals);
3043
+ } else {
3044
+ requiredNative = getSimpleFallback(chainKey);
3045
+ }
3046
+ let hasEnoughGas = true;
3047
+ if (isNativeSelected) {
3048
+ hasEnoughGas = nativeBalance - (amountNum ?? 0) >= requiredNative;
3049
+ } else {
3050
+ hasEnoughGas = nativeBalance >= requiredNative;
3051
+ }
3052
+ return {
3053
+ nativeSym,
3054
+ nativeBalance,
3055
+ requiredNative,
3056
+ balancesKnown,
3057
+ isNativeSelected,
3058
+ hasEnoughGas: balancesKnown ? hasEnoughGas : true
3059
+ };
3060
+ }, [
3061
+ chainKey,
3062
+ nativeCurrencySymbol,
3063
+ nativeCurrencyAddress,
3064
+ nativeCurrencyDecimals,
3065
+ selectedAssetSymbol,
3066
+ quoteFees,
3067
+ quoteSrcChainKey,
3068
+ amountNum,
3069
+ balancesKnown,
3070
+ nativeBalanceValue
3071
+ ]);
3072
+ return result;
2481
3073
  }
2482
3074
  function getRouteDisplayName(route) {
2483
3075
  if (!route) return "Stargate Bridge";
@@ -2494,6 +3086,7 @@ const Details = () => {
2494
3086
  const { toChain, fromChain, chains } = useChainsStore();
2495
3087
  const { quote, status } = useBridgeQuoteStore();
2496
3088
  const { slippageBps, routePriority } = useSettingsStore();
3089
+ const gas = useGasEstimate();
2497
3090
  const dstToken = resolveTokenOnChainFromMatrix$2(
2498
3091
  assetMatrix,
2499
3092
  selectedAssetSymbol,
@@ -2504,9 +3097,8 @@ const Details = () => {
2504
3097
  selectedAssetSymbol,
2505
3098
  fromChain?.chainKey
2506
3099
  );
2507
- const quoteWithFees = addTonNetworkFee(quote, chains);
2508
3100
  const quoteDetails = getQuoteDetails(
2509
- quoteWithFees || quote,
3101
+ quote,
2510
3102
  srcToken,
2511
3103
  dstToken,
2512
3104
  tokens,
@@ -2517,21 +3109,7 @@ const Details = () => {
2517
3109
  const isLoading = status === "loading";
2518
3110
  const receiveText = quoteDetails.outputAmount != null ? Number(quoteDetails.outputAmount).toFixed(6) : "0.00";
2519
3111
  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
- })();
3112
+ const totalFeeDisplay = gas.requiredNative > 0 ? `${gas.requiredNative.toFixed(6)} ${gas.nativeSym}` : "—";
2535
3113
  const currentSlippageText = formatPercentage(slippageBps);
2536
3114
  const routeText = quote?.route ? getRouteDisplayName(quote.route) : t(`settings.routePresets.${routePriority}`);
2537
3115
  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 +3129,7 @@ const Details = () => {
2551
3129
  DetailsRow,
2552
3130
  {
2553
3131
  label: t("transaction.route"),
2554
- value: /* @__PURE__ */ jsxs("strong", { className: "flex items-center font-semibold gap-2", children: [
3132
+ value: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
2555
3133
  /* @__PURE__ */ jsx(StargateIcon, { className: "w-4 h-4" }),
2556
3134
  /* @__PURE__ */ jsx("p", { className: "", children: routeText })
2557
3135
  ] })
@@ -2578,7 +3156,14 @@ const Details = () => {
2578
3156
  label: t("transaction.totalFee"),
2579
3157
  value: /* @__PURE__ */ jsxs("strong", { className: "inline-flex items-center gap-1", children: [
2580
3158
  /* @__PURE__ */ jsx("span", { children: totalFeeDisplay }),
2581
- /* @__PURE__ */ jsx(TokenSymbol, { symbol, className: "w-4 h-4", alt: "token" })
3159
+ /* @__PURE__ */ jsx(
3160
+ TokenSymbol,
3161
+ {
3162
+ symbol: gas.nativeSym,
3163
+ className: "w-4 h-4",
3164
+ alt: "token"
3165
+ }
3166
+ )
2582
3167
  ] }),
2583
3168
  isLoading
2584
3169
  }
@@ -2593,7 +3178,7 @@ const DetailsRow = ({
2593
3178
  }) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
2594
3179
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2595
3180
  /* @__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" }) })
3181
+ /* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(InfoIcon, {}) })
2597
3182
  ] }),
2598
3183
  isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16 rounded-md" }) : /* @__PURE__ */ jsx("div", { className: "text-foreground text-sm", children: value ?? "—" })
2599
3184
  ] });
@@ -2646,6 +3231,20 @@ const useTransactionStore = create((set, get) => ({
2646
3231
  };
2647
3232
  set({ current: next });
2648
3233
  },
3234
+ updateActualFee: (feeValue, feeSymbol) => {
3235
+ const cur = get().current;
3236
+ if (!cur) return;
3237
+ const next = {
3238
+ ...cur,
3239
+ metadata: {
3240
+ ...cur.metadata,
3241
+ actualFeeValue: feeValue,
3242
+ actualFeeSymbol: feeSymbol
3243
+ },
3244
+ updatedAt: Date.now()
3245
+ };
3246
+ set({ current: next });
3247
+ },
2649
3248
  reset: () => {
2650
3249
  set({ current: void 0 });
2651
3250
  }
@@ -2791,9 +3390,13 @@ function isUserRejection(error) {
2791
3390
  if (error instanceof TransactionRejectedError) {
2792
3391
  return true;
2793
3392
  }
3393
+ if (typeof error === "string") {
3394
+ const message = error.toLowerCase();
3395
+ 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");
3396
+ }
2794
3397
  if (error instanceof Error) {
2795
3398
  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");
3399
+ 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
3400
  }
2798
3401
  return false;
2799
3402
  }
@@ -2812,17 +3415,13 @@ function toChainStrategyError(error, chainKey, context) {
2812
3415
  error
2813
3416
  );
2814
3417
  }
2815
- return new ChainStrategyError(
2816
- String(error),
2817
- "UNKNOWN_ERROR",
2818
- chainKey
2819
- );
3418
+ return new ChainStrategyError(String(error), "UNKNOWN_ERROR", chainKey);
2820
3419
  }
2821
3420
  function useBridgeTransaction() {
2822
3421
  const { quote } = useBridgeQuoteStore();
2823
3422
  const { chainRegistry } = useChainStrategies();
2824
3423
  const { srcAddress, dstAddress } = useAddresses();
2825
- const { assetMatrix, tokens, selectedAssetSymbol } = useTokensStore();
3424
+ const { assetMatrix, selectedAssetSymbol } = useTokensStore();
2826
3425
  const { chains } = useChainsStore();
2827
3426
  const txStore = useTransactionStore();
2828
3427
  const [isProcessing, setIsProcessing] = useState(false);
@@ -2851,31 +3450,33 @@ function useBridgeTransaction() {
2851
3450
  }
2852
3451
  const srcChain = chains?.find((c) => c.chainKey === quote.srcChainKey);
2853
3452
  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);
3453
+ const amounts = getQuoteAmounts(quote, srcToken, dstToken);
2857
3454
  const metadata = {
2858
3455
  srcChainName: srcChain?.name || quote.srcChainKey,
2859
3456
  dstChainName: dstChain?.name || quote.dstChainKey,
2860
3457
  srcTokenSymbol: srcToken?.symbol || quote.srcToken,
2861
3458
  dstTokenSymbol: dstToken?.symbol || quote.dstToken,
2862
3459
  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
3460
+ dstAmountHuman: amounts.outputHuman
3461
+ // Actual fee will be updated when transaction completes
2867
3462
  };
2868
3463
  txStore.setTransaction(quote, "executing", metadata);
2869
3464
  setIsProcessing(true);
2870
3465
  try {
2871
3466
  const steps = quote.steps || [];
2872
3467
  if (steps.length === 0) {
2873
- throw new InvalidStepsError(quote.srcChainKey, "No transaction steps found in quote");
3468
+ throw new InvalidStepsError(
3469
+ quote.srcChainKey,
3470
+ "No transaction steps found in quote"
3471
+ );
2874
3472
  }
2875
3473
  const chainKey = steps[0].chainKey;
2876
3474
  const strategy = chainRegistry.getStrategy(chainKey);
2877
3475
  if (!strategy) {
2878
- throw new InvalidStepsError(chainKey, `No strategy available for chain: ${chainKey}`);
3476
+ throw new InvalidStepsError(
3477
+ chainKey,
3478
+ `No strategy available for chain: ${chainKey}`
3479
+ );
2879
3480
  }
2880
3481
  if (!strategy.isConnected()) {
2881
3482
  throw new WalletNotConnectedError(chainKey);
@@ -2887,7 +3488,6 @@ function useBridgeTransaction() {
2887
3488
  srcChainKey: quote.srcChainKey,
2888
3489
  dstChainKey: quote.dstChainKey
2889
3490
  };
2890
- console.log(steps);
2891
3491
  const txResult = await strategy.executeSteps(steps, context, (hash) => {
2892
3492
  txStore.setSrcHash(hash);
2893
3493
  txStore.updateStatus("processing");
@@ -2898,6 +3498,21 @@ function useBridgeTransaction() {
2898
3498
  if (result.dstTxHash) {
2899
3499
  txStore.setDstHash(result.dstTxHash);
2900
3500
  }
3501
+ if (result.actualFeeValue) {
3502
+ let feeSymbol = result.actualFeeSymbol;
3503
+ if (!feeSymbol) {
3504
+ const srcChain2 = chains?.find(
3505
+ (c) => c.chainKey === quote.srcChainKey
3506
+ );
3507
+ feeSymbol = srcChain2?.nativeCurrency?.symbol || "";
3508
+ }
3509
+ if (feeSymbol) {
3510
+ const feeValue = parseFloat(result.actualFeeValue);
3511
+ if (!isNaN(feeValue)) {
3512
+ txStore.updateActualFee(feeValue, feeSymbol);
3513
+ }
3514
+ }
3515
+ }
2901
3516
  txStore.updateStatus("completed");
2902
3517
  console.log("Transaction completed successfully");
2903
3518
  } else {
@@ -2915,19 +3530,26 @@ function useBridgeTransaction() {
2915
3530
  console.error("Error tracking completion:", err);
2916
3531
  });
2917
3532
  } else {
2918
- throw new TransactionFailedError(chainKey, "Transaction hash not received from wallet");
3533
+ throw new TransactionFailedError(
3534
+ chainKey,
3535
+ "Transaction hash not received from wallet"
3536
+ );
2919
3537
  }
2920
3538
  return txResult;
2921
3539
  } catch (err) {
2922
3540
  if (isUserRejection(err)) {
2923
3541
  txStore.setError("TRANSACTION_REJECTED");
2924
- throw new TransactionFailedError(quote.srcChainKey, "Transaction rejected by user");
3542
+ throw new TransactionFailedError(
3543
+ quote.srcChainKey,
3544
+ "Transaction rejected by user"
3545
+ );
2925
3546
  }
2926
3547
  if (ChainStrategyError.isChainStrategyError(err)) {
2927
3548
  txStore.setError(err.code, { chainKey: err.chainKey });
2928
3549
  console.error("Chain strategy error:", err.toJSON());
2929
3550
  throw err;
2930
3551
  }
3552
+ console.log(err);
2931
3553
  txStore.setError("UNKNOWN_ERROR");
2932
3554
  throw err;
2933
3555
  } finally {
@@ -2940,66 +3562,6 @@ function useBridgeTransaction() {
2940
3562
  hasQuote: !!quote
2941
3563
  };
2942
3564
  }
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
3565
  function useBalanceCheck(amountNum, gas) {
3004
3566
  const { fromChain } = useChainsStore();
3005
3567
  const { selectedAssetSymbol } = useTokensStore();
@@ -3330,8 +3892,8 @@ const WalletSelectModal = () => {
3330
3892
  {
3331
3893
  strategy: tonWallet,
3332
3894
  walletId: "ton",
3333
- name: t("wallets.tonKeeper"),
3334
- icon: TonKeeperIcon
3895
+ name: t("wallets.tonconnect"),
3896
+ icon: TonConnectIcon
3335
3897
  },
3336
3898
  {
3337
3899
  strategy: metaMaskWallet,
@@ -3369,8 +3931,8 @@ const WalletSelectModal = () => {
3369
3931
  const tonWallets = [
3370
3932
  {
3371
3933
  id: "ton",
3372
- name: t("wallets.tonKeeper"),
3373
- icon: TonKeeperIcon,
3934
+ name: t("wallets.tonconnect"),
3935
+ icon: TonConnectIcon,
3374
3936
  enabled: true
3375
3937
  }
3376
3938
  ];
@@ -3405,9 +3967,9 @@ const WalletSelectModal = () => {
3405
3967
  wallets: tronWallets.filter(notConnected)
3406
3968
  }
3407
3969
  ].filter((category) => category.wallets.length > 0);
3408
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
3970
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "p-10", children: [
3409
3971
  /* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
3410
- /* @__PURE__ */ jsx(DialogTitle, { children: t("wallets.chooseWallet") }),
3972
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl leading-8", children: t("wallets.chooseWallet") }),
3411
3973
  /* @__PURE__ */ jsx(DialogDescription, { children: t("wallets.oneWalletPerEnv") })
3412
3974
  ] }),
3413
3975
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
@@ -3582,7 +4144,7 @@ const SuccessStep = ({
3582
4144
  /* @__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
4145
  icon,
3584
4146
  /* @__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: [
4147
+ /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2 mt-4 relative z-10 pb-14", children: [
3586
4148
  metadata?.srcAmountHuman && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3587
4149
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.bridged") }),
3588
4150
  /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
@@ -3594,15 +4156,13 @@ const SuccessStep = ({
3594
4156
  ] }),
3595
4157
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3596
4158
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.transferTitle") }),
3597
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
4159
+ /* @__PURE__ */ jsxs("span", { className: "font-medium space-x-1", children: [
3598
4160
  /* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
3599
4161
  metadata?.srcChainName,
3600
4162
  " ",
3601
4163
  /* @__PURE__ */ jsx(NetworkSymbol, { chainKey: metadata.srcChainName })
3602
4164
  ] }),
3603
- " ",
3604
- "→",
3605
- " ",
4165
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "→" }),
3606
4166
  /* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
3607
4167
  metadata?.dstChainName,
3608
4168
  " ",
@@ -3616,17 +4176,17 @@ const SuccessStep = ({
3616
4176
  "button",
3617
4177
  {
3618
4178
  onClick: handleOpenExplorer,
3619
- className: "font-medium hover:underline cursor-pointer inline-flex items-center gap-1",
4179
+ className: "font-medium cursor-pointer inline-flex items-center gap-1 underline hover:no-underline",
3620
4180
  children: formatHash(srcTxHash)
3621
4181
  }
3622
4182
  )
3623
4183
  ] }),
3624
- metadata?.totalFeeValue && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
4184
+ metadata?.actualFeeValue && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
3625
4185
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.finalFee") }),
3626
4186
  /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
3627
- metadata.totalFeeValue,
4187
+ metadata.actualFeeValue,
3628
4188
  " ",
3629
- metadata.totalFeeSymbol
4189
+ metadata.actualFeeSymbol
3630
4190
  ] })
3631
4191
  ] })
3632
4192
  ] })
@@ -3723,7 +4283,7 @@ const useTokens = () => {
3723
4283
  return { ...query };
3724
4284
  };
3725
4285
  const useChains = () => {
3726
- const { setChains, setFromChain } = useChainsStore();
4286
+ const { setChains, setFromChain, fromChain } = useChainsStore();
3727
4287
  const query = useQuery({
3728
4288
  queryKey: ["chains"],
3729
4289
  queryFn: () => getChains(),
@@ -3737,10 +4297,16 @@ const useChains = () => {
3737
4297
  });
3738
4298
  useEffect(() => {
3739
4299
  if (query.isSuccess && query.data?.length) {
4300
+ console.log(
4301
+ `[DEBUG] Loaded ${query.data.length} chains from API:`,
4302
+ query.data.map((c) => c.chainKey)
4303
+ );
3740
4304
  setChains(query.data);
3741
- setFromChain(query.data[0]);
4305
+ if (!fromChain) {
4306
+ setFromChain(query.data[0]);
4307
+ }
3742
4308
  }
3743
- }, [query.isSuccess, query.data, setChains, setFromChain]);
4309
+ }, [query.isSuccess, query.data]);
3744
4310
  useEffect(() => {
3745
4311
  if (query.isError && query.error) {
3746
4312
  console.error(query.error.name, query.error.message);
@@ -3898,7 +4464,9 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
3898
4464
  }
3899
4465
  } catch (error) {
3900
4466
  const errorMessage = error instanceof Error ? error.message : String(error);
3901
- const isZeroDataError = errorMessage.includes('returned no data ("0x")');
4467
+ const isZeroDataError = errorMessage.includes(
4468
+ 'returned no data ("0x")'
4469
+ );
3902
4470
  if (!isZeroDataError) {
3903
4471
  console.debug(
3904
4472
  `Failed to get priority token balance for ${priorityToken.symbol}:`,
@@ -4009,12 +4577,9 @@ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
4009
4577
  }
4010
4578
  const client = getTonClient(customTonClient, tonApiKey);
4011
4579
  const accountAddress = Address$1.parse(address);
4012
- console.log(address);
4013
- console.log(tokens);
4014
4580
  try {
4015
4581
  const balance = await client.getBalance(accountAddress);
4016
4582
  const tonBalance = Number(balance) / 1e9;
4017
- console.log("tonBalance", tonBalance);
4018
4583
  if (tonBalance > 0) {
4019
4584
  balances.TON = { balance: tonBalance, address: "ton-native" };
4020
4585
  }
@@ -4071,8 +4636,23 @@ async function getTronBalances(tronWeb, address, tokens) {
4071
4636
  try {
4072
4637
  if (!tronWeb) throw new Error("TronWeb not available");
4073
4638
  const ownerB58 = toTronBase58(address, tronWeb);
4639
+ try {
4640
+ const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
4641
+ const sunNum = Number(trxBalanceSun);
4642
+ const trxVal = tronWeb.fromSun(sunNum);
4643
+ const trxStr = typeof trxVal === "string" ? trxVal : trxVal.toString();
4644
+ const trxBalance = parseFloat(trxStr);
4645
+ if (trxBalance > 0) {
4646
+ balances.TRX = { balance: trxBalance, address: ownerB58 };
4647
+ }
4648
+ } catch (error) {
4649
+ console.warn("Failed to get native TRX balance:", error);
4650
+ }
4074
4651
  for (const token of tokens) {
4075
4652
  try {
4653
+ if (isNativeAddress(token.address) && token.symbol.toUpperCase() === "TRX") {
4654
+ continue;
4655
+ }
4076
4656
  let balance = 0;
4077
4657
  if (isNativeAddress(token.address)) {
4078
4658
  const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
@@ -4159,6 +4739,9 @@ class EvmChainStrategy {
4159
4739
  getConnectLabel(t) {
4160
4740
  return t("wallets.connectEvmWallet");
4161
4741
  }
4742
+ getClient() {
4743
+ return this.provider;
4744
+ }
4162
4745
  async getBalances(address, tokens, priorityToken) {
4163
4746
  if (!this.publicClient) {
4164
4747
  console.warn("No publicClient available for balance query");
@@ -4175,52 +4758,6 @@ class EvmChainStrategy {
4175
4758
  if (!address) return false;
4176
4759
  return /^0x[0-9a-fA-F]{40}$/.test(address);
4177
4760
  }
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
4761
  validateSteps(steps) {
4225
4762
  if (!steps || steps.length === 0) {
4226
4763
  throw new InvalidStepsError("evm", "No transaction steps provided");
@@ -4313,8 +4850,24 @@ class EvmChainStrategy {
4313
4850
  console.log(
4314
4851
  `EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
4315
4852
  );
4853
+ let actualFeeValue;
4854
+ try {
4855
+ const gasUsed = receipt.gasUsed;
4856
+ const effectiveGasPrice = receipt.gasPrice;
4857
+ if (gasUsed && effectiveGasPrice) {
4858
+ const feeWei = gasUsed * effectiveGasPrice;
4859
+ const { formatUnits: formatUnits2 } = await import("ethers");
4860
+ const feeInNative = formatUnits2(feeWei, 18);
4861
+ actualFeeValue = feeInNative;
4862
+ console.log(`EVM transaction fee: ${feeInNative} (native token)`);
4863
+ }
4864
+ } catch (error) {
4865
+ console.warn("Failed to calculate actual fee:", error);
4866
+ }
4316
4867
  return {
4317
- completed: true
4868
+ completed: true,
4869
+ actualFeeValue
4870
+ // Symbol will be determined by the caller based on chain info
4318
4871
  };
4319
4872
  } catch (error) {
4320
4873
  if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
@@ -4327,8 +4880,26 @@ class EvmChainStrategy {
4327
4880
  console.log(
4328
4881
  `Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
4329
4882
  );
4883
+ let actualFeeValue;
4884
+ try {
4885
+ const receipt = error.receipt;
4886
+ const gasUsed = receipt.gasUsed;
4887
+ const effectiveGasPrice = receipt.gasPrice || receipt.effectiveGasPrice;
4888
+ if (gasUsed && effectiveGasPrice) {
4889
+ const feeWei = gasUsed * effectiveGasPrice;
4890
+ const { formatUnits: formatUnits2 } = await import("ethers");
4891
+ const feeInNative = formatUnits2(feeWei, 18);
4892
+ actualFeeValue = feeInNative;
4893
+ console.log(
4894
+ `Replacement transaction fee: ${feeInNative} (native token)`
4895
+ );
4896
+ }
4897
+ } catch (feeError) {
4898
+ console.warn("Failed to calculate replacement transaction fee:", feeError);
4899
+ }
4330
4900
  return {
4331
- completed: true
4901
+ completed: true,
4902
+ actualFeeValue
4332
4903
  };
4333
4904
  } else {
4334
4905
  const chainError2 = new TransactionRevertedError("evm", txHash);
@@ -4531,6 +5102,9 @@ class TonChainStrategy {
4531
5102
  getConnectLabel(t) {
4532
5103
  return t("wallets.connectTonWallet");
4533
5104
  }
5105
+ getClient() {
5106
+ return getTonClient(this.config.tonClient, this.config.tonApiKey);
5107
+ }
4534
5108
  async getBalances(address, tokens) {
4535
5109
  return await getTonBalances(
4536
5110
  address,
@@ -4543,39 +5117,6 @@ class TonChainStrategy {
4543
5117
  if (!address) return false;
4544
5118
  return true;
4545
5119
  }
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
5120
  validateSteps(steps) {
4580
5121
  if (!steps || steps.length === 0) {
4581
5122
  throw new InvalidStepsError("ton", "No transaction steps provided");
@@ -4652,11 +5193,11 @@ class TonChainStrategy {
4652
5193
  }
4653
5194
  async waitForCompletion(txHash) {
4654
5195
  try {
4655
- const confirmed = await this.checkTonTransaction(
5196
+ const result = await this.checkTonTransaction(
4656
5197
  txHash,
4657
5198
  TON_CONFIG.timeout
4658
5199
  );
4659
- if (!confirmed) {
5200
+ if (!result.confirmed) {
4660
5201
  const error = new TransactionFailedError(
4661
5202
  "ton",
4662
5203
  "Transaction not confirmed on-chain",
@@ -4668,7 +5209,9 @@ class TonChainStrategy {
4668
5209
  };
4669
5210
  }
4670
5211
  return {
4671
- completed: true
5212
+ completed: true,
5213
+ actualFeeValue: result.fee,
5214
+ actualFeeSymbol: "TON"
4672
5215
  };
4673
5216
  } catch (error) {
4674
5217
  const chainError = toChainStrategyError(
@@ -4711,7 +5254,7 @@ class TonChainStrategy {
4711
5254
  "Expected external-in message, got:",
4712
5255
  inMessage.info.type
4713
5256
  );
4714
- return false;
5257
+ return { confirmed: false };
4715
5258
  }
4716
5259
  accountAddress = inMessage.info.dest;
4717
5260
  targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
@@ -4719,7 +5262,7 @@ class TonChainStrategy {
4719
5262
  targetMessageHash = Buffer.from(hashOrBoc, "hex");
4720
5263
  if (!this.config.tonAddress) {
4721
5264
  console.debug("No wallet address available for hex hash lookup");
4722
- return false;
5265
+ return { confirmed: false };
4723
5266
  }
4724
5267
  accountAddress = Address.parse(this.config.tonAddress);
4725
5268
  }
@@ -4746,7 +5289,13 @@ class TonChainStrategy {
4746
5289
  );
4747
5290
  if (txInMessageHash.equals(targetMessageHash)) {
4748
5291
  console.debug("Transaction found by in-message hash");
4749
- return true;
5292
+ const totalFees = tx.totalFees;
5293
+ const feeInTon = Number(totalFees) / 1e9;
5294
+ console.log(`TON transaction fee: ${feeInTon} TON`);
5295
+ return {
5296
+ confirmed: true,
5297
+ fee: feeInTon.toString()
5298
+ };
4750
5299
  }
4751
5300
  }
4752
5301
  }
@@ -4761,10 +5310,10 @@ class TonChainStrategy {
4761
5310
  hash = void 0;
4762
5311
  }
4763
5312
  }
4764
- return false;
5313
+ return { confirmed: false };
4765
5314
  } catch (error) {
4766
5315
  console.debug("Error parsing BOC or checking transaction:", error);
4767
- return false;
5316
+ return { confirmed: false };
4768
5317
  }
4769
5318
  }
4770
5319
  }
@@ -4818,7 +5367,7 @@ class TronChainStrategy {
4818
5367
  return t("wallets.connectTronWallet");
4819
5368
  }
4820
5369
  async getBalances(address, tokens) {
4821
- const tronWeb = this.getTronWeb();
5370
+ const tronWeb = this.getClient();
4822
5371
  if (!tronWeb) return {};
4823
5372
  return await getTronBalances(tronWeb, address, tokens);
4824
5373
  }
@@ -4826,35 +5375,6 @@ class TronChainStrategy {
4826
5375
  if (!address) return false;
4827
5376
  return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
4828
5377
  }
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
5378
  validateSteps(steps) {
4859
5379
  console.log("validateSteps");
4860
5380
  if (!steps?.length) {
@@ -4871,8 +5391,7 @@ class TronChainStrategy {
4871
5391
  }
4872
5392
  }
4873
5393
  async executeSteps(steps, _context, onFirstHash) {
4874
- console.log("executeSteps");
4875
- const tronWeb = this.getTronWeb();
5394
+ const tronWeb = this.getClient();
4876
5395
  if (!tronWeb) {
4877
5396
  throw new ProviderNotAvailableError("tron");
4878
5397
  }
@@ -4966,7 +5485,7 @@ class TronChainStrategy {
4966
5485
  }
4967
5486
  async waitForCompletion(txHash) {
4968
5487
  try {
4969
- const tronWeb = this.getTronWeb();
5488
+ const tronWeb = this.getClient();
4970
5489
  if (!tronWeb) {
4971
5490
  throw new ProviderNotAvailableError("tron");
4972
5491
  }
@@ -4975,6 +5494,7 @@ class TronChainStrategy {
4975
5494
  );
4976
5495
  const deadline = Date.now() + TRON_CONFIG.timeout;
4977
5496
  let txBlockNumber = null;
5497
+ let actualFeeTrx = null;
4978
5498
  while (Date.now() < deadline && !txBlockNumber) {
4979
5499
  try {
4980
5500
  const info = await tronWeb.trx.getTransactionInfo(txHash);
@@ -5009,7 +5529,9 @@ class TronChainStrategy {
5009
5529
  };
5010
5530
  }
5011
5531
  txBlockNumber = info.blockNumber;
5012
- console.log(`TRON transaction found in block ${txBlockNumber}`);
5532
+ const feeSun = info.fee || 0;
5533
+ actualFeeTrx = feeSun / 1e6;
5534
+ console.log(`TRON transaction found in block ${txBlockNumber}, fee: ${actualFeeTrx} TRX`);
5013
5535
  }
5014
5536
  } catch (e) {
5015
5537
  console.debug("TRON getTransactionInfo error:", e);
@@ -5037,7 +5559,9 @@ class TronChainStrategy {
5037
5559
  `TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
5038
5560
  );
5039
5561
  return {
5040
- completed: true
5562
+ completed: true,
5563
+ actualFeeValue: actualFeeTrx?.toString(),
5564
+ actualFeeSymbol: "TRX"
5041
5565
  };
5042
5566
  }
5043
5567
  console.log(
@@ -5066,7 +5590,7 @@ class TronChainStrategy {
5066
5590
  };
5067
5591
  }
5068
5592
  }
5069
- getTronWeb() {
5593
+ getClient() {
5070
5594
  return typeof window !== "undefined" ? window.tronWeb : void 0;
5071
5595
  }
5072
5596
  /**
@@ -5383,7 +5907,7 @@ class TronChainStrategy {
5383
5907
  */
5384
5908
  async checkSolidified(txHash) {
5385
5909
  try {
5386
- const tronWeb = this.getTronWeb();
5910
+ const tronWeb = this.getClient();
5387
5911
  if (!tronWeb) {
5388
5912
  return false;
5389
5913
  }
@@ -5522,14 +6046,14 @@ const SettingsModal = ({ isOpen, onClose }) => {
5522
6046
  );
5523
6047
  const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
5524
6048
  const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
5525
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), children: [
5526
- /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("settings.title") }) }),
6049
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), className: "p-10", children: [
6050
+ /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl leading-8", children: t("settings.title") }) }),
5527
6051
  /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
5528
6052
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
5529
6053
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
5530
6054
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5531
6055
  /* @__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" }) })
6056
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5533
6057
  ] }),
5534
6058
  /* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
5535
6059
  ] }),
@@ -5551,9 +6075,8 @@ const SettingsModal = ({ isOpen, onClose }) => {
5551
6075
  Badge,
5552
6076
  {
5553
6077
  onClick: () => setGasPreset(g),
5554
- size: "lg",
5555
6078
  className: cn(
5556
- "cursor-pointer",
6079
+ "cursor-pointer h-7 rounded px-2",
5557
6080
  gasPreset === g ? activeBtn : notActiveBtn
5558
6081
  ),
5559
6082
  children: t(`settings.gasPresets.${g}`)
@@ -5567,7 +6090,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
5567
6090
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
5568
6091
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5569
6092
  /* @__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" }) })
6093
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5571
6094
  ] }),
5572
6095
  slippageBps >= 500 && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
5573
6096
  defaultValue: "High slippage warning"
@@ -5578,13 +6101,12 @@ const SettingsModal = ({ isOpen, onClose }) => {
5578
6101
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsx(
5579
6102
  Badge,
5580
6103
  {
5581
- size: "lg",
5582
6104
  onClick: () => {
5583
6105
  const bps = parseFloat(p.replace("%", "")) * 100;
5584
6106
  setSlippageBps(bps);
5585
6107
  },
5586
6108
  className: cn(
5587
- "cursor-pointer",
6109
+ "cursor-pointer h-7 rounded px-2",
5588
6110
  activeSlippagePreset === p ? activeBtn : notActiveBtn
5589
6111
  ),
5590
6112
  children: p
@@ -5597,15 +6119,14 @@ const SettingsModal = ({ isOpen, onClose }) => {
5597
6119
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
5598
6120
  /* @__PURE__ */ jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
5599
6121
  /* @__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" }) })
6122
+ /* @__PURE__ */ jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
5601
6123
  ] }) }),
5602
6124
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsx(
5603
6125
  Badge,
5604
6126
  {
5605
- size: "lg",
5606
6127
  onClick: () => setRoutePriority(r),
5607
6128
  className: cn(
5608
- "cursor-pointer",
6129
+ "cursor-pointer h-7 rounded px-2",
5609
6130
  routePriority === r ? activeBtn : notActiveBtn
5610
6131
  ),
5611
6132
  children: t(`settings.routePresets.${r}`)
@@ -5630,7 +6151,7 @@ const TokenRow = ({
5630
6151
  Button,
5631
6152
  {
5632
6153
  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" : ""}`,
6154
+ className: `w-full px-5 rounded-xs bg-transparent flex items-center justify-between gap-3 hover:bg-accent hover:scale-100 ${isSelected ? "bg-accent hover:bg-accent/50" : ""}`,
5634
6155
  children: [
5635
6156
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
5636
6157
  /* @__PURE__ */ jsx(
@@ -5642,16 +6163,16 @@ const TokenRow = ({
5642
6163
  }
5643
6164
  ),
5644
6165
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-1", children: [
5645
- /* @__PURE__ */ jsx("p", { className: "font-semibold text-foreground text-lg leading-4 truncate flex items-center gap-1", children: symbol }),
5646
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground truncate", children: name })
6166
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-foreground text-sm leading-4 truncate flex items-center gap-1", children: symbol }),
6167
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground truncate", children: name })
5647
6168
  ] })
5648
6169
  ] }),
5649
6170
  /* @__PURE__ */ jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end gap-1", children: [
5650
6171
  /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-16 rounded-md" }),
5651
6172
  /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
5652
6173
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5653
- /* @__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) : "—" })
6174
+ /* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground text-sm leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
6175
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground/50", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
5655
6176
  ] }) })
5656
6177
  ]
5657
6178
  }
@@ -5794,8 +6315,8 @@ const TokenSelectModal = ({
5794
6315
  [groupedTokens.willChangeSrcChain]
5795
6316
  );
5796
6317
  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: [
5798
- /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectToken") }) }),
6318
+ 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 p-10", children: [
6319
+ /* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl leading-8", children: t("bridge.selectToken") }) }),
5799
6320
  /* @__PURE__ */ jsx(
5800
6321
  SearchInput,
5801
6322
  {
@@ -5808,24 +6329,29 @@ const TokenSelectModal = ({
5808
6329
  /* @__PURE__ */ jsx(
5809
6330
  Button,
5810
6331
  {
5811
- variant: tab === "my" ? "default" : "outline",
6332
+ variant: tab === "my" ? "default" : "ghost",
5812
6333
  onClick: () => setTab("my"),
5813
6334
  size: "sm",
6335
+ className: cn("px-4", tab !== "my" && "bg-muted hover:bg-accent"),
5814
6336
  children: t("bridge.myTokens")
5815
6337
  }
5816
6338
  ),
5817
6339
  /* @__PURE__ */ jsx(
5818
6340
  Button,
5819
6341
  {
5820
- variant: tab === "all" ? "default" : "outline",
6342
+ variant: tab === "all" ? "default" : "ghost",
5821
6343
  onClick: () => setTab("all"),
5822
6344
  size: "sm",
6345
+ className: cn(
6346
+ "px-4",
6347
+ tab !== "all" && "bg-muted hover:bg-accent"
6348
+ ),
5823
6349
  children: t("bridge.allTokens")
5824
6350
  }
5825
6351
  )
5826
6352
  ] }),
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") }),
6353
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto -mx-5", children: hasNoResults ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6354
+ effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 px-5 text-base text-muted-foreground uppercase py-2", children: t("bridge.noBalancesFound") }),
5829
6355
  tokensToRender.map(({ token, willChangeSrc }) => {
5830
6356
  const bal = getBalance(token.symbol);
5831
6357
  const usd = getTokenUsdValue(token.symbol, token.price?.usd);
@@ -5846,7 +6372,7 @@ const TokenSelectModal = ({
5846
6372
  );
5847
6373
  }),
5848
6374
  effectiveTab === "all" && willChangeSrcTokens.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
5849
- /* @__PURE__ */ jsx("div", { className: "px-5 flex leading-4 text-base py-2 font-semibold text-muted-foreground uppercase mt-8", children: /* @__PURE__ */ jsx("p", { children: t("bridge.willChangeSourceChain") }) }),
6375
+ /* @__PURE__ */ jsx("div", { className: "px-5 flex leading-4 text-base py-2 text-muted-foreground uppercase mt-8", children: /* @__PURE__ */ jsx("p", { children: t("bridge.willChangeSourceChain") }) }),
5850
6376
  willChangeSrcTokens.map(({ token, willChangeSrc }) => {
5851
6377
  const bal = getBalance(token.symbol);
5852
6378
  const usd = getTokenUsdValue(
@@ -5876,6 +6402,7 @@ const TokenSelectModal = ({
5876
6402
  function useBridgeRefresh() {
5877
6403
  const qc = useQueryClient();
5878
6404
  const { srcAddress, dstAddress } = useAddresses();
6405
+ const { fromChain, toChain } = useChainsStore();
5879
6406
  const { setLoading: setQLoading, triggerRefetch } = useBridgeQuoteStore();
5880
6407
  const { hasAnyWallet } = useConnectedWalletsStore();
5881
6408
  const [spinning, setSpinning] = useState(false);
@@ -5888,14 +6415,30 @@ function useBridgeRefresh() {
5888
6415
  qc.invalidateQueries({ queryKey: ["chains"] })
5889
6416
  ];
5890
6417
  if (hasAnyWallet()) {
5891
- if (srcAddress)
6418
+ if (srcAddress) {
5892
6419
  queries.push(
5893
6420
  qc.invalidateQueries({ queryKey: ["srcTokens", srcAddress] })
5894
6421
  );
5895
- if (dstAddress)
6422
+ if (fromChain?.chainKey) {
6423
+ queries.push(
6424
+ qc.invalidateQueries({
6425
+ queryKey: ["balances", fromChain.chainKey, srcAddress]
6426
+ })
6427
+ );
6428
+ }
6429
+ }
6430
+ if (dstAddress) {
5896
6431
  queries.push(
5897
6432
  qc.invalidateQueries({ queryKey: ["dstTokens", dstAddress] })
5898
6433
  );
6434
+ if (toChain?.chainKey) {
6435
+ queries.push(
6436
+ qc.invalidateQueries({
6437
+ queryKey: ["balances", toChain.chainKey, dstAddress]
6438
+ })
6439
+ );
6440
+ }
6441
+ }
5899
6442
  }
5900
6443
  await Promise.all(queries);
5901
6444
  triggerRefetch();
@@ -5906,6 +6449,8 @@ function useBridgeRefresh() {
5906
6449
  qc,
5907
6450
  srcAddress,
5908
6451
  dstAddress,
6452
+ fromChain?.chainKey,
6453
+ toChain?.chainKey,
5909
6454
  hasAnyWallet,
5910
6455
  setQLoading,
5911
6456
  triggerRefetch
@@ -5925,7 +6470,7 @@ const RefreshButton = () => {
5925
6470
  onClick: handleRefresh,
5926
6471
  disabled: spinning,
5927
6472
  variant: "secondary",
5928
- className: "bg-card text-card-foreground hover:text-card-foreground h-9",
6473
+ className: "h-9 w-11",
5929
6474
  size: "sm",
5930
6475
  children: /* @__PURE__ */ jsx(
5931
6476
  ReloadIcon,
@@ -5953,7 +6498,7 @@ const SelectTokenButton = ({
5953
6498
  onClick,
5954
6499
  size: "sm",
5955
6500
  variant: "secondary",
5956
- className: "shrink-0 gap-2 bg-card text-card-foreground hover:text-card-foreground h-9",
6501
+ className: "shrink-0 gap-2 h-9 !pl-2",
5957
6502
  type: "button",
5958
6503
  "aria-label": label,
5959
6504
  children: [
@@ -6001,7 +6546,7 @@ const Toolbar = () => {
6001
6546
  {
6002
6547
  onClick: onOpenSettings,
6003
6548
  size: "sm",
6004
- className: "bg-card text-card-foreground hover:text-card-foreground h-9",
6549
+ className: "h-9 w-11",
6005
6550
  variant: "secondary",
6006
6551
  children: /* @__PURE__ */ jsx(BoltIcon, { stroke: "currentColor" })
6007
6552
  }
@@ -6282,11 +6827,16 @@ export {
6282
6827
  EvaaBridge,
6283
6828
  RoutePriority,
6284
6829
  RouteType,
6830
+ addEvmNetworkFee,
6285
6831
  addTonNetworkFee,
6832
+ addTronNetworkFee,
6286
6833
  addrForApi,
6287
6834
  buildAssetMatrix,
6288
6835
  calculateMinReceived,
6289
6836
  computeFeesUsdFromArray,
6837
+ estimateEvmNetworkFee,
6838
+ estimateTonNetworkFee,
6839
+ estimateTronNetworkFee,
6290
6840
  findNativeMeta,
6291
6841
  formatAddress,
6292
6842
  formatBalance,