@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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [51.0.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Introduce server‑sent events quote streaming and integrates incremental quote updates into the bridge controller polling flow ([#6760](https://github.com/MetaMask/core/pull/6760))
|
|
15
|
+
- Add private `handleQuoteStreaming` method that calls `getQuoteStream` when the `sseEnabled` flag is enabled in LaunchDarkly
|
|
16
|
+
- Reuse existing polling, metrics and validation utilities when processing server-sent quotes
|
|
17
|
+
- Add dependency on `@microsoft/fetch-event-source` at `^2.0.1` ([#6760](https://github.com/MetaMask/core/pull/6760))
|
|
18
|
+
- Note that clients need to patch this library such that it rejects instead of resolving when the quote request is cancelled. This preserves the controller's expected request cancellation behavior
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Extract some logic from bridge-controller and move them to utility files for better readability ([#6760](https://github.com/MetaMask/core/pull/6760))
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Remove cache options from spot-prices and getQuote api calls since they are only required by the extension client ([#6760](https://github.com/MetaMask/core/pull/6760))
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Pass abortSignal to fetchAssetPricesForCurrency in order to cancel exchange rate fetching when quote parameters change ([#6760](https://github.com/MetaMask/core/pull/6760))
|
|
31
|
+
|
|
32
|
+
## [50.0.0]
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **BREAKING:** Bump peer dependency `@metamask/assets-controllers` from `^79.0.0` to `^80.0.0` ([#6818](https://github.com/MetaMask/core/pull/6818))
|
|
37
|
+
|
|
10
38
|
## [49.0.1]
|
|
11
39
|
|
|
12
40
|
### Changed
|
|
@@ -690,7 +718,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
690
718
|
|
|
691
719
|
- Initial release ([#5317](https://github.com/MetaMask/core/pull/5317))
|
|
692
720
|
|
|
693
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@
|
|
721
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@51.0.0...HEAD
|
|
722
|
+
[51.0.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@50.0.0...@metamask/bridge-controller@51.0.0
|
|
723
|
+
[50.0.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@49.0.1...@metamask/bridge-controller@50.0.0
|
|
694
724
|
[49.0.1]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@49.0.0...@metamask/bridge-controller@49.0.1
|
|
695
725
|
[49.0.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@48.0.0...@metamask/bridge-controller@49.0.0
|
|
696
726
|
[48.0.0]: https://github.com/MetaMask/core/compare/@metamask/bridge-controller@47.2.0...@metamask/bridge-controller@48.0.0
|
|
@@ -10,16 +10,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
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");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
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,
|
|
13
|
+
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;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.BridgeController = void 0;
|
|
16
16
|
const contracts_1 = require("@ethersproject/contracts");
|
|
17
17
|
const providers_1 = require("@ethersproject/providers");
|
|
18
18
|
const metamask_eth_abis_1 = require("@metamask/metamask-eth-abis");
|
|
19
19
|
const polling_controller_1 = require("@metamask/polling-controller");
|
|
20
|
-
const utils_1 = require("@metamask/utils");
|
|
21
20
|
const bridge_1 = require("./constants/bridge.cjs");
|
|
22
|
-
const chains_1 = require("./constants/chains.cjs");
|
|
23
21
|
const traces_1 = require("./constants/traces.cjs");
|
|
24
22
|
const selectors_1 = require("./selectors.cjs");
|
|
25
23
|
const types_1 = require("./types.cjs");
|
|
@@ -32,8 +30,8 @@ const fetch_1 = require("./utils/fetch.cjs");
|
|
|
32
30
|
const constants_1 = require("./utils/metrics/constants.cjs");
|
|
33
31
|
const properties_1 = require("./utils/metrics/properties.cjs");
|
|
34
32
|
const quote_1 = require("./utils/quote.cjs");
|
|
33
|
+
const quote_fees_1 = require("./utils/quote-fees.cjs");
|
|
35
34
|
const snaps_1 = require("./utils/snaps.cjs");
|
|
36
|
-
const validators_1 = require("./utils/validators.cjs");
|
|
37
35
|
const metadata = {
|
|
38
36
|
quoteRequest: {
|
|
39
37
|
includeInStateLogs: true,
|
|
@@ -115,31 +113,14 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
115
113
|
await __classPrivateFieldGet(this, _BridgeController_fetchBridgeQuotes, "f").call(this, pollingInput);
|
|
116
114
|
};
|
|
117
115
|
this.updateBridgeQuoteRequestParams = async (paramsToUpdate, context) => {
|
|
118
|
-
this.stopAllPolling();
|
|
119
|
-
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort(constants_1.AbortReason.QuoteRequestUpdated);
|
|
120
116
|
__classPrivateFieldGet(this, _BridgeController_trackInputChangedEvents, "f").call(this, paramsToUpdate);
|
|
117
|
+
this.resetState(constants_1.AbortReason.QuoteRequestUpdated);
|
|
121
118
|
const updatedQuoteRequest = {
|
|
122
119
|
...bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest,
|
|
123
120
|
...paramsToUpdate,
|
|
124
121
|
};
|
|
125
122
|
this.update((state) => {
|
|
126
123
|
state.quoteRequest = updatedQuoteRequest;
|
|
127
|
-
state.quotes = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
128
|
-
state.quotesLastFetched =
|
|
129
|
-
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched;
|
|
130
|
-
state.quotesLoadingStatus =
|
|
131
|
-
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus;
|
|
132
|
-
state.quoteFetchError = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quoteFetchError;
|
|
133
|
-
state.quotesRefreshCount =
|
|
134
|
-
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotesRefreshCount;
|
|
135
|
-
state.quotesInitialLoadTime =
|
|
136
|
-
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotesInitialLoadTime;
|
|
137
|
-
// Reset required minimum balance if the source chain is not Solana
|
|
138
|
-
if (updatedQuoteRequest.srcChainId &&
|
|
139
|
-
!(0, bridge_2.isSolanaChainId)(updatedQuoteRequest.srcChainId)) {
|
|
140
|
-
state.minimumBalanceForRentExemptionInLamports =
|
|
141
|
-
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.minimumBalanceForRentExemptionInLamports;
|
|
142
|
-
}
|
|
143
124
|
});
|
|
144
125
|
await __classPrivateFieldGet(this, _BridgeController_fetchAssetExchangeRates, "f").call(this, updatedQuoteRequest).catch((error) => console.warn('Failed to fetch asset exchange rates', error));
|
|
145
126
|
if ((0, quote_1.isValidQuoteRequest)(updatedQuoteRequest)) {
|
|
@@ -156,10 +137,16 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
156
137
|
insufficientBal = true;
|
|
157
138
|
}
|
|
158
139
|
else {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
140
|
+
try {
|
|
141
|
+
// Otherwise query the src token balance from the RPC provider
|
|
142
|
+
insufficientBal =
|
|
143
|
+
paramsToUpdate.insufficientBal ??
|
|
144
|
+
!(await __classPrivateFieldGet(this, _BridgeController_hasSufficientBalance, "f").call(this, updatedQuoteRequest));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.warn('Failed to fetch balance', error);
|
|
148
|
+
insufficientBal = true;
|
|
149
|
+
}
|
|
163
150
|
}
|
|
164
151
|
const networkClientId = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getSelectedNetworkClientId).call(this);
|
|
165
152
|
// Set refresh rate based on the source chain before starting polling
|
|
@@ -194,17 +181,8 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
194
181
|
? { ...quoteRequest, ...quoteRequestOverrides }
|
|
195
182
|
: quoteRequest, abortSignal, __classPrivateFieldGet(this, _BridgeController_clientId, "f"), __classPrivateFieldGet(this, _BridgeController_fetchFn, "f"), __classPrivateFieldGet(this, _BridgeController_config, "f").customBridgeApiBaseUrl ?? bridge_1.BRIDGE_PROD_API_BASE_URL, featureId, __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"));
|
|
196
183
|
__classPrivateFieldGet(this, _BridgeController_trackResponseValidationFailures, "f").call(this, validationFailures);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
const quotesWithFees = quotesWithL1GasFees ?? quotesWithNonEvmFees ?? baseQuotes;
|
|
200
|
-
// Sort perps quotes by increasing estimated processing time (fastest first)
|
|
201
|
-
if (featureId === validators_1.FeatureId.PERPS) {
|
|
202
|
-
return quotesWithFees.sort((a, b) => {
|
|
203
|
-
return (a.estimatedProcessingTimeInSeconds -
|
|
204
|
-
b.estimatedProcessingTimeInSeconds);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
return quotesWithFees;
|
|
184
|
+
const quotesWithFees = await (0, quote_fees_1.appendFeesToQuotes)(baseQuotes, this.messagingSystem, __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f"), __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getMultichainSelectedAccount).call(this, quoteRequest.walletAddress));
|
|
185
|
+
return (0, quote_1.sortQuotes)(quotesWithFees, featureId);
|
|
208
186
|
};
|
|
209
187
|
_BridgeController_trackResponseValidationFailures.set(this, (validationFailures) => {
|
|
210
188
|
if (validationFailures.length === 0) {
|
|
@@ -255,6 +233,7 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
255
233
|
clientId: __classPrivateFieldGet(this, _BridgeController_clientId, "f"),
|
|
256
234
|
clientVersion: __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"),
|
|
257
235
|
fetchFn: __classPrivateFieldGet(this, _BridgeController_fetchFn, "f"),
|
|
236
|
+
signal: __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal,
|
|
258
237
|
});
|
|
259
238
|
const exchangeRates = (0, assets_1.toExchangeRates)(currency, pricesByAssetId);
|
|
260
239
|
this.update((state) => {
|
|
@@ -265,10 +244,6 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
265
244
|
});
|
|
266
245
|
});
|
|
267
246
|
_BridgeController_hasSufficientBalance.set(this, async (quoteRequest) => {
|
|
268
|
-
// Only check balance for EVM chains
|
|
269
|
-
if ((0, bridge_2.isNonEvmChainId)(quoteRequest.srcChainId)) {
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
247
|
const srcChainIdInHex = (0, caip_formatters_1.formatChainIdToHex)(quoteRequest.srcChainId);
|
|
273
248
|
const provider = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getSelectedNetworkClient).call(this)?.provider;
|
|
274
249
|
const normalizedSrcTokenAddress = (0, caip_formatters_1.formatAddressToCaipReference)(quoteRequest.srcTokenAddress);
|
|
@@ -282,8 +257,8 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
282
257
|
this.stopAllPolling();
|
|
283
258
|
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort(reason);
|
|
284
259
|
};
|
|
285
|
-
this.resetState = () => {
|
|
286
|
-
this.stopPollingForQuotes(
|
|
260
|
+
this.resetState = (reason = constants_1.AbortReason.ResetState) => {
|
|
261
|
+
this.stopPollingForQuotes(reason);
|
|
287
262
|
this.update((state) => {
|
|
288
263
|
// Cannot do direct assignment to state, i.e. state = {... }, need to manually assign each field
|
|
289
264
|
state.quoteRequest = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest;
|
|
@@ -320,10 +295,13 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
320
295
|
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort('New quote request');
|
|
321
296
|
__classPrivateFieldSet(this, _BridgeController_abortController, new AbortController(), "f");
|
|
322
297
|
this.trackUnifiedSwapBridgeEvent(constants_1.UnifiedSwapBridgeEventName.QuotesRequested, context);
|
|
298
|
+
const { sseEnabled, maxRefreshCount } = (0, feature_flags_1.getBridgeFeatureFlags)(this.messagingSystem);
|
|
299
|
+
const shouldStream = Boolean(sseEnabled);
|
|
323
300
|
this.update((state) => {
|
|
324
|
-
state.quotesLoadingStatus = types_1.RequestStatus.LOADING;
|
|
325
301
|
state.quoteRequest = updatedQuoteRequest;
|
|
326
302
|
state.quoteFetchError = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quoteFetchError;
|
|
303
|
+
state.quotesLastFetched = Date.now();
|
|
304
|
+
state.quotesLoadingStatus = types_1.RequestStatus.LOADING;
|
|
327
305
|
});
|
|
328
306
|
try {
|
|
329
307
|
await __classPrivateFieldGet(this, _BridgeController_trace, "f").call(this, {
|
|
@@ -335,22 +313,36 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
335
313
|
destChainId: (0, caip_formatters_1.formatChainIdToCaip)(updatedQuoteRequest.destChainId),
|
|
336
314
|
},
|
|
337
315
|
}, async () => {
|
|
316
|
+
const selectedAccount = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getMultichainSelectedAccount).call(this, updatedQuoteRequest.walletAddress);
|
|
338
317
|
// This call is not awaited to prevent blocking quote fetching if the snap takes too long to respond
|
|
339
318
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
340
|
-
__classPrivateFieldGet(this, _BridgeController_setMinimumBalanceForRentExemptionInLamports, "f").call(this, updatedQuoteRequest.srcChainId);
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
319
|
+
__classPrivateFieldGet(this, _BridgeController_setMinimumBalanceForRentExemptionInLamports, "f").call(this, updatedQuoteRequest.srcChainId, selectedAccount.metadata?.snap?.id);
|
|
320
|
+
// Use SSE if enabled and return early
|
|
321
|
+
if (shouldStream) {
|
|
322
|
+
await __classPrivateFieldGet(this, _BridgeController_handleQuoteStreaming, "f").call(this, updatedQuoteRequest, selectedAccount);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
// Otherwise use regular fetch
|
|
326
|
+
const quotes = await this.fetchQuotes(updatedQuoteRequest, __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal);
|
|
347
327
|
this.update((state) => {
|
|
328
|
+
// Set the initial load time if this is the first fetch
|
|
329
|
+
if (state.quotesRefreshCount ===
|
|
330
|
+
bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotesRefreshCount &&
|
|
331
|
+
__classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")) {
|
|
332
|
+
state.quotesInitialLoadTime =
|
|
333
|
+
Date.now() - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f");
|
|
334
|
+
}
|
|
348
335
|
state.quotes = quotes;
|
|
349
336
|
state.quotesLoadingStatus = types_1.RequestStatus.FETCHED;
|
|
350
337
|
});
|
|
351
338
|
});
|
|
352
339
|
}
|
|
353
340
|
catch (error) {
|
|
341
|
+
// Reset the quotes list if the fetch fails to avoid showing stale quotes
|
|
342
|
+
this.update((state) => {
|
|
343
|
+
state.quotes = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
344
|
+
});
|
|
345
|
+
// Ignore abort errors
|
|
354
346
|
const isAbortError = error.name === 'AbortError';
|
|
355
347
|
if (isAbortError ||
|
|
356
348
|
[
|
|
@@ -361,141 +353,89 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
361
353
|
// Exit the function early to prevent other state updates
|
|
362
354
|
return;
|
|
363
355
|
}
|
|
356
|
+
// Update loading status and error message
|
|
364
357
|
this.update((state) => {
|
|
365
|
-
|
|
366
|
-
|
|
358
|
+
// The error object reference is not guaranteed to exist on mobile so reading
|
|
359
|
+
// the message directly could cause an error.
|
|
360
|
+
let errorMessage;
|
|
361
|
+
try {
|
|
362
|
+
errorMessage =
|
|
363
|
+
error?.message ?? error.toString();
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// Intentionally empty
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
state.quoteFetchError = errorMessage ?? 'Unknown error';
|
|
370
|
+
}
|
|
367
371
|
state.quotesLoadingStatus = types_1.RequestStatus.ERROR;
|
|
368
|
-
state.quotes = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
369
372
|
});
|
|
373
|
+
// Track event and log error
|
|
370
374
|
this.trackUnifiedSwapBridgeEvent(constants_1.UnifiedSwapBridgeEventName.QuotesError, context);
|
|
371
|
-
console.log(
|
|
375
|
+
console.log(`Failed to ${shouldStream ? 'stream' : 'fetch'} bridge quotes`, error);
|
|
372
376
|
}
|
|
373
|
-
|
|
374
|
-
|
|
377
|
+
// Update refresh count after fetching, validation and fee calculation have completed
|
|
378
|
+
this.update((state) => {
|
|
379
|
+
state.quotesRefreshCount += 1;
|
|
380
|
+
});
|
|
375
381
|
// Stop polling if the maximum number of refreshes has been reached
|
|
376
382
|
if (updatedQuoteRequest.insufficientBal ||
|
|
377
383
|
(!updatedQuoteRequest.insufficientBal &&
|
|
378
384
|
this.state.quotesRefreshCount >= maxRefreshCount)) {
|
|
379
385
|
this.stopAllPolling();
|
|
380
386
|
}
|
|
381
|
-
// Update quote fetching stats
|
|
382
|
-
const quotesLastFetched = Date.now();
|
|
383
|
-
this.update((state) => {
|
|
384
|
-
state.quotesInitialLoadTime =
|
|
385
|
-
state.quotesRefreshCount === 0 && __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")
|
|
386
|
-
? quotesLastFetched - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")
|
|
387
|
-
: this.state.quotesInitialLoadTime;
|
|
388
|
-
state.quotesLastFetched = quotesLastFetched;
|
|
389
|
-
state.quotesRefreshCount += 1;
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
_BridgeController_appendL1GasFees.set(this, async (quotes) => {
|
|
393
|
-
// Indicates whether some of the quotes are not for optimism or base
|
|
394
|
-
const hasInvalidQuotes = quotes.some(({ quote }) => {
|
|
395
|
-
const chainId = (0, caip_formatters_1.formatChainIdToCaip)(quote.srcChainId);
|
|
396
|
-
return ![chains_1.CHAIN_IDS.OPTIMISM, chains_1.CHAIN_IDS.BASE]
|
|
397
|
-
.map(caip_formatters_1.formatChainIdToCaip)
|
|
398
|
-
.includes(chainId);
|
|
399
|
-
});
|
|
400
|
-
// Only append L1 gas fees if all quotes are for either optimism or base
|
|
401
|
-
if (hasInvalidQuotes) {
|
|
402
|
-
return undefined;
|
|
403
|
-
}
|
|
404
|
-
const l1GasFeePromises = Promise.allSettled(quotes.map(async (quoteResponse) => {
|
|
405
|
-
const { quote, trade, approval } = quoteResponse;
|
|
406
|
-
const chainId = (0, utils_1.numberToHex)(quote.srcChainId);
|
|
407
|
-
const getTxParams = (txData) => ({
|
|
408
|
-
from: txData.from,
|
|
409
|
-
to: txData.to,
|
|
410
|
-
value: txData.value,
|
|
411
|
-
data: txData.data,
|
|
412
|
-
gasLimit: txData.gasLimit?.toString(),
|
|
413
|
-
});
|
|
414
|
-
const approvalL1GasFees = approval
|
|
415
|
-
? await __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f").call(this, {
|
|
416
|
-
transactionParams: getTxParams(approval),
|
|
417
|
-
chainId,
|
|
418
|
-
})
|
|
419
|
-
: '0x0';
|
|
420
|
-
const tradeL1GasFees = await __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f").call(this, {
|
|
421
|
-
transactionParams: getTxParams(trade),
|
|
422
|
-
chainId,
|
|
423
|
-
});
|
|
424
|
-
if (approvalL1GasFees === undefined || tradeL1GasFees === undefined) {
|
|
425
|
-
return undefined;
|
|
426
|
-
}
|
|
427
|
-
return {
|
|
428
|
-
...quoteResponse,
|
|
429
|
-
l1GasFeesInHexWei: (0, bridge_2.sumHexes)(approvalL1GasFees, tradeL1GasFees),
|
|
430
|
-
};
|
|
431
|
-
}));
|
|
432
|
-
const quotesWithL1GasFees = (await l1GasFeePromises).reduce((acc, result) => {
|
|
433
|
-
if (result.status === 'fulfilled' && result.value) {
|
|
434
|
-
acc.push(result.value);
|
|
435
|
-
}
|
|
436
|
-
else if (result.status === 'rejected') {
|
|
437
|
-
console.error('Error calculating L1 gas fees for quote', result.reason);
|
|
438
|
-
}
|
|
439
|
-
return acc;
|
|
440
|
-
}, []);
|
|
441
|
-
return quotesWithL1GasFees;
|
|
442
387
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
388
|
+
_BridgeController_handleQuoteStreaming.set(this, async (updatedQuoteRequest, selectedAccount) => {
|
|
389
|
+
/**
|
|
390
|
+
* Tracks the number of valid quotes received from the current stream, which is used
|
|
391
|
+
* to determine when to clear the quotes list and set the initial load time
|
|
392
|
+
*/
|
|
393
|
+
let validQuotesCounter = 0;
|
|
394
|
+
await (0, fetch_1.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_1.BRIDGE_PROD_API_BASE_URL, {
|
|
395
|
+
onValidationFailure: __classPrivateFieldGet(this, _BridgeController_trackResponseValidationFailures, "f"),
|
|
396
|
+
onValidQuoteReceived: async (quote) => {
|
|
397
|
+
const quotesWithFees = await (0, quote_fees_1.appendFeesToQuotes)([quote], this.messagingSystem, __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f"), selectedAccount);
|
|
398
|
+
if (quotesWithFees.length > 0) {
|
|
399
|
+
validQuotesCounter += 1;
|
|
400
|
+
}
|
|
449
401
|
this.update((state) => {
|
|
450
|
-
|
|
402
|
+
// Clear previous quotes and quotes load time when first quote in the current
|
|
403
|
+
// polling loop is received
|
|
404
|
+
// This enables clients to continue showing the previous quotes while new
|
|
405
|
+
// quotes are loading
|
|
406
|
+
// Note: If there are no valid quotes until the 2nd fetch, quotesInitialLoadTime will be > refreshRate
|
|
407
|
+
if (validQuotesCounter === 1) {
|
|
408
|
+
state.quotes = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
409
|
+
if (!state.quotesInitialLoadTime && __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")) {
|
|
410
|
+
// Set the initial load time after the first quote is received
|
|
411
|
+
state.quotesInitialLoadTime =
|
|
412
|
+
Date.now() - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
state.quotes = [...state.quotes, ...quotesWithFees];
|
|
451
416
|
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
console.error('Error setting minimum balance for rent exemption', error);
|
|
417
|
+
},
|
|
418
|
+
onClose: () => {
|
|
455
419
|
this.update((state) => {
|
|
456
|
-
|
|
457
|
-
|
|
420
|
+
// If there are no valid quotes in the current stream, clear the quotes list
|
|
421
|
+
// to remove quotes from the previous stream
|
|
422
|
+
if (validQuotesCounter === 0) {
|
|
423
|
+
state.quotes = bridge_1.DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
424
|
+
}
|
|
425
|
+
state.quotesLoadingStatus = types_1.RequestStatus.FETCHED;
|
|
458
426
|
});
|
|
459
|
-
}
|
|
460
|
-
|
|
427
|
+
},
|
|
428
|
+
}, __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"));
|
|
461
429
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
* @param quotes - Array of quote responses to append fees to
|
|
466
|
-
* @param walletAddress - The wallet address for which the quotes were requested
|
|
467
|
-
* @returns Array of quotes with fees appended, or undefined if quotes are for EVM chains
|
|
468
|
-
*/
|
|
469
|
-
_BridgeController_appendNonEvmFees.set(this, async (quotes, walletAddress) => {
|
|
470
|
-
if (quotes.some(({ quote: { srcChainId } }) => !(0, bridge_2.isNonEvmChainId)(srcChainId))) {
|
|
471
|
-
return undefined;
|
|
430
|
+
_BridgeController_setMinimumBalanceForRentExemptionInLamports.set(this, async (srcChainId, snapId) => {
|
|
431
|
+
if (!(0, bridge_2.isSolanaChainId)(srcChainId) || !snapId) {
|
|
432
|
+
return;
|
|
472
433
|
}
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const response = (await this.messagingSystem.call('SnapController:handleRequest', (0, snaps_1.computeFeeRequest)(selectedAccount.metadata.snap?.id, trade, selectedAccount.id, scope)));
|
|
479
|
-
const baseFee = response?.find((fee) => fee.type === 'base');
|
|
480
|
-
// Store fees in native units as returned by the snap (e.g., SOL, BTC)
|
|
481
|
-
const feeInNative = baseFee?.asset?.amount || '0';
|
|
482
|
-
return {
|
|
483
|
-
...quoteResponse,
|
|
484
|
-
nonEvmFeesInNative: feeInNative,
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
return quoteResponse;
|
|
488
|
-
}));
|
|
489
|
-
const quotesWithNonEvmFees = (await nonEvmFeePromises).reduce((acc, result) => {
|
|
490
|
-
if (result.status === 'fulfilled' && result.value) {
|
|
491
|
-
acc.push(result.value);
|
|
492
|
-
}
|
|
493
|
-
else if (result.status === 'rejected') {
|
|
494
|
-
console.error('Error calculating non-EVM fees for quote', result.reason);
|
|
495
|
-
}
|
|
496
|
-
return acc;
|
|
497
|
-
}, []);
|
|
498
|
-
return quotesWithNonEvmFees;
|
|
434
|
+
const minimumBalanceForRentExemptionInLamports = await (0, snaps_1.getMinimumBalanceForRentExemptionInLamports)(snapId, this.messagingSystem);
|
|
435
|
+
this.update((state) => {
|
|
436
|
+
state.minimumBalanceForRentExemptionInLamports =
|
|
437
|
+
minimumBalanceForRentExemptionInLamports;
|
|
438
|
+
});
|
|
499
439
|
});
|
|
500
440
|
_BridgeController_getRequestParams.set(this, () => {
|
|
501
441
|
const srcChainIdCaip = (0, caip_formatters_1.formatChainIdToCaip)(this.state.quoteRequest.srcChainId ||
|
|
@@ -661,7 +601,7 @@ class BridgeController extends (0, polling_controller_1.StaticIntervalPollingCon
|
|
|
661
601
|
}
|
|
662
602
|
}
|
|
663
603
|
exports.BridgeController = BridgeController;
|
|
664
|
-
_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(),
|
|
604
|
+
_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) {
|
|
665
605
|
const addressToUse = walletAddress ?? this.state.quoteRequest.walletAddress;
|
|
666
606
|
if (!addressToUse) {
|
|
667
607
|
throw new Error('Account address is required');
|