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

Merged
merged 20 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
18 changes: 13 additions & 5 deletions app/components/UI/Tokens/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
selectChainId,
selectNetworkConfigurations,
} from '../../../selectors/networkController';
import { getDecimalChainId } from '../../../util/networks';
import { getDecimalChainId, isPortfolioViewEnabled } from '../../../util/networks';
import { isZero } from '../../../util/lodash';
import createStyles from './styles';
import { TokenList } from './TokenList';
Expand Down Expand Up @@ -183,7 +183,17 @@ const Tokens: React.FC<TokensI> = ({ tokens }) => {
TokenDetectionController.detectTokens(),
AccountTrackerController.refresh(),
CurrencyRateController.updateExchangeRate(nativeCurrencies),
TokenRatesController.updateExchangeRates(),
...(isPortfolioViewEnabled
? Object.values(networkConfigurationsByChainId)
: [networkConfigurationsByChainId[chainId]]
).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 Expand Up @@ -239,15 +249,13 @@ const Tokens: React.FC<TokensI> = ({ tokens }) => {
const onActionSheetPress = (index: number) =>
index === 0 ? removeToken() : null;

const isTokenFilterEnabled = process.env.PORTFOLIO_VIEW === 'true';

return (
<View
style={styles.wrapper}
testID={WalletViewSelectorsIDs.TOKENS_CONTAINER}
>
<View style={styles.actionBarWrapper}>
{isTokenFilterEnabled ? (
{isPortfolioViewEnabled ? (
<View style={styles.controlButtonOuterWrapper}>
<ButtonBase
label={
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}</>;
};
51 changes: 51 additions & 0 deletions app/components/hooks/AssetPolling/useTokenRatesPolling.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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, and stop polling on dismount', async () => {

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

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

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

expect(mockedTokenRatesController.stopPollingByPollingToken).toHaveBeenCalledTimes(0);
unmount();
expect(mockedTokenRatesController.stopPollingByPollingToken).toHaveBeenCalledTimes(1);
});
});
39 changes: 39 additions & 0 deletions app/components/hooks/AssetPolling/useTokenRatesPolling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useSelector } from 'react-redux';
import usePolling from '../usePolling';
import Engine from '../../../core/Engine';
import { selectChainId, selectNetworkConfigurations } from '../../../selectors/networkController';
import { Hex } from '@metamask/utils';
import { selectContractExchangeRates, selectTokenMarketData } from '../../../selectors/tokenRatesController';
import { isPortfolioViewEnabled } from '../../../util/networks';

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

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

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

const chainIdsToPoll = isPortfolioViewEnabled
? (chainIds ?? Object.keys(networkConfigurations))
: [currentChainId];

const { TokenRatesController } = Engine.context;

usePolling({
startPolling:
TokenRatesController.startPolling.bind(TokenRatesController),
stopPollingByPollingToken:
TokenRatesController.stopPollingByPollingToken.bind(TokenRatesController),
input: chainIdsToPoll.map((chainId) => ({chainId: chainId as Hex})),
});

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 @@ -1543,6 +1543,8 @@ export class Engine {
assetsContractController.getBalancesInSingleCall.bind(
assetsContractController,
),
platform: 'mobile',
useAccountsAPI: true,
}),

new NftDetectionController({
Expand Down Expand Up @@ -1909,14 +1911,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 @@ -2159,11 +2159,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,
);
3 changes: 3 additions & 0 deletions app/util/networks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,6 @@ export const isChainPermissionsFeatureEnabled =

export const isPermissionsSettingsV1Enabled =
process.env.MM_PERMISSIONS_SETTINGS_V1_ENABLED === '1';

export const isPortfolioViewEnabled =
process.env.PORTFOLIO_VIEW === 'true';
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/controller-utils": "^11.3.0",
Expand Down
Loading
Loading