@metamask/bridge-controller 49.0.1 → 51.0.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/CHANGELOG.md +31 -1
- package/dist/bridge-controller.cjs +109 -169
- package/dist/bridge-controller.cjs.map +1 -1
- package/dist/bridge-controller.d.cts +5 -4
- package/dist/bridge-controller.d.cts.map +1 -1
- package/dist/bridge-controller.d.mts +5 -4
- package/dist/bridge-controller.d.mts.map +1 -1
- package/dist/bridge-controller.mjs +113 -173
- package/dist/bridge-controller.mjs.map +1 -1
- package/dist/selectors.d.cts +100 -78
- package/dist/selectors.d.cts.map +1 -1
- package/dist/selectors.d.mts +100 -78
- package/dist/selectors.d.mts.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +21 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +21 -10
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/feature-flags.d.cts +1 -0
- package/dist/utils/feature-flags.d.cts.map +1 -1
- package/dist/utils/feature-flags.d.mts +1 -0
- package/dist/utils/feature-flags.d.mts.map +1 -1
- package/dist/utils/fetch.cjs +97 -19
- package/dist/utils/fetch.cjs.map +1 -1
- package/dist/utils/fetch.d.cts +23 -2
- package/dist/utils/fetch.d.cts.map +1 -1
- package/dist/utils/fetch.d.mts +23 -2
- package/dist/utils/fetch.d.mts.map +1 -1
- package/dist/utils/fetch.mjs +95 -18
- package/dist/utils/fetch.mjs.map +1 -1
- package/dist/utils/quote-fees.cjs +120 -0
- package/dist/utils/quote-fees.cjs.map +1 -0
- package/dist/utils/quote-fees.d.cts +18 -0
- package/dist/utils/quote-fees.d.cts.map +1 -0
- package/dist/utils/quote-fees.d.mts +18 -0
- package/dist/utils/quote-fees.d.mts.map +1 -0
- package/dist/utils/quote-fees.mjs +116 -0
- package/dist/utils/quote-fees.mjs.map +1 -0
- package/dist/utils/quote.cjs +12 -1
- package/dist/utils/quote.cjs.map +1 -1
- package/dist/utils/quote.d.cts +2 -0
- package/dist/utils/quote.d.cts.map +1 -1
- package/dist/utils/quote.d.mts +2 -0
- package/dist/utils/quote.d.mts.map +1 -1
- package/dist/utils/quote.mjs +10 -0
- package/dist/utils/quote.mjs.map +1 -1
- package/dist/utils/snaps.cjs +19 -1
- package/dist/utils/snaps.cjs.map +1 -1
- package/dist/utils/snaps.d.cts +9 -0
- package/dist/utils/snaps.d.cts.map +1 -1
- package/dist/utils/snaps.d.mts +9 -0
- package/dist/utils/snaps.d.mts.map +1 -1
- package/dist/utils/snaps.mjs +17 -0
- package/dist/utils/snaps.mjs.map +1 -1
- package/dist/utils/validators.cjs +1 -0
- package/dist/utils/validators.cjs.map +1 -1
- package/dist/utils/validators.d.cts +10 -7
- package/dist/utils/validators.d.cts.map +1 -1
- package/dist/utils/validators.d.mts +10 -7
- package/dist/utils/validators.d.mts.map +1 -1
- package/dist/utils/validators.mjs +1 -0
- package/dist/utils/validators.mjs.map +1 -1
- package/package.json +4 -3
|
@@ -9,28 +9,26 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var _BridgeController_instances, _BridgeController_abortController, _BridgeController_quotesFirstFetched, _BridgeController_clientId, _BridgeController_clientVersion, _BridgeController_getLayer1GasFee, _BridgeController_fetchFn, _BridgeController_trackMetaMetricsFn, _BridgeController_trace, _BridgeController_config, _BridgeController_trackResponseValidationFailures, _BridgeController_getExchangeRateSources, _BridgeController_fetchAssetExchangeRates, _BridgeController_hasSufficientBalance, _BridgeController_fetchBridgeQuotes,
|
|
12
|
+
var _BridgeController_instances, _BridgeController_abortController, _BridgeController_quotesFirstFetched, _BridgeController_clientId, _BridgeController_clientVersion, _BridgeController_getLayer1GasFee, _BridgeController_fetchFn, _BridgeController_trackMetaMetricsFn, _BridgeController_trace, _BridgeController_config, _BridgeController_trackResponseValidationFailures, _BridgeController_getExchangeRateSources, _BridgeController_fetchAssetExchangeRates, _BridgeController_hasSufficientBalance, _BridgeController_fetchBridgeQuotes, _BridgeController_handleQuoteStreaming, _BridgeController_setMinimumBalanceForRentExemptionInLamports, _BridgeController_getMultichainSelectedAccount, _BridgeController_getSelectedNetworkClientId, _BridgeController_getSelectedNetworkClient, _BridgeController_getRequestParams, _BridgeController_getRequestMetadata, _BridgeController_getQuoteFetchData, _BridgeController_getEventProperties, _BridgeController_trackInputChangedEvents;
|
|
13
13
|
import { Contract } from "@ethersproject/contracts";
|
|
14
14
|
import { Web3Provider } from "@ethersproject/providers";
|
|
15
15
|
import { abiERC20 } from "@metamask/metamask-eth-abis";
|
|
16
16
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
17
|
-
import { numberToHex } from "@metamask/utils";
|
|
18
17
|
import { BRIDGE_CONTROLLER_NAME, BRIDGE_PROD_API_BASE_URL, DEFAULT_BRIDGE_CONTROLLER_STATE, METABRIDGE_CHAIN_TO_ADDRESS_MAP, REFRESH_INTERVAL_MS } from "./constants/bridge.mjs";
|
|
19
|
-
import { CHAIN_IDS } from "./constants/chains.mjs";
|
|
20
18
|
import { TraceName } from "./constants/traces.mjs";
|
|
21
19
|
import { selectIsAssetExchangeRateInState } from "./selectors.mjs";
|
|
22
20
|
import { RequestStatus } from "./types.mjs";
|
|
23
21
|
import { getAssetIdsForToken, toExchangeRates } from "./utils/assets.mjs";
|
|
24
22
|
import { hasSufficientBalance } from "./utils/balance.mjs";
|
|
25
|
-
import { getDefaultBridgeControllerState, isCrossChain, isNonEvmChainId, isSolanaChainId
|
|
23
|
+
import { getDefaultBridgeControllerState, isCrossChain, isNonEvmChainId, isSolanaChainId } from "./utils/bridge.mjs";
|
|
26
24
|
import { formatAddressToCaipReference, formatChainIdToCaip, formatChainIdToHex } from "./utils/caip-formatters.mjs";
|
|
27
25
|
import { getBridgeFeatureFlags } from "./utils/feature-flags.mjs";
|
|
28
|
-
import { fetchAssetPrices, fetchBridgeQuotes } from "./utils/fetch.mjs";
|
|
26
|
+
import { fetchAssetPrices, fetchBridgeQuotes, fetchBridgeQuoteStream } from "./utils/fetch.mjs";
|
|
29
27
|
import { AbortReason, MetricsActionType, UnifiedSwapBridgeEventName } from "./utils/metrics/constants.mjs";
|
|
30
28
|
import { formatProviderLabel, getRequestParams, getSwapTypeFromQuote, isCustomSlippage, isHardwareWallet, toInputChangedPropertyKey, toInputChangedPropertyValue } from "./utils/metrics/properties.mjs";
|
|
31
|
-
import { isValidQuoteRequest } from "./utils/quote.mjs";
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
29
|
+
import { isValidQuoteRequest, sortQuotes } from "./utils/quote.mjs";
|
|
30
|
+
import { appendFeesToQuotes } from "./utils/quote-fees.mjs";
|
|
31
|
+
import { getMinimumBalanceForRentExemptionInLamports } from "./utils/snaps.mjs";
|
|
34
32
|
const metadata = {
|
|
35
33
|
quoteRequest: {
|
|
36
34
|
includeInStateLogs: true,
|
|
@@ -112,31 +110,14 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
112
110
|
await __classPrivateFieldGet(this, _BridgeController_fetchBridgeQuotes, "f").call(this, pollingInput);
|
|
113
111
|
};
|
|
114
112
|
this.updateBridgeQuoteRequestParams = async (paramsToUpdate, context) => {
|
|
115
|
-
this.stopAllPolling();
|
|
116
|
-
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort(AbortReason.QuoteRequestUpdated);
|
|
117
113
|
__classPrivateFieldGet(this, _BridgeController_trackInputChangedEvents, "f").call(this, paramsToUpdate);
|
|
114
|
+
this.resetState(AbortReason.QuoteRequestUpdated);
|
|
118
115
|
const updatedQuoteRequest = {
|
|
119
116
|
...DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest,
|
|
120
117
|
...paramsToUpdate,
|
|
121
118
|
};
|
|
122
119
|
this.update((state) => {
|
|
123
120
|
state.quoteRequest = updatedQuoteRequest;
|
|
124
|
-
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
125
|
-
state.quotesLastFetched =
|
|
126
|
-
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched;
|
|
127
|
-
state.quotesLoadingStatus =
|
|
128
|
-
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus;
|
|
129
|
-
state.quoteFetchError = DEFAULT_BRIDGE_CONTROLLER_STATE.quoteFetchError;
|
|
130
|
-
state.quotesRefreshCount =
|
|
131
|
-
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesRefreshCount;
|
|
132
|
-
state.quotesInitialLoadTime =
|
|
133
|
-
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesInitialLoadTime;
|
|
134
|
-
// Reset required minimum balance if the source chain is not Solana
|
|
135
|
-
if (updatedQuoteRequest.srcChainId &&
|
|
136
|
-
!isSolanaChainId(updatedQuoteRequest.srcChainId)) {
|
|
137
|
-
state.minimumBalanceForRentExemptionInLamports =
|
|
138
|
-
DEFAULT_BRIDGE_CONTROLLER_STATE.minimumBalanceForRentExemptionInLamports;
|
|
139
|
-
}
|
|
140
121
|
});
|
|
141
122
|
await __classPrivateFieldGet(this, _BridgeController_fetchAssetExchangeRates, "f").call(this, updatedQuoteRequest).catch((error) => console.warn('Failed to fetch asset exchange rates', error));
|
|
142
123
|
if (isValidQuoteRequest(updatedQuoteRequest)) {
|
|
@@ -153,10 +134,16 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
153
134
|
insufficientBal = true;
|
|
154
135
|
}
|
|
155
136
|
else {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
137
|
+
try {
|
|
138
|
+
// Otherwise query the src token balance from the RPC provider
|
|
139
|
+
insufficientBal =
|
|
140
|
+
paramsToUpdate.insufficientBal ??
|
|
141
|
+
!(await __classPrivateFieldGet(this, _BridgeController_hasSufficientBalance, "f").call(this, updatedQuoteRequest));
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.warn('Failed to fetch balance', error);
|
|
145
|
+
insufficientBal = true;
|
|
146
|
+
}
|
|
160
147
|
}
|
|
161
148
|
const networkClientId = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getSelectedNetworkClientId).call(this);
|
|
162
149
|
// Set refresh rate based on the source chain before starting polling
|
|
@@ -191,17 +178,8 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
191
178
|
? { ...quoteRequest, ...quoteRequestOverrides }
|
|
192
179
|
: quoteRequest, abortSignal, __classPrivateFieldGet(this, _BridgeController_clientId, "f"), __classPrivateFieldGet(this, _BridgeController_fetchFn, "f"), __classPrivateFieldGet(this, _BridgeController_config, "f").customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL, featureId, __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"));
|
|
193
180
|
__classPrivateFieldGet(this, _BridgeController_trackResponseValidationFailures, "f").call(this, validationFailures);
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
const quotesWithFees = quotesWithL1GasFees ?? quotesWithNonEvmFees ?? baseQuotes;
|
|
197
|
-
// Sort perps quotes by increasing estimated processing time (fastest first)
|
|
198
|
-
if (featureId === FeatureId.PERPS) {
|
|
199
|
-
return quotesWithFees.sort((a, b) => {
|
|
200
|
-
return (a.estimatedProcessingTimeInSeconds -
|
|
201
|
-
b.estimatedProcessingTimeInSeconds);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
return quotesWithFees;
|
|
181
|
+
const quotesWithFees = await appendFeesToQuotes(baseQuotes, this.messagingSystem, __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f"), __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getMultichainSelectedAccount).call(this, quoteRequest.walletAddress));
|
|
182
|
+
return sortQuotes(quotesWithFees, featureId);
|
|
205
183
|
};
|
|
206
184
|
_BridgeController_trackResponseValidationFailures.set(this, (validationFailures) => {
|
|
207
185
|
if (validationFailures.length === 0) {
|
|
@@ -252,6 +230,7 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
252
230
|
clientId: __classPrivateFieldGet(this, _BridgeController_clientId, "f"),
|
|
253
231
|
clientVersion: __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"),
|
|
254
232
|
fetchFn: __classPrivateFieldGet(this, _BridgeController_fetchFn, "f"),
|
|
233
|
+
signal: __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal,
|
|
255
234
|
});
|
|
256
235
|
const exchangeRates = toExchangeRates(currency, pricesByAssetId);
|
|
257
236
|
this.update((state) => {
|
|
@@ -262,10 +241,6 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
262
241
|
});
|
|
263
242
|
});
|
|
264
243
|
_BridgeController_hasSufficientBalance.set(this, async (quoteRequest) => {
|
|
265
|
-
// Only check balance for EVM chains
|
|
266
|
-
if (isNonEvmChainId(quoteRequest.srcChainId)) {
|
|
267
|
-
return true;
|
|
268
|
-
}
|
|
269
244
|
const srcChainIdInHex = formatChainIdToHex(quoteRequest.srcChainId);
|
|
270
245
|
const provider = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getSelectedNetworkClient).call(this)?.provider;
|
|
271
246
|
const normalizedSrcTokenAddress = formatAddressToCaipReference(quoteRequest.srcTokenAddress);
|
|
@@ -279,8 +254,8 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
279
254
|
this.stopAllPolling();
|
|
280
255
|
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort(reason);
|
|
281
256
|
};
|
|
282
|
-
this.resetState = () => {
|
|
283
|
-
this.stopPollingForQuotes(
|
|
257
|
+
this.resetState = (reason = AbortReason.ResetState) => {
|
|
258
|
+
this.stopPollingForQuotes(reason);
|
|
284
259
|
this.update((state) => {
|
|
285
260
|
// Cannot do direct assignment to state, i.e. state = {... }, need to manually assign each field
|
|
286
261
|
state.quoteRequest = DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest;
|
|
@@ -317,10 +292,13 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
317
292
|
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort('New quote request');
|
|
318
293
|
__classPrivateFieldSet(this, _BridgeController_abortController, new AbortController(), "f");
|
|
319
294
|
this.trackUnifiedSwapBridgeEvent(UnifiedSwapBridgeEventName.QuotesRequested, context);
|
|
295
|
+
const { sseEnabled, maxRefreshCount } = getBridgeFeatureFlags(this.messagingSystem);
|
|
296
|
+
const shouldStream = Boolean(sseEnabled);
|
|
320
297
|
this.update((state) => {
|
|
321
|
-
state.quotesLoadingStatus = RequestStatus.LOADING;
|
|
322
298
|
state.quoteRequest = updatedQuoteRequest;
|
|
323
299
|
state.quoteFetchError = DEFAULT_BRIDGE_CONTROLLER_STATE.quoteFetchError;
|
|
300
|
+
state.quotesLastFetched = Date.now();
|
|
301
|
+
state.quotesLoadingStatus = RequestStatus.LOADING;
|
|
324
302
|
});
|
|
325
303
|
try {
|
|
326
304
|
await __classPrivateFieldGet(this, _BridgeController_trace, "f").call(this, {
|
|
@@ -332,22 +310,36 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
332
310
|
destChainId: formatChainIdToCaip(updatedQuoteRequest.destChainId),
|
|
333
311
|
},
|
|
334
312
|
}, async () => {
|
|
313
|
+
const selectedAccount = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getMultichainSelectedAccount).call(this, updatedQuoteRequest.walletAddress);
|
|
335
314
|
// This call is not awaited to prevent blocking quote fetching if the snap takes too long to respond
|
|
336
315
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
337
|
-
__classPrivateFieldGet(this, _BridgeController_setMinimumBalanceForRentExemptionInLamports, "f").call(this, updatedQuoteRequest.srcChainId);
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
316
|
+
__classPrivateFieldGet(this, _BridgeController_setMinimumBalanceForRentExemptionInLamports, "f").call(this, updatedQuoteRequest.srcChainId, selectedAccount.metadata?.snap?.id);
|
|
317
|
+
// Use SSE if enabled and return early
|
|
318
|
+
if (shouldStream) {
|
|
319
|
+
await __classPrivateFieldGet(this, _BridgeController_handleQuoteStreaming, "f").call(this, updatedQuoteRequest, selectedAccount);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Otherwise use regular fetch
|
|
323
|
+
const quotes = await this.fetchQuotes(updatedQuoteRequest, __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal);
|
|
344
324
|
this.update((state) => {
|
|
325
|
+
// Set the initial load time if this is the first fetch
|
|
326
|
+
if (state.quotesRefreshCount ===
|
|
327
|
+
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesRefreshCount &&
|
|
328
|
+
__classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")) {
|
|
329
|
+
state.quotesInitialLoadTime =
|
|
330
|
+
Date.now() - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f");
|
|
331
|
+
}
|
|
345
332
|
state.quotes = quotes;
|
|
346
333
|
state.quotesLoadingStatus = RequestStatus.FETCHED;
|
|
347
334
|
});
|
|
348
335
|
});
|
|
349
336
|
}
|
|
350
337
|
catch (error) {
|
|
338
|
+
// Reset the quotes list if the fetch fails to avoid showing stale quotes
|
|
339
|
+
this.update((state) => {
|
|
340
|
+
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
341
|
+
});
|
|
342
|
+
// Ignore abort errors
|
|
351
343
|
const isAbortError = error.name === 'AbortError';
|
|
352
344
|
if (isAbortError ||
|
|
353
345
|
[
|
|
@@ -358,141 +350,89 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
358
350
|
// Exit the function early to prevent other state updates
|
|
359
351
|
return;
|
|
360
352
|
}
|
|
353
|
+
// Update loading status and error message
|
|
361
354
|
this.update((state) => {
|
|
362
|
-
|
|
363
|
-
|
|
355
|
+
// The error object reference is not guaranteed to exist on mobile so reading
|
|
356
|
+
// the message directly could cause an error.
|
|
357
|
+
let errorMessage;
|
|
358
|
+
try {
|
|
359
|
+
errorMessage =
|
|
360
|
+
error?.message ?? error.toString();
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
// Intentionally empty
|
|
364
|
+
}
|
|
365
|
+
finally {
|
|
366
|
+
state.quoteFetchError = errorMessage ?? 'Unknown error';
|
|
367
|
+
}
|
|
364
368
|
state.quotesLoadingStatus = RequestStatus.ERROR;
|
|
365
|
-
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
366
369
|
});
|
|
370
|
+
// Track event and log error
|
|
367
371
|
this.trackUnifiedSwapBridgeEvent(UnifiedSwapBridgeEventName.QuotesError, context);
|
|
368
|
-
console.log(
|
|
372
|
+
console.log(`Failed to ${shouldStream ? 'stream' : 'fetch'} bridge quotes`, error);
|
|
369
373
|
}
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
// Update refresh count after fetching, validation and fee calculation have completed
|
|
375
|
+
this.update((state) => {
|
|
376
|
+
state.quotesRefreshCount += 1;
|
|
377
|
+
});
|
|
372
378
|
// Stop polling if the maximum number of refreshes has been reached
|
|
373
379
|
if (updatedQuoteRequest.insufficientBal ||
|
|
374
380
|
(!updatedQuoteRequest.insufficientBal &&
|
|
375
381
|
this.state.quotesRefreshCount >= maxRefreshCount)) {
|
|
376
382
|
this.stopAllPolling();
|
|
377
383
|
}
|
|
378
|
-
// Update quote fetching stats
|
|
379
|
-
const quotesLastFetched = Date.now();
|
|
380
|
-
this.update((state) => {
|
|
381
|
-
state.quotesInitialLoadTime =
|
|
382
|
-
state.quotesRefreshCount === 0 && __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")
|
|
383
|
-
? quotesLastFetched - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")
|
|
384
|
-
: this.state.quotesInitialLoadTime;
|
|
385
|
-
state.quotesLastFetched = quotesLastFetched;
|
|
386
|
-
state.quotesRefreshCount += 1;
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
_BridgeController_appendL1GasFees.set(this, async (quotes) => {
|
|
390
|
-
// Indicates whether some of the quotes are not for optimism or base
|
|
391
|
-
const hasInvalidQuotes = quotes.some(({ quote }) => {
|
|
392
|
-
const chainId = formatChainIdToCaip(quote.srcChainId);
|
|
393
|
-
return ![CHAIN_IDS.OPTIMISM, CHAIN_IDS.BASE]
|
|
394
|
-
.map(formatChainIdToCaip)
|
|
395
|
-
.includes(chainId);
|
|
396
|
-
});
|
|
397
|
-
// Only append L1 gas fees if all quotes are for either optimism or base
|
|
398
|
-
if (hasInvalidQuotes) {
|
|
399
|
-
return undefined;
|
|
400
|
-
}
|
|
401
|
-
const l1GasFeePromises = Promise.allSettled(quotes.map(async (quoteResponse) => {
|
|
402
|
-
const { quote, trade, approval } = quoteResponse;
|
|
403
|
-
const chainId = numberToHex(quote.srcChainId);
|
|
404
|
-
const getTxParams = (txData) => ({
|
|
405
|
-
from: txData.from,
|
|
406
|
-
to: txData.to,
|
|
407
|
-
value: txData.value,
|
|
408
|
-
data: txData.data,
|
|
409
|
-
gasLimit: txData.gasLimit?.toString(),
|
|
410
|
-
});
|
|
411
|
-
const approvalL1GasFees = approval
|
|
412
|
-
? await __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f").call(this, {
|
|
413
|
-
transactionParams: getTxParams(approval),
|
|
414
|
-
chainId,
|
|
415
|
-
})
|
|
416
|
-
: '0x0';
|
|
417
|
-
const tradeL1GasFees = await __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f").call(this, {
|
|
418
|
-
transactionParams: getTxParams(trade),
|
|
419
|
-
chainId,
|
|
420
|
-
});
|
|
421
|
-
if (approvalL1GasFees === undefined || tradeL1GasFees === undefined) {
|
|
422
|
-
return undefined;
|
|
423
|
-
}
|
|
424
|
-
return {
|
|
425
|
-
...quoteResponse,
|
|
426
|
-
l1GasFeesInHexWei: sumHexes(approvalL1GasFees, tradeL1GasFees),
|
|
427
|
-
};
|
|
428
|
-
}));
|
|
429
|
-
const quotesWithL1GasFees = (await l1GasFeePromises).reduce((acc, result) => {
|
|
430
|
-
if (result.status === 'fulfilled' && result.value) {
|
|
431
|
-
acc.push(result.value);
|
|
432
|
-
}
|
|
433
|
-
else if (result.status === 'rejected') {
|
|
434
|
-
console.error('Error calculating L1 gas fees for quote', result.reason);
|
|
435
|
-
}
|
|
436
|
-
return acc;
|
|
437
|
-
}, []);
|
|
438
|
-
return quotesWithL1GasFees;
|
|
439
384
|
});
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
385
|
+
_BridgeController_handleQuoteStreaming.set(this, async (updatedQuoteRequest, selectedAccount) => {
|
|
386
|
+
/**
|
|
387
|
+
* Tracks the number of valid quotes received from the current stream, which is used
|
|
388
|
+
* to determine when to clear the quotes list and set the initial load time
|
|
389
|
+
*/
|
|
390
|
+
let validQuotesCounter = 0;
|
|
391
|
+
await fetchBridgeQuoteStream(__classPrivateFieldGet(this, _BridgeController_fetchFn, "f"), updatedQuoteRequest, __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal, __classPrivateFieldGet(this, _BridgeController_clientId, "f"), __classPrivateFieldGet(this, _BridgeController_config, "f").customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL, {
|
|
392
|
+
onValidationFailure: __classPrivateFieldGet(this, _BridgeController_trackResponseValidationFailures, "f"),
|
|
393
|
+
onValidQuoteReceived: async (quote) => {
|
|
394
|
+
const quotesWithFees = await appendFeesToQuotes([quote], this.messagingSystem, __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f"), selectedAccount);
|
|
395
|
+
if (quotesWithFees.length > 0) {
|
|
396
|
+
validQuotesCounter += 1;
|
|
397
|
+
}
|
|
446
398
|
this.update((state) => {
|
|
447
|
-
|
|
399
|
+
// Clear previous quotes and quotes load time when first quote in the current
|
|
400
|
+
// polling loop is received
|
|
401
|
+
// This enables clients to continue showing the previous quotes while new
|
|
402
|
+
// quotes are loading
|
|
403
|
+
// Note: If there are no valid quotes until the 2nd fetch, quotesInitialLoadTime will be > refreshRate
|
|
404
|
+
if (validQuotesCounter === 1) {
|
|
405
|
+
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
406
|
+
if (!state.quotesInitialLoadTime && __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")) {
|
|
407
|
+
// Set the initial load time after the first quote is received
|
|
408
|
+
state.quotesInitialLoadTime =
|
|
409
|
+
Date.now() - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
state.quotes = [...state.quotes, ...quotesWithFees];
|
|
448
413
|
});
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
console.error('Error setting minimum balance for rent exemption', error);
|
|
414
|
+
},
|
|
415
|
+
onClose: () => {
|
|
452
416
|
this.update((state) => {
|
|
453
|
-
|
|
454
|
-
|
|
417
|
+
// If there are no valid quotes in the current stream, clear the quotes list
|
|
418
|
+
// to remove quotes from the previous stream
|
|
419
|
+
if (validQuotesCounter === 0) {
|
|
420
|
+
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
421
|
+
}
|
|
422
|
+
state.quotesLoadingStatus = RequestStatus.FETCHED;
|
|
455
423
|
});
|
|
456
|
-
}
|
|
457
|
-
|
|
424
|
+
},
|
|
425
|
+
}, __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"));
|
|
458
426
|
});
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
* @param quotes - Array of quote responses to append fees to
|
|
463
|
-
* @param walletAddress - The wallet address for which the quotes were requested
|
|
464
|
-
* @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains
|
|
465
|
-
*/
|
|
466
|
-
_BridgeController_appendNonEvmFees.set(this, async (quotes, walletAddress) => {
|
|
467
|
-
if (quotes.some(({ quote: { srcChainId } }) => !isNonEvmChainId(srcChainId))) {
|
|
468
|
-
return undefined;
|
|
427
|
+
_BridgeController_setMinimumBalanceForRentExemptionInLamports.set(this, async (srcChainId, snapId) => {
|
|
428
|
+
if (!isSolanaChainId(srcChainId) || !snapId) {
|
|
429
|
+
return;
|
|
469
430
|
}
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const response = (await this.messagingSystem.call('SnapController:handleRequest', computeFeeRequest(selectedAccount.metadata.snap?.id, trade, selectedAccount.id, scope)));
|
|
476
|
-
const baseFee = response?.find((fee) => fee.type === 'base');
|
|
477
|
-
// Store fees in native units as returned by the snap (e.g., SOL, BTC)
|
|
478
|
-
const feeInNative = baseFee?.asset?.amount || '0';
|
|
479
|
-
return {
|
|
480
|
-
...quoteResponse,
|
|
481
|
-
nonEvmFeesInNative: feeInNative,
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
return quoteResponse;
|
|
485
|
-
}));
|
|
486
|
-
const quotesWithNonEvmFees = (await nonEvmFeePromises).reduce((acc, result) => {
|
|
487
|
-
if (result.status === 'fulfilled' && result.value) {
|
|
488
|
-
acc.push(result.value);
|
|
489
|
-
}
|
|
490
|
-
else if (result.status === 'rejected') {
|
|
491
|
-
console.error('Error calculating non-EVM fees for quote', result.reason);
|
|
492
|
-
}
|
|
493
|
-
return acc;
|
|
494
|
-
}, []);
|
|
495
|
-
return quotesWithNonEvmFees;
|
|
431
|
+
const minimumBalanceForRentExemptionInLamports = await getMinimumBalanceForRentExemptionInLamports(snapId, this.messagingSystem);
|
|
432
|
+
this.update((state) => {
|
|
433
|
+
state.minimumBalanceForRentExemptionInLamports =
|
|
434
|
+
minimumBalanceForRentExemptionInLamports;
|
|
435
|
+
});
|
|
496
436
|
});
|
|
497
437
|
_BridgeController_getRequestParams.set(this, () => {
|
|
498
438
|
const srcChainIdCaip = formatChainIdToCaip(this.state.quoteRequest.srcChainId ||
|
|
@@ -657,7 +597,7 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
657
597
|
this.messagingSystem.registerActionHandler(`${BRIDGE_CONTROLLER_NAME}:fetchQuotes`, this.fetchQuotes.bind(this));
|
|
658
598
|
}
|
|
659
599
|
}
|
|
660
|
-
_BridgeController_abortController = new WeakMap(), _BridgeController_quotesFirstFetched = new WeakMap(), _BridgeController_clientId = new WeakMap(), _BridgeController_clientVersion = new WeakMap(), _BridgeController_getLayer1GasFee = new WeakMap(), _BridgeController_fetchFn = new WeakMap(), _BridgeController_trackMetaMetricsFn = new WeakMap(), _BridgeController_trace = new WeakMap(), _BridgeController_config = new WeakMap(), _BridgeController_trackResponseValidationFailures = new WeakMap(), _BridgeController_getExchangeRateSources = new WeakMap(), _BridgeController_fetchAssetExchangeRates = new WeakMap(), _BridgeController_hasSufficientBalance = new WeakMap(), _BridgeController_fetchBridgeQuotes = new WeakMap(),
|
|
600
|
+
_BridgeController_abortController = new WeakMap(), _BridgeController_quotesFirstFetched = new WeakMap(), _BridgeController_clientId = new WeakMap(), _BridgeController_clientVersion = new WeakMap(), _BridgeController_getLayer1GasFee = new WeakMap(), _BridgeController_fetchFn = new WeakMap(), _BridgeController_trackMetaMetricsFn = new WeakMap(), _BridgeController_trace = new WeakMap(), _BridgeController_config = new WeakMap(), _BridgeController_trackResponseValidationFailures = new WeakMap(), _BridgeController_getExchangeRateSources = new WeakMap(), _BridgeController_fetchAssetExchangeRates = new WeakMap(), _BridgeController_hasSufficientBalance = new WeakMap(), _BridgeController_fetchBridgeQuotes = new WeakMap(), _BridgeController_handleQuoteStreaming = new WeakMap(), _BridgeController_setMinimumBalanceForRentExemptionInLamports = new WeakMap(), _BridgeController_getRequestParams = new WeakMap(), _BridgeController_getRequestMetadata = new WeakMap(), _BridgeController_getQuoteFetchData = new WeakMap(), _BridgeController_getEventProperties = new WeakMap(), _BridgeController_trackInputChangedEvents = new WeakMap(), _BridgeController_instances = new WeakSet(), _BridgeController_getMultichainSelectedAccount = function _BridgeController_getMultichainSelectedAccount(walletAddress) {
|
|
661
601
|
const addressToUse = walletAddress ?? this.state.quoteRequest.walletAddress;
|
|
662
602
|
if (!addressToUse) {
|
|
663
603
|
throw new Error('Account address is required');
|