@rash2x/bridge-widget 0.2.11 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -0
- package/README.md +103 -138
- package/dist/evaa-bridge.cjs +1419 -869
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +1421 -871
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +58 -4
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/assets/fonts/Gilroy-Black.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Bold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-ExtraBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Heavy.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Light.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Medium.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Regular.ttf +0 -0
- package/dist/assets/fonts/Gilroy-SemiBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Thin.ttf +0 -0
- package/dist/assets/fonts/Gilroy-UltraLight.ttf +0 -0
- package/dist/assets/fonts/fonts.css +0 -81
- package/dist/assets/fonts/hanson-bold.woff +0 -0
- package/dist/assets/fonts/hanson-bold.woff2 +0 -0
package/dist/evaa-bridge.cjs
CHANGED
|
@@ -34,6 +34,7 @@ const wagmi = require("wagmi");
|
|
|
34
34
|
const tronwalletAdapterReactHooks = require("@tronweb3/tronwallet-adapter-react-hooks");
|
|
35
35
|
const uiReact = require("@tonconnect/ui-react");
|
|
36
36
|
const core = require("@ton/core");
|
|
37
|
+
const ton = require("@ton/ton");
|
|
37
38
|
const reactQuery = require("@tanstack/react-query");
|
|
38
39
|
const utils = require("@/lib/utils");
|
|
39
40
|
const skeleton = require("@/components/ui/skeleton");
|
|
@@ -44,7 +45,6 @@ const lucideReact = require("lucide-react");
|
|
|
44
45
|
const framerMotion = require("framer-motion");
|
|
45
46
|
const accordion = require("@/components/ui/accordion");
|
|
46
47
|
const tooltip = require("@/components/ui/tooltip");
|
|
47
|
-
const ton = require("@ton/ton");
|
|
48
48
|
const sonner = require("sonner");
|
|
49
49
|
const reactDialog = require("@radix-ui/react-dialog");
|
|
50
50
|
const ethers = require("ethers");
|
|
@@ -53,8 +53,8 @@ const tronwalletAdapters = require("@tronweb3/tronwallet-adapters");
|
|
|
53
53
|
const card = require("@/components/ui/card");
|
|
54
54
|
const badge = require("@/components/ui/badge");
|
|
55
55
|
const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
|
|
56
|
-
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", "
|
|
57
|
-
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
|
|
56
|
+
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" };
|
|
57
|
+
const bridge$1 = { "max": "Max", "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will change source network", "willChangeSourceNetworkAndToken": "Will change source token", "noBalancesFound": "No balances found.", "noResults": "No results", "sendToAnotherAddress": "Send to another address", "youWillReceive": "You will receive", "anotherAddressPlaceholder": "Address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
|
|
58
58
|
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" };
|
|
59
59
|
const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
|
|
60
60
|
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" } };
|
|
@@ -69,8 +69,8 @@ const en = {
|
|
|
69
69
|
errors: errors$1
|
|
70
70
|
};
|
|
71
71
|
const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
|
|
72
|
-
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", "
|
|
73
|
-
const bridge = { "max": "Макс", "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
|
|
72
|
+
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": "Подключить кошелёк" };
|
|
73
|
+
const bridge = { "max": "Макс", "sourceNetwork": "Исходная сеть", "destinationNetwork": "Целевая сеть", "selectToken": "Выбрать токен", "selectNetwork": "Выбрать сеть", "searchToken": "Поиск токена", "searchDestinationChain": "Поиск целевой сети", "myTokens": "Мои токены", "allTokens": "Все токены", "willChangeSourceChain": "Сменит исходную сеть", "willChangeSourceNetworkAndToken": "Сменит исходный токен", "noBalancesFound": "Балансы не найдены.", "noResults": "Нет результатов", "sendToAnotherAddress": "Отправить на другой адрес", "youWillReceive": "Вы получите", "anotherAddressPlaceholder": "Адрес", "addressDoesntMatch": "Адрес не соответствует сети {{network}}", "checkBeforeTransfer": "Проверьте корректность перед переводом" };
|
|
74
74
|
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": "Ошибка котировки" };
|
|
75
75
|
const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
|
|
76
76
|
const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
|
|
@@ -121,72 +121,282 @@ function BridgeI18nProvider({
|
|
|
121
121
|
function useBridgeTranslation() {
|
|
122
122
|
return reactI18next.useTranslation("evaa-bridge", { i18n: bridgeI18n });
|
|
123
123
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
124
|
+
function createJSONStorage(getStorage, options) {
|
|
125
|
+
let storage;
|
|
126
|
+
try {
|
|
127
|
+
storage = getStorage();
|
|
128
|
+
} catch (e) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const persistStorage = {
|
|
132
|
+
getItem: (name) => {
|
|
133
|
+
var _a;
|
|
134
|
+
const parse = (str2) => {
|
|
135
|
+
if (str2 === null) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return JSON.parse(str2, void 0);
|
|
139
|
+
};
|
|
140
|
+
const str = (_a = storage.getItem(name)) != null ? _a : null;
|
|
141
|
+
if (str instanceof Promise) {
|
|
142
|
+
return str.then(parse);
|
|
139
143
|
}
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!prev || !data) return false;
|
|
160
|
-
if (prev.length !== data.length) return false;
|
|
161
|
-
for (let i = 0; i < prev.length; i++) {
|
|
162
|
-
if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
|
|
144
|
+
return parse(str);
|
|
145
|
+
},
|
|
146
|
+
setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, void 0)),
|
|
147
|
+
removeItem: (name) => storage.removeItem(name)
|
|
148
|
+
};
|
|
149
|
+
return persistStorage;
|
|
150
|
+
}
|
|
151
|
+
const toThenable = (fn) => (input2) => {
|
|
152
|
+
try {
|
|
153
|
+
const result = fn(input2);
|
|
154
|
+
if (result instanceof Promise) {
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
then(onFulfilled) {
|
|
159
|
+
return toThenable(onFulfilled)(result);
|
|
160
|
+
},
|
|
161
|
+
catch(_onRejected) {
|
|
162
|
+
return this;
|
|
163
163
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (!prev && !data) return true;
|
|
173
|
-
if (!prev || !data) return false;
|
|
174
|
-
if (prev.length !== data.length) return false;
|
|
175
|
-
for (let i = 0; i < prev.length; i++) {
|
|
176
|
-
if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
|
|
164
|
+
};
|
|
165
|
+
} catch (e) {
|
|
166
|
+
return {
|
|
167
|
+
then(_onFulfilled) {
|
|
168
|
+
return this;
|
|
169
|
+
},
|
|
170
|
+
catch(onRejected) {
|
|
171
|
+
return toThenable(onRejected)(e);
|
|
177
172
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const persistImpl = (config, baseOptions) => (set, get, api) => {
|
|
177
|
+
let options = {
|
|
178
|
+
storage: createJSONStorage(() => localStorage),
|
|
179
|
+
partialize: (state) => state,
|
|
180
|
+
version: 0,
|
|
181
|
+
merge: (persistedState, currentState) => ({
|
|
182
|
+
...currentState,
|
|
183
|
+
...persistedState
|
|
184
|
+
}),
|
|
185
|
+
...baseOptions
|
|
186
|
+
};
|
|
187
|
+
let hasHydrated = false;
|
|
188
|
+
const hydrationListeners = /* @__PURE__ */ new Set();
|
|
189
|
+
const finishHydrationListeners = /* @__PURE__ */ new Set();
|
|
190
|
+
let storage = options.storage;
|
|
191
|
+
if (!storage) {
|
|
192
|
+
return config(
|
|
193
|
+
(...args) => {
|
|
194
|
+
console.warn(
|
|
195
|
+
`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
|
|
196
|
+
);
|
|
197
|
+
set(...args);
|
|
198
|
+
},
|
|
199
|
+
get,
|
|
200
|
+
api
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const setItem = () => {
|
|
204
|
+
const state = options.partialize({ ...get() });
|
|
205
|
+
return storage.setItem(options.name, {
|
|
206
|
+
state,
|
|
207
|
+
version: options.version
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
const savedSetState = api.setState;
|
|
211
|
+
api.setState = (state, replace) => {
|
|
212
|
+
savedSetState(state, replace);
|
|
213
|
+
return setItem();
|
|
214
|
+
};
|
|
215
|
+
const configResult = config(
|
|
216
|
+
(...args) => {
|
|
217
|
+
set(...args);
|
|
218
|
+
return setItem();
|
|
219
|
+
},
|
|
220
|
+
get,
|
|
221
|
+
api
|
|
222
|
+
);
|
|
223
|
+
api.getInitialState = () => configResult;
|
|
224
|
+
let stateFromStorage;
|
|
225
|
+
const hydrate = () => {
|
|
226
|
+
var _a, _b;
|
|
227
|
+
if (!storage) return;
|
|
228
|
+
hasHydrated = false;
|
|
229
|
+
hydrationListeners.forEach((cb) => {
|
|
230
|
+
var _a2;
|
|
231
|
+
return cb((_a2 = get()) != null ? _a2 : configResult);
|
|
232
|
+
});
|
|
233
|
+
const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;
|
|
234
|
+
return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {
|
|
235
|
+
if (deserializedStorageValue) {
|
|
236
|
+
if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) {
|
|
237
|
+
if (options.migrate) {
|
|
238
|
+
const migration = options.migrate(
|
|
239
|
+
deserializedStorageValue.state,
|
|
240
|
+
deserializedStorageValue.version
|
|
241
|
+
);
|
|
242
|
+
if (migration instanceof Promise) {
|
|
243
|
+
return migration.then((result) => [true, result]);
|
|
244
|
+
}
|
|
245
|
+
return [true, migration];
|
|
246
|
+
}
|
|
247
|
+
console.error(
|
|
248
|
+
`State loaded from storage couldn't be migrated since no migrate function was provided`
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
return [false, deserializedStorageValue.state];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return [false, void 0];
|
|
255
|
+
}).then((migrationResult) => {
|
|
256
|
+
var _a2;
|
|
257
|
+
const [migrated, migratedState] = migrationResult;
|
|
258
|
+
stateFromStorage = options.merge(
|
|
259
|
+
migratedState,
|
|
260
|
+
(_a2 = get()) != null ? _a2 : configResult
|
|
261
|
+
);
|
|
262
|
+
set(stateFromStorage, true);
|
|
263
|
+
if (migrated) {
|
|
264
|
+
return setItem();
|
|
265
|
+
}
|
|
266
|
+
}).then(() => {
|
|
267
|
+
postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);
|
|
268
|
+
stateFromStorage = get();
|
|
269
|
+
hasHydrated = true;
|
|
270
|
+
finishHydrationListeners.forEach((cb) => cb(stateFromStorage));
|
|
271
|
+
}).catch((e) => {
|
|
272
|
+
postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
api.persist = {
|
|
276
|
+
setOptions: (newOptions) => {
|
|
277
|
+
options = {
|
|
278
|
+
...options,
|
|
279
|
+
...newOptions
|
|
280
|
+
};
|
|
281
|
+
if (newOptions.storage) {
|
|
282
|
+
storage = newOptions.storage;
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
clearStorage: () => {
|
|
286
|
+
storage == null ? void 0 : storage.removeItem(options.name);
|
|
287
|
+
},
|
|
288
|
+
getOptions: () => options,
|
|
289
|
+
rehydrate: () => hydrate(),
|
|
290
|
+
hasHydrated: () => hasHydrated,
|
|
291
|
+
onHydrate: (cb) => {
|
|
292
|
+
hydrationListeners.add(cb);
|
|
293
|
+
return () => {
|
|
294
|
+
hydrationListeners.delete(cb);
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
onFinishHydration: (cb) => {
|
|
298
|
+
finishHydrationListeners.add(cb);
|
|
299
|
+
return () => {
|
|
300
|
+
finishHydrationListeners.delete(cb);
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
if (!options.skipHydration) {
|
|
305
|
+
hydrate();
|
|
306
|
+
}
|
|
307
|
+
return stateFromStorage || configResult;
|
|
308
|
+
};
|
|
309
|
+
const persist = persistImpl;
|
|
310
|
+
const useChainsStore = zustand.create()(
|
|
311
|
+
persist(
|
|
312
|
+
(set, get) => ({
|
|
313
|
+
chains: void 0,
|
|
314
|
+
fromChain: void 0,
|
|
315
|
+
toChain: void 0,
|
|
316
|
+
allowedFromChains: void 0,
|
|
317
|
+
allowedToChains: void 0,
|
|
318
|
+
isLoadingToChains: false,
|
|
319
|
+
setChains: async (data) => {
|
|
320
|
+
const prev = get().chains;
|
|
321
|
+
const same = (() => {
|
|
322
|
+
if (!prev && !data) return true;
|
|
323
|
+
if (!prev || !data) return false;
|
|
324
|
+
if (prev.length !== data.length) return false;
|
|
325
|
+
for (let i = 0; i < prev.length; i++) {
|
|
326
|
+
if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
})();
|
|
330
|
+
if (same) return;
|
|
331
|
+
set({ chains: data });
|
|
332
|
+
},
|
|
333
|
+
setFromChain: (data) => {
|
|
334
|
+
const prev = get().fromChain;
|
|
335
|
+
if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
|
|
336
|
+
set({ fromChain: data });
|
|
337
|
+
},
|
|
338
|
+
setToChain: (data) => {
|
|
339
|
+
const prev = get().toChain;
|
|
340
|
+
if ((prev?.chainKey ?? null) === (data?.chainKey ?? null)) return;
|
|
341
|
+
set({ toChain: data });
|
|
342
|
+
},
|
|
343
|
+
setAllowedFromChains: (data) => {
|
|
344
|
+
const prev = get().allowedFromChains;
|
|
345
|
+
const same = (() => {
|
|
346
|
+
if (!prev && !data) return true;
|
|
347
|
+
if (!prev || !data) return false;
|
|
348
|
+
if (prev.length !== data.length) return false;
|
|
349
|
+
for (let i = 0; i < prev.length; i++) {
|
|
350
|
+
if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
})();
|
|
354
|
+
if (same) return;
|
|
355
|
+
set({ allowedFromChains: data });
|
|
356
|
+
},
|
|
357
|
+
setAllowedToChains: (data) => {
|
|
358
|
+
const prev = get().allowedToChains;
|
|
359
|
+
const same = (() => {
|
|
360
|
+
if (!prev && !data) return true;
|
|
361
|
+
if (!prev || !data) return false;
|
|
362
|
+
if (prev.length !== data.length) return false;
|
|
363
|
+
for (let i = 0; i < prev.length; i++) {
|
|
364
|
+
if (prev[i]?.chainKey !== data[i]?.chainKey) return false;
|
|
365
|
+
}
|
|
366
|
+
return true;
|
|
367
|
+
})();
|
|
368
|
+
if (same) return;
|
|
369
|
+
set({ allowedToChains: data });
|
|
370
|
+
},
|
|
371
|
+
setIsLoadingToChains: (v) => set({ isLoadingToChains: v }),
|
|
372
|
+
swapChains: () => set((state) => ({
|
|
373
|
+
fromChain: state.toChain,
|
|
374
|
+
toChain: state.fromChain
|
|
375
|
+
}))
|
|
376
|
+
}),
|
|
377
|
+
{
|
|
378
|
+
name: "evaa-bridge-chains",
|
|
379
|
+
storage: createJSONStorage(() => sessionStorage),
|
|
380
|
+
partialize: (state) => ({
|
|
381
|
+
fromChain: state.fromChain,
|
|
382
|
+
toChain: state.toChain
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
function normalizeTickerSymbol$1(s) {
|
|
388
|
+
return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
389
|
+
}
|
|
390
|
+
function normalizeTokenSymbol(token) {
|
|
391
|
+
let normalizedSymbol = token.symbol.toUpperCase().replace(/₮/g, "T");
|
|
392
|
+
normalizedSymbol = normalizedSymbol.replace(/^S\*/, "");
|
|
393
|
+
normalizedSymbol = normalizedSymbol.replace(/0\.S$/i, "").replace(/0$/, "").replace(/\.S$/i, "");
|
|
394
|
+
normalizedSymbol = normalizedSymbol.replace(/\.E$/i, "").replace(/\.N$/i, "");
|
|
395
|
+
return {
|
|
396
|
+
...token,
|
|
397
|
+
symbol: normalizedSymbol
|
|
398
|
+
};
|
|
399
|
+
}
|
|
190
400
|
const POPULAR_ORDER = [
|
|
191
401
|
"USDT",
|
|
192
402
|
"USDC",
|
|
@@ -222,6 +432,16 @@ function buildAssetMatrix(tokens) {
|
|
|
222
432
|
if (!symbol) continue;
|
|
223
433
|
(m[symbol] || (m[symbol] = {}))[t.chainKey] = t;
|
|
224
434
|
}
|
|
435
|
+
const chainStats = {};
|
|
436
|
+
Object.values(m).forEach((byChain) => {
|
|
437
|
+
Object.keys(byChain).forEach((chainKey) => {
|
|
438
|
+
chainStats[chainKey] = (chainStats[chainKey] || 0) + 1;
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
console.log(
|
|
442
|
+
`[DEBUG] Asset matrix built: ${Object.keys(m).length} assets across chains:`,
|
|
443
|
+
chainStats
|
|
444
|
+
);
|
|
225
445
|
return m;
|
|
226
446
|
}
|
|
227
447
|
function listAssetsForSelect(tokens) {
|
|
@@ -231,11 +451,11 @@ function listAssetsForSelect(tokens) {
|
|
|
231
451
|
const preferred = pickOrder.map((k) => byChain[k]).find(Boolean) ?? Object.values(byChain)[0];
|
|
232
452
|
return preferred;
|
|
233
453
|
});
|
|
234
|
-
const byNorm = new Map(base.map((e) => [
|
|
454
|
+
const byNorm = new Map(base.map((e) => [normalizeTickerSymbol$1(e.symbol), e]));
|
|
235
455
|
const used = /* @__PURE__ */ new Set();
|
|
236
456
|
const popular = [];
|
|
237
457
|
for (const s of POPULAR_ORDER) {
|
|
238
|
-
const key =
|
|
458
|
+
const key = normalizeTickerSymbol$1(s);
|
|
239
459
|
const item = byNorm.get(key);
|
|
240
460
|
if (item && !used.has(key)) {
|
|
241
461
|
popular.push(item);
|
|
@@ -243,11 +463,11 @@ function listAssetsForSelect(tokens) {
|
|
|
243
463
|
}
|
|
244
464
|
}
|
|
245
465
|
const stable = base.filter((e) => {
|
|
246
|
-
const k =
|
|
466
|
+
const k = normalizeTickerSymbol$1(e.symbol);
|
|
247
467
|
return !used.has(k) && STABLES.has(k);
|
|
248
468
|
}).sort((a, b) => a.symbol.localeCompare(b.symbol));
|
|
249
|
-
for (const e of stable) used.add(
|
|
250
|
-
const other = base.filter((e) => !used.has(
|
|
469
|
+
for (const e of stable) used.add(normalizeTickerSymbol$1(e.symbol));
|
|
470
|
+
const other = base.filter((e) => !used.has(normalizeTickerSymbol$1(e.symbol))).sort((a, b) => a.symbol.localeCompare(b.symbol));
|
|
251
471
|
return [...popular, ...stable, ...other];
|
|
252
472
|
}
|
|
253
473
|
function resolveTokenOnChain(tokens, assetSymbol, chainKey) {
|
|
@@ -652,163 +872,636 @@ function isAddressValidForChain(chainKey, addr) {
|
|
|
652
872
|
if (chainKey === "tron") return isTronAddress(addr);
|
|
653
873
|
return isEvmAddress(addr);
|
|
654
874
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
refetchTrigger,
|
|
663
|
-
setLoading: setQuoteLoading,
|
|
664
|
-
setQuote,
|
|
665
|
-
setError: setQError,
|
|
666
|
-
setNoRoute,
|
|
667
|
-
resetWithIdle
|
|
668
|
-
} = useBridgeQuoteStore();
|
|
669
|
-
const input2 = inputAmount;
|
|
670
|
-
const srcTokenOnFrom = react.useMemo(
|
|
671
|
-
() => resolveTokenOnChainFromMatrix$2(
|
|
672
|
-
assetMatrix,
|
|
673
|
-
selectedAssetSymbol,
|
|
674
|
-
fromChain?.chainKey
|
|
675
|
-
),
|
|
676
|
-
[assetMatrix, selectedAssetSymbol, fromChain?.chainKey]
|
|
677
|
-
);
|
|
678
|
-
const dstTokenOnTo = react.useMemo(
|
|
679
|
-
() => resolveTokenOnChainFromMatrix$2(
|
|
680
|
-
assetMatrix,
|
|
681
|
-
selectedAssetSymbol,
|
|
682
|
-
toChain?.chainKey
|
|
683
|
-
),
|
|
684
|
-
[assetMatrix, selectedAssetSymbol, toChain?.chainKey]
|
|
685
|
-
);
|
|
686
|
-
const [loading, setLoading] = react.useState(false);
|
|
687
|
-
react.useEffect(() => {
|
|
688
|
-
if (!input2 || input2 === "") {
|
|
689
|
-
setLoading(false);
|
|
690
|
-
resetWithIdle();
|
|
691
|
-
}
|
|
692
|
-
}, [input2, resetWithIdle]);
|
|
693
|
-
react.useEffect(() => {
|
|
694
|
-
const resetUi = (withError) => {
|
|
695
|
-
setLoading(false);
|
|
696
|
-
{
|
|
697
|
-
resetWithIdle();
|
|
698
|
-
}
|
|
699
|
-
};
|
|
700
|
-
if (!input2 || Number(input2) <= 0 || !fromChain?.chainKey || !toChain?.chainKey || !srcTokenOnFrom || !dstTokenOnTo || !srcAddress || !dstAddress) {
|
|
701
|
-
resetUi();
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
if (!isAddressValidForChain(fromChain.chainKey, srcAddress) || !isAddressValidForChain(toChain.chainKey, dstAddress)) {
|
|
705
|
-
resetUi();
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
if (fromChain.chainKey === toChain.chainKey) {
|
|
709
|
-
resetUi();
|
|
710
|
-
return;
|
|
711
|
-
}
|
|
712
|
-
let cancelled = false;
|
|
713
|
-
const run = async () => {
|
|
714
|
-
try {
|
|
715
|
-
setLoading(true);
|
|
716
|
-
setQuoteLoading?.();
|
|
717
|
-
if (assetMatrix && selectedAssetSymbol) {
|
|
718
|
-
const tokenMatrix = assetMatrix[selectedAssetSymbol.toUpperCase()];
|
|
719
|
-
if (!tokenMatrix || !tokenMatrix[fromChain.chainKey] || !tokenMatrix[toChain.chainKey]) {
|
|
720
|
-
if (!cancelled) {
|
|
721
|
-
resetUi();
|
|
722
|
-
}
|
|
723
|
-
return;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
const srcAmountLD = toLD(input2, srcTokenOnFrom.decimals);
|
|
727
|
-
const srcAddrApi = addrForApi(fromChain.chainKey, srcAddress);
|
|
728
|
-
const dstAddrApi = addrForApi(toChain.chainKey, dstAddress);
|
|
729
|
-
const dstNativeAmount = getDstNativeAmount(toChain.chainKey);
|
|
730
|
-
const slippageDecimal = getSlippageDecimal();
|
|
731
|
-
const approximateMinLD = BigInt(srcAmountLD) * BigInt(9500) / BigInt(1e4);
|
|
732
|
-
const quoteRoute = await getQuotesByPriority({
|
|
733
|
-
srcChainKey: fromChain.chainKey,
|
|
734
|
-
dstChainKey: toChain.chainKey,
|
|
735
|
-
srcToken: srcTokenOnFrom.address,
|
|
736
|
-
dstToken: dstTokenOnTo.address,
|
|
737
|
-
srcAmountLD,
|
|
738
|
-
dstAmountMinLD: approximateMinLD.toString(),
|
|
739
|
-
srcAddress: srcAddrApi,
|
|
740
|
-
dstAddress: dstAddrApi,
|
|
741
|
-
dstNativeAmount,
|
|
742
|
-
slippage: slippageDecimal,
|
|
743
|
-
routePriority
|
|
744
|
-
});
|
|
745
|
-
if (quoteRoute?.route === null) {
|
|
746
|
-
setNoRoute(true);
|
|
747
|
-
if (!cancelled) resetUi();
|
|
748
|
-
return;
|
|
749
|
-
} else {
|
|
750
|
-
setNoRoute(false);
|
|
751
|
-
}
|
|
752
|
-
if (!quoteRoute || !quoteRoute.dstAmount || BigInt(quoteRoute.dstAmount) === BigInt(0)) {
|
|
753
|
-
if (!cancelled) resetUi();
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
756
|
-
if (cancelled) return;
|
|
757
|
-
setQuote(quoteRoute);
|
|
758
|
-
} catch {
|
|
759
|
-
if (!cancelled) {
|
|
760
|
-
resetUi();
|
|
761
|
-
}
|
|
762
|
-
} finally {
|
|
763
|
-
if (!cancelled) setLoading(false);
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
run();
|
|
767
|
-
return () => {
|
|
768
|
-
cancelled = true;
|
|
769
|
-
};
|
|
770
|
-
}, [
|
|
771
|
-
input2,
|
|
772
|
-
fromChain?.chainKey,
|
|
773
|
-
toChain?.chainKey,
|
|
774
|
-
srcTokenOnFrom,
|
|
775
|
-
dstTokenOnTo,
|
|
776
|
-
srcAddress,
|
|
777
|
-
dstAddress,
|
|
778
|
-
setQuoteLoading,
|
|
779
|
-
setQuote,
|
|
780
|
-
setQError,
|
|
781
|
-
slippageBps,
|
|
782
|
-
routePriority,
|
|
783
|
-
refetchTrigger
|
|
784
|
-
]);
|
|
785
|
-
return { loading };
|
|
786
|
-
}
|
|
787
|
-
async function getChains() {
|
|
788
|
-
const res = await fetch("https://stargate.finance/api/v1/chains", {
|
|
789
|
-
credentials: "same-origin"
|
|
790
|
-
});
|
|
791
|
-
if (!res.ok) {
|
|
792
|
-
throw new Error(`Failed to load chains: ${res.status}`);
|
|
875
|
+
const ChainStrategyContext = react.createContext(void 0);
|
|
876
|
+
function useChainStrategies() {
|
|
877
|
+
const context = react.useContext(ChainStrategyContext);
|
|
878
|
+
if (!context) {
|
|
879
|
+
throw new Error(
|
|
880
|
+
"useChainStrategies must be used within ChainStrategyProvider"
|
|
881
|
+
);
|
|
793
882
|
}
|
|
794
|
-
|
|
795
|
-
return Array.isArray(data) ? data : data.chains ?? [];
|
|
883
|
+
return context;
|
|
796
884
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
885
|
+
const truncateToDecimals = (num, decimals) => {
|
|
886
|
+
if (!isFinite(num) || isNaN(num)) return "0.00";
|
|
887
|
+
const multiplier = Math.pow(10, decimals);
|
|
888
|
+
const truncated = Math.floor(num * multiplier) / multiplier;
|
|
889
|
+
return truncated.toFixed(decimals);
|
|
890
|
+
};
|
|
891
|
+
const formatTokenAmount = (amount, symbol, options) => {
|
|
892
|
+
const normalizedSymbol = (symbol ?? "").toUpperCase();
|
|
893
|
+
if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
|
|
894
|
+
return `${Math.floor(amount)} ${normalizedSymbol}`;
|
|
803
895
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
896
|
+
if (options?.decimals !== void 0) {
|
|
897
|
+
return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
|
|
898
|
+
}
|
|
899
|
+
if (amount >= 1) {
|
|
900
|
+
return `${amount.toFixed(0)} ${normalizedSymbol}`;
|
|
901
|
+
} else if (amount >= 1e-3) {
|
|
902
|
+
return `${amount.toFixed(3)} ${normalizedSymbol}`;
|
|
903
|
+
} else {
|
|
904
|
+
return `${amount.toFixed(6)} ${normalizedSymbol}`;
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
const formatUsd = (value) => {
|
|
908
|
+
if (!value || !isFinite(value)) return "$0";
|
|
909
|
+
if (value >= 1) return `$${value.toFixed(2)}`;
|
|
910
|
+
return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
|
|
911
|
+
};
|
|
912
|
+
const formatPercentage = (bps, decimals = 2) => {
|
|
913
|
+
return `${(bps / 100).toFixed(decimals)}%`;
|
|
914
|
+
};
|
|
915
|
+
const formatBalance = (amount, decimals = 2) => {
|
|
916
|
+
if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
|
|
917
|
+
return "0.00";
|
|
918
|
+
}
|
|
919
|
+
return amount.toFixed(decimals);
|
|
920
|
+
};
|
|
921
|
+
const formatHash = (hash, startChars = 4, endChars = 4) => {
|
|
922
|
+
if (!hash) return "";
|
|
923
|
+
if (hash.length <= startChars + endChars) return hash;
|
|
924
|
+
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
925
|
+
};
|
|
926
|
+
const formatAddress = formatHash;
|
|
927
|
+
const EVM_CONFIG = {
|
|
928
|
+
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
929
|
+
gasEstimates: {
|
|
930
|
+
approve: 65000n,
|
|
931
|
+
bridge: 300000n
|
|
932
|
+
},
|
|
933
|
+
gasBuffer: 1.2,
|
|
934
|
+
// 20% buffer
|
|
935
|
+
timeout: 3e5,
|
|
936
|
+
// 5 minutes (increased for slower networks)
|
|
937
|
+
requiredConfirmations: 3
|
|
938
|
+
// Wait for 3 confirmations for reorg protection
|
|
939
|
+
};
|
|
940
|
+
const TON_CONFIG = {
|
|
941
|
+
apiUrl: "https://toncenter.com/api/v2",
|
|
942
|
+
timeout: 36e4,
|
|
943
|
+
// 6 minutes
|
|
944
|
+
validUntil: 600,
|
|
945
|
+
// 10 minutes
|
|
946
|
+
pollingInterval: 5e3,
|
|
947
|
+
// 5 seconds between transaction status checks
|
|
948
|
+
estimatedNetworkFee: "100000000"
|
|
949
|
+
// 0.1 TON in nanoton (conservative estimate)
|
|
950
|
+
};
|
|
951
|
+
const TRON_CONFIG = {
|
|
952
|
+
timeout: 12e4,
|
|
953
|
+
// 2 minutes (for 19 confirmations)
|
|
954
|
+
feeLimit: 1e8,
|
|
955
|
+
// 100 TRX in sun
|
|
956
|
+
requiredConfirmations: 19,
|
|
957
|
+
// TRON standard: 19 blocks for confirmation
|
|
958
|
+
pollingInterval: 3e3,
|
|
959
|
+
// 3 seconds between checks
|
|
960
|
+
estimatedNetworkFee: "10000000"
|
|
961
|
+
// 10 TRX in SUN (fallback estimate)
|
|
962
|
+
};
|
|
963
|
+
let tonClientInstance = null;
|
|
964
|
+
function getTonClient(customClient, apiKey) {
|
|
965
|
+
if (customClient) {
|
|
966
|
+
return customClient;
|
|
967
|
+
}
|
|
968
|
+
if (!tonClientInstance) {
|
|
969
|
+
tonClientInstance = new ton.TonClient({
|
|
970
|
+
endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
|
|
971
|
+
apiKey
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
return tonClientInstance;
|
|
975
|
+
}
|
|
976
|
+
function getQuoteAmounts(quote, srcToken, dstToken) {
|
|
977
|
+
if (!quote || !srcToken || !dstToken) {
|
|
978
|
+
return {
|
|
979
|
+
inputHuman: 0,
|
|
980
|
+
outputHuman: 0,
|
|
981
|
+
outputHumanRounded: "0",
|
|
982
|
+
minReceivedHuman: 0
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
|
|
986
|
+
const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
|
|
987
|
+
const outputHumanRounded = truncateToDecimals(outputHuman, 2);
|
|
988
|
+
const minReceivedHuman = fromLD(
|
|
989
|
+
quote.dstAmountMin || "0",
|
|
990
|
+
dstToken.decimals
|
|
991
|
+
);
|
|
992
|
+
return {
|
|
993
|
+
inputHuman,
|
|
994
|
+
outputHuman,
|
|
995
|
+
outputHumanRounded,
|
|
996
|
+
minReceivedHuman
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
|
|
1000
|
+
if (!quote || !tokens || !chains) {
|
|
1001
|
+
return {
|
|
1002
|
+
totalUsd: 0,
|
|
1003
|
+
protocolFeeUsd: void 0,
|
|
1004
|
+
messageFeeUsd: void 0,
|
|
1005
|
+
serviceUsd: void 0,
|
|
1006
|
+
blockchainUsd: void 0,
|
|
1007
|
+
inSrcToken: void 0,
|
|
1008
|
+
inDstToken: void 0
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
|
|
1012
|
+
let inSrcToken = void 0;
|
|
1013
|
+
let inDstToken = void 0;
|
|
1014
|
+
if (srcToken && quote.srcChainKey) {
|
|
1015
|
+
const feeInSrcLD = sumFeeByTokenLD(
|
|
1016
|
+
quote.fees,
|
|
1017
|
+
srcToken.address,
|
|
1018
|
+
quote.srcChainKey
|
|
1019
|
+
);
|
|
1020
|
+
const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
|
|
1021
|
+
if (feeInSrcHuman > 0) {
|
|
1022
|
+
inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
|
|
1023
|
+
} else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
|
|
1024
|
+
const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
|
|
1025
|
+
inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (dstToken && quote.dstChainKey) {
|
|
1029
|
+
const feeInDstLD = sumFeeByTokenLD(
|
|
1030
|
+
quote.fees,
|
|
1031
|
+
dstToken.address,
|
|
1032
|
+
quote.dstChainKey
|
|
1033
|
+
);
|
|
1034
|
+
const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
|
|
1035
|
+
if (feeInDstHuman > 0) {
|
|
1036
|
+
inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return {
|
|
1040
|
+
totalUsd: feeData.totalUsd,
|
|
1041
|
+
protocolFeeUsd: feeData.protocolFeeUsd,
|
|
1042
|
+
messageFeeUsd: feeData.messageFeeUsd,
|
|
1043
|
+
serviceUsd: feeData.serviceUsd,
|
|
1044
|
+
blockchainUsd: feeData.blockchainUsd,
|
|
1045
|
+
inSrcToken,
|
|
1046
|
+
inDstToken
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
function calculateMinReceived(quote, slippageBps, dstToken) {
|
|
1050
|
+
if (!quote || !dstToken) return 0;
|
|
1051
|
+
const dstAmountLD = BigInt(quote.dstAmount);
|
|
1052
|
+
const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
|
|
1053
|
+
return fromLD(minAmountLD.toString(), dstToken.decimals);
|
|
1054
|
+
}
|
|
1055
|
+
function estimateTonNetworkFee(quote) {
|
|
1056
|
+
try {
|
|
1057
|
+
const tonStep = quote.steps?.find(
|
|
1058
|
+
(s) => s.chainKey.toLowerCase() === "ton" && s.type === "bridge"
|
|
1059
|
+
);
|
|
1060
|
+
if (!tonStep?.transaction?.messages) {
|
|
1061
|
+
console.warn("No TON messages found in quote, using fallback");
|
|
1062
|
+
return TON_CONFIG.estimatedNetworkFee;
|
|
1063
|
+
}
|
|
1064
|
+
const messages = tonStep.transaction.messages;
|
|
1065
|
+
const messageCount = messages.length;
|
|
1066
|
+
const baseFeePerMessage = 10000000n;
|
|
1067
|
+
let totalFee = 0n;
|
|
1068
|
+
for (const message of messages) {
|
|
1069
|
+
let messageFee = baseFeePerMessage;
|
|
1070
|
+
if (message.payload) {
|
|
1071
|
+
const payloadSize = message.payload.length;
|
|
1072
|
+
const sizeFee = BigInt(Math.floor(payloadSize / 100)) * 100000n;
|
|
1073
|
+
messageFee += sizeFee;
|
|
1074
|
+
}
|
|
1075
|
+
if (message.amount) {
|
|
1076
|
+
const amount = BigInt(message.amount);
|
|
1077
|
+
if (amount > 0n && message.payload) {
|
|
1078
|
+
messageFee += 5000000n;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
totalFee += messageFee;
|
|
1082
|
+
}
|
|
1083
|
+
const minBuffer = 50000000n;
|
|
1084
|
+
totalFee = totalFee > minBuffer ? totalFee : minBuffer;
|
|
1085
|
+
totalFee = totalFee * 120n / 100n;
|
|
1086
|
+
const maxFee = 1000000000n;
|
|
1087
|
+
if (totalFee > maxFee) {
|
|
1088
|
+
totalFee = maxFee;
|
|
1089
|
+
}
|
|
1090
|
+
console.log(
|
|
1091
|
+
`TON fee estimate: ${messageCount} messages = ${Number(totalFee) / 1e9} TON`
|
|
1092
|
+
);
|
|
1093
|
+
return totalFee.toString();
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
console.warn("Failed to estimate TON fee:", error);
|
|
1096
|
+
return TON_CONFIG.estimatedNetworkFee;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function addTonNetworkFee(quote, chains, estimatedFee) {
|
|
1100
|
+
if (!quote || quote.srcChainKey.toLowerCase() !== "ton") {
|
|
1101
|
+
return quote;
|
|
1102
|
+
}
|
|
1103
|
+
const tonChain = chains?.find(
|
|
1104
|
+
(c) => c.chainKey.toLowerCase() === "ton"
|
|
1105
|
+
);
|
|
1106
|
+
if (!tonChain?.nativeCurrency?.address) {
|
|
1107
|
+
console.warn("Could not find TON native currency address");
|
|
1108
|
+
return quote;
|
|
1109
|
+
}
|
|
1110
|
+
const networkFee = {
|
|
1111
|
+
token: tonChain.nativeCurrency.address,
|
|
1112
|
+
chainKey: "ton",
|
|
1113
|
+
amount: estimatedFee || TON_CONFIG.estimatedNetworkFee,
|
|
1114
|
+
type: "network"
|
|
1115
|
+
};
|
|
1116
|
+
const hasNetworkFee = quote.fees?.some(
|
|
1117
|
+
(fee) => fee.type === "network" && fee.chainKey === "ton"
|
|
1118
|
+
);
|
|
1119
|
+
if (hasNetworkFee) {
|
|
1120
|
+
return quote;
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
...quote,
|
|
1124
|
+
fees: [...quote.fees || [], networkFee]
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
async function estimateTronNetworkFee(tronWeb, quote, userAddress) {
|
|
1128
|
+
try {
|
|
1129
|
+
const accountResources = await tronWeb.trx.getAccountResources(userAddress);
|
|
1130
|
+
const availableEnergy = accountResources.EnergyLimit || 0;
|
|
1131
|
+
const tronStep = quote.steps?.find(
|
|
1132
|
+
(s) => s.chainKey.toLowerCase() === "tron" && s.type === "bridge"
|
|
1133
|
+
);
|
|
1134
|
+
if (!tronStep?.transaction?.data || !tronStep.transaction.to) {
|
|
1135
|
+
console.warn("No TRON transaction data in quote, using fallback");
|
|
1136
|
+
return TRON_CONFIG.estimatedNetworkFee;
|
|
1137
|
+
}
|
|
1138
|
+
const contractAddress = tronWeb.address.fromHex(
|
|
1139
|
+
tronStep.transaction.to.startsWith("41") ? tronStep.transaction.to : "41" + tronStep.transaction.to.slice(2)
|
|
1140
|
+
);
|
|
1141
|
+
const simulation = await tronWeb.transactionBuilder.triggerSmartContract(
|
|
1142
|
+
contractAddress,
|
|
1143
|
+
"function signature doesn't matter for energy estimation",
|
|
1144
|
+
{
|
|
1145
|
+
feeLimit: TRON_CONFIG.feeLimit,
|
|
1146
|
+
callValue: tronStep.transaction.value ? Number(tronStep.transaction.value) : 0
|
|
1147
|
+
},
|
|
1148
|
+
[],
|
|
1149
|
+
userAddress
|
|
1150
|
+
);
|
|
1151
|
+
const requiredEnergy = simulation.energy_used || 0;
|
|
1152
|
+
const energyDeficit = Math.max(0, requiredEnergy - availableEnergy);
|
|
1153
|
+
if (energyDeficit === 0) {
|
|
1154
|
+
return "0";
|
|
1155
|
+
}
|
|
1156
|
+
const energyPriceInSun = 420;
|
|
1157
|
+
const feeSun = energyDeficit * energyPriceInSun;
|
|
1158
|
+
console.log(
|
|
1159
|
+
`TRON fee estimation: ${requiredEnergy} energy required, ${availableEnergy} available, ${feeSun / 1e6} TRX fee`
|
|
1160
|
+
);
|
|
1161
|
+
return feeSun.toString();
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
console.warn("Failed to estimate TRON network fee:", error);
|
|
1164
|
+
return TRON_CONFIG.estimatedNetworkFee;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
function addTronNetworkFee(quote, chains, estimatedFee) {
|
|
1168
|
+
if (!quote || quote.srcChainKey.toLowerCase() !== "tron") {
|
|
1169
|
+
return quote;
|
|
1170
|
+
}
|
|
1171
|
+
const tronChain = chains?.find(
|
|
1172
|
+
(c) => c.chainKey.toLowerCase() === "tron"
|
|
1173
|
+
);
|
|
1174
|
+
if (!tronChain?.nativeCurrency?.address) {
|
|
1175
|
+
console.warn("Could not find TRON native currency address");
|
|
1176
|
+
return quote;
|
|
1177
|
+
}
|
|
1178
|
+
const networkFee = {
|
|
1179
|
+
token: tronChain.nativeCurrency.address,
|
|
1180
|
+
chainKey: "tron",
|
|
1181
|
+
amount: estimatedFee || TRON_CONFIG.estimatedNetworkFee,
|
|
1182
|
+
type: "network"
|
|
1183
|
+
};
|
|
1184
|
+
const hasNetworkFee = quote.fees?.some(
|
|
1185
|
+
(fee) => fee.type === "network" && fee.chainKey === "tron"
|
|
1186
|
+
);
|
|
1187
|
+
if (hasNetworkFee) {
|
|
1188
|
+
return quote;
|
|
1189
|
+
}
|
|
1190
|
+
return {
|
|
1191
|
+
...quote,
|
|
1192
|
+
fees: [...quote.fees || [], networkFee]
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
async function estimateEvmNetworkFee(publicClient, quote, chainKey) {
|
|
1196
|
+
try {
|
|
1197
|
+
let totalGasLimit = 0n;
|
|
1198
|
+
for (const step of quote.steps || []) {
|
|
1199
|
+
if (step.chainKey.toLowerCase() !== chainKey.toLowerCase()) continue;
|
|
1200
|
+
if (step.type === "approve") {
|
|
1201
|
+
totalGasLimit += EVM_CONFIG.gasEstimates.approve;
|
|
1202
|
+
} else if (step.type === "bridge") {
|
|
1203
|
+
totalGasLimit += EVM_CONFIG.gasEstimates.bridge;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
if (totalGasLimit === 0n) {
|
|
1207
|
+
return "0";
|
|
1208
|
+
}
|
|
1209
|
+
totalGasLimit = totalGasLimit * BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100)) / 100n;
|
|
1210
|
+
const gasPrice = await publicClient.getGasPrice();
|
|
1211
|
+
const totalCostWei = totalGasLimit * gasPrice;
|
|
1212
|
+
console.log(
|
|
1213
|
+
`EVM gas estimate: ${totalGasLimit} gas × ${gasPrice} Wei/gas = ${totalCostWei} Wei`
|
|
1214
|
+
);
|
|
1215
|
+
return totalCostWei.toString();
|
|
1216
|
+
} catch (error) {
|
|
1217
|
+
console.warn("Failed to estimate EVM gas fee:", error);
|
|
1218
|
+
return "10000000000000000";
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
function addEvmNetworkFee(quote, chains, estimatedFee) {
|
|
1222
|
+
if (!quote) return quote;
|
|
1223
|
+
const srcChainKey = quote.srcChainKey.toLowerCase();
|
|
1224
|
+
if (srcChainKey === "ton" || srcChainKey === "tron") {
|
|
1225
|
+
return quote;
|
|
1226
|
+
}
|
|
1227
|
+
const srcChain = chains?.find(
|
|
1228
|
+
(c) => c.chainKey.toLowerCase() === srcChainKey
|
|
1229
|
+
);
|
|
1230
|
+
if (!srcChain?.nativeCurrency?.address) {
|
|
1231
|
+
return quote;
|
|
1232
|
+
}
|
|
1233
|
+
const hasNetworkFee = quote.fees?.some(
|
|
1234
|
+
(fee) => fee.type === "network" && fee.chainKey.toLowerCase() === srcChainKey
|
|
1235
|
+
);
|
|
1236
|
+
if (hasNetworkFee) {
|
|
1237
|
+
return quote;
|
|
1238
|
+
}
|
|
1239
|
+
const networkFee = {
|
|
1240
|
+
token: srcChain.nativeCurrency.address,
|
|
1241
|
+
chainKey: quote.srcChainKey,
|
|
1242
|
+
amount: estimatedFee || "10000000000000000",
|
|
1243
|
+
// 0.01 ETH fallback
|
|
1244
|
+
type: "network"
|
|
1245
|
+
};
|
|
1246
|
+
return {
|
|
1247
|
+
...quote,
|
|
1248
|
+
fees: [...quote.fees || [], networkFee]
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
function getQuoteDetails(quote, srcToken, dstToken, tokens, chains, slippageBps) {
|
|
1252
|
+
const amounts = getQuoteAmounts(quote, srcToken, dstToken);
|
|
1253
|
+
const fees = getQuoteFees(quote, tokens, chains, srcToken, dstToken);
|
|
1254
|
+
const minimumReceived = calculateMinReceived(quote, slippageBps, dstToken);
|
|
1255
|
+
return {
|
|
1256
|
+
inputAmount: amounts.inputHuman,
|
|
1257
|
+
outputAmount: amounts.outputHuman,
|
|
1258
|
+
outputAmountRounded: amounts.outputHumanRounded,
|
|
1259
|
+
minimumReceived,
|
|
1260
|
+
etaSeconds: quote?.duration?.estimated,
|
|
1261
|
+
fees
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
function useBridgeQuote() {
|
|
1265
|
+
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
1266
|
+
const { fromChain, toChain, chains } = useChainsStore();
|
|
1267
|
+
const { srcAddress, dstAddress } = useAddresses();
|
|
1268
|
+
const { slippageBps, routePriority, getDstNativeAmount, getSlippageDecimal } = useSettingsStore();
|
|
1269
|
+
const {
|
|
1270
|
+
inputAmount,
|
|
1271
|
+
refetchTrigger,
|
|
1272
|
+
setLoading: setQuoteLoading,
|
|
1273
|
+
setQuote,
|
|
1274
|
+
setError: setQError,
|
|
1275
|
+
setNoRoute,
|
|
1276
|
+
resetWithIdle
|
|
1277
|
+
} = useBridgeQuoteStore();
|
|
1278
|
+
const { chainRegistry } = useChainStrategies();
|
|
1279
|
+
const publicClient = wagmi.usePublicClient();
|
|
1280
|
+
const input2 = inputAmount;
|
|
1281
|
+
const srcTokenOnFrom = react.useMemo(
|
|
1282
|
+
() => resolveTokenOnChainFromMatrix$2(
|
|
1283
|
+
assetMatrix,
|
|
1284
|
+
selectedAssetSymbol,
|
|
1285
|
+
fromChain?.chainKey
|
|
1286
|
+
),
|
|
1287
|
+
[assetMatrix, selectedAssetSymbol, fromChain?.chainKey]
|
|
1288
|
+
);
|
|
1289
|
+
const dstTokenOnTo = react.useMemo(
|
|
1290
|
+
() => resolveTokenOnChainFromMatrix$2(
|
|
1291
|
+
assetMatrix,
|
|
1292
|
+
selectedAssetSymbol,
|
|
1293
|
+
toChain?.chainKey
|
|
1294
|
+
),
|
|
1295
|
+
[assetMatrix, selectedAssetSymbol, toChain?.chainKey]
|
|
1296
|
+
);
|
|
1297
|
+
const [loading, setLoading] = react.useState(false);
|
|
1298
|
+
react.useEffect(() => {
|
|
1299
|
+
if (!input2 || input2 === "") {
|
|
1300
|
+
setLoading(false);
|
|
1301
|
+
resetWithIdle();
|
|
1302
|
+
}
|
|
1303
|
+
}, [input2, resetWithIdle]);
|
|
1304
|
+
react.useEffect(() => {
|
|
1305
|
+
const resetUi = (withError) => {
|
|
1306
|
+
setLoading(false);
|
|
1307
|
+
{
|
|
1308
|
+
resetWithIdle();
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
if (!input2 || Number(input2) <= 0 || !fromChain?.chainKey || !toChain?.chainKey || !srcTokenOnFrom || !dstTokenOnTo || !srcAddress || !dstAddress) {
|
|
1312
|
+
resetUi();
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (!isAddressValidForChain(fromChain.chainKey, srcAddress) || !isAddressValidForChain(toChain.chainKey, dstAddress)) {
|
|
1316
|
+
resetUi();
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (fromChain.chainKey === toChain.chainKey) {
|
|
1320
|
+
resetUi();
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
let cancelled = false;
|
|
1324
|
+
const run = async () => {
|
|
1325
|
+
try {
|
|
1326
|
+
setLoading(true);
|
|
1327
|
+
setQuoteLoading?.();
|
|
1328
|
+
if (assetMatrix && selectedAssetSymbol) {
|
|
1329
|
+
const tokenMatrix = assetMatrix[selectedAssetSymbol.toUpperCase()];
|
|
1330
|
+
if (!tokenMatrix || !tokenMatrix[fromChain.chainKey] || !tokenMatrix[toChain.chainKey]) {
|
|
1331
|
+
if (!cancelled) {
|
|
1332
|
+
resetUi();
|
|
1333
|
+
}
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
const srcAmountLD = toLD(input2, srcTokenOnFrom.decimals);
|
|
1338
|
+
const srcAddrApi = addrForApi(fromChain.chainKey, srcAddress);
|
|
1339
|
+
const dstAddrApi = addrForApi(toChain.chainKey, dstAddress);
|
|
1340
|
+
const dstNativeAmount = getDstNativeAmount(toChain.chainKey);
|
|
1341
|
+
const slippageDecimal = getSlippageDecimal();
|
|
1342
|
+
const approximateMinLD = BigInt(srcAmountLD) * BigInt(9500) / BigInt(1e4);
|
|
1343
|
+
const quoteRoute = await getQuotesByPriority({
|
|
1344
|
+
srcChainKey: fromChain.chainKey,
|
|
1345
|
+
dstChainKey: toChain.chainKey,
|
|
1346
|
+
srcToken: srcTokenOnFrom.address,
|
|
1347
|
+
dstToken: dstTokenOnTo.address,
|
|
1348
|
+
srcAmountLD,
|
|
1349
|
+
dstAmountMinLD: approximateMinLD.toString(),
|
|
1350
|
+
srcAddress: srcAddrApi,
|
|
1351
|
+
dstAddress: dstAddrApi,
|
|
1352
|
+
dstNativeAmount,
|
|
1353
|
+
slippage: slippageDecimal,
|
|
1354
|
+
routePriority
|
|
1355
|
+
});
|
|
1356
|
+
if (quoteRoute?.route === null) {
|
|
1357
|
+
setNoRoute(true);
|
|
1358
|
+
if (!cancelled) resetUi();
|
|
1359
|
+
return;
|
|
1360
|
+
} else {
|
|
1361
|
+
setNoRoute(false);
|
|
1362
|
+
}
|
|
1363
|
+
if (!quoteRoute || !quoteRoute.dstAmount || BigInt(quoteRoute.dstAmount) === BigInt(0)) {
|
|
1364
|
+
if (!cancelled) resetUi();
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
if (cancelled) return;
|
|
1368
|
+
let quoteWithFees = quoteRoute;
|
|
1369
|
+
if (quoteRoute.srcChainKey.toLowerCase() === "ton") {
|
|
1370
|
+
try {
|
|
1371
|
+
const estimatedFee = estimateTonNetworkFee(quoteRoute);
|
|
1372
|
+
quoteWithFees = addTonNetworkFee(quoteRoute, chains, estimatedFee);
|
|
1373
|
+
console.log("TON network fee estimated for quote:", estimatedFee);
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
console.warn("Failed to estimate TON fee, using fallback:", error);
|
|
1376
|
+
quoteWithFees = addTonNetworkFee(quoteRoute, chains);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (quoteRoute.srcChainKey.toLowerCase() === "tron") {
|
|
1380
|
+
const tronStrategy = chainRegistry.getStrategy("tron");
|
|
1381
|
+
if (tronStrategy?.isConnected()) {
|
|
1382
|
+
const tronWeb = tronStrategy.getClient();
|
|
1383
|
+
const tronAddress = tronStrategy.getAccount();
|
|
1384
|
+
if (tronWeb && tronAddress) {
|
|
1385
|
+
try {
|
|
1386
|
+
const estimatedFee = await estimateTronNetworkFee(
|
|
1387
|
+
tronWeb,
|
|
1388
|
+
quoteRoute,
|
|
1389
|
+
tronAddress
|
|
1390
|
+
);
|
|
1391
|
+
quoteWithFees = addTronNetworkFee(
|
|
1392
|
+
quoteWithFees || quoteRoute,
|
|
1393
|
+
chains,
|
|
1394
|
+
estimatedFee
|
|
1395
|
+
);
|
|
1396
|
+
console.log("TRON network fee estimated for quote:", estimatedFee);
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
console.warn("Failed to estimate TRON fee, using fallback:", error);
|
|
1399
|
+
quoteWithFees = addTronNetworkFee(
|
|
1400
|
+
quoteWithFees || quoteRoute,
|
|
1401
|
+
chains
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
} else {
|
|
1405
|
+
quoteWithFees = addTronNetworkFee(
|
|
1406
|
+
quoteWithFees || quoteRoute,
|
|
1407
|
+
chains
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
quoteWithFees = addTronNetworkFee(
|
|
1412
|
+
quoteWithFees || quoteRoute,
|
|
1413
|
+
chains
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
const srcChainKey = quoteRoute.srcChainKey.toLowerCase();
|
|
1418
|
+
if (srcChainKey !== "ton" && srcChainKey !== "tron") {
|
|
1419
|
+
if (publicClient) {
|
|
1420
|
+
try {
|
|
1421
|
+
const estimatedFee = await estimateEvmNetworkFee(
|
|
1422
|
+
publicClient,
|
|
1423
|
+
quoteRoute,
|
|
1424
|
+
quoteRoute.srcChainKey
|
|
1425
|
+
);
|
|
1426
|
+
quoteWithFees = addEvmNetworkFee(
|
|
1427
|
+
quoteWithFees || quoteRoute,
|
|
1428
|
+
chains,
|
|
1429
|
+
estimatedFee
|
|
1430
|
+
);
|
|
1431
|
+
console.log("EVM network fee estimated for quote:", estimatedFee);
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
console.warn("Failed to estimate EVM fee, using fallback:", error);
|
|
1434
|
+
quoteWithFees = addEvmNetworkFee(
|
|
1435
|
+
quoteWithFees || quoteRoute,
|
|
1436
|
+
chains
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
} else {
|
|
1440
|
+
quoteWithFees = addEvmNetworkFee(
|
|
1441
|
+
quoteWithFees || quoteRoute,
|
|
1442
|
+
chains
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
setQuote(quoteWithFees || quoteRoute);
|
|
1447
|
+
} catch {
|
|
1448
|
+
if (!cancelled) {
|
|
1449
|
+
resetUi();
|
|
1450
|
+
}
|
|
1451
|
+
} finally {
|
|
1452
|
+
if (!cancelled) setLoading(false);
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
run();
|
|
1456
|
+
return () => {
|
|
1457
|
+
cancelled = true;
|
|
1458
|
+
};
|
|
1459
|
+
}, [
|
|
1460
|
+
input2,
|
|
1461
|
+
fromChain?.chainKey,
|
|
1462
|
+
toChain?.chainKey,
|
|
1463
|
+
srcTokenOnFrom,
|
|
1464
|
+
dstTokenOnTo,
|
|
1465
|
+
srcAddress,
|
|
1466
|
+
dstAddress,
|
|
1467
|
+
setQuoteLoading,
|
|
1468
|
+
setQuote,
|
|
1469
|
+
setQError,
|
|
1470
|
+
slippageBps,
|
|
1471
|
+
routePriority,
|
|
1472
|
+
refetchTrigger,
|
|
1473
|
+
chainRegistry,
|
|
1474
|
+
chains,
|
|
1475
|
+
publicClient
|
|
1476
|
+
]);
|
|
1477
|
+
return { loading };
|
|
1478
|
+
}
|
|
1479
|
+
async function getChains() {
|
|
1480
|
+
const res = await fetch("https://stargate.finance/api/v1/chains", {
|
|
1481
|
+
credentials: "same-origin"
|
|
1482
|
+
});
|
|
1483
|
+
if (!res.ok) {
|
|
1484
|
+
throw new Error(`Failed to load chains: ${res.status}`);
|
|
1485
|
+
}
|
|
1486
|
+
const data = await res.json();
|
|
1487
|
+
return Array.isArray(data) ? data : data.chains ?? [];
|
|
1488
|
+
}
|
|
1489
|
+
async function getTokens() {
|
|
1490
|
+
const res = await fetch("https://stargate.finance/api/v1/tokens", {
|
|
1491
|
+
credentials: "same-origin"
|
|
1492
|
+
});
|
|
1493
|
+
if (!res.ok) {
|
|
1494
|
+
throw new Error(`Failed to load chains: ${res.status}`);
|
|
1495
|
+
}
|
|
1496
|
+
const data = await res.json();
|
|
1497
|
+
const tokens = Array.isArray(data) ? data : data.tokens ?? [];
|
|
1498
|
+
return tokens.map(normalizeTokenSymbol);
|
|
1499
|
+
}
|
|
1500
|
+
async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
1501
|
+
const url = new URL("https://stargate.finance/api/v1/tokens");
|
|
1502
|
+
url.searchParams.set("srcChainKey", srcChainKey);
|
|
1503
|
+
url.searchParams.set("srcToken", srcTokenAddr);
|
|
1504
|
+
const res = await fetch(url.toString(), { credentials: "omit" });
|
|
812
1505
|
if (!res.ok) throw new Error(`Failed to load dest tokens: ${res.status}`);
|
|
813
1506
|
const data = await res.json();
|
|
814
1507
|
const raw = data.tokens ?? [];
|
|
@@ -829,10 +1522,7 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
|
829
1522
|
if (sa === sb) return (a.name ?? "").localeCompare(b.name ?? "");
|
|
830
1523
|
return sa.localeCompare(sb);
|
|
831
1524
|
});
|
|
832
|
-
return unique;
|
|
833
|
-
}
|
|
834
|
-
function normalizeTickerSymbol$1(s) {
|
|
835
|
-
return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
1525
|
+
return unique.map(normalizeTokenSymbol);
|
|
836
1526
|
}
|
|
837
1527
|
function resolveTokenOnChainFromMatrix$1(assetMatrix, assetSymbol, chainKey) {
|
|
838
1528
|
if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
|
|
@@ -859,7 +1549,14 @@ const useChainDerivations = () => {
|
|
|
859
1549
|
return [];
|
|
860
1550
|
const byChain = assetMatrix[selectedAssetSymbol.toUpperCase()];
|
|
861
1551
|
const keys = new Set(Object.keys(byChain ?? {}));
|
|
862
|
-
|
|
1552
|
+
const result = chains.filter((c) => keys.has(c.chainKey));
|
|
1553
|
+
console.log(
|
|
1554
|
+
`[DEBUG] supportedFrom for ${selectedAssetSymbol}:`,
|
|
1555
|
+
`${result.length} of ${chains.length} chains`,
|
|
1556
|
+
result.map((c) => c.chainKey),
|
|
1557
|
+
`| Has Arbitrum: ${result.some((c) => c.chainKey === "arbitrum")}`
|
|
1558
|
+
);
|
|
1559
|
+
return result;
|
|
863
1560
|
}, [selectedAssetSymbol, assetMatrix, chains]);
|
|
864
1561
|
react.useEffect(() => {
|
|
865
1562
|
setAllowedFromChains(supportedFrom);
|
|
@@ -927,6 +1624,13 @@ const useChainDerivations = () => {
|
|
|
927
1624
|
if (fromChain?.chainKey) {
|
|
928
1625
|
list = list.filter((c) => c.chainKey !== fromChain.chainKey);
|
|
929
1626
|
}
|
|
1627
|
+
console.log(
|
|
1628
|
+
`[DEBUG] allowedToChains from ${fromChain?.chainKey}:`,
|
|
1629
|
+
`${list.length} destinations`,
|
|
1630
|
+
list.map((c) => c.chainKey),
|
|
1631
|
+
`| Has Arbitrum: ${list.some((c) => c.chainKey === "arbitrum")}`,
|
|
1632
|
+
`| API returned ${dest.length} tokens, filtered to ${filteredDest.length}`
|
|
1633
|
+
);
|
|
930
1634
|
setAllowedToChains(list);
|
|
931
1635
|
if (!toChain || !list.some((c) => c.chainKey === toChain.chainKey)) {
|
|
932
1636
|
setToChain(list[0]);
|
|
@@ -961,16 +1665,6 @@ const useChainDerivations = () => {
|
|
|
961
1665
|
isLoadingToChains
|
|
962
1666
|
};
|
|
963
1667
|
};
|
|
964
|
-
const ChainStrategyContext = react.createContext(void 0);
|
|
965
|
-
function useChainStrategies() {
|
|
966
|
-
const context = react.useContext(ChainStrategyContext);
|
|
967
|
-
if (!context) {
|
|
968
|
-
throw new Error(
|
|
969
|
-
"useChainStrategies must be used within ChainStrategyProvider"
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
return context;
|
|
973
|
-
}
|
|
974
1668
|
function useBalances(chainKey, address, priorityTokenSymbol) {
|
|
975
1669
|
const { chainRegistry } = useChainStrategies();
|
|
976
1670
|
const { assetMatrix } = useTokensStore();
|
|
@@ -1334,7 +2028,7 @@ const StargateIcon = (props) => {
|
|
|
1334
2028
|
"path",
|
|
1335
2029
|
{
|
|
1336
2030
|
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",
|
|
1337
|
-
fill: "
|
|
2031
|
+
fill: "currentColor"
|
|
1338
2032
|
}
|
|
1339
2033
|
)
|
|
1340
2034
|
] }),
|
|
@@ -1470,7 +2164,7 @@ const WalletConnectIcon = (props) => {
|
|
|
1470
2164
|
}
|
|
1471
2165
|
);
|
|
1472
2166
|
};
|
|
1473
|
-
const
|
|
2167
|
+
const TonConnectIcon = (props) => {
|
|
1474
2168
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1475
2169
|
"svg",
|
|
1476
2170
|
{
|
|
@@ -1591,7 +2285,7 @@ const SwapButton = () => {
|
|
|
1591
2285
|
variant: "secondary",
|
|
1592
2286
|
size: "sm",
|
|
1593
2287
|
className: utils.cn(
|
|
1594
|
-
"
|
|
2288
|
+
"backdrop-blur-md h-9 w-9",
|
|
1595
2289
|
"absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2",
|
|
1596
2290
|
!canSwap || isSwapping ? "opacity-50 cursor-not-allowed" : "hover:scale-110"
|
|
1597
2291
|
),
|
|
@@ -1660,7 +2354,7 @@ const SelectNetworkButton = ({
|
|
|
1660
2354
|
size: "sm",
|
|
1661
2355
|
variant: "secondary",
|
|
1662
2356
|
type: "button",
|
|
1663
|
-
className: "shrink-0 gap-2
|
|
2357
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
1664
2358
|
"aria-label": label,
|
|
1665
2359
|
children: [
|
|
1666
2360
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
@@ -1751,49 +2445,36 @@ const SearchInput = ({
|
|
|
1751
2445
|
placeholder,
|
|
1752
2446
|
value,
|
|
1753
2447
|
onChange,
|
|
1754
|
-
className
|
|
1755
|
-
containerClassName
|
|
2448
|
+
className
|
|
1756
2449
|
}) => {
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
className: utils.cn(
|
|
1773
|
-
"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",
|
|
1774
|
-
className
|
|
1775
|
-
),
|
|
1776
|
-
value,
|
|
1777
|
-
onChange: (e) => onChange(e.target.value),
|
|
1778
|
-
onFocus: () => setIsFocused(true),
|
|
1779
|
-
onBlur: () => setIsFocused(false)
|
|
1780
|
-
}
|
|
1781
|
-
)
|
|
1782
|
-
]
|
|
1783
|
-
}
|
|
1784
|
-
);
|
|
2450
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("rounded-xs relative"), children: [
|
|
2451
|
+
/* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "w-6 h-6 absolute left-5 top-0 bottom-0 my-auto" }),
|
|
2452
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2453
|
+
input.Input,
|
|
2454
|
+
{
|
|
2455
|
+
placeholder,
|
|
2456
|
+
className: utils.cn(
|
|
2457
|
+
"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",
|
|
2458
|
+
className
|
|
2459
|
+
),
|
|
2460
|
+
value,
|
|
2461
|
+
onChange: (e) => onChange(e.target.value)
|
|
2462
|
+
}
|
|
2463
|
+
)
|
|
2464
|
+
] });
|
|
1785
2465
|
};
|
|
1786
2466
|
const ChainSelectModal = ({
|
|
1787
2467
|
isOpen,
|
|
1788
2468
|
onClose,
|
|
1789
2469
|
items,
|
|
1790
2470
|
allowedItems,
|
|
1791
|
-
onChangeChain
|
|
2471
|
+
onChangeChain,
|
|
2472
|
+
isSource
|
|
1792
2473
|
}) => {
|
|
1793
2474
|
const { t } = useBridgeTranslation();
|
|
1794
2475
|
const [query, setQuery] = react.useState("");
|
|
1795
2476
|
const { setFromChain, chains, fromChain, toChain } = useChainsStore();
|
|
1796
|
-
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2477
|
+
const { assetMatrix, selectedAssetSymbol, setSelectedAssetSymbol } = useTokensStore();
|
|
1797
2478
|
const handleClose = react.useCallback(() => {
|
|
1798
2479
|
setQuery("");
|
|
1799
2480
|
onClose();
|
|
@@ -1822,40 +2503,98 @@ const ChainSelectModal = ({
|
|
|
1822
2503
|
},
|
|
1823
2504
|
[chains, selectedAssetSymbol, assetMatrix]
|
|
1824
2505
|
);
|
|
2506
|
+
const findCompatibleTokenAndSrcChain = react.useCallback(
|
|
2507
|
+
(dstChain) => {
|
|
2508
|
+
if (!chains || !assetMatrix) return void 0;
|
|
2509
|
+
const PRIORITY_CHAINS = [
|
|
2510
|
+
"ethereum",
|
|
2511
|
+
"arbitrum",
|
|
2512
|
+
"bsc",
|
|
2513
|
+
"polygon",
|
|
2514
|
+
"optimism",
|
|
2515
|
+
"base"
|
|
2516
|
+
];
|
|
2517
|
+
for (const tokenSymbol of Object.keys(assetMatrix)) {
|
|
2518
|
+
const assetByChain = assetMatrix[tokenSymbol];
|
|
2519
|
+
if (!assetByChain[dstChain.chainKey]) continue;
|
|
2520
|
+
const availableChains = chains.filter(
|
|
2521
|
+
(c) => assetByChain[c.chainKey] && c.chainKey !== dstChain.chainKey
|
|
2522
|
+
);
|
|
2523
|
+
if (availableChains.length === 0) continue;
|
|
2524
|
+
let sourceChain;
|
|
2525
|
+
for (const chainKey of PRIORITY_CHAINS) {
|
|
2526
|
+
sourceChain = availableChains.find((c) => c.chainKey === chainKey);
|
|
2527
|
+
if (sourceChain) break;
|
|
2528
|
+
}
|
|
2529
|
+
if (!sourceChain) sourceChain = availableChains[0];
|
|
2530
|
+
return { tokenSymbol, sourceChain };
|
|
2531
|
+
}
|
|
2532
|
+
return void 0;
|
|
2533
|
+
},
|
|
2534
|
+
[chains, assetMatrix]
|
|
2535
|
+
);
|
|
1825
2536
|
const groupedChains = react.useMemo(() => {
|
|
1826
2537
|
const q = query.trim().toLowerCase();
|
|
1827
2538
|
const filtered = q ? (items ?? []).filter(
|
|
1828
2539
|
(c) => c.name.toLowerCase().includes(q) || c.chainKey.toLowerCase().includes(q)
|
|
1829
2540
|
) : items ?? [];
|
|
1830
|
-
const groups = {
|
|
2541
|
+
const groups = {
|
|
2542
|
+
available: [],
|
|
2543
|
+
willChangeSrc: [],
|
|
2544
|
+
willChangeTokenAndSrc: []
|
|
2545
|
+
};
|
|
1831
2546
|
for (const chain of filtered) {
|
|
1832
2547
|
const isAllowed = allowedItems?.some((c) => c.chainKey === chain.chainKey) || false;
|
|
1833
2548
|
if (isAllowed) {
|
|
1834
2549
|
groups.available.push(chain);
|
|
1835
2550
|
} else {
|
|
1836
2551
|
const compatibleSrc = findCompatibleSrcChain(chain);
|
|
1837
|
-
if (compatibleSrc)
|
|
2552
|
+
if (compatibleSrc) {
|
|
2553
|
+
groups.willChangeSrc.push(chain);
|
|
2554
|
+
} else {
|
|
2555
|
+
const compatibleTokenAndSrc = findCompatibleTokenAndSrcChain(chain);
|
|
2556
|
+
if (compatibleTokenAndSrc) {
|
|
2557
|
+
groups.willChangeTokenAndSrc.push(chain);
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
1838
2560
|
}
|
|
1839
2561
|
}
|
|
1840
2562
|
groups.available.sort((a, b) => a.name.localeCompare(b.name));
|
|
1841
2563
|
groups.willChangeSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
2564
|
+
groups.willChangeTokenAndSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
1842
2565
|
return groups;
|
|
1843
|
-
}, [
|
|
1844
|
-
|
|
1845
|
-
|
|
2566
|
+
}, [
|
|
2567
|
+
items,
|
|
2568
|
+
query,
|
|
2569
|
+
allowedItems,
|
|
2570
|
+
findCompatibleSrcChain,
|
|
2571
|
+
findCompatibleTokenAndSrcChain
|
|
2572
|
+
]);
|
|
2573
|
+
const onChainPick = (chain, willChangeSrc = false, willChangeTokenAndSrc = false) => {
|
|
2574
|
+
if (willChangeTokenAndSrc) {
|
|
2575
|
+
const result = findCompatibleTokenAndSrcChain(chain);
|
|
2576
|
+
if (result) {
|
|
2577
|
+
setSelectedAssetSymbol(result.tokenSymbol);
|
|
2578
|
+
if (setFromChain) setFromChain(result.sourceChain);
|
|
2579
|
+
}
|
|
2580
|
+
} else if (willChangeSrc) {
|
|
1846
2581
|
const newSrcChain = findCompatibleSrcChain(chain);
|
|
1847
2582
|
if (newSrcChain && setFromChain) setFromChain(newSrcChain);
|
|
1848
2583
|
}
|
|
1849
2584
|
onChangeChain(chain);
|
|
1850
2585
|
handleClose();
|
|
1851
2586
|
};
|
|
1852
|
-
const renderChainItem = (chain, willChangeSrc) => {
|
|
1853
|
-
const isSelected = fromChain?.chainKey === chain.chainKey
|
|
2587
|
+
const renderChainItem = (chain, willChangeSrc, willChangeTokenAndSrc = false) => {
|
|
2588
|
+
const isSelected = isSource ? fromChain?.chainKey === chain.chainKey : toChain?.chainKey === chain.chainKey;
|
|
1854
2589
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1855
2590
|
button.Button,
|
|
1856
2591
|
{
|
|
1857
|
-
onClick: () => onChainPick(chain, willChangeSrc),
|
|
1858
|
-
className:
|
|
2592
|
+
onClick: () => onChainPick(chain, willChangeSrc, willChangeTokenAndSrc),
|
|
2593
|
+
className: utils.cn(
|
|
2594
|
+
"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",
|
|
2595
|
+
isSelected ? "bg-accent" : "",
|
|
2596
|
+
willChangeSrc || willChangeTokenAndSrc ? "opacity-50 hover:opacity-100" : ""
|
|
2597
|
+
),
|
|
1859
2598
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1860
2599
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1861
2600
|
NetworkSymbol,
|
|
@@ -1871,27 +2610,44 @@ const ChainSelectModal = ({
|
|
|
1871
2610
|
chain.chainKey
|
|
1872
2611
|
);
|
|
1873
2612
|
};
|
|
1874
|
-
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "max-h-[90dvh] h-[90dvh] overflow-hidden flex flex-col", children: [
|
|
1875
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectNetwork") }) }),
|
|
2613
|
+
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col p-10", children: [
|
|
2614
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { className: "text-2xl leading-8", children: t("bridge.selectNetwork") }) }),
|
|
1876
2615
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1877
2616
|
SearchInput,
|
|
1878
2617
|
{
|
|
1879
2618
|
placeholder: t("bridge.searchDestinationChain"),
|
|
1880
2619
|
value: query,
|
|
1881
2620
|
onChange: setQuery,
|
|
1882
|
-
containerClassName: "rounded-md",
|
|
1883
2621
|
className: "text-foreground placeholder:text-muted-foreground"
|
|
1884
2622
|
}
|
|
1885
2623
|
),
|
|
1886
2624
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1887
2625
|
groupedChains.available.length > 0 && groupedChains.available.map((c) => renderChainItem(c, false)),
|
|
1888
|
-
groupedChains.
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2626
|
+
groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2627
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2628
|
+
"p",
|
|
2629
|
+
{
|
|
2630
|
+
className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 ? "mt-10" : ""}`,
|
|
2631
|
+
children: t("bridge.willChangeSourceChain")
|
|
2632
|
+
}
|
|
2633
|
+
),
|
|
2634
|
+
groupedChains.willChangeSrc.map(
|
|
2635
|
+
(c) => renderChainItem(c, true, false)
|
|
2636
|
+
)
|
|
2637
|
+
] }),
|
|
2638
|
+
groupedChains.willChangeTokenAndSrc.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2639
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2640
|
+
"p",
|
|
2641
|
+
{
|
|
2642
|
+
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" : ""}`,
|
|
2643
|
+
children: t("bridge.willChangeSourceNetworkAndToken")
|
|
2644
|
+
}
|
|
2645
|
+
),
|
|
2646
|
+
groupedChains.willChangeTokenAndSrc.map(
|
|
2647
|
+
(c) => renderChainItem(c, false, true)
|
|
1892
2648
|
)
|
|
1893
2649
|
] }),
|
|
1894
|
-
groupedChains.available.length === 0 && groupedChains.willChangeSrc.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4 text-sm text-muted-foreground", children: t("bridge.noResults") })
|
|
2650
|
+
groupedChains.available.length === 0 && groupedChains.willChangeSrc.length === 0 && groupedChains.willChangeTokenAndSrc.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4 text-sm text-muted-foreground", children: t("bridge.noResults") })
|
|
1895
2651
|
] })
|
|
1896
2652
|
] }) });
|
|
1897
2653
|
};
|
|
@@ -1901,52 +2657,10 @@ const useWalletSelectModal = zustand.create((set) => ({
|
|
|
1901
2657
|
onOpen: (addressType) => set({ isOpen: true, addressType }),
|
|
1902
2658
|
onClose: () => set({ isOpen: false, addressType: void 0 })
|
|
1903
2659
|
}));
|
|
1904
|
-
const truncateToDecimals = (num, decimals) => {
|
|
1905
|
-
if (!isFinite(num) || isNaN(num)) return "0.00";
|
|
1906
|
-
const multiplier = Math.pow(10, decimals);
|
|
1907
|
-
const truncated = Math.floor(num * multiplier) / multiplier;
|
|
1908
|
-
return truncated.toFixed(decimals);
|
|
1909
|
-
};
|
|
1910
|
-
const formatTokenAmount = (amount, symbol, options) => {
|
|
1911
|
-
const normalizedSymbol = (symbol ?? "").toUpperCase();
|
|
1912
|
-
if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
|
|
1913
|
-
return `${Math.floor(amount)} ${normalizedSymbol}`;
|
|
1914
|
-
}
|
|
1915
|
-
if (options?.decimals !== void 0) {
|
|
1916
|
-
return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
|
|
1917
|
-
}
|
|
1918
|
-
if (amount >= 1) {
|
|
1919
|
-
return `${amount.toFixed(0)} ${normalizedSymbol}`;
|
|
1920
|
-
} else if (amount >= 1e-3) {
|
|
1921
|
-
return `${amount.toFixed(3)} ${normalizedSymbol}`;
|
|
1922
|
-
} else {
|
|
1923
|
-
return `${amount.toFixed(6)} ${normalizedSymbol}`;
|
|
1924
|
-
}
|
|
1925
|
-
};
|
|
1926
|
-
const formatUsd = (value) => {
|
|
1927
|
-
if (!value || !isFinite(value)) return "$0";
|
|
1928
|
-
if (value >= 1) return `$${value.toFixed(2)}`;
|
|
1929
|
-
return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
|
|
1930
|
-
};
|
|
1931
|
-
const formatPercentage = (bps, decimals = 2) => {
|
|
1932
|
-
return `${(bps / 100).toFixed(decimals)}%`;
|
|
1933
|
-
};
|
|
1934
|
-
const formatBalance = (amount, decimals = 2) => {
|
|
1935
|
-
if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
|
|
1936
|
-
return "0.00";
|
|
1937
|
-
}
|
|
1938
|
-
return amount.toFixed(decimals);
|
|
1939
|
-
};
|
|
1940
|
-
const formatHash = (hash, startChars = 4, endChars = 4) => {
|
|
1941
|
-
if (!hash) return "";
|
|
1942
|
-
if (hash.length <= startChars + endChars) return hash;
|
|
1943
|
-
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1944
|
-
};
|
|
1945
|
-
const formatAddress = formatHash;
|
|
1946
2660
|
const prefixIcons = {
|
|
1947
2661
|
tronlink: /* @__PURE__ */ jsxRuntime.jsx(TronLinkIcon, { className: "w-5 h-5" }),
|
|
1948
2662
|
metamask: /* @__PURE__ */ jsxRuntime.jsx(MetaMaskIcon, { className: "w-5 h-5" }),
|
|
1949
|
-
ton: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2663
|
+
ton: /* @__PURE__ */ jsxRuntime.jsx(TonConnectIcon, { className: "w-5 h-5" })
|
|
1950
2664
|
};
|
|
1951
2665
|
const mapWalletToType = (wallet) => {
|
|
1952
2666
|
switch (wallet) {
|
|
@@ -1997,7 +2711,7 @@ const WalletInlineButton = ({
|
|
|
1997
2711
|
disabled: isButtonDisabled,
|
|
1998
2712
|
variant: "ghost",
|
|
1999
2713
|
size: "sm",
|
|
2000
|
-
className: "flex gap-1 cursor-pointer !px-0 pr-1 h-5",
|
|
2714
|
+
className: "flex gap-1 cursor-pointer hover:opacity-60 hover:bg-transparent !px-0 pr-1 h-5",
|
|
2001
2715
|
children: [
|
|
2002
2716
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: isConnected ? prefixIcons[wallet] : null }),
|
|
2003
2717
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "leading-3 text-sm border-b border-dotted border-link text-link", children: buttonText })
|
|
@@ -2133,7 +2847,8 @@ const SwapSection = ({
|
|
|
2133
2847
|
onClose,
|
|
2134
2848
|
items: chains,
|
|
2135
2849
|
allowedItems: allowedChains,
|
|
2136
|
-
onChangeChain
|
|
2850
|
+
onChangeChain,
|
|
2851
|
+
isSource
|
|
2137
2852
|
}
|
|
2138
2853
|
)
|
|
2139
2854
|
] });
|
|
@@ -2187,7 +2902,6 @@ const AnotherAddress = () => {
|
|
|
2187
2902
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2188
2903
|
_switch.Switch,
|
|
2189
2904
|
{
|
|
2190
|
-
className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
|
|
2191
2905
|
"aria-pressed": enabled,
|
|
2192
2906
|
checked: enabled,
|
|
2193
2907
|
onClick: () => setEnabled((v) => !v)
|
|
@@ -2206,7 +2920,7 @@ const AnotherAddress = () => {
|
|
|
2206
2920
|
"div",
|
|
2207
2921
|
{
|
|
2208
2922
|
className: utils.cn(
|
|
2209
|
-
"bg-input py-2
|
|
2923
|
+
"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",
|
|
2210
2924
|
{
|
|
2211
2925
|
"py-4": value,
|
|
2212
2926
|
"border border-ring": isFocused,
|
|
@@ -2216,13 +2930,13 @@ const AnotherAddress = () => {
|
|
|
2216
2930
|
children: [
|
|
2217
2931
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
|
|
2218
2932
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2219
|
-
|
|
2933
|
+
"textarea",
|
|
2220
2934
|
{
|
|
2221
2935
|
className: utils.cn(
|
|
2222
|
-
"
|
|
2936
|
+
"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"
|
|
2223
2937
|
),
|
|
2224
2938
|
placeholder: t("bridge.anotherAddressPlaceholder"),
|
|
2225
|
-
|
|
2939
|
+
rows: value.length >= 32 ? 2 : 1,
|
|
2226
2940
|
value,
|
|
2227
2941
|
onFocus: () => setIsFocused(true),
|
|
2228
2942
|
onBlur: () => setIsFocused(false),
|
|
@@ -2239,7 +2953,7 @@ const AnotherAddress = () => {
|
|
|
2239
2953
|
button.Button,
|
|
2240
2954
|
{
|
|
2241
2955
|
variant: "secondary",
|
|
2242
|
-
className: "bg-
|
|
2956
|
+
className: "bg-accent text-card-foreground uppercase text-xs",
|
|
2243
2957
|
size: "sm",
|
|
2244
2958
|
onClick: onPaste,
|
|
2245
2959
|
children: t("common.paste")
|
|
@@ -2250,257 +2964,135 @@ const AnotherAddress = () => {
|
|
|
2250
2964
|
variant: "ghost",
|
|
2251
2965
|
size: "sm",
|
|
2252
2966
|
className: "rounded-full p-0 w-5 h-5 self-start",
|
|
2253
|
-
onClick: () => setValue(""),
|
|
2254
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
2255
|
-
}
|
|
2256
|
-
)
|
|
2257
|
-
]
|
|
2258
|
-
}
|
|
2259
|
-
)
|
|
2260
|
-
},
|
|
2261
|
-
"custom-address-block"
|
|
2262
|
-
) })
|
|
2263
|
-
] });
|
|
2264
|
-
};
|
|
2265
|
-
const
|
|
2266
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
2267
|
-
"svg",
|
|
2268
|
-
{
|
|
2269
|
-
width: "16",
|
|
2270
|
-
height: "16",
|
|
2271
|
-
viewBox: "0 0 16 16",
|
|
2272
|
-
fill: "none",
|
|
2273
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2274
|
-
...props,
|
|
2275
|
-
children:
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
width: "100%"
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
) }),
|
|
2288
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2289
|
-
"circle",
|
|
2290
|
-
{
|
|
2291
|
-
"data-figma-bg-blur-radius": "4.4893",
|
|
2292
|
-
cx: "8",
|
|
2293
|
-
cy: "8",
|
|
2294
|
-
r: "8",
|
|
2295
|
-
fill: "currentColor",
|
|
2296
|
-
fillOpacity: "0.2"
|
|
2297
|
-
}
|
|
2298
|
-
),
|
|
2299
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2300
|
-
"path",
|
|
2301
|
-
{
|
|
2302
|
-
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",
|
|
2303
|
-
stroke: "currentColor",
|
|
2304
|
-
strokeOpacity: "0.5",
|
|
2305
|
-
strokeWidth: "1.68349",
|
|
2306
|
-
strokeLinecap: "round"
|
|
2307
|
-
}
|
|
2308
|
-
),
|
|
2309
|
-
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2310
|
-
"clipPath",
|
|
2311
|
-
{
|
|
2312
|
-
id: "bgblur_0_13066_11660_clip_path",
|
|
2313
|
-
transform: "translate(4.4893 4.4893)",
|
|
2314
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "8" })
|
|
2315
|
-
}
|
|
2316
|
-
) })
|
|
2317
|
-
]
|
|
2318
|
-
}
|
|
2319
|
-
);
|
|
2320
|
-
};
|
|
2321
|
-
const Tip = (props) => {
|
|
2322
|
-
const { children, text } = props;
|
|
2323
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(tooltip.Tooltip, { children: [
|
|
2324
|
-
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { children }),
|
|
2325
|
-
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: text }) })
|
|
2326
|
-
] });
|
|
2327
|
-
};
|
|
2328
|
-
const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/tokens";
|
|
2329
|
-
const TokenSymbol = ({
|
|
2330
|
-
symbol,
|
|
2331
|
-
className = "w-4 h-4",
|
|
2332
|
-
alt
|
|
2333
|
-
}) => {
|
|
2334
|
-
const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
|
|
2335
|
-
const src = `${BASE_URL}/${normalizedSymbol}.svg`;
|
|
2336
|
-
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? symbol, className });
|
|
2337
|
-
};
|
|
2338
|
-
const EVM_CONFIG = {
|
|
2339
|
-
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
2340
|
-
gasEstimates: {
|
|
2341
|
-
approve: 65000n,
|
|
2342
|
-
bridge: 300000n
|
|
2343
|
-
},
|
|
2344
|
-
gasBuffer: 1.2,
|
|
2345
|
-
// 20% buffer
|
|
2346
|
-
timeout: 3e5,
|
|
2347
|
-
// 5 minutes (increased for slower networks)
|
|
2348
|
-
requiredConfirmations: 3
|
|
2349
|
-
// Wait for 3 confirmations for reorg protection
|
|
2350
|
-
};
|
|
2351
|
-
const TON_CONFIG = {
|
|
2352
|
-
apiUrl: "https://toncenter.com/api/v2",
|
|
2353
|
-
timeout: 36e4,
|
|
2354
|
-
// 6 minutes
|
|
2355
|
-
validUntil: 600,
|
|
2356
|
-
// 10 minutes
|
|
2357
|
-
pollingInterval: 5e3,
|
|
2358
|
-
// 5 seconds between transaction status checks
|
|
2359
|
-
estimatedNetworkFee: "100000000"
|
|
2360
|
-
// 0.1 TON in nanoton (conservative estimate)
|
|
2361
|
-
};
|
|
2362
|
-
const TRON_CONFIG = {
|
|
2363
|
-
timeout: 12e4,
|
|
2364
|
-
// 2 minutes (for 19 confirmations)
|
|
2365
|
-
feeLimit: 1e8,
|
|
2366
|
-
// 100 TRX in sun
|
|
2367
|
-
requiredConfirmations: 19,
|
|
2368
|
-
// TRON standard: 19 blocks for confirmation
|
|
2369
|
-
pollingInterval: 3e3
|
|
2370
|
-
// 3 seconds between checks
|
|
2371
|
-
};
|
|
2372
|
-
let tonClientInstance = null;
|
|
2373
|
-
function getTonClient(customClient, apiKey) {
|
|
2374
|
-
if (customClient) {
|
|
2375
|
-
return customClient;
|
|
2376
|
-
}
|
|
2377
|
-
if (!tonClientInstance) {
|
|
2378
|
-
tonClientInstance = new ton.TonClient({
|
|
2379
|
-
endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
|
|
2380
|
-
apiKey
|
|
2381
|
-
});
|
|
2382
|
-
}
|
|
2383
|
-
return tonClientInstance;
|
|
2384
|
-
}
|
|
2385
|
-
function getQuoteAmounts(quote, srcToken, dstToken) {
|
|
2386
|
-
if (!quote || !srcToken || !dstToken) {
|
|
2387
|
-
return {
|
|
2388
|
-
inputHuman: 0,
|
|
2389
|
-
outputHuman: 0,
|
|
2390
|
-
outputHumanRounded: "0",
|
|
2391
|
-
minReceivedHuman: 0
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
|
|
2395
|
-
const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
|
|
2396
|
-
const outputHumanRounded = truncateToDecimals(outputHuman, 2);
|
|
2397
|
-
const minReceivedHuman = fromLD(
|
|
2398
|
-
quote.dstAmountMin || "0",
|
|
2399
|
-
dstToken.decimals
|
|
2400
|
-
);
|
|
2401
|
-
return {
|
|
2402
|
-
inputHuman,
|
|
2403
|
-
outputHuman,
|
|
2404
|
-
outputHumanRounded,
|
|
2405
|
-
minReceivedHuman
|
|
2406
|
-
};
|
|
2407
|
-
}
|
|
2408
|
-
function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
|
|
2409
|
-
if (!quote || !tokens || !chains) {
|
|
2410
|
-
return {
|
|
2411
|
-
totalUsd: 0,
|
|
2412
|
-
protocolFeeUsd: void 0,
|
|
2413
|
-
messageFeeUsd: void 0,
|
|
2414
|
-
serviceUsd: void 0,
|
|
2415
|
-
blockchainUsd: void 0,
|
|
2416
|
-
inSrcToken: void 0,
|
|
2417
|
-
inDstToken: void 0
|
|
2418
|
-
};
|
|
2419
|
-
}
|
|
2420
|
-
const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
|
|
2421
|
-
let inSrcToken = void 0;
|
|
2422
|
-
let inDstToken = void 0;
|
|
2423
|
-
if (srcToken && quote.srcChainKey) {
|
|
2424
|
-
const feeInSrcLD = sumFeeByTokenLD(
|
|
2425
|
-
quote.fees,
|
|
2426
|
-
srcToken.address,
|
|
2427
|
-
quote.srcChainKey
|
|
2428
|
-
);
|
|
2429
|
-
const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
|
|
2430
|
-
if (feeInSrcHuman > 0) {
|
|
2431
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
|
|
2432
|
-
} else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
|
|
2433
|
-
const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
|
|
2434
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
if (dstToken && quote.dstChainKey) {
|
|
2438
|
-
const feeInDstLD = sumFeeByTokenLD(
|
|
2439
|
-
quote.fees,
|
|
2440
|
-
dstToken.address,
|
|
2441
|
-
quote.dstChainKey
|
|
2442
|
-
);
|
|
2443
|
-
const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
|
|
2444
|
-
if (feeInDstHuman > 0) {
|
|
2445
|
-
inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
|
|
2967
|
+
onClick: () => setValue(""),
|
|
2968
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
|
|
2969
|
+
}
|
|
2970
|
+
)
|
|
2971
|
+
]
|
|
2972
|
+
}
|
|
2973
|
+
)
|
|
2974
|
+
},
|
|
2975
|
+
"custom-address-block"
|
|
2976
|
+
) })
|
|
2977
|
+
] });
|
|
2978
|
+
};
|
|
2979
|
+
const InfoIcon = (props) => {
|
|
2980
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2981
|
+
"svg",
|
|
2982
|
+
{
|
|
2983
|
+
width: "16",
|
|
2984
|
+
height: "16",
|
|
2985
|
+
viewBox: "0 0 16 16",
|
|
2986
|
+
fill: "none",
|
|
2987
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2988
|
+
...props,
|
|
2989
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2990
|
+
"path",
|
|
2991
|
+
{
|
|
2992
|
+
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",
|
|
2993
|
+
stroke: "currentColor",
|
|
2994
|
+
"stroke-width": "1.68349",
|
|
2995
|
+
"stroke-linecap": "round"
|
|
2996
|
+
}
|
|
2997
|
+
)
|
|
2446
2998
|
}
|
|
2447
|
-
}
|
|
2448
|
-
return {
|
|
2449
|
-
totalUsd: feeData.totalUsd,
|
|
2450
|
-
protocolFeeUsd: feeData.protocolFeeUsd,
|
|
2451
|
-
messageFeeUsd: feeData.messageFeeUsd,
|
|
2452
|
-
serviceUsd: feeData.serviceUsd,
|
|
2453
|
-
blockchainUsd: feeData.blockchainUsd,
|
|
2454
|
-
inSrcToken,
|
|
2455
|
-
inDstToken
|
|
2456
|
-
};
|
|
2457
|
-
}
|
|
2458
|
-
function calculateMinReceived(quote, slippageBps, dstToken) {
|
|
2459
|
-
if (!quote || !dstToken) return 0;
|
|
2460
|
-
const dstAmountLD = BigInt(quote.dstAmount);
|
|
2461
|
-
const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
|
|
2462
|
-
return fromLD(minAmountLD.toString(), dstToken.decimals);
|
|
2463
|
-
}
|
|
2464
|
-
function addTonNetworkFee(quote, chains) {
|
|
2465
|
-
if (!quote || quote.srcChainKey.toLowerCase() !== "ton") {
|
|
2466
|
-
return quote;
|
|
2467
|
-
}
|
|
2468
|
-
const tonChain = chains?.find(
|
|
2469
|
-
(c) => c.chainKey.toLowerCase() === "ton"
|
|
2470
|
-
);
|
|
2471
|
-
if (!tonChain?.nativeCurrency?.address) {
|
|
2472
|
-
console.warn("Could not find TON native currency address");
|
|
2473
|
-
return quote;
|
|
2474
|
-
}
|
|
2475
|
-
const networkFee = {
|
|
2476
|
-
token: tonChain.nativeCurrency.address,
|
|
2477
|
-
chainKey: "ton",
|
|
2478
|
-
amount: TON_CONFIG.estimatedNetworkFee,
|
|
2479
|
-
type: "network"
|
|
2480
|
-
};
|
|
2481
|
-
const hasNetworkFee = quote.fees?.some(
|
|
2482
|
-
(fee) => fee.type === "network" && fee.chainKey === "ton"
|
|
2483
2999
|
);
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
}
|
|
2487
|
-
return {
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
};
|
|
3000
|
+
};
|
|
3001
|
+
const Tip = (props) => {
|
|
3002
|
+
const { children, text } = props;
|
|
3003
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(tooltip.Tooltip, { children: [
|
|
3004
|
+
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { className: "w-4 h-4 rounded-full bg-muted text-muted-foreground", children }),
|
|
3005
|
+
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: text }) })
|
|
3006
|
+
] });
|
|
3007
|
+
};
|
|
3008
|
+
const BASE_URL = "https://icons-ckg.pages.dev/stargate-light/tokens";
|
|
3009
|
+
const TokenSymbol = ({
|
|
3010
|
+
symbol,
|
|
3011
|
+
className = "w-4 h-4",
|
|
3012
|
+
alt
|
|
3013
|
+
}) => {
|
|
3014
|
+
const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
|
|
3015
|
+
const src = `${BASE_URL}/${normalizedSymbol}.svg`;
|
|
3016
|
+
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? symbol, className });
|
|
3017
|
+
};
|
|
3018
|
+
function getSimpleFallback(chainKey) {
|
|
3019
|
+
const key = chainKey.toLowerCase();
|
|
3020
|
+
if (key === "ton") return 0.15;
|
|
3021
|
+
if (key === "tron") return 10;
|
|
3022
|
+
return 0.01;
|
|
2491
3023
|
}
|
|
2492
|
-
function
|
|
2493
|
-
const
|
|
2494
|
-
const
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
3024
|
+
function useGasEstimate(amountNum) {
|
|
3025
|
+
const { fromChain } = useChainsStore();
|
|
3026
|
+
const { selectedAssetSymbol } = useTokensStore();
|
|
3027
|
+
const { srcAddress } = useAddresses();
|
|
3028
|
+
const { balances, isLoading: balancesLoading } = useBalances(
|
|
3029
|
+
fromChain?.chainKey,
|
|
3030
|
+
srcAddress
|
|
3031
|
+
);
|
|
3032
|
+
const { quote } = useBridgeQuoteStore();
|
|
3033
|
+
const balancesKnown = !balancesLoading;
|
|
3034
|
+
const chainKey = fromChain?.chainKey;
|
|
3035
|
+
const nativeCurrencySymbol = fromChain?.nativeCurrency?.symbol;
|
|
3036
|
+
const nativeCurrencyAddress = fromChain?.nativeCurrency?.address;
|
|
3037
|
+
const nativeCurrencyDecimals = fromChain?.nativeCurrency?.decimals;
|
|
3038
|
+
const quoteFees = quote?.fees ? JSON.stringify(quote.fees) : null;
|
|
3039
|
+
const quoteSrcChainKey = quote?.srcChainKey;
|
|
3040
|
+
const nativeBalanceValue = nativeCurrencySymbol ? Number(balances[nativeCurrencySymbol.toUpperCase()]?.balance ?? 0) : 0;
|
|
3041
|
+
const result = react.useMemo(() => {
|
|
3042
|
+
if (!chainKey || !nativeCurrencySymbol) {
|
|
3043
|
+
return {
|
|
3044
|
+
nativeSym: "",
|
|
3045
|
+
nativeBalance: 0,
|
|
3046
|
+
requiredNative: 0,
|
|
3047
|
+
balancesKnown,
|
|
3048
|
+
isNativeSelected: false,
|
|
3049
|
+
hasEnoughGas: true
|
|
3050
|
+
};
|
|
3051
|
+
}
|
|
3052
|
+
const nativeSym = nativeCurrencySymbol.toUpperCase();
|
|
3053
|
+
const nativeBalance = nativeBalanceValue;
|
|
3054
|
+
const isNativeSelected = nativeSym === (selectedAssetSymbol || "").toUpperCase();
|
|
3055
|
+
let requiredNative = 0;
|
|
3056
|
+
if (quoteFees && quoteSrcChainKey === chainKey) {
|
|
3057
|
+
const fees = JSON.parse(quoteFees);
|
|
3058
|
+
const feesInNative = fees.filter(
|
|
3059
|
+
(f) => f.chainKey === chainKey && f.token === nativeCurrencyAddress
|
|
3060
|
+
).reduce(
|
|
3061
|
+
(sum, f) => sum + BigInt(f.amount || "0"),
|
|
3062
|
+
0n
|
|
3063
|
+
);
|
|
3064
|
+
const decimals = nativeCurrencyDecimals || 18;
|
|
3065
|
+
requiredNative = Number(feesInNative) / Math.pow(10, decimals);
|
|
3066
|
+
} else {
|
|
3067
|
+
requiredNative = getSimpleFallback(chainKey);
|
|
3068
|
+
}
|
|
3069
|
+
let hasEnoughGas = true;
|
|
3070
|
+
if (isNativeSelected) {
|
|
3071
|
+
hasEnoughGas = nativeBalance - (amountNum ?? 0) >= requiredNative;
|
|
3072
|
+
} else {
|
|
3073
|
+
hasEnoughGas = nativeBalance >= requiredNative;
|
|
3074
|
+
}
|
|
3075
|
+
return {
|
|
3076
|
+
nativeSym,
|
|
3077
|
+
nativeBalance,
|
|
3078
|
+
requiredNative,
|
|
3079
|
+
balancesKnown,
|
|
3080
|
+
isNativeSelected,
|
|
3081
|
+
hasEnoughGas: balancesKnown ? hasEnoughGas : true
|
|
3082
|
+
};
|
|
3083
|
+
}, [
|
|
3084
|
+
chainKey,
|
|
3085
|
+
nativeCurrencySymbol,
|
|
3086
|
+
nativeCurrencyAddress,
|
|
3087
|
+
nativeCurrencyDecimals,
|
|
3088
|
+
selectedAssetSymbol,
|
|
3089
|
+
quoteFees,
|
|
3090
|
+
quoteSrcChainKey,
|
|
3091
|
+
amountNum,
|
|
3092
|
+
balancesKnown,
|
|
3093
|
+
nativeBalanceValue
|
|
3094
|
+
]);
|
|
3095
|
+
return result;
|
|
2504
3096
|
}
|
|
2505
3097
|
function getRouteDisplayName(route) {
|
|
2506
3098
|
if (!route) return "Stargate Bridge";
|
|
@@ -2517,6 +3109,7 @@ const Details = () => {
|
|
|
2517
3109
|
const { toChain, fromChain, chains } = useChainsStore();
|
|
2518
3110
|
const { quote, status } = useBridgeQuoteStore();
|
|
2519
3111
|
const { slippageBps, routePriority } = useSettingsStore();
|
|
3112
|
+
const gas = useGasEstimate();
|
|
2520
3113
|
const dstToken = resolveTokenOnChainFromMatrix$2(
|
|
2521
3114
|
assetMatrix,
|
|
2522
3115
|
selectedAssetSymbol,
|
|
@@ -2527,9 +3120,8 @@ const Details = () => {
|
|
|
2527
3120
|
selectedAssetSymbol,
|
|
2528
3121
|
fromChain?.chainKey
|
|
2529
3122
|
);
|
|
2530
|
-
const quoteWithFees = addTonNetworkFee(quote, chains);
|
|
2531
3123
|
const quoteDetails = getQuoteDetails(
|
|
2532
|
-
|
|
3124
|
+
quote,
|
|
2533
3125
|
srcToken,
|
|
2534
3126
|
dstToken,
|
|
2535
3127
|
tokens,
|
|
@@ -2540,21 +3132,7 @@ const Details = () => {
|
|
|
2540
3132
|
const isLoading = status === "loading";
|
|
2541
3133
|
const receiveText = quoteDetails.outputAmount != null ? Number(quoteDetails.outputAmount).toFixed(6) : "0.00";
|
|
2542
3134
|
const etaText = quoteDetails.etaSeconds != null ? `≈ ${Math.max(1, Math.round(quoteDetails.etaSeconds / 60))}m` : "—";
|
|
2543
|
-
const
|
|
2544
|
-
const feeInDst = quoteDetails.fees?.inDstToken;
|
|
2545
|
-
const totalFeeUsd = quoteDetails.fees?.totalUsd;
|
|
2546
|
-
const totalFeeDisplay = (() => {
|
|
2547
|
-
if (feeInSrc != null && srcToken?.symbol) {
|
|
2548
|
-
return `${Number(feeInSrc).toFixed(6)} ${srcToken.symbol.toUpperCase()}`;
|
|
2549
|
-
}
|
|
2550
|
-
if (feeInDst != null && dstToken?.symbol) {
|
|
2551
|
-
return `${Number(feeInDst).toFixed(6)} ${dstToken.symbol.toUpperCase()}`;
|
|
2552
|
-
}
|
|
2553
|
-
if (totalFeeUsd != null) {
|
|
2554
|
-
return formatUsd(totalFeeUsd);
|
|
2555
|
-
}
|
|
2556
|
-
return "—";
|
|
2557
|
-
})();
|
|
3135
|
+
const totalFeeDisplay = gas.requiredNative > 0 ? `${gas.requiredNative.toFixed(6)} ${gas.nativeSym}` : "—";
|
|
2558
3136
|
const currentSlippageText = formatPercentage(slippageBps);
|
|
2559
3137
|
const routeText = quote?.route ? getRouteDisplayName(quote.route) : t(`settings.routePresets.${routePriority}`);
|
|
2560
3138
|
return /* @__PURE__ */ jsxRuntime.jsx(accordion.Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsxs(accordion.AccordionItem, { value: "item-1", className: "bg-muted/50 rounded-sm", children: [
|
|
@@ -2574,7 +3152,7 @@ const Details = () => {
|
|
|
2574
3152
|
DetailsRow,
|
|
2575
3153
|
{
|
|
2576
3154
|
label: t("transaction.route"),
|
|
2577
|
-
value: /* @__PURE__ */ jsxRuntime.jsxs("
|
|
3155
|
+
value: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2578
3156
|
/* @__PURE__ */ jsxRuntime.jsx(StargateIcon, { className: "w-4 h-4" }),
|
|
2579
3157
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "", children: routeText })
|
|
2580
3158
|
] })
|
|
@@ -2601,7 +3179,14 @@ const Details = () => {
|
|
|
2601
3179
|
label: t("transaction.totalFee"),
|
|
2602
3180
|
value: /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "inline-flex items-center gap-1", children: [
|
|
2603
3181
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: totalFeeDisplay }),
|
|
2604
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3182
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3183
|
+
TokenSymbol,
|
|
3184
|
+
{
|
|
3185
|
+
symbol: gas.nativeSym,
|
|
3186
|
+
className: "w-4 h-4",
|
|
3187
|
+
alt: "token"
|
|
3188
|
+
}
|
|
3189
|
+
)
|
|
2605
3190
|
] }),
|
|
2606
3191
|
isLoading
|
|
2607
3192
|
}
|
|
@@ -2616,7 +3201,7 @@ const DetailsRow = ({
|
|
|
2616
3201
|
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
2617
3202
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2618
3203
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-priority font-normal", children: label }),
|
|
2619
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: label, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3204
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: label, children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, {}) })
|
|
2620
3205
|
] }),
|
|
2621
3206
|
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-4 w-16 rounded-md" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-foreground text-sm", children: value ?? "—" })
|
|
2622
3207
|
] });
|
|
@@ -2669,6 +3254,20 @@ const useTransactionStore = zustand.create((set, get) => ({
|
|
|
2669
3254
|
};
|
|
2670
3255
|
set({ current: next });
|
|
2671
3256
|
},
|
|
3257
|
+
updateActualFee: (feeValue, feeSymbol) => {
|
|
3258
|
+
const cur = get().current;
|
|
3259
|
+
if (!cur) return;
|
|
3260
|
+
const next = {
|
|
3261
|
+
...cur,
|
|
3262
|
+
metadata: {
|
|
3263
|
+
...cur.metadata,
|
|
3264
|
+
actualFeeValue: feeValue,
|
|
3265
|
+
actualFeeSymbol: feeSymbol
|
|
3266
|
+
},
|
|
3267
|
+
updatedAt: Date.now()
|
|
3268
|
+
};
|
|
3269
|
+
set({ current: next });
|
|
3270
|
+
},
|
|
2672
3271
|
reset: () => {
|
|
2673
3272
|
set({ current: void 0 });
|
|
2674
3273
|
}
|
|
@@ -2814,9 +3413,13 @@ function isUserRejection(error) {
|
|
|
2814
3413
|
if (error instanceof TransactionRejectedError) {
|
|
2815
3414
|
return true;
|
|
2816
3415
|
}
|
|
3416
|
+
if (typeof error === "string") {
|
|
3417
|
+
const message = error.toLowerCase();
|
|
3418
|
+
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");
|
|
3419
|
+
}
|
|
2817
3420
|
if (error instanceof Error) {
|
|
2818
3421
|
const message = error.message.toLowerCase();
|
|
2819
|
-
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");
|
|
3422
|
+
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");
|
|
2820
3423
|
}
|
|
2821
3424
|
return false;
|
|
2822
3425
|
}
|
|
@@ -2835,17 +3438,13 @@ function toChainStrategyError(error, chainKey, context) {
|
|
|
2835
3438
|
error
|
|
2836
3439
|
);
|
|
2837
3440
|
}
|
|
2838
|
-
return new ChainStrategyError(
|
|
2839
|
-
String(error),
|
|
2840
|
-
"UNKNOWN_ERROR",
|
|
2841
|
-
chainKey
|
|
2842
|
-
);
|
|
3441
|
+
return new ChainStrategyError(String(error), "UNKNOWN_ERROR", chainKey);
|
|
2843
3442
|
}
|
|
2844
3443
|
function useBridgeTransaction() {
|
|
2845
3444
|
const { quote } = useBridgeQuoteStore();
|
|
2846
3445
|
const { chainRegistry } = useChainStrategies();
|
|
2847
3446
|
const { srcAddress, dstAddress } = useAddresses();
|
|
2848
|
-
const { assetMatrix,
|
|
3447
|
+
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2849
3448
|
const { chains } = useChainsStore();
|
|
2850
3449
|
const txStore = useTransactionStore();
|
|
2851
3450
|
const [isProcessing, setIsProcessing] = react.useState(false);
|
|
@@ -2874,31 +3473,33 @@ function useBridgeTransaction() {
|
|
|
2874
3473
|
}
|
|
2875
3474
|
const srcChain = chains?.find((c) => c.chainKey === quote.srcChainKey);
|
|
2876
3475
|
const dstChain = chains?.find((c) => c.chainKey === quote.dstChainKey);
|
|
2877
|
-
const
|
|
2878
|
-
const amounts = getQuoteAmounts(quoteWithFees, srcToken, dstToken);
|
|
2879
|
-
const fees = getQuoteFees(quoteWithFees, tokens, chains, srcToken, dstToken);
|
|
3476
|
+
const amounts = getQuoteAmounts(quote, srcToken, dstToken);
|
|
2880
3477
|
const metadata = {
|
|
2881
3478
|
srcChainName: srcChain?.name || quote.srcChainKey,
|
|
2882
3479
|
dstChainName: dstChain?.name || quote.dstChainKey,
|
|
2883
3480
|
srcTokenSymbol: srcToken?.symbol || quote.srcToken,
|
|
2884
3481
|
dstTokenSymbol: dstToken?.symbol || quote.dstToken,
|
|
2885
3482
|
srcAmountHuman: amounts.inputHuman,
|
|
2886
|
-
dstAmountHuman: amounts.outputHuman
|
|
2887
|
-
//
|
|
2888
|
-
totalFeeValue: fees.inSrcToken ?? fees.inDstToken ?? void 0,
|
|
2889
|
-
totalFeeSymbol: fees.inSrcToken ? srcToken?.symbol : fees.inDstToken ? dstToken?.symbol : void 0
|
|
3483
|
+
dstAmountHuman: amounts.outputHuman
|
|
3484
|
+
// Actual fee will be updated when transaction completes
|
|
2890
3485
|
};
|
|
2891
3486
|
txStore.setTransaction(quote, "executing", metadata);
|
|
2892
3487
|
setIsProcessing(true);
|
|
2893
3488
|
try {
|
|
2894
3489
|
const steps = quote.steps || [];
|
|
2895
3490
|
if (steps.length === 0) {
|
|
2896
|
-
throw new InvalidStepsError(
|
|
3491
|
+
throw new InvalidStepsError(
|
|
3492
|
+
quote.srcChainKey,
|
|
3493
|
+
"No transaction steps found in quote"
|
|
3494
|
+
);
|
|
2897
3495
|
}
|
|
2898
3496
|
const chainKey = steps[0].chainKey;
|
|
2899
3497
|
const strategy = chainRegistry.getStrategy(chainKey);
|
|
2900
3498
|
if (!strategy) {
|
|
2901
|
-
throw new InvalidStepsError(
|
|
3499
|
+
throw new InvalidStepsError(
|
|
3500
|
+
chainKey,
|
|
3501
|
+
`No strategy available for chain: ${chainKey}`
|
|
3502
|
+
);
|
|
2902
3503
|
}
|
|
2903
3504
|
if (!strategy.isConnected()) {
|
|
2904
3505
|
throw new WalletNotConnectedError(chainKey);
|
|
@@ -2910,7 +3511,6 @@ function useBridgeTransaction() {
|
|
|
2910
3511
|
srcChainKey: quote.srcChainKey,
|
|
2911
3512
|
dstChainKey: quote.dstChainKey
|
|
2912
3513
|
};
|
|
2913
|
-
console.log(steps);
|
|
2914
3514
|
const txResult = await strategy.executeSteps(steps, context, (hash) => {
|
|
2915
3515
|
txStore.setSrcHash(hash);
|
|
2916
3516
|
txStore.updateStatus("processing");
|
|
@@ -2921,6 +3521,21 @@ function useBridgeTransaction() {
|
|
|
2921
3521
|
if (result.dstTxHash) {
|
|
2922
3522
|
txStore.setDstHash(result.dstTxHash);
|
|
2923
3523
|
}
|
|
3524
|
+
if (result.actualFeeValue) {
|
|
3525
|
+
let feeSymbol = result.actualFeeSymbol;
|
|
3526
|
+
if (!feeSymbol) {
|
|
3527
|
+
const srcChain2 = chains?.find(
|
|
3528
|
+
(c) => c.chainKey === quote.srcChainKey
|
|
3529
|
+
);
|
|
3530
|
+
feeSymbol = srcChain2?.nativeCurrency?.symbol || "";
|
|
3531
|
+
}
|
|
3532
|
+
if (feeSymbol) {
|
|
3533
|
+
const feeValue = parseFloat(result.actualFeeValue);
|
|
3534
|
+
if (!isNaN(feeValue)) {
|
|
3535
|
+
txStore.updateActualFee(feeValue, feeSymbol);
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
2924
3539
|
txStore.updateStatus("completed");
|
|
2925
3540
|
console.log("Transaction completed successfully");
|
|
2926
3541
|
} else {
|
|
@@ -2938,19 +3553,26 @@ function useBridgeTransaction() {
|
|
|
2938
3553
|
console.error("Error tracking completion:", err);
|
|
2939
3554
|
});
|
|
2940
3555
|
} else {
|
|
2941
|
-
throw new TransactionFailedError(
|
|
3556
|
+
throw new TransactionFailedError(
|
|
3557
|
+
chainKey,
|
|
3558
|
+
"Transaction hash not received from wallet"
|
|
3559
|
+
);
|
|
2942
3560
|
}
|
|
2943
3561
|
return txResult;
|
|
2944
3562
|
} catch (err) {
|
|
2945
3563
|
if (isUserRejection(err)) {
|
|
2946
3564
|
txStore.setError("TRANSACTION_REJECTED");
|
|
2947
|
-
throw new TransactionFailedError(
|
|
3565
|
+
throw new TransactionFailedError(
|
|
3566
|
+
quote.srcChainKey,
|
|
3567
|
+
"Transaction rejected by user"
|
|
3568
|
+
);
|
|
2948
3569
|
}
|
|
2949
3570
|
if (ChainStrategyError.isChainStrategyError(err)) {
|
|
2950
3571
|
txStore.setError(err.code, { chainKey: err.chainKey });
|
|
2951
3572
|
console.error("Chain strategy error:", err.toJSON());
|
|
2952
3573
|
throw err;
|
|
2953
3574
|
}
|
|
3575
|
+
console.log(err);
|
|
2954
3576
|
txStore.setError("UNKNOWN_ERROR");
|
|
2955
3577
|
throw err;
|
|
2956
3578
|
} finally {
|
|
@@ -2963,66 +3585,6 @@ function useBridgeTransaction() {
|
|
|
2963
3585
|
hasQuote: !!quote
|
|
2964
3586
|
};
|
|
2965
3587
|
}
|
|
2966
|
-
function useGasEstimate(amountNum) {
|
|
2967
|
-
const { fromChain } = useChainsStore();
|
|
2968
|
-
const { selectedAssetSymbol, tokens } = useTokensStore();
|
|
2969
|
-
const getSourceGasReserveHuman = useSettingsStore(
|
|
2970
|
-
(state) => state.getSourceGasReserveHuman
|
|
2971
|
-
);
|
|
2972
|
-
const { srcAddress } = useAddresses();
|
|
2973
|
-
const { balances, isLoading: balancesLoading } = useBalances(
|
|
2974
|
-
fromChain?.chainKey,
|
|
2975
|
-
srcAddress
|
|
2976
|
-
);
|
|
2977
|
-
const { chainRegistry } = useChainStrategies();
|
|
2978
|
-
const balancesKnown = !balancesLoading;
|
|
2979
|
-
const strategy = react.useMemo(() => {
|
|
2980
|
-
if (!fromChain) return null;
|
|
2981
|
-
return chainRegistry.getStrategy(fromChain.chainKey);
|
|
2982
|
-
}, [fromChain, chainRegistry]);
|
|
2983
|
-
const { data: gasRequirement } = reactQuery.useQuery({
|
|
2984
|
-
queryKey: [
|
|
2985
|
-
"gas-estimate",
|
|
2986
|
-
fromChain?.chainKey,
|
|
2987
|
-
selectedAssetSymbol,
|
|
2988
|
-
amountNum,
|
|
2989
|
-
balances
|
|
2990
|
-
],
|
|
2991
|
-
queryFn: async () => {
|
|
2992
|
-
if (!fromChain || !strategy) {
|
|
2993
|
-
return null;
|
|
2994
|
-
}
|
|
2995
|
-
const selectedToken = tokens?.find(
|
|
2996
|
-
(t) => t.symbol.toUpperCase() === selectedAssetSymbol?.toUpperCase()
|
|
2997
|
-
) || null;
|
|
2998
|
-
const nativeTokenSymbol = fromChain.nativeCurrency?.symbol ?? "";
|
|
2999
|
-
const nativeDecimals = fromChain.nativeCurrency?.decimals || 18;
|
|
3000
|
-
const reserveFallback = getSourceGasReserveHuman(fromChain.chainKey);
|
|
3001
|
-
const result = await strategy.estimateGasRequirement({
|
|
3002
|
-
selectedToken,
|
|
3003
|
-
nativeTokenSymbol,
|
|
3004
|
-
amount: amountNum,
|
|
3005
|
-
balances,
|
|
3006
|
-
nativeDecimals,
|
|
3007
|
-
reserveFallback
|
|
3008
|
-
});
|
|
3009
|
-
return result;
|
|
3010
|
-
},
|
|
3011
|
-
enabled: !!fromChain && !!strategy,
|
|
3012
|
-
staleTime: 3e4,
|
|
3013
|
-
gcTime: 5 * 6e4,
|
|
3014
|
-
refetchOnWindowFocus: false,
|
|
3015
|
-
retry: 1
|
|
3016
|
-
});
|
|
3017
|
-
return {
|
|
3018
|
-
nativeSym: gasRequirement?.nativeSym || "",
|
|
3019
|
-
nativeBalance: gasRequirement?.nativeBalance || 0,
|
|
3020
|
-
requiredNative: gasRequirement?.requiredNative || 0,
|
|
3021
|
-
balancesKnown,
|
|
3022
|
-
isNativeSelected: gasRequirement?.isNativeSelected || false,
|
|
3023
|
-
hasEnoughGas: balancesKnown ? gasRequirement?.hasEnoughGas ?? true : true
|
|
3024
|
-
};
|
|
3025
|
-
}
|
|
3026
3588
|
function useBalanceCheck(amountNum, gas) {
|
|
3027
3589
|
const { fromChain } = useChainsStore();
|
|
3028
3590
|
const { selectedAssetSymbol } = useTokensStore();
|
|
@@ -3353,8 +3915,8 @@ const WalletSelectModal = () => {
|
|
|
3353
3915
|
{
|
|
3354
3916
|
strategy: tonWallet,
|
|
3355
3917
|
walletId: "ton",
|
|
3356
|
-
name: t("wallets.
|
|
3357
|
-
icon:
|
|
3918
|
+
name: t("wallets.tonconnect"),
|
|
3919
|
+
icon: TonConnectIcon
|
|
3358
3920
|
},
|
|
3359
3921
|
{
|
|
3360
3922
|
strategy: metaMaskWallet,
|
|
@@ -3392,8 +3954,8 @@ const WalletSelectModal = () => {
|
|
|
3392
3954
|
const tonWallets = [
|
|
3393
3955
|
{
|
|
3394
3956
|
id: "ton",
|
|
3395
|
-
name: t("wallets.
|
|
3396
|
-
icon:
|
|
3957
|
+
name: t("wallets.tonconnect"),
|
|
3958
|
+
icon: TonConnectIcon,
|
|
3397
3959
|
enabled: true
|
|
3398
3960
|
}
|
|
3399
3961
|
];
|
|
@@ -3428,9 +3990,9 @@ const WalletSelectModal = () => {
|
|
|
3428
3990
|
wallets: tronWallets.filter(notConnected)
|
|
3429
3991
|
}
|
|
3430
3992
|
].filter((category) => category.wallets.length > 0);
|
|
3431
|
-
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { children: [
|
|
3993
|
+
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "p-10", children: [
|
|
3432
3994
|
/* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogHeader, { className: "text-left", children: [
|
|
3433
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("wallets.chooseWallet") }),
|
|
3995
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { className: "text-2xl leading-8", children: t("wallets.chooseWallet") }),
|
|
3434
3996
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogDescription, { children: t("wallets.oneWalletPerEnv") })
|
|
3435
3997
|
] }),
|
|
3436
3998
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
@@ -3605,7 +4167,7 @@ const SuccessStep = ({
|
|
|
3605
4167
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 pt-10 px-8 flex-1 justify-start items-center text-center noise bg-background", children: [
|
|
3606
4168
|
icon,
|
|
3607
4169
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("transaction.success") }) }),
|
|
3608
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-
|
|
4170
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-2 mt-4 relative z-10 pb-14", children: [
|
|
3609
4171
|
metadata?.srcAmountHuman && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3610
4172
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.bridged") }),
|
|
3611
4173
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
|
|
@@ -3617,15 +4179,13 @@ const SuccessStep = ({
|
|
|
3617
4179
|
] }),
|
|
3618
4180
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3619
4181
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.transferTitle") }),
|
|
3620
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
4182
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium space-x-1", children: [
|
|
3621
4183
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3622
4184
|
metadata?.srcChainName,
|
|
3623
4185
|
" ",
|
|
3624
4186
|
/* @__PURE__ */ jsxRuntime.jsx(NetworkSymbol, { chainKey: metadata.srcChainName })
|
|
3625
4187
|
] }),
|
|
3626
|
-
" ",
|
|
3627
|
-
"→",
|
|
3628
|
-
" ",
|
|
4188
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "→" }),
|
|
3629
4189
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3630
4190
|
metadata?.dstChainName,
|
|
3631
4191
|
" ",
|
|
@@ -3639,17 +4199,17 @@ const SuccessStep = ({
|
|
|
3639
4199
|
"button",
|
|
3640
4200
|
{
|
|
3641
4201
|
onClick: handleOpenExplorer,
|
|
3642
|
-
className: "font-medium
|
|
4202
|
+
className: "font-medium cursor-pointer inline-flex items-center gap-1 underline hover:no-underline",
|
|
3643
4203
|
children: formatHash(srcTxHash)
|
|
3644
4204
|
}
|
|
3645
4205
|
)
|
|
3646
4206
|
] }),
|
|
3647
|
-
metadata?.
|
|
4207
|
+
metadata?.actualFeeValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3648
4208
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.finalFee") }),
|
|
3649
4209
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
3650
|
-
metadata.
|
|
4210
|
+
metadata.actualFeeValue,
|
|
3651
4211
|
" ",
|
|
3652
|
-
metadata.
|
|
4212
|
+
metadata.actualFeeSymbol
|
|
3653
4213
|
] })
|
|
3654
4214
|
] })
|
|
3655
4215
|
] })
|
|
@@ -3746,7 +4306,7 @@ const useTokens = () => {
|
|
|
3746
4306
|
return { ...query };
|
|
3747
4307
|
};
|
|
3748
4308
|
const useChains = () => {
|
|
3749
|
-
const { setChains, setFromChain } = useChainsStore();
|
|
4309
|
+
const { setChains, setFromChain, fromChain } = useChainsStore();
|
|
3750
4310
|
const query = reactQuery.useQuery({
|
|
3751
4311
|
queryKey: ["chains"],
|
|
3752
4312
|
queryFn: () => getChains(),
|
|
@@ -3760,10 +4320,16 @@ const useChains = () => {
|
|
|
3760
4320
|
});
|
|
3761
4321
|
react.useEffect(() => {
|
|
3762
4322
|
if (query.isSuccess && query.data?.length) {
|
|
4323
|
+
console.log(
|
|
4324
|
+
`[DEBUG] Loaded ${query.data.length} chains from API:`,
|
|
4325
|
+
query.data.map((c) => c.chainKey)
|
|
4326
|
+
);
|
|
3763
4327
|
setChains(query.data);
|
|
3764
|
-
|
|
4328
|
+
if (!fromChain) {
|
|
4329
|
+
setFromChain(query.data[0]);
|
|
4330
|
+
}
|
|
3765
4331
|
}
|
|
3766
|
-
}, [query.isSuccess, query.data
|
|
4332
|
+
}, [query.isSuccess, query.data]);
|
|
3767
4333
|
react.useEffect(() => {
|
|
3768
4334
|
if (query.isError && query.error) {
|
|
3769
4335
|
console.error(query.error.name, query.error.message);
|
|
@@ -3921,7 +4487,9 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
|
|
|
3921
4487
|
}
|
|
3922
4488
|
} catch (error) {
|
|
3923
4489
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3924
|
-
const isZeroDataError = errorMessage.includes(
|
|
4490
|
+
const isZeroDataError = errorMessage.includes(
|
|
4491
|
+
'returned no data ("0x")'
|
|
4492
|
+
);
|
|
3925
4493
|
if (!isZeroDataError) {
|
|
3926
4494
|
console.debug(
|
|
3927
4495
|
`Failed to get priority token balance for ${priorityToken.symbol}:`,
|
|
@@ -4032,12 +4600,9 @@ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
|
|
|
4032
4600
|
}
|
|
4033
4601
|
const client = getTonClient(customTonClient, tonApiKey);
|
|
4034
4602
|
const accountAddress = ton.Address.parse(address);
|
|
4035
|
-
console.log(address);
|
|
4036
|
-
console.log(tokens);
|
|
4037
4603
|
try {
|
|
4038
4604
|
const balance = await client.getBalance(accountAddress);
|
|
4039
4605
|
const tonBalance = Number(balance) / 1e9;
|
|
4040
|
-
console.log("tonBalance", tonBalance);
|
|
4041
4606
|
if (tonBalance > 0) {
|
|
4042
4607
|
balances.TON = { balance: tonBalance, address: "ton-native" };
|
|
4043
4608
|
}
|
|
@@ -4094,8 +4659,23 @@ async function getTronBalances(tronWeb, address, tokens) {
|
|
|
4094
4659
|
try {
|
|
4095
4660
|
if (!tronWeb) throw new Error("TronWeb not available");
|
|
4096
4661
|
const ownerB58 = toTronBase58(address, tronWeb);
|
|
4662
|
+
try {
|
|
4663
|
+
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
4664
|
+
const sunNum = Number(trxBalanceSun);
|
|
4665
|
+
const trxVal = tronWeb.fromSun(sunNum);
|
|
4666
|
+
const trxStr = typeof trxVal === "string" ? trxVal : trxVal.toString();
|
|
4667
|
+
const trxBalance = parseFloat(trxStr);
|
|
4668
|
+
if (trxBalance > 0) {
|
|
4669
|
+
balances.TRX = { balance: trxBalance, address: ownerB58 };
|
|
4670
|
+
}
|
|
4671
|
+
} catch (error) {
|
|
4672
|
+
console.warn("Failed to get native TRX balance:", error);
|
|
4673
|
+
}
|
|
4097
4674
|
for (const token of tokens) {
|
|
4098
4675
|
try {
|
|
4676
|
+
if (isNativeAddress(token.address) && token.symbol.toUpperCase() === "TRX") {
|
|
4677
|
+
continue;
|
|
4678
|
+
}
|
|
4099
4679
|
let balance = 0;
|
|
4100
4680
|
if (isNativeAddress(token.address)) {
|
|
4101
4681
|
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
@@ -4182,6 +4762,9 @@ class EvmChainStrategy {
|
|
|
4182
4762
|
getConnectLabel(t) {
|
|
4183
4763
|
return t("wallets.connectEvmWallet");
|
|
4184
4764
|
}
|
|
4765
|
+
getClient() {
|
|
4766
|
+
return this.provider;
|
|
4767
|
+
}
|
|
4185
4768
|
async getBalances(address, tokens, priorityToken) {
|
|
4186
4769
|
if (!this.publicClient) {
|
|
4187
4770
|
console.warn("No publicClient available for balance query");
|
|
@@ -4198,52 +4781,6 @@ class EvmChainStrategy {
|
|
|
4198
4781
|
if (!address) return false;
|
|
4199
4782
|
return /^0x[0-9a-fA-F]{40}$/.test(address);
|
|
4200
4783
|
}
|
|
4201
|
-
async estimateGasRequirement(params) {
|
|
4202
|
-
const provider = this.provider;
|
|
4203
|
-
const {
|
|
4204
|
-
selectedToken,
|
|
4205
|
-
nativeTokenSymbol,
|
|
4206
|
-
amount,
|
|
4207
|
-
balances,
|
|
4208
|
-
nativeDecimals,
|
|
4209
|
-
reserveFallback
|
|
4210
|
-
} = params;
|
|
4211
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4212
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4213
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4214
|
-
let estimatedGas = null;
|
|
4215
|
-
if (provider) {
|
|
4216
|
-
try {
|
|
4217
|
-
const gasPriceHex = await provider.send("eth_gasPrice", []);
|
|
4218
|
-
const gasPrice = BigInt(gasPriceHex);
|
|
4219
|
-
const approveGas = isNativeSelected ? 0n : EVM_CONFIG.gasEstimates.approve;
|
|
4220
|
-
const bridgeGas = EVM_CONFIG.gasEstimates.bridge;
|
|
4221
|
-
const totalGas = approveGas + bridgeGas;
|
|
4222
|
-
const bufferMultiplier = BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100));
|
|
4223
|
-
const requiredWei = gasPrice * totalGas * bufferMultiplier / 100n;
|
|
4224
|
-
const { formatUnits } = await import("ethers");
|
|
4225
|
-
estimatedGas = Number(formatUnits(requiredWei, nativeDecimals));
|
|
4226
|
-
} catch {
|
|
4227
|
-
estimatedGas = null;
|
|
4228
|
-
}
|
|
4229
|
-
}
|
|
4230
|
-
const requiredNative = estimatedGas ?? reserveFallback;
|
|
4231
|
-
const amountNum = amount ?? 0;
|
|
4232
|
-
let hasEnoughGas = true;
|
|
4233
|
-
if (isNativeSelected) {
|
|
4234
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4235
|
-
} else {
|
|
4236
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4237
|
-
}
|
|
4238
|
-
return {
|
|
4239
|
-
estimatedGas,
|
|
4240
|
-
nativeBalance,
|
|
4241
|
-
requiredNative,
|
|
4242
|
-
hasEnoughGas,
|
|
4243
|
-
nativeSym,
|
|
4244
|
-
isNativeSelected
|
|
4245
|
-
};
|
|
4246
|
-
}
|
|
4247
4784
|
validateSteps(steps) {
|
|
4248
4785
|
if (!steps || steps.length === 0) {
|
|
4249
4786
|
throw new InvalidStepsError("evm", "No transaction steps provided");
|
|
@@ -4336,8 +4873,24 @@ class EvmChainStrategy {
|
|
|
4336
4873
|
console.log(
|
|
4337
4874
|
`EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
|
|
4338
4875
|
);
|
|
4876
|
+
let actualFeeValue;
|
|
4877
|
+
try {
|
|
4878
|
+
const gasUsed = receipt.gasUsed;
|
|
4879
|
+
const effectiveGasPrice = receipt.gasPrice;
|
|
4880
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4881
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4882
|
+
const { formatUnits } = await import("ethers");
|
|
4883
|
+
const feeInNative = formatUnits(feeWei, 18);
|
|
4884
|
+
actualFeeValue = feeInNative;
|
|
4885
|
+
console.log(`EVM transaction fee: ${feeInNative} (native token)`);
|
|
4886
|
+
}
|
|
4887
|
+
} catch (error) {
|
|
4888
|
+
console.warn("Failed to calculate actual fee:", error);
|
|
4889
|
+
}
|
|
4339
4890
|
return {
|
|
4340
|
-
completed: true
|
|
4891
|
+
completed: true,
|
|
4892
|
+
actualFeeValue
|
|
4893
|
+
// Symbol will be determined by the caller based on chain info
|
|
4341
4894
|
};
|
|
4342
4895
|
} catch (error) {
|
|
4343
4896
|
if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
|
|
@@ -4350,8 +4903,26 @@ class EvmChainStrategy {
|
|
|
4350
4903
|
console.log(
|
|
4351
4904
|
`Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
|
|
4352
4905
|
);
|
|
4906
|
+
let actualFeeValue;
|
|
4907
|
+
try {
|
|
4908
|
+
const receipt = error.receipt;
|
|
4909
|
+
const gasUsed = receipt.gasUsed;
|
|
4910
|
+
const effectiveGasPrice = receipt.gasPrice || receipt.effectiveGasPrice;
|
|
4911
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4912
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4913
|
+
const { formatUnits } = await import("ethers");
|
|
4914
|
+
const feeInNative = formatUnits(feeWei, 18);
|
|
4915
|
+
actualFeeValue = feeInNative;
|
|
4916
|
+
console.log(
|
|
4917
|
+
`Replacement transaction fee: ${feeInNative} (native token)`
|
|
4918
|
+
);
|
|
4919
|
+
}
|
|
4920
|
+
} catch (feeError) {
|
|
4921
|
+
console.warn("Failed to calculate replacement transaction fee:", feeError);
|
|
4922
|
+
}
|
|
4353
4923
|
return {
|
|
4354
|
-
completed: true
|
|
4924
|
+
completed: true,
|
|
4925
|
+
actualFeeValue
|
|
4355
4926
|
};
|
|
4356
4927
|
} else {
|
|
4357
4928
|
const chainError2 = new TransactionRevertedError("evm", txHash);
|
|
@@ -4554,6 +5125,9 @@ class TonChainStrategy {
|
|
|
4554
5125
|
getConnectLabel(t) {
|
|
4555
5126
|
return t("wallets.connectTonWallet");
|
|
4556
5127
|
}
|
|
5128
|
+
getClient() {
|
|
5129
|
+
return getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
5130
|
+
}
|
|
4557
5131
|
async getBalances(address, tokens) {
|
|
4558
5132
|
return await getTonBalances(
|
|
4559
5133
|
address,
|
|
@@ -4566,39 +5140,6 @@ class TonChainStrategy {
|
|
|
4566
5140
|
if (!address) return false;
|
|
4567
5141
|
return true;
|
|
4568
5142
|
}
|
|
4569
|
-
async estimateGasRequirement(params) {
|
|
4570
|
-
const {
|
|
4571
|
-
selectedToken,
|
|
4572
|
-
nativeTokenSymbol,
|
|
4573
|
-
amount,
|
|
4574
|
-
balances,
|
|
4575
|
-
nativeDecimals = 9,
|
|
4576
|
-
reserveFallback
|
|
4577
|
-
} = params;
|
|
4578
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4579
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4580
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4581
|
-
const { formatUnits } = await import("ethers");
|
|
4582
|
-
const estimatedGas = Number(
|
|
4583
|
-
formatUnits(TON_CONFIG.estimatedNetworkFee, nativeDecimals)
|
|
4584
|
-
);
|
|
4585
|
-
const requiredNative = estimatedGas > 0 ? estimatedGas : reserveFallback;
|
|
4586
|
-
const amountNum = amount ?? 0;
|
|
4587
|
-
let hasEnoughGas = true;
|
|
4588
|
-
if (isNativeSelected) {
|
|
4589
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4590
|
-
} else {
|
|
4591
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4592
|
-
}
|
|
4593
|
-
return {
|
|
4594
|
-
estimatedGas,
|
|
4595
|
-
nativeBalance,
|
|
4596
|
-
requiredNative,
|
|
4597
|
-
hasEnoughGas,
|
|
4598
|
-
nativeSym,
|
|
4599
|
-
isNativeSelected
|
|
4600
|
-
};
|
|
4601
|
-
}
|
|
4602
5143
|
validateSteps(steps) {
|
|
4603
5144
|
if (!steps || steps.length === 0) {
|
|
4604
5145
|
throw new InvalidStepsError("ton", "No transaction steps provided");
|
|
@@ -4675,11 +5216,11 @@ class TonChainStrategy {
|
|
|
4675
5216
|
}
|
|
4676
5217
|
async waitForCompletion(txHash) {
|
|
4677
5218
|
try {
|
|
4678
|
-
const
|
|
5219
|
+
const result = await this.checkTonTransaction(
|
|
4679
5220
|
txHash,
|
|
4680
5221
|
TON_CONFIG.timeout
|
|
4681
5222
|
);
|
|
4682
|
-
if (!confirmed) {
|
|
5223
|
+
if (!result.confirmed) {
|
|
4683
5224
|
const error = new TransactionFailedError(
|
|
4684
5225
|
"ton",
|
|
4685
5226
|
"Transaction not confirmed on-chain",
|
|
@@ -4691,7 +5232,9 @@ class TonChainStrategy {
|
|
|
4691
5232
|
};
|
|
4692
5233
|
}
|
|
4693
5234
|
return {
|
|
4694
|
-
completed: true
|
|
5235
|
+
completed: true,
|
|
5236
|
+
actualFeeValue: result.fee,
|
|
5237
|
+
actualFeeSymbol: "TON"
|
|
4695
5238
|
};
|
|
4696
5239
|
} catch (error) {
|
|
4697
5240
|
const chainError = toChainStrategyError(
|
|
@@ -4734,7 +5277,7 @@ class TonChainStrategy {
|
|
|
4734
5277
|
"Expected external-in message, got:",
|
|
4735
5278
|
inMessage.info.type
|
|
4736
5279
|
);
|
|
4737
|
-
return false;
|
|
5280
|
+
return { confirmed: false };
|
|
4738
5281
|
}
|
|
4739
5282
|
accountAddress = inMessage.info.dest;
|
|
4740
5283
|
targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
@@ -4742,7 +5285,7 @@ class TonChainStrategy {
|
|
|
4742
5285
|
targetMessageHash = Buffer.from(hashOrBoc, "hex");
|
|
4743
5286
|
if (!this.config.tonAddress) {
|
|
4744
5287
|
console.debug("No wallet address available for hex hash lookup");
|
|
4745
|
-
return false;
|
|
5288
|
+
return { confirmed: false };
|
|
4746
5289
|
}
|
|
4747
5290
|
accountAddress = core.Address.parse(this.config.tonAddress);
|
|
4748
5291
|
}
|
|
@@ -4769,7 +5312,13 @@ class TonChainStrategy {
|
|
|
4769
5312
|
);
|
|
4770
5313
|
if (txInMessageHash.equals(targetMessageHash)) {
|
|
4771
5314
|
console.debug("Transaction found by in-message hash");
|
|
4772
|
-
|
|
5315
|
+
const totalFees = tx.totalFees;
|
|
5316
|
+
const feeInTon = Number(totalFees) / 1e9;
|
|
5317
|
+
console.log(`TON transaction fee: ${feeInTon} TON`);
|
|
5318
|
+
return {
|
|
5319
|
+
confirmed: true,
|
|
5320
|
+
fee: feeInTon.toString()
|
|
5321
|
+
};
|
|
4773
5322
|
}
|
|
4774
5323
|
}
|
|
4775
5324
|
}
|
|
@@ -4784,10 +5333,10 @@ class TonChainStrategy {
|
|
|
4784
5333
|
hash = void 0;
|
|
4785
5334
|
}
|
|
4786
5335
|
}
|
|
4787
|
-
return false;
|
|
5336
|
+
return { confirmed: false };
|
|
4788
5337
|
} catch (error) {
|
|
4789
5338
|
console.debug("Error parsing BOC or checking transaction:", error);
|
|
4790
|
-
return false;
|
|
5339
|
+
return { confirmed: false };
|
|
4791
5340
|
}
|
|
4792
5341
|
}
|
|
4793
5342
|
}
|
|
@@ -4841,7 +5390,7 @@ class TronChainStrategy {
|
|
|
4841
5390
|
return t("wallets.connectTronWallet");
|
|
4842
5391
|
}
|
|
4843
5392
|
async getBalances(address, tokens) {
|
|
4844
|
-
const tronWeb = this.
|
|
5393
|
+
const tronWeb = this.getClient();
|
|
4845
5394
|
if (!tronWeb) return {};
|
|
4846
5395
|
return await getTronBalances(tronWeb, address, tokens);
|
|
4847
5396
|
}
|
|
@@ -4849,35 +5398,6 @@ class TronChainStrategy {
|
|
|
4849
5398
|
if (!address) return false;
|
|
4850
5399
|
return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
|
|
4851
5400
|
}
|
|
4852
|
-
async estimateGasRequirement(params) {
|
|
4853
|
-
const {
|
|
4854
|
-
selectedToken,
|
|
4855
|
-
nativeTokenSymbol,
|
|
4856
|
-
amount,
|
|
4857
|
-
balances,
|
|
4858
|
-
reserveFallback
|
|
4859
|
-
} = params;
|
|
4860
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4861
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4862
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4863
|
-
const estimatedGas = null;
|
|
4864
|
-
const requiredNative = reserveFallback;
|
|
4865
|
-
const amountNum = amount ?? 0;
|
|
4866
|
-
let hasEnoughGas = true;
|
|
4867
|
-
if (isNativeSelected) {
|
|
4868
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4869
|
-
} else {
|
|
4870
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4871
|
-
}
|
|
4872
|
-
return {
|
|
4873
|
-
estimatedGas,
|
|
4874
|
-
nativeBalance,
|
|
4875
|
-
requiredNative,
|
|
4876
|
-
hasEnoughGas,
|
|
4877
|
-
nativeSym,
|
|
4878
|
-
isNativeSelected
|
|
4879
|
-
};
|
|
4880
|
-
}
|
|
4881
5401
|
validateSteps(steps) {
|
|
4882
5402
|
console.log("validateSteps");
|
|
4883
5403
|
if (!steps?.length) {
|
|
@@ -4894,8 +5414,7 @@ class TronChainStrategy {
|
|
|
4894
5414
|
}
|
|
4895
5415
|
}
|
|
4896
5416
|
async executeSteps(steps, _context, onFirstHash) {
|
|
4897
|
-
|
|
4898
|
-
const tronWeb = this.getTronWeb();
|
|
5417
|
+
const tronWeb = this.getClient();
|
|
4899
5418
|
if (!tronWeb) {
|
|
4900
5419
|
throw new ProviderNotAvailableError("tron");
|
|
4901
5420
|
}
|
|
@@ -4989,7 +5508,7 @@ class TronChainStrategy {
|
|
|
4989
5508
|
}
|
|
4990
5509
|
async waitForCompletion(txHash) {
|
|
4991
5510
|
try {
|
|
4992
|
-
const tronWeb = this.
|
|
5511
|
+
const tronWeb = this.getClient();
|
|
4993
5512
|
if (!tronWeb) {
|
|
4994
5513
|
throw new ProviderNotAvailableError("tron");
|
|
4995
5514
|
}
|
|
@@ -4998,6 +5517,7 @@ class TronChainStrategy {
|
|
|
4998
5517
|
);
|
|
4999
5518
|
const deadline = Date.now() + TRON_CONFIG.timeout;
|
|
5000
5519
|
let txBlockNumber = null;
|
|
5520
|
+
let actualFeeTrx = null;
|
|
5001
5521
|
while (Date.now() < deadline && !txBlockNumber) {
|
|
5002
5522
|
try {
|
|
5003
5523
|
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
@@ -5032,7 +5552,9 @@ class TronChainStrategy {
|
|
|
5032
5552
|
};
|
|
5033
5553
|
}
|
|
5034
5554
|
txBlockNumber = info.blockNumber;
|
|
5035
|
-
|
|
5555
|
+
const feeSun = info.fee || 0;
|
|
5556
|
+
actualFeeTrx = feeSun / 1e6;
|
|
5557
|
+
console.log(`TRON transaction found in block ${txBlockNumber}, fee: ${actualFeeTrx} TRX`);
|
|
5036
5558
|
}
|
|
5037
5559
|
} catch (e) {
|
|
5038
5560
|
console.debug("TRON getTransactionInfo error:", e);
|
|
@@ -5060,7 +5582,9 @@ class TronChainStrategy {
|
|
|
5060
5582
|
`TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
|
|
5061
5583
|
);
|
|
5062
5584
|
return {
|
|
5063
|
-
completed: true
|
|
5585
|
+
completed: true,
|
|
5586
|
+
actualFeeValue: actualFeeTrx?.toString(),
|
|
5587
|
+
actualFeeSymbol: "TRX"
|
|
5064
5588
|
};
|
|
5065
5589
|
}
|
|
5066
5590
|
console.log(
|
|
@@ -5089,7 +5613,7 @@ class TronChainStrategy {
|
|
|
5089
5613
|
};
|
|
5090
5614
|
}
|
|
5091
5615
|
}
|
|
5092
|
-
|
|
5616
|
+
getClient() {
|
|
5093
5617
|
return typeof window !== "undefined" ? window.tronWeb : void 0;
|
|
5094
5618
|
}
|
|
5095
5619
|
/**
|
|
@@ -5406,7 +5930,7 @@ class TronChainStrategy {
|
|
|
5406
5930
|
*/
|
|
5407
5931
|
async checkSolidified(txHash) {
|
|
5408
5932
|
try {
|
|
5409
|
-
const tronWeb = this.
|
|
5933
|
+
const tronWeb = this.getClient();
|
|
5410
5934
|
if (!tronWeb) {
|
|
5411
5935
|
return false;
|
|
5412
5936
|
}
|
|
@@ -5545,14 +6069,14 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|
|
5545
6069
|
);
|
|
5546
6070
|
const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
|
|
5547
6071
|
const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
|
|
5548
|
-
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), children: [
|
|
5549
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("settings.title") }) }),
|
|
6072
|
+
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), className: "p-10", children: [
|
|
6073
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { className: "text-2xl leading-8", children: t("settings.title") }) }),
|
|
5550
6074
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5", children: [
|
|
5551
6075
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5552
6076
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5553
6077
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5554
6078
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.gasOnDestination") }),
|
|
5555
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6079
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5556
6080
|
] }),
|
|
5557
6081
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
|
|
5558
6082
|
] }),
|
|
@@ -5574,9 +6098,8 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|
|
5574
6098
|
badge.Badge,
|
|
5575
6099
|
{
|
|
5576
6100
|
onClick: () => setGasPreset(g),
|
|
5577
|
-
size: "lg",
|
|
5578
6101
|
className: utils.cn(
|
|
5579
|
-
"cursor-pointer",
|
|
6102
|
+
"cursor-pointer h-7 rounded px-2",
|
|
5580
6103
|
gasPreset === g ? activeBtn : notActiveBtn
|
|
5581
6104
|
),
|
|
5582
6105
|
children: t(`settings.gasPresets.${g}`)
|
|
@@ -5590,7 +6113,7 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|
|
5590
6113
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5591
6114
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5592
6115
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.slippageTolerance") }),
|
|
5593
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6116
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5594
6117
|
] }),
|
|
5595
6118
|
slippageBps >= 500 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
|
|
5596
6119
|
defaultValue: "High slippage warning"
|
|
@@ -5601,13 +6124,12 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|
|
5601
6124
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5602
6125
|
badge.Badge,
|
|
5603
6126
|
{
|
|
5604
|
-
size: "lg",
|
|
5605
6127
|
onClick: () => {
|
|
5606
6128
|
const bps = parseFloat(p.replace("%", "")) * 100;
|
|
5607
6129
|
setSlippageBps(bps);
|
|
5608
6130
|
},
|
|
5609
6131
|
className: utils.cn(
|
|
5610
|
-
"cursor-pointer",
|
|
6132
|
+
"cursor-pointer h-7 rounded px-2",
|
|
5611
6133
|
activeSlippagePreset === p ? activeBtn : notActiveBtn
|
|
5612
6134
|
),
|
|
5613
6135
|
children: p
|
|
@@ -5620,15 +6142,14 @@ const SettingsModal = ({ isOpen, onClose }) => {
|
|
|
5620
6142
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5621
6143
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5622
6144
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.routePriority") }),
|
|
5623
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6145
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5624
6146
|
] }) }),
|
|
5625
6147
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5626
6148
|
badge.Badge,
|
|
5627
6149
|
{
|
|
5628
|
-
size: "lg",
|
|
5629
6150
|
onClick: () => setRoutePriority(r),
|
|
5630
6151
|
className: utils.cn(
|
|
5631
|
-
"cursor-pointer",
|
|
6152
|
+
"cursor-pointer h-7 rounded px-2",
|
|
5632
6153
|
routePriority === r ? activeBtn : notActiveBtn
|
|
5633
6154
|
),
|
|
5634
6155
|
children: t(`settings.routePresets.${r}`)
|
|
@@ -5653,7 +6174,7 @@ const TokenRow = ({
|
|
|
5653
6174
|
button.Button,
|
|
5654
6175
|
{
|
|
5655
6176
|
onClick: onPick,
|
|
5656
|
-
className: `w-full rounded-
|
|
6177
|
+
className: `w-full px-5 rounded-xs bg-transparent flex items-center justify-between gap-3 hover:bg-accent hover:scale-100 ${isSelected ? "bg-accent hover:bg-accent/50" : ""}`,
|
|
5657
6178
|
children: [
|
|
5658
6179
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5659
6180
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5665,16 +6186,16 @@ const TokenRow = ({
|
|
|
5665
6186
|
}
|
|
5666
6187
|
),
|
|
5667
6188
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-1", children: [
|
|
5668
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-foreground text-
|
|
5669
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3
|
|
6189
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold text-foreground text-sm leading-4 truncate flex items-center gap-1", children: symbol }),
|
|
6190
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground truncate", children: name })
|
|
5670
6191
|
] })
|
|
5671
6192
|
] }),
|
|
5672
6193
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right space-y-1", children: isBalanceLoading && hasAnyWallet ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1", children: [
|
|
5673
6194
|
/* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-5 w-16 rounded-md" }),
|
|
5674
6195
|
/* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-3 w-12 rounded-md" })
|
|
5675
6196
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5676
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground text-
|
|
5677
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
6197
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground text-sm leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
|
|
6198
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground/50", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
5678
6199
|
] }) })
|
|
5679
6200
|
]
|
|
5680
6201
|
}
|
|
@@ -5817,8 +6338,8 @@ const TokenSelectModal = ({
|
|
|
5817
6338
|
[groupedTokens.willChangeSrcChain]
|
|
5818
6339
|
);
|
|
5819
6340
|
const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
|
|
5820
|
-
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "max-h-[90dvh] h-[90dvh] overflow-hidden flex flex-col", children: [
|
|
5821
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectToken") }) }),
|
|
6341
|
+
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && handleClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { className: "md:max-h-[90dvh] md:h-[90dvh] overflow-hidden flex flex-col p-10", children: [
|
|
6342
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { className: "text-2xl leading-8", children: t("bridge.selectToken") }) }),
|
|
5822
6343
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5823
6344
|
SearchInput,
|
|
5824
6345
|
{
|
|
@@ -5831,24 +6352,29 @@ const TokenSelectModal = ({
|
|
|
5831
6352
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5832
6353
|
button.Button,
|
|
5833
6354
|
{
|
|
5834
|
-
variant: tab === "my" ? "default" : "
|
|
6355
|
+
variant: tab === "my" ? "default" : "ghost",
|
|
5835
6356
|
onClick: () => setTab("my"),
|
|
5836
6357
|
size: "sm",
|
|
6358
|
+
className: utils.cn("px-4", tab !== "my" && "bg-muted hover:bg-accent"),
|
|
5837
6359
|
children: t("bridge.myTokens")
|
|
5838
6360
|
}
|
|
5839
6361
|
),
|
|
5840
6362
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5841
6363
|
button.Button,
|
|
5842
6364
|
{
|
|
5843
|
-
variant: tab === "all" ? "default" : "
|
|
6365
|
+
variant: tab === "all" ? "default" : "ghost",
|
|
5844
6366
|
onClick: () => setTab("all"),
|
|
5845
6367
|
size: "sm",
|
|
6368
|
+
className: utils.cn(
|
|
6369
|
+
"px-4",
|
|
6370
|
+
tab !== "all" && "bg-muted hover:bg-accent"
|
|
6371
|
+
),
|
|
5846
6372
|
children: t("bridge.allTokens")
|
|
5847
6373
|
}
|
|
5848
6374
|
)
|
|
5849
6375
|
] }),
|
|
5850
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto -mx-
|
|
5851
|
-
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-4 text-base
|
|
6376
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto -mx-5", children: hasNoResults ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground px-5 py-4", children: t("bridge.noResults") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6377
|
+
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-4 px-5 text-base text-muted-foreground uppercase py-2", children: t("bridge.noBalancesFound") }),
|
|
5852
6378
|
tokensToRender.map(({ token, willChangeSrc }) => {
|
|
5853
6379
|
const bal = getBalance(token.symbol);
|
|
5854
6380
|
const usd = getTokenUsdValue(token.symbol, token.price?.usd);
|
|
@@ -5869,7 +6395,7 @@ const TokenSelectModal = ({
|
|
|
5869
6395
|
);
|
|
5870
6396
|
}),
|
|
5871
6397
|
effectiveTab === "all" && willChangeSrcTokens.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5872
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 flex leading-4 text-base py-2
|
|
6398
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 flex leading-4 text-base py-2 text-muted-foreground uppercase mt-8", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: t("bridge.willChangeSourceChain") }) }),
|
|
5873
6399
|
willChangeSrcTokens.map(({ token, willChangeSrc }) => {
|
|
5874
6400
|
const bal = getBalance(token.symbol);
|
|
5875
6401
|
const usd = getTokenUsdValue(
|
|
@@ -5899,6 +6425,7 @@ const TokenSelectModal = ({
|
|
|
5899
6425
|
function useBridgeRefresh() {
|
|
5900
6426
|
const qc = reactQuery.useQueryClient();
|
|
5901
6427
|
const { srcAddress, dstAddress } = useAddresses();
|
|
6428
|
+
const { fromChain, toChain } = useChainsStore();
|
|
5902
6429
|
const { setLoading: setQLoading, triggerRefetch } = useBridgeQuoteStore();
|
|
5903
6430
|
const { hasAnyWallet } = useConnectedWalletsStore();
|
|
5904
6431
|
const [spinning, setSpinning] = react.useState(false);
|
|
@@ -5911,14 +6438,30 @@ function useBridgeRefresh() {
|
|
|
5911
6438
|
qc.invalidateQueries({ queryKey: ["chains"] })
|
|
5912
6439
|
];
|
|
5913
6440
|
if (hasAnyWallet()) {
|
|
5914
|
-
if (srcAddress)
|
|
6441
|
+
if (srcAddress) {
|
|
5915
6442
|
queries.push(
|
|
5916
6443
|
qc.invalidateQueries({ queryKey: ["srcTokens", srcAddress] })
|
|
5917
6444
|
);
|
|
5918
|
-
|
|
6445
|
+
if (fromChain?.chainKey) {
|
|
6446
|
+
queries.push(
|
|
6447
|
+
qc.invalidateQueries({
|
|
6448
|
+
queryKey: ["balances", fromChain.chainKey, srcAddress]
|
|
6449
|
+
})
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
if (dstAddress) {
|
|
5919
6454
|
queries.push(
|
|
5920
6455
|
qc.invalidateQueries({ queryKey: ["dstTokens", dstAddress] })
|
|
5921
6456
|
);
|
|
6457
|
+
if (toChain?.chainKey) {
|
|
6458
|
+
queries.push(
|
|
6459
|
+
qc.invalidateQueries({
|
|
6460
|
+
queryKey: ["balances", toChain.chainKey, dstAddress]
|
|
6461
|
+
})
|
|
6462
|
+
);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
5922
6465
|
}
|
|
5923
6466
|
await Promise.all(queries);
|
|
5924
6467
|
triggerRefetch();
|
|
@@ -5929,6 +6472,8 @@ function useBridgeRefresh() {
|
|
|
5929
6472
|
qc,
|
|
5930
6473
|
srcAddress,
|
|
5931
6474
|
dstAddress,
|
|
6475
|
+
fromChain?.chainKey,
|
|
6476
|
+
toChain?.chainKey,
|
|
5932
6477
|
hasAnyWallet,
|
|
5933
6478
|
setQLoading,
|
|
5934
6479
|
triggerRefetch
|
|
@@ -5948,7 +6493,7 @@ const RefreshButton = () => {
|
|
|
5948
6493
|
onClick: handleRefresh,
|
|
5949
6494
|
disabled: spinning,
|
|
5950
6495
|
variant: "secondary",
|
|
5951
|
-
className: "
|
|
6496
|
+
className: "h-9 w-11",
|
|
5952
6497
|
size: "sm",
|
|
5953
6498
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5954
6499
|
ReloadIcon,
|
|
@@ -5976,7 +6521,7 @@ const SelectTokenButton = ({
|
|
|
5976
6521
|
onClick,
|
|
5977
6522
|
size: "sm",
|
|
5978
6523
|
variant: "secondary",
|
|
5979
|
-
className: "shrink-0 gap-2
|
|
6524
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
5980
6525
|
type: "button",
|
|
5981
6526
|
"aria-label": label,
|
|
5982
6527
|
children: [
|
|
@@ -6024,7 +6569,7 @@ const Toolbar = () => {
|
|
|
6024
6569
|
{
|
|
6025
6570
|
onClick: onOpenSettings,
|
|
6026
6571
|
size: "sm",
|
|
6027
|
-
className: "
|
|
6572
|
+
className: "h-9 w-11",
|
|
6028
6573
|
variant: "secondary",
|
|
6029
6574
|
children: /* @__PURE__ */ jsxRuntime.jsx(BoltIcon, { stroke: "currentColor" })
|
|
6030
6575
|
}
|
|
@@ -6304,11 +6849,16 @@ exports.DEFAULT_SLIPPAGE_BPS = DEFAULT_SLIPPAGE_BPS;
|
|
|
6304
6849
|
exports.EvaaBridge = EvaaBridge;
|
|
6305
6850
|
exports.RoutePriority = RoutePriority;
|
|
6306
6851
|
exports.RouteType = RouteType;
|
|
6852
|
+
exports.addEvmNetworkFee = addEvmNetworkFee;
|
|
6307
6853
|
exports.addTonNetworkFee = addTonNetworkFee;
|
|
6854
|
+
exports.addTronNetworkFee = addTronNetworkFee;
|
|
6308
6855
|
exports.addrForApi = addrForApi;
|
|
6309
6856
|
exports.buildAssetMatrix = buildAssetMatrix;
|
|
6310
6857
|
exports.calculateMinReceived = calculateMinReceived;
|
|
6311
6858
|
exports.computeFeesUsdFromArray = computeFeesUsdFromArray;
|
|
6859
|
+
exports.estimateEvmNetworkFee = estimateEvmNetworkFee;
|
|
6860
|
+
exports.estimateTonNetworkFee = estimateTonNetworkFee;
|
|
6861
|
+
exports.estimateTronNetworkFee = estimateTronNetworkFee;
|
|
6312
6862
|
exports.findNativeMeta = findNativeMeta;
|
|
6313
6863
|
exports.formatAddress = formatAddress;
|
|
6314
6864
|
exports.formatBalance = formatBalance;
|