From 673c8e7b6d8a9c9d41138ef83f0806da4cc95ac5 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 21 Aug 2024 20:46:22 +0800 Subject: [PATCH] fix: bump accounts controller and add migration to fix undefined selectedAccount --- app/scripts/migrations/126.test.ts | 69 ++++++++++++++++++++++++++++++ app/scripts/migrations/126.ts | 49 +++++++++++++++++++++ app/scripts/migrations/index.js | 1 + package.json | 2 +- yarn.lock | 50 +++++++++++++++++++++- 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 app/scripts/migrations/126.test.ts create mode 100644 app/scripts/migrations/126.ts diff --git a/app/scripts/migrations/126.test.ts b/app/scripts/migrations/126.test.ts new file mode 100644 index 000000000000..99ddcbf09616 --- /dev/null +++ b/app/scripts/migrations/126.test.ts @@ -0,0 +1,69 @@ +import { migrate, version } from './126'; +import { createMockInternalAccount } from '../../../test/jest/mocks'; +import { AccountsControllerState } from '@metamask/accounts-controller'; + +const oldVersion = 125; + +const mockInternalAccount = createMockInternalAccount(); +const mockAccountsControllerState: AccountsControllerState = { + internalAccounts: { + accounts: { + [mockInternalAccount.id]: mockInternalAccount, + }, + selectedAccount: mockInternalAccount.id, + }, +}; + +describe('migration #126', () => { + afterEach(() => jest.resetAllMocks()); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + AccountsController: mockAccountsControllerState, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('updates selected account if it is not found in the list of accounts', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + AccountsController: { + ...mockAccountsControllerState, + internalAccounts: { + ...mockAccountsControllerState.internalAccounts, + selectedAccount: 'unknown id', + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + const selectedAccount = ( + newStorage.data.AccountsController as AccountsControllerState + ).internalAccounts.selectedAccount; + expect(selectedAccount).toStrictEqual(mockInternalAccount.id); + expect(newStorage.data.AccountsController).toStrictEqual( + mockAccountsControllerState, + ); + }); + + it('does nothing if the selectedAccount is found in the list of accounts', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + AccountsController: mockAccountsControllerState, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data.AccountsController).toStrictEqual( + mockAccountsControllerState, + ); + }); +}); diff --git a/app/scripts/migrations/126.ts b/app/scripts/migrations/126.ts new file mode 100644 index 000000000000..da7a72b19d3a --- /dev/null +++ b/app/scripts/migrations/126.ts @@ -0,0 +1,49 @@ +import { AccountsControllerState } from '@metamask/accounts-controller'; +import { cloneDeep } from 'lodash'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 126; + +/** + * This migration removes depreciated `Txcontroller` key if it is present in state. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly + * what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by + * controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + const accountsControllerState = state?.AccountsController as + | AccountsControllerState + | undefined; + + if ( + accountsControllerState && + Object.values(accountsControllerState?.internalAccounts.accounts).length > + 0 && + !accountsControllerState?.internalAccounts.accounts[ + accountsControllerState?.internalAccounts.selectedAccount + ] + ) { + accountsControllerState.internalAccounts.selectedAccount = Object.values( + accountsControllerState?.internalAccounts.accounts, + )[0].id; + } + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 305343e7124f..afc1484f3085 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -142,6 +142,7 @@ const migrations = [ require('./123'), require('./124'), require('./125'), + require('./126'), ]; export default migrations; diff --git a/package.json b/package.json index 9a42f5d033b9..12c1f0602d19 100644 --- a/package.json +++ b/package.json @@ -297,7 +297,7 @@ "@metamask-institutional/transaction-update": "^0.2.5", "@metamask-institutional/types": "^1.1.0", "@metamask/abi-utils": "^2.0.2", - "@metamask/accounts-controller": "^17.2.0", + "@metamask/accounts-controller": "^18.1.0", "@metamask/address-book-controller": "^4.0.1", "@metamask/announcement-controller": "^6.1.0", "@metamask/approval-controller": "^7.0.0", diff --git a/yarn.lock b/yarn.lock index be302858867f..a26ca4bec889 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4850,6 +4850,28 @@ __metadata: languageName: node linkType: hard +"@metamask/accounts-controller@npm:^18.1.0": + version: 18.1.0 + resolution: "@metamask/accounts-controller@npm:18.1.0" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/base-controller": "npm:^6.0.3" + "@metamask/eth-snap-keyring": "npm:^4.3.1" + "@metamask/keyring-api": "npm:^8.1.0" + "@metamask/snaps-sdk": "npm:^6.1.1" + "@metamask/snaps-utils": "npm:^7.8.1" + "@metamask/utils": "npm:^9.1.0" + deepmerge: "npm:^4.2.2" + ethereum-cryptography: "npm:^2.1.2" + immer: "npm:^9.0.6" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/keyring-controller": ^17.0.0 + "@metamask/snaps-controllers": ^9.3.0 + checksum: 10/31885a06f394a36e90a941ab15ed96fae8a39f2c650ce7523489114af0df1acf522bb8b5275e5e572354cdb42b286be805c336718b721105a2888c8be695c616 + languageName: node + linkType: hard + "@metamask/address-book-controller@npm:^4.0.1": version: 4.0.1 resolution: "@metamask/address-book-controller@npm:4.0.1" @@ -4997,6 +5019,16 @@ __metadata: languageName: node linkType: hard +"@metamask/base-controller@npm:^6.0.3": + version: 6.0.3 + resolution: "@metamask/base-controller@npm:6.0.3" + dependencies: + "@metamask/utils": "npm:^9.1.0" + immer: "npm:^9.0.6" + checksum: 10/43e208627c673094e3b4a7766ef4df34cd5a9ec7f09721cc3e60123b69a22b82c68752b963d17f4ad925a01c6e5dc89f125cac33aeee4e90e0a8346a1d153aae + languageName: node + linkType: hard + "@metamask/bitcoin-wallet-snap@npm:^0.4.0": version: 0.4.0 resolution: "@metamask/bitcoin-wallet-snap@npm:0.4.0" @@ -5672,6 +5704,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-api@npm:^8.1.0": + version: 8.1.0 + resolution: "@metamask/keyring-api@npm:8.1.0" + dependencies: + "@metamask/snaps-sdk": "npm:^6.1.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.1.0" + "@types/uuid": "npm:^9.0.8" + bech32: "npm:^2.0.0" + uuid: "npm:^9.0.1" + peerDependencies: + "@metamask/providers": ">=15 <18" + checksum: 10/15711ddaa0007794cc23f9c02f6cfbee85aa1cf79a46468a0398404c295eef1511555ce6bd60a691081d33864d288ea8b309ee9ac9c4d6f277ab22e4d97cb76e + languageName: node + linkType: hard + "@metamask/keyring-controller@npm:17.1.1": version: 17.1.1 resolution: "@metamask/keyring-controller@npm:17.1.1" @@ -25987,7 +26035,7 @@ __metadata: "@metamask-institutional/transaction-update": "npm:^0.2.5" "@metamask-institutional/types": "npm:^1.1.0" "@metamask/abi-utils": "npm:^2.0.2" - "@metamask/accounts-controller": "npm:^17.2.0" + "@metamask/accounts-controller": "npm:^18.1.0" "@metamask/address-book-controller": "npm:^4.0.1" "@metamask/announcement-controller": "npm:^6.1.0" "@metamask/api-specs": "npm:^0.9.3"