diff --git a/app/components/UI/NetworkModal/index.tsx b/app/components/UI/NetworkModal/index.tsx index d62f026fc21..3c58495c9f5 100644 --- a/app/components/UI/NetworkModal/index.tsx +++ b/app/components/UI/NetworkModal/index.tsx @@ -317,12 +317,10 @@ const NetworkModals = (props: NetworkProps) => { }; const switchNetwork = async () => { - const { NetworkController, CurrencyRateController } = Engine.context; + const { NetworkController } = Engine.context; const url = new URLPARSE(rpcUrl); const existingNetwork = networkConfigurationByChainId[chainId]; - CurrencyRateController.updateExchangeRate([ticker]); - if (!isPrivateConnection(url.hostname)) { url.set('protocol', 'https:'); } diff --git a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx index 49eab63ff75..f9921440bd4 100644 --- a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx +++ b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.test.tsx @@ -303,18 +303,6 @@ describe('NetworkSwitcher View', () => { ], ] `); - expect( - (Engine.context.CurrencyRateController.updateExchangeRate as jest.Mock) - .mock.calls, - ).toMatchInlineSnapshot(` - [ - [ - [ - "POL", - ], - ], - ] - `); }); it('renders correctly with errors', async () => { diff --git a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx index 5178c067e10..9894e7e5d13 100644 --- a/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx +++ b/app/components/UI/Ramp/Views/NetworkSwitcher/NetworkSwitcher.tsx @@ -153,14 +153,13 @@ function NetworkSwitcher() { const switchNetwork = useCallback( (networkConfiguration) => { - const { CurrencyRateController, NetworkController } = Engine.context; + const { NetworkController } = Engine.context; const config = Object.values(networkConfigurations).find( ({ chainId }) => chainId === networkConfiguration.chainId, ); if (config) { const { - nativeCurrency: ticker, rpcEndpoints, defaultRpcEndpointIndex, } = config; @@ -168,7 +167,6 @@ function NetworkSwitcher() { const { networkClientId } = rpcEndpoints?.[defaultRpcEndpointIndex] ?? {}; - CurrencyRateController.updateExchangeRate([ticker]); NetworkController.setActiveNetwork(networkClientId); navigateToGetStarted(); } diff --git a/app/components/Views/NetworkSelector/NetworkSelector.tsx b/app/components/Views/NetworkSelector/NetworkSelector.tsx index ea36deab166..21bf02d1b71 100644 --- a/app/components/Views/NetworkSelector/NetworkSelector.tsx +++ b/app/components/Views/NetworkSelector/NetworkSelector.tsx @@ -37,9 +37,7 @@ import Networks, { } from '../../../util/networks'; import { LINEA_MAINNET, - LINEA_SEPOLIA, MAINNET, - SEPOLIA, } from '../../../constants/network'; import Button from '../../../component-library/components/Buttons/Button/Button'; import { @@ -65,7 +63,6 @@ import createStyles from './NetworkSelector.styles'; import { BUILT_IN_NETWORKS, InfuraNetworkType, - TESTNET_TICKER_SYMBOLS, } from '@metamask/controller-utils'; import InfoModal from '../../../../app/components/UI/Swaps/components/InfoModal'; import hideKeyFromUrl from '../../../util/hideKeyFromUrl'; @@ -241,7 +238,6 @@ const NetworkSelector = () => { const onSetRpcTarget = async (networkConfiguration: NetworkConfiguration) => { const { - CurrencyRateController, NetworkController, SelectedNetworkController, } = Engine.context; @@ -254,7 +250,6 @@ const NetworkSelector = () => { const { name: nickname, chainId, - nativeCurrency: ticker, rpcEndpoints, defaultRpcEndpointIndex, } = networkConfiguration; @@ -268,8 +263,6 @@ const NetworkSelector = () => { networkConfigurationId, ); } else { - CurrencyRateController.updateExchangeRate([ticker]); - const { networkClientId } = rpcEndpoints[defaultRpcEndpointIndex]; await NetworkController.setActiveNetwork(networkClientId); @@ -370,7 +363,6 @@ const NetworkSelector = () => { }); const { NetworkController, - CurrencyRateController, AccountTrackerController, SelectedNetworkController, } = Engine.context; @@ -378,13 +370,6 @@ const NetworkSelector = () => { if (domainIsConnectedDapp && process.env.MULTICHAIN_V1) { SelectedNetworkController.setNetworkClientIdForDomain(origin, type); } else { - let ticker = type; - if (type === LINEA_SEPOLIA) { - ticker = TESTNET_TICKER_SYMBOLS.LINEA_SEPOLIA as InfuraNetworkType; - } - if (type === SEPOLIA) { - ticker = TESTNET_TICKER_SYMBOLS.SEPOLIA as InfuraNetworkType; - } const networkConfiguration = networkConfigurations[BUILT_IN_NETWORKS[type].chainId]; @@ -394,7 +379,6 @@ const NetworkSelector = () => { networkConfiguration.defaultRpcEndpointIndex ].networkClientId ?? type; - CurrencyRateController.updateExchangeRate([ticker]); NetworkController.setActiveNetwork(clientId); closeRpcModal(); AccountTrackerController.refresh(); diff --git a/app/components/Views/Root/index.js b/app/components/Views/Root/index.js index 7ffa81485d3..05a52cb033b 100644 --- a/app/components/Views/Root/index.js +++ b/app/components/Views/Root/index.js @@ -12,6 +12,7 @@ import { useAppTheme, ThemeContext } from '../../../util/theme'; import { ToastContextWrapper } from '../../../component-library/components/Toast'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { isTest } from '../../../util/test/utils'; +import { AssetPollingProvider } from '../../hooks/AssetPolling/AssetPollingProvider'; /** * Top level of the component hierarchy @@ -85,9 +86,11 @@ const ConnectedRoot = () => { - - - + + + + + diff --git a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js index 44873cd9344..2833c06f795 100644 --- a/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js +++ b/app/components/Views/Settings/NetworksSettings/NetworkSettings/index.js @@ -822,14 +822,13 @@ export class NetworkSettings extends PureComponent { shouldNetworkSwitchPopToWallet, navigation, }) => { - const { NetworkController, CurrencyRateController } = Engine.context; + const { NetworkController } = Engine.context; const url = new URL(rpcUrl); if (!isPrivateConnection(url.hostname)) { url.set('protocol', 'https:'); } - CurrencyRateController.updateExchangeRate([ticker]); const existingNetwork = this.props.networkConfigurations[chainId]; const indexRpc = rpcUrls.findIndex(({ url }) => url === rpcUrl); @@ -1530,7 +1529,7 @@ export class NetworkSettings extends PureComponent { }; switchToMainnet = () => { - const { NetworkController, CurrencyRateController } = Engine.context; + const { NetworkController } = Engine.context; const { networkConfigurations } = this.props; const { networkClientId } = @@ -1538,7 +1537,6 @@ export class NetworkSettings extends PureComponent { networkConfigurations.defaultRpcEndpointIndex ] ?? {}; - CurrencyRateController.updateExchangeRate(NetworksTicker.mainnet); NetworkController.setActiveNetwork(networkClientId); setTimeout(async () => { diff --git a/app/components/Views/Settings/NetworksSettings/index.js b/app/components/Views/Settings/NetworksSettings/index.js index b96e2bb7e4e..56a1b062165 100644 --- a/app/components/Views/Settings/NetworksSettings/index.js +++ b/app/components/Views/Settings/NetworksSettings/index.js @@ -186,9 +186,8 @@ class NetworksSettings extends PureComponent { }; switchToMainnet = () => { - const { NetworkController, CurrencyRateController } = Engine.context; + const { NetworkController } = Engine.context; - CurrencyRateController.updateExchangeRate(NetworksTicker.mainnet); NetworkController.setProviderType(MAINNET); setTimeout(async () => { diff --git a/app/components/hooks/AssetPolling/AssetPollingProvider.tsx b/app/components/hooks/AssetPolling/AssetPollingProvider.tsx new file mode 100644 index 00000000000..7c7c3eda5fa --- /dev/null +++ b/app/components/hooks/AssetPolling/AssetPollingProvider.tsx @@ -0,0 +1,11 @@ +import React, { ReactNode } from 'react'; +import useCurrencyRatePolling from './useCurrencyRatePolling'; + +// This provider is a step towards making controller polling fully UI based. +// Eventually, individual UI components will call the use*Polling hooks to +// poll and return particular data. This polls globally in the meantime. +export const AssetPollingProvider = ({ children }: { children: ReactNode }) => { + useCurrencyRatePolling(); + + return <>{children}; +}; diff --git a/app/components/hooks/AssetPolling/useCurrencyRatePolling.test.ts b/app/components/hooks/AssetPolling/useCurrencyRatePolling.test.ts new file mode 100644 index 00000000000..fbf94e9d316 --- /dev/null +++ b/app/components/hooks/AssetPolling/useCurrencyRatePolling.test.ts @@ -0,0 +1,42 @@ +import useCurrencyRatePolling from './useCurrencyRatePolling'; +import { renderHookWithProvider } from '../../../util/test/renderWithProvider'; +import Engine from '../../../core/Engine'; + +jest.mock('../../../core/Engine', () => ({ + context: { + CurrencyRateController: { + startPolling: jest.fn(), + stopPollingByPollingToken: jest.fn(), + }, + }, +})); + +describe('useCurrencyRatePolling', () => { + + it('Should poll by the native currencies in network state', async () => { + + const state = { + engine: { + backgroundState: { + NetworkController: { + networkConfigurationsByChainId: { + '0x1': { + nativeCurrency: 'ETH', + }, + '0x89': { + nativeCurrency: 'POL', + }, + }, + }, + }, + }, + }; + + renderHookWithProvider(() => useCurrencyRatePolling(), {state}); + + expect( + jest.mocked(Engine.context.CurrencyRateController.startPolling) + ).toHaveBeenCalledWith({nativeCurrencies: ['ETH', 'POL']}); + + }); +}); diff --git a/app/components/hooks/AssetPolling/useCurrencyRatePolling.ts b/app/components/hooks/AssetPolling/useCurrencyRatePolling.ts new file mode 100644 index 00000000000..7663c1e5dcb --- /dev/null +++ b/app/components/hooks/AssetPolling/useCurrencyRatePolling.ts @@ -0,0 +1,39 @@ +import { useSelector } from 'react-redux'; +import usePolling from '../usePolling'; +import { selectNetworkConfigurations } from '../../../selectors/networkController'; +import Engine from '../../../core/Engine'; +import { selectConversionRate, selectCurrencyRates } from '../../../selectors/currencyRateController'; + +// Polls native currency prices across networks. +const useCurrencyRatePolling = () => { + + // Selectors to determine polling input + const networkConfigurations = useSelector(selectNetworkConfigurations); + + // Selectors returning state updated by the polling + const conversionRate = useSelector(selectConversionRate); + const currencyRates = useSelector(selectCurrencyRates); + + const nativeCurrencies = [ + ...new Set( + Object.values(networkConfigurations).map((n) => n.nativeCurrency), + ), + ]; + + const { CurrencyRateController } = Engine.context; + + usePolling({ + startPolling: + CurrencyRateController.startPolling.bind(CurrencyRateController), + stopPollingByPollingToken: + CurrencyRateController.stopPollingByPollingToken.bind(CurrencyRateController), + input: [{nativeCurrencies}], + }); + + return { + conversionRate, + currencyRates, + }; +}; + +export default useCurrencyRatePolling; diff --git a/app/core/Engine.ts b/app/core/Engine.ts index f166cc73782..55d6fdbf2e3 100644 --- a/app/core/Engine.ts +++ b/app/core/Engine.ts @@ -708,15 +708,7 @@ export class Engine { }), state: initialState.CurrencyRateController, }); - const currentNetworkConfig = - networkController.getNetworkConfigurationByNetworkClientId( - networkController?.state.selectedNetworkClientId, - ); - currencyRateController.startPolling({ - nativeCurrencies: currentNetworkConfig?.nativeCurrency - ? [currentNetworkConfig?.nativeCurrency] - : [], - }); + const gasFeeController = new GasFeeController({ // @ts-expect-error TODO: Resolve mismatch between base-controller versions. messenger: this.controllerMessenger.getRestricted({ diff --git a/app/core/RPCMethods/lib/ethereum-chain-utils.js b/app/core/RPCMethods/lib/ethereum-chain-utils.js index cb71659205f..dc3575ad718 100644 --- a/app/core/RPCMethods/lib/ethereum-chain-utils.js +++ b/app/core/RPCMethods/lib/ethereum-chain-utils.js @@ -207,7 +207,6 @@ export async function switchToNetwork({ isAddNetworkFlow = false, }) { const { - CurrencyRateController, NetworkController, PermissionController, SelectedNetworkController, @@ -300,7 +299,6 @@ export async function switchToNetwork({ networkConfigurationId || networkConfiguration.networkType, ); } else { - CurrencyRateController.updateExchangeRate(requestData.ticker); NetworkController.setActiveNetwork( networkConfigurationId || networkConfiguration.networkType, ); diff --git a/app/core/RPCMethods/wallet_addEthereumChain.test.js b/app/core/RPCMethods/wallet_addEthereumChain.test.js index ff2bbd5cd61..5ed8451424b 100644 --- a/app/core/RPCMethods/wallet_addEthereumChain.test.js +++ b/app/core/RPCMethods/wallet_addEthereumChain.test.js @@ -429,7 +429,6 @@ describe('RPC Method - wallet_addEthereumChain', () => { }), ); expect(spyOnSetActiveNetwork).toHaveBeenCalledTimes(1); - expect(spyOnUpdateExchangeRate).toHaveBeenCalledTimes(1); }); it('should not add a networkConfiguration that has a chainId that already exists in wallet state, and should switch to the existing network', async () => { @@ -468,7 +467,6 @@ describe('RPC Method - wallet_addEthereumChain', () => { expect(spyOnAddNetwork).not.toHaveBeenCalled(); expect(spyOnSetActiveNetwork).toHaveBeenCalledTimes(1); - expect(spyOnUpdateExchangeRate).toHaveBeenCalledTimes(1); }); describe('MM_CHAIN_PERMISSIONS is enabled', () => { diff --git a/app/selectors/currencyRateController.ts b/app/selectors/currencyRateController.ts index fa58efe5106..9617d02710d 100644 --- a/app/selectors/currencyRateController.ts +++ b/app/selectors/currencyRateController.ts @@ -34,3 +34,10 @@ export const selectCurrentCurrency = createSelector( (currencyRateControllerState: CurrencyRateState) => currencyRateControllerState?.currentCurrency, ); + +export const selectCurrencyRates = createSelector( + selectCurrencyRateControllerState, + ( + currencyRateControllerState: CurrencyRateState, + ) => currencyRateControllerState?.currencyRates, +); diff --git a/app/util/networks/handleNetworkSwitch.test.ts b/app/util/networks/handleNetworkSwitch.test.ts index 9084fb3c89e..776891fd60b 100644 --- a/app/util/networks/handleNetworkSwitch.test.ts +++ b/app/util/networks/handleNetworkSwitch.test.ts @@ -136,9 +136,6 @@ describe('useHandleNetworkSwitch', () => { const nickname = handleNetworkSwitch('1338'); - expect( - mockEngine.context.CurrencyRateController.updateExchangeRate, - ).toBeCalledWith(['TEST']); expect( mockEngine.context.NetworkController.setActiveNetwork, ).toBeCalledWith('networkId1'); @@ -153,10 +150,6 @@ describe('useHandleNetworkSwitch', () => { const networkType = handleNetworkSwitch('11155111'); - // TODO: This is a bug, it should be set to SepoliaETH - expect( - mockEngine.context.CurrencyRateController.updateExchangeRate, - ).toBeCalledWith(['ETH']); expect( mockEngine.context.NetworkController.setProviderType, ).not.toBeCalledWith(); diff --git a/app/util/networks/handleNetworkSwitch.ts b/app/util/networks/handleNetworkSwitch.ts index b05ad5df60c..60fddb9012f 100644 --- a/app/util/networks/handleNetworkSwitch.ts +++ b/app/util/networks/handleNetworkSwitch.ts @@ -1,4 +1,3 @@ -import { CurrencyRateController } from '@metamask/assets-controllers'; import { toHex } from '@metamask/controller-utils'; import { NetworkController } from '@metamask/network-controller'; import Engine from '../../core/Engine'; @@ -22,8 +21,6 @@ const handleNetworkSwitch = (switchToChainId: string): string | undefined => { return; } - const currencyRateController = Engine.context - .CurrencyRateController as CurrencyRateController; const networkController = Engine.context .NetworkController as NetworkController; const chainId = selectChainId(store.getState()); @@ -44,13 +41,11 @@ const handleNetworkSwitch = (switchToChainId: string): string | undefined => { , { name: nickname, - nativeCurrency: ticker, rpcEndpoints, defaultRpcEndpointIndex, }, ] = entry; - currencyRateController.updateExchangeRate([ticker]); const { networkClientId } = rpcEndpoints[defaultRpcEndpointIndex]; networkController.setActiveNetwork(networkClientId);