Skip to content

Feat: remove current chain id dependency asset contract controllers #5698

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

Draft
wants to merge 4 commits into
base: feat/make-chainId-mandatory-in-nft-controller
Choose a base branch
from
Draft
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
6 changes: 2 additions & 4 deletions eslint-warning-thresholds.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,11 @@
},
"packages/assets-controllers/src/NftController.test.ts": {
"import-x/namespace": 9,
"import-x/order": 3,
"jest/no-conditional-in-test": 8
"jest/no-conditional-in-test": 6
},
"packages/assets-controllers/src/NftController.ts": {
"@typescript-eslint/prefer-readonly": 1,
"jsdoc/check-tag-names": 46,
"jsdoc/tag-lines": 4
"jsdoc/check-tag-names": 46
},
"packages/assets-controllers/src/NftDetectionController.test.ts": {
"import-x/namespace": 6,
Expand Down
50 changes: 25 additions & 25 deletions packages/assets-controllers/src/AccountTrackerController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ describe('AccountTrackerController', () => {
listAccounts: [mockAccount1, mockAccount2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);
expect(controller.state).toStrictEqual({
accountsByChainId: {
'0x1': {
Expand All @@ -154,7 +154,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand All @@ -181,7 +181,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1, ACCOUNT_2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand All @@ -207,7 +207,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1, ACCOUNT_2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -237,7 +237,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1, ACCOUNT_2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -272,7 +272,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1, ACCOUNT_2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('AccountTrackerController', () => {
listAccounts: [ACCOUNT_1, ACCOUNT_2],
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -366,7 +366,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh(networkClientId);
await controller.refresh(['networkClientId1']);
expect(controller.state).toStrictEqual({
accountsByChainId: {
'0x1': {
Expand Down Expand Up @@ -403,7 +403,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh(networkClientId);
await controller.refresh(['networkClientId1']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -441,7 +441,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh(networkClientId);
await controller.refresh(['networkClientId1']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -477,7 +477,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh(networkClientId);
await controller.refresh(['networkClientId1']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -517,7 +517,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -558,7 +558,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -598,7 +598,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -640,7 +640,7 @@ describe('AccountTrackerController', () => {
},
},
async ({ controller }) => {
await controller.refresh();
await controller.refresh(['mainnet']);

expect(controller.state).toStrictEqual({
accountsByChainId: {
Expand Down Expand Up @@ -726,7 +726,7 @@ describe('AccountTrackerController', () => {
jest.spyOn(controller, 'refresh').mockResolvedValue();

await controller.startPolling({
networkClientId: 'networkClientId1',
networkClientIds: ['networkClientId1'],
});
await advanceTime({ clock, duration: 1 });

Expand Down Expand Up @@ -759,34 +759,34 @@ describe('AccountTrackerController', () => {
.mockResolvedValue();

controller.startPolling({
networkClientId: networkClientId1,
networkClientIds: [networkClientId1],
});

await advanceTime({ clock, duration: 0 });
expect(refreshSpy).toHaveBeenNthCalledWith(1, networkClientId1);
expect(refreshSpy).toHaveBeenNthCalledWith(1, [networkClientId1]);
expect(refreshSpy).toHaveBeenCalledTimes(1);
await advanceTime({ clock, duration: 50 });
expect(refreshSpy).toHaveBeenCalledTimes(1);
await advanceTime({ clock, duration: 50 });
expect(refreshSpy).toHaveBeenNthCalledWith(2, networkClientId1);
expect(refreshSpy).toHaveBeenNthCalledWith(2, [networkClientId1]);
expect(refreshSpy).toHaveBeenCalledTimes(2);

const pollToken = controller.startPolling({
networkClientId: networkClientId2,
networkClientIds: [networkClientId2],
});

await advanceTime({ clock, duration: 0 });
expect(refreshSpy).toHaveBeenNthCalledWith(3, networkClientId2);
expect(refreshSpy).toHaveBeenNthCalledWith(3, [networkClientId2]);
expect(refreshSpy).toHaveBeenCalledTimes(3);
await advanceTime({ clock, duration: 100 });
expect(refreshSpy).toHaveBeenNthCalledWith(4, networkClientId1);
expect(refreshSpy).toHaveBeenNthCalledWith(5, networkClientId2);
expect(refreshSpy).toHaveBeenNthCalledWith(4, [networkClientId1]);
expect(refreshSpy).toHaveBeenNthCalledWith(5, [networkClientId2]);
expect(refreshSpy).toHaveBeenCalledTimes(5);

controller.stopPollingByPollingToken(pollToken);

await advanceTime({ clock, duration: 100 });
expect(refreshSpy).toHaveBeenNthCalledWith(6, networkClientId1);
expect(refreshSpy).toHaveBeenNthCalledWith(6, [networkClientId1]);
expect(refreshSpy).toHaveBeenCalledTimes(6);

controller.stopAllPolling();
Expand All @@ -810,7 +810,7 @@ describe('AccountTrackerController', () => {

expect(refreshSpy).not.toHaveBeenCalled();
controller.startPolling({
networkClientId: 'networkClientId1',
networkClientIds: ['networkClientId1'],
});

await advanceTime({ clock, duration: 1 });
Expand Down
118 changes: 81 additions & 37 deletions packages/assets-controllers/src/AccountTrackerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export type AccountTrackerControllerMessenger = RestrictedMessenger<

/** The input to start polling for the {@link AccountTrackerController} */
type AccountTrackerPollingInput = {
networkClientId: NetworkClientId;
networkClientIds: NetworkClientId[];
};

/**
Expand Down Expand Up @@ -194,7 +194,7 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
'AccountsController:selectedEvmAccountChange',
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// eslint-disable-next-line @typescript-eslint/no-misused-promises
() => this.refresh(),
() => this.refresh(this.#getNetworkClientIds()),
);
}

Expand Down Expand Up @@ -282,69 +282,113 @@ export class AccountTrackerController extends StaticIntervalPollingController<Ac
};
}

/**
* Retrieves the list of network client IDs.
*
* @returns An array of network client IDs.
*/
#getNetworkClientIds(): NetworkClientId[] {
const { networkConfigurationsByChainId } = this.messagingSystem.call(
'NetworkController:getState',
);
return Object.values(networkConfigurationsByChainId).flatMap(
(networkConfiguration) =>
networkConfiguration.rpcEndpoints.map(
(rpcEndpoint) => rpcEndpoint.networkClientId,
),
);
}

/**
* Refreshes the balances of the accounts using the networkClientId
*
* @param input - The input for the poll.
* @param input.networkClientId - The network client ID used to get balances.
* @param input.networkClientIds - The network client IDs used to get balances.
*/
async _executePoll({
networkClientId,
networkClientIds,
}: AccountTrackerPollingInput): Promise<void> {
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.refresh(networkClientId);
this.refresh(networkClientIds);
}

/**
* Refreshes the balances of the accounts depending on the multi-account setting.
* If multi-account is disabled, only updates the selected account balance.
* If multi-account is enabled, updates balances for all accounts.
*
* @param networkClientId - Optional networkClientId to fetch a network client with
* @param networkClientIds - Optional network client IDs to fetch a network client with
*/
async refresh(networkClientId?: NetworkClientId) {
async refresh(networkClientIds: NetworkClientId[]) {
const selectedAccount = this.messagingSystem.call(
'AccountsController:getSelectedAccount',
);
const releaseLock = await this.#refreshMutex.acquire();
try {
const { chainId, ethQuery } =
this.#getCorrectNetworkClient(networkClientId);
this.syncAccounts(chainId);
const { accountsByChainId } = this.state;
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(
'PreferencesController:getState',
);

const accountsToUpdate = isMultiAccountBalancesEnabled
? Object.keys(accountsByChainId[chainId])
: [toChecksumHexAddress(selectedAccount.address)];

const accountsForChain = { ...accountsByChainId[chainId] };
for (const address of accountsToUpdate) {
const balance = await this.#getBalanceFromChain(address, ethQuery);
if (balance) {
accountsForChain[address] = {
balance,
};
}
if (this.#includeStakedAssets) {
const stakedBalance = await this.#getStakedBalanceForChain(
address,
networkClientId,
// Create an array of promises for each networkClientId
const updatePromises = networkClientIds.map(async (networkClientId) => {
const { chainId, ethQuery } =
this.#getCorrectNetworkClient(networkClientId);
this.syncAccounts(chainId);
const { accountsByChainId } = this.state;
const { isMultiAccountBalancesEnabled } = this.messagingSystem.call(
'PreferencesController:getState',
);

const accountsToUpdate = isMultiAccountBalancesEnabled
? Object.keys(accountsByChainId[chainId])
: [toChecksumHexAddress(selectedAccount.address)];

const accountsForChain = { ...accountsByChainId[chainId] };

// Create an array of promises for balance and staked balance fetching
const balancePromises = accountsToUpdate.map(async (address) => {
const balancePromise = this.#getBalanceFromChain(address, ethQuery);
const stakedBalancePromise = this.#includeStakedAssets
? this.#getStakedBalanceForChain(address, networkClientId)
: Promise.resolve(null);

const [balanceResult, stakedBalanceResult] = await Promise.allSettled(
[balancePromise, stakedBalancePromise],
);
if (stakedBalance) {

// Update account balances
if (balanceResult.status === 'fulfilled' && balanceResult.value) {
accountsForChain[address] = {
balance: balanceResult.value,
};
}

if (
stakedBalanceResult.status === 'fulfilled' &&
stakedBalanceResult.value
) {
accountsForChain[address] = {
...accountsForChain[address],
stakedBalance,
stakedBalance: stakedBalanceResult.value,
};
}
}
}
});

this.update((state) => {
state.accountsByChainId[chainId] = accountsForChain;
// Wait for all balance-related promises to settle
await Promise.allSettled(balancePromises);

// After all promises for this networkClientId are settled, return the updated data
return { chainId, accountsForChain };
});

// Wait for all networkClientId updates to settle in parallel
const allResults = await Promise.allSettled(updatePromises);

// Update the state once all networkClientId updates are completed
allResults.forEach((result) => {
if (result.status === 'fulfilled') {
const { chainId, accountsForChain } = result.value;
this.update((state) => {
state.accountsByChainId[chainId] = accountsForChain;
});
}
});
} finally {
releaseLock();
Expand Down
Loading
Loading