diff --git a/app/scripts/controllers/swaps.js b/app/scripts/controllers/swaps.js index a2225aa59..2d35c97f1 100644 --- a/app/scripts/controllers/swaps.js +++ b/app/scripts/controllers/swaps.js @@ -1,10 +1,11 @@ import { Web3Provider } from '@ethersproject/providers'; import { Contract } from '@ethersproject/contracts'; -import log from 'loglevel'; import BigNumber from 'bignumber.js'; import { ObservableStore } from '@metamask/obs-store'; import { mapValues, cloneDeep } from 'lodash'; import abi from 'human-standard-token-abi'; +import { captureException } from '@sentry/browser'; + import { decGWEIToHexWEI, sumHexes, @@ -18,6 +19,11 @@ import { } from '../../../shared/constants/swaps'; import { GasEstimateTypes } from '../../../shared/constants/gas'; import { CHAIN_IDS, NetworkStatus } from '../../../shared/constants/network'; +import { + MetaMetricsEventCategory, + MetaMetricsEventName, + MetaMetricsEventErrorType, +} from '../../../shared/constants/metametrics'; import { FALLBACK_SMART_TRANSACTIONS_REFRESH_TIME, FALLBACK_SMART_TRANSACTIONS_DEADLINE, @@ -115,6 +121,7 @@ export default class SwapsController { getCurrentChainId, getEIP1559GasFeeEstimates, onNetworkStateChange, + trackMetaMetricsEvent, }, state, ) { @@ -140,6 +147,7 @@ export default class SwapsController { this.getBufferedGasLimit = getBufferedGasLimit; this.getTokenRatesState = getTokenRatesState; + this.trackMetaMetricsEvent = trackMetaMetricsEvent; this.pollCount = 0; this.getProviderConfig = getProviderConfig; @@ -362,6 +370,7 @@ export default class SwapsController { } else if (!isPolledRequest) { const { gasLimit: approvalGas } = await this.timedoutGasReturn( firstQuote.approvalNeeded, + firstQuote.aggregator, ); newQuotes = mapValues(newQuotes, (quote) => ({ @@ -463,6 +472,7 @@ export default class SwapsController { Object.values(quotes).map(async (quote) => { const { gasLimit, simulationFails } = await this.timedoutGasReturn( quote.trade, + quote.aggregator, ); return [gasLimit, simulationFails, quote.aggregator]; }), @@ -492,13 +502,24 @@ export default class SwapsController { return newQuotes; } - timedoutGasReturn(tradeTxParams) { + timedoutGasReturn(tradeTxParams, aggregator = '') { return new Promise((resolve) => { let gasTimedOut = false; const gasTimeout = setTimeout(() => { gasTimedOut = true; - resolve({ gasLimit: null, simulationFails: true }); + this.trackMetaMetricsEvent({ + event: MetaMetricsEventName.QuoteError, + category: MetaMetricsEventCategory.Swaps, + properties: { + error_type: MetaMetricsEventErrorType.GasTimeout, + aggregator, + }, + }); + resolve({ + gasLimit: null, + simulationFails: true, + }); }, SECOND * 5); // Remove gas from params that will be passed to the `estimateGas` call @@ -519,7 +540,11 @@ export default class SwapsController { } }) .catch((e) => { - log.error(e); + captureException(e, { + extra: { + aggregator, + }, + }); if (!gasTimedOut) { clearTimeout(gasTimeout); resolve({ gasLimit: null, simulationFails: true }); @@ -534,7 +559,10 @@ export default class SwapsController { const quoteToUpdate = { ...swapsState.quotes[initialAggId] }; const { gasLimit: newGasEstimate, simulationFails } = - await this.timedoutGasReturn(quoteToUpdate.trade); + await this.timedoutGasReturn( + quoteToUpdate.trade, + quoteToUpdate.aggregator, + ); if (newGasEstimate && !simulationFails) { const gasEstimateWithRefund = calculateGasEstimateWithRefund( diff --git a/app/scripts/controllers/swaps.test.js b/app/scripts/controllers/swaps.test.js index b68a8dfb4..56f5f5614 100644 --- a/app/scripts/controllers/swaps.test.js +++ b/app/scripts/controllers/swaps.test.js @@ -860,7 +860,10 @@ describe('SwapsController', function () { // Mocked quotes approvalNeeded is null, so it will only be called with the gas assert.strictEqual( - timedoutGasReturnStub.calledOnceWithExactly(MOCK_APPROVAL_NEEDED), + timedoutGasReturnStub.calledOnceWithExactly( + MOCK_APPROVAL_NEEDED, + TEST_AGG_ID_APPROVAL, + ), true, ); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 03d1e114e..5a52dae61 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1445,6 +1445,9 @@ export default class MetamaskController extends EventEmitter { this.gasFeeController.fetchGasFeeEstimates.bind( this.gasFeeController, ), + trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind( + this.metaMetricsController, + ), }, initState.SwapsController, ); diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index c9d664e3a..ab43e4a54 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -550,6 +550,7 @@ export enum MetaMetricsEventName { PortfolioLinkClicked = 'Portfolio Link Clicked', ProviderMethodCalled = 'Provider Method Called', PublicAddressCopied = 'Public Address Copied', + QuoteError = 'Quote Error', ServiceWorkerRestarted = 'Service Worker Restarted', SignatureApproved = 'Signature Approved', SignatureFailed = 'Signature Failed', @@ -690,6 +691,7 @@ export enum MetaMetricsEventKeyType { export enum MetaMetricsEventErrorType { InsufficientGas = 'insufficient_gas', + GasTimeout = 'gas_timeout', } export enum MetaMetricsNetworkEventSource {