Skip to content

Commit 1bef1aa

Browse files
authored
refactor: migrate tokenRates controller to modular controller init (#31254)
…chitecture <!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/31254?quickstart=1) ## **Related issues** refactor the token rates controller to modular init architecture Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
1 parent aa1b09a commit 1bef1aa

File tree

9 files changed

+168
-29
lines changed

9 files changed

+168
-29
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { TokenRatesControllerInit } from './token-rates-controller-init';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
TokenRatesController,
3+
TokenRatesControllerMessenger,
4+
} from '@metamask/assets-controllers';
5+
import { Messenger } from '@metamask/base-controller';
6+
import { buildControllerInitRequestMock } from '../test/utils';
7+
import { ControllerInitRequest } from '../types';
8+
import { getTokenRatesControllerMessenger } from '../messengers/assets';
9+
import { TokenRatesControllerInit } from './token-rates-controller-init';
10+
11+
jest.mock('@metamask/assets-controllers');
12+
13+
function buildInitRequestMock(): jest.Mocked<
14+
ControllerInitRequest<TokenRatesControllerMessenger>
15+
> {
16+
const baseControllerMessenger = new Messenger();
17+
18+
return {
19+
...buildControllerInitRequestMock(),
20+
controllerMessenger: getTokenRatesControllerMessenger(
21+
baseControllerMessenger,
22+
),
23+
initMessenger: undefined,
24+
};
25+
}
26+
27+
describe('TokenRatesControllerInit', () => {
28+
const tokenRatesControllerClassMock = jest.mocked(TokenRatesController);
29+
30+
beforeEach(() => {
31+
jest.resetAllMocks();
32+
});
33+
34+
it('returns controller instance', () => {
35+
const requestMock = buildInitRequestMock();
36+
expect(TokenRatesControllerInit(requestMock).controller).toBeInstanceOf(
37+
TokenRatesController,
38+
);
39+
});
40+
41+
it('initializes with correct messenger and state', () => {
42+
const requestMock = buildInitRequestMock();
43+
TokenRatesControllerInit(requestMock);
44+
45+
expect(tokenRatesControllerClassMock).toHaveBeenCalled();
46+
});
47+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {
2+
CodefiTokenPricesServiceV2,
3+
TokenRatesController,
4+
} from '@metamask/assets-controllers';
5+
import { ControllerInitFunction } from '../types';
6+
import { TokenRatesControllerMessenger } from '../messengers/assets';
7+
8+
/**
9+
* Initialize the Token Rates controller.
10+
*
11+
* @param request - The request object.
12+
* @param request.controllerMessenger - The messenger to use for the controller.
13+
* @param request.persistedState - The persisted state of the extension.
14+
* @returns The initialized controller.
15+
*/
16+
export const TokenRatesControllerInit: ControllerInitFunction<
17+
TokenRatesController,
18+
TokenRatesControllerMessenger
19+
> = ({ controllerMessenger, persistedState }) => {
20+
const controller = new TokenRatesController({
21+
messenger: controllerMessenger,
22+
state: persistedState.TokenRatesController,
23+
tokenPricesService: new CodefiTokenPricesServiceV2(),
24+
disabled: !persistedState.PreferencesController?.useCurrencyRateCheck,
25+
});
26+
27+
return {
28+
controller,
29+
};
30+
};

app/scripts/controller-init/controller-list.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
MultichainAssetsController,
1616
MultichainAssetsRatesController,
1717
MultichainBalancesController,
18+
TokenRatesController,
1819
} from '@metamask/assets-controllers';
1920
import { MultichainNetworkController } from '@metamask/multichain-network-controller';
2021
import { MultichainTransactionsController } from '@metamask/multichain-transactions-controller';
@@ -73,7 +74,8 @@ export type Controller =
7374
name: 'TransactionUpdateController';
7475
state: Record<string, unknown>;
7576
})
76-
| UserStorageController;
77+
| UserStorageController
78+
| TokenRatesController;
7779

7880
/**
7981
* Flat state object for all controllers supporting or required by modular initialization.
@@ -104,4 +106,5 @@ export type ControllerFlatState = AccountsController['state'] &
104106
SnapInterfaceController['state'] &
105107
TransactionController['state'] &
106108
SwapsController['state'] &
107-
UserStorageController['state'];
109+
UserStorageController['state'] &
110+
TokenRatesController['state'];
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { getTokenRatesControllerMessenger } from './token-rates-controller-messenger';
2+
3+
export type { TokenRatesControllerMessenger } from './token-rates-controller-messenger';
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Messenger, RestrictedMessenger } from '@metamask/base-controller';
2+
import { getTokenRatesControllerMessenger } from './token-rates-controller-messenger';
3+
4+
describe('getTokenRatesControllerMessenger', () => {
5+
it('returns a restricted messenger', () => {
6+
const messenger = new Messenger<never, never>();
7+
const tokenRatesControllerMessenger =
8+
getTokenRatesControllerMessenger(messenger);
9+
10+
expect(tokenRatesControllerMessenger).toBeInstanceOf(RestrictedMessenger);
11+
});
12+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Messenger } from '@metamask/base-controller';
2+
import {
3+
NetworkControllerGetStateAction,
4+
NetworkControllerGetNetworkClientByIdAction,
5+
NetworkControllerStateChangeEvent,
6+
} from '@metamask/network-controller';
7+
import {
8+
AccountsControllerGetSelectedAccountAction,
9+
AccountsControllerGetAccountAction,
10+
AccountsControllerSelectedEvmAccountChangeEvent,
11+
} from '@metamask/accounts-controller';
12+
import { PreferencesControllerStateChangeEvent } from '@metamask/preferences-controller';
13+
import {
14+
TokensControllerGetStateAction,
15+
TokensControllerStateChangeEvent,
16+
} from '@metamask/assets-controllers';
17+
18+
type Actions =
19+
| TokensControllerGetStateAction
20+
| NetworkControllerGetNetworkClientByIdAction
21+
| NetworkControllerGetStateAction
22+
| AccountsControllerGetAccountAction
23+
| AccountsControllerGetSelectedAccountAction;
24+
25+
type Events =
26+
| NetworkControllerStateChangeEvent
27+
| AccountsControllerSelectedEvmAccountChangeEvent
28+
| PreferencesControllerStateChangeEvent
29+
| TokensControllerStateChangeEvent;
30+
31+
export type TokenRatesControllerMessenger = ReturnType<
32+
typeof getTokenRatesControllerMessenger
33+
>;
34+
35+
/**
36+
* Get a restricted messenger for the Token Rates controller. This is scoped to the
37+
* actions and events that the Token Rates controller is allowed to handle.
38+
*
39+
* @param messenger - The controller messenger to restrict.
40+
* @returns The restricted controller messenger.
41+
*/
42+
export function getTokenRatesControllerMessenger(
43+
messenger: Messenger<Actions, Events>,
44+
) {
45+
return messenger.getRestricted({
46+
name: 'TokenRatesController',
47+
allowedActions: [
48+
'TokensController:getState',
49+
'NetworkController:getNetworkClientById',
50+
'NetworkController:getState',
51+
'AccountsController:getAccount',
52+
'AccountsController:getSelectedAccount',
53+
],
54+
allowedEvents: [
55+
'NetworkController:stateChange',
56+
'AccountsController:selectedEvmAccountChange',
57+
'PreferencesController:stateChange',
58+
'TokensController:stateChange',
59+
],
60+
});
61+
}

app/scripts/controller-init/messengers/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
getAuthenticationControllerMessenger,
3030
getUserStorageControllerMessenger,
3131
} from './identity';
32+
import { getTokenRatesControllerMessenger } from './assets';
3233
import {
3334
getNotificationServicesControllerMessenger,
3435
getNotificationServicesPushControllerMessenger,
@@ -107,4 +108,8 @@ export const CONTROLLER_MESSENGERS = {
107108
getMessenger: getUserStorageControllerMessenger,
108109
getInitMessenger: noop,
109110
},
111+
TokenRatesController: {
112+
getMessenger: getTokenRatesControllerMessenger,
113+
getInitMessenger: noop,
114+
},
110115
} as const;

app/scripts/metamask-controller.js

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {
77
NftDetectionController,
88
TokenDetectionController,
99
TokenListController,
10-
TokenRatesController,
1110
TokensController,
12-
CodefiTokenPricesServiceV2,
1311
RatesController,
1412
fetchMultiExchangeRate,
1513
TokenBalancesController,
@@ -374,6 +372,7 @@ import {
374372
///: END:ONLY_INCLUDE_IF
375373
MultichainNetworkControllerInit,
376374
} from './controller-init/multichain';
375+
import { TokenRatesControllerInit } from './controller-init/assets';
377376
import { TransactionControllerInit } from './controller-init/confirmations/transaction-controller-init';
378377
import { PPOMControllerInit } from './controller-init/confirmations/ppom-controller-init';
379378
import { initControllers } from './controller-init/utils';
@@ -1036,31 +1035,6 @@ export default class MetamaskController extends EventEmitter {
10361035
fetchMultiExchangeRate,
10371036
});
10381037

1039-
const tokenRatesMessenger = this.controllerMessenger.getRestricted({
1040-
name: 'TokenRatesController',
1041-
allowedActions: [
1042-
'TokensController:getState',
1043-
'NetworkController:getNetworkClientById',
1044-
'NetworkController:getState',
1045-
'AccountsController:getAccount',
1046-
'AccountsController:getSelectedAccount',
1047-
],
1048-
allowedEvents: [
1049-
'NetworkController:stateChange',
1050-
'AccountsController:selectedEvmAccountChange',
1051-
'PreferencesController:stateChange',
1052-
'TokensController:stateChange',
1053-
],
1054-
});
1055-
1056-
// token exchange rate tracker
1057-
this.tokenRatesController = new TokenRatesController({
1058-
state: initState.TokenRatesController,
1059-
messenger: tokenRatesMessenger,
1060-
tokenPricesService: new CodefiTokenPricesServiceV2(),
1061-
disabled: !this.preferencesController.state.useCurrencyRateCheck,
1062-
});
1063-
10641038
this.controllerMessenger.subscribe(
10651039
'PreferencesController:stateChange',
10661040
previousValueComparator((prevState, currState) => {
@@ -1916,6 +1890,7 @@ export default class MetamaskController extends EventEmitter {
19161890
MultichainTransactionsController: MultichainTransactionsControllerInit,
19171891
///: END:ONLY_INCLUDE_IF
19181892
MultichainNetworkController: MultichainNetworkControllerInit,
1893+
TokenRatesController: TokenRatesControllerInit,
19191894
AuthenticationController: AuthenticationControllerInit,
19201895
UserStorageController: UserStorageControllerInit,
19211896
NotificationServicesController: NotificationServicesControllerInit,
@@ -1958,6 +1933,7 @@ export default class MetamaskController extends EventEmitter {
19581933
this.multichainAssetsRatesController =
19591934
controllersByName.MultichainAssetsRatesController;
19601935
///: END:ONLY_INCLUDE_IF
1936+
this.tokenRatesController = controllersByName.TokenRatesController;
19611937
this.multichainNetworkController =
19621938
controllersByName.MultichainNetworkController;
19631939
this.authenticationController = controllersByName.AuthenticationController;
@@ -2208,6 +2184,7 @@ export default class MetamaskController extends EventEmitter {
22082184
MultichainTransactionsController: this.multichainTransactionsController,
22092185
MultichainAssetsRatesController: this.multichainAssetsRatesController,
22102186
///: END:ONLY_INCLUDE_IF
2187+
TokenRatesController: this.tokenRatesController,
22112188
MultichainNetworkController: this.multichainNetworkController,
22122189
NetworkController: this.networkController,
22132190
KeyringController: this.keyringController,

0 commit comments

Comments
 (0)