Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade assets controllers to 42 with multichain token rates #12270

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions app/components/UI/Tokens/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jest.mock('../../../core/Engine', () => ({
updateExchangeRate: jest.fn(() => Promise.resolve()),
},
TokenRatesController: {
updateExchangeRates: jest.fn(() => Promise.resolve()),
updateExchangeRatesByChainId: jest.fn(() => Promise.resolve()),
},
NetworkController: {
getNetworkClientById: () => ({
Expand Down Expand Up @@ -359,7 +359,7 @@ describe('Tokens', () => {
Engine.context.CurrencyRateController.updateExchangeRate,
).toHaveBeenCalled();
expect(
Engine.context.TokenRatesController.updateExchangeRates,
Engine.context.TokenRatesController.updateExchangeRatesByChainId,
).toHaveBeenCalled();
});
});
Expand Down
6 changes: 5 additions & 1 deletion app/components/UI/Tokens/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ const Tokens: React.FC<TokensI> = ({ tokens }) => {
TokenDetectionController.detectTokens(),
AccountTrackerController.refresh(),
CurrencyRateController.updateExchangeRate(nativeCurrencies),
TokenRatesController.updateExchangeRates(),
...Object.values(networkConfigurationsByChainId).map((network) =>
TokenRatesController.updateExchangeRatesByChainId(
{ chainId: network.chainId, nativeCurrency: network.nativeCurrency }
),
),
];
await Promise.all(actions).catch((error) => {
Logger.error(error, 'Error while refreshing tokens');
Expand Down
2 changes: 2 additions & 0 deletions app/components/hooks/AssetPolling/AssetPollingProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { ReactNode } from 'react';
import useCurrencyRatePolling from './useCurrencyRatePolling';
import useTokenRatesPolling from './useTokenRatesPolling';

// 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();
useTokenRatesPolling();

return <>{children}</>;
};
63 changes: 63 additions & 0 deletions app/components/hooks/AssetPolling/useTokenRatesPolling.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import useTokenRatesPolling from './useTokenRatesPolling';
import { renderHookWithProvider } from '../../../util/test/renderWithProvider';
import Engine from '../../../core/Engine';

jest.mock('../../../core/Engine', () => ({
context: {
TokenRatesController: {
startPolling: jest.fn(),
stopPollingByPollingToken: jest.fn(),
},
},
}));

describe('useTokenRatesPolling', () => {

beforeEach(() => {
jest.resetAllMocks();
});

const state = {
engine: {
backgroundState: {
TokenRatesController: {
marketData: {},
},
NetworkController: {
networkConfigurationsByChainId: {
'0x1': {},
'0x89': {},
},
},
},
},
};

it('Should poll by provided chain ids', async () => {

renderHookWithProvider(() => useTokenRatesPolling({chainIds: ['0x1']}), {state});

const mockedTokenRatesController = jest.mocked(Engine.context.TokenRatesController);

expect(mockedTokenRatesController.startPolling).toHaveBeenCalledTimes(1);
expect(
mockedTokenRatesController.startPolling
).toHaveBeenCalledWith({chainId: '0x1'});

});

it('Should poll by all chain ids in network state, if no chain ids are explicitly provided', async () => {

renderHookWithProvider(() => useTokenRatesPolling(), {state});

const mockedTokenRatesController = jest.mocked(Engine.context.TokenRatesController);

expect(mockedTokenRatesController.startPolling).toHaveBeenCalledTimes(2);
expect(
mockedTokenRatesController.startPolling
).toHaveBeenCalledWith({chainId: '0x1'});
expect(
mockedTokenRatesController.startPolling
).toHaveBeenCalledWith({chainId: '0x89'});
});
});
33 changes: 33 additions & 0 deletions app/components/hooks/AssetPolling/useTokenRatesPolling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useSelector } from 'react-redux';
import usePolling from '../usePolling';
import Engine from '../../../core/Engine';
import { selectNetworkConfigurations } from '../../../selectors/networkController';
import { Hex } from '@metamask/utils';
import { selectContractExchangeRates, selectTokenMarketData } from '../../../selectors/tokenRatesController';

const useTokenRatesPolling = ({ chainIds }: { chainIds?: Hex[] } = {}) => {

// Selectors to determine polling input
const networkConfigurations = useSelector(selectNetworkConfigurations);

// Selectors returning state updated by the polling
const contractExchangeRates = useSelector(selectContractExchangeRates);
const tokenMarketData = useSelector(selectTokenMarketData);

const { TokenRatesController } = Engine.context;

usePolling({
startPolling:
TokenRatesController.startPolling.bind(TokenRatesController),
stopPollingByPollingToken:
TokenRatesController.stopPollingByPollingToken.bind(TokenRatesController),
input: (chainIds ?? Object.keys(networkConfigurations)).map((chainId) => ({chainId: chainId as Hex})),
bergeron marked this conversation as resolved.
Show resolved Hide resolved
});

return {
contractExchangeRates,
tokenMarketData
};
};

export default useTokenRatesPolling;
12 changes: 6 additions & 6 deletions app/core/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,8 @@ export class Engine {
assetsContractController.getBalancesInSingleCall.bind(
assetsContractController,
),
platform: 'mobile',
useAccountsAPI: true,
}),

new NftDetectionController({
Expand Down Expand Up @@ -1908,14 +1910,12 @@ export class Engine {
TokenDetectionController,
TokenListController,
TransactionController,
TokenRatesController,
} = this.context;

TokenListController.start();
TokenDetectionController.start();
// leaving the reference of TransactionController here, rather than importing it from utils to avoid circular dependency
TransactionController.startIncomingTransactionPolling();
TokenRatesController.start();
}

configureControllersOnNetworkChange() {
Expand Down Expand Up @@ -2158,11 +2158,11 @@ export class Engine {
// SelectedNetworkController.unsetAllDomains()

//Clear assets info
TokensController.reset();
NftController.reset();
TokensController.resetState();
NftController.resetState();

TokenBalancesController.reset();
TokenRatesController.reset();
TokenBalancesController.resetState();
TokenRatesController.resetState();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
(TransactionController as any).update(() => ({
Expand Down
6 changes: 6 additions & 0 deletions app/selectors/tokenRatesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ export const selectContractExchangeRates = createSelector(
(chainId: Hex, tokenRatesControllerState: TokenRatesControllerState) =>
tokenRatesControllerState.marketData[chainId],
);

export const selectTokenMarketData = createSelector(
selectTokenRatesControllerState,
(tokenRatesControllerState: TokenRatesControllerState) =>
tokenRatesControllerState.marketData,
);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
"@metamask/accounts-controller": "^18.2.1",
"@metamask/address-book-controller": "^6.0.1",
"@metamask/approval-controller": "^7.1.0",
"@metamask/assets-controllers": "^41.0.0",
"@metamask/assets-controllers": "^42.0.0",
"@metamask/base-controller": "^7.0.1",
"@metamask/composable-controller": "^3.0.0",
"@metamask/contract-metadata": "^2.1.0",
Expand Down
Loading
Loading