Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
5c69e3d
refactor: migrate `ApprovalController` to `@metamask/messenger`
mikesposito Aug 26, 2025
65c4c78
update changelog
mikesposito Aug 26, 2025
0549ba0
refactor: migrate `TransactionController` to `@metamask/messenger`
mikesposito Aug 27, 2025
4941db5
refactor: migrate `NetworkController` to `@metamask/messenger`
mikesposito Aug 28, 2025
e5bf8bd
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Aug 28, 2025
373c5c4
migrate `TransactionControllerIntegration.test.ts`
mikesposito Aug 28, 2025
38eb76d
update changelogs
mikesposito Aug 28, 2025
5f3e1f3
fix lint
mikesposito Aug 28, 2025
6210cf2
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Sep 3, 2025
f482a84
refactor: migrate `gas-fee-controller` to `@metamask/messenger`
mikesposito Sep 3, 2025
b176039
migrate `NftController` messenger
mikesposito Sep 3, 2025
01cd576
migrate `TokensController` messenger
mikesposito Sep 3, 2025
b72884f
migrate `AssetsContractController` messenger
mikesposito Sep 3, 2025
31c7ee0
update changelogs
mikesposito Sep 3, 2025
5bd5f52
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Sep 3, 2025
1051a2e
fix `NetworkController` tests
mikesposito Sep 3, 2025
eeade8f
update eslint thresholds
mikesposito Sep 3, 2025
edff4d4
remove `StaticIntervalPollingControllerNext`
mikesposito Sep 3, 2025
3ec10e1
migrate `bridge-controller` messenger
mikesposito Sep 3, 2025
e795b14
migrate `BridgeStatusController` messenger
mikesposito Sep 3, 2025
19c7d6a
migrate `TokenDetectionController` messenger
mikesposito Sep 3, 2025
6a818b8
migrate `DeFiPositionsController` messenger
mikesposito Sep 3, 2025
ae15545
migrate `MultichainAssetsRatesController` messenger
mikesposito Sep 3, 2025
c70832c
migrate `AccountTrackerController` messenger
mikesposito Sep 3, 2025
0cf862d
migrate `CurrencyRateController` messenger
mikesposito Sep 3, 2025
d4b7bac
migrate `TokenBalancesController` messenger
mikesposito Sep 3, 2025
20420e5
migrate `TokenRatesController` messenger
mikesposito Sep 3, 2025
1afff1a
migrate `TokenListController` messenger
mikesposito Sep 3, 2025
e7c5bc9
fix `TokenDetectionController` test
mikesposito Sep 3, 2025
f4b79ec
update lockfile
mikesposito Sep 3, 2025
b7ebf4e
update changelogs
mikesposito Sep 4, 2025
ef59e6f
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Sep 23, 2025
0267ab2
rename `anonymous` to `inclueInDebugSnapshot`
mikesposito Sep 23, 2025
d899417
update tsconfig and README
mikesposito Sep 23, 2025
b2a2585
refactor: use `MockAnyNetwork`
mikesposito Sep 23, 2025
f786d7b
update messenger to 0.3.0
mikesposito Sep 23, 2025
c07c63d
fix `NftController` lint
mikesposito Sep 23, 2025
cf380bd
fix changelogs
mikesposito Sep 23, 2025
d8541b3
add entry to gas-fee-controller changelog
mikesposito Sep 23, 2025
69a0891
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Sep 23, 2025
9072d6f
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Sep 23, 2025
86f184c
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Sep 23, 2025
98b89d4
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Sep 23, 2025
fa9084b
migrate leftover `messagingSystem` usage
mikesposito Sep 23, 2025
d565ab5
fix changelogs
mikesposito Sep 23, 2025
126e88c
apply suggestion from @mcmire
mikesposito Sep 23, 2025
d927cc4
migrate `BlockTrackerPollingController`
mikesposito Sep 24, 2025
601100b
update tsconfig and README
mikesposito Sep 24, 2025
6d0f21d
fix test types
mikesposito Sep 24, 2025
bffbca0
rename `anonymous` to `includeInDebugSnapshot`
mikesposito Sep 29, 2025
52899ba
fix `transaction.test.ts`
mikesposito Sep 29, 2025
b896289
rename leftover `messagingSystem`
mikesposito Sep 29, 2025
ab37030
apply @mcmire suggestions
mikesposito Oct 13, 2025
d948037
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 13, 2025
a3ebc02
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 13, 2025
ca266fd
fix: migrate leftover `messagingSystem` to `messenger`
mikesposito Oct 13, 2025
e9a7bbd
add `BridgeControllerStateChangeEvent` to `index.ts`
mikesposito Oct 13, 2025
d6a1433
fix `bridge-status-controller/CHANGELOG`
mikesposito Oct 13, 2025
1bc9e1f
remove polling-controller changelog entry
mikesposito Oct 13, 2025
836d1c7
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 15, 2025
73d8b09
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 15, 2025
79acaff
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 20, 2025
c266b53
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 20, 2025
5cb7667
migrate `SubscriptionController` messenger
mikesposito Oct 20, 2025
c5534c1
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 20, 2025
747d5ee
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 20, 2025
b1f0c53
migrate new `messagingSystem` call
mikesposito Oct 20, 2025
37e2352
migrate new `messagingSystem` call
mikesposito Oct 20, 2025
bd89947
fix `TokenBalancesController` tests
mikesposito Oct 20, 2025
5cfd256
lint: import `MockAnyNamespace` as type
mikesposito Oct 21, 2025
26d91dc
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 21, 2025
79578d7
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 21, 2025
377b9a4
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 21, 2025
3983931
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 23, 2025
cb415e8
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 23, 2025
0e9ca2d
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 24, 2025
466e167
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 24, 2025
a5be219
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 24, 2025
4881477
rename `messagingSystem` to `messenger`
mikesposito Oct 24, 2025
243da17
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 24, 2025
4e2a9ea
Merge branch 'main' into mikesposito/messenger/approval-controller
mikesposito Oct 24, 2025
1eedb0b
Merge branch 'mikesposito/messenger/approval-controller' into mikespo…
mikesposito Oct 24, 2025
cf3638e
Update messagingSystem references to messenger
Gudahtt Oct 24, 2025
998ffb9
Update changelogs following branch update
Gudahtt Oct 24, 2025
221243e
Merge remote-tracking branch 'origin/mikesposito/messenger/approval-c…
Gudahtt Oct 24, 2025
ac99639
Fix changelog mistakes from recent merges
Gudahtt Oct 24, 2025
9660de8
Update ESLint warning threshold
Gudahtt Oct 24, 2025
53b2087
Merge branch 'main' into mikesposito/messenger/polling-controllers
mikesposito Oct 27, 2025
84c7f4f
remove `StaticIntervalPollingControllerNext` usage
mikesposito Oct 27, 2025
b9a05a4
remove variable export
mikesposito Oct 27, 2025
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ linkStyle default opacity:0.5
bridge_controller --> base_controller;
bridge_controller --> controller_utils;
bridge_controller --> gas_fee_controller;
bridge_controller --> messenger;
bridge_controller --> multichain_network_controller;
bridge_controller --> polling_controller;
bridge_controller --> accounts_controller;
Expand Down Expand Up @@ -334,6 +335,7 @@ linkStyle default opacity:0.5
signature_controller --> network_controller;
subscription_controller --> base_controller;
subscription_controller --> controller_utils;
subscription_controller --> messenger;
subscription_controller --> polling_controller;
subscription_controller --> profile_sync_controller;
token_search_discovery_controller --> base_controller;
Expand Down
2 changes: 1 addition & 1 deletion eslint-warning-thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
},
"packages/assets-controllers/src/TokenListController.test.ts": {
"import-x/namespace": 7,
"import-x/order": 3,
"import-x/order": 2,
"jest/no-conditional-in-test": 2
},
"packages/assets-controllers/src/TokenRatesController.test.ts": {
Expand Down
14 changes: 13 additions & 1 deletion packages/assets-controllers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add export for `CurrencyRateMessenger` ([#6444](https://github.com/MetaMask/core/pull/6444))

### Changed

- **BREAKING:** Migrate the following controllers to the new `Messenger` from `@metamask/messenger` ([#6444](https://github.com/MetaMask/core/pull/6444))
- `AccountTrackerController`
- `CurrencyRateController`
- `DeFiPositionController`
- `MultichainAssetsRatesController`
- `TokenBalancesController`
- `TokenDetectionController`
- `TokenListController`
- `TokenRatesController`
- **BREAKING:** Migrate `AssetsContractController`, `NftController`, and `TokensController` to new `Messenger` from `@metamask/messenger` ([#6386](https://github.com/MetaMask/core/pull/6386))
- Previously, the controllers accepted a `RestrictedMessenger` instance from `@metamask/base-controller`.
- Bump `@metamask/polling-controller` from `^14.0.1` to `^14.0.2` ([#6940](https://github.com/MetaMask/core/pull/6940))
Expand Down Expand Up @@ -40,7 +53,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- **BREAKING:** Add new event listeners to refresh balances on `TransactionControllerUnapprovedTransactionAddedEvent` and `TransactionControllerTransactionConfirmedEvent` ([#6903](https://github.com/MetaMask/core/pull/6903))

- Add multicall addresses in `MULTICALL_CONTRACT_BY_CHAINID` ([#6896](https://github.com/MetaMask/core/pull/6896))
- Add multicall address for Chains: `Injective`, `Hemi`, `Plasma`, `Nonmia`, `XRPL`, `Soneium`, `Genesys`, `EDU`, `Abstract`, `Berachain`, `MegaETH Testnet`, `Apechain`, `Matchain`, `Monad Testnet`, `Monad`, `Katana`, `Lens`, `Plume`, `XDC`

Expand Down
62 changes: 39 additions & 23 deletions packages/assets-controllers/src/AccountTrackerController.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller';
import { deriveStateFromMetadata } from '@metamask/base-controller/next';
import { query, toChecksumHexAddress } from '@metamask/controller-utils';
import type { InternalAccount } from '@metamask/keyring-internal-api';
import {
MOCK_ANY_NAMESPACE,
Messenger,
type MessengerActions,
type MessengerEvents,
type MockAnyNamespace,
} from '@metamask/messenger';
import {
type NetworkClientId,
type NetworkClientConfiguration,
Expand All @@ -14,26 +21,30 @@ import {
import BN from 'bn.js';
import { useFakeTimers, type SinonFakeTimers } from 'sinon';

import type {
AccountTrackerControllerMessenger,
AllowedActions,
AllowedEvents,
} from './AccountTrackerController';
import type { AccountTrackerControllerMessenger } from './AccountTrackerController';
import { AccountTrackerController } from './AccountTrackerController';
import { AccountsApiBalanceFetcher } from './multi-chain-accounts-service/api-balance-fetcher';
import { getTokenBalancesForMultipleAddresses } from './multicall';
import { FakeProvider } from '../../../tests/fake-provider';
import { advanceTime } from '../../../tests/helpers';
import { createMockInternalAccount } from '../../accounts-controller/src/tests/mocks';
import type {
ExtractAvailableAction,
ExtractAvailableEvent,
} from '../../base-controller/tests/helpers';
import {
buildCustomNetworkClientConfiguration,
buildMockGetNetworkClientById,
} from '../../network-controller/tests/helpers';

type AllAccountTrackerControllerActions =
MessengerActions<AccountTrackerControllerMessenger>;

type AllAccountTrackerControllerEvents =
MessengerEvents<AccountTrackerControllerMessenger>;

type RootMessenger = Messenger<
MockAnyNamespace,
AllAccountTrackerControllerActions,
AllAccountTrackerControllerEvents
>;

jest.mock('@metamask/controller-utils', () => {
return {
...jest.requireActual('@metamask/controller-utils'),
Expand Down Expand Up @@ -1368,7 +1379,7 @@ describe('AccountTrackerController', () => {
deriveStateFromMetadata(
controller.state,
controller.metadata,
'anonymous',
'includeInDebugSnapshot',
),
).toMatchInlineSnapshot(`Object {}`);
});
Expand Down Expand Up @@ -1428,10 +1439,7 @@ type WithControllerCallback<ReturnValue> = ({
controller,
}: {
controller: AccountTrackerController;
messenger: Messenger<
ExtractAvailableAction<AccountTrackerControllerMessenger> | AllowedActions,
ExtractAvailableEvent<AccountTrackerControllerMessenger> | AllowedEvents
>;
messenger: RootMessenger;
triggerSelectedAccountChange: (account: InternalAccount) => void;
refresh: (
clock: SinonFakeTimers,
Expand Down Expand Up @@ -1475,10 +1483,9 @@ async function withController<ReturnValue>(
testFunction,
] = args.length === 2 ? args : [{}, args[0]];

const messenger = new Messenger<
ExtractAvailableAction<AccountTrackerControllerMessenger> | AllowedActions,
ExtractAvailableEvent<AccountTrackerControllerMessenger> | AllowedEvents
>();
const messenger: RootMessenger = new Messenger({
namespace: MOCK_ANY_NAMESPACE,
});

const mockGetSelectedAccount = jest.fn().mockReturnValue(selectedAccount);
messenger.registerActionHandler(
Expand Down Expand Up @@ -1584,16 +1591,25 @@ async function withController<ReturnValue>(
mockNetworkState,
);

const accountTrackerMessenger = messenger.getRestricted({
name: 'AccountTrackerController',
allowedActions: [
const accountTrackerMessenger = new Messenger<
'AccountTrackerController',
AllAccountTrackerControllerActions,
AllAccountTrackerControllerEvents,
RootMessenger
>({
namespace: 'AccountTrackerController',
parent: messenger,
});
messenger.delegate({
messenger: accountTrackerMessenger,
actions: [
'NetworkController:getNetworkClientById',
'NetworkController:getState',
'PreferencesController:getState',
'AccountsController:getSelectedAccount',
'AccountsController:listAccounts',
],
allowedEvents: [
events: [
'AccountsController:selectedEvmAccountChange',
'TransactionController:unapprovedTransactionAdded',
'TransactionController:transactionConfirmed',
Expand Down
56 changes: 26 additions & 30 deletions packages/assets-controllers/src/AccountTrackerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import type {
import type {
ControllerStateChangeEvent,
ControllerGetStateAction,
RestrictedMessenger,
} from '@metamask/base-controller';
StateMetadata,
} from '@metamask/base-controller/next';
import {
query,
safelyExecuteWithTimeout,
toChecksumHexAddress,
} from '@metamask/controller-utils';
import EthQuery from '@metamask/eth-query';
import type { InternalAccount } from '@metamask/keyring-internal-api';
import type { Messenger } from '@metamask/messenger';
import type {
NetworkClient,
NetworkClientId,
Expand Down Expand Up @@ -127,11 +128,11 @@ export type AccountTrackerControllerState = {
accountsByChainId: Record<string, { [address: string]: AccountInformation }>;
};

const accountTrackerMetadata = {
const accountTrackerMetadata: StateMetadata<AccountTrackerControllerState> = {
accountsByChainId: {
includeInStateLogs: false,
persist: true,
anonymous: false,
includeInDebugSnapshot: false,
usedInUi: true,
},
};
Expand Down Expand Up @@ -205,12 +206,10 @@ export type AllowedEvents =
/**
* The messenger of the {@link AccountTrackerController}.
*/
export type AccountTrackerControllerMessenger = RestrictedMessenger<
export type AccountTrackerControllerMessenger = Messenger<
typeof controllerName,
AccountTrackerControllerActions | AllowedActions,
AccountTrackerControllerEvents | AllowedEvents,
AllowedActions['type'],
AllowedEvents['type']
AccountTrackerControllerEvents | AllowedEvents
>;

/** The input to start polling for the {@link AccountTrackerController} */
Expand Down Expand Up @@ -243,7 +242,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
* @param options - The controller options.
* @param options.interval - Polling interval used to fetch new account balances.
* @param options.state - Initial state to set on this controller.
* @param options.messenger - The controller messaging system.
* @param options.messenger - The controller messenger.
* @param options.getStakedBalanceForChain - The function to get the staked native asset balance for a chain.
* @param options.includeStakedAssets - Whether to include staked assets in the account balances.
* @param options.accountsApiChainIds - Function that returns array of chainIds that should use Accounts-API strategy (if supported by API).
Expand Down Expand Up @@ -305,7 +304,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac

this.setIntervalLength(interval);

this.messagingSystem.subscribe(
this.messenger.subscribe(
'AccountsController:selectedEvmAccountChange',
(newAddress, prevAddress) => {
if (newAddress !== prevAddress) {
Expand All @@ -317,7 +316,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
(event): string => event.address,
);

this.messagingSystem.subscribe(
this.messenger.subscribe(
'TransactionController:unapprovedTransactionAdded',
async (transactionMeta: TransactionMeta) => {
await this.#refreshAddress(
Expand All @@ -327,7 +326,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
},
);

this.messagingSystem.subscribe(
this.messenger.subscribe(
'TransactionController:transactionConfirmed',
async (transactionMeta: TransactionMeta) => {
await this.#refreshAddress(
Expand All @@ -342,12 +341,12 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac

private syncAccounts(newChainIds: string[]) {
const accountsByChainId = cloneDeep(this.state.accountsByChainId);
const { selectedNetworkClientId } = this.messagingSystem.call(
const { selectedNetworkClientId } = this.messenger.call(
'NetworkController:getState',
);
const {
configuration: { chainId: currentChainId },
} = this.messagingSystem.call(
} = this.messenger.call(
'NetworkController:getNetworkClientById',
selectedNetworkClientId,
);
Expand All @@ -367,7 +366,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
// Note: The address from the preferences controller are checksummed
// The addresses from the accounts controller are lowercased
const addresses = Object.values(
this.messagingSystem
this.messenger
.call('AccountsController:listAccounts')
.map((internalAccount) =>
toChecksumHexAddress(internalAccount.address),
Expand Down Expand Up @@ -401,25 +400,25 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
}

readonly #getProvider = (chainId: Hex): Web3Provider => {
const { networkConfigurationsByChainId } = this.messagingSystem.call(
const { networkConfigurationsByChainId } = this.messenger.call(
'NetworkController:getState',
);
const cfg = networkConfigurationsByChainId[chainId];
const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
const client = this.messagingSystem.call(
const client = this.messenger.call(
'NetworkController:getNetworkClientById',
networkClientId,
);
return new Web3Provider(client.provider);
};

readonly #getNetworkClient = (chainId: Hex) => {
const { networkConfigurationsByChainId } = this.messagingSystem.call(
const { networkConfigurationsByChainId } = this.messenger.call(
'NetworkController:getState',
);
const cfg = networkConfigurationsByChainId[chainId];
const { networkClientId } = cfg.rpcEndpoints[cfg.defaultRpcEndpointIndex];
return this.messagingSystem.call(
return this.messenger.call(
'NetworkController:getNetworkClientById',
networkClientId,
);
Expand Down Expand Up @@ -460,13 +459,12 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
#getCorrectNetworkClient(networkClientId?: NetworkClientId) {
const selectedNetworkClientId =
networkClientId ??
this.messagingSystem.call('NetworkController:getState')
.selectedNetworkClientId;
this.messenger.call('NetworkController:getState').selectedNetworkClientId;
const {
configuration: { chainId },
provider,
blockTracker,
} = this.messagingSystem.call(
} = this.messenger.call(
'NetworkController:getNetworkClientById',
selectedNetworkClientId,
);
Expand All @@ -485,7 +483,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
* @returns An array of network client IDs.
*/
#getNetworkClientIds(): NetworkClientId[] {
const { networkConfigurationsByChainId } = this.messagingSystem.call(
const { networkConfigurationsByChainId } = this.messenger.call(
'NetworkController:getState',
);
return Object.values(networkConfigurationsByChainId).flatMap(
Expand Down Expand Up @@ -524,13 +522,11 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
networkClientIds: NetworkClientId[],
queryAllAccounts: boolean = false,
) {
const selectedAccount = this.messagingSystem.call(
const selectedAccount = this.messenger.call(
'AccountsController:getSelectedAccount',
);
const allAccounts = this.messagingSystem.call(
'AccountsController:listAccounts',
);
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(
const allAccounts = this.messenger.call('AccountsController:listAccounts');
const { isMultiAccountBalancesEnabled } = this.messenger.call(
'PreferencesController:getState',
);

Expand Down Expand Up @@ -863,12 +859,12 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
}

#registerMessageHandlers() {
this.messagingSystem.registerActionHandler(
this.messenger.registerActionHandler(
`${controllerName}:updateNativeBalances` as const,
this.updateNativeBalances.bind(this),
);

this.messagingSystem.registerActionHandler(
this.messenger.registerActionHandler(
`${controllerName}:updateStakedBalances` as const,
this.updateStakedBalances.bind(this),
);
Expand Down
Loading
Loading