@rash2x/bridge-widget 0.2.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -0
- package/README.md +126 -157
- package/dist/evaa-bridge.cjs +1402 -673
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +1406 -677
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +63 -3
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/assets/fonts/Gilroy-Black.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Bold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-ExtraBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Heavy.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Light.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Medium.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Regular.ttf +0 -0
- package/dist/assets/fonts/Gilroy-SemiBold.ttf +0 -0
- package/dist/assets/fonts/Gilroy-Thin.ttf +0 -0
- package/dist/assets/fonts/Gilroy-UltraLight.ttf +0 -0
- package/dist/assets/fonts/fonts.css +0 -81
- package/dist/assets/fonts/hanson-bold.woff +0 -0
- package/dist/assets/fonts/hanson-bold.woff2 +0 -0
package/dist/evaa-bridge.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");
|
|
@@ -45,15 +46,15 @@ const framerMotion = require("framer-motion");
|
|
|
45
46
|
const accordion = require("@/components/ui/accordion");
|
|
46
47
|
const tooltip = require("@/components/ui/tooltip");
|
|
47
48
|
const sonner = require("sonner");
|
|
49
|
+
const reactDialog = require("@radix-ui/react-dialog");
|
|
48
50
|
const ethers = require("ethers");
|
|
49
51
|
const viem = require("viem");
|
|
50
|
-
const ton = require("@ton/ton");
|
|
51
52
|
const tronwalletAdapters = require("@tronweb3/tronwallet-adapters");
|
|
52
53
|
const card = require("@/components/ui/card");
|
|
53
54
|
const badge = require("@/components/ui/badge");
|
|
54
55
|
const common$1 = { "connecting": "Connecting…", "initializing": "Initializing...", "loading": "Loading...", "paste": "paste", "close": "Close", "zeroPlaceholder": "0", "nativeToken": "Native Token" };
|
|
55
|
-
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", "
|
|
56
|
-
const bridge$1 = { "sourceNetwork": "Source network", "destinationNetwork": "Destination network", "selectToken": "Select token", "selectNetwork": "Select network", "searchToken": "Search token", "searchDestinationChain": "Search destination chain", "myTokens": "My tokens", "allTokens": "All tokens", "willChangeSourceChain": "Will
|
|
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 network and token", "noBalancesFound": "No balances found.", "noResults": "No results", "sendToAnotherAddress": "Send to another address", "youWillReceive": "You will receive", "anotherAddressPlaceholder": "Address", "addressDoesntMatch": "Address doesn't match the {{network}} network", "checkBeforeTransfer": "Check correctness before transfer" };
|
|
57
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" };
|
|
58
59
|
const app$1 = { "stargateWidgetName": "Stargate Bridge Widget", "liveWidget": "Live Widget", "getStarted": "Get Started" };
|
|
59
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" } };
|
|
@@ -68,8 +69,8 @@ const en = {
|
|
|
68
69
|
errors: errors$1
|
|
69
70
|
};
|
|
70
71
|
const common = { "connecting": "Подключение…", "initializing": "Инициализация...", "loading": "Загрузка...", "paste": "вставить", "close": "Закрыть", "zeroPlaceholder": "0", "nativeToken": "Нативный токен" };
|
|
71
|
-
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", "
|
|
72
|
-
const bridge = { "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": "Проверьте корректность перед переводом" };
|
|
73
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": "Ошибка котировки" };
|
|
74
75
|
const app = { "stargateWidgetName": "Виджет Stargate Bridge", "liveWidget": "Живой виджет", "getStarted": "Начало работы" };
|
|
75
76
|
const settings = { "title": "Настройки", "gasOnDestination": "Газ на назначении", "slippageTolerance": "Толерантность к проскальзыванию", "routePriority": "Приоритет маршрута", "highSlippageWarning": "Высокое проскальзывание", "gasPresets": { "auto": "Авто", "none": "Нет", "medium": "Средний", "max": "Макс" }, "routePresets": { "fastest": "Быстрейший", "cheapest": "Дешевейший", "recommended": "Рекомендуемый" } };
|
|
@@ -120,72 +121,282 @@ function BridgeI18nProvider({
|
|
|
120
121
|
function useBridgeTranslation() {
|
|
121
122
|
return reactI18next.useTranslation("evaa-bridge", { i18n: bridgeI18n });
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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);
|
|
138
143
|
}
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (!prev || !data) return false;
|
|
159
|
-
if (prev.length !== data.length) return false;
|
|
160
|
-
for (let i = 0; i < prev.length; i++) {
|
|
161
|
-
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;
|
|
162
163
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (!prev && !data) return true;
|
|
172
|
-
if (!prev || !data) return false;
|
|
173
|
-
if (prev.length !== data.length) return false;
|
|
174
|
-
for (let i = 0; i < prev.length; i++) {
|
|
175
|
-
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);
|
|
176
172
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
+
}
|
|
189
400
|
const POPULAR_ORDER = [
|
|
190
401
|
"USDT",
|
|
191
402
|
"USDC",
|
|
@@ -221,6 +432,16 @@ function buildAssetMatrix(tokens) {
|
|
|
221
432
|
if (!symbol) continue;
|
|
222
433
|
(m[symbol] || (m[symbol] = {}))[t.chainKey] = t;
|
|
223
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
|
+
);
|
|
224
445
|
return m;
|
|
225
446
|
}
|
|
226
447
|
function listAssetsForSelect(tokens) {
|
|
@@ -230,11 +451,11 @@ function listAssetsForSelect(tokens) {
|
|
|
230
451
|
const preferred = pickOrder.map((k) => byChain[k]).find(Boolean) ?? Object.values(byChain)[0];
|
|
231
452
|
return preferred;
|
|
232
453
|
});
|
|
233
|
-
const byNorm = new Map(base.map((e) => [
|
|
454
|
+
const byNorm = new Map(base.map((e) => [normalizeTickerSymbol$1(e.symbol), e]));
|
|
234
455
|
const used = /* @__PURE__ */ new Set();
|
|
235
456
|
const popular = [];
|
|
236
457
|
for (const s of POPULAR_ORDER) {
|
|
237
|
-
const key =
|
|
458
|
+
const key = normalizeTickerSymbol$1(s);
|
|
238
459
|
const item = byNorm.get(key);
|
|
239
460
|
if (item && !used.has(key)) {
|
|
240
461
|
popular.push(item);
|
|
@@ -242,11 +463,11 @@ function listAssetsForSelect(tokens) {
|
|
|
242
463
|
}
|
|
243
464
|
}
|
|
244
465
|
const stable = base.filter((e) => {
|
|
245
|
-
const k =
|
|
466
|
+
const k = normalizeTickerSymbol$1(e.symbol);
|
|
246
467
|
return !used.has(k) && STABLES.has(k);
|
|
247
468
|
}).sort((a, b) => a.symbol.localeCompare(b.symbol));
|
|
248
|
-
for (const e of stable) used.add(
|
|
249
|
-
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));
|
|
250
471
|
return [...popular, ...stable, ...other];
|
|
251
472
|
}
|
|
252
473
|
function resolveTokenOnChain(tokens, assetSymbol, chainKey) {
|
|
@@ -651,9 +872,398 @@ function isAddressValidForChain(chainKey, addr) {
|
|
|
651
872
|
if (chainKey === "tron") return isTronAddress(addr);
|
|
652
873
|
return isEvmAddress(addr);
|
|
653
874
|
}
|
|
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
|
+
);
|
|
882
|
+
}
|
|
883
|
+
return context;
|
|
884
|
+
}
|
|
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}`;
|
|
895
|
+
}
|
|
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
|
+
}
|
|
654
1264
|
function useBridgeQuote() {
|
|
655
1265
|
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
656
|
-
const { fromChain, toChain } = useChainsStore();
|
|
1266
|
+
const { fromChain, toChain, chains } = useChainsStore();
|
|
657
1267
|
const { srcAddress, dstAddress } = useAddresses();
|
|
658
1268
|
const { slippageBps, routePriority, getDstNativeAmount, getSlippageDecimal } = useSettingsStore();
|
|
659
1269
|
const {
|
|
@@ -665,6 +1275,8 @@ function useBridgeQuote() {
|
|
|
665
1275
|
setNoRoute,
|
|
666
1276
|
resetWithIdle
|
|
667
1277
|
} = useBridgeQuoteStore();
|
|
1278
|
+
const { chainRegistry } = useChainStrategies();
|
|
1279
|
+
const publicClient = wagmi.usePublicClient();
|
|
668
1280
|
const input2 = inputAmount;
|
|
669
1281
|
const srcTokenOnFrom = react.useMemo(
|
|
670
1282
|
() => resolveTokenOnChainFromMatrix$2(
|
|
@@ -753,7 +1365,85 @@ function useBridgeQuote() {
|
|
|
753
1365
|
return;
|
|
754
1366
|
}
|
|
755
1367
|
if (cancelled) return;
|
|
756
|
-
|
|
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);
|
|
757
1447
|
} catch {
|
|
758
1448
|
if (!cancelled) {
|
|
759
1449
|
resetUi();
|
|
@@ -779,7 +1469,10 @@ function useBridgeQuote() {
|
|
|
779
1469
|
setQError,
|
|
780
1470
|
slippageBps,
|
|
781
1471
|
routePriority,
|
|
782
|
-
refetchTrigger
|
|
1472
|
+
refetchTrigger,
|
|
1473
|
+
chainRegistry,
|
|
1474
|
+
chains,
|
|
1475
|
+
publicClient
|
|
783
1476
|
]);
|
|
784
1477
|
return { loading };
|
|
785
1478
|
}
|
|
@@ -801,7 +1494,8 @@ async function getTokens() {
|
|
|
801
1494
|
throw new Error(`Failed to load chains: ${res.status}`);
|
|
802
1495
|
}
|
|
803
1496
|
const data = await res.json();
|
|
804
|
-
|
|
1497
|
+
const tokens = Array.isArray(data) ? data : data.tokens ?? [];
|
|
1498
|
+
return tokens.map(normalizeTokenSymbol);
|
|
805
1499
|
}
|
|
806
1500
|
async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
807
1501
|
const url = new URL("https://stargate.finance/api/v1/tokens");
|
|
@@ -828,10 +1522,7 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
|
828
1522
|
if (sa === sb) return (a.name ?? "").localeCompare(b.name ?? "");
|
|
829
1523
|
return sa.localeCompare(sb);
|
|
830
1524
|
});
|
|
831
|
-
return unique;
|
|
832
|
-
}
|
|
833
|
-
function normalizeTickerSymbol$1(s) {
|
|
834
|
-
return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
1525
|
+
return unique.map(normalizeTokenSymbol);
|
|
835
1526
|
}
|
|
836
1527
|
function resolveTokenOnChainFromMatrix$1(assetMatrix, assetSymbol, chainKey) {
|
|
837
1528
|
if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
|
|
@@ -858,7 +1549,14 @@ const useChainDerivations = () => {
|
|
|
858
1549
|
return [];
|
|
859
1550
|
const byChain = assetMatrix[selectedAssetSymbol.toUpperCase()];
|
|
860
1551
|
const keys = new Set(Object.keys(byChain ?? {}));
|
|
861
|
-
|
|
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;
|
|
862
1560
|
}, [selectedAssetSymbol, assetMatrix, chains]);
|
|
863
1561
|
react.useEffect(() => {
|
|
864
1562
|
setAllowedFromChains(supportedFrom);
|
|
@@ -926,6 +1624,13 @@ const useChainDerivations = () => {
|
|
|
926
1624
|
if (fromChain?.chainKey) {
|
|
927
1625
|
list = list.filter((c) => c.chainKey !== fromChain.chainKey);
|
|
928
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
|
+
);
|
|
929
1634
|
setAllowedToChains(list);
|
|
930
1635
|
if (!toChain || !list.some((c) => c.chainKey === toChain.chainKey)) {
|
|
931
1636
|
setToChain(list[0]);
|
|
@@ -960,16 +1665,6 @@ const useChainDerivations = () => {
|
|
|
960
1665
|
isLoadingToChains
|
|
961
1666
|
};
|
|
962
1667
|
};
|
|
963
|
-
const ChainStrategyContext = react.createContext(void 0);
|
|
964
|
-
function useChainStrategies() {
|
|
965
|
-
const context = react.useContext(ChainStrategyContext);
|
|
966
|
-
if (!context) {
|
|
967
|
-
throw new Error(
|
|
968
|
-
"useChainStrategies must be used within ChainStrategyProvider"
|
|
969
|
-
);
|
|
970
|
-
}
|
|
971
|
-
return context;
|
|
972
|
-
}
|
|
973
1668
|
function useBalances(chainKey, address, priorityTokenSymbol) {
|
|
974
1669
|
const { chainRegistry } = useChainStrategies();
|
|
975
1670
|
const { assetMatrix } = useTokensStore();
|
|
@@ -1333,7 +2028,7 @@ const StargateIcon = (props) => {
|
|
|
1333
2028
|
"path",
|
|
1334
2029
|
{
|
|
1335
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",
|
|
1336
|
-
fill: "
|
|
2031
|
+
fill: "currentColor"
|
|
1337
2032
|
}
|
|
1338
2033
|
)
|
|
1339
2034
|
] }),
|
|
@@ -1469,7 +2164,7 @@ const WalletConnectIcon = (props) => {
|
|
|
1469
2164
|
}
|
|
1470
2165
|
);
|
|
1471
2166
|
};
|
|
1472
|
-
const
|
|
2167
|
+
const TonConnectIcon = (props) => {
|
|
1473
2168
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1474
2169
|
"svg",
|
|
1475
2170
|
{
|
|
@@ -1607,18 +2302,30 @@ const SwapButton = () => {
|
|
|
1607
2302
|
) });
|
|
1608
2303
|
};
|
|
1609
2304
|
const WalletBalance = (props) => {
|
|
1610
|
-
const { value, isLoading = false } = props;
|
|
2305
|
+
const { value, isLoading = false, showMax = false, onMaxClick } = props;
|
|
2306
|
+
const { t } = useBridgeTranslation();
|
|
1611
2307
|
const hasNoData = !value || value === "0" || value === "0.00" || value === "0.0";
|
|
1612
2308
|
const shouldShowSkeleton = isLoading && hasNoData;
|
|
2309
|
+
const shouldShowMaxButton = showMax && !hasNoData && !isLoading;
|
|
1613
2310
|
if (shouldShowSkeleton) {
|
|
1614
2311
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
1615
2312
|
/* @__PURE__ */ jsxRuntime.jsx(WalletIcon, { className: "text-muted-foreground" }),
|
|
1616
|
-
/* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-4 w-
|
|
2313
|
+
/* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-4 w-12 rounded-md" })
|
|
1617
2314
|
] });
|
|
1618
2315
|
}
|
|
1619
2316
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
1620
2317
|
/* @__PURE__ */ jsxRuntime.jsx(WalletIcon, { className: "text-muted-foreground" }),
|
|
1621
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: value })
|
|
2318
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm leading-5 font-medium text-muted-foreground", children: value }),
|
|
2319
|
+
shouldShowMaxButton && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2320
|
+
button.Button,
|
|
2321
|
+
{
|
|
2322
|
+
variant: "ghost",
|
|
2323
|
+
size: "sm",
|
|
2324
|
+
onClick: onMaxClick,
|
|
2325
|
+
className: "h-auto p-0 px-0 text-xs font-medium border-b border-foreground rounded-none",
|
|
2326
|
+
children: t("bridge.max")
|
|
2327
|
+
}
|
|
2328
|
+
)
|
|
1622
2329
|
] });
|
|
1623
2330
|
};
|
|
1624
2331
|
const BASE_URL$1 = "https://icons-ckg.pages.dev/stargate-light/networks";
|
|
@@ -1647,7 +2354,7 @@ const SelectNetworkButton = ({
|
|
|
1647
2354
|
size: "sm",
|
|
1648
2355
|
variant: "secondary",
|
|
1649
2356
|
type: "button",
|
|
1650
|
-
className: "shrink-0 gap-2
|
|
2357
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
1651
2358
|
"aria-label": label,
|
|
1652
2359
|
children: [
|
|
1653
2360
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
|
|
@@ -1738,49 +2445,37 @@ const SearchInput = ({
|
|
|
1738
2445
|
placeholder,
|
|
1739
2446
|
value,
|
|
1740
2447
|
onChange,
|
|
1741
|
-
className
|
|
1742
|
-
containerClassName
|
|
2448
|
+
className
|
|
1743
2449
|
}) => {
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
className: utils.cn(
|
|
1760
|
-
"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",
|
|
1761
|
-
className
|
|
1762
|
-
),
|
|
1763
|
-
value,
|
|
1764
|
-
onChange: (e) => onChange(e.target.value),
|
|
1765
|
-
onFocus: () => setIsFocused(true),
|
|
1766
|
-
onBlur: () => setIsFocused(false)
|
|
1767
|
-
}
|
|
1768
|
-
)
|
|
1769
|
-
]
|
|
1770
|
-
}
|
|
1771
|
-
);
|
|
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
|
+
] });
|
|
1772
2465
|
};
|
|
1773
2466
|
const ChainSelectModal = ({
|
|
1774
2467
|
isOpen,
|
|
1775
2468
|
onClose,
|
|
1776
2469
|
items,
|
|
1777
2470
|
allowedItems,
|
|
1778
|
-
onChangeChain
|
|
2471
|
+
onChangeChain,
|
|
2472
|
+
isSource
|
|
1779
2473
|
}) => {
|
|
1780
2474
|
const { t } = useBridgeTranslation();
|
|
1781
2475
|
const [query, setQuery] = react.useState("");
|
|
1782
2476
|
const { setFromChain, chains, fromChain, toChain } = useChainsStore();
|
|
1783
|
-
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2477
|
+
const { assetMatrix, selectedAssetSymbol, setSelectedAssetSymbol } = useTokensStore();
|
|
2478
|
+
console.log("allowedItems", allowedItems);
|
|
1784
2479
|
const handleClose = react.useCallback(() => {
|
|
1785
2480
|
setQuery("");
|
|
1786
2481
|
onClose();
|
|
@@ -1809,40 +2504,84 @@ const ChainSelectModal = ({
|
|
|
1809
2504
|
},
|
|
1810
2505
|
[chains, selectedAssetSymbol, assetMatrix]
|
|
1811
2506
|
);
|
|
2507
|
+
const findCompatibleTokenAndSrcChain = react.useCallback(
|
|
2508
|
+
(dstChain) => {
|
|
2509
|
+
if (!chains || !assetMatrix) return void 0;
|
|
2510
|
+
const PRIORITY_CHAINS = [
|
|
2511
|
+
"ethereum",
|
|
2512
|
+
"arbitrum",
|
|
2513
|
+
"bsc",
|
|
2514
|
+
"polygon",
|
|
2515
|
+
"optimism",
|
|
2516
|
+
"base"
|
|
2517
|
+
];
|
|
2518
|
+
for (const tokenSymbol of Object.keys(assetMatrix)) {
|
|
2519
|
+
const assetByChain = assetMatrix[tokenSymbol];
|
|
2520
|
+
if (!assetByChain[dstChain.chainKey]) continue;
|
|
2521
|
+
const availableChains = chains.filter(
|
|
2522
|
+
(c) => assetByChain[c.chainKey] && c.chainKey !== dstChain.chainKey
|
|
2523
|
+
);
|
|
2524
|
+
if (availableChains.length === 0) continue;
|
|
2525
|
+
let sourceChain;
|
|
2526
|
+
for (const chainKey of PRIORITY_CHAINS) {
|
|
2527
|
+
sourceChain = availableChains.find((c) => c.chainKey === chainKey);
|
|
2528
|
+
if (sourceChain) break;
|
|
2529
|
+
}
|
|
2530
|
+
if (!sourceChain) sourceChain = availableChains[0];
|
|
2531
|
+
return { tokenSymbol, sourceChain };
|
|
2532
|
+
}
|
|
2533
|
+
return void 0;
|
|
2534
|
+
},
|
|
2535
|
+
[chains, assetMatrix]
|
|
2536
|
+
);
|
|
1812
2537
|
const groupedChains = react.useMemo(() => {
|
|
1813
2538
|
const q = query.trim().toLowerCase();
|
|
1814
2539
|
const filtered = q ? (items ?? []).filter(
|
|
1815
2540
|
(c) => c.name.toLowerCase().includes(q) || c.chainKey.toLowerCase().includes(q)
|
|
1816
2541
|
) : items ?? [];
|
|
1817
|
-
const groups = { available: [], willChangeSrc: [] };
|
|
2542
|
+
const groups = { available: [], willChangeSrc: [], willChangeTokenAndSrc: [] };
|
|
1818
2543
|
for (const chain of filtered) {
|
|
1819
2544
|
const isAllowed = allowedItems?.some((c) => c.chainKey === chain.chainKey) || false;
|
|
1820
2545
|
if (isAllowed) {
|
|
1821
2546
|
groups.available.push(chain);
|
|
1822
2547
|
} else {
|
|
1823
2548
|
const compatibleSrc = findCompatibleSrcChain(chain);
|
|
1824
|
-
if (compatibleSrc)
|
|
2549
|
+
if (compatibleSrc) {
|
|
2550
|
+
groups.willChangeSrc.push(chain);
|
|
2551
|
+
} else {
|
|
2552
|
+
const compatibleTokenAndSrc = findCompatibleTokenAndSrcChain(chain);
|
|
2553
|
+
if (compatibleTokenAndSrc) {
|
|
2554
|
+
groups.willChangeTokenAndSrc.push(chain);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
1825
2557
|
}
|
|
1826
2558
|
}
|
|
1827
2559
|
groups.available.sort((a, b) => a.name.localeCompare(b.name));
|
|
1828
2560
|
groups.willChangeSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
2561
|
+
groups.willChangeTokenAndSrc.sort((a, b) => a.name.localeCompare(b.name));
|
|
1829
2562
|
return groups;
|
|
1830
|
-
}, [items, query, allowedItems, findCompatibleSrcChain]);
|
|
1831
|
-
const onChainPick = (chain, willChangeSrc = false) => {
|
|
1832
|
-
if (
|
|
2563
|
+
}, [items, query, allowedItems, findCompatibleSrcChain, findCompatibleTokenAndSrcChain]);
|
|
2564
|
+
const onChainPick = (chain, willChangeSrc = false, willChangeTokenAndSrc = false) => {
|
|
2565
|
+
if (willChangeTokenAndSrc) {
|
|
2566
|
+
const result = findCompatibleTokenAndSrcChain(chain);
|
|
2567
|
+
if (result) {
|
|
2568
|
+
setSelectedAssetSymbol(result.tokenSymbol);
|
|
2569
|
+
if (setFromChain) setFromChain(result.sourceChain);
|
|
2570
|
+
}
|
|
2571
|
+
} else if (willChangeSrc) {
|
|
1833
2572
|
const newSrcChain = findCompatibleSrcChain(chain);
|
|
1834
2573
|
if (newSrcChain && setFromChain) setFromChain(newSrcChain);
|
|
1835
2574
|
}
|
|
1836
2575
|
onChangeChain(chain);
|
|
1837
2576
|
handleClose();
|
|
1838
2577
|
};
|
|
1839
|
-
const renderChainItem = (chain, willChangeSrc) => {
|
|
1840
|
-
const isSelected = fromChain?.chainKey === chain.chainKey
|
|
2578
|
+
const renderChainItem = (chain, willChangeSrc, willChangeTokenAndSrc = false) => {
|
|
2579
|
+
const isSelected = isSource ? fromChain?.chainKey === chain.chainKey : toChain?.chainKey === chain.chainKey;
|
|
1841
2580
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1842
2581
|
button.Button,
|
|
1843
2582
|
{
|
|
1844
|
-
onClick: () => onChainPick(chain, willChangeSrc),
|
|
1845
|
-
className: `w-full cursor-pointer flex shadow-none rounded-
|
|
2583
|
+
onClick: () => onChainPick(chain, willChangeSrc, willChangeTokenAndSrc),
|
|
2584
|
+
className: `w-full cursor-pointer flex shadow-none rounded-xs items-center justify-between gap-3 px-5 py-4 h-13 font-semibold capitalize bg-transparent hover:scale-100 hover:bg-accent ${isSelected ? "border border-ring" : ""}`,
|
|
1846
2585
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1847
2586
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1848
2587
|
NetworkSymbol,
|
|
@@ -1858,27 +2597,32 @@ const ChainSelectModal = ({
|
|
|
1858
2597
|
chain.chainKey
|
|
1859
2598
|
);
|
|
1860
2599
|
};
|
|
1861
|
-
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: [
|
|
1862
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectNetwork") }) }),
|
|
2600
|
+
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", children: [
|
|
2601
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectNetwork") }) }),
|
|
1863
2602
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1864
2603
|
SearchInput,
|
|
1865
2604
|
{
|
|
1866
2605
|
placeholder: t("bridge.searchDestinationChain"),
|
|
1867
2606
|
value: query,
|
|
1868
2607
|
onChange: setQuery,
|
|
1869
|
-
containerClassName: "rounded-md",
|
|
1870
2608
|
className: "text-foreground placeholder:text-muted-foreground"
|
|
1871
2609
|
}
|
|
1872
2610
|
),
|
|
1873
2611
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
1874
2612
|
groupedChains.available.length > 0 && groupedChains.available.map((c) => renderChainItem(c, false)),
|
|
1875
|
-
groupedChains.
|
|
1876
|
-
|
|
1877
|
-
groupedChains.willChangeSrc.
|
|
1878
|
-
(c) => renderChainItem(c, true)
|
|
2613
|
+
groupedChains.willChangeSrc.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2614
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceChain") }),
|
|
2615
|
+
groupedChains.willChangeSrc.map(
|
|
2616
|
+
(c) => renderChainItem(c, true, false)
|
|
1879
2617
|
)
|
|
1880
2618
|
] }),
|
|
1881
|
-
groupedChains.
|
|
2619
|
+
groupedChains.willChangeTokenAndSrc.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2620
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase ${groupedChains.available.length > 0 || groupedChains.willChangeSrc.length > 0 ? "mt-10" : ""}`, children: t("bridge.willChangeSourceNetworkAndToken") }),
|
|
2621
|
+
groupedChains.willChangeTokenAndSrc.map(
|
|
2622
|
+
(c) => renderChainItem(c, false, true)
|
|
2623
|
+
)
|
|
2624
|
+
] }),
|
|
2625
|
+
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") })
|
|
1882
2626
|
] })
|
|
1883
2627
|
] }) });
|
|
1884
2628
|
};
|
|
@@ -1888,52 +2632,10 @@ const useWalletSelectModal = zustand.create((set) => ({
|
|
|
1888
2632
|
onOpen: (addressType) => set({ isOpen: true, addressType }),
|
|
1889
2633
|
onClose: () => set({ isOpen: false, addressType: void 0 })
|
|
1890
2634
|
}));
|
|
1891
|
-
const truncateToDecimals = (num, decimals) => {
|
|
1892
|
-
if (!isFinite(num) || isNaN(num)) return "0.00";
|
|
1893
|
-
const multiplier = Math.pow(10, decimals);
|
|
1894
|
-
const truncated = Math.floor(num * multiplier) / multiplier;
|
|
1895
|
-
return truncated.toFixed(decimals);
|
|
1896
|
-
};
|
|
1897
|
-
const formatTokenAmount = (amount, symbol, options) => {
|
|
1898
|
-
const normalizedSymbol = (symbol ?? "").toUpperCase();
|
|
1899
|
-
if (["USDT", "USDC", "DAI", "BUSD"].includes(normalizedSymbol) && amount >= 1) {
|
|
1900
|
-
return `${Math.floor(amount)} ${normalizedSymbol}`;
|
|
1901
|
-
}
|
|
1902
|
-
if (options?.decimals !== void 0) {
|
|
1903
|
-
return `${amount.toFixed(options.decimals)} ${normalizedSymbol}`;
|
|
1904
|
-
}
|
|
1905
|
-
if (amount >= 1) {
|
|
1906
|
-
return `${amount.toFixed(0)} ${normalizedSymbol}`;
|
|
1907
|
-
} else if (amount >= 1e-3) {
|
|
1908
|
-
return `${amount.toFixed(3)} ${normalizedSymbol}`;
|
|
1909
|
-
} else {
|
|
1910
|
-
return `${amount.toFixed(6)} ${normalizedSymbol}`;
|
|
1911
|
-
}
|
|
1912
|
-
};
|
|
1913
|
-
const formatUsd = (value) => {
|
|
1914
|
-
if (!value || !isFinite(value)) return "$0";
|
|
1915
|
-
if (value >= 1) return `$${value.toFixed(2)}`;
|
|
1916
|
-
return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
|
|
1917
|
-
};
|
|
1918
|
-
const formatPercentage = (bps, decimals = 2) => {
|
|
1919
|
-
return `${(bps / 100).toFixed(decimals)}%`;
|
|
1920
|
-
};
|
|
1921
|
-
const formatBalance = (amount, decimals = 2) => {
|
|
1922
|
-
if (!isFinite(amount) || isNaN(amount) || amount <= 0) {
|
|
1923
|
-
return "0.00";
|
|
1924
|
-
}
|
|
1925
|
-
return amount.toFixed(decimals);
|
|
1926
|
-
};
|
|
1927
|
-
const formatHash = (hash, startChars = 4, endChars = 4) => {
|
|
1928
|
-
if (!hash) return "";
|
|
1929
|
-
if (hash.length <= startChars + endChars) return hash;
|
|
1930
|
-
return `${hash.slice(0, startChars)}...${hash.slice(-endChars)}`;
|
|
1931
|
-
};
|
|
1932
|
-
const formatAddress = formatHash;
|
|
1933
2635
|
const prefixIcons = {
|
|
1934
2636
|
tronlink: /* @__PURE__ */ jsxRuntime.jsx(TronLinkIcon, { className: "w-5 h-5" }),
|
|
1935
2637
|
metamask: /* @__PURE__ */ jsxRuntime.jsx(MetaMaskIcon, { className: "w-5 h-5" }),
|
|
1936
|
-
ton: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2638
|
+
ton: /* @__PURE__ */ jsxRuntime.jsx(TonConnectIcon, { className: "w-5 h-5" })
|
|
1937
2639
|
};
|
|
1938
2640
|
const mapWalletToType = (wallet) => {
|
|
1939
2641
|
switch (wallet) {
|
|
@@ -1984,7 +2686,7 @@ const WalletInlineButton = ({
|
|
|
1984
2686
|
disabled: isButtonDisabled,
|
|
1985
2687
|
variant: "ghost",
|
|
1986
2688
|
size: "sm",
|
|
1987
|
-
className: "flex gap-1 cursor-pointer px-0 pr-1 h-5",
|
|
2689
|
+
className: "flex gap-1 cursor-pointer hover:opacity-60 hover:bg-transparent !px-0 pr-1 h-5",
|
|
1988
2690
|
children: [
|
|
1989
2691
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: isConnected ? prefixIcons[wallet] : null }),
|
|
1990
2692
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "leading-3 text-sm border-b border-dotted border-link text-link", children: buttonText })
|
|
@@ -2049,6 +2751,11 @@ const SwapSection = ({
|
|
|
2049
2751
|
},
|
|
2050
2752
|
[onSelect, onClose]
|
|
2051
2753
|
);
|
|
2754
|
+
const handleMaxClick = react.useCallback(() => {
|
|
2755
|
+
if (balance.balance && onAmountChange) {
|
|
2756
|
+
onAmountChange(balance.balance.toString());
|
|
2757
|
+
}
|
|
2758
|
+
}, [balance.balance, onAmountChange]);
|
|
2052
2759
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2053
2760
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2054
2761
|
"div",
|
|
@@ -2065,7 +2772,9 @@ const SwapSection = ({
|
|
|
2065
2772
|
WalletBalance,
|
|
2066
2773
|
{
|
|
2067
2774
|
value: truncateToDecimals(balance.balance, 2),
|
|
2068
|
-
isLoading: balance.isLoading
|
|
2775
|
+
isLoading: balance.isLoading,
|
|
2776
|
+
showMax: isSource,
|
|
2777
|
+
onMaxClick: handleMaxClick
|
|
2069
2778
|
}
|
|
2070
2779
|
)
|
|
2071
2780
|
] }),
|
|
@@ -2113,7 +2822,8 @@ const SwapSection = ({
|
|
|
2113
2822
|
onClose,
|
|
2114
2823
|
items: chains,
|
|
2115
2824
|
allowedItems: allowedChains,
|
|
2116
|
-
onChangeChain
|
|
2825
|
+
onChangeChain,
|
|
2826
|
+
isSource
|
|
2117
2827
|
}
|
|
2118
2828
|
)
|
|
2119
2829
|
] });
|
|
@@ -2167,7 +2877,6 @@ const AnotherAddress = () => {
|
|
|
2167
2877
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2168
2878
|
_switch.Switch,
|
|
2169
2879
|
{
|
|
2170
|
-
className: "data-[state=unchecked]:bg-switch-inactive data-[state=checked]:bg-switch-active",
|
|
2171
2880
|
"aria-pressed": enabled,
|
|
2172
2881
|
checked: enabled,
|
|
2173
2882
|
onClick: () => setEnabled((v) => !v)
|
|
@@ -2186,7 +2895,7 @@ const AnotherAddress = () => {
|
|
|
2186
2895
|
"div",
|
|
2187
2896
|
{
|
|
2188
2897
|
className: utils.cn(
|
|
2189
|
-
"bg-input py-2
|
|
2898
|
+
"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",
|
|
2190
2899
|
{
|
|
2191
2900
|
"py-4": value,
|
|
2192
2901
|
"border border-ring": isFocused,
|
|
@@ -2196,13 +2905,13 @@ const AnotherAddress = () => {
|
|
|
2196
2905
|
children: [
|
|
2197
2906
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
|
|
2198
2907
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2199
|
-
|
|
2908
|
+
"textarea",
|
|
2200
2909
|
{
|
|
2201
2910
|
className: utils.cn(
|
|
2202
|
-
"
|
|
2911
|
+
"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"
|
|
2203
2912
|
),
|
|
2204
2913
|
placeholder: t("bridge.anotherAddressPlaceholder"),
|
|
2205
|
-
|
|
2914
|
+
rows: value.length >= 32 ? 2 : 1,
|
|
2206
2915
|
value,
|
|
2207
2916
|
onFocus: () => setIsFocused(true),
|
|
2208
2917
|
onBlur: () => setIsFocused(false),
|
|
@@ -2219,7 +2928,7 @@ const AnotherAddress = () => {
|
|
|
2219
2928
|
button.Button,
|
|
2220
2929
|
{
|
|
2221
2930
|
variant: "secondary",
|
|
2222
|
-
className: "bg-
|
|
2931
|
+
className: "bg-accent text-card-foreground uppercase text-xs",
|
|
2223
2932
|
size: "sm",
|
|
2224
2933
|
onClick: onPaste,
|
|
2225
2934
|
children: t("common.paste")
|
|
@@ -2242,8 +2951,8 @@ const AnotherAddress = () => {
|
|
|
2242
2951
|
) })
|
|
2243
2952
|
] });
|
|
2244
2953
|
};
|
|
2245
|
-
const
|
|
2246
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
2954
|
+
const InfoIcon = (props) => {
|
|
2955
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2247
2956
|
"svg",
|
|
2248
2957
|
{
|
|
2249
2958
|
width: "16",
|
|
@@ -2252,56 +2961,22 @@ const TipIcon = (props) => {
|
|
|
2252
2961
|
fill: "none",
|
|
2253
2962
|
xmlns: "http://www.w3.org/2000/svg",
|
|
2254
2963
|
...props,
|
|
2255
|
-
children:
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
width: "100%"
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
) }),
|
|
2268
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2269
|
-
"circle",
|
|
2270
|
-
{
|
|
2271
|
-
"data-figma-bg-blur-radius": "4.4893",
|
|
2272
|
-
cx: "8",
|
|
2273
|
-
cy: "8",
|
|
2274
|
-
r: "8",
|
|
2275
|
-
fill: "currentColor",
|
|
2276
|
-
fillOpacity: "0.2"
|
|
2277
|
-
}
|
|
2278
|
-
),
|
|
2279
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2280
|
-
"path",
|
|
2281
|
-
{
|
|
2282
|
-
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",
|
|
2283
|
-
stroke: "currentColor",
|
|
2284
|
-
strokeOpacity: "0.5",
|
|
2285
|
-
strokeWidth: "1.68349",
|
|
2286
|
-
strokeLinecap: "round"
|
|
2287
|
-
}
|
|
2288
|
-
),
|
|
2289
|
-
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2290
|
-
"clipPath",
|
|
2291
|
-
{
|
|
2292
|
-
id: "bgblur_0_13066_11660_clip_path",
|
|
2293
|
-
transform: "translate(4.4893 4.4893)",
|
|
2294
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "8" })
|
|
2295
|
-
}
|
|
2296
|
-
) })
|
|
2297
|
-
]
|
|
2964
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2965
|
+
"path",
|
|
2966
|
+
{
|
|
2967
|
+
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",
|
|
2968
|
+
stroke: "currentColor",
|
|
2969
|
+
"stroke-width": "1.68349",
|
|
2970
|
+
"stroke-linecap": "round"
|
|
2971
|
+
}
|
|
2972
|
+
)
|
|
2298
2973
|
}
|
|
2299
2974
|
);
|
|
2300
2975
|
};
|
|
2301
2976
|
const Tip = (props) => {
|
|
2302
2977
|
const { children, text } = props;
|
|
2303
2978
|
return /* @__PURE__ */ jsxRuntime.jsxs(tooltip.Tooltip, { children: [
|
|
2304
|
-
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { children }),
|
|
2979
|
+
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipTrigger, { className: "w-4 h-4 rounded-full bg-muted text-muted-foreground", children }),
|
|
2305
2980
|
/* @__PURE__ */ jsxRuntime.jsx(tooltip.TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: text }) })
|
|
2306
2981
|
] });
|
|
2307
2982
|
};
|
|
@@ -2315,97 +2990,84 @@ const TokenSymbol = ({
|
|
|
2315
2990
|
const src = `${BASE_URL}/${normalizedSymbol}.svg`;
|
|
2316
2991
|
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? symbol, className });
|
|
2317
2992
|
};
|
|
2318
|
-
function
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
outputHumanRounded: "0",
|
|
2324
|
-
minReceivedHuman: 0
|
|
2325
|
-
};
|
|
2326
|
-
}
|
|
2327
|
-
const inputHuman = fromLD(quote.srcAmount, srcToken.decimals);
|
|
2328
|
-
const outputHuman = fromLD(quote.dstAmount, dstToken.decimals);
|
|
2329
|
-
const outputHumanRounded = truncateToDecimals(outputHuman, 2);
|
|
2330
|
-
const minReceivedHuman = fromLD(
|
|
2331
|
-
quote.dstAmountMin || "0",
|
|
2332
|
-
dstToken.decimals
|
|
2333
|
-
);
|
|
2334
|
-
return {
|
|
2335
|
-
inputHuman,
|
|
2336
|
-
outputHuman,
|
|
2337
|
-
outputHumanRounded,
|
|
2338
|
-
minReceivedHuman
|
|
2339
|
-
};
|
|
2340
|
-
}
|
|
2341
|
-
function getQuoteFees(quote, tokens, chains, srcToken, dstToken) {
|
|
2342
|
-
if (!quote || !tokens || !chains) {
|
|
2343
|
-
return {
|
|
2344
|
-
totalUsd: 0,
|
|
2345
|
-
protocolFeeUsd: void 0,
|
|
2346
|
-
messageFeeUsd: void 0,
|
|
2347
|
-
serviceUsd: void 0,
|
|
2348
|
-
blockchainUsd: void 0,
|
|
2349
|
-
inSrcToken: void 0,
|
|
2350
|
-
inDstToken: void 0
|
|
2351
|
-
};
|
|
2352
|
-
}
|
|
2353
|
-
const feeData = computeFeesUsdFromArray(quote.fees, tokens, chains);
|
|
2354
|
-
let inSrcToken = void 0;
|
|
2355
|
-
let inDstToken = void 0;
|
|
2356
|
-
if (srcToken && quote.srcChainKey) {
|
|
2357
|
-
const feeInSrcLD = sumFeeByTokenLD(
|
|
2358
|
-
quote.fees,
|
|
2359
|
-
srcToken.address,
|
|
2360
|
-
quote.srcChainKey
|
|
2361
|
-
);
|
|
2362
|
-
const feeInSrcHuman = fromLD(feeInSrcLD, srcToken.decimals);
|
|
2363
|
-
if (feeInSrcHuman > 0) {
|
|
2364
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcHuman, 8));
|
|
2365
|
-
} else if (feeData.totalUsd > 0 && srcToken.price?.usd) {
|
|
2366
|
-
const feeInSrcApprox = feeData.totalUsd / srcToken.price.usd;
|
|
2367
|
-
inSrcToken = Number(truncateToDecimals(feeInSrcApprox, 8));
|
|
2368
|
-
}
|
|
2369
|
-
}
|
|
2370
|
-
if (dstToken && quote.dstChainKey) {
|
|
2371
|
-
const feeInDstLD = sumFeeByTokenLD(
|
|
2372
|
-
quote.fees,
|
|
2373
|
-
dstToken.address,
|
|
2374
|
-
quote.dstChainKey
|
|
2375
|
-
);
|
|
2376
|
-
const feeInDstHuman = fromLD(feeInDstLD, dstToken.decimals);
|
|
2377
|
-
if (feeInDstHuman > 0) {
|
|
2378
|
-
inDstToken = Number(truncateToDecimals(feeInDstHuman, 8));
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
return {
|
|
2382
|
-
totalUsd: feeData.totalUsd,
|
|
2383
|
-
protocolFeeUsd: feeData.protocolFeeUsd,
|
|
2384
|
-
messageFeeUsd: feeData.messageFeeUsd,
|
|
2385
|
-
serviceUsd: feeData.serviceUsd,
|
|
2386
|
-
blockchainUsd: feeData.blockchainUsd,
|
|
2387
|
-
inSrcToken,
|
|
2388
|
-
inDstToken
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
function calculateMinReceived(quote, slippageBps, dstToken) {
|
|
2392
|
-
if (!quote || !dstToken) return 0;
|
|
2393
|
-
const dstAmountLD = BigInt(quote.dstAmount);
|
|
2394
|
-
const minAmountLD = dstAmountLD * BigInt(1e4 - slippageBps) / BigInt(1e4);
|
|
2395
|
-
return fromLD(minAmountLD.toString(), dstToken.decimals);
|
|
2993
|
+
function getSimpleFallback(chainKey) {
|
|
2994
|
+
const key = chainKey.toLowerCase();
|
|
2995
|
+
if (key === "ton") return 0.15;
|
|
2996
|
+
if (key === "tron") return 10;
|
|
2997
|
+
return 0.01;
|
|
2396
2998
|
}
|
|
2397
|
-
function
|
|
2398
|
-
const
|
|
2399
|
-
const
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2999
|
+
function useGasEstimate(amountNum) {
|
|
3000
|
+
const { fromChain } = useChainsStore();
|
|
3001
|
+
const { selectedAssetSymbol } = useTokensStore();
|
|
3002
|
+
const { srcAddress } = useAddresses();
|
|
3003
|
+
const { balances, isLoading: balancesLoading } = useBalances(
|
|
3004
|
+
fromChain?.chainKey,
|
|
3005
|
+
srcAddress
|
|
3006
|
+
);
|
|
3007
|
+
const { quote } = useBridgeQuoteStore();
|
|
3008
|
+
const balancesKnown = !balancesLoading;
|
|
3009
|
+
const chainKey = fromChain?.chainKey;
|
|
3010
|
+
const nativeCurrencySymbol = fromChain?.nativeCurrency?.symbol;
|
|
3011
|
+
const nativeCurrencyAddress = fromChain?.nativeCurrency?.address;
|
|
3012
|
+
const nativeCurrencyDecimals = fromChain?.nativeCurrency?.decimals;
|
|
3013
|
+
const quoteFees = quote?.fees ? JSON.stringify(quote.fees) : null;
|
|
3014
|
+
const quoteSrcChainKey = quote?.srcChainKey;
|
|
3015
|
+
const nativeBalanceValue = nativeCurrencySymbol ? Number(balances[nativeCurrencySymbol.toUpperCase()]?.balance ?? 0) : 0;
|
|
3016
|
+
const result = react.useMemo(() => {
|
|
3017
|
+
if (!chainKey || !nativeCurrencySymbol) {
|
|
3018
|
+
return {
|
|
3019
|
+
nativeSym: "",
|
|
3020
|
+
nativeBalance: 0,
|
|
3021
|
+
requiredNative: 0,
|
|
3022
|
+
balancesKnown,
|
|
3023
|
+
isNativeSelected: false,
|
|
3024
|
+
hasEnoughGas: true
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
3027
|
+
const nativeSym = nativeCurrencySymbol.toUpperCase();
|
|
3028
|
+
const nativeBalance = nativeBalanceValue;
|
|
3029
|
+
const isNativeSelected = nativeSym === (selectedAssetSymbol || "").toUpperCase();
|
|
3030
|
+
let requiredNative = 0;
|
|
3031
|
+
if (quoteFees && quoteSrcChainKey === chainKey) {
|
|
3032
|
+
const fees = JSON.parse(quoteFees);
|
|
3033
|
+
const feesInNative = fees.filter(
|
|
3034
|
+
(f) => f.chainKey === chainKey && f.token === nativeCurrencyAddress
|
|
3035
|
+
).reduce(
|
|
3036
|
+
(sum, f) => sum + BigInt(f.amount || "0"),
|
|
3037
|
+
0n
|
|
3038
|
+
);
|
|
3039
|
+
const decimals = nativeCurrencyDecimals || 18;
|
|
3040
|
+
requiredNative = Number(feesInNative) / Math.pow(10, decimals);
|
|
3041
|
+
} else {
|
|
3042
|
+
requiredNative = getSimpleFallback(chainKey);
|
|
3043
|
+
}
|
|
3044
|
+
let hasEnoughGas = true;
|
|
3045
|
+
if (isNativeSelected) {
|
|
3046
|
+
hasEnoughGas = nativeBalance - (amountNum ?? 0) >= requiredNative;
|
|
3047
|
+
} else {
|
|
3048
|
+
hasEnoughGas = nativeBalance >= requiredNative;
|
|
3049
|
+
}
|
|
3050
|
+
return {
|
|
3051
|
+
nativeSym,
|
|
3052
|
+
nativeBalance,
|
|
3053
|
+
requiredNative,
|
|
3054
|
+
balancesKnown,
|
|
3055
|
+
isNativeSelected,
|
|
3056
|
+
hasEnoughGas: balancesKnown ? hasEnoughGas : true
|
|
3057
|
+
};
|
|
3058
|
+
}, [
|
|
3059
|
+
chainKey,
|
|
3060
|
+
nativeCurrencySymbol,
|
|
3061
|
+
nativeCurrencyAddress,
|
|
3062
|
+
nativeCurrencyDecimals,
|
|
3063
|
+
selectedAssetSymbol,
|
|
3064
|
+
quoteFees,
|
|
3065
|
+
quoteSrcChainKey,
|
|
3066
|
+
amountNum,
|
|
3067
|
+
balancesKnown,
|
|
3068
|
+
nativeBalanceValue
|
|
3069
|
+
]);
|
|
3070
|
+
return result;
|
|
2409
3071
|
}
|
|
2410
3072
|
function getRouteDisplayName(route) {
|
|
2411
3073
|
if (!route) return "Stargate Bridge";
|
|
@@ -2422,6 +3084,7 @@ const Details = () => {
|
|
|
2422
3084
|
const { toChain, fromChain, chains } = useChainsStore();
|
|
2423
3085
|
const { quote, status } = useBridgeQuoteStore();
|
|
2424
3086
|
const { slippageBps, routePriority } = useSettingsStore();
|
|
3087
|
+
const gas = useGasEstimate();
|
|
2425
3088
|
const dstToken = resolveTokenOnChainFromMatrix$2(
|
|
2426
3089
|
assetMatrix,
|
|
2427
3090
|
selectedAssetSymbol,
|
|
@@ -2444,24 +3107,10 @@ const Details = () => {
|
|
|
2444
3107
|
const isLoading = status === "loading";
|
|
2445
3108
|
const receiveText = quoteDetails.outputAmount != null ? Number(quoteDetails.outputAmount).toFixed(6) : "0.00";
|
|
2446
3109
|
const etaText = quoteDetails.etaSeconds != null ? `≈ ${Math.max(1, Math.round(quoteDetails.etaSeconds / 60))}m` : "—";
|
|
2447
|
-
const
|
|
2448
|
-
const feeInDst = quoteDetails.fees?.inDstToken;
|
|
2449
|
-
const totalFeeUsd = quoteDetails.fees?.totalUsd;
|
|
2450
|
-
const totalFeeDisplay = (() => {
|
|
2451
|
-
if (feeInSrc != null && srcToken?.symbol) {
|
|
2452
|
-
return `${Number(feeInSrc).toFixed(6)} ${srcToken.symbol.toUpperCase()}`;
|
|
2453
|
-
}
|
|
2454
|
-
if (feeInDst != null && dstToken?.symbol) {
|
|
2455
|
-
return `${Number(feeInDst).toFixed(6)} ${dstToken.symbol.toUpperCase()}`;
|
|
2456
|
-
}
|
|
2457
|
-
if (totalFeeUsd != null) {
|
|
2458
|
-
return formatUsd(totalFeeUsd);
|
|
2459
|
-
}
|
|
2460
|
-
return "—";
|
|
2461
|
-
})();
|
|
3110
|
+
const totalFeeDisplay = gas.requiredNative > 0 ? `${gas.requiredNative.toFixed(6)} ${gas.nativeSym}` : "—";
|
|
2462
3111
|
const currentSlippageText = formatPercentage(slippageBps);
|
|
2463
3112
|
const routeText = quote?.route ? getRouteDisplayName(quote.route) : t(`settings.routePresets.${routePriority}`);
|
|
2464
|
-
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 rounded-sm", children: [
|
|
3113
|
+
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: [
|
|
2465
3114
|
/* @__PURE__ */ jsxRuntime.jsx(accordion.AccordionTrigger, { className: "w-full gap-1 items-center py-6 px-5 rounded-b-sm data-[state=open]:pb-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex items-center justify-between", children: [
|
|
2466
3115
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-normal text-priority leading-4", children: t("bridge.youWillReceive", { defaultValue: "You will receive" }) }),
|
|
2467
3116
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-transparent hover:bg-transparent shadow-none h-4 p-0 px-0 py-0 flex items-center gap-2", children: [
|
|
@@ -2478,7 +3127,7 @@ const Details = () => {
|
|
|
2478
3127
|
DetailsRow,
|
|
2479
3128
|
{
|
|
2480
3129
|
label: t("transaction.route"),
|
|
2481
|
-
value: /* @__PURE__ */ jsxRuntime.jsxs("
|
|
3130
|
+
value: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
2482
3131
|
/* @__PURE__ */ jsxRuntime.jsx(StargateIcon, { className: "w-4 h-4" }),
|
|
2483
3132
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "", children: routeText })
|
|
2484
3133
|
] })
|
|
@@ -2505,7 +3154,14 @@ const Details = () => {
|
|
|
2505
3154
|
label: t("transaction.totalFee"),
|
|
2506
3155
|
value: /* @__PURE__ */ jsxRuntime.jsxs("strong", { className: "inline-flex items-center gap-1", children: [
|
|
2507
3156
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: totalFeeDisplay }),
|
|
2508
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3157
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3158
|
+
TokenSymbol,
|
|
3159
|
+
{
|
|
3160
|
+
symbol: gas.nativeSym,
|
|
3161
|
+
className: "w-4 h-4",
|
|
3162
|
+
alt: "token"
|
|
3163
|
+
}
|
|
3164
|
+
)
|
|
2509
3165
|
] }),
|
|
2510
3166
|
isLoading
|
|
2511
3167
|
}
|
|
@@ -2520,7 +3176,7 @@ const DetailsRow = ({
|
|
|
2520
3176
|
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
2521
3177
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2522
3178
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-priority font-normal", children: label }),
|
|
2523
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: label, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3179
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: label, children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, {}) })
|
|
2524
3180
|
] }),
|
|
2525
3181
|
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-4 w-16 rounded-md" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-foreground text-sm", children: value ?? "—" })
|
|
2526
3182
|
] });
|
|
@@ -2573,6 +3229,20 @@ const useTransactionStore = zustand.create((set, get) => ({
|
|
|
2573
3229
|
};
|
|
2574
3230
|
set({ current: next });
|
|
2575
3231
|
},
|
|
3232
|
+
updateActualFee: (feeValue, feeSymbol) => {
|
|
3233
|
+
const cur = get().current;
|
|
3234
|
+
if (!cur) return;
|
|
3235
|
+
const next = {
|
|
3236
|
+
...cur,
|
|
3237
|
+
metadata: {
|
|
3238
|
+
...cur.metadata,
|
|
3239
|
+
actualFeeValue: feeValue,
|
|
3240
|
+
actualFeeSymbol: feeSymbol
|
|
3241
|
+
},
|
|
3242
|
+
updatedAt: Date.now()
|
|
3243
|
+
};
|
|
3244
|
+
set({ current: next });
|
|
3245
|
+
},
|
|
2576
3246
|
reset: () => {
|
|
2577
3247
|
set({ current: void 0 });
|
|
2578
3248
|
}
|
|
@@ -2718,9 +3388,13 @@ function isUserRejection(error) {
|
|
|
2718
3388
|
if (error instanceof TransactionRejectedError) {
|
|
2719
3389
|
return true;
|
|
2720
3390
|
}
|
|
3391
|
+
if (typeof error === "string") {
|
|
3392
|
+
const message = error.toLowerCase();
|
|
3393
|
+
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");
|
|
3394
|
+
}
|
|
2721
3395
|
if (error instanceof Error) {
|
|
2722
3396
|
const message = error.message.toLowerCase();
|
|
2723
|
-
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");
|
|
3397
|
+
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");
|
|
2724
3398
|
}
|
|
2725
3399
|
return false;
|
|
2726
3400
|
}
|
|
@@ -2739,17 +3413,13 @@ function toChainStrategyError(error, chainKey, context) {
|
|
|
2739
3413
|
error
|
|
2740
3414
|
);
|
|
2741
3415
|
}
|
|
2742
|
-
return new ChainStrategyError(
|
|
2743
|
-
String(error),
|
|
2744
|
-
"UNKNOWN_ERROR",
|
|
2745
|
-
chainKey
|
|
2746
|
-
);
|
|
3416
|
+
return new ChainStrategyError(String(error), "UNKNOWN_ERROR", chainKey);
|
|
2747
3417
|
}
|
|
2748
3418
|
function useBridgeTransaction() {
|
|
2749
3419
|
const { quote } = useBridgeQuoteStore();
|
|
2750
3420
|
const { chainRegistry } = useChainStrategies();
|
|
2751
3421
|
const { srcAddress, dstAddress } = useAddresses();
|
|
2752
|
-
const { assetMatrix,
|
|
3422
|
+
const { assetMatrix, selectedAssetSymbol } = useTokensStore();
|
|
2753
3423
|
const { chains } = useChainsStore();
|
|
2754
3424
|
const txStore = useTransactionStore();
|
|
2755
3425
|
const [isProcessing, setIsProcessing] = react.useState(false);
|
|
@@ -2779,29 +3449,32 @@ function useBridgeTransaction() {
|
|
|
2779
3449
|
const srcChain = chains?.find((c) => c.chainKey === quote.srcChainKey);
|
|
2780
3450
|
const dstChain = chains?.find((c) => c.chainKey === quote.dstChainKey);
|
|
2781
3451
|
const amounts = getQuoteAmounts(quote, srcToken, dstToken);
|
|
2782
|
-
const fees = getQuoteFees(quote, tokens, chains, srcToken, dstToken);
|
|
2783
3452
|
const metadata = {
|
|
2784
3453
|
srcChainName: srcChain?.name || quote.srcChainKey,
|
|
2785
3454
|
dstChainName: dstChain?.name || quote.dstChainKey,
|
|
2786
3455
|
srcTokenSymbol: srcToken?.symbol || quote.srcToken,
|
|
2787
3456
|
dstTokenSymbol: dstToken?.symbol || quote.dstToken,
|
|
2788
3457
|
srcAmountHuman: amounts.inputHuman,
|
|
2789
|
-
dstAmountHuman: amounts.outputHuman
|
|
2790
|
-
//
|
|
2791
|
-
totalFeeValue: fees.inSrcToken ?? fees.inDstToken ?? void 0,
|
|
2792
|
-
totalFeeSymbol: fees.inSrcToken ? srcToken?.symbol : fees.inDstToken ? dstToken?.symbol : void 0
|
|
3458
|
+
dstAmountHuman: amounts.outputHuman
|
|
3459
|
+
// Actual fee will be updated when transaction completes
|
|
2793
3460
|
};
|
|
2794
3461
|
txStore.setTransaction(quote, "executing", metadata);
|
|
2795
3462
|
setIsProcessing(true);
|
|
2796
3463
|
try {
|
|
2797
3464
|
const steps = quote.steps || [];
|
|
2798
3465
|
if (steps.length === 0) {
|
|
2799
|
-
throw new InvalidStepsError(
|
|
3466
|
+
throw new InvalidStepsError(
|
|
3467
|
+
quote.srcChainKey,
|
|
3468
|
+
"No transaction steps found in quote"
|
|
3469
|
+
);
|
|
2800
3470
|
}
|
|
2801
3471
|
const chainKey = steps[0].chainKey;
|
|
2802
3472
|
const strategy = chainRegistry.getStrategy(chainKey);
|
|
2803
3473
|
if (!strategy) {
|
|
2804
|
-
throw new InvalidStepsError(
|
|
3474
|
+
throw new InvalidStepsError(
|
|
3475
|
+
chainKey,
|
|
3476
|
+
`No strategy available for chain: ${chainKey}`
|
|
3477
|
+
);
|
|
2805
3478
|
}
|
|
2806
3479
|
if (!strategy.isConnected()) {
|
|
2807
3480
|
throw new WalletNotConnectedError(chainKey);
|
|
@@ -2813,7 +3486,6 @@ function useBridgeTransaction() {
|
|
|
2813
3486
|
srcChainKey: quote.srcChainKey,
|
|
2814
3487
|
dstChainKey: quote.dstChainKey
|
|
2815
3488
|
};
|
|
2816
|
-
console.log(steps);
|
|
2817
3489
|
const txResult = await strategy.executeSteps(steps, context, (hash) => {
|
|
2818
3490
|
txStore.setSrcHash(hash);
|
|
2819
3491
|
txStore.updateStatus("processing");
|
|
@@ -2824,6 +3496,21 @@ function useBridgeTransaction() {
|
|
|
2824
3496
|
if (result.dstTxHash) {
|
|
2825
3497
|
txStore.setDstHash(result.dstTxHash);
|
|
2826
3498
|
}
|
|
3499
|
+
if (result.actualFeeValue) {
|
|
3500
|
+
let feeSymbol = result.actualFeeSymbol;
|
|
3501
|
+
if (!feeSymbol) {
|
|
3502
|
+
const srcChain2 = chains?.find(
|
|
3503
|
+
(c) => c.chainKey === quote.srcChainKey
|
|
3504
|
+
);
|
|
3505
|
+
feeSymbol = srcChain2?.nativeCurrency?.symbol || "";
|
|
3506
|
+
}
|
|
3507
|
+
if (feeSymbol) {
|
|
3508
|
+
const feeValue = parseFloat(result.actualFeeValue);
|
|
3509
|
+
if (!isNaN(feeValue)) {
|
|
3510
|
+
txStore.updateActualFee(feeValue, feeSymbol);
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
2827
3514
|
txStore.updateStatus("completed");
|
|
2828
3515
|
console.log("Transaction completed successfully");
|
|
2829
3516
|
} else {
|
|
@@ -2841,19 +3528,26 @@ function useBridgeTransaction() {
|
|
|
2841
3528
|
console.error("Error tracking completion:", err);
|
|
2842
3529
|
});
|
|
2843
3530
|
} else {
|
|
2844
|
-
throw new TransactionFailedError(
|
|
3531
|
+
throw new TransactionFailedError(
|
|
3532
|
+
chainKey,
|
|
3533
|
+
"Transaction hash not received from wallet"
|
|
3534
|
+
);
|
|
2845
3535
|
}
|
|
2846
3536
|
return txResult;
|
|
2847
3537
|
} catch (err) {
|
|
2848
3538
|
if (isUserRejection(err)) {
|
|
2849
3539
|
txStore.setError("TRANSACTION_REJECTED");
|
|
2850
|
-
throw new TransactionFailedError(
|
|
3540
|
+
throw new TransactionFailedError(
|
|
3541
|
+
quote.srcChainKey,
|
|
3542
|
+
"Transaction rejected by user"
|
|
3543
|
+
);
|
|
2851
3544
|
}
|
|
2852
3545
|
if (ChainStrategyError.isChainStrategyError(err)) {
|
|
2853
3546
|
txStore.setError(err.code, { chainKey: err.chainKey });
|
|
2854
3547
|
console.error("Chain strategy error:", err.toJSON());
|
|
2855
3548
|
throw err;
|
|
2856
3549
|
}
|
|
3550
|
+
console.log(err);
|
|
2857
3551
|
txStore.setError("UNKNOWN_ERROR");
|
|
2858
3552
|
throw err;
|
|
2859
3553
|
} finally {
|
|
@@ -2866,66 +3560,6 @@ function useBridgeTransaction() {
|
|
|
2866
3560
|
hasQuote: !!quote
|
|
2867
3561
|
};
|
|
2868
3562
|
}
|
|
2869
|
-
function useGasEstimate(amountNum) {
|
|
2870
|
-
const { fromChain } = useChainsStore();
|
|
2871
|
-
const { selectedAssetSymbol, tokens } = useTokensStore();
|
|
2872
|
-
const getSourceGasReserveHuman = useSettingsStore(
|
|
2873
|
-
(state) => state.getSourceGasReserveHuman
|
|
2874
|
-
);
|
|
2875
|
-
const { srcAddress } = useAddresses();
|
|
2876
|
-
const { balances, isLoading: balancesLoading } = useBalances(
|
|
2877
|
-
fromChain?.chainKey,
|
|
2878
|
-
srcAddress
|
|
2879
|
-
);
|
|
2880
|
-
const { chainRegistry } = useChainStrategies();
|
|
2881
|
-
const balancesKnown = !balancesLoading;
|
|
2882
|
-
const strategy = react.useMemo(() => {
|
|
2883
|
-
if (!fromChain) return null;
|
|
2884
|
-
return chainRegistry.getStrategy(fromChain.chainKey);
|
|
2885
|
-
}, [fromChain, chainRegistry]);
|
|
2886
|
-
const { data: gasRequirement } = reactQuery.useQuery({
|
|
2887
|
-
queryKey: [
|
|
2888
|
-
"gas-estimate",
|
|
2889
|
-
fromChain?.chainKey,
|
|
2890
|
-
selectedAssetSymbol,
|
|
2891
|
-
amountNum,
|
|
2892
|
-
balances
|
|
2893
|
-
],
|
|
2894
|
-
queryFn: async () => {
|
|
2895
|
-
if (!fromChain || !strategy) {
|
|
2896
|
-
return null;
|
|
2897
|
-
}
|
|
2898
|
-
const selectedToken = tokens?.find(
|
|
2899
|
-
(t) => t.symbol.toUpperCase() === selectedAssetSymbol?.toUpperCase()
|
|
2900
|
-
) || null;
|
|
2901
|
-
const nativeTokenSymbol = fromChain.nativeCurrency?.symbol ?? "";
|
|
2902
|
-
const nativeDecimals = fromChain.nativeCurrency?.decimals || 18;
|
|
2903
|
-
const reserveFallback = getSourceGasReserveHuman(fromChain.chainKey);
|
|
2904
|
-
const result = await strategy.estimateGasRequirement({
|
|
2905
|
-
selectedToken,
|
|
2906
|
-
nativeTokenSymbol,
|
|
2907
|
-
amount: amountNum,
|
|
2908
|
-
balances,
|
|
2909
|
-
nativeDecimals,
|
|
2910
|
-
reserveFallback
|
|
2911
|
-
});
|
|
2912
|
-
return result;
|
|
2913
|
-
},
|
|
2914
|
-
enabled: !!fromChain && !!strategy,
|
|
2915
|
-
staleTime: 3e4,
|
|
2916
|
-
gcTime: 5 * 6e4,
|
|
2917
|
-
refetchOnWindowFocus: false,
|
|
2918
|
-
retry: 1
|
|
2919
|
-
});
|
|
2920
|
-
return {
|
|
2921
|
-
nativeSym: gasRequirement?.nativeSym || "",
|
|
2922
|
-
nativeBalance: gasRequirement?.nativeBalance || 0,
|
|
2923
|
-
requiredNative: gasRequirement?.requiredNative || 0,
|
|
2924
|
-
balancesKnown,
|
|
2925
|
-
isNativeSelected: gasRequirement?.isNativeSelected || false,
|
|
2926
|
-
hasEnoughGas: balancesKnown ? gasRequirement?.hasEnoughGas ?? true : true
|
|
2927
|
-
};
|
|
2928
|
-
}
|
|
2929
3563
|
function useBalanceCheck(amountNum, gas) {
|
|
2930
3564
|
const { fromChain } = useChainsStore();
|
|
2931
3565
|
const { selectedAssetSymbol } = useTokensStore();
|
|
@@ -3178,6 +3812,7 @@ const WalletModalButton = (props) => {
|
|
|
3178
3812
|
const { icon: IconComponent, name, onClose } = props;
|
|
3179
3813
|
const { chainRegistry } = useChainStrategies();
|
|
3180
3814
|
const { connect, isPending } = wagmi.useConnect();
|
|
3815
|
+
const [isConnecting, setIsConnecting] = react.useState(false);
|
|
3181
3816
|
if (props.variant === "connected") {
|
|
3182
3817
|
const { address, onDisconnect } = props;
|
|
3183
3818
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: buttonBaseClasses, children: [
|
|
@@ -3201,6 +3836,7 @@ const WalletModalButton = (props) => {
|
|
|
3201
3836
|
}
|
|
3202
3837
|
const { walletId, connector } = props;
|
|
3203
3838
|
const handleConnect = async () => {
|
|
3839
|
+
setIsConnecting(true);
|
|
3204
3840
|
try {
|
|
3205
3841
|
if (connector) {
|
|
3206
3842
|
connect({ connector });
|
|
@@ -3213,9 +3849,13 @@ const WalletModalButton = (props) => {
|
|
|
3213
3849
|
onClose?.();
|
|
3214
3850
|
} catch (error) {
|
|
3215
3851
|
console.error("Failed to connect wallet:", error);
|
|
3852
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to connect wallet. Please try again.";
|
|
3853
|
+
sonner.toast.error(errorMessage);
|
|
3854
|
+
} finally {
|
|
3855
|
+
setIsConnecting(false);
|
|
3216
3856
|
}
|
|
3217
3857
|
};
|
|
3218
|
-
const isDisabled = connector ? isPending :
|
|
3858
|
+
const isDisabled = connector ? isPending : isConnecting;
|
|
3219
3859
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3220
3860
|
button.Button,
|
|
3221
3861
|
{
|
|
@@ -3250,8 +3890,8 @@ const WalletSelectModal = () => {
|
|
|
3250
3890
|
{
|
|
3251
3891
|
strategy: tonWallet,
|
|
3252
3892
|
walletId: "ton",
|
|
3253
|
-
name: t("wallets.
|
|
3254
|
-
icon:
|
|
3893
|
+
name: t("wallets.tonconnect"),
|
|
3894
|
+
icon: TonConnectIcon
|
|
3255
3895
|
},
|
|
3256
3896
|
{
|
|
3257
3897
|
strategy: metaMaskWallet,
|
|
@@ -3289,8 +3929,8 @@ const WalletSelectModal = () => {
|
|
|
3289
3929
|
const tonWallets = [
|
|
3290
3930
|
{
|
|
3291
3931
|
id: "ton",
|
|
3292
|
-
name: t("wallets.
|
|
3293
|
-
icon:
|
|
3932
|
+
name: t("wallets.tonconnect"),
|
|
3933
|
+
icon: TonConnectIcon,
|
|
3294
3934
|
enabled: true
|
|
3295
3935
|
}
|
|
3296
3936
|
];
|
|
@@ -3326,7 +3966,7 @@ const WalletSelectModal = () => {
|
|
|
3326
3966
|
}
|
|
3327
3967
|
].filter((category) => category.wallets.length > 0);
|
|
3328
3968
|
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { children: [
|
|
3329
|
-
/* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogHeader, { children: [
|
|
3969
|
+
/* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogHeader, { className: "text-left", children: [
|
|
3330
3970
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("wallets.chooseWallet") }),
|
|
3331
3971
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogDescription, { children: t("wallets.oneWalletPerEnv") })
|
|
3332
3972
|
] }),
|
|
@@ -3369,7 +4009,7 @@ const WalletSelectModal = () => {
|
|
|
3369
4009
|
] }) });
|
|
3370
4010
|
};
|
|
3371
4011
|
const ProgressStep = ({
|
|
3372
|
-
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-
|
|
4012
|
+
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "w-16 h-16 animate-spin" })
|
|
3373
4013
|
}) => {
|
|
3374
4014
|
const { t } = useBridgeTranslation();
|
|
3375
4015
|
return /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogContent, { showCloseButton: false, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex relative flex-col gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise bg-background", children: [
|
|
@@ -3379,40 +4019,130 @@ const ProgressStep = ({
|
|
|
3379
4019
|
] }) });
|
|
3380
4020
|
};
|
|
3381
4021
|
const FailedStep = ({
|
|
3382
|
-
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircleIcon, { className: "w-
|
|
4022
|
+
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircleIcon, { className: "w-16 h-16" })
|
|
3383
4023
|
}) => {
|
|
3384
4024
|
const { current, reset } = useTransactionStore();
|
|
3385
4025
|
const { t } = useBridgeTranslation();
|
|
3386
4026
|
return /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { showCloseButton: false, children: [
|
|
3387
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col relative gap-4
|
|
4027
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col relative gap-4 py-10 px-8 flex-1 items-center justify-start text-center noise", children: [
|
|
3388
4028
|
icon,
|
|
3389
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
4029
|
+
/* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogHeader, { children: [
|
|
4030
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("transaction.failed") }),
|
|
4031
|
+
current?.errorCode && /* @__PURE__ */ jsxRuntime.jsx(reactDialog.DialogDescription, { children: t(
|
|
4032
|
+
`errors.${current.errorCode}`,
|
|
4033
|
+
current.errorParams || {}
|
|
4034
|
+
) })
|
|
4035
|
+
] })
|
|
3394
4036
|
] }),
|
|
3395
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4037
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "outline", className: "w-full min-w-40", onClick: reset, children: t("common.close") }) })
|
|
3396
4038
|
] });
|
|
3397
4039
|
};
|
|
4040
|
+
const EXPLORER_CONFIGS = {
|
|
4041
|
+
// TON
|
|
4042
|
+
ton: {
|
|
4043
|
+
baseUrl: "https://tonscan.org",
|
|
4044
|
+
txPath: "/tx/"
|
|
4045
|
+
},
|
|
4046
|
+
// TRON
|
|
4047
|
+
tron: {
|
|
4048
|
+
baseUrl: "https://tronscan.org",
|
|
4049
|
+
txPath: "/#/transaction/"
|
|
4050
|
+
},
|
|
4051
|
+
// Ethereum & EVM chains
|
|
4052
|
+
ethereum: {
|
|
4053
|
+
baseUrl: "https://etherscan.io",
|
|
4054
|
+
txPath: "/tx/"
|
|
4055
|
+
},
|
|
4056
|
+
eth: {
|
|
4057
|
+
baseUrl: "https://etherscan.io",
|
|
4058
|
+
txPath: "/tx/"
|
|
4059
|
+
},
|
|
4060
|
+
// BSC (Binance Smart Chain)
|
|
4061
|
+
bsc: {
|
|
4062
|
+
baseUrl: "https://bscscan.com",
|
|
4063
|
+
txPath: "/tx/"
|
|
4064
|
+
},
|
|
4065
|
+
"binance-smart-chain": {
|
|
4066
|
+
baseUrl: "https://bscscan.com",
|
|
4067
|
+
txPath: "/tx/"
|
|
4068
|
+
},
|
|
4069
|
+
// Polygon
|
|
4070
|
+
polygon: {
|
|
4071
|
+
baseUrl: "https://polygonscan.com",
|
|
4072
|
+
txPath: "/tx/"
|
|
4073
|
+
},
|
|
4074
|
+
matic: {
|
|
4075
|
+
baseUrl: "https://polygonscan.com",
|
|
4076
|
+
txPath: "/tx/"
|
|
4077
|
+
},
|
|
4078
|
+
// Avalanche
|
|
4079
|
+
avalanche: {
|
|
4080
|
+
baseUrl: "https://snowtrace.io",
|
|
4081
|
+
txPath: "/tx/"
|
|
4082
|
+
},
|
|
4083
|
+
avax: {
|
|
4084
|
+
baseUrl: "https://snowtrace.io",
|
|
4085
|
+
txPath: "/tx/"
|
|
4086
|
+
},
|
|
4087
|
+
// Arbitrum
|
|
4088
|
+
arbitrum: {
|
|
4089
|
+
baseUrl: "https://arbiscan.io",
|
|
4090
|
+
txPath: "/tx/"
|
|
4091
|
+
},
|
|
4092
|
+
// Optimism
|
|
4093
|
+
optimism: {
|
|
4094
|
+
baseUrl: "https://optimistic.etherscan.io",
|
|
4095
|
+
txPath: "/tx/"
|
|
4096
|
+
},
|
|
4097
|
+
// Base
|
|
4098
|
+
base: {
|
|
4099
|
+
baseUrl: "https://basescan.org",
|
|
4100
|
+
txPath: "/tx/"
|
|
4101
|
+
},
|
|
4102
|
+
// Fantom
|
|
4103
|
+
fantom: {
|
|
4104
|
+
baseUrl: "https://ftmscan.com",
|
|
4105
|
+
txPath: "/tx/"
|
|
4106
|
+
}
|
|
4107
|
+
};
|
|
4108
|
+
function getExplorerTxUrl(chainKey, txHash) {
|
|
4109
|
+
if (!chainKey || !txHash) {
|
|
4110
|
+
return null;
|
|
4111
|
+
}
|
|
4112
|
+
const normalizedChainKey = chainKey.toLowerCase();
|
|
4113
|
+
const config = EXPLORER_CONFIGS[normalizedChainKey];
|
|
4114
|
+
if (!config) {
|
|
4115
|
+
console.warn(
|
|
4116
|
+
`No explorer config found for chain: ${chainKey}. Please add it to EXPLORER_CONFIGS.`
|
|
4117
|
+
);
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
4120
|
+
return `${config.baseUrl}${config.txPath}${txHash}`;
|
|
4121
|
+
}
|
|
4122
|
+
function openTransactionInExplorer(chainKey, txHash) {
|
|
4123
|
+
const url = getExplorerTxUrl(chainKey, txHash);
|
|
4124
|
+
if (url && typeof window !== "undefined") {
|
|
4125
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
3398
4128
|
const SuccessStep = ({
|
|
3399
|
-
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "w-
|
|
4129
|
+
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "w-16 h-16" })
|
|
3400
4130
|
}) => {
|
|
3401
4131
|
const { current, reset } = useTransactionStore();
|
|
3402
4132
|
const { t } = useBridgeTranslation();
|
|
3403
4133
|
const metadata = current?.metadata;
|
|
3404
4134
|
const srcTxHash = current?.srcTxHash;
|
|
3405
|
-
const
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
4135
|
+
const srcChainKey = current?.quote?.srcChainKey;
|
|
4136
|
+
const handleOpenExplorer = () => {
|
|
4137
|
+
if (srcTxHash && srcChainKey) {
|
|
4138
|
+
openTransactionInExplorer(srcChainKey, srcTxHash);
|
|
3409
4139
|
}
|
|
3410
4140
|
};
|
|
3411
4141
|
return /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { showCloseButton: false, children: [
|
|
3412
4142
|
/* @__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: [
|
|
3413
4143
|
icon,
|
|
3414
4144
|
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("transaction.success") }) }),
|
|
3415
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-
|
|
4145
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-2 mt-4 relative z-10 pb-14", children: [
|
|
3416
4146
|
metadata?.srcAmountHuman && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3417
4147
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.bridged") }),
|
|
3418
4148
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
|
|
@@ -3424,15 +4154,13 @@ const SuccessStep = ({
|
|
|
3424
4154
|
] }),
|
|
3425
4155
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3426
4156
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.transferTitle") }),
|
|
3427
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
4157
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium space-x-1", children: [
|
|
3428
4158
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3429
4159
|
metadata?.srcChainName,
|
|
3430
4160
|
" ",
|
|
3431
4161
|
/* @__PURE__ */ jsxRuntime.jsx(NetworkSymbol, { chainKey: metadata.srcChainName })
|
|
3432
4162
|
] }),
|
|
3433
|
-
" ",
|
|
3434
|
-
"→",
|
|
3435
|
-
" ",
|
|
4163
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "→" }),
|
|
3436
4164
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex gap-1 items-center", children: [
|
|
3437
4165
|
metadata?.dstChainName,
|
|
3438
4166
|
" ",
|
|
@@ -3445,18 +4173,18 @@ const SuccessStep = ({
|
|
|
3445
4173
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3446
4174
|
"button",
|
|
3447
4175
|
{
|
|
3448
|
-
onClick:
|
|
3449
|
-
className: "font-medium hover:underline
|
|
4176
|
+
onClick: handleOpenExplorer,
|
|
4177
|
+
className: "font-medium cursor-pointer inline-flex items-center gap-1 underline hover:no-underline",
|
|
3450
4178
|
children: formatHash(srcTxHash)
|
|
3451
4179
|
}
|
|
3452
4180
|
)
|
|
3453
4181
|
] }),
|
|
3454
|
-
metadata?.
|
|
4182
|
+
metadata?.actualFeeValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
3455
4183
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("transaction.finalFee") }),
|
|
3456
4184
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
3457
|
-
metadata.
|
|
4185
|
+
metadata.actualFeeValue,
|
|
3458
4186
|
" ",
|
|
3459
|
-
metadata.
|
|
4187
|
+
metadata.actualFeeSymbol
|
|
3460
4188
|
] })
|
|
3461
4189
|
] })
|
|
3462
4190
|
] })
|
|
@@ -3482,7 +4210,7 @@ const useCountdown = (initialSeconds) => {
|
|
|
3482
4210
|
};
|
|
3483
4211
|
};
|
|
3484
4212
|
const ConfirmStep = ({
|
|
3485
|
-
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-
|
|
4213
|
+
icon = /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-16 h-16" })
|
|
3486
4214
|
}) => {
|
|
3487
4215
|
const { t } = useBridgeTranslation();
|
|
3488
4216
|
const { formatTime } = useCountdown(90);
|
|
@@ -3553,7 +4281,7 @@ const useTokens = () => {
|
|
|
3553
4281
|
return { ...query };
|
|
3554
4282
|
};
|
|
3555
4283
|
const useChains = () => {
|
|
3556
|
-
const { setChains, setFromChain } = useChainsStore();
|
|
4284
|
+
const { setChains, setFromChain, fromChain } = useChainsStore();
|
|
3557
4285
|
const query = reactQuery.useQuery({
|
|
3558
4286
|
queryKey: ["chains"],
|
|
3559
4287
|
queryFn: () => getChains(),
|
|
@@ -3567,10 +4295,16 @@ const useChains = () => {
|
|
|
3567
4295
|
});
|
|
3568
4296
|
react.useEffect(() => {
|
|
3569
4297
|
if (query.isSuccess && query.data?.length) {
|
|
4298
|
+
console.log(
|
|
4299
|
+
`[DEBUG] Loaded ${query.data.length} chains from API:`,
|
|
4300
|
+
query.data.map((c) => c.chainKey)
|
|
4301
|
+
);
|
|
3570
4302
|
setChains(query.data);
|
|
3571
|
-
|
|
4303
|
+
if (!fromChain) {
|
|
4304
|
+
setFromChain(query.data[0]);
|
|
4305
|
+
}
|
|
3572
4306
|
}
|
|
3573
|
-
}, [query.isSuccess, query.data
|
|
4307
|
+
}, [query.isSuccess, query.data]);
|
|
3574
4308
|
react.useEffect(() => {
|
|
3575
4309
|
if (query.isError && query.error) {
|
|
3576
4310
|
console.error(query.error.name, query.error.message);
|
|
@@ -3636,49 +4370,6 @@ class ChainStrategyRegistry {
|
|
|
3636
4370
|
await strategy.disconnect();
|
|
3637
4371
|
}
|
|
3638
4372
|
}
|
|
3639
|
-
const EVM_CONFIG = {
|
|
3640
|
-
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
3641
|
-
gasEstimates: {
|
|
3642
|
-
approve: 65000n,
|
|
3643
|
-
bridge: 300000n
|
|
3644
|
-
},
|
|
3645
|
-
gasBuffer: 1.2,
|
|
3646
|
-
// 20% buffer
|
|
3647
|
-
timeout: 3e5,
|
|
3648
|
-
// 5 minutes (increased for slower networks)
|
|
3649
|
-
requiredConfirmations: 3
|
|
3650
|
-
// Wait for 3 confirmations for reorg protection
|
|
3651
|
-
};
|
|
3652
|
-
const TON_CONFIG = {
|
|
3653
|
-
apiUrl: "https://toncenter.com/api/v2",
|
|
3654
|
-
timeout: 36e4,
|
|
3655
|
-
// 6 minutes
|
|
3656
|
-
validUntil: 600
|
|
3657
|
-
// 10 minutes
|
|
3658
|
-
};
|
|
3659
|
-
const TRON_CONFIG = {
|
|
3660
|
-
timeout: 12e4,
|
|
3661
|
-
// 2 minutes (for 19 confirmations)
|
|
3662
|
-
feeLimit: 1e8,
|
|
3663
|
-
// 100 TRX in sun
|
|
3664
|
-
requiredConfirmations: 19,
|
|
3665
|
-
// TRON standard: 19 blocks for confirmation
|
|
3666
|
-
pollingInterval: 3e3
|
|
3667
|
-
// 3 seconds between checks
|
|
3668
|
-
};
|
|
3669
|
-
let tonClientInstance = null;
|
|
3670
|
-
function getTonClient(customClient, apiKey) {
|
|
3671
|
-
if (customClient) {
|
|
3672
|
-
return customClient;
|
|
3673
|
-
}
|
|
3674
|
-
if (!tonClientInstance) {
|
|
3675
|
-
tonClientInstance = new ton.TonClient({
|
|
3676
|
-
endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
|
|
3677
|
-
apiKey
|
|
3678
|
-
});
|
|
3679
|
-
}
|
|
3680
|
-
return tonClientInstance;
|
|
3681
|
-
}
|
|
3682
4373
|
function isNativeAddress(addr) {
|
|
3683
4374
|
if (!addr) return false;
|
|
3684
4375
|
const a = addr.toLowerCase();
|
|
@@ -3771,7 +4462,9 @@ async function getEvmBalances(publicClient, address, tokens, priorityToken) {
|
|
|
3771
4462
|
}
|
|
3772
4463
|
} catch (error) {
|
|
3773
4464
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3774
|
-
const isZeroDataError = errorMessage.includes(
|
|
4465
|
+
const isZeroDataError = errorMessage.includes(
|
|
4466
|
+
'returned no data ("0x")'
|
|
4467
|
+
);
|
|
3775
4468
|
if (!isZeroDataError) {
|
|
3776
4469
|
console.debug(
|
|
3777
4470
|
`Failed to get priority token balance for ${priorityToken.symbol}:`,
|
|
@@ -3882,12 +4575,9 @@ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
|
|
|
3882
4575
|
}
|
|
3883
4576
|
const client = getTonClient(customTonClient, tonApiKey);
|
|
3884
4577
|
const accountAddress = ton.Address.parse(address);
|
|
3885
|
-
console.log(address);
|
|
3886
|
-
console.log(tokens);
|
|
3887
4578
|
try {
|
|
3888
4579
|
const balance = await client.getBalance(accountAddress);
|
|
3889
4580
|
const tonBalance = Number(balance) / 1e9;
|
|
3890
|
-
console.log("tonBalance", tonBalance);
|
|
3891
4581
|
if (tonBalance > 0) {
|
|
3892
4582
|
balances.TON = { balance: tonBalance, address: "ton-native" };
|
|
3893
4583
|
}
|
|
@@ -3944,8 +4634,23 @@ async function getTronBalances(tronWeb, address, tokens) {
|
|
|
3944
4634
|
try {
|
|
3945
4635
|
if (!tronWeb) throw new Error("TronWeb not available");
|
|
3946
4636
|
const ownerB58 = toTronBase58(address, tronWeb);
|
|
4637
|
+
try {
|
|
4638
|
+
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
4639
|
+
const sunNum = Number(trxBalanceSun);
|
|
4640
|
+
const trxVal = tronWeb.fromSun(sunNum);
|
|
4641
|
+
const trxStr = typeof trxVal === "string" ? trxVal : trxVal.toString();
|
|
4642
|
+
const trxBalance = parseFloat(trxStr);
|
|
4643
|
+
if (trxBalance > 0) {
|
|
4644
|
+
balances.TRX = { balance: trxBalance, address: ownerB58 };
|
|
4645
|
+
}
|
|
4646
|
+
} catch (error) {
|
|
4647
|
+
console.warn("Failed to get native TRX balance:", error);
|
|
4648
|
+
}
|
|
3947
4649
|
for (const token of tokens) {
|
|
3948
4650
|
try {
|
|
4651
|
+
if (isNativeAddress(token.address) && token.symbol.toUpperCase() === "TRX") {
|
|
4652
|
+
continue;
|
|
4653
|
+
}
|
|
3949
4654
|
let balance = 0;
|
|
3950
4655
|
if (isNativeAddress(token.address)) {
|
|
3951
4656
|
const trxBalanceSun = await tronWeb.trx.getBalance(ownerB58);
|
|
@@ -4032,6 +4737,9 @@ class EvmChainStrategy {
|
|
|
4032
4737
|
getConnectLabel(t) {
|
|
4033
4738
|
return t("wallets.connectEvmWallet");
|
|
4034
4739
|
}
|
|
4740
|
+
getClient() {
|
|
4741
|
+
return this.provider;
|
|
4742
|
+
}
|
|
4035
4743
|
async getBalances(address, tokens, priorityToken) {
|
|
4036
4744
|
if (!this.publicClient) {
|
|
4037
4745
|
console.warn("No publicClient available for balance query");
|
|
@@ -4048,52 +4756,6 @@ class EvmChainStrategy {
|
|
|
4048
4756
|
if (!address) return false;
|
|
4049
4757
|
return /^0x[0-9a-fA-F]{40}$/.test(address);
|
|
4050
4758
|
}
|
|
4051
|
-
async estimateGasRequirement(params) {
|
|
4052
|
-
const provider = this.provider;
|
|
4053
|
-
const {
|
|
4054
|
-
selectedToken,
|
|
4055
|
-
nativeTokenSymbol,
|
|
4056
|
-
amount,
|
|
4057
|
-
balances,
|
|
4058
|
-
nativeDecimals,
|
|
4059
|
-
reserveFallback
|
|
4060
|
-
} = params;
|
|
4061
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4062
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4063
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4064
|
-
let estimatedGas = null;
|
|
4065
|
-
if (provider) {
|
|
4066
|
-
try {
|
|
4067
|
-
const gasPriceHex = await provider.send("eth_gasPrice", []);
|
|
4068
|
-
const gasPrice = BigInt(gasPriceHex);
|
|
4069
|
-
const approveGas = isNativeSelected ? 0n : EVM_CONFIG.gasEstimates.approve;
|
|
4070
|
-
const bridgeGas = EVM_CONFIG.gasEstimates.bridge;
|
|
4071
|
-
const totalGas = approveGas + bridgeGas;
|
|
4072
|
-
const bufferMultiplier = BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100));
|
|
4073
|
-
const requiredWei = gasPrice * totalGas * bufferMultiplier / 100n;
|
|
4074
|
-
const { formatUnits } = await import("ethers");
|
|
4075
|
-
estimatedGas = Number(formatUnits(requiredWei, nativeDecimals));
|
|
4076
|
-
} catch {
|
|
4077
|
-
estimatedGas = null;
|
|
4078
|
-
}
|
|
4079
|
-
}
|
|
4080
|
-
const requiredNative = estimatedGas ?? reserveFallback;
|
|
4081
|
-
const amountNum = amount ?? 0;
|
|
4082
|
-
let hasEnoughGas = true;
|
|
4083
|
-
if (isNativeSelected) {
|
|
4084
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4085
|
-
} else {
|
|
4086
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4087
|
-
}
|
|
4088
|
-
return {
|
|
4089
|
-
estimatedGas,
|
|
4090
|
-
nativeBalance,
|
|
4091
|
-
requiredNative,
|
|
4092
|
-
hasEnoughGas,
|
|
4093
|
-
nativeSym,
|
|
4094
|
-
isNativeSelected
|
|
4095
|
-
};
|
|
4096
|
-
}
|
|
4097
4759
|
validateSteps(steps) {
|
|
4098
4760
|
if (!steps || steps.length === 0) {
|
|
4099
4761
|
throw new InvalidStepsError("evm", "No transaction steps provided");
|
|
@@ -4186,8 +4848,24 @@ class EvmChainStrategy {
|
|
|
4186
4848
|
console.log(
|
|
4187
4849
|
`EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
|
|
4188
4850
|
);
|
|
4851
|
+
let actualFeeValue;
|
|
4852
|
+
try {
|
|
4853
|
+
const gasUsed = receipt.gasUsed;
|
|
4854
|
+
const effectiveGasPrice = receipt.gasPrice;
|
|
4855
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4856
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4857
|
+
const { formatUnits } = await import("ethers");
|
|
4858
|
+
const feeInNative = formatUnits(feeWei, 18);
|
|
4859
|
+
actualFeeValue = feeInNative;
|
|
4860
|
+
console.log(`EVM transaction fee: ${feeInNative} (native token)`);
|
|
4861
|
+
}
|
|
4862
|
+
} catch (error) {
|
|
4863
|
+
console.warn("Failed to calculate actual fee:", error);
|
|
4864
|
+
}
|
|
4189
4865
|
return {
|
|
4190
|
-
completed: true
|
|
4866
|
+
completed: true,
|
|
4867
|
+
actualFeeValue
|
|
4868
|
+
// Symbol will be determined by the caller based on chain info
|
|
4191
4869
|
};
|
|
4192
4870
|
} catch (error) {
|
|
4193
4871
|
if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
|
|
@@ -4200,8 +4878,26 @@ class EvmChainStrategy {
|
|
|
4200
4878
|
console.log(
|
|
4201
4879
|
`Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
|
|
4202
4880
|
);
|
|
4881
|
+
let actualFeeValue;
|
|
4882
|
+
try {
|
|
4883
|
+
const receipt = error.receipt;
|
|
4884
|
+
const gasUsed = receipt.gasUsed;
|
|
4885
|
+
const effectiveGasPrice = receipt.gasPrice || receipt.effectiveGasPrice;
|
|
4886
|
+
if (gasUsed && effectiveGasPrice) {
|
|
4887
|
+
const feeWei = gasUsed * effectiveGasPrice;
|
|
4888
|
+
const { formatUnits } = await import("ethers");
|
|
4889
|
+
const feeInNative = formatUnits(feeWei, 18);
|
|
4890
|
+
actualFeeValue = feeInNative;
|
|
4891
|
+
console.log(
|
|
4892
|
+
`Replacement transaction fee: ${feeInNative} (native token)`
|
|
4893
|
+
);
|
|
4894
|
+
}
|
|
4895
|
+
} catch (feeError) {
|
|
4896
|
+
console.warn("Failed to calculate replacement transaction fee:", feeError);
|
|
4897
|
+
}
|
|
4203
4898
|
return {
|
|
4204
|
-
completed: true
|
|
4899
|
+
completed: true,
|
|
4900
|
+
actualFeeValue
|
|
4205
4901
|
};
|
|
4206
4902
|
} else {
|
|
4207
4903
|
const chainError2 = new TransactionRevertedError("evm", txHash);
|
|
@@ -4404,6 +5100,9 @@ class TonChainStrategy {
|
|
|
4404
5100
|
getConnectLabel(t) {
|
|
4405
5101
|
return t("wallets.connectTonWallet");
|
|
4406
5102
|
}
|
|
5103
|
+
getClient() {
|
|
5104
|
+
return getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
5105
|
+
}
|
|
4407
5106
|
async getBalances(address, tokens) {
|
|
4408
5107
|
return await getTonBalances(
|
|
4409
5108
|
address,
|
|
@@ -4416,35 +5115,6 @@ class TonChainStrategy {
|
|
|
4416
5115
|
if (!address) return false;
|
|
4417
5116
|
return true;
|
|
4418
5117
|
}
|
|
4419
|
-
async estimateGasRequirement(params) {
|
|
4420
|
-
const {
|
|
4421
|
-
selectedToken,
|
|
4422
|
-
nativeTokenSymbol,
|
|
4423
|
-
amount,
|
|
4424
|
-
balances,
|
|
4425
|
-
reserveFallback
|
|
4426
|
-
} = params;
|
|
4427
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4428
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4429
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4430
|
-
const estimatedGas = null;
|
|
4431
|
-
const requiredNative = reserveFallback;
|
|
4432
|
-
const amountNum = amount ?? 0;
|
|
4433
|
-
let hasEnoughGas = true;
|
|
4434
|
-
if (isNativeSelected) {
|
|
4435
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4436
|
-
} else {
|
|
4437
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4438
|
-
}
|
|
4439
|
-
return {
|
|
4440
|
-
estimatedGas,
|
|
4441
|
-
nativeBalance,
|
|
4442
|
-
requiredNative,
|
|
4443
|
-
hasEnoughGas,
|
|
4444
|
-
nativeSym,
|
|
4445
|
-
isNativeSelected
|
|
4446
|
-
};
|
|
4447
|
-
}
|
|
4448
5118
|
validateSteps(steps) {
|
|
4449
5119
|
if (!steps || steps.length === 0) {
|
|
4450
5120
|
throw new InvalidStepsError("ton", "No transaction steps provided");
|
|
@@ -4497,23 +5167,35 @@ class TonChainStrategy {
|
|
|
4497
5167
|
const result = await this.config.tonConnectUI.sendTransaction(
|
|
4498
5168
|
transaction2
|
|
4499
5169
|
);
|
|
4500
|
-
const
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
5170
|
+
const bocBase64 = result.boc;
|
|
5171
|
+
try {
|
|
5172
|
+
const inMessage = core.loadMessage(core.Cell.fromBase64(bocBase64).beginParse());
|
|
5173
|
+
const messageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
5174
|
+
const hexHash = messageHash.toString("hex");
|
|
5175
|
+
onFirstHash?.(hexHash);
|
|
5176
|
+
return {
|
|
5177
|
+
chainKey: "ton",
|
|
5178
|
+
hash: hexHash
|
|
5179
|
+
};
|
|
5180
|
+
} catch (error) {
|
|
5181
|
+
console.error("Error parsing BOC to hex hash:", error);
|
|
5182
|
+
onFirstHash?.(bocBase64);
|
|
5183
|
+
return {
|
|
5184
|
+
chainKey: "ton",
|
|
5185
|
+
hash: bocBase64
|
|
5186
|
+
};
|
|
5187
|
+
}
|
|
4506
5188
|
} catch (error) {
|
|
4507
5189
|
throw toChainStrategyError(error, "ton", "transaction");
|
|
4508
5190
|
}
|
|
4509
5191
|
}
|
|
4510
5192
|
async waitForCompletion(txHash) {
|
|
4511
5193
|
try {
|
|
4512
|
-
const
|
|
5194
|
+
const result = await this.checkTonTransaction(
|
|
4513
5195
|
txHash,
|
|
4514
5196
|
TON_CONFIG.timeout
|
|
4515
5197
|
);
|
|
4516
|
-
if (!confirmed) {
|
|
5198
|
+
if (!result.confirmed) {
|
|
4517
5199
|
const error = new TransactionFailedError(
|
|
4518
5200
|
"ton",
|
|
4519
5201
|
"Transaction not confirmed on-chain",
|
|
@@ -4525,7 +5207,9 @@ class TonChainStrategy {
|
|
|
4525
5207
|
};
|
|
4526
5208
|
}
|
|
4527
5209
|
return {
|
|
4528
|
-
completed: true
|
|
5210
|
+
completed: true,
|
|
5211
|
+
actualFeeValue: result.fee,
|
|
5212
|
+
actualFeeSymbol: "TON"
|
|
4529
5213
|
};
|
|
4530
5214
|
} catch (error) {
|
|
4531
5215
|
const chainError = toChainStrategyError(
|
|
@@ -4555,20 +5239,31 @@ class TonChainStrategy {
|
|
|
4555
5239
|
};
|
|
4556
5240
|
return core.beginCell().store(core.storeMessage(normalizedMessage, { forceRef: true })).endCell().hash();
|
|
4557
5241
|
}
|
|
4558
|
-
async checkTonTransaction(
|
|
5242
|
+
async checkTonTransaction(hashOrBoc, timeoutMs = 36e4) {
|
|
4559
5243
|
const deadline = Date.now() + timeoutMs;
|
|
4560
5244
|
const client = getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
4561
5245
|
try {
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
5246
|
+
let targetMessageHash;
|
|
5247
|
+
let accountAddress;
|
|
5248
|
+
try {
|
|
5249
|
+
const inMessage = core.loadMessage(core.Cell.fromBase64(hashOrBoc).beginParse());
|
|
5250
|
+
if (inMessage.info.type !== "external-in") {
|
|
5251
|
+
console.debug(
|
|
5252
|
+
"Expected external-in message, got:",
|
|
5253
|
+
inMessage.info.type
|
|
5254
|
+
);
|
|
5255
|
+
return { confirmed: false };
|
|
5256
|
+
}
|
|
5257
|
+
accountAddress = inMessage.info.dest;
|
|
5258
|
+
targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
5259
|
+
} catch {
|
|
5260
|
+
targetMessageHash = Buffer.from(hashOrBoc, "hex");
|
|
5261
|
+
if (!this.config.tonAddress) {
|
|
5262
|
+
console.debug("No wallet address available for hex hash lookup");
|
|
5263
|
+
return { confirmed: false };
|
|
5264
|
+
}
|
|
5265
|
+
accountAddress = core.Address.parse(this.config.tonAddress);
|
|
4569
5266
|
}
|
|
4570
|
-
const accountAddress = inMessage.info.dest;
|
|
4571
|
-
const targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
4572
5267
|
let lt = void 0;
|
|
4573
5268
|
let hash = void 0;
|
|
4574
5269
|
while (Date.now() < deadline) {
|
|
@@ -4580,7 +5275,7 @@ class TonChainStrategy {
|
|
|
4580
5275
|
archival: true
|
|
4581
5276
|
});
|
|
4582
5277
|
if (transactions.length === 0) {
|
|
4583
|
-
await new Promise((r) => setTimeout(r,
|
|
5278
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4584
5279
|
lt = void 0;
|
|
4585
5280
|
hash = void 0;
|
|
4586
5281
|
continue;
|
|
@@ -4592,24 +5287,31 @@ class TonChainStrategy {
|
|
|
4592
5287
|
);
|
|
4593
5288
|
if (txInMessageHash.equals(targetMessageHash)) {
|
|
4594
5289
|
console.debug("Transaction found by in-message hash");
|
|
4595
|
-
|
|
5290
|
+
const totalFees = tx.totalFees;
|
|
5291
|
+
const feeInTon = Number(totalFees) / 1e9;
|
|
5292
|
+
console.log(`TON transaction fee: ${feeInTon} TON`);
|
|
5293
|
+
return {
|
|
5294
|
+
confirmed: true,
|
|
5295
|
+
fee: feeInTon.toString()
|
|
5296
|
+
};
|
|
4596
5297
|
}
|
|
4597
5298
|
}
|
|
4598
5299
|
}
|
|
4599
5300
|
const lastTx = transactions[transactions.length - 1];
|
|
4600
5301
|
lt = lastTx.lt.toString();
|
|
4601
5302
|
hash = lastTx.hash().toString("base64");
|
|
5303
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4602
5304
|
} catch (error) {
|
|
4603
5305
|
console.debug("Error fetching transactions:", error);
|
|
4604
|
-
await new Promise((r) => setTimeout(r,
|
|
5306
|
+
await new Promise((r) => setTimeout(r, TON_CONFIG.pollingInterval));
|
|
4605
5307
|
lt = void 0;
|
|
4606
5308
|
hash = void 0;
|
|
4607
5309
|
}
|
|
4608
5310
|
}
|
|
4609
|
-
return false;
|
|
5311
|
+
return { confirmed: false };
|
|
4610
5312
|
} catch (error) {
|
|
4611
5313
|
console.debug("Error parsing BOC or checking transaction:", error);
|
|
4612
|
-
return false;
|
|
5314
|
+
return { confirmed: false };
|
|
4613
5315
|
}
|
|
4614
5316
|
}
|
|
4615
5317
|
}
|
|
@@ -4628,9 +5330,14 @@ class TronChainStrategy {
|
|
|
4628
5330
|
return "TRON Chain Strategy";
|
|
4629
5331
|
}
|
|
4630
5332
|
async connect() {
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
5333
|
+
if (!this.isTronLinkInstalled()) {
|
|
5334
|
+
if (typeof window !== "undefined") {
|
|
5335
|
+
window.open("https://www.tronlink.org/", "_blank");
|
|
5336
|
+
}
|
|
5337
|
+
throw new WalletNotFoundError(
|
|
5338
|
+
"tron",
|
|
5339
|
+
"TronLink wallet is not installed. Please install TronLink extension and try again."
|
|
5340
|
+
);
|
|
4634
5341
|
}
|
|
4635
5342
|
this.config.tronSelect(tronwalletAdapters.TronLinkAdapterName);
|
|
4636
5343
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
@@ -4658,7 +5365,7 @@ class TronChainStrategy {
|
|
|
4658
5365
|
return t("wallets.connectTronWallet");
|
|
4659
5366
|
}
|
|
4660
5367
|
async getBalances(address, tokens) {
|
|
4661
|
-
const tronWeb = this.
|
|
5368
|
+
const tronWeb = this.getClient();
|
|
4662
5369
|
if (!tronWeb) return {};
|
|
4663
5370
|
return await getTronBalances(tronWeb, address, tokens);
|
|
4664
5371
|
}
|
|
@@ -4666,35 +5373,6 @@ class TronChainStrategy {
|
|
|
4666
5373
|
if (!address) return false;
|
|
4667
5374
|
return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(address);
|
|
4668
5375
|
}
|
|
4669
|
-
async estimateGasRequirement(params) {
|
|
4670
|
-
const {
|
|
4671
|
-
selectedToken,
|
|
4672
|
-
nativeTokenSymbol,
|
|
4673
|
-
amount,
|
|
4674
|
-
balances,
|
|
4675
|
-
reserveFallback
|
|
4676
|
-
} = params;
|
|
4677
|
-
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
4678
|
-
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
4679
|
-
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
4680
|
-
const estimatedGas = null;
|
|
4681
|
-
const requiredNative = reserveFallback;
|
|
4682
|
-
const amountNum = amount ?? 0;
|
|
4683
|
-
let hasEnoughGas = true;
|
|
4684
|
-
if (isNativeSelected) {
|
|
4685
|
-
hasEnoughGas = nativeBalance - amountNum >= requiredNative;
|
|
4686
|
-
} else {
|
|
4687
|
-
hasEnoughGas = nativeBalance >= requiredNative;
|
|
4688
|
-
}
|
|
4689
|
-
return {
|
|
4690
|
-
estimatedGas,
|
|
4691
|
-
nativeBalance,
|
|
4692
|
-
requiredNative,
|
|
4693
|
-
hasEnoughGas,
|
|
4694
|
-
nativeSym,
|
|
4695
|
-
isNativeSelected
|
|
4696
|
-
};
|
|
4697
|
-
}
|
|
4698
5376
|
validateSteps(steps) {
|
|
4699
5377
|
console.log("validateSteps");
|
|
4700
5378
|
if (!steps?.length) {
|
|
@@ -4711,8 +5389,7 @@ class TronChainStrategy {
|
|
|
4711
5389
|
}
|
|
4712
5390
|
}
|
|
4713
5391
|
async executeSteps(steps, _context, onFirstHash) {
|
|
4714
|
-
|
|
4715
|
-
const tronWeb = this.getTronWeb();
|
|
5392
|
+
const tronWeb = this.getClient();
|
|
4716
5393
|
if (!tronWeb) {
|
|
4717
5394
|
throw new ProviderNotAvailableError("tron");
|
|
4718
5395
|
}
|
|
@@ -4806,7 +5483,7 @@ class TronChainStrategy {
|
|
|
4806
5483
|
}
|
|
4807
5484
|
async waitForCompletion(txHash) {
|
|
4808
5485
|
try {
|
|
4809
|
-
const tronWeb = this.
|
|
5486
|
+
const tronWeb = this.getClient();
|
|
4810
5487
|
if (!tronWeb) {
|
|
4811
5488
|
throw new ProviderNotAvailableError("tron");
|
|
4812
5489
|
}
|
|
@@ -4815,6 +5492,7 @@ class TronChainStrategy {
|
|
|
4815
5492
|
);
|
|
4816
5493
|
const deadline = Date.now() + TRON_CONFIG.timeout;
|
|
4817
5494
|
let txBlockNumber = null;
|
|
5495
|
+
let actualFeeTrx = null;
|
|
4818
5496
|
while (Date.now() < deadline && !txBlockNumber) {
|
|
4819
5497
|
try {
|
|
4820
5498
|
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
@@ -4849,7 +5527,9 @@ class TronChainStrategy {
|
|
|
4849
5527
|
};
|
|
4850
5528
|
}
|
|
4851
5529
|
txBlockNumber = info.blockNumber;
|
|
4852
|
-
|
|
5530
|
+
const feeSun = info.fee || 0;
|
|
5531
|
+
actualFeeTrx = feeSun / 1e6;
|
|
5532
|
+
console.log(`TRON transaction found in block ${txBlockNumber}, fee: ${actualFeeTrx} TRX`);
|
|
4853
5533
|
}
|
|
4854
5534
|
} catch (e) {
|
|
4855
5535
|
console.debug("TRON getTransactionInfo error:", e);
|
|
@@ -4877,7 +5557,9 @@ class TronChainStrategy {
|
|
|
4877
5557
|
`TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
|
|
4878
5558
|
);
|
|
4879
5559
|
return {
|
|
4880
|
-
completed: true
|
|
5560
|
+
completed: true,
|
|
5561
|
+
actualFeeValue: actualFeeTrx?.toString(),
|
|
5562
|
+
actualFeeSymbol: "TRX"
|
|
4881
5563
|
};
|
|
4882
5564
|
}
|
|
4883
5565
|
console.log(
|
|
@@ -4906,9 +5588,26 @@ class TronChainStrategy {
|
|
|
4906
5588
|
};
|
|
4907
5589
|
}
|
|
4908
5590
|
}
|
|
4909
|
-
|
|
5591
|
+
getClient() {
|
|
4910
5592
|
return typeof window !== "undefined" ? window.tronWeb : void 0;
|
|
4911
5593
|
}
|
|
5594
|
+
/**
|
|
5595
|
+
* Check if TronLink wallet is actually installed
|
|
5596
|
+
* This excludes Bybit Wallet which also injects tronLink for compatibility
|
|
5597
|
+
*/
|
|
5598
|
+
isTronLinkInstalled() {
|
|
5599
|
+
if (typeof window === "undefined") {
|
|
5600
|
+
return false;
|
|
5601
|
+
}
|
|
5602
|
+
const hasBybitWallet = typeof window.bybitWallet !== "undefined" && typeof window.bybitWallet.tronLink !== "undefined";
|
|
5603
|
+
if (hasBybitWallet && !window.tronLink) {
|
|
5604
|
+
return false;
|
|
5605
|
+
}
|
|
5606
|
+
if (!window.tronLink) {
|
|
5607
|
+
return false;
|
|
5608
|
+
}
|
|
5609
|
+
return true;
|
|
5610
|
+
}
|
|
4912
5611
|
hexToAscii(h) {
|
|
4913
5612
|
if (!h) return null;
|
|
4914
5613
|
const clean = h.replace(/^0x/, "");
|
|
@@ -5206,7 +5905,7 @@ class TronChainStrategy {
|
|
|
5206
5905
|
*/
|
|
5207
5906
|
async checkSolidified(txHash) {
|
|
5208
5907
|
try {
|
|
5209
|
-
const tronWeb = this.
|
|
5908
|
+
const tronWeb = this.getClient();
|
|
5210
5909
|
if (!tronWeb) {
|
|
5211
5910
|
return false;
|
|
5212
5911
|
}
|
|
@@ -5312,7 +6011,7 @@ const routePresets = [
|
|
|
5312
6011
|
RoutePriority.CHEAPEST,
|
|
5313
6012
|
RoutePriority.RECOMMENDED
|
|
5314
6013
|
];
|
|
5315
|
-
const
|
|
6014
|
+
const SettingsModal = ({ isOpen, onClose }) => {
|
|
5316
6015
|
const { t } = useBridgeTranslation();
|
|
5317
6016
|
const { toChain } = useChainsStore();
|
|
5318
6017
|
const { tokens } = useTokensStore();
|
|
@@ -5345,14 +6044,14 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5345
6044
|
);
|
|
5346
6045
|
const activeBtn = "bg-primary hover:bg-primary/80 text-primary-foreground transition-colors";
|
|
5347
6046
|
const notActiveBtn = "bg-accent hover:bg-accent/80 text-accent-foreground transition-colors";
|
|
5348
|
-
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { children: [
|
|
5349
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("settings.title") }) }),
|
|
6047
|
+
return /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { onOpenAutoFocus: (e) => e.preventDefault(), children: [
|
|
6048
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("settings.title") }) }),
|
|
5350
6049
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5", children: [
|
|
5351
6050
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5352
6051
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5353
6052
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5354
6053
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.gasOnDestination") }),
|
|
5355
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6054
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.gasOnDestination"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5356
6055
|
] }),
|
|
5357
6056
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground text-sm font-medium leading-4", children: formatUsd(gasUsdValue) })
|
|
5358
6057
|
] }),
|
|
@@ -5374,6 +6073,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5374
6073
|
badge.Badge,
|
|
5375
6074
|
{
|
|
5376
6075
|
onClick: () => setGasPreset(g),
|
|
6076
|
+
size: "lg",
|
|
5377
6077
|
className: utils.cn(
|
|
5378
6078
|
"cursor-pointer",
|
|
5379
6079
|
gasPreset === g ? activeBtn : notActiveBtn
|
|
@@ -5389,7 +6089,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5389
6089
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
5390
6090
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5391
6091
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.slippageTolerance") }),
|
|
5392
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6092
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.slippageTolerance"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5393
6093
|
] }),
|
|
5394
6094
|
slippageBps >= 500 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-destructive text-xs font-medium", children: t("settings.highSlippageWarning", {
|
|
5395
6095
|
defaultValue: "High slippage warning"
|
|
@@ -5400,6 +6100,7 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5400
6100
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: slippagePresets.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5401
6101
|
badge.Badge,
|
|
5402
6102
|
{
|
|
6103
|
+
size: "lg",
|
|
5403
6104
|
onClick: () => {
|
|
5404
6105
|
const bps = parseFloat(p.replace("%", "")) * 100;
|
|
5405
6106
|
setSlippageBps(bps);
|
|
@@ -5418,11 +6119,12 @@ const SettingModal = ({ isOpen, onClose }) => {
|
|
|
5418
6119
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
5419
6120
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between items-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5420
6121
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-sm font-medium leading-4", children: t("settings.routePriority") }),
|
|
5421
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6122
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tip, { text: t("settings.routePriority"), children: /* @__PURE__ */ jsxRuntime.jsx(InfoIcon, { className: "w-4 h-4 text-muted-foreground" }) })
|
|
5422
6123
|
] }) }),
|
|
5423
6124
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-2", children: routePresets.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
5424
6125
|
badge.Badge,
|
|
5425
6126
|
{
|
|
6127
|
+
size: "lg",
|
|
5426
6128
|
onClick: () => setRoutePriority(r),
|
|
5427
6129
|
className: utils.cn(
|
|
5428
6130
|
"cursor-pointer",
|
|
@@ -5450,7 +6152,7 @@ const TokenRow = ({
|
|
|
5450
6152
|
button.Button,
|
|
5451
6153
|
{
|
|
5452
6154
|
onClick: onPick,
|
|
5453
|
-
className: `w-full rounded-
|
|
6155
|
+
className: `w-full rounded-xs bg-transparent flex items-center justify-between gap-3 px-3 hover:bg-accent hover:scale-100 ${isSelected ? "bg-accent hover:bg-accent/50" : ""}`,
|
|
5454
6156
|
children: [
|
|
5455
6157
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
5456
6158
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5471,7 +6173,7 @@ const TokenRow = ({
|
|
|
5471
6173
|
/* @__PURE__ */ jsxRuntime.jsx(skeleton.Skeleton, { className: "h-3 w-12 rounded-md" })
|
|
5472
6174
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5473
6175
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-foreground text-lg leading-4 truncate", children: hasAnyWallet ? formatBalance(balance) : "—" }),
|
|
5474
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
6176
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 text-muted-foreground/50", children: hasAnyWallet && balance > 0 && usdValue > 0 ? formatUsd(usdValue) : "—" })
|
|
5475
6177
|
] }) })
|
|
5476
6178
|
]
|
|
5477
6179
|
}
|
|
@@ -5614,8 +6316,8 @@ const TokenSelectModal = ({
|
|
|
5614
6316
|
[groupedTokens.willChangeSrcChain]
|
|
5615
6317
|
);
|
|
5616
6318
|
const hasNoResults = tokensToRender.length === 0 && willChangeSrcTokens.length === 0;
|
|
5617
|
-
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: [
|
|
5618
|
-
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectToken") }) }),
|
|
6319
|
+
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", children: [
|
|
6320
|
+
/* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { className: "text-left", children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: t("bridge.selectToken") }) }),
|
|
5619
6321
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5620
6322
|
SearchInput,
|
|
5621
6323
|
{
|
|
@@ -5628,24 +6330,26 @@ const TokenSelectModal = ({
|
|
|
5628
6330
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5629
6331
|
button.Button,
|
|
5630
6332
|
{
|
|
5631
|
-
variant: tab === "my" ? "default" : "
|
|
6333
|
+
variant: tab === "my" ? "default" : "ghost",
|
|
5632
6334
|
onClick: () => setTab("my"),
|
|
5633
6335
|
size: "sm",
|
|
6336
|
+
className: utils.cn(tab !== "my" && "bg-muted hover:bg-accent"),
|
|
5634
6337
|
children: t("bridge.myTokens")
|
|
5635
6338
|
}
|
|
5636
6339
|
),
|
|
5637
6340
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5638
6341
|
button.Button,
|
|
5639
6342
|
{
|
|
5640
|
-
variant: tab === "all" ? "default" : "
|
|
6343
|
+
variant: tab === "all" ? "default" : "ghost",
|
|
5641
6344
|
onClick: () => setTab("all"),
|
|
5642
6345
|
size: "sm",
|
|
6346
|
+
className: utils.cn(tab !== "all" && "bg-muted hover:bg-accent"),
|
|
5643
6347
|
children: t("bridge.allTokens")
|
|
5644
6348
|
}
|
|
5645
6349
|
)
|
|
5646
6350
|
] }),
|
|
5647
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto -
|
|
5648
|
-
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase
|
|
6351
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto px-1", 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: [
|
|
6352
|
+
effectiveTab === "my" && myTokens.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "leading-4 text-base font-semibold text-muted-foreground uppercase py-2", children: t("bridge.noBalancesFound") }),
|
|
5649
6353
|
tokensToRender.map(({ token, willChangeSrc }) => {
|
|
5650
6354
|
const bal = getBalance(token.symbol);
|
|
5651
6355
|
const usd = getTokenUsdValue(token.symbol, token.price?.usd);
|
|
@@ -5696,6 +6400,7 @@ const TokenSelectModal = ({
|
|
|
5696
6400
|
function useBridgeRefresh() {
|
|
5697
6401
|
const qc = reactQuery.useQueryClient();
|
|
5698
6402
|
const { srcAddress, dstAddress } = useAddresses();
|
|
6403
|
+
const { fromChain, toChain } = useChainsStore();
|
|
5699
6404
|
const { setLoading: setQLoading, triggerRefetch } = useBridgeQuoteStore();
|
|
5700
6405
|
const { hasAnyWallet } = useConnectedWalletsStore();
|
|
5701
6406
|
const [spinning, setSpinning] = react.useState(false);
|
|
@@ -5708,14 +6413,30 @@ function useBridgeRefresh() {
|
|
|
5708
6413
|
qc.invalidateQueries({ queryKey: ["chains"] })
|
|
5709
6414
|
];
|
|
5710
6415
|
if (hasAnyWallet()) {
|
|
5711
|
-
if (srcAddress)
|
|
6416
|
+
if (srcAddress) {
|
|
5712
6417
|
queries.push(
|
|
5713
6418
|
qc.invalidateQueries({ queryKey: ["srcTokens", srcAddress] })
|
|
5714
6419
|
);
|
|
5715
|
-
|
|
6420
|
+
if (fromChain?.chainKey) {
|
|
6421
|
+
queries.push(
|
|
6422
|
+
qc.invalidateQueries({
|
|
6423
|
+
queryKey: ["balances", fromChain.chainKey, srcAddress]
|
|
6424
|
+
})
|
|
6425
|
+
);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
if (dstAddress) {
|
|
5716
6429
|
queries.push(
|
|
5717
6430
|
qc.invalidateQueries({ queryKey: ["dstTokens", dstAddress] })
|
|
5718
6431
|
);
|
|
6432
|
+
if (toChain?.chainKey) {
|
|
6433
|
+
queries.push(
|
|
6434
|
+
qc.invalidateQueries({
|
|
6435
|
+
queryKey: ["balances", toChain.chainKey, dstAddress]
|
|
6436
|
+
})
|
|
6437
|
+
);
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
5719
6440
|
}
|
|
5720
6441
|
await Promise.all(queries);
|
|
5721
6442
|
triggerRefetch();
|
|
@@ -5726,6 +6447,8 @@ function useBridgeRefresh() {
|
|
|
5726
6447
|
qc,
|
|
5727
6448
|
srcAddress,
|
|
5728
6449
|
dstAddress,
|
|
6450
|
+
fromChain?.chainKey,
|
|
6451
|
+
toChain?.chainKey,
|
|
5729
6452
|
hasAnyWallet,
|
|
5730
6453
|
setQLoading,
|
|
5731
6454
|
triggerRefetch
|
|
@@ -5745,7 +6468,7 @@ const RefreshButton = () => {
|
|
|
5745
6468
|
onClick: handleRefresh,
|
|
5746
6469
|
disabled: spinning,
|
|
5747
6470
|
variant: "secondary",
|
|
5748
|
-
className: "
|
|
6471
|
+
className: "h-9 w-11",
|
|
5749
6472
|
size: "sm",
|
|
5750
6473
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5751
6474
|
ReloadIcon,
|
|
@@ -5773,7 +6496,7 @@ const SelectTokenButton = ({
|
|
|
5773
6496
|
onClick,
|
|
5774
6497
|
size: "sm",
|
|
5775
6498
|
variant: "secondary",
|
|
5776
|
-
className: "shrink-0 gap-2
|
|
6499
|
+
className: "shrink-0 gap-2 h-9 !pl-2",
|
|
5777
6500
|
type: "button",
|
|
5778
6501
|
"aria-label": label,
|
|
5779
6502
|
children: [
|
|
@@ -5821,7 +6544,7 @@ const Toolbar = () => {
|
|
|
5821
6544
|
{
|
|
5822
6545
|
onClick: onOpenSettings,
|
|
5823
6546
|
size: "sm",
|
|
5824
|
-
className: "
|
|
6547
|
+
className: "h-9 w-11",
|
|
5825
6548
|
variant: "secondary",
|
|
5826
6549
|
children: /* @__PURE__ */ jsxRuntime.jsx(BoltIcon, { stroke: "currentColor" })
|
|
5827
6550
|
}
|
|
@@ -5839,7 +6562,7 @@ const Toolbar = () => {
|
|
|
5839
6562
|
}
|
|
5840
6563
|
}
|
|
5841
6564
|
),
|
|
5842
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6565
|
+
/* @__PURE__ */ jsxRuntime.jsx(SettingsModal, { isOpen: isOpenSettings, onClose: onCloseSettings })
|
|
5843
6566
|
] });
|
|
5844
6567
|
};
|
|
5845
6568
|
const EvaaBridgeWithProviders = (props) => {
|
|
@@ -6101,10 +6824,16 @@ exports.DEFAULT_SLIPPAGE_BPS = DEFAULT_SLIPPAGE_BPS;
|
|
|
6101
6824
|
exports.EvaaBridge = EvaaBridge;
|
|
6102
6825
|
exports.RoutePriority = RoutePriority;
|
|
6103
6826
|
exports.RouteType = RouteType;
|
|
6827
|
+
exports.addEvmNetworkFee = addEvmNetworkFee;
|
|
6828
|
+
exports.addTonNetworkFee = addTonNetworkFee;
|
|
6829
|
+
exports.addTronNetworkFee = addTronNetworkFee;
|
|
6104
6830
|
exports.addrForApi = addrForApi;
|
|
6105
6831
|
exports.buildAssetMatrix = buildAssetMatrix;
|
|
6106
6832
|
exports.calculateMinReceived = calculateMinReceived;
|
|
6107
6833
|
exports.computeFeesUsdFromArray = computeFeesUsdFromArray;
|
|
6834
|
+
exports.estimateEvmNetworkFee = estimateEvmNetworkFee;
|
|
6835
|
+
exports.estimateTonNetworkFee = estimateTonNetworkFee;
|
|
6836
|
+
exports.estimateTronNetworkFee = estimateTronNetworkFee;
|
|
6108
6837
|
exports.findNativeMeta = findNativeMeta;
|
|
6109
6838
|
exports.formatAddress = formatAddress;
|
|
6110
6839
|
exports.formatBalance = formatBalance;
|