diff --git a/src/SmartTransactionsController.test.ts b/src/SmartTransactionsController.test.ts index 998fff44..67f19604 100644 --- a/src/SmartTransactionsController.test.ts +++ b/src/SmartTransactionsController.test.ts @@ -1,5 +1,6 @@ -import { convertHexToDecimal } from '@metamask/controller-utils'; +import { NetworkType, convertHexToDecimal } from '@metamask/controller-utils'; import type { NetworkState } from '@metamask/network-controller'; +import { NetworkStatus } from '@metamask/network-controller'; import nock from 'nock'; import * as sinon from 'sinon'; @@ -9,7 +10,7 @@ import { API_BASE_URL, CHAIN_IDS } from './constants'; import SmartTransactionsController, { DEFAULT_INTERVAL, } from './SmartTransactionsController'; -import { flushPromises, advanceTime } from './test-helpers'; +import { advanceTime, flushPromises } from './test-helpers'; import type { SmartTransaction, UnsignedTransaction } from './types'; import { SmartTransactionStatuses } from './types'; import * as utils from './utils'; @@ -308,12 +309,47 @@ const defaultState = { }, }; +const mockProvider = { + sendAsync: jest.fn(), +}; + +const mockProviderConfig = { + chainId: '0x1' as `0x${string}`, + provider: mockProvider, + type: NetworkType.mainnet, + ticker: 'ticker', +}; + +const mockNetworkState = { + providerConfig: mockProviderConfig, + selectedNetworkClientId: 'id', + networkConfigurations: { + id: { + id: 'id', + rpcUrl: 'string', + chainId: '0x1' as `0x${string}`, + ticker: 'string', + }, + }, + networksMetadata: { + id: { + EIPS: { + 1155: true, + }, + status: NetworkStatus.Available, + }, + }, +}; + describe('SmartTransactionsController', () => { let smartTransactionsController: SmartTransactionsController; let networkListener: (networkState: NetworkState) => void; + beforeEach(() => { smartTransactionsController = new SmartTransactionsController({ - onNetworkStateChange: (listener) => { + onNetworkStateChange: ( + listener: (networkState: NetworkState) => void, + ) => { networkListener = listener; }, getNonceLock: jest.fn(() => { @@ -346,6 +382,8 @@ describe('SmartTransactionsController', () => { }); // eslint-disable-next-line jest/prefer-spy-on smartTransactionsController.subscribe = jest.fn(); + + networkListener(mockNetworkState); }); afterEach(async () => { diff --git a/src/SmartTransactionsController.ts b/src/SmartTransactionsController.ts index 6e2360d1..5cfa3248 100644 --- a/src/SmartTransactionsController.ts +++ b/src/SmartTransactionsController.ts @@ -1,18 +1,18 @@ // eslint-disable-next-line import/no-nodejs-modules import { hexlify } from '@ethersproject/bytes'; import type { BaseConfig, BaseState } from '@metamask/base-controller'; -import { safelyExecute, query } from '@metamask/controller-utils'; +import { query, safelyExecute } from '@metamask/controller-utils'; import type { Provider } from '@metamask/eth-query'; import EthQuery from '@metamask/eth-query'; import type { - NetworkState, - NetworkController, NetworkClientId, + NetworkController, + NetworkState, } from '@metamask/network-controller'; import { StaticIntervalPollingControllerV1 } from '@metamask/polling-controller'; import { BigNumber } from 'bignumber.js'; // eslint-disable-next-line import/no-nodejs-modules -import { EventEmitter } from 'events'; +import EventEmitter from 'events'; import cloneDeep from 'lodash/cloneDeep'; import { @@ -21,31 +21,33 @@ import { MetaMetricsEventName, } from './constants'; import type { - SmartTransaction, - SignedTransaction, - SignedCanceledTransaction, - UnsignedTransaction, - SmartTransactionsStatus, Fees, - IndividualTxFees, Hex, + IndividualTxFees, + SignedCanceledTransaction, + SignedTransaction, + SmartTransaction, + SmartTransactionsStatus, + UnsignedTransaction, } from './types'; import { APIType, SmartTransactionStatuses } from './types'; import { - getAPIRequestURL, - isSmartTransactionPending, calculateStatus, - snapshotFromTxMeta, - replayHistory, generateHistoryEntry, + getAPIRequestURL, getStxProcessingTime, handleFetch, - isSmartTransactionCancellable, incrementNonceInHex, + isSmartTransactionCancellable, + isSmartTransactionPending, + replayHistory, + snapshotFromTxMeta, } from './utils'; const SECOND = 1000; export const DEFAULT_INTERVAL = SECOND * 5; +const ETH_QUERY_ERROR_MSG = + '`ethQuery` is not defined on SmartTransactionsController'; export type SmartTransactionsControllerConfig = BaseConfig & { interval: number; @@ -84,7 +86,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo private readonly getNonceLock: any; - private ethQuery: EthQuery; + private ethQuery: EthQuery | undefined; public confirmExternalTransaction: any; @@ -168,7 +170,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo this.initialize(); this.setIntervalLength(this.config.interval); this.getNonceLock = getNonceLock; - this.ethQuery = new EthQuery(provider); + this.ethQuery = undefined; this.confirmExternalTransaction = confirmExternalTransaction; this.trackMetaMetricsEvent = trackMetaMetricsEvent; this.getNetworkClientById = getNetworkClientById; @@ -333,7 +335,7 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo ethQuery = this.ethQuery, }: { chainId: Hex; - ethQuery: EthQuery; + ethQuery: EthQuery | undefined; }, ): void { const { smartTransactionsState } = this.state; @@ -345,6 +347,9 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo const isNewSmartTransaction = this.isNewSmartTransaction( smartTransaction.uuid, ); + if (this.ethQuery === undefined) { + throw new Error(ETH_QUERY_ERROR_MSG); + } this.trackStxStatusChange( smartTransaction, @@ -445,12 +450,16 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo ethQuery = this.ethQuery, }: { chainId: Hex; - ethQuery: EthQuery; + ethQuery: EthQuery | undefined; }, ) { if (smartTransaction.skipConfirm) { return; } + + if (ethQuery === undefined) { + throw new Error(ETH_QUERY_ERROR_MSG); + } const txHash = smartTransaction.statusMetadata?.minedHash; try { const transactionReceipt: { @@ -744,9 +753,15 @@ export default class SmartTransactionsController extends StaticIntervalPollingCo }: { networkClientId?: NetworkClientId; } = {}): EthQuery { - return networkClientId - ? new EthQuery(this.getNetworkClientById(networkClientId).provider) - : this.ethQuery; + if (networkClientId) { + return new EthQuery(this.getNetworkClientById(networkClientId).provider); + } + + if (this.ethQuery === undefined) { + throw new Error(ETH_QUERY_ERROR_MSG); + } + + return this.ethQuery; } // TODO: This should return if the cancellation was on chain or not (for nonce management)