From 3703e95462db4e3fd57e9c504a480bfc3b5954a9 Mon Sep 17 00:00:00 2001 From: Micaela Estabillo Date: Tue, 15 Oct 2024 15:32:43 -0700 Subject: [PATCH] chore: abort quote requests --- .../controllers/bridge/bridge-controller.ts | 85 ++++++++++--------- ui/pages/bridge/bridge.util.ts | 7 +- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/app/scripts/controllers/bridge/bridge-controller.ts b/app/scripts/controllers/bridge/bridge-controller.ts index 490b279aa7f5..61020c65cb97 100644 --- a/app/scripts/controllers/bridge/bridge-controller.ts +++ b/app/scripts/controllers/bridge/bridge-controller.ts @@ -42,6 +42,8 @@ const metadata: StateMetadata<{ bridgeState: BridgeControllerState }> = { }, }; +const RESET_STATE_ABORT_MESSAGE = 'Reset controller state'; + export default class BridgeController extends StaticIntervalPollingController< typeof BRIDGE_CONTROLLER_NAME, { bridgeState: BridgeControllerState }, @@ -49,6 +51,8 @@ export default class BridgeController extends StaticIntervalPollingController< > { #pollingTokenForQuotes: string | undefined; + #abortController: AbortController | undefined; + constructor({ messenger }: { messenger: BridgeControllerMessenger }) { super({ name: BRIDGE_CONTROLLER_NAME, @@ -102,27 +106,26 @@ export default class BridgeController extends StaticIntervalPollingController< updateBridgeQuoteRequestParams = async ( paramsToUpdate: Partial, ) => { + this.stopAllPolling(); + this.#abortController?.abort('Quote request updated'); + if (this.#pollingTokenForQuotes) { this.stopPollingByPollingToken(this.#pollingTokenForQuotes); } - // TODO abort previous fetchBridgeQuotes request const { bridgeState } = this.state; const updatedQuoteRequest = { ...bridgeState.quoteRequest, ...paramsToUpdate, }; - const { quotes, quotesLastFetched, quotesLoadingStatus } = - DEFAULT_BRIDGE_CONTROLLER_STATE; this.update((_state) => { _state.bridgeState = { - ...bridgeState, - quoteRequest: { - ...updatedQuoteRequest, - }, - quotes, - quotesLastFetched, - quotesLoadingStatus, + ..._state.bridgeState, + quoteRequest: updatedQuoteRequest, + quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, + quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, + quotesLoadingStatus: + DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, }; }); @@ -151,26 +154,16 @@ export default class BridgeController extends StaticIntervalPollingController< insufficientBal, }, ); - } else { - this.stopAllPolling(); - this.update((_state) => { - _state.bridgeState = { - ..._state.bridgeState, - quotes, - quotesLastFetched, - quotesLoadingStatus, - }; - }); } }; resetState = () => { this.stopAllPolling(); + this.#abortController?.abort(RESET_STATE_ABORT_MESSAGE); + this.update((_state) => { _state.bridgeState = { - ..._state.bridgeState, ...DEFAULT_BRIDGE_CONTROLLER_STATE, - quotes: [], bridgeFeatureFlags: _state.bridgeState.bridgeFeatureFlags, }; }); @@ -203,11 +196,11 @@ export default class BridgeController extends StaticIntervalPollingController< await this.#setTokens(chainId, 'destTokens'); }; - switchToAndFromInputs = () => { - const { bridgeState } = this.state; - const { quotes, quotesLastFetched, quotesLoadingStatus, quoteRequest } = - DEFAULT_BRIDGE_CONTROLLER_STATE; + switchToAndFromInputs = async () => { this.stopAllPolling(); + this.#abortController?.abort('Switched to and from inputs'); + + const { bridgeState } = this.state; this.update((_state) => { _state.bridgeState = { ...bridgeState, @@ -215,45 +208,57 @@ export default class BridgeController extends StaticIntervalPollingController< destTopAssets: bridgeState.srcTopAssets, srcTokens: bridgeState.destTokens, destTokens: bridgeState.srcTokens, - quotes, - quotesLastFetched, - quotesLoadingStatus, - quoteRequest: { - ...quoteRequest, - srcChainId: bridgeState.quoteRequest.destChainId, - destChainId: bridgeState.quoteRequest.srcChainId, - srcTokenAddress: bridgeState.quoteRequest.destTokenAddress, - destTokenAddress: bridgeState.quoteRequest.srcTokenAddress, - }, + quotes: DEFAULT_BRIDGE_CONTROLLER_STATE.quotes, + quotesLastFetched: DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLastFetched, + quotesLoadingStatus: + DEFAULT_BRIDGE_CONTROLLER_STATE.quotesLoadingStatus, }; }); + + await this.updateBridgeQuoteRequestParams({ + srcChainId: bridgeState.quoteRequest.destChainId, + destChainId: bridgeState.quoteRequest.srcChainId, + srcTokenAddress: bridgeState.quoteRequest.destTokenAddress, + destTokenAddress: bridgeState.quoteRequest.srcTokenAddress, + srcTokenAmount: '', + }); }; #fetchBridgeQuotes = async (request: QuoteRequest) => { + this.#abortController?.abort('New quote request'); + this.#abortController = new AbortController(); + if (request.srcChainId === request.destChainId) { + return; + } const { bridgeState } = this.state; this.update((_state) => { _state.bridgeState = { ...bridgeState, - quotesLastFetched: Date.now(), quotesLoadingStatus: RequestStatus.LOADING, }; }); try { - // TODO abort controller integration - const quotes = await fetchBridgeQuotes(request); + const quotes = await fetchBridgeQuotes( + request, + this.#abortController.signal, + ); this.update((_state) => { _state.bridgeState = { ...bridgeState, quotes, + quotesLastFetched: Date.now(), quotesLoadingStatus: RequestStatus.FETCHED, }; }); } catch (error) { - console.error('Failed to fetch bridge quotes', error); this.update((_state) => { _state.bridgeState = { ...bridgeState, + quoteRequest: + error === RESET_STATE_ABORT_MESSAGE + ? DEFAULT_BRIDGE_CONTROLLER_STATE.quoteRequest + : _state.bridgeState.quoteRequest, quotesLoadingStatus: RequestStatus.ERROR, }; }); diff --git a/ui/pages/bridge/bridge.util.ts b/ui/pages/bridge/bridge.util.ts index 7a68de35cdf0..5c503512fea3 100644 --- a/ui/pages/bridge/bridge.util.ts +++ b/ui/pages/bridge/bridge.util.ts @@ -180,13 +180,18 @@ export async function fetchBridgeTokens( // Returns a list of bridge tx quotes export async function fetchBridgeQuotes( request: QuoteRequest, + signal: AbortSignal, ): Promise { const url = `${BRIDGE_API_BASE_URL}/getQuote?${Object.entries(request) .map(([k, v]) => `${k}=${v}`) .join('&')}`; const quotes = await fetchWithCache({ url, - fetchOptions: { method: 'GET', headers: CLIENT_ID_HEADER }, + fetchOptions: { + method: 'GET', + headers: CLIENT_ID_HEADER, + signal, + }, cacheOptions: { cacheRefreshTime: 0 }, functionName: 'fetchBridgeQuotes', });