@rash2x/bridge-widget 0.2.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -0
- package/README.md +126 -157
- package/dist/evaa-bridge.cjs +1402 -673
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +1406 -677
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +63 -3
- 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,35 +2,36 @@ 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
|
-
import { Address, beginCell as beginCell$1, storeMessage
|
|
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";
|
|
17
18
|
import { Input } from "@/components/ui/input";
|
|
18
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
|
19
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog";
|
|
19
20
|
import { Switch } from "@/components/ui/switch";
|
|
20
21
|
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
25
|
import { toast, Toaster } from "sonner";
|
|
26
|
+
import { DialogDescription as DialogDescription$1 } from "@radix-ui/react-dialog";
|
|
25
27
|
import { BrowserProvider, Contract, parseUnits } from "ethers";
|
|
26
28
|
import { isAddress, formatUnits } from "viem";
|
|
27
|
-
import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
|
|
28
29
|
import { TronLinkAdapterName } from "@tronweb3/tronwallet-adapters";
|
|
29
30
|
import { CardHeader, CardTitle, CardAction, Card, CardContent, CardFooter } from "@/components/ui/card";
|
|
30
31
|
import { Badge } from "@/components/ui/badge";
|
|
31
32
|
const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
|
|
32
|
-
const wallets$1 = { "addTonWallet": "Add TON wallet", "addEvmWallet": "Add EVM wallet", "connectTonWallet": "Connect TON wallet", "connectEvmWallet": "Connect EVM wallet", "initializingMetamask": "Initializing MetaMask SDK...", "initializingTronlink": "Initializing TronLink...", "failedToConnectTon": "Failed to connect to TON wallet", "failedToDisconnect": "Failed to disconnect", "metamaskConnectionError": "MetaMask connection error", "failedToConnectMetamask": "Failed to connect to MetaMask", "failedToDisconnectMetamask": "Failed to disconnect from MetaMask", "selectWallet": "Select Wallet", "tonWallets": "TON", "evmWallets": "EVM", "tronWallets": "TRON", "
|
|
33
|
-
const bridge$1 = { "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will
|
|
33
|
+
const wallets$1 = { "addTonWallet": "Add TON wallet", "addEvmWallet": "Add EVM wallet", "connectTonWallet": "Connect TON wallet", "connectEvmWallet": "Connect EVM wallet", "initializingMetamask": "Initializing MetaMask SDK...", "initializingTronlink": "Initializing TronLink...", "failedToConnectTon": "Failed to connect to TON wallet", "failedToDisconnect": "Failed to disconnect", "metamaskConnectionError": "MetaMask connection error", "failedToConnectMetamask": "Failed to connect to MetaMask", "failedToDisconnectMetamask": "Failed to disconnect from MetaMask", "selectWallet": "Select Wallet", "tonWallets": "TON", "evmWallets": "EVM", "tronWallets": "TRON", "tonconnect": "TonConnect", "metaMask": "WalletConnect", "tronLink": "TronLink", "addTronWallet": "Add Tron wallet", "comingSoon": "Coming Soon", "connected": "CONNECTED", "disconnect": "Disconnect", "chooseWallet": "Choose wallet", "oneWalletPerEnv": "You can only connect one wallet per environment.", "connect": "Connect", "connectTronWallet": "Connect Tron wallet", "connectWallet": "Connect wallet" };
|
|
34
|
+
const bridge$1 = { "max": "Max", "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will change source network", "willChangeSourceNetworkAndToken": "Will change source network and token", "noBalancesFound": "No balances found.", "noResults": "No results", "sendToAnotherAddress": "Send to another address", "youWillReceive": "You will receive", "anotherAddressPlaceholder": "Address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
|
|
34
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" };
|
|
35
36
|
const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
|
|
36
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" } };
|
|
@@ -45,8 +46,8 @@ const en = {
|
|
|
45
46
|
errors: errors$1
|
|
46
47
|
};
|
|
47
48
|
const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
|
|
48
|
-
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", "
|
|
49
|
-
const bridge = { "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": "Проверьте корректность перед переводом" };
|
|
50
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": "Ошибка котировки" };
|
|
51
52
|
const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
|
|
52
53
|
const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
|
|
@@ -97,72 +98,282 @@ function BridgeI18nProvider({
|
|
|
97
98
|
function useBridgeTranslation() {
|
|
98
99
|
return useTranslation("evaa-bridge", { i18n: bridgeI18n });
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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);
|
|
115
120
|
}
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!prev || !data) return false;
|
|
136
|
-
if (prev.length !== data.length) return false;
|
|
137
|
-
for (let i = 0; i < prev.length; i++) {
|
|
138
|
-
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;
|
|
139
140
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (!prev && !data) return true;
|
|
149
|
-
if (!prev || !data) return false;
|
|
150
|
-
if (prev.length !== data.length) return false;
|
|
151
|
-
for (let i = 0; i < prev.length; i++) {
|
|
152
|
-
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);
|
|
153
149
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|
|
166
377
|
const POPULAR_ORDER = [
|
|
167
378
|
"USDT",
|
|
168
379
|
"USDC",
|
|
@@ -198,6 +409,16 @@ function buildAssetMatrix(tokens) {
|
|
|
198
409
|
if (!symbol) continue;
|
|
199
410
|
(m[symbol] || (m[symbol] = {}))[t.chainKey] = t;
|
|
200
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
|
+
);
|
|
201
422
|
return m;
|
|
202
423
|
}
|
|
203
424
|
function listAssetsForSelect(tokens) {
|
|
@@ -207,11 +428,11 @@ function listAssetsForSelect(tokens) {
|
|
|
207
428
|
const preferred = pickOrder.map((k) => byChain[k]).find(Boolean) ?? Object.values(byChain)[0];
|
|
208
429
|
return preferred;
|
|
209
430
|
});
|
|
210
|
-
const byNorm = new Map(base.map((e) => [
|
|
431
|
+
const byNorm = new Map(base.map((e) => [normalizeTickerSymbol$1(e.symbol), e]));
|
|
211
432
|
const used = /* @__PURE__ */ new Set();
|
|
212
433
|
const popular = [];
|
|
213
434
|
for (const s of POPULAR_ORDER) {
|
|
214
|
-
const key =
|
|
435
|
+
const key = normalizeTickerSymbol$1(s);
|
|
215
436
|
const item = byNorm.get(key);
|
|
216
437
|
if (item && !used.has(key)) {
|
|
217
438
|
popular.push(item);
|
|
@@ -219,11 +440,11 @@ function listAssetsForSelect(tokens) {
|
|
|
219
440
|
}
|
|
220
441
|
}
|
|
221
442
|
const stable = base.filter((e) => {
|
|
222
|
-
const k =
|
|
443
|
+
const k = normalizeTickerSymbol$1(e.symbol);
|
|
223
444
|
return !used.has(k) && STABLES.has(k);
|
|
224
445
|
}).sort((a, b) => a.symbol.localeCompare(b.symbol));
|
|
225
|
-
for (const e of stable) used.add(
|
|
226
|
-
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));
|
|
227
448
|
return [...popular, ...stable, ...other];
|
|
228
449
|
}
|
|
229
450
|
function resolveTokenOnChain(tokens, assetSymbol, chainKey) {
|
|
@@ -628,9 +849,398 @@ function isAddressValidForChain(chainKey, addr) {
|
|
|
628
849
|
if (chainKey === "tron") return isTronAddress(addr);
|
|
629
850
|
return isEvmAddress(addr);
|
|
630
851
|
}
|
|
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
|
+
);
|
|
859
|
+
}
|
|
860
|
+
return context;
|
|
861
|
+
}
|
|
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}`;
|
|
872
|
+
}
|
|
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
|
+
}
|
|
631
1241
|
function useBridgeQuote() {
|
|
632
1242
|
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
633
|
-
const { fromChain, toChain } = useChainsStore();
|
|
1243
|
+
const { fromChain, toChain, chains } = useChainsStore();
|
|
634
1244
|
const { srcAddress, dstAddress } = useAddresses();
|
|
635
1245
|
const { slippageBps, routePriority, getDstNativeAmount, getSlippageDecimal } = useSettingsStore();
|
|
636
1246
|
const {
|
|
@@ -642,6 +1252,8 @@ function useBridgeQuote() {
|
|
|
642
1252
|
setNoRoute,
|
|
643
1253
|
resetWithIdle
|
|
644
1254
|
} = useBridgeQuoteStore();
|
|
1255
|
+
const { chainRegistry } = useChainStrategies();
|
|
1256
|
+
const publicClient = usePublicClient();
|
|
645
1257
|
const input = inputAmount;
|
|
646
1258
|
const srcTokenOnFrom = useMemo(
|
|
647
1259
|
() => resolveTokenOnChainFromMatrix$2(
|
|
@@ -730,7 +1342,85 @@ function useBridgeQuote() {
|
|
|
730
1342
|
return;
|
|
731
1343
|
}
|
|
732
1344
|
if (cancelled) return;
|
|
733
|
-
|
|
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);
|
|
734
1424
|
} catch {
|
|
735
1425
|
if (!cancelled) {
|
|
736
1426
|
resetUi();
|
|
@@ -756,7 +1446,10 @@ function useBridgeQuote() {
|
|
|
756
1446
|
setQError,
|
|
757
1447
|
slippageBps,
|
|
758
1448
|
routePriority,
|
|
759
|
-
refetchTrigger
|
|
1449
|
+
refetchTrigger,
|
|
1450
|
+
chainRegistry,
|
|
1451
|
+
chains,
|
|
1452
|
+
publicClient
|
|
760
1453
|
]);
|
|
761
1454
|
return { loading };
|
|
762
1455
|
}
|
|
@@ -778,7 +1471,8 @@ async function getTokens() {
|
|
|
778
1471
|
throw new Error(`Failed to load chains: ${res.status}`);
|
|
779
1472
|
}
|
|
780
1473
|
const data = await res.json();
|
|
781
|
-
|
|
1474
|
+
const tokens = Array.isArray(data) ? data : data.tokens ?? [];
|
|
1475
|
+
return tokens.map(normalizeTokenSymbol);
|
|
782
1476
|
}
|
|
783
1477
|
async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
784
1478
|
const url = new URL("https://stargate.finance/api/v1/tokens");
|
|
@@ -805,10 +1499,7 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
|
805
1499
|
if (sa === sb) return (a.name ?? "").localeCompare(b.name ?? "");
|
|
806
1500
|
return sa.localeCompare(sb);
|
|
807
1501
|
});
|
|
808
|
-
return unique;
|
|
809
|
-
}
|
|
810
|
-
function normalizeTickerSymbol$1(s) {
|
|
811
|
-
return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
1502
|
+
return unique.map(normalizeTokenSymbol);
|
|
812
1503
|
}
|
|
813
1504
|
function resolveTokenOnChainFromMatrix$1(assetMatrix, assetSymbol, chainKey) {
|
|
814
1505
|
if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
|
|
@@ -835,7 +1526,14 @@ const useChainDerivations = () => {
|
|
|
835
1526
|
return [];
|
|
836
1527
|
const byChain = assetMatrix[selectedAssetSymbol.toUpperCase()];
|
|
837
1528
|
const keys = new Set(Object.keys(byChain ?? {}));
|
|
838
|
-
|
|
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;
|
|
839
1537
|
}, [selectedAssetSymbol, assetMatrix, chains]);
|
|
840
1538
|
useEffect(() => {
|
|
841
1539
|
setAllowedFromChains(supportedFrom);
|
|
@@ -903,6 +1601,13 @@ const useChainDerivations = () => {
|
|
|
903
1601
|
if (fromChain?.chainKey) {
|
|
904
1602
|
list = list.filter((c) => c.chainKey !== fromChain.chainKey);
|
|
905
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
|
+
);
|
|
906
1611
|
setAllowedToChains(list);
|
|
907
1612
|
if (!toChain || !list.some((c) => c.chainKey === toChain.chainKey)) {
|
|
908
1613
|
setToChain(list[0]);
|
|
@@ -937,16 +1642,6 @@ const useChainDerivations = () => {
|
|
|
937
1642
|
isLoadingToChains
|
|
938
1643
|
};
|
|
939
1644
|
};
|
|
940
|
-
const ChainStrategyContext = createContext(void 0);
|
|
941
|
-
function useChainStrategies() {
|
|
942
|
-
const context = useContext(ChainStrategyContext);
|
|
943
|
-
if (!context) {
|
|
944
|
-
throw new Error(
|
|
945
|
-
"useChainStrategies must be used within ChainStrategyProvider"
|
|
946
|
-
);
|
|
947
|
-
}
|
|
948
|
-
return context;
|
|
949
|
-
}
|
|
950
1645
|
function useBalances(chainKey, address, priorityTokenSymbol) {
|
|
951
1646
|
const { chainRegistry } = useChainStrategies();
|
|
952
1647
|
const { assetMatrix } = useTokensStore();
|
|
@@ -1310,7 +2005,7 @@ const StargateIcon = (props) => {
|
|
|
1310
2005
|
"path",
|
|
1311
2006
|
{
|
|
1312
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",
|
|
1313
|
-
fill: "
|
|
2008
|
+
fill: "currentColor"
|
|
1314
2009
|
}
|
|
1315
2010
|
)
|
|
1316
2011
|
] }),
|
|
@@ -1446,7 +2141,7 @@ const WalletConnectIcon = (props) => {
|
|
|
1446
2141
|
}
|
|
1447
2142
|
);
|
|
1448
2143
|
};
|
|
1449
|
-
const
|
|
2144
|
+
const TonConnectIcon = (props) => {
|
|
1450
2145
|
return /* @__PURE__ */ jsxs(
|
|
1451
2146
|
"svg",
|
|
1452
2147
|
{
|
|
@@ -1584,18 +2279,30 @@ const SwapButton = () => {
|
|
|
1584
2279
|
) });
|
|
1585
2280
|
};
|
|
1586
2281
|
const WalletBalance = (props) => {
|
|
1587
|
-
const { value, isLoading = false } = props;
|
|
2282
|
+
const { value, isLoading = false, showMax = false, onMaxClick } = props;
|
|
2283
|
+
const { t } = useBridgeTranslation();
|
|
1588
2284
|
const hasNoData = !value || value === "0" || value === "0.00" || value === "0.0";
|
|
1589
2285
|
const shouldShowSkeleton = isLoading && hasNoData;
|
|
2286
|
+
const shouldShowMaxButton = showMax && !hasNoData && !isLoading;
|
|
1590
2287
|
if (shouldShowSkeleton) {
|
|
1591
2288
|
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
1592
2289
|
/* @__PURE__ */ jsx(WalletIcon, { className: "text-muted-foreground" }),
|
|
1593
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-
|
|
2290
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-12 rounded-md" })
|
|
1594
2291
|
] });
|
|
1595
2292
|
}
|
|
1596
2293
|
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
1597
2294
|
/* @__PURE__ */ jsx(WalletIcon, { className: "text-muted-foreground" }),
|
|
1598
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: value })
|
|
2295
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: value }),
|
|
2296
|
+
shouldShowMaxButton && /* @__PURE__ */ jsx(
|
|
2297
|
+
Button,
|
|
2298
|
+
{
|
|
2299
|
+
variant: "ghost",
|
|
2300
|
+
size: "sm",
|
|
2301
|
+
onClick: onMaxClick,
|
|
2302
|
+
className: "h-auto p-0 px-0 text-xs font-medium border-b border-foreground rounded-none",
|
|
2303
|
+
children: t("bridge.max")
|
|
2304
|
+
}
|
|
2305
|
+
)
|
|
1599
2306
|
] });
|
|
1600
2307
|
};
|
|
1601
2308
|
const BASE_URL$1 = "https://icons-ckg.pages.dev/stargate-light/networks";
|
|
@@ -1624,7 +2331,7 @@ const SelectNetworkButton = ({
|
|
|
1624
2331
|
size: "sm",
|
|
1625
2332
|
variant: "secondary",
|
|
1626
2333
|
type: "button",
|
|
1627
|
-
className: "shrink-0 gap-2
|
|
2334
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
1628
2335
|
"aria-label": label,
|
|
1629
2336
|
children: [
|
|
1630
2337
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
@@ -1715,49 +2422,37 @@ const SearchInput = ({
|
|
|
1715
2422
|
placeholder,
|
|
1716
2423
|
value,
|
|
1717
2424
|
onChange,
|
|
1718
|
-
className
|
|
1719
|
-
containerClassName
|
|
2425
|
+
className
|
|
1720
2426
|
}) => {
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
className: cn(
|
|
1737
|
-
"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",
|
|
1738
|
-
className
|
|
1739
|
-
),
|
|
1740
|
-
value,
|
|
1741
|
-
onChange: (e) => onChange(e.target.value),
|
|
1742
|
-
onFocus: () => setIsFocused(true),
|
|
1743
|
-
onBlur: () => setIsFocused(false)
|
|
1744
|
-
}
|
|
1745
|
-
)
|
|
1746
|
-
]
|
|
1747
|
-
}
|
|
1748
|
-
);
|
|
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
|
+
] });
|
|
1749
2442
|
};
|
|
1750
2443
|
const ChainSelectModal = ({
|
|
1751
2444
|
isOpen,
|
|
1752
2445
|
onClose,
|
|
1753
2446
|
items,
|
|
1754
2447
|
allowedItems,
|
|
1755
|
-
onChangeChain
|
|
2448
|
+
onChangeChain,
|
|
2449
|
+
isSource
|
|
1756
2450
|
}) => {
|
|
1757
2451
|
const { t } = useBridgeTranslation();
|
|
1758
2452
|
const [query, setQuery] = useState("");
|
|
1759
2453
|
const { setFromChain, chains, fromChain, toChain } = useChainsStore();
|
|
1760
|
-
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2454
|
+
const { assetMatrix, selectedAssetSymbol, setSelectedAssetSymbol } = useTokensStore();
|
|
2455
|
+
console.log("allowedItems", allowedItems);
|
|
1761
2456
|
const handleClose = useCallback(() => {
|
|
1762
2457
|
setQuery("");
|
|
1763
2458
|
onClose();
|
|
@@ -1786,40 +2481,84 @@ const ChainSelectModal = ({
|
|
|
1786
2481
|
},
|
|
1787
2482
|
[chains, selectedAssetSymbol, assetMatrix]
|
|
1788
2483
|
);
|
|
2484
|
+
const findCompatibleTokenAndSrcChain = useCallback(
|
|
2485
|
+
(dstChain) => {
|
|
2486
|
+
if (!chains || !assetMatrix) return void 0;
|
|
2487
|
+
const PRIORITY_CHAINS = [
|
|
2488
|
+
"ethereum",
|
|
2489
|
+
"arbitrum",
|
|
2490
|
+
"bsc",
|
|
2491
|
+
"polygon",
|
|
2492
|
+
"optimism",
|
|
2493
|
+
"base"
|
|
2494
|
+
];
|
|
2495
|
+
for (const tokenSymbol of Object.keys(assetMatrix)) {
|
|
2496
|
+
const assetByChain = assetMatrix[tokenSymbol];
|
|
2497
|
+
if (!assetByChain[dstChain.chainKey]) continue;
|
|
2498
|
+
const availableChains = chains.filter(
|
|
2499
|
+
(c) => assetByChain[c.chainKey] && c.chainKey !== dstChain.chainKey
|
|
2500
|
+
);
|
|
2501
|
+
if (availableChains.length === 0) continue;
|
|
2502
|
+
let sourceChain;
|
|
2503
|
+
for (const chainKey of PRIORITY_CHAINS) {
|
|
2504
|
+
sourceChain = availableChains.find((c) => c.chainKey === chainKey);
|
|
2505
|
+
if (sourceChain) break;
|
|
2506
|
+
}
|
|
2507
|
+
if (!sourceChain) sourceChain = availableChains[0];
|
|
2508
|
+
return { tokenSymbol, sourceChain };
|
|
2509
|
+
}
|
|
2510
|
+
return void 0;
|
|
2511
|
+
},
|
|
2512
|
+
[chains, assetMatrix]
|
|
2513
|
+
);
|
|
1789
2514
|
const groupedChains = useMemo(() => {
|
|
1790
2515
|
const q = query.trim().toLowerCase();
|
|
1791
2516
|
const filtered = q ? (items ?? []).filter(
|
|
1792
2517
|
(c) => c.name.toLowerCase().includes(q) || c.chainKey.toLowerCase().includes(q)
|
|
1793
2518
|
) : items ?? [];
|
|
1794
|
-
const groups = { available: [], willChangeSrc: [] };
|
|
2519
|
+
const groups = { available: [], willChangeSrc: [], willChangeTokenAndSrc: [] };
|
|
1795
2520
|
for (const chain of filtered) {
|
|
1796
2521
|
const isAllowed = allowedItems?.some((c) => c.chainKey === chain.chainKey) || false;
|
|
1797
2522
|
if (isAllowed) {
|
|
1798
2523
|
groups.available.push(chain);
|
|
1799
2524
|
} else {
|
|
1800
2525
|
const compatibleSrc = findCompatibleSrcChain(chain);
|
|
1801
|
-
if (compatibleSrc)
|
|
2526
|
+
if (compatibleSrc) {
|
|
2527
|
+
groups.willChangeSrc.push(chain);
|
|
2528
|
+
} else {
|
|
2529
|
+
const compatibleTokenAndSrc = findCompatibleTokenAndSrcChain(chain);
|
|
2530
|
+
if (compatibleTokenAndSrc) {
|
|
2531
|
+
groups.willChangeTokenAndSrc.push(chain);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
1802
2534
|
}
|
|
1803
2535
|
}
|
|
1804
2536
|
groups.available.sort((a, b) => a.name.localeCompare(b.name));
|
|
1805
2537
|
groups.willChangeSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
2538
|
+
groups.willChangeTokenAndSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
1806
2539
|
return groups;
|
|
1807
|
-
}, [items, query, allowedItems, findCompatibleSrcChain]);
|
|
1808
|
-
const onChainPick = (chain, willChangeSrc = false) => {
|
|
1809
|
-
if (
|
|
2540
|
+
}, [items, query, allowedItems, findCompatibleSrcChain, findCompatibleTokenAndSrcChain]);
|
|
2541
|
+
const onChainPick = (chain, willChangeSrc = false, willChangeTokenAndSrc = false) => {
|
|
2542
|
+
if (willChangeTokenAndSrc) {
|
|
2543
|
+
const result = findCompatibleTokenAndSrcChain(chain);
|
|
2544
|
+
if (result) {
|
|
2545
|
+
setSelectedAssetSymbol(result.tokenSymbol);
|
|
2546
|
+
if (setFromChain) setFromChain(result.sourceChain);
|
|
2547
|
+
}
|
|
2548
|
+
} else if (willChangeSrc) {
|
|
1810
2549
|
const newSrcChain = findCompatibleSrcChain(chain);
|
|
1811
2550
|
if (newSrcChain && setFromChain) setFromChain(newSrcChain);
|
|
1812
2551
|
}
|
|
1813
2552
|
onChangeChain(chain);
|
|
1814
2553
|
handleClose();
|
|
1815
2554
|
};
|
|
1816
|
-
const renderChainItem = (chain, willChangeSrc) => {
|
|
1817
|
-
const isSelected = fromChain?.chainKey === chain.chainKey
|
|
2555
|
+
const renderChainItem = (chain, willChangeSrc, willChangeTokenAndSrc = false) => {
|
|
2556
|
+
const isSelected = isSource ? fromChain?.chainKey === chain.chainKey : toChain?.chainKey === chain.chainKey;
|
|
1818
2557
|
return /* @__PURE__ */ jsx(
|
|
1819
2558
|
Button,
|
|
1820
2559
|
{
|
|
1821
|
-
onClick: () => onChainPick(chain, willChangeSrc),
|
|
1822
|
-
className: `w-full cursor-pointer flex shadow-none rounded-
|
|
2560
|
+
onClick: () => onChainPick(chain, willChangeSrc, willChangeTokenAndSrc),
|
|
2561
|
+
className: `w-full cursor-pointer flex shadow-none rounded-xs items-center justify-between gap-3 px-5 py-4 h-13 font-semibold capitalize bg-transparent hover:scale-100 hover:bg-accent ${isSelected ? "border border-ring" : ""}`,
|
|
1823
2562
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1824
2563
|
/* @__PURE__ */ jsx(
|
|
1825
2564
|
NetworkSymbol,
|
|
@@ -1835,27 +2574,32 @@ const ChainSelectModal = ({
|
|
|
1835
2574
|
chain.chainKey
|
|
1836
2575
|
);
|
|
1837
2576
|
};
|
|
1838
|
-
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: [
|
|
1839
|
-
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectNetwork") }) }),
|
|
2577
|
+
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col", children: [
|
|
2578
|
+
/* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectNetwork") }) }),
|
|
1840
2579
|
/* @__PURE__ */ jsx(
|
|
1841
2580
|
SearchInput,
|
|
1842
2581
|
{
|
|
1843
2582
|
placeholder: t("bridge.searchDestinationChain"),
|
|
1844
2583
|
value: query,
|
|
1845
2584
|
onChange: setQuery,
|
|
1846
|
-
containerClassName: "rounded-md",
|
|
1847
2585
|
className: "text-foreground placeholder:text-muted-foreground"
|
|
1848
2586
|
}
|
|
1849
2587
|
),
|
|
1850
2588
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1851
2589
|
groupedChains.available.length > 0 && groupedChains.available.map((c) => renderChainItem(c, false)),
|
|
1852
|
-
groupedChains.
|
|
1853
|
-
|
|
1854
|
-
groupedChains.willChangeSrc.
|
|
1855
|
-
(c) => renderChainItem(c, true)
|
|
2590
|
+
groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2591
|
+
/* @__PURE__ */ jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceChain") }),
|
|
2592
|
+
groupedChains.willChangeSrc.map(
|
|
2593
|
+
(c) => renderChainItem(c, true, false)
|
|
1856
2594
|
)
|
|
1857
2595
|
] }),
|
|
1858
|
-
groupedChains.
|
|
2596
|
+
groupedChains.willChangeTokenAndSrc.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2597
|
+
/* @__PURE__ */ jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 || groupedChains.willChangeSrc.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceNetworkAndToken") }),
|
|
2598
|
+
groupedChains.willChangeTokenAndSrc.map(
|
|
2599
|
+
(c) => renderChainItem(c, false, true)
|
|
2600
|
+
)
|
|
2601
|
+
] }),
|
|
2602
|
+
groupedChains.available.length === 0 && groupedChains.willChangeSrc.length === 0 && groupedChains.willChangeTokenAndSrc.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-5 py-4 text-sm text-muted-foreground", children: t("bridge.noResults") })
|
|
1859
2603
|
] })
|
|
1860
2604
|
] }) });
|
|
1861
2605
|
};
|
|
@@ -1865,52 +2609,10 @@ const useWalletSelectModal = create((set) => ({
|
|
|
1865
2609
|
onOpen: (addressType) => set({ isOpen: true, addressType }),
|
|
1866
2610
|
onClose: () => set({ isOpen: false, addressType: void 0 })
|
|
1867
2611
|
}));
|
|
1868
|
-
const truncateToDecimals = (num, decimals) => {
|
|
1869
|
-
if (!isFinite(num) || isNaN(num)) return "0.00";
|
|
1870
|
-
const multiplier = Math.pow(10, decimals);
|
|
1871
|
-
const truncated = Math.floor(num * multiplier) / multiplier;
|
|
1872
|
-
return truncated.toFixed(decimals);
|
|
1873
|
-
};
|
|
1874
|
-
const formatTokenAmount = (amount, symbol, options) => {
|
|
1875
|
-
const normalizedSymbol = (symbol ?? "").toUpperCase();
|
|
1876
|
-
if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
|
|
1877
|
-
return `${Math.floor(amount)} ${normalizedSymbol}`;
|
|
1878
|
-
}
|
|
1879
|
-
if (options?.decimals !== void 0) {
|
|
1880
|
-
return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
|
|
1881
|
-
}
|
|
1882
|
-
if (amount >= 1) {
|
|
1883
|
-
return `${amount.toFixed(0)} ${normalizedSymbol}`;
|
|
1884
|
-
} else if (amount >= 1e-3) {
|
|
1885
|
-
return `${amount.toFixed(3)} ${normalizedSymbol}`;
|
|
1886
|
-
} else {
|
|
1887
|
-
return `${amount.toFixed(6)} ${normalizedSymbol}`;
|
|
1888
|
-
}
|
|
1889
|
-
};
|
|
1890
|
-
const formatUsd = (value) => {
|
|
1891
|
-
if (!value || !isFinite(value)) return "$0";
|
|
1892
|
-
if (value >= 1) return `$${value.toFixed(2)}`;
|
|
1893
|
-
return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
|
|
1894
|
-
};
|
|
1895
|
-
const formatPercentage = (bps, decimals = 2) => {
|
|
1896
|
-
return `${(bps / 100).toFixed(decimals)}%`;
|
|
1897
|
-
};
|
|
1898
|
-
const formatBalance = (amount, decimals = 2) => {
|
|
1899
|
-
if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
|
|
1900
|
-
return "0.00";
|
|
1901
|
-
}
|
|
1902
|
-
return amount.toFixed(decimals);
|
|
1903
|
-
};
|
|
1904
|
-
const formatHash = (hash, startChars = 4, endChars = 4) => {
|
|
1905
|
-
if (!hash) return "";
|
|
1906
|
-
if (hash.length <= startChars + endChars) return hash;
|
|
1907
|
-
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1908
|
-
};
|
|
1909
|
-
const formatAddress = formatHash;
|
|
1910
2612
|
const prefixIcons = {
|
|
1911
2613
|
tronlink: /* @__PURE__ */ jsx(TronLinkIcon, { className: "w-5 h-5" }),
|
|
1912
2614
|
metamask: /* @__PURE__ */ jsx(MetaMaskIcon, { className: "w-5 h-5" }),
|
|
1913
|
-
ton: /* @__PURE__ */ jsx(
|
|
2615
|
+
ton: /* @__PURE__ */ jsx(TonConnectIcon, { className: "w-5 h-5" })
|
|
1914
2616
|
};
|
|
1915
2617
|
const mapWalletToType = (wallet) => {
|
|
1916
2618
|
switch (wallet) {
|
|
@@ -1961,7 +2663,7 @@ const WalletInlineButton = ({
|
|
|
1961
2663
|
disabled: isButtonDisabled,
|
|
1962
2664
|
variant: "ghost",
|
|
1963
2665
|
size: "sm",
|
|
1964
|
-
className: "flex gap-1 cursor-pointer px-0 pr-1 h-5",
|
|
2666
|
+
className: "flex gap-1 cursor-pointer hover:opacity-60 hover:bg-transparent !px-0 pr-1 h-5",
|
|
1965
2667
|
children: [
|
|
1966
2668
|
/* @__PURE__ */ jsx("span", { children: isConnected ? prefixIcons[wallet] : null }),
|
|
1967
2669
|
/* @__PURE__ */ jsx("span", { className: "leading-3 text-sm border-b border-dotted border-link text-link", children: buttonText })
|
|
@@ -2026,6 +2728,11 @@ const SwapSection = ({
|
|
|
2026
2728
|
},
|
|
2027
2729
|
[onSelect, onClose]
|
|
2028
2730
|
);
|
|
2731
|
+
const handleMaxClick = useCallback(() => {
|
|
2732
|
+
if (balance.balance && onAmountChange) {
|
|
2733
|
+
onAmountChange(balance.balance.toString());
|
|
2734
|
+
}
|
|
2735
|
+
}, [balance.balance, onAmountChange]);
|
|
2029
2736
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2030
2737
|
/* @__PURE__ */ jsxs(
|
|
2031
2738
|
"div",
|
|
@@ -2042,7 +2749,9 @@ const SwapSection = ({
|
|
|
2042
2749
|
WalletBalance,
|
|
2043
2750
|
{
|
|
2044
2751
|
value: truncateToDecimals(balance.balance, 2),
|
|
2045
|
-
isLoading: balance.isLoading
|
|
2752
|
+
isLoading: balance.isLoading,
|
|
2753
|
+
showMax: isSource,
|
|
2754
|
+
onMaxClick: handleMaxClick
|
|
2046
2755
|
}
|
|
2047
2756
|
)
|
|
2048
2757
|
] }),
|
|
@@ -2090,7 +2799,8 @@ const SwapSection = ({
|
|
|
2090
2799
|
onClose,
|
|
2091
2800
|
items: chains,
|
|
2092
2801
|
allowedItems: allowedChains,
|
|
2093
|
-
onChangeChain
|
|
2802
|
+
onChangeChain,
|
|
2803
|
+
isSource
|
|
2094
2804
|
}
|
|
2095
2805
|
)
|
|
2096
2806
|
] });
|
|
@@ -2144,7 +2854,6 @@ const AnotherAddress = () => {
|
|
|
2144
2854
|
/* @__PURE__ */ jsx(
|
|
2145
2855
|
Switch,
|
|
2146
2856
|
{
|
|
2147
|
-
className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
|
|
2148
2857
|
"aria-pressed": enabled,
|
|
2149
2858
|
checked: enabled,
|
|
2150
2859
|
onClick: () => setEnabled((v) => !v)
|
|
@@ -2163,7 +2872,7 @@ const AnotherAddress = () => {
|
|
|
2163
2872
|
"div",
|
|
2164
2873
|
{
|
|
2165
2874
|
className: cn(
|
|
2166
|
-
"bg-input py-2
|
|
2875
|
+
"bg-input px-4 py-2 pr-2 mt-4 w-full flex items-center gap-3 rounded-xs justify-between border border-transparent transition-all",
|
|
2167
2876
|
{
|
|
2168
2877
|
"py-4": value,
|
|
2169
2878
|
"border border-ring": isFocused,
|
|
@@ -2173,13 +2882,13 @@ const AnotherAddress = () => {
|
|
|
2173
2882
|
children: [
|
|
2174
2883
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
|
|
2175
2884
|
/* @__PURE__ */ jsx(
|
|
2176
|
-
|
|
2885
|
+
"textarea",
|
|
2177
2886
|
{
|
|
2178
2887
|
className: cn(
|
|
2179
|
-
"
|
|
2888
|
+
"h-auto text-base leading-5 font-semibold w-full bg-transparent dark:bg-transparent placeholder:text-muted-foreground/50 border-none focus:outline-none focus:ring-0 resize-none transition-all"
|
|
2180
2889
|
),
|
|
2181
2890
|
placeholder: t("bridge.anotherAddressPlaceholder"),
|
|
2182
|
-
|
|
2891
|
+
rows: value.length >= 32 ? 2 : 1,
|
|
2183
2892
|
value,
|
|
2184
2893
|
onFocus: () => setIsFocused(true),
|
|
2185
2894
|
onBlur: () => setIsFocused(false),
|
|
@@ -2196,7 +2905,7 @@ const AnotherAddress = () => {
|
|
|
2196
2905
|
Button,
|
|
2197
2906
|
{
|
|
2198
2907
|
variant: "secondary",
|
|
2199
|
-
className: "bg-
|
|
2908
|
+
className: "bg-accent text-card-foreground uppercase text-xs",
|
|
2200
2909
|
size: "sm",
|
|
2201
2910
|
onClick: onPaste,
|
|
2202
2911
|
children: t("common.paste")
|
|
@@ -2219,8 +2928,8 @@ const AnotherAddress = () => {
|
|
|
2219
2928
|
) })
|
|
2220
2929
|
] });
|
|
2221
2930
|
};
|
|
2222
|
-
const
|
|
2223
|
-
return /* @__PURE__ */
|
|
2931
|
+
const InfoIcon = (props) => {
|
|
2932
|
+
return /* @__PURE__ */ jsx(
|
|
2224
2933
|
"svg",
|
|
2225
2934
|
{
|
|
2226
2935
|
width: "16",
|
|
@@ -2229,56 +2938,22 @@ const TipIcon = (props) => {
|
|
|
2229
2938
|
fill: "none",
|
|
2230
2939
|
xmlns: "http://www.w3.org/2000/svg",
|
|
2231
2940
|
...props,
|
|
2232
|
-
children:
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
width: "100%"
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
) }),
|
|
2245
|
-
/* @__PURE__ */ jsx(
|
|
2246
|
-
"circle",
|
|
2247
|
-
{
|
|
2248
|
-
"data-figma-bg-blur-radius": "4.4893",
|
|
2249
|
-
cx: "8",
|
|
2250
|
-
cy: "8",
|
|
2251
|
-
r: "8",
|
|
2252
|
-
fill: "currentColor",
|
|
2253
|
-
fillOpacity: "0.2"
|
|
2254
|
-
}
|
|
2255
|
-
),
|
|
2256
|
-
/* @__PURE__ */ jsx(
|
|
2257
|
-
"path",
|
|
2258
|
-
{
|
|
2259
|
-
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",
|
|
2260
|
-
stroke: "currentColor",
|
|
2261
|
-
strokeOpacity: "0.5",
|
|
2262
|
-
strokeWidth: "1.68349",
|
|
2263
|
-
strokeLinecap: "round"
|
|
2264
|
-
}
|
|
2265
|
-
),
|
|
2266
|
-
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
|
|
2267
|
-
"clipPath",
|
|
2268
|
-
{
|
|
2269
|
-
id: "bgblur_0_13066_11660_clip_path",
|
|
2270
|
-
transform: "translate(4.4893 4.4893)",
|
|
2271
|
-
children: /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "8" })
|
|
2272
|
-
}
|
|
2273
|
-
) })
|
|
2274
|
-
]
|
|
2941
|
+
children: /* @__PURE__ */ jsx(
|
|
2942
|
+
"path",
|
|
2943
|
+
{
|
|
2944
|
+
d: "M6.56836 5.8519C6.56836 5.06104 7.20948 4.41992 8.00034 4.41992C8.7912 4.41992 9.43232 5.06104 9.43232 5.8519C9.43232 6.13698 9.34902 6.40259 9.20543 6.62574C8.77748 7.29081 8.00034 7.92501 8.00034 8.71587V9.07386M8.00034 11.3159V11.5798",
|
|
2945
|
+
stroke: "currentColor",
|
|
2946
|
+
"stroke-width": "1.68349",
|
|
2947
|
+
"stroke-linecap": "round"
|
|
2948
|
+
}
|
|
2949
|
+
)
|
|
2275
2950
|
}
|
|
2276
2951
|
);
|
|
2277
2952
|
};
|
|
2278
2953
|
const Tip = (props) => {
|
|
2279
2954
|
const { children, text } = props;
|
|
2280
2955
|
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
2281
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { children }),
|
|
2956
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { className: "w-4 h-4 rounded-full bg-muted text-muted-foreground", children }),
|
|
2282
2957
|
/* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: text }) })
|
|
2283
2958
|
] });
|
|
2284
2959
|
};
|
|
@@ -2292,97 +2967,84 @@ const TokenSymbol = ({
|
|
|
2292
2967
|
const src = `${BASE_URL}/${normalizedSymbol}.svg`;
|
|
2293
2968
|
return /* @__PURE__ */ jsx("img", { src, alt: alt ?? symbol, className });
|
|
2294
2969
|
};
|
|
2295
|
-
function
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
outputHumanRounded: "0",
|
|
2301
|
-
minReceivedHuman: 0
|
|
2302
|
-
};
|
|
2303
|
-
}
|
|
2304
|
-
const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
|
|
2305
|
-
const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
|
|
2306
|
-
const outputHumanRounded = truncateToDecimals(outputHuman, 2);
|
|
2307
|
-
const minReceivedHuman = fromLD(
|
|
2308
|
-
quote.dstAmountMin || "0",
|
|
2309
|
-
dstToken.decimals
|
|
2310
|
-
);
|
|
2311
|
-
return {
|
|
2312
|
-
inputHuman,
|
|
2313
|
-
outputHuman,
|
|
2314
|
-
outputHumanRounded,
|
|
2315
|
-
minReceivedHuman
|
|
2316
|
-
};
|
|
2317
|
-
}
|
|
2318
|
-
function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
|
|
2319
|
-
if (!quote || !tokens || !chains) {
|
|
2320
|
-
return {
|
|
2321
|
-
totalUsd: 0,
|
|
2322
|
-
protocolFeeUsd: void 0,
|
|
2323
|
-
messageFeeUsd: void 0,
|
|
2324
|
-
serviceUsd: void 0,
|
|
2325
|
-
blockchainUsd: void 0,
|
|
2326
|
-
inSrcToken: void 0,
|
|
2327
|
-
inDstToken: void 0
|
|
2328
|
-
};
|
|
2329
|
-
}
|
|
2330
|
-
const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
|
|
2331
|
-
let inSrcToken = void 0;
|
|
2332
|
-
let inDstToken = void 0;
|
|
2333
|
-
if (srcToken && quote.srcChainKey) {
|
|
2334
|
-
const feeInSrcLD = sumFeeByTokenLD(
|
|
2335
|
-
quote.fees,
|
|
2336
|
-
srcToken.address,
|
|
2337
|
-
quote.srcChainKey
|
|
2338
|
-
);
|
|
2339
|
-
const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
|
|
2340
|
-
if (feeInSrcHuman > 0) {
|
|
2341
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
|
|
2342
|
-
} else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
|
|
2343
|
-
const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
|
|
2344
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
if (dstToken && quote.dstChainKey) {
|
|
2348
|
-
const feeInDstLD = sumFeeByTokenLD(
|
|
2349
|
-
quote.fees,
|
|
2350
|
-
dstToken.address,
|
|
2351
|
-
quote.dstChainKey
|
|
2352
|
-
);
|
|
2353
|
-
const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
|
|
2354
|
-
if (feeInDstHuman > 0) {
|
|
2355
|
-
inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
|
|
2356
|
-
}
|
|
2357
|
-
}
|
|
2358
|
-
return {
|
|
2359
|
-
totalUsd: feeData.totalUsd,
|
|
2360
|
-
protocolFeeUsd: feeData.protocolFeeUsd,
|
|
2361
|
-
messageFeeUsd: feeData.messageFeeUsd,
|
|
2362
|
-
serviceUsd: feeData.serviceUsd,
|
|
2363
|
-
blockchainUsd: feeData.blockchainUsd,
|
|
2364
|
-
inSrcToken,
|
|
2365
|
-
inDstToken
|
|
2366
|
-
};
|
|
2367
|
-
}
|
|
2368
|
-
function calculateMinReceived(quote, slippageBps, dstToken) {
|
|
2369
|
-
if (!quote || !dstToken) return 0;
|
|
2370
|
-
const dstAmountLD = BigInt(quote.dstAmount);
|
|
2371
|
-
const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
|
|
2372
|
-
return fromLD(minAmountLD.toString(), dstToken.decimals);
|
|
2970
|
+
function getSimpleFallback(chainKey) {
|
|
2971
|
+
const key = chainKey.toLowerCase();
|
|
2972
|
+
if (key === "ton") return 0.15;
|
|
2973
|
+
if (key === "tron") return 10;
|
|
2974
|
+
return 0.01;
|
|
2373
2975
|
}
|
|
2374
|
-
function
|
|
2375
|
-
const
|
|
2376
|
-
const
|
|
2377
|
-
const
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2976
|
+
function useGasEstimate(amountNum) {
|
|
2977
|
+
const { fromChain } = useChainsStore();
|
|
2978
|
+
const { selectedAssetSymbol } = useTokensStore();
|
|
2979
|
+
const { srcAddress } = useAddresses();
|
|
2980
|
+
const { balances, isLoading: balancesLoading } = useBalances(
|
|
2981
|
+
fromChain?.chainKey,
|
|
2982
|
+
srcAddress
|
|
2983
|
+
);
|
|
2984
|
+
const { quote } = useBridgeQuoteStore();
|
|
2985
|
+
const balancesKnown = !balancesLoading;
|
|
2986
|
+
const chainKey = fromChain?.chainKey;
|
|
2987
|
+
const nativeCurrencySymbol = fromChain?.nativeCurrency?.symbol;
|
|
2988
|
+
const nativeCurrencyAddress = fromChain?.nativeCurrency?.address;
|
|
2989
|
+
const nativeCurrencyDecimals = fromChain?.nativeCurrency?.decimals;
|
|
2990
|
+
const quoteFees = quote?.fees ? JSON.stringify(quote.fees) : null;
|
|
2991
|
+
const quoteSrcChainKey = quote?.srcChainKey;
|
|
2992
|
+
const nativeBalanceValue = nativeCurrencySymbol ? Number(balances[nativeCurrencySymbol.toUpperCase()]?.balance ?? 0) : 0;
|
|
2993
|
+
const result = useMemo(() => {
|
|
2994
|
+
if (!chainKey || !nativeCurrencySymbol) {
|
|
2995
|
+
return {
|
|
2996
|
+
nativeSym: "",
|
|
2997
|
+
nativeBalance: 0,
|
|
2998
|
+
requiredNative: 0,
|
|
2999
|
+
balancesKnown,
|
|
3000
|
+
isNativeSelected: false,
|
|
3001
|
+
hasEnoughGas: true
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
const nativeSym = nativeCurrencySymbol.toUpperCase();
|
|
3005
|
+
const nativeBalance = nativeBalanceValue;
|
|
3006
|
+
const isNativeSelected = nativeSym === (selectedAssetSymbol || "").toUpperCase();
|
|
3007
|
+
let requiredNative = 0;
|
|
3008
|
+
if (quoteFees && quoteSrcChainKey === chainKey) {
|
|
3009
|
+
const fees = JSON.parse(quoteFees);
|
|
3010
|
+
const feesInNative = fees.filter(
|
|
3011
|
+
(f) => f.chainKey === chainKey && f.token === nativeCurrencyAddress
|
|
3012
|
+
).reduce(
|
|
3013
|
+
(sum, f) => sum + BigInt(f.amount || "0"),
|
|
3014
|
+
0n
|
|
3015
|
+
);
|
|
3016
|
+
const decimals = nativeCurrencyDecimals || 18;
|
|
3017
|
+
requiredNative = Number(feesInNative) / Math.pow(10, decimals);
|
|
3018
|
+
} else {
|
|
3019
|
+
requiredNative = getSimpleFallback(chainKey);
|
|
3020
|
+
}
|
|
3021
|
+
let hasEnoughGas = true;
|
|
3022
|
+
if (isNativeSelected) {
|
|
3023
|
+
hasEnoughGas = nativeBalance - (amountNum ?? 0) >= requiredNative;
|
|
3024
|
+
} else {
|
|
3025
|
+
hasEnoughGas = nativeBalance >= requiredNative;
|
|
3026
|
+
}
|
|
3027
|
+
return {
|
|
3028
|
+
nativeSym,
|
|
3029
|
+
nativeBalance,
|
|
3030
|
+
requiredNative,
|
|
3031
|
+
balancesKnown,
|
|
3032
|
+
isNativeSelected,
|
|
3033
|
+
hasEnoughGas: balancesKnown ? hasEnoughGas : true
|
|
3034
|
+
};
|
|
3035
|
+
}, [
|
|
3036
|
+
chainKey,
|
|
3037
|
+
nativeCurrencySymbol,
|
|
3038
|
+
nativeCurrencyAddress,
|
|
3039
|
+
nativeCurrencyDecimals,
|
|
3040
|
+
selectedAssetSymbol,
|
|
3041
|
+
quoteFees,
|
|
3042
|
+
quoteSrcChainKey,
|
|
3043
|
+
amountNum,
|
|
3044
|
+
balancesKnown,
|
|
3045
|
+
nativeBalanceValue
|
|
3046
|
+
]);
|
|
3047
|
+
return result;
|
|
2386
3048
|
}
|
|
2387
3049
|
function getRouteDisplayName(route) {
|
|
2388
3050
|
if (!route) return "Stargate Bridge";
|
|
@@ -2399,6 +3061,7 @@ const Details = () => {
|
|
|
2399
3061
|
const { toChain, fromChain, chains } = useChainsStore();
|
|
2400
3062
|
const { quote, status } = useBridgeQuoteStore();
|
|
2401
3063
|
const { slippageBps, routePriority } = useSettingsStore();
|
|
3064
|
+
const gas = useGasEstimate();
|
|
2402
3065
|
const dstToken = resolveTokenOnChainFromMatrix$2(
|
|
2403
3066
|
assetMatrix,
|
|
2404
3067
|
selectedAssetSymbol,
|
|
@@ -2421,24 +3084,10 @@ const Details = () => {
|
|
|
2421
3084
|
const isLoading = status === "loading";
|
|
2422
3085
|
const receiveText = quoteDetails.outputAmount != null ? Number(quoteDetails.outputAmount).toFixed(6) : "0.00";
|
|
2423
3086
|
const etaText = quoteDetails.etaSeconds != null ? `≈ ${Math.max(1, Math.round(quoteDetails.etaSeconds / 60))}m` : "—";
|
|
2424
|
-
const
|
|
2425
|
-
const feeInDst = quoteDetails.fees?.inDstToken;
|
|
2426
|
-
const totalFeeUsd = quoteDetails.fees?.totalUsd;
|
|
2427
|
-
const totalFeeDisplay = (() => {
|
|
2428
|
-
if (feeInSrc != null && srcToken?.symbol) {
|
|
2429
|
-
return `${Number(feeInSrc).toFixed(6)} ${srcToken.symbol.toUpperCase()}`;
|
|
2430
|
-
}
|
|
2431
|
-
if (feeInDst != null && dstToken?.symbol) {
|
|
2432
|
-
return `${Number(feeInDst).toFixed(6)} ${dstToken.symbol.toUpperCase()}`;
|
|
2433
|
-
}
|
|
2434
|
-
if (totalFeeUsd != null) {
|
|
2435
|
-
return formatUsd(totalFeeUsd);
|
|
2436
|
-
}
|
|
2437
|
-
return "—";
|
|
2438
|
-
})();
|
|
3087
|
+
const totalFeeDisplay = gas.requiredNative > 0 ? `${gas.requiredNative.toFixed(6)} ${gas.nativeSym}` : "—";
|
|
2439
3088
|
const currentSlippageText = formatPercentage(slippageBps);
|
|
2440
3089
|
const routeText = quote?.route ? getRouteDisplayName(quote.route) : t(`settings.routePresets.${routePriority}`);
|
|
2441
|
-
return /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", className: "bg-muted rounded-sm", children: [
|
|
3090
|
+
return /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", className: "bg-muted/50 rounded-sm", children: [
|
|
2442
3091
|
/* @__PURE__ */ jsx(AccordionTrigger, { className: "w-full gap-1 items-center py-6 px-5 rounded-b-sm data-[state=open]:pb-3", children: /* @__PURE__ */ jsxs("div", { className: "w-full flex items-center justify-between", children: [
|
|
2443
3092
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-normal text-priority leading-4", children: t("bridge.youWillReceive", { defaultValue: "You will receive" }) }),
|
|
2444
3093
|
/* @__PURE__ */ jsxs("div", { className: "bg-transparent hover:bg-transparent shadow-none h-4 p-0 px-0 py-0 flex items-center gap-2", children: [
|
|
@@ -2455,7 +3104,7 @@ const Details = () => {
|
|
|
2455
3104
|
DetailsRow,
|
|
2456
3105
|
{
|
|
2457
3106
|
label: t("transaction.route"),
|
|
2458
|
-
value: /* @__PURE__ */ jsxs("
|
|
3107
|
+
value: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2459
3108
|
/* @__PURE__ */ jsx(StargateIcon, { className: "w-4 h-4" }),
|
|
2460
3109
|
/* @__PURE__ */ jsx("p", { className: "", children: routeText })
|
|
2461
3110
|
] })
|
|
@@ -2482,7 +3131,14 @@ const Details = () => {
|
|
|
2482
3131
|
label: t("transaction.totalFee"),
|
|
2483
3132
|
value: /* @__PURE__ */ jsxs("strong", { className: "inline-flex items-center gap-1", children: [
|
|
2484
3133
|
/* @__PURE__ */ jsx("span", { children: totalFeeDisplay }),
|
|
2485
|
-
/* @__PURE__ */ jsx(
|
|
3134
|
+
/* @__PURE__ */ jsx(
|
|
3135
|
+
TokenSymbol,
|
|
3136
|
+
{
|
|
3137
|
+
symbol: gas.nativeSym,
|
|
3138
|
+
className: "w-4 h-4",
|
|
3139
|
+
alt: "token"
|
|
3140
|
+
}
|
|
3141
|
+
)
|
|
2486
3142
|
] }),
|
|
2487
3143
|
isLoading
|
|
2488
3144
|
}
|
|
@@ -2497,7 +3153,7 @@ const DetailsRow = ({
|
|
|
2497
3153
|
}) => /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
2498
3154
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2499
3155
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-priority font-normal", children: label }),
|
|
2500
|
-
/* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(
|
|
3156
|
+
/* @__PURE__ */ jsx(Tip, { text: label, children: /* @__PURE__ */ jsx(InfoIcon, {}) })
|
|
2501
3157
|
] }),
|
|
2502
3158
|
isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-16 rounded-md" }) : /* @__PURE__ */ jsx("div", { className: "text-foreground text-sm", children: value ?? "—" })
|
|
2503
3159
|
] });
|
|
@@ -2550,6 +3206,20 @@ const useTransactionStore = create((set, get) => ({
|
|
|
2550
3206
|
};
|
|
2551
3207
|
set({ current: next });
|
|
2552
3208
|
},
|
|
3209
|
+
updateActualFee: (feeValue, feeSymbol) => {
|
|
3210
|
+
const cur = get().current;
|
|
3211
|
+
if (!cur) return;
|
|
3212
|
+
const next = {
|
|
3213
|
+
...cur,
|
|
3214
|
+
metadata: {
|
|
3215
|
+
...cur.metadata,
|
|
3216
|
+
actualFeeValue: feeValue,
|
|
3217
|
+
actualFeeSymbol: feeSymbol
|
|
3218
|
+
},
|
|
3219
|
+
updatedAt: Date.now()
|
|
3220
|
+
};
|
|
3221
|
+
set({ current: next });
|
|
3222
|
+
},
|
|
2553
3223
|
reset: () => {
|
|
2554
3224
|
set({ current: void 0 });
|
|
2555
3225
|
}
|
|
@@ -2695,9 +3365,13 @@ function isUserRejection(error) {
|
|
|
2695
3365
|
if (error instanceof TransactionRejectedError) {
|
|
2696
3366
|
return true;
|
|
2697
3367
|
}
|
|
3368
|
+
if (typeof error === "string") {
|
|
3369
|
+
const message = error.toLowerCase();
|
|
3370
|
+
return message.includes("rejected") || message.includes("denied") || message.includes("declined") || message.includes("not sent") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request") || message.includes("declined by user");
|
|
3371
|
+
}
|
|
2698
3372
|
if (error instanceof Error) {
|
|
2699
3373
|
const message = error.message.toLowerCase();
|
|
2700
|
-
return message.includes("rejected") || message.includes("denied") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request");
|
|
3374
|
+
return message.includes("rejected") || message.includes("denied") || message.includes("declined") || message.includes("not sent") || message.includes("user denied") || message.includes("user rejected") || message.includes("user reject") || message.includes("user cancelled") || message.includes("user canceled") || message.includes("action_rejected") || message.includes("ethers-user-denied") || message.includes("denied transaction") || message.includes("reject request") || message.includes("declined by user");
|
|
2701
3375
|
}
|
|
2702
3376
|
return false;
|
|
2703
3377
|
}
|
|
@@ -2716,17 +3390,13 @@ function toChainStrategyError(error, chainKey, context) {
|
|
|
2716
3390
|
error
|
|
2717
3391
|
);
|
|
2718
3392
|
}
|
|
2719
|
-
return new ChainStrategyError(
|
|
2720
|
-
String(error),
|
|
2721
|
-
"UNKNOWN_ERROR",
|
|
2722
|
-
chainKey
|
|
2723
|
-
);
|
|
3393
|
+
return new ChainStrategyError(String(error), "UNKNOWN_ERROR", chainKey);
|
|
2724
3394
|
}
|
|
2725
3395
|
function useBridgeTransaction() {
|
|
2726
3396
|
const { quote } = useBridgeQuoteStore();
|
|
2727
3397
|
const { chainRegistry } = useChainStrategies();
|
|
2728
3398
|
const { srcAddress, dstAddress } = useAddresses();
|
|
2729
|
-
const { assetMatrix,
|
|
3399
|
+
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2730
3400
|
const { chains } = useChainsStore();
|
|
2731
3401
|
const txStore = useTransactionStore();
|
|
2732
3402
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
@@ -2756,29 +3426,32 @@ function useBridgeTransaction() {
|
|
|
2756
3426
|
const srcChain = chains?.find((c) => c.chainKey === quote.srcChainKey);
|
|
2757
3427
|
const dstChain = chains?.find((c) => c.chainKey === quote.dstChainKey);
|
|
2758
3428
|
const amounts = getQuoteAmounts(quote, srcToken, dstToken);
|
|
2759
|
-
const fees = getQuoteFees(quote, tokens, chains, srcToken, dstToken);
|
|
2760
3429
|
const metadata = {
|
|
2761
3430
|
srcChainName: srcChain?.name || quote.srcChainKey,
|
|
2762
3431
|
dstChainName: dstChain?.name || quote.dstChainKey,
|
|
2763
3432
|
srcTokenSymbol: srcToken?.symbol || quote.srcToken,
|
|
2764
3433
|
dstTokenSymbol: dstToken?.symbol || quote.dstToken,
|
|
2765
3434
|
srcAmountHuman: amounts.inputHuman,
|
|
2766
|
-
dstAmountHuman: amounts.outputHuman
|
|
2767
|
-
//
|
|
2768
|
-
totalFeeValue: fees.inSrcToken ?? fees.inDstToken ?? void 0,
|
|
2769
|
-
totalFeeSymbol: fees.inSrcToken ? srcToken?.symbol : fees.inDstToken ? dstToken?.symbol : void 0
|
|
3435
|
+
dstAmountHuman: amounts.outputHuman
|
|
3436
|
+
// Actual fee will be updated when transaction completes
|
|
2770
3437
|
};
|
|
2771
3438
|
txStore.setTransaction(quote, "executing", metadata);
|
|
2772
3439
|
setIsProcessing(true);
|
|
2773
3440
|
try {
|
|
2774
3441
|
const steps = quote.steps || [];
|
|
2775
3442
|
if (steps.length === 0) {
|
|
2776
|
-
throw new InvalidStepsError(
|
|
3443
|
+
throw new InvalidStepsError(
|
|
3444
|
+
quote.srcChainKey,
|
|
3445
|
+
"No transaction steps found in quote"
|
|
3446
|
+
);
|
|
2777
3447
|
}
|
|
2778
3448
|
const chainKey = steps[0].chainKey;
|
|
2779
3449
|
const strategy = chainRegistry.getStrategy(chainKey);
|
|
2780
3450
|
if (!strategy) {
|
|
2781
|
-
throw new InvalidStepsError(
|
|
3451
|
+
throw new InvalidStepsError(
|
|
3452
|
+
chainKey,
|
|
3453
|
+
`No strategy available for chain: ${chainKey}`
|
|
3454
|
+
);
|
|
2782
3455
|
}
|
|
2783
3456
|
if (!strategy.isConnected()) {
|
|
2784
3457
|
throw new WalletNotConnectedError(chainKey);
|
|
@@ -2790,7 +3463,6 @@ function useBridgeTransaction() {
|
|
|
2790
3463
|
srcChainKey: quote.srcChainKey,
|
|
2791
3464
|
dstChainKey: quote.dstChainKey
|
|
2792
3465
|
};
|
|
2793
|
-
console.log(steps);
|
|
2794
3466
|
const txResult = await strategy.executeSteps(steps, context, (hash) => {
|
|
2795
3467
|
txStore.setSrcHash(hash);
|
|
2796
3468
|
txStore.updateStatus("processing");
|
|
@@ -2801,6 +3473,21 @@ function useBridgeTransaction() {
|
|
|
2801
3473
|
if (result.dstTxHash) {
|
|
2802
3474
|
txStore.setDstHash(result.dstTxHash);
|
|
2803
3475
|
}
|
|
3476
|
+
if (result.actualFeeValue) {
|
|
3477
|
+
let feeSymbol = result.actualFeeSymbol;
|
|
3478
|
+
if (!feeSymbol) {
|
|
3479
|
+
const srcChain2 = chains?.find(
|
|
3480
|
+
(c) => c.chainKey === quote.srcChainKey
|
|
3481
|
+
);
|
|
3482
|
+
feeSymbol = srcChain2?.nativeCurrency?.symbol || "";
|
|
3483
|
+
}
|
|
3484
|
+
if (feeSymbol) {
|
|
3485
|
+
const feeValue = parseFloat(result.actualFeeValue);
|
|
3486
|
+
if (!isNaN(feeValue)) {
|
|
3487
|
+
txStore.updateActualFee(feeValue, feeSymbol);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
2804
3491
|
txStore.updateStatus("completed");
|
|
2805
3492
|
console.log("Transaction completed successfully");
|
|
2806
3493
|
} else {
|
|
@@ -2818,19 +3505,26 @@ function useBridgeTransaction() {
|
|
|
2818
3505
|
console.error("Error tracking completion:", err);
|
|
2819
3506
|
});
|
|
2820
3507
|
} else {
|
|
2821
|
-
throw new TransactionFailedError(
|
|
3508
|
+
throw new TransactionFailedError(
|
|
3509
|
+
chainKey,
|
|
3510
|
+
"Transaction hash not received from wallet"
|
|
3511
|
+
);
|
|
2822
3512
|
}
|
|
2823
3513
|
return txResult;
|
|
2824
3514
|
} catch (err) {
|
|
2825
3515
|
if (isUserRejection(err)) {
|
|
2826
3516
|
txStore.setError("TRANSACTION_REJECTED");
|
|
2827
|
-
throw new TransactionFailedError(
|
|
3517
|
+
throw new TransactionFailedError(
|
|
3518
|
+
quote.srcChainKey,
|
|
3519
|
+
"Transaction rejected by user"
|
|
3520
|
+
);
|
|
2828
3521
|
}
|
|
2829
3522
|
if (ChainStrategyError.isChainStrategyError(err)) {
|
|
2830
3523
|
txStore.setError(err.code, { chainKey: err.chainKey });
|
|
2831
3524
|
console.error("Chain strategy error:", err.toJSON());
|
|
2832
3525
|
throw err;
|
|
2833
3526
|
}
|
|
3527
|
+
console.log(err);
|
|
2834
3528
|
txStore.setError("UNKNOWN_ERROR");
|
|
2835
3529
|
throw err;
|
|
2836
3530
|
} finally {
|
|
@@ -2843,66 +3537,6 @@ function useBridgeTransaction() {
|
|
|
2843
3537
|
hasQuote: !!quote
|
|
2844
3538
|
};
|
|
2845
3539
|
}
|
|
2846
|
-
function useGasEstimate(amountNum) {
|
|
2847
|
-
const { fromChain } = useChainsStore();
|
|
2848
|
-
const { selectedAssetSymbol, tokens } = useTokensStore();
|
|
2849
|
-
const getSourceGasReserveHuman = useSettingsStore(
|
|
2850
|
-
(state) => state.getSourceGasReserveHuman
|
|
2851
|
-
);
|
|
2852
|
-
const { srcAddress } = useAddresses();
|
|
2853
|
-
const { balances, isLoading: balancesLoading } = useBalances(
|
|
2854
|
-
fromChain?.chainKey,
|
|
2855
|
-
srcAddress
|
|
2856
|
-
);
|
|
2857
|
-
const { chainRegistry } = useChainStrategies();
|
|
2858
|
-
const balancesKnown = !balancesLoading;
|
|
2859
|
-
const strategy = useMemo(() => {
|
|
2860
|
-
if (!fromChain) return null;
|
|
2861
|
-
return chainRegistry.getStrategy(fromChain.chainKey);
|
|
2862
|
-
}, [fromChain, chainRegistry]);
|
|
2863
|
-
const { data: gasRequirement } = useQuery({
|
|
2864
|
-
queryKey: [
|
|
2865
|
-
"gas-estimate",
|
|
2866
|
-
fromChain?.chainKey,
|
|
2867
|
-
selectedAssetSymbol,
|
|
2868
|
-
amountNum,
|
|
2869
|
-
balances
|
|
2870
|
-
],
|
|
2871
|
-
queryFn: async () => {
|
|
2872
|
-
if (!fromChain || !strategy) {
|
|
2873
|
-
return null;
|
|
2874
|
-
}
|
|
2875
|
-
const selectedToken = tokens?.find(
|
|
2876
|
-
(t) => t.symbol.toUpperCase() === selectedAssetSymbol?.toUpperCase()
|
|
2877
|
-
) || null;
|
|
2878
|
-
const nativeTokenSymbol = fromChain.nativeCurrency?.symbol ?? "";
|
|
2879
|
-
const nativeDecimals = fromChain.nativeCurrency?.decimals || 18;
|
|
2880
|
-
const reserveFallback = getSourceGasReserveHuman(fromChain.chainKey);
|
|
2881
|
-
const result = await strategy.estimateGasRequirement({
|
|
2882
|
-
selectedToken,
|
|
2883
|
-
nativeTokenSymbol,
|
|
2884
|
-
amount: amountNum,
|
|
2885
|
-
balances,
|
|
2886
|
-
nativeDecimals,
|
|
2887
|
-
reserveFallback
|
|
2888
|
-
});
|
|
2889
|
-
return result;
|
|
2890
|
-
},
|
|
2891
|
-
enabled: !!fromChain && !!strategy,
|
|
2892
|
-
staleTime: 3e4,
|
|
2893
|
-
gcTime: 5 * 6e4,
|
|
2894
|
-
refetchOnWindowFocus: false,
|
|
2895
|
-
retry: 1
|
|
2896
|
-
});
|
|
2897
|
-
return {
|
|
2898
|
-
nativeSym: gasRequirement?.nativeSym || "",
|
|
2899
|
-
nativeBalance: gasRequirement?.nativeBalance || 0,
|
|
2900
|
-
requiredNative: gasRequirement?.requiredNative || 0,
|
|
2901
|
-
balancesKnown,
|
|
2902
|
-
isNativeSelected: gasRequirement?.isNativeSelected || false,
|
|
2903
|
-
hasEnoughGas: balancesKnown ? gasRequirement?.hasEnoughGas ?? true : true
|
|
2904
|
-
};
|
|
2905
|
-
}
|
|
2906
3540
|
function useBalanceCheck(amountNum, gas) {
|
|
2907
3541
|
const { fromChain } = useChainsStore();
|
|
2908
3542
|
const { selectedAssetSymbol } = useTokensStore();
|
|
@@ -3155,6 +3789,7 @@ const WalletModalButton = (props) => {
|
|
|
3155
3789
|
const { icon: IconComponent, name, onClose } = props;
|
|
3156
3790
|
const { chainRegistry } = useChainStrategies();
|
|
3157
3791
|
const { connect, isPending } = useConnect();
|
|
3792
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
3158
3793
|
if (props.variant === "connected") {
|
|
3159
3794
|
const { address, onDisconnect } = props;
|
|
3160
3795
|
return /* @__PURE__ */ jsx("div", { className: "-mx-3", children: /* @__PURE__ */ jsxs("div", { className: buttonBaseClasses, children: [
|
|
@@ -3178,6 +3813,7 @@ const WalletModalButton = (props) => {
|
|
|
3178
3813
|
}
|
|
3179
3814
|
const { walletId, connector } = props;
|
|
3180
3815
|
const handleConnect = async () => {
|
|
3816
|
+
setIsConnecting(true);
|
|
3181
3817
|
try {
|
|
3182
3818
|
if (connector) {
|
|
3183
3819
|
connect({ connector });
|
|
@@ -3190,9 +3826,13 @@ const WalletModalButton = (props) => {
|
|
|
3190
3826
|
onClose?.();
|
|
3191
3827
|
} catch (error) {
|
|
3192
3828
|
console.error("Failed to connect wallet:", error);
|
|
3829
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to connect wallet. Please try again.";
|
|
3830
|
+
toast.error(errorMessage);
|
|
3831
|
+
} finally {
|
|
3832
|
+
setIsConnecting(false);
|
|
3193
3833
|
}
|
|
3194
3834
|
};
|
|
3195
|
-
const isDisabled = connector ? isPending :
|
|
3835
|
+
const isDisabled = connector ? isPending : isConnecting;
|
|
3196
3836
|
return /* @__PURE__ */ jsxs(
|
|
3197
3837
|
Button,
|
|
3198
3838
|
{
|
|
@@ -3227,8 +3867,8 @@ const WalletSelectModal = () => {
|
|
|
3227
3867
|
{
|
|
3228
3868
|
strategy: tonWallet,
|
|
3229
3869
|
walletId: "ton",
|
|
3230
|
-
name: t("wallets.
|
|
3231
|
-
icon:
|
|
3870
|
+
name: t("wallets.tonconnect"),
|
|
3871
|
+
icon: TonConnectIcon
|
|
3232
3872
|
},
|
|
3233
3873
|
{
|
|
3234
3874
|
strategy: metaMaskWallet,
|
|
@@ -3266,8 +3906,8 @@ const WalletSelectModal = () => {
|
|
|
3266
3906
|
const tonWallets = [
|
|
3267
3907
|
{
|
|
3268
3908
|
id: "ton",
|
|
3269
|
-
name: t("wallets.
|
|
3270
|
-
icon:
|
|
3909
|
+
name: t("wallets.tonconnect"),
|
|
3910
|
+
icon: TonConnectIcon,
|
|
3271
3911
|
enabled: true
|
|
3272
3912
|
}
|
|
3273
3913
|
];
|
|
@@ -3303,7 +3943,7 @@ const WalletSelectModal = () => {
|
|
|
3303
3943
|
}
|
|
3304
3944
|
].filter((category) => category.wallets.length > 0);
|
|
3305
3945
|
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
3306
|
-
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3946
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "text-left", children: [
|
|
3307
3947
|
/* @__PURE__ */ jsx(DialogTitle, { children: t("wallets.chooseWallet") }),
|
|
3308
3948
|
/* @__PURE__ */ jsx(DialogDescription, { children: t("wallets.oneWalletPerEnv") })
|
|
3309
3949
|
] }),
|
|
@@ -3346,7 +3986,7 @@ const WalletSelectModal = () => {
|
|
|
3346
3986
|
] }) });
|
|
3347
3987
|
};
|
|
3348
3988
|
const ProgressStep = ({
|
|
3349
|
-
icon = /* @__PURE__ */ jsx(Loader2, { className: "w-
|
|
3989
|
+
icon = /* @__PURE__ */ jsx(Loader2, { className: "w-16 h-16 animate-spin" })
|
|
3350
3990
|
}) => {
|
|
3351
3991
|
const { t } = useBridgeTranslation();
|
|
3352
3992
|
return /* @__PURE__ */ jsx(DialogContent, { showCloseButton: false, children: /* @__PURE__ */ jsxs("div", { className: "flex relative flex-col gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise bg-background", children: [
|
|
@@ -3356,40 +3996,130 @@ const ProgressStep = ({
|
|
|
3356
3996
|
] }) });
|
|
3357
3997
|
};
|
|
3358
3998
|
const FailedStep = ({
|
|
3359
|
-
icon = /* @__PURE__ */ jsx(AlertCircleIcon, { className: "w-
|
|
3999
|
+
icon = /* @__PURE__ */ jsx(AlertCircleIcon, { className: "w-16 h-16" })
|
|
3360
4000
|
}) => {
|
|
3361
4001
|
const { current, reset } = useTransactionStore();
|
|
3362
4002
|
const { t } = useBridgeTranslation();
|
|
3363
4003
|
return /* @__PURE__ */ jsxs(DialogContent, { showCloseButton: false, children: [
|
|
3364
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col relative gap-4
|
|
4004
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col relative gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise", children: [
|
|
3365
4005
|
icon,
|
|
3366
|
-
/* @__PURE__ */
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
4006
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
4007
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: t("transaction.failed") }),
|
|
4008
|
+
current?.errorCode && /* @__PURE__ */ jsx(DialogDescription$1, { children: t(
|
|
4009
|
+
`errors.${current.errorCode}`,
|
|
4010
|
+
current.errorParams || {}
|
|
4011
|
+
) })
|
|
4012
|
+
] })
|
|
3371
4013
|
] }),
|
|
3372
|
-
/* @__PURE__ */ jsx(
|
|
4014
|
+
/* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "w-full min-w-40", onClick: reset, children: t("common.close") }) })
|
|
3373
4015
|
] });
|
|
3374
4016
|
};
|
|
4017
|
+
const EXPLORER_CONFIGS = {
|
|
4018
|
+
// TON
|
|
4019
|
+
ton: {
|
|
4020
|
+
baseUrl: "https://tonscan.org",
|
|
4021
|
+
txPath: "/tx/"
|
|
4022
|
+
},
|
|
4023
|
+
// TRON
|
|
4024
|
+
tron: {
|
|
4025
|
+
baseUrl: "https://tronscan.org",
|
|
4026
|
+
txPath: "/#/transaction/"
|
|
4027
|
+
},
|
|
4028
|
+
// Ethereum & EVM chains
|
|
4029
|
+
ethereum: {
|
|
4030
|
+
baseUrl: "https://etherscan.io",
|
|
4031
|
+
txPath: "/tx/"
|
|
4032
|
+
},
|
|
4033
|
+
eth: {
|
|
4034
|
+
baseUrl: "https://etherscan.io",
|
|
4035
|
+
txPath: "/tx/"
|
|
4036
|
+
},
|
|
4037
|
+
// BSC (Binance Smart Chain)
|
|
4038
|
+
bsc: {
|
|
4039
|
+
baseUrl: "https://bscscan.com",
|
|
4040
|
+
txPath: "/tx/"
|
|
4041
|
+
},
|
|
4042
|
+
"binance-smart-chain": {
|
|
4043
|
+
baseUrl: "https://bscscan.com",
|
|
4044
|
+
txPath: "/tx/"
|
|
4045
|
+
},
|
|
4046
|
+
// Polygon
|
|
4047
|
+
polygon: {
|
|
4048
|
+
baseUrl: "https://polygonscan.com",
|
|
4049
|
+
txPath: "/tx/"
|
|
4050
|
+
},
|
|
4051
|
+
matic: {
|
|
4052
|
+
baseUrl: "https://polygonscan.com",
|
|
4053
|
+
txPath: "/tx/"
|
|
4054
|
+
},
|
|
4055
|
+
// Avalanche
|
|
4056
|
+
avalanche: {
|
|
4057
|
+
baseUrl: "https://snowtrace.io",
|
|
4058
|
+
txPath: "/tx/"
|
|
4059
|
+
},
|
|
4060
|
+
avax: {
|
|
4061
|
+
baseUrl: "https://snowtrace.io",
|
|
4062
|
+
txPath: "/tx/"
|
|
4063
|
+
},
|
|
4064
|
+
// Arbitrum
|
|
4065
|
+
arbitrum: {
|
|
4066
|
+
baseUrl: "https://arbiscan.io",
|
|
4067
|
+
txPath: "/tx/"
|
|
4068
|
+
},
|
|
4069
|
+
// Optimism
|
|
4070
|
+
optimism: {
|
|
4071
|
+
baseUrl: "https://optimistic.etherscan.io",
|
|
4072
|
+
txPath: "/tx/"
|
|
4073
|
+
},
|
|
4074
|
+
// Base
|
|
4075
|
+
base: {
|
|
4076
|
+
baseUrl: "https://basescan.org",
|
|
4077
|
+
txPath: "/tx/"
|
|
4078
|
+
},
|
|
4079
|
+
// Fantom
|
|
4080
|
+
fantom: {
|
|
4081
|
+
baseUrl: "https://ftmscan.com",
|
|
4082
|
+
txPath: "/tx/"
|
|
4083
|
+
}
|
|
4084
|
+
};
|
|
4085
|
+
function getExplorerTxUrl(chainKey, txHash) {
|
|
4086
|
+
if (!chainKey || !txHash) {
|
|
4087
|
+
return null;
|
|
4088
|
+
}
|
|
4089
|
+
const normalizedChainKey = chainKey.toLowerCase();
|
|
4090
|
+
const config = EXPLORER_CONFIGS[normalizedChainKey];
|
|
4091
|
+
if (!config) {
|
|
4092
|
+
console.warn(
|
|
4093
|
+
`No explorer config found for chain: ${chainKey}. Please add it to EXPLORER_CONFIGS.`
|
|
4094
|
+
);
|
|
4095
|
+
return null;
|
|
4096
|
+
}
|
|
4097
|
+
return `${config.baseUrl}${config.txPath}${txHash}`;
|
|
4098
|
+
}
|
|
4099
|
+
function openTransactionInExplorer(chainKey, txHash) {
|
|
4100
|
+
const url = getExplorerTxUrl(chainKey, txHash);
|
|
4101
|
+
if (url && typeof window !== "undefined") {
|
|
4102
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
3375
4105
|
const SuccessStep = ({
|
|
3376
|
-
icon = /* @__PURE__ */ jsx(CheckCircle2, { className: "w-
|
|
4106
|
+
icon = /* @__PURE__ */ jsx(CheckCircle2, { className: "w-16 h-16" })
|
|
3377
4107
|
}) => {
|
|
3378
4108
|
const { current, reset } = useTransactionStore();
|
|
3379
4109
|
const { t } = useBridgeTranslation();
|
|
3380
4110
|
const metadata = current?.metadata;
|
|
3381
4111
|
const srcTxHash = current?.srcTxHash;
|
|
3382
|
-
const
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
4112
|
+
const srcChainKey = current?.quote?.srcChainKey;
|
|
4113
|
+
const handleOpenExplorer = () => {
|
|
4114
|
+
if (srcTxHash && srcChainKey) {
|
|
4115
|
+
openTransactionInExplorer(srcChainKey, srcTxHash);
|
|
3386
4116
|
}
|
|
3387
4117
|
};
|
|
3388
4118
|
return /* @__PURE__ */ jsxs(DialogContent, { showCloseButton: false, children: [
|
|
3389
4119
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 pt-10 px-8 flex-1 justify-start items-center text-center noise bg-background", children: [
|
|
3390
4120
|
icon,
|
|
3391
4121
|
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("transaction.success") }) }),
|
|
3392
|
-
/* @__PURE__ */ jsxs("div", { className: "w-full space-y-
|
|
4122
|
+
/* @__PURE__ */ jsxs("div", { className: "w-full space-y-2 mt-4 relative z-10 pb-14", children: [
|
|
3393
4123
|
metadata?.srcAmountHuman && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3394
4124
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.bridged") }),
|
|
3395
4125
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
|
|
@@ -3401,15 +4131,13 @@ const SuccessStep = ({
|
|
|
3401
4131
|
] }),
|
|
3402
4132
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3403
4133
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.transferTitle") }),
|
|
3404
|
-
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
4134
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium space-x-1", children: [
|
|
3405
4135
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3406
4136
|
metadata?.srcChainName,
|
|
3407
4137
|
" ",
|
|
3408
4138
|
/* @__PURE__ */ jsx(NetworkSymbol, { chainKey: metadata.srcChainName })
|
|
3409
4139
|
] }),
|
|
3410
|
-
" ",
|
|
3411
|
-
"→",
|
|
3412
|
-
" ",
|
|
4140
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "→" }),
|
|
3413
4141
|
/* @__PURE__ */ jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3414
4142
|
metadata?.dstChainName,
|
|
3415
4143
|
" ",
|
|
@@ -3422,18 +4150,18 @@ const SuccessStep = ({
|
|
|
3422
4150
|
/* @__PURE__ */ jsx(
|
|
3423
4151
|
"button",
|
|
3424
4152
|
{
|
|
3425
|
-
onClick:
|
|
3426
|
-
className: "font-medium hover:underline
|
|
4153
|
+
onClick: handleOpenExplorer,
|
|
4154
|
+
className: "font-medium cursor-pointer inline-flex items-center gap-1 underline hover:no-underline",
|
|
3427
4155
|
children: formatHash(srcTxHash)
|
|
3428
4156
|
}
|
|
3429
4157
|
)
|
|
3430
4158
|
] }),
|
|
3431
|
-
metadata?.
|
|
4159
|
+
metadata?.actualFeeValue && /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3432
4160
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("transaction.finalFee") }),
|
|
3433
4161
|
/* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
3434
|
-
metadata.
|
|
4162
|
+
metadata.actualFeeValue,
|
|
3435
4163
|
" ",
|
|
3436
|
-
metadata.
|
|
4164
|
+
metadata.actualFeeSymbol
|
|
3437
4165
|
] })
|
|
3438
4166
|
] })
|
|
3439
4167
|
] })
|
|
@@ -3459,7 +4187,7 @@ const useCountdown = (initialSeconds) => {
|
|
|
3459
4187
|
};
|
|
3460
4188
|
};
|
|
3461
4189
|
const ConfirmStep = ({
|
|
3462
|
-
icon = /* @__PURE__ */ jsx(Clock, { className: "w-
|
|
4190
|
+
icon = /* @__PURE__ */ jsx(Clock, { className: "w-16 h-16" })
|
|
3463
4191
|
}) => {
|
|
3464
4192
|
const { t } = useBridgeTranslation();
|
|
3465
4193
|
const { formatTime } = useCountdown(90);
|
|
@@ -3530,7 +4258,7 @@ const useTokens = () => {
|
|
|
3530
4258
|
return { ...query };
|
|
3531
4259
|
};
|
|
3532
4260
|
const useChains = () => {
|
|
3533
|
-
const { setChains, setFromChain } = useChainsStore();
|
|
4261
|
+
const { setChains, setFromChain, fromChain } = useChainsStore();
|
|
3534
4262
|
const query = useQuery({
|
|
3535
4263
|
queryKey: ["chains"],
|
|
3536
4264
|
queryFn: () => getChains(),
|
|
@@ -3544,10 +4272,16 @@ const useChains = () => {
|
|
|
3544
4272
|
});
|
|
3545
4273
|
useEffect(() => {
|
|
3546
4274
|
if (query.isSuccess && query.data?.length) {
|
|
4275
|
+
console.log(
|
|
4276
|
+
`[DEBUG] Loaded ${query.data.length} chains from API:`,
|
|
4277
|
+
query.data.map((c) => c.chainKey)
|
|
4278
|
+
);
|
|
3547
4279
|
setChains(query.data);
|
|
3548
|
-
|
|
4280
|
+
if (!fromChain) {
|
|
4281
|
+
setFromChain(query.data[0]);
|
|
4282
|
+
}
|
|
3549
4283
|
}
|
|
3550
|
-
}, [query.isSuccess, query.data
|
|
4284
|
+
}, [query.isSuccess, query.data]);
|
|
3551
4285
|
useEffect(() => {
|
|
3552
4286
|
if (query.isError && query.error) {
|
|
3553
4287
|
console.error(query.error.name, query.error.message);
|
|
@@ -3613,49 +4347,6 @@ class ChainStrategyRegistry {
|
|
|
3613
4347
|
await strategy.disconnect();
|
|
3614
4348
|
}
|
|
3615
4349
|
}
|
|
3616
|
-
const EVM_CONFIG = {
|
|
3617
|
-
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
3618
|
-
gasEstimates: {
|
|
3619
|
-
approve: 65000n,
|
|
3620
|
-
bridge: 300000n
|
|
3621
|
-
},
|
|
3622
|
-
gasBuffer: 1.2,
|
|
3623
|
-
// 20% buffer
|
|
3624
|
-
timeout: 3e5,
|
|
3625
|
-
// 5 minutes (increased for slower networks)
|
|
3626
|
-
requiredConfirmations: 3
|
|
3627
|
-
// Wait for 3 confirmations for reorg protection
|
|
3628
|
-
};
|
|
3629
|
-
const TON_CONFIG = {
|
|
3630
|
-
apiUrl: "https://toncenter.com/api/v2",
|
|
3631
|
-
timeout: 36e4,
|
|
3632
|
-
// 6 minutes
|
|
3633
|
-
validUntil: 600
|
|
3634
|
-
// 10 minutes
|
|
3635
|
-
};
|
|
3636
|
-
const TRON_CONFIG = {
|
|
3637
|
-
timeout: 12e4,
|
|
3638
|
-
// 2 minutes (for 19 confirmations)
|
|
3639
|
-
feeLimit: 1e8,
|
|
3640
|
-
// 100 TRX in sun
|
|
3641
|
-
requiredConfirmations: 19,
|
|
3642
|
-
// TRON standard: 19 blocks for confirmation
|
|
3643
|
-
pollingInterval: 3e3
|
|
3644
|
-
// 3 seconds between checks
|
|
3645
|
-
};
|
|
3646
|
-
let tonClientInstance = null;
|
|
3647
|
-
function getTonClient(customClient, apiKey) {
|
|
3648
|
-
if (customClient) {
|
|
3649
|
-
return customClient;
|
|
3650
|
-
}
|
|
3651
|
-
if (!tonClientInstance) {
|
|
3652
|
-
tonClientInstance = new TonClient({
|
|
3653
|
-
endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
|
|
3654
|
-
apiKey
|
|
3655
|
-
});
|
|
3656
|
-
}
|
|
3657
|
-
return tonClientInstance;
|
|
3658
|
-
}
|
|
3659
4350
|
function isNativeAddress(addr) {
|
|
3660
4351
|
if (!addr) return false;
|
|
3661
4352
|
const a = addr.toLowerCase();
|
|
@@ -3748,7 +4439,9 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
|
|
|
3748
4439
|
}
|
|
3749
4440
|
} catch (error) {
|
|
3750
4441
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3751
|
-
const isZeroDataError = errorMessage.includes(
|
|
4442
|
+
const isZeroDataError = errorMessage.includes(
|
|
4443
|
+
'returned no data ("0x")'
|
|
4444
|
+
);
|
|
3752
4445
|
if (!isZeroDataError) {
|
|
3753
4446
|
console.debug(
|
|
3754
4447
|
`Failed to get priority token balance for ${priorityToken.symbol}:`,
|
|
@@ -3859,12 +4552,9 @@ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
|
|
|
3859
4552
|
}
|
|
3860
4553
|
const client = getTonClient(customTonClient, tonApiKey);
|
|
3861
4554
|
const accountAddress = Address$1.parse(address);
|
|
3862
|
-
console.log(address);
|
|
3863
|
-
console.log(tokens);
|
|
3864
4555
|
try {
|
|
3865
4556
|
const balance = await client.getBalance(accountAddress);
|
|
3866
4557
|
const tonBalance = Number(balance) / 1e9;
|
|
3867
|
-
console.log("tonBalance", tonBalance);
|
|
3868
4558
|
if (tonBalance > 0) {
|
|
3869
4559
|
balances.TON = { balance: tonBalance, address: "ton-native" };
|
|
3870
4560
|
}
|
|
@@ -3921,8 +4611,23 @@ async function getTronBalances(tronWeb, address, tokens) {
|
|
|
3921
4611
|
try {
|
|
3922
4612
|
if (!tronWeb) throw new Error("TronWeb not available");
|
|
3923
4613
|
const ownerB58 = toTronBase58(address, tronWeb);
|
|
4614
|
+
try {
|
|
4615
|
+
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
4616
|
+
const sunNum = Number(trxBalanceSun);
|
|
4617
|
+
const trxVal = tronWeb.fromSun(sunNum);
|
|
4618
|
+
const trxStr = typeof trxVal === "string" ? trxVal : trxVal.toString();
|
|
4619
|
+
const trxBalance = parseFloat(trxStr);
|
|
4620
|
+
if (trxBalance > 0) {
|
|
4621
|
+
balances.TRX = { balance: trxBalance, address: ownerB58 };
|
|
4622
|
+
}
|
|
4623
|
+
} catch (error) {
|
|
4624
|
+
console.warn("Failed to get native TRX balance:", error);
|
|
4625
|
+
}
|
|
3924
4626
|
for (const token of tokens) {
|
|
3925
4627
|
try {
|
|
4628
|
+
if (isNativeAddress(token.address) && token.symbol.toUpperCase() === "TRX") {
|
|
4629
|
+
continue;
|
|
4630
|
+
}
|
|
3926
4631
|
let balance = 0;
|
|
3927
4632
|
if (isNativeAddress(token.address)) {
|
|
3928
4633
|
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
@@ -4009,6 +4714,9 @@ class EvmChainStrategy {
|
|
|
4009
4714
|
getConnectLabel(t) {
|
|
4010
4715
|
return t("wallets.connectEvmWallet");
|
|
4011
4716
|
}
|
|
4717
|
+
getClient() {
|
|
4718
|
+
return this.provider;
|
|
4719
|
+
}
|
|
4012
4720
|
async getBalances(address, tokens, priorityToken) {
|
|
4013
4721
|
if (!this.publicClient) {
|
|
4014
4722
|
console.warn("No publicClient available for balance query");
|
|
@@ -4025,52 +4733,6 @@ class EvmChainStrategy {
|
|
|
4025
4733
|
if (!address) return false;
|
|
4026
4734
|
return /^0x[0-9a-fA-F]{40}$/.test(address);
|
|
4027
4735
|
}
|
|
4028
|
-
async estimateGasRequirement(params) {
|
|
4029
|
-
const provider = this.provider;
|
|
4030
|
-
const {
|
|
4031
|
-
selectedToken,
|
|
4032
|
-
nativeTokenSymbol,
|
|
4033
|
-
amount,
|
|
4034
|
-
balances,
|
|
4035
|
-
nativeDecimals,
|
|
4036
|
-
reserveFallback
|
|
4037
|
-
} = params;
|
|
4038
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4039
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4040
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4041
|
-
let estimatedGas = null;
|
|
4042
|
-
if (provider) {
|
|
4043
|
-
try {
|
|
4044
|
-
const gasPriceHex = await provider.send("eth_gasPrice", []);
|
|
4045
|
-
const gasPrice = BigInt(gasPriceHex);
|
|
4046
|
-
const approveGas = isNativeSelected ? 0n : EVM_CONFIG.gasEstimates.approve;
|
|
4047
|
-
const bridgeGas = EVM_CONFIG.gasEstimates.bridge;
|
|
4048
|
-
const totalGas = approveGas + bridgeGas;
|
|
4049
|
-
const bufferMultiplier = BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100));
|
|
4050
|
-
const requiredWei = gasPrice * totalGas * bufferMultiplier / 100n;
|
|
4051
|
-
const { formatUnits: formatUnits2 } = await import("ethers");
|
|
4052
|
-
estimatedGas = Number(formatUnits2(requiredWei, nativeDecimals));
|
|
4053
|
-
} catch {
|
|
4054
|
-
estimatedGas = null;
|
|
4055
|
-
}
|
|
4056
|
-
}
|
|
4057
|
-
const requiredNative = estimatedGas ?? reserveFallback;
|
|
4058
|
-
const amountNum = amount ?? 0;
|
|
4059
|
-
let hasEnoughGas = true;
|
|
4060
|
-
if (isNativeSelected) {
|
|
4061
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4062
|
-
} else {
|
|
4063
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4064
|
-
}
|
|
4065
|
-
return {
|
|
4066
|
-
estimatedGas,
|
|
4067
|
-
nativeBalance,
|
|
4068
|
-
requiredNative,
|
|
4069
|
-
hasEnoughGas,
|
|
4070
|
-
nativeSym,
|
|
4071
|
-
isNativeSelected
|
|
4072
|
-
};
|
|
4073
|
-
}
|
|
4074
4736
|
validateSteps(steps) {
|
|
4075
4737
|
if (!steps || steps.length === 0) {
|
|
4076
4738
|
throw new InvalidStepsError("evm", "No transaction steps provided");
|
|
@@ -4163,8 +4825,24 @@ class EvmChainStrategy {
|
|
|
4163
4825
|
console.log(
|
|
4164
4826
|
`EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
|
|
4165
4827
|
);
|
|
4828
|
+
let actualFeeValue;
|
|
4829
|
+
try {
|
|
4830
|
+
const gasUsed = receipt.gasUsed;
|
|
4831
|
+
const effectiveGasPrice = receipt.gasPrice;
|
|
4832
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4833
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4834
|
+
const { formatUnits: formatUnits2 } = await import("ethers");
|
|
4835
|
+
const feeInNative = formatUnits2(feeWei, 18);
|
|
4836
|
+
actualFeeValue = feeInNative;
|
|
4837
|
+
console.log(`EVM transaction fee: ${feeInNative} (native token)`);
|
|
4838
|
+
}
|
|
4839
|
+
} catch (error) {
|
|
4840
|
+
console.warn("Failed to calculate actual fee:", error);
|
|
4841
|
+
}
|
|
4166
4842
|
return {
|
|
4167
|
-
completed: true
|
|
4843
|
+
completed: true,
|
|
4844
|
+
actualFeeValue
|
|
4845
|
+
// Symbol will be determined by the caller based on chain info
|
|
4168
4846
|
};
|
|
4169
4847
|
} catch (error) {
|
|
4170
4848
|
if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
|
|
@@ -4177,8 +4855,26 @@ class EvmChainStrategy {
|
|
|
4177
4855
|
console.log(
|
|
4178
4856
|
`Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
|
|
4179
4857
|
);
|
|
4858
|
+
let actualFeeValue;
|
|
4859
|
+
try {
|
|
4860
|
+
const receipt = error.receipt;
|
|
4861
|
+
const gasUsed = receipt.gasUsed;
|
|
4862
|
+
const effectiveGasPrice = receipt.gasPrice || receipt.effectiveGasPrice;
|
|
4863
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4864
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4865
|
+
const { formatUnits: formatUnits2 } = await import("ethers");
|
|
4866
|
+
const feeInNative = formatUnits2(feeWei, 18);
|
|
4867
|
+
actualFeeValue = feeInNative;
|
|
4868
|
+
console.log(
|
|
4869
|
+
`Replacement transaction fee: ${feeInNative} (native token)`
|
|
4870
|
+
);
|
|
4871
|
+
}
|
|
4872
|
+
} catch (feeError) {
|
|
4873
|
+
console.warn("Failed to calculate replacement transaction fee:", feeError);
|
|
4874
|
+
}
|
|
4180
4875
|
return {
|
|
4181
|
-
completed: true
|
|
4876
|
+
completed: true,
|
|
4877
|
+
actualFeeValue
|
|
4182
4878
|
};
|
|
4183
4879
|
} else {
|
|
4184
4880
|
const chainError2 = new TransactionRevertedError("evm", txHash);
|
|
@@ -4381,6 +5077,9 @@ class TonChainStrategy {
|
|
|
4381
5077
|
getConnectLabel(t) {
|
|
4382
5078
|
return t("wallets.connectTonWallet");
|
|
4383
5079
|
}
|
|
5080
|
+
getClient() {
|
|
5081
|
+
return getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
5082
|
+
}
|
|
4384
5083
|
async getBalances(address, tokens) {
|
|
4385
5084
|
return await getTonBalances(
|
|
4386
5085
|
address,
|
|
@@ -4393,35 +5092,6 @@ class TonChainStrategy {
|
|
|
4393
5092
|
if (!address) return false;
|
|
4394
5093
|
return true;
|
|
4395
5094
|
}
|
|
4396
|
-
async estimateGasRequirement(params) {
|
|
4397
|
-
const {
|
|
4398
|
-
selectedToken,
|
|
4399
|
-
nativeTokenSymbol,
|
|
4400
|
-
amount,
|
|
4401
|
-
balances,
|
|
4402
|
-
reserveFallback
|
|
4403
|
-
} = params;
|
|
4404
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4405
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4406
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4407
|
-
const estimatedGas = null;
|
|
4408
|
-
const requiredNative = reserveFallback;
|
|
4409
|
-
const amountNum = amount ?? 0;
|
|
4410
|
-
let hasEnoughGas = true;
|
|
4411
|
-
if (isNativeSelected) {
|
|
4412
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4413
|
-
} else {
|
|
4414
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4415
|
-
}
|
|
4416
|
-
return {
|
|
4417
|
-
estimatedGas,
|
|
4418
|
-
nativeBalance,
|
|
4419
|
-
requiredNative,
|
|
4420
|
-
hasEnoughGas,
|
|
4421
|
-
nativeSym,
|
|
4422
|
-
isNativeSelected
|
|
4423
|
-
};
|
|
4424
|
-
}
|
|
4425
5095
|
validateSteps(steps) {
|
|
4426
5096
|
if (!steps || steps.length === 0) {
|
|
4427
5097
|
throw new InvalidStepsError("ton", "No transaction steps provided");
|
|
@@ -4474,23 +5144,35 @@ class TonChainStrategy {
|
|
|
4474
5144
|
const result = await this.config.tonConnectUI.sendTransaction(
|
|
4475
5145
|
transaction2
|
|
4476
5146
|
);
|
|
4477
|
-
const
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
5147
|
+
const bocBase64 = result.boc;
|
|
5148
|
+
try {
|
|
5149
|
+
const inMessage = loadMessage(Cell.fromBase64(bocBase64).beginParse());
|
|
5150
|
+
const messageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
5151
|
+
const hexHash = messageHash.toString("hex");
|
|
5152
|
+
onFirstHash?.(hexHash);
|
|
5153
|
+
return {
|
|
5154
|
+
chainKey: "ton",
|
|
5155
|
+
hash: hexHash
|
|
5156
|
+
};
|
|
5157
|
+
} catch (error) {
|
|
5158
|
+
console.error("Error parsing BOC to hex hash:", error);
|
|
5159
|
+
onFirstHash?.(bocBase64);
|
|
5160
|
+
return {
|
|
5161
|
+
chainKey: "ton",
|
|
5162
|
+
hash: bocBase64
|
|
5163
|
+
};
|
|
5164
|
+
}
|
|
4483
5165
|
} catch (error) {
|
|
4484
5166
|
throw toChainStrategyError(error, "ton", "transaction");
|
|
4485
5167
|
}
|
|
4486
5168
|
}
|
|
4487
5169
|
async waitForCompletion(txHash) {
|
|
4488
5170
|
try {
|
|
4489
|
-
const
|
|
5171
|
+
const result = await this.checkTonTransaction(
|
|
4490
5172
|
txHash,
|
|
4491
5173
|
TON_CONFIG.timeout
|
|
4492
5174
|
);
|
|
4493
|
-
if (!confirmed) {
|
|
5175
|
+
if (!result.confirmed) {
|
|
4494
5176
|
const error = new TransactionFailedError(
|
|
4495
5177
|
"ton",
|
|
4496
5178
|
"Transaction not confirmed on-chain",
|
|
@@ -4502,7 +5184,9 @@ class TonChainStrategy {
|
|
|
4502
5184
|
};
|
|
4503
5185
|
}
|
|
4504
5186
|
return {
|
|
4505
|
-
completed: true
|
|
5187
|
+
completed: true,
|
|
5188
|
+
actualFeeValue: result.fee,
|
|
5189
|
+
actualFeeSymbol: "TON"
|
|
4506
5190
|
};
|
|
4507
5191
|
} catch (error) {
|
|
4508
5192
|
const chainError = toChainStrategyError(
|
|
@@ -4532,20 +5216,31 @@ class TonChainStrategy {
|
|
|
4532
5216
|
};
|
|
4533
5217
|
return beginCell$1().store(storeMessage(normalizedMessage, { forceRef: true })).endCell().hash();
|
|
4534
5218
|
}
|
|
4535
|
-
async checkTonTransaction(
|
|
5219
|
+
async checkTonTransaction(hashOrBoc, timeoutMs = 36e4) {
|
|
4536
5220
|
const deadline = Date.now() + timeoutMs;
|
|
4537
5221
|
const client = getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
4538
5222
|
try {
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
5223
|
+
let targetMessageHash;
|
|
5224
|
+
let accountAddress;
|
|
5225
|
+
try {
|
|
5226
|
+
const inMessage = loadMessage(Cell.fromBase64(hashOrBoc).beginParse());
|
|
5227
|
+
if (inMessage.info.type !== "external-in") {
|
|
5228
|
+
console.debug(
|
|
5229
|
+
"Expected external-in message, got:",
|
|
5230
|
+
inMessage.info.type
|
|
5231
|
+
);
|
|
5232
|
+
return { confirmed: false };
|
|
5233
|
+
}
|
|
5234
|
+
accountAddress = inMessage.info.dest;
|
|
5235
|
+
targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
5236
|
+
} catch {
|
|
5237
|
+
targetMessageHash = Buffer.from(hashOrBoc, "hex");
|
|
5238
|
+
if (!this.config.tonAddress) {
|
|
5239
|
+
console.debug("No wallet address available for hex hash lookup");
|
|
5240
|
+
return { confirmed: false };
|
|
5241
|
+
}
|
|
5242
|
+
accountAddress = Address.parse(this.config.tonAddress);
|
|
4546
5243
|
}
|
|
4547
|
-
const accountAddress = inMessage.info.dest;
|
|
4548
|
-
const targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
4549
5244
|
let lt = void 0;
|
|
4550
5245
|
let hash = void 0;
|
|
4551
5246
|
while (Date.now() < deadline) {
|
|
@@ -4557,7 +5252,7 @@ class TonChainStrategy {
|
|
|
4557
5252
|
archival: true
|
|
4558
5253
|
});
|
|
4559
5254
|
if (transactions.length === 0) {
|
|
4560
|
-
await new Promise((r) => setTimeout(r,
|
|
5255
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4561
5256
|
lt = void 0;
|
|
4562
5257
|
hash = void 0;
|
|
4563
5258
|
continue;
|
|
@@ -4569,24 +5264,31 @@ class TonChainStrategy {
|
|
|
4569
5264
|
);
|
|
4570
5265
|
if (txInMessageHash.equals(targetMessageHash)) {
|
|
4571
5266
|
console.debug("Transaction found by in-message hash");
|
|
4572
|
-
|
|
5267
|
+
const totalFees = tx.totalFees;
|
|
5268
|
+
const feeInTon = Number(totalFees) / 1e9;
|
|
5269
|
+
console.log(`TON transaction fee: ${feeInTon} TON`);
|
|
5270
|
+
return {
|
|
5271
|
+
confirmed: true,
|
|
5272
|
+
fee: feeInTon.toString()
|
|
5273
|
+
};
|
|
4573
5274
|
}
|
|
4574
5275
|
}
|
|
4575
5276
|
}
|
|
4576
5277
|
const lastTx = transactions[transactions.length - 1];
|
|
4577
5278
|
lt = lastTx.lt.toString();
|
|
4578
5279
|
hash = lastTx.hash().toString("base64");
|
|
5280
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4579
5281
|
} catch (error) {
|
|
4580
5282
|
console.debug("Error fetching transactions:", error);
|
|
4581
|
-
await new Promise((r) => setTimeout(r,
|
|
5283
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4582
5284
|
lt = void 0;
|
|
4583
5285
|
hash = void 0;
|
|
4584
5286
|
}
|
|
4585
5287
|
}
|
|
4586
|
-
return false;
|
|
5288
|
+
return { confirmed: false };
|
|
4587
5289
|
} catch (error) {
|
|
4588
5290
|
console.debug("Error parsing BOC or checking transaction:", error);
|
|
4589
|
-
return false;
|
|
5291
|
+
return { confirmed: false };
|
|
4590
5292
|
}
|
|
4591
5293
|
}
|
|
4592
5294
|
}
|
|
@@ -4605,9 +5307,14 @@ class TronChainStrategy {
|
|
|
4605
5307
|
return "TRON Chain Strategy";
|
|
4606
5308
|
}
|
|
4607
5309
|
async connect() {
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
5310
|
+
if (!this.isTronLinkInstalled()) {
|
|
5311
|
+
if (typeof window !== "undefined") {
|
|
5312
|
+
window.open("https://www.tronlink.org/", "_blank");
|
|
5313
|
+
}
|
|
5314
|
+
throw new WalletNotFoundError(
|
|
5315
|
+
"tron",
|
|
5316
|
+
"TronLink wallet is not installed. Please install TronLink extension and try again."
|
|
5317
|
+
);
|
|
4611
5318
|
}
|
|
4612
5319
|
this.config.tronSelect(TronLinkAdapterName);
|
|
4613
5320
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -4635,7 +5342,7 @@ class TronChainStrategy {
|
|
|
4635
5342
|
return t("wallets.connectTronWallet");
|
|
4636
5343
|
}
|
|
4637
5344
|
async getBalances(address, tokens) {
|
|
4638
|
-
const tronWeb = this.
|
|
5345
|
+
const tronWeb = this.getClient();
|
|
4639
5346
|
if (!tronWeb) return {};
|
|
4640
5347
|
return await getTronBalances(tronWeb, address, tokens);
|
|
4641
5348
|
}
|
|
@@ -4643,35 +5350,6 @@ class TronChainStrategy {
|
|
|
4643
5350
|
if (!address) return false;
|
|
4644
5351
|
return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
|
|
4645
5352
|
}
|
|
4646
|
-
async estimateGasRequirement(params) {
|
|
4647
|
-
const {
|
|
4648
|
-
selectedToken,
|
|
4649
|
-
nativeTokenSymbol,
|
|
4650
|
-
amount,
|
|
4651
|
-
balances,
|
|
4652
|
-
reserveFallback
|
|
4653
|
-
} = params;
|
|
4654
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4655
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4656
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4657
|
-
const estimatedGas = null;
|
|
4658
|
-
const requiredNative = reserveFallback;
|
|
4659
|
-
const amountNum = amount ?? 0;
|
|
4660
|
-
let hasEnoughGas = true;
|
|
4661
|
-
if (isNativeSelected) {
|
|
4662
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4663
|
-
} else {
|
|
4664
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4665
|
-
}
|
|
4666
|
-
return {
|
|
4667
|
-
estimatedGas,
|
|
4668
|
-
nativeBalance,
|
|
4669
|
-
requiredNative,
|
|
4670
|
-
hasEnoughGas,
|
|
4671
|
-
nativeSym,
|
|
4672
|
-
isNativeSelected
|
|
4673
|
-
};
|
|
4674
|
-
}
|
|
4675
5353
|
validateSteps(steps) {
|
|
4676
5354
|
console.log("validateSteps");
|
|
4677
5355
|
if (!steps?.length) {
|
|
@@ -4688,8 +5366,7 @@ class TronChainStrategy {
|
|
|
4688
5366
|
}
|
|
4689
5367
|
}
|
|
4690
5368
|
async executeSteps(steps, _context, onFirstHash) {
|
|
4691
|
-
|
|
4692
|
-
const tronWeb = this.getTronWeb();
|
|
5369
|
+
const tronWeb = this.getClient();
|
|
4693
5370
|
if (!tronWeb) {
|
|
4694
5371
|
throw new ProviderNotAvailableError("tron");
|
|
4695
5372
|
}
|
|
@@ -4783,7 +5460,7 @@ class TronChainStrategy {
|
|
|
4783
5460
|
}
|
|
4784
5461
|
async waitForCompletion(txHash) {
|
|
4785
5462
|
try {
|
|
4786
|
-
const tronWeb = this.
|
|
5463
|
+
const tronWeb = this.getClient();
|
|
4787
5464
|
if (!tronWeb) {
|
|
4788
5465
|
throw new ProviderNotAvailableError("tron");
|
|
4789
5466
|
}
|
|
@@ -4792,6 +5469,7 @@ class TronChainStrategy {
|
|
|
4792
5469
|
);
|
|
4793
5470
|
const deadline = Date.now() + TRON_CONFIG.timeout;
|
|
4794
5471
|
let txBlockNumber = null;
|
|
5472
|
+
let actualFeeTrx = null;
|
|
4795
5473
|
while (Date.now() < deadline && !txBlockNumber) {
|
|
4796
5474
|
try {
|
|
4797
5475
|
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
@@ -4826,7 +5504,9 @@ class TronChainStrategy {
|
|
|
4826
5504
|
};
|
|
4827
5505
|
}
|
|
4828
5506
|
txBlockNumber = info.blockNumber;
|
|
4829
|
-
|
|
5507
|
+
const feeSun = info.fee || 0;
|
|
5508
|
+
actualFeeTrx = feeSun / 1e6;
|
|
5509
|
+
console.log(`TRON transaction found in block ${txBlockNumber}, fee: ${actualFeeTrx} TRX`);
|
|
4830
5510
|
}
|
|
4831
5511
|
} catch (e) {
|
|
4832
5512
|
console.debug("TRON getTransactionInfo error:", e);
|
|
@@ -4854,7 +5534,9 @@ class TronChainStrategy {
|
|
|
4854
5534
|
`TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
|
|
4855
5535
|
);
|
|
4856
5536
|
return {
|
|
4857
|
-
completed: true
|
|
5537
|
+
completed: true,
|
|
5538
|
+
actualFeeValue: actualFeeTrx?.toString(),
|
|
5539
|
+
actualFeeSymbol: "TRX"
|
|
4858
5540
|
};
|
|
4859
5541
|
}
|
|
4860
5542
|
console.log(
|
|
@@ -4883,9 +5565,26 @@ class TronChainStrategy {
|
|
|
4883
5565
|
};
|
|
4884
5566
|
}
|
|
4885
5567
|
}
|
|
4886
|
-
|
|
5568
|
+
getClient() {
|
|
4887
5569
|
return typeof window !== "undefined" ? window.tronWeb : void 0;
|
|
4888
5570
|
}
|
|
5571
|
+
/**
|
|
5572
|
+
* Check if TronLink wallet is actually installed
|
|
5573
|
+
* This excludes Bybit Wallet which also injects tronLink for compatibility
|
|
5574
|
+
*/
|
|
5575
|
+
isTronLinkInstalled() {
|
|
5576
|
+
if (typeof window === "undefined") {
|
|
5577
|
+
return false;
|
|
5578
|
+
}
|
|
5579
|
+
const hasBybitWallet = typeof window.bybitWallet !== "undefined" && typeof window.bybitWallet.tronLink !== "undefined";
|
|
5580
|
+
if (hasBybitWallet && !window.tronLink) {
|
|
5581
|
+
return false;
|
|
5582
|
+
}
|
|
5583
|
+
if (!window.tronLink) {
|
|
5584
|
+
return false;
|
|
5585
|
+
}
|
|
5586
|
+
return true;
|
|
5587
|
+
}
|
|
4889
5588
|
hexToAscii(h) {
|
|
4890
5589
|
if (!h) return null;
|
|
4891
5590
|
const clean = h.replace(/^0x/, "");
|
|
@@ -5183,7 +5882,7 @@ class TronChainStrategy {
|
|
|
5183
5882
|
*/
|
|
5184
5883
|
async checkSolidified(txHash) {
|
|
5185
5884
|
try {
|
|
5186
|
-
const tronWeb = this.
|
|
5885
|
+
const tronWeb = this.getClient();
|
|
5187
5886
|
if (!tronWeb) {
|
|
5188
5887
|
return false;
|
|
5189
5888
|
}
|
|
@@ -5289,7 +5988,7 @@ const routePresets = [
|
|
|
5289
5988
|
RoutePriority.CHEAPEST,
|
|
5290
5989
|
RoutePriority.RECOMMENDED
|
|
5291
5990
|
];
|
|
5292
|
-
const
|
|
5991
|
+
const SettingsModal = ({ isOpen, onClose }) => {
|
|
5293
5992
|
const { t } = useBridgeTranslation();
|
|
5294
5993
|
const { toChain } = useChainsStore();
|
|
5295
5994
|
const { tokens } = useTokensStore();
|
|
@@ -5322,14 +6021,14 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5322
6021
|
);
|
|
5323
6022
|
const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
|
|
5324
6023
|
const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
|
|
5325
|
-
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
|
|
5326
|
-
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("settings.title") }) }),
|
|
6024
|
+
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), children: [
|
|
6025
|
+
/* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("settings.title") }) }),
|
|
5327
6026
|
/* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
5328
6027
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5329
6028
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5330
6029
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5331
6030
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.gasOnDestination") }),
|
|
5332
|
-
/* @__PURE__ */ jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(
|
|
6031
|
+
/* @__PURE__ */ jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5333
6032
|
] }),
|
|
5334
6033
|
/* @__PURE__ */ jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
|
|
5335
6034
|
] }),
|
|
@@ -5351,6 +6050,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5351
6050
|
Badge,
|
|
5352
6051
|
{
|
|
5353
6052
|
onClick: () => setGasPreset(g),
|
|
6053
|
+
size: "lg",
|
|
5354
6054
|
className: cn(
|
|
5355
6055
|
"cursor-pointer",
|
|
5356
6056
|
gasPreset === g ? activeBtn : notActiveBtn
|
|
@@ -5366,7 +6066,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5366
6066
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5367
6067
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5368
6068
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.slippageTolerance") }),
|
|
5369
|
-
/* @__PURE__ */ jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(
|
|
6069
|
+
/* @__PURE__ */ jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5370
6070
|
] }),
|
|
5371
6071
|
slippageBps >= 500 && /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
|
|
5372
6072
|
defaultValue: "High slippage warning"
|
|
@@ -5377,6 +6077,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5377
6077
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsx(
|
|
5378
6078
|
Badge,
|
|
5379
6079
|
{
|
|
6080
|
+
size: "lg",
|
|
5380
6081
|
onClick: () => {
|
|
5381
6082
|
const bps = parseFloat(p.replace("%", "")) * 100;
|
|
5382
6083
|
setSlippageBps(bps);
|
|
@@ -5395,11 +6096,12 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5395
6096
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5396
6097
|
/* @__PURE__ */ jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5397
6098
|
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.routePriority") }),
|
|
5398
|
-
/* @__PURE__ */ jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsx(
|
|
6099
|
+
/* @__PURE__ */ jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5399
6100
|
] }) }),
|
|
5400
6101
|
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsx(
|
|
5401
6102
|
Badge,
|
|
5402
6103
|
{
|
|
6104
|
+
size: "lg",
|
|
5403
6105
|
onClick: () => setRoutePriority(r),
|
|
5404
6106
|
className: cn(
|
|
5405
6107
|
"cursor-pointer",
|
|
@@ -5427,7 +6129,7 @@ const TokenRow = ({
|
|
|
5427
6129
|
Button,
|
|
5428
6130
|
{
|
|
5429
6131
|
onClick: onPick,
|
|
5430
|
-
className: `w-full rounded-
|
|
6132
|
+
className: `w-full rounded-xs bg-transparent flex items-center justify-between gap-3 px-3 hover:bg-accent hover:scale-100 ${isSelected ? "bg-accent hover:bg-accent/50" : ""}`,
|
|
5431
6133
|
children: [
|
|
5432
6134
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5433
6135
|
/* @__PURE__ */ jsx(
|
|
@@ -5448,7 +6150,7 @@ const TokenRow = ({
|
|
|
5448
6150
|
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-12 rounded-md" })
|
|
5449
6151
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5450
6152
|
/* @__PURE__ */ jsx("div", { className: "font-semibold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
|
|
5451
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
6153
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs leading-3 text-muted-foreground/50", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
5452
6154
|
] }) })
|
|
5453
6155
|
]
|
|
5454
6156
|
}
|
|
@@ -5591,8 +6293,8 @@ const TokenSelectModal = ({
|
|
|
5591
6293
|
[groupedTokens.willChangeSrcChain]
|
|
5592
6294
|
);
|
|
5593
6295
|
const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
|
|
5594
|
-
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: [
|
|
5595
|
-
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectToken") }) }),
|
|
6296
|
+
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col", children: [
|
|
6297
|
+
/* @__PURE__ */ jsx(DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsx(DialogTitle, { children: t("bridge.selectToken") }) }),
|
|
5596
6298
|
/* @__PURE__ */ jsx(
|
|
5597
6299
|
SearchInput,
|
|
5598
6300
|
{
|
|
@@ -5605,24 +6307,26 @@ const TokenSelectModal = ({
|
|
|
5605
6307
|
/* @__PURE__ */ jsx(
|
|
5606
6308
|
Button,
|
|
5607
6309
|
{
|
|
5608
|
-
variant: tab === "my" ? "default" : "
|
|
6310
|
+
variant: tab === "my" ? "default" : "ghost",
|
|
5609
6311
|
onClick: () => setTab("my"),
|
|
5610
6312
|
size: "sm",
|
|
6313
|
+
className: cn(tab !== "my" && "bg-muted hover:bg-accent"),
|
|
5611
6314
|
children: t("bridge.myTokens")
|
|
5612
6315
|
}
|
|
5613
6316
|
),
|
|
5614
6317
|
/* @__PURE__ */ jsx(
|
|
5615
6318
|
Button,
|
|
5616
6319
|
{
|
|
5617
|
-
variant: tab === "all" ? "default" : "
|
|
6320
|
+
variant: tab === "all" ? "default" : "ghost",
|
|
5618
6321
|
onClick: () => setTab("all"),
|
|
5619
6322
|
size: "sm",
|
|
6323
|
+
className: cn(tab !== "all" && "bg-muted hover:bg-accent"),
|
|
5620
6324
|
children: t("bridge.allTokens")
|
|
5621
6325
|
}
|
|
5622
6326
|
)
|
|
5623
6327
|
] }),
|
|
5624
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto -
|
|
5625
|
-
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase
|
|
6328
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto px-1", children: hasNoResults ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6329
|
+
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase py-2", children: t("bridge.noBalancesFound") }),
|
|
5626
6330
|
tokensToRender.map(({ token, willChangeSrc }) => {
|
|
5627
6331
|
const bal = getBalance(token.symbol);
|
|
5628
6332
|
const usd = getTokenUsdValue(token.symbol, token.price?.usd);
|
|
@@ -5673,6 +6377,7 @@ const TokenSelectModal = ({
|
|
|
5673
6377
|
function useBridgeRefresh() {
|
|
5674
6378
|
const qc = useQueryClient();
|
|
5675
6379
|
const { srcAddress, dstAddress } = useAddresses();
|
|
6380
|
+
const { fromChain, toChain } = useChainsStore();
|
|
5676
6381
|
const { setLoading: setQLoading, triggerRefetch } = useBridgeQuoteStore();
|
|
5677
6382
|
const { hasAnyWallet } = useConnectedWalletsStore();
|
|
5678
6383
|
const [spinning, setSpinning] = useState(false);
|
|
@@ -5685,14 +6390,30 @@ function useBridgeRefresh() {
|
|
|
5685
6390
|
qc.invalidateQueries({ queryKey: ["chains"] })
|
|
5686
6391
|
];
|
|
5687
6392
|
if (hasAnyWallet()) {
|
|
5688
|
-
if (srcAddress)
|
|
6393
|
+
if (srcAddress) {
|
|
5689
6394
|
queries.push(
|
|
5690
6395
|
qc.invalidateQueries({ queryKey: ["srcTokens", srcAddress] })
|
|
5691
6396
|
);
|
|
5692
|
-
|
|
6397
|
+
if (fromChain?.chainKey) {
|
|
6398
|
+
queries.push(
|
|
6399
|
+
qc.invalidateQueries({
|
|
6400
|
+
queryKey: ["balances", fromChain.chainKey, srcAddress]
|
|
6401
|
+
})
|
|
6402
|
+
);
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
if (dstAddress) {
|
|
5693
6406
|
queries.push(
|
|
5694
6407
|
qc.invalidateQueries({ queryKey: ["dstTokens", dstAddress] })
|
|
5695
6408
|
);
|
|
6409
|
+
if (toChain?.chainKey) {
|
|
6410
|
+
queries.push(
|
|
6411
|
+
qc.invalidateQueries({
|
|
6412
|
+
queryKey: ["balances", toChain.chainKey, dstAddress]
|
|
6413
|
+
})
|
|
6414
|
+
);
|
|
6415
|
+
}
|
|
6416
|
+
}
|
|
5696
6417
|
}
|
|
5697
6418
|
await Promise.all(queries);
|
|
5698
6419
|
triggerRefetch();
|
|
@@ -5703,6 +6424,8 @@ function useBridgeRefresh() {
|
|
|
5703
6424
|
qc,
|
|
5704
6425
|
srcAddress,
|
|
5705
6426
|
dstAddress,
|
|
6427
|
+
fromChain?.chainKey,
|
|
6428
|
+
toChain?.chainKey,
|
|
5706
6429
|
hasAnyWallet,
|
|
5707
6430
|
setQLoading,
|
|
5708
6431
|
triggerRefetch
|
|
@@ -5722,7 +6445,7 @@ const RefreshButton = () => {
|
|
|
5722
6445
|
onClick: handleRefresh,
|
|
5723
6446
|
disabled: spinning,
|
|
5724
6447
|
variant: "secondary",
|
|
5725
|
-
className: "
|
|
6448
|
+
className: "h-9 w-11",
|
|
5726
6449
|
size: "sm",
|
|
5727
6450
|
children: /* @__PURE__ */ jsx(
|
|
5728
6451
|
ReloadIcon,
|
|
@@ -5750,7 +6473,7 @@ const SelectTokenButton = ({
|
|
|
5750
6473
|
onClick,
|
|
5751
6474
|
size: "sm",
|
|
5752
6475
|
variant: "secondary",
|
|
5753
|
-
className: "shrink-0 gap-2
|
|
6476
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
5754
6477
|
type: "button",
|
|
5755
6478
|
"aria-label": label,
|
|
5756
6479
|
children: [
|
|
@@ -5798,7 +6521,7 @@ const Toolbar = () => {
|
|
|
5798
6521
|
{
|
|
5799
6522
|
onClick: onOpenSettings,
|
|
5800
6523
|
size: "sm",
|
|
5801
|
-
className: "
|
|
6524
|
+
className: "h-9 w-11",
|
|
5802
6525
|
variant: "secondary",
|
|
5803
6526
|
children: /* @__PURE__ */ jsx(BoltIcon, { stroke: "currentColor" })
|
|
5804
6527
|
}
|
|
@@ -5816,7 +6539,7 @@ const Toolbar = () => {
|
|
|
5816
6539
|
}
|
|
5817
6540
|
}
|
|
5818
6541
|
),
|
|
5819
|
-
/* @__PURE__ */ jsx(
|
|
6542
|
+
/* @__PURE__ */ jsx(SettingsModal, { isOpen: isOpenSettings, onClose: onCloseSettings })
|
|
5820
6543
|
] });
|
|
5821
6544
|
};
|
|
5822
6545
|
const EvaaBridgeWithProviders = (props) => {
|
|
@@ -6079,10 +6802,16 @@ export {
|
|
|
6079
6802
|
EvaaBridge,
|
|
6080
6803
|
RoutePriority,
|
|
6081
6804
|
RouteType,
|
|
6805
|
+
addEvmNetworkFee,
|
|
6806
|
+
addTonNetworkFee,
|
|
6807
|
+
addTronNetworkFee,
|
|
6082
6808
|
addrForApi,
|
|
6083
6809
|
buildAssetMatrix,
|
|
6084
6810
|
calculateMinReceived,
|
|
6085
6811
|
computeFeesUsdFromArray,
|
|
6812
|
+
estimateEvmNetworkFee,
|
|
6813
|
+
estimateTonNetworkFee,
|
|
6814
|
+
estimateTronNetworkFee,
|
|
6086
6815
|
findNativeMeta,
|
|
6087
6816
|
formatAddress,
|
|
6088
6817
|
formatBalance,
|