@metamask-previews/bridge-controller 79.2.0-preview-c2ef75d18 → 79.2.0-preview-e81fe8853
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 +0 -4
- package/dist/bridge-controller.cjs +29 -95
- package/dist/bridge-controller.cjs.map +1 -1
- package/dist/bridge-controller.d.cts +2 -2
- package/dist/bridge-controller.d.cts.map +1 -1
- package/dist/bridge-controller.d.mts +2 -2
- package/dist/bridge-controller.d.mts.map +1 -1
- package/dist/bridge-controller.mjs +30 -96
- package/dist/bridge-controller.mjs.map +1 -1
- package/dist/constants/traces.cjs +0 -1
- package/dist/constants/traces.cjs.map +1 -1
- package/dist/constants/traces.d.cts +0 -1
- package/dist/constants/traces.d.cts.map +1 -1
- package/dist/constants/traces.d.mts +0 -1
- package/dist/constants/traces.d.mts.map +1 -1
- package/dist/constants/traces.mjs +0 -1
- package/dist/constants/traces.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -16,7 +16,6 @@ import { Contract } from "@ethersproject/contracts";
|
|
|
16
16
|
import { Web3Provider } from "@ethersproject/providers";
|
|
17
17
|
import { abiERC20 } from "@metamask/metamask-eth-abis";
|
|
18
18
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
19
|
-
import { v4 as uuid } from "uuid";
|
|
20
19
|
import { toQuoteResponseV2 } from "./coercers/quote-response-v1-to-v2.mjs";
|
|
21
20
|
import { BRIDGE_CONTROLLER_NAME, BRIDGE_PROD_API_BASE_URL, DEFAULT_BRIDGE_CONTROLLER_STATE, METABRIDGE_ETHEREUM_ADDRESS, REFRESH_INTERVAL_MS } from "./constants/bridge.mjs";
|
|
22
21
|
import { CHAIN_IDS } from "./constants/chains.mjs";
|
|
@@ -30,7 +29,7 @@ import { formatAddressToCaipReference, formatChainIdToCaip, formatChainIdToHex }
|
|
|
30
29
|
import { getBridgeFeatureFlags, hasMinimumRequiredVersion } from "./utils/feature-flags.mjs";
|
|
31
30
|
import { fetchAssetPrices, fetchBridgeQuotes, fetchBridgeQuoteStream, fetchBatchSellTrades } from "./utils/fetch.mjs";
|
|
32
31
|
import { AbortReason, BatchSellMetricsEventName, MetaMetricsSwapsEventSource, MetricsActionType, UnifiedSwapBridgeEventName } from "./utils/metrics/constants.mjs";
|
|
33
|
-
import { formatProviderLabel, getAccountHardwareType, getRequestParams,
|
|
32
|
+
import { formatProviderLabel, getAccountHardwareType, getRequestParams, getSwapTypeFromQuote, isCustomSlippage, toInputChangedPropertyKey, toInputChangedPropertyValue } from "./utils/metrics/properties.mjs";
|
|
34
33
|
import { appendFeesToQuotes } from "./utils/quote-fees.mjs";
|
|
35
34
|
import { getMinimumBalanceForRentExemptionInLamports } from "./utils/snaps.mjs";
|
|
36
35
|
import { sortQuotes } from "./utils/sort-quotes.mjs";
|
|
@@ -127,18 +126,6 @@ const metadata = {
|
|
|
127
126
|
usedInUi: true,
|
|
128
127
|
},
|
|
129
128
|
};
|
|
130
|
-
const QUOTE_ABORT_REASONS = new Set(Object.values(AbortReason));
|
|
131
|
-
const isExpectedQuoteAbort = (error, signal) => {
|
|
132
|
-
if (signal?.aborted) {
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
if (QUOTE_ABORT_REASONS.has(String(error))) {
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
const errorText = error instanceof Error ? `${error.name} ${error.message}` : String(error);
|
|
139
|
-
return (errorText.includes('AbortError') ||
|
|
140
|
-
errorText.includes('FetchRequestCanceledException'));
|
|
141
|
-
};
|
|
142
129
|
const MESSENGER_EXPOSED_METHODS = [
|
|
143
130
|
'updateBridgeQuoteRequestParams',
|
|
144
131
|
'fetchQuotes',
|
|
@@ -514,9 +501,6 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
514
501
|
__classPrivateFieldGet(this, _BridgeController_abortController, "f")?.abort(AbortReason.NewQuoteRequest);
|
|
515
502
|
__classPrivateFieldGet(this, _BridgeController_batchSellTradesAbortController, "f")?.abort(AbortReason.NewQuoteRequest);
|
|
516
503
|
__classPrivateFieldSet(this, _BridgeController_abortController, new AbortController(), "f");
|
|
517
|
-
const quoteTraceStartTime = Date.now();
|
|
518
|
-
const quoteTraceRequestId = uuid();
|
|
519
|
-
const quoteAbortSignal = __classPrivateFieldGet(this, _BridgeController_abortController, "f").signal;
|
|
520
504
|
__classPrivateFieldGet(this, _BridgeController_fetchAssetExchangeRates, "f").call(this, quoteRequests).catch((error) => console.warn('Failed to fetch asset exchange rates', error));
|
|
521
505
|
this.trackUnifiedSwapBridgeEvent(UnifiedSwapBridgeEventName.QuotesRequested, context);
|
|
522
506
|
const { sse, maxRefreshCount } = getBridgeFeatureFlags(this.messenger);
|
|
@@ -537,81 +521,43 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
537
521
|
}
|
|
538
522
|
});
|
|
539
523
|
const jwt = await __classPrivateFieldGet(this, _BridgeController_getJwt, "f").call(this);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (!isBatchSellRequest) {
|
|
544
|
-
traceName = isCrossChain(firstQuoteRequest.srcChainId, firstQuoteRequest.destChainId)
|
|
524
|
+
try {
|
|
525
|
+
const [firstQuoteRequest] = quoteRequests;
|
|
526
|
+
const unifiedSwapTraceName = isCrossChain(firstQuoteRequest.srcChainId, firstQuoteRequest.destChainId)
|
|
545
527
|
? TraceName.BridgeQuotesFetched
|
|
546
528
|
: TraceName.SwapQuotesFetched;
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
await __classPrivateFieldGet(this, _BridgeController_trace, "f").call(this, request, () => undefined);
|
|
552
|
-
}
|
|
553
|
-
catch {
|
|
554
|
-
// Telemetry failures must not affect quote fetching or state updates.
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
const traceProviderFirstResult = async (providerData) => {
|
|
558
|
-
const provider = formatProviderLabel(providerData);
|
|
559
|
-
if (isBatchSellRequest || tracedProviders.has(provider)) {
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
tracedProviders.add(provider);
|
|
563
|
-
await traceWithoutImpact({
|
|
564
|
-
name: TraceName.QuoteProviderFirstResult,
|
|
565
|
-
startTime: quoteTraceStartTime,
|
|
529
|
+
await __classPrivateFieldGet(this, _BridgeController_trace, "f").call(this, {
|
|
530
|
+
name: isBatchSellRequest
|
|
531
|
+
? TraceName.BatchSellQuotesFetched
|
|
532
|
+
: unifiedSwapTraceName,
|
|
566
533
|
data: {
|
|
567
|
-
provider,
|
|
568
|
-
request_id: quoteTraceRequestId,
|
|
569
|
-
swap_type: getSwapType(firstQuoteRequest.srcChainId, firstQuoteRequest.destChainId),
|
|
570
534
|
srcChainId: formatChainIdToCaip(firstQuoteRequest.srcChainId),
|
|
571
535
|
destChainId: formatChainIdToCaip(firstQuoteRequest.destChainId),
|
|
572
|
-
result: 'success',
|
|
573
536
|
},
|
|
574
|
-
})
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (shouldStream || isBatchSellRequest) {
|
|
583
|
-
const quoteCount = await __classPrivateFieldGet(this, _BridgeController_handleQuoteStreaming, "f").call(this, {
|
|
584
|
-
quoteRequests,
|
|
585
|
-
featureId: context.feature_id,
|
|
586
|
-
jwt,
|
|
587
|
-
selectedAccount,
|
|
588
|
-
signal: quoteAbortSignal,
|
|
589
|
-
traceProviderFirstResult,
|
|
590
|
-
});
|
|
591
|
-
if (quoteAbortSignal.aborted) {
|
|
592
|
-
traceResult = 'cancelled';
|
|
537
|
+
}, async () => {
|
|
538
|
+
const selectedAccount = __classPrivateFieldGet(this, _BridgeController_instances, "m", _BridgeController_getMultichainSelectedAccount).call(this, firstQuoteRequest.walletAddress);
|
|
539
|
+
// This call is not awaited to prevent blocking quote fetching if the snap takes too long to respond
|
|
540
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
541
|
+
__classPrivateFieldGet(this, _BridgeController_setMinimumBalanceForRentExemptionInLamports, "f").call(this, firstQuoteRequest.srcChainId, selectedAccount?.metadata?.snap?.id);
|
|
542
|
+
// Use SSE if enabled and return early
|
|
543
|
+
if (shouldStream || isBatchSellRequest) {
|
|
544
|
+
await __classPrivateFieldGet(this, _BridgeController_handleQuoteStreaming, "f").call(this, quoteRequests, context.feature_id, jwt, selectedAccount);
|
|
593
545
|
return;
|
|
594
546
|
}
|
|
595
|
-
traceResult = quoteCount > 0 ? 'success' : 'no_quotes';
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
547
|
// Otherwise use regular fetch
|
|
599
|
-
const quotes = await this.fetchQuotes(firstQuoteRequest, context.feature_id,
|
|
600
|
-
for (const quote of quotes) {
|
|
601
|
-
await traceProviderFirstResult(quote.quote);
|
|
602
|
-
}
|
|
548
|
+
const quotes = await this.fetchQuotes(firstQuoteRequest, context.feature_id, __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal);
|
|
603
549
|
this.update((state) => {
|
|
604
550
|
// Set the initial load time if this is the first fetch
|
|
605
551
|
if (state.quotesRefreshCount ===
|
|
606
552
|
DEFAULT_BRIDGE_CONTROLLER_STATE.quotesRefreshCount &&
|
|
607
553
|
__classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f")) {
|
|
608
|
-
state.quotesInitialLoadTime =
|
|
554
|
+
state.quotesInitialLoadTime =
|
|
555
|
+
Date.now() - __classPrivateFieldGet(this, _BridgeController_quotesFirstFetched, "f");
|
|
609
556
|
}
|
|
610
557
|
state.quotes = quotes.map(toQuoteResponseV2);
|
|
611
558
|
state.quotesLoadingStatus = RequestStatus.FETCHED;
|
|
612
559
|
});
|
|
613
|
-
|
|
614
|
-
}
|
|
560
|
+
});
|
|
615
561
|
}
|
|
616
562
|
catch (error) {
|
|
617
563
|
// Reset the quotes list if the fetch fails to avoid showing stale quotes
|
|
@@ -619,8 +565,14 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
619
565
|
state.quotes = DEFAULT_BRIDGE_CONTROLLER_STATE.quotes;
|
|
620
566
|
});
|
|
621
567
|
// Ignore abort errors
|
|
622
|
-
if (
|
|
623
|
-
|
|
568
|
+
if (error.toString().includes('AbortError') ||
|
|
569
|
+
error.toString().includes('FetchRequestCanceledException') ||
|
|
570
|
+
[
|
|
571
|
+
AbortReason.ResetState,
|
|
572
|
+
AbortReason.NewQuoteRequest,
|
|
573
|
+
AbortReason.QuoteRequestUpdated,
|
|
574
|
+
AbortReason.TransactionSubmitted,
|
|
575
|
+
].includes(error)) {
|
|
624
576
|
// Exit the function early to prevent other state updates
|
|
625
577
|
return;
|
|
626
578
|
}
|
|
@@ -645,22 +597,6 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
645
597
|
this.trackUnifiedSwapBridgeEvent(UnifiedSwapBridgeEventName.QuotesError, context);
|
|
646
598
|
console.log(`Failed to ${shouldStream ? 'stream' : 'fetch'} bridge quotes`, error);
|
|
647
599
|
}
|
|
648
|
-
finally {
|
|
649
|
-
await traceWithoutImpact({
|
|
650
|
-
name: traceName,
|
|
651
|
-
startTime: quoteTraceStartTime,
|
|
652
|
-
data: {
|
|
653
|
-
srcChainId: formatChainIdToCaip(firstQuoteRequest.srcChainId),
|
|
654
|
-
destChainId: formatChainIdToCaip(firstQuoteRequest.destChainId),
|
|
655
|
-
...(!isBatchSellRequest && {
|
|
656
|
-
request_id: quoteTraceRequestId,
|
|
657
|
-
swap_type: getSwapType(firstQuoteRequest.srcChainId, firstQuoteRequest.destChainId),
|
|
658
|
-
feature_id: context.feature_id,
|
|
659
|
-
result: traceResult,
|
|
660
|
-
}),
|
|
661
|
-
},
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
600
|
// Update refresh count after fetching, validation and fee calculation have completed
|
|
665
601
|
this.update((state) => {
|
|
666
602
|
state.quotesRefreshCount += 1;
|
|
@@ -675,7 +611,7 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
675
611
|
this.stopAllPolling();
|
|
676
612
|
}
|
|
677
613
|
});
|
|
678
|
-
_BridgeController_handleQuoteStreaming.set(this, async (
|
|
614
|
+
_BridgeController_handleQuoteStreaming.set(this, async (quoteRequests, featureId, jwt, selectedAccount) => {
|
|
679
615
|
/**
|
|
680
616
|
* Tracks the number of valid quotes received from the current stream, which is used
|
|
681
617
|
* to determine when to clear the quotes list and set the initial load time
|
|
@@ -686,10 +622,9 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
686
622
|
* before setting quotesLoadingStatus to FETCHED
|
|
687
623
|
*/
|
|
688
624
|
const pendingFeeAppendPromises = new Set();
|
|
689
|
-
await fetchBridgeQuoteStream(__classPrivateFieldGet(this, _BridgeController_fetchFn, "f"), quoteRequests, signal, featureId, __classPrivateFieldGet(this, _BridgeController_clientId, "f"), jwt, __classPrivateFieldGet(this, _BridgeController_config, "f").customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL, {
|
|
625
|
+
await fetchBridgeQuoteStream(__classPrivateFieldGet(this, _BridgeController_fetchFn, "f"), quoteRequests, __classPrivateFieldGet(this, _BridgeController_abortController, "f")?.signal, featureId, __classPrivateFieldGet(this, _BridgeController_clientId, "f"), jwt, __classPrivateFieldGet(this, _BridgeController_config, "f").customBridgeApiBaseUrl ?? BRIDGE_PROD_API_BASE_URL, {
|
|
690
626
|
onQuoteValidationFailure: (validationFailures) => __classPrivateFieldGet(this, _BridgeController_trackQuoteValidationFailures, "f").call(this, validationFailures, featureId),
|
|
691
627
|
onValidQuoteReceived: async (quote) => {
|
|
692
|
-
await traceProviderFirstResult(quote.quote);
|
|
693
628
|
const feeAppendPromise = (async () => {
|
|
694
629
|
const quotesWithFees = await appendFeesToQuotes(quote.chainId, [quote], this.messenger, __classPrivateFieldGet(this, _BridgeController_getLayer1GasFee, "f"), selectedAccount);
|
|
695
630
|
if (quotesWithFees.length > 0) {
|
|
@@ -753,7 +688,6 @@ export class BridgeController extends StaticIntervalPollingController() {
|
|
|
753
688
|
});
|
|
754
689
|
},
|
|
755
690
|
}, __classPrivateFieldGet(this, _BridgeController_clientVersion, "f"));
|
|
756
|
-
return validQuotesCounter;
|
|
757
691
|
});
|
|
758
692
|
_BridgeController_setMinimumBalanceForRentExemptionInLamports.set(this, async (srcChainId, snapId) => {
|
|
759
693
|
if (!isSolanaChainId(srcChainId) || !snapId) {
|