@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.
- package/LICENSE +2 -0
- package/README.md +103 -138
- package/dist/evaa-bridge.cjs +1419 -869
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +1421 -871
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +58 -4
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/assets/fonts/Gilroy-Black.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Bold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-ExtraBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Heavy.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Light.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Medium.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Regular.ttf +0 -0
- package/dist/assets/fonts/Gilroy-SemiBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Thin.ttf +0 -0
- package/dist/assets/fonts/Gilroy-UltraLight.ttf +0 -0
- package/dist/assets/fonts/fonts.css +0 -81
- package/dist/assets/fonts/hanson-bold.woff +0 -0
- package/dist/assets/fonts/hanson-bold.woff2 +0 -0
package/dist/evaa-bridge.mjs
CHANGED
|
@@ -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,
|
|
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
|
|
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", "
|
|
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
|
|
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", "
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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) => [
|
|
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 =
|
|
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 =
|
|
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(
|
|
227
|
-
const other = base.filter((e) => !used.has(
|
|
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
|
-
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
-
|
|
772
|
-
return Array.isArray(data) ? data : data.chains ?? [];
|
|
860
|
+
return context;
|
|
773
861
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
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
|
-
|
|
782
|
-
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
-
|
|
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: "
|
|
2008
|
+
fill: "currentColor"
|
|
1315
2009
|
}
|
|
1316
2010
|
)
|
|
1317
2011
|
] }),
|
|
@@ -1447,7 +2141,7 @@ const WalletConnectIcon = (props) => {
|
|
|
1447
2141
|
}
|
|
1448
2142
|
);
|
|
1449
2143
|
};
|
|
1450
|
-
const
|
|
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
|
-
"
|
|
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
|
|
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
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
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 = {
|
|
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)
|
|
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
|
-
}, [
|
|
1821
|
-
|
|
1822
|
-
|
|
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
|
|
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:
|
|
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.
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
2910
|
+
"textarea",
|
|
2197
2911
|
{
|
|
2198
2912
|
className: cn(
|
|
2199
|
-
"
|
|
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
|
-
|
|
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-
|
|
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
|
|
2243
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
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
|
-
|
|
2462
|
-
|
|
2463
|
-
}
|
|
2464
|
-
return {
|
|
2465
|
-
|
|
2466
|
-
|
|
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
|
|
2470
|
-
const
|
|
2471
|
-
const
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
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
|
-
|
|
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
|
|
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("
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
|
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
|
-
//
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
3334
|
-
icon:
|
|
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.
|
|
3373
|
-
icon:
|
|
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-
|
|
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
|
|
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?.
|
|
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.
|
|
4187
|
+
metadata.actualFeeValue,
|
|
3628
4188
|
" ",
|
|
3629
|
-
metadata.
|
|
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
|
-
|
|
4305
|
+
if (!fromChain) {
|
|
4306
|
+
setFromChain(query.data[0]);
|
|
4307
|
+
}
|
|
3742
4308
|
}
|
|
3743
|
-
}, [query.isSuccess, query.data
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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-
|
|
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-
|
|
5646
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs leading-3
|
|
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-
|
|
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" : "
|
|
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" : "
|
|
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-
|
|
5828
|
-
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base
|
|
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
|
|
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
|
-
|
|
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: "
|
|
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
|
|
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: "
|
|
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,
|