From 30ed126dbcf3f901ed8326d6ed506632cfb474ec Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 28 Aug 2023 20:26:23 +0800 Subject: [PATCH 01/54] feat: initial accounts controller commit --- packages/accounts-controller/CHANGELOG.md | 8 + packages/accounts-controller/LICENSE | 20 + packages/accounts-controller/README.md | 15 + packages/accounts-controller/jest.config.js | 26 + packages/accounts-controller/package.json | 62 + .../src/AccountsController.ts | 485 +++++++ packages/accounts-controller/src/index.ts | 1 + .../accounts-controller/tsconfig.build.json | 18 + packages/accounts-controller/tsconfig.json | 16 + packages/accounts-controller/typedoc.json | 7 + tsconfig.build.json | 3 + tsconfig.json | 3 + yarn.lock | 1122 ++++++++++++++++- 13 files changed, 1760 insertions(+), 26 deletions(-) create mode 100644 packages/accounts-controller/CHANGELOG.md create mode 100644 packages/accounts-controller/LICENSE create mode 100644 packages/accounts-controller/README.md create mode 100644 packages/accounts-controller/jest.config.js create mode 100644 packages/accounts-controller/package.json create mode 100644 packages/accounts-controller/src/AccountsController.ts create mode 100644 packages/accounts-controller/src/index.ts create mode 100644 packages/accounts-controller/tsconfig.build.json create mode 100644 packages/accounts-controller/tsconfig.json create mode 100644 packages/accounts-controller/typedoc.json diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md new file mode 100644 index 00000000000..11bddf32c5b --- /dev/null +++ b/packages/accounts-controller/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] diff --git a/packages/accounts-controller/LICENSE b/packages/accounts-controller/LICENSE new file mode 100644 index 00000000000..ddfbecf9020 --- /dev/null +++ b/packages/accounts-controller/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2018 MetaMask + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE diff --git a/packages/accounts-controller/README.md b/packages/accounts-controller/README.md new file mode 100644 index 00000000000..690cc95ba32 --- /dev/null +++ b/packages/accounts-controller/README.md @@ -0,0 +1,15 @@ +# `@metamask/accounts-controller` + +Manages internal accounts used in place of regular ethereum addresses. + +## Installation + +`yarn add @metamask/accounts-controller` + +or + +`npm install @metamask/accounts-controller` + +## Contributing + +This package is part of a monorepo. Instructions for contributing can be found in the [monorepo README](https://github.com/MetaMask/core#readme). diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js new file mode 100644 index 00000000000..ca084133399 --- /dev/null +++ b/packages/accounts-controller/jest.config.js @@ -0,0 +1,26 @@ +/* + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/configuration + */ + +const merge = require('deepmerge'); +const path = require('path'); + +const baseConfig = require('../../jest.config.packages'); + +const displayName = path.basename(__dirname); + +module.exports = merge(baseConfig, { + // The display name when running multiple projects + displayName, + + // An object that configures minimum threshold enforcement for coverage results + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json new file mode 100644 index 00000000000..f67d93a3aff --- /dev/null +++ b/packages/accounts-controller/package.json @@ -0,0 +1,62 @@ +{ + "name": "@metamask/accounts-controller", + "version": "3.5.0", + "description": "Manages internal accounts", + "keywords": [ + "MetaMask", + "Ethereum" + ], + "homepage": "https://github.com/MetaMask/core/tree/main/packages/accounts-controller#readme", + "bugs": { + "url": "https://github.com/MetaMask/core/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/MetaMask/core.git" + }, + "license": "MIT", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist/" + ], + "scripts": { + "build:docs": "typedoc", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/approval-controller", + "publish:preview": "yarn npm publish --tag preview", + "test": "jest", + "test:watch": "jest --watch" + }, + "dependencies": { + "@metamask/base-controller": "^3.2.0", + "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0", + "@metamask/keyring-controller": "^7.1.0", + "@metamask/snaps-controllers": "^1.0.1", + "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1", + "@metamask/snaps-utils": "^1.0.1", + "@metamask/utils": "^6.2.0", + "eth-rpc-errors": "^4.0.2", + "ethereumjs-util": "^7.1.5", + "immer": "^9.0.6", + "nanoid": "^3.1.31", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@metamask/auto-changelog": "^3.1.0", + "@types/jest": "^27.4.1", + "deepmerge": "^4.2.2", + "jest": "^27.5.1", + "sinon": "^9.2.4", + "ts-jest": "^27.1.4", + "typedoc": "^0.22.15", + "typedoc-plugin-missing-exports": "^0.22.6", + "typescript": "~4.6.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + } +} diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts new file mode 100644 index 00000000000..bdf791ffd9f --- /dev/null +++ b/packages/accounts-controller/src/AccountsController.ts @@ -0,0 +1,485 @@ +import type { RestrictedControllerMessenger } from '@metamask/base-controller'; +import { BaseControllerV2 } from '@metamask/base-controller'; +import type { InternalAccount } from '@metamask/eth-snap-keyring'; +import { SnapKeyring } from '@metamask/eth-snap-keyring'; +import type { + KeyringControllerState, + KeyringController, + KeyringControllerEvents, +} from '@metamask/keyring-controller'; +import type { + SnapController, + SnapControllerEvents, +} from '@metamask/snaps-controllers'; +import type { SnapControllerState } from '@metamask/snaps-controllers-flask'; +import type { Snap } from '@metamask/snaps-utils'; +import { sha256FromString } from 'ethereumjs-util'; +import type { Patch } from 'immer'; +import { v4 as uuid } from 'uuid'; + +const controllerName = 'AccountsController'; + +export type AccountsControllerState = { + internalAccounts: { + accounts: Record; + selectedAccount: string; // id of the selected account + }; +}; + +export type AccountsControllerGetStateAction = { + type: `${typeof controllerName}:getState`; + handler: () => AccountsControllerState; +}; + +export type AccountsControllerActions = AccountsControllerGetStateAction; + +export type AccountsControllerChangeEvent = { + type: `${typeof controllerName}:stateChange`; + payload: [AccountsControllerState, Patch[]]; +}; + +export type AccountsControllerSelectedAccountChangeEvent = { + type: `${typeof controllerName}:selectedAccountChange`; + payload: [InternalAccount]; +}; + +export type AccountsControllerEvents = + | AccountsControllerChangeEvent + | AccountsControllerSelectedAccountChangeEvent + | SnapControllerEvents + | KeyringControllerEvents; + +export type AccountsControllerMessenger = RestrictedControllerMessenger< + typeof controllerName, + AccountsControllerActions, + AccountsControllerEvents, + string, + string +>; + +const accountsControllerMetadata = { + internalAccounts: { + persist: true, + anonymous: false, + }, + selectedAccount: { + persist: true, + anonymous: false, + }, +}; + +const defaultState: AccountsControllerState = { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, +}; + +export default class AccountsController extends BaseControllerV2< + typeof controllerName, + AccountsControllerState, + AccountsControllerMessenger +> { + #keyringController: KeyringController; + + #snapController: SnapController; + + constructor({ + messenger, + state, + keyringController, + snapController, + onSnapStateChange, + onKeyringStateChange, + }: { + messenger: AccountsControllerMessenger; + state: AccountsControllerState; + keyringController: KeyringController; + snapController: SnapController; + onKeyringStateChange: ( + listener: (keyringState: KeyringControllerState) => void, + ) => void; + onSnapStateChange: ( + listener: (snapState: SnapControllerState) => void, + ) => void; + }) { + super({ + messenger, + name: controllerName, + metadata: accountsControllerMetadata, + state: { + ...defaultState, + ...state, + }, + }); + + this.#keyringController = keyringController; + this.#snapController = snapController; + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + onSnapStateChange(async (snapState: SnapControllerState) => { + // only check if snaps changed in status + const { snaps } = snapState; + const accounts = this.listAccounts(); + + const disabledSnaps: Snap[] = []; + for (const snap of Object.values(snaps)) { + if (!(await this.#isSnapEnabled(snap.id))) { + disabledSnaps.push(snap); + } + } + + const accountsToUpdate = accounts.filter( + (account) => + account.metadata.snap && + disabledSnaps.find((snap) => snap.id === account.metadata.snap?.id), + ); + + this.update((currentState: AccountsControllerState) => { + accountsToUpdate.forEach((account) => { + if ( + currentState.internalAccounts.accounts[account.id] && + currentState.internalAccounts.accounts[account.id].metadata?.snap + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this account is guaranteed to have snap metadata + currentState.internalAccounts.accounts[ + account.id + ].metadata.snap.enabled = false; + } + }); + }); + }); + + onKeyringStateChange( + // eslint-disable-next-line @typescript-eslint/no-misused-promises + async (keyringState: KeyringControllerState): Promise => { + // check if there are any new accounts added + // TODO: change when accountAdded event is added to the keyring controller + + if (keyringState.isUnlocked) { + // TODO: ACCOUNTS_CONTROLLER keyring will return accounts instead of addresses, remove this flatMap after and just get the latest id + const updatedKeyringAddresses = keyringState.keyrings.flatMap( + (keyring) => keyring.accounts, + ); + const previousAccounts = this.listAccounts(); + + // if there are no overlaps between the addresses in the keyring and previous accounts, + // it means the keyring is being reinitialized because the vault is being restored with the same SRP + const overlaps = updatedKeyringAddresses.filter((address) => + previousAccounts.find( + (account) => + account.address.toLowerCase() === address.toLowerCase(), + ), + ); + + await this.updateAccounts(); + + if (updatedKeyringAddresses.length > previousAccounts.length) { + this.#handleNewAccountAdded( + updatedKeyringAddresses, + previousAccounts, + ); + } else if ( + updatedKeyringAddresses.length > 0 && + overlaps.length === 0 + ) { + // if the keyring is being reinitialized, the selected account will be reset to the first account + this.setSelectedAccount(this.listAccounts()[0].id); + } else if ( + updatedKeyringAddresses.length < previousAccounts.length && + overlaps.length > 0 && + !this.getAccount(this.state.internalAccounts.selectedAccount) + ) { + this.#handleSelectedAccountRemoved(); + } + } + }, + ); + + // if somehow the selected account becomes lost then select the first account + if ( + this.state.internalAccounts.selectedAccount !== '' && + !this.getAccount(this.state.internalAccounts.selectedAccount) + ) { + this.setSelectedAccount(this.listAccounts()[0]?.id); + } + } + + getAccount(accountId: string): InternalAccount | undefined { + return this.state.internalAccounts.accounts[accountId]; + } + + listAccounts(): InternalAccount[] { + return Object.values(this.state.internalAccounts.accounts); + } + + getAccountExpect(accountId: string): InternalAccount { + // Edge case where the extension is setup but the srp is not yet created + // certain ui elements will query the selected address before any accounts are created. + if (!accountId) { + return { + id: '', + name: '', + address: '', + options: {}, + supportedMethods: [], + type: 'eip155:eoa', + metadata: { + keyring: { + type: '', + }, + }, + }; + } + + const account = this.getAccount(accountId); + if (account === undefined) { + throw new Error(`Account Id ${accountId} not found`); + } + return account; + } + + getSelectedAccount(): InternalAccount { + return this.getAccountExpect(this.state.internalAccounts.selectedAccount); + } + + async updateAccounts(): Promise { + let legacyAccounts = await this.#listLegacyAccounts(); + const snapAccounts = await this.#listSnapAccounts(); + // remove duplicate accounts that are retrieved from the snap keyring. + legacyAccounts = legacyAccounts.filter( + (account) => + !snapAccounts.find( + (snapAccount) => snapAccount.address !== account.address, + ), + ); + + // keyring type map. + const keyringTypes = new Map(); + const previousAccounts = this.state.internalAccounts.accounts; + + const accounts: Record = [ + ...legacyAccounts, + ...snapAccounts, + ].reduce((internalAccountMap, internalAccount) => { + const keyringTypeName = keyringTypeToName( + internalAccount.metadata.keyring.type, + ); + const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0; + if (keyringAccountIndex) { + keyringTypes.set(keyringTypeName, keyringAccountIndex + 1); + } else { + keyringTypes.set(keyringTypeName, 1); + } + + const existingAccount = previousAccounts[internalAccount.id]; + + internalAccountMap[internalAccount.id] = { + ...internalAccount, + name: existingAccount + ? existingAccount.name + : `${keyringTypeName} ${keyringAccountIndex + 1}`, + metadata: { + ...internalAccount.metadata, + lastSelected: existingAccount?.metadata?.lastSelected, + }, + }; + + return internalAccountMap; + }, {} as Record); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore Type instantiation is excessively deep and possibly infinite. + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts.accounts = accounts; + }); + } + + async #listSnapAccounts(): Promise { + const [snapKeyring] = this.#keyringController.getKeyringsByType( + SnapKeyring.type, + ); + + const snapAccounts = + (await (snapKeyring as SnapKeyring)?.listAccounts(false)) ?? []; + + for (const account of snapAccounts) { + const snapId = account.metadata.snap?.id as string; + + account.metadata = { + snap: { + id: snapId, + enabled: await this.#isSnapEnabled(snapId), + name: account.name, + }, + keyring: { + type: (snapKeyring as SnapKeyring).type, + }, + }; + } + + return snapAccounts; + } + + // Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface + async #listLegacyAccounts(): Promise[]> { + const addresses = await this.#keyringController.getAccounts(); + const internalAccounts: Omit[] = []; + for (const address of addresses) { + const keyring = await this.#keyringController.getKeyringForAccount( + address, + ); + // TODO: this is done until the keyrings all implement the InternalAccount interface + const v4options = { + random: sha256FromString(address).slice(0, 16), + }; + + internalAccounts.push({ + id: uuid(v4options), + address, + options: {}, + supportedMethods: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v2', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + keyring: { + type: (keyring as any).type as string, + }, + }, + }); + } + + return internalAccounts.filter( + (account) => account.metadata.keyring.type !== 'Snap Keyring', + ); + } + + setSelectedAccount(accountId: string): void { + const account = this.getAccountExpect(accountId); + + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts.accounts[account.id].metadata.lastSelected = + Date.now(); + currentState.internalAccounts.selectedAccount = account.id; + }); + + this.messagingSystem.publish(`${this.name}:selectedAccountChange`, account); + } + + setAccountName(accountId: string, accountName: string): void { + const account = this.getAccountExpect(accountId); + + if ( + this.listAccounts().find( + (internalAccount) => + internalAccount.name === accountName && + internalAccount.id !== accountId, + ) + ) { + throw new Error('Account name already exists'); + } + + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts.accounts[accountId] = { + ...account, + name: accountName, + }; + }); + } + + async #isSnapEnabled(snapId: string): Promise { + const snap = (await this.#snapController.getSnapState(snapId)) as any; + if (!snap) { + return false; + } + return snap?.enabled && !snap?.blocked; + } + + #handleSelectedAccountRemoved() { + const previousAccount = this.listAccounts() + .filter( + (account) => account.id !== this.state.internalAccounts.selectedAccount, + ) + .sort((accountA, accountB) => { + // sort by lastSelected descending + return ( + (accountB.metadata?.lastSelected ?? 0) - + (accountA.metadata?.lastSelected ?? 0) + ); + })[0]; + + this.setSelectedAccount(previousAccount.id); + } + + #handleNewAccountAdded( + updatedKeyringAddresses: string[], + previousAccounts: InternalAccount[], + ) { + const newAddress = updatedKeyringAddresses.find( + (address) => + !previousAccounts.find( + (account) => account.address.toLowerCase() === address.toLowerCase(), + ), + ); + + const newAccount = this.listAccounts().find( + (account) => + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + account.address.toLowerCase() === newAddress!.toLowerCase(), + ); + + // console.log('new account in onKeyringStateChange', newAccount); + + // set the first new account as the selected account + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.setSelectedAccount(newAccount!.id); + } +} + +/** + * Returns the name of the keyring type. + * + * @param keyringType - The type of the keyring. + * @returns The name of the keyring type. + */ +export function keyringTypeToName(keyringType: string): string { + switch (keyringType) { + case 'Simple Key Pair': { + return 'Account'; + } + case 'HD Key Tree': { + return 'Account'; + } + case 'Trezor Hardware': { + return 'Trezor'; + } + case 'Ledger Hardware': { + return 'Ledger'; + } + case 'Lattice Hardware': { + return 'Lattice'; + } + case 'QR Hardware Wallet Device': { + return 'QR'; + } + case 'Snap Keyring': { + return 'Snap Account'; + } + case 'Custody': { + return 'Custody'; + } + default: { + return 'Account'; + } + } +} diff --git a/packages/accounts-controller/src/index.ts b/packages/accounts-controller/src/index.ts new file mode 100644 index 00000000000..6197f0e008e --- /dev/null +++ b/packages/accounts-controller/src/index.ts @@ -0,0 +1 @@ +export * from './AccountsController'; diff --git a/packages/accounts-controller/tsconfig.build.json b/packages/accounts-controller/tsconfig.build.json new file mode 100644 index 00000000000..9ea6fa18ca8 --- /dev/null +++ b/packages/accounts-controller/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.packages.build.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist", + "rootDir": "./src" + }, + "references": [ + { + "path": "../base-controller/tsconfig.build.json" + }, + { + "path": "../controller-utils/tsconfig.build.json" + }, + { "path": "../keyring-controller/tsconfig.build.json" } + ], + "include": ["../../types", "./src"] +} diff --git a/packages/accounts-controller/tsconfig.json b/packages/accounts-controller/tsconfig.json new file mode 100644 index 00000000000..5b422a56914 --- /dev/null +++ b/packages/accounts-controller/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.packages.json", + "compilerOptions": { + "baseUrl": "./" + }, + "references": [ + { + "path": "../base-controller" + }, + { + "path": "../controller-utils" + }, + { "path": "../keyring-controller" } + ], + "include": ["../../types", "./src"] +} diff --git a/packages/accounts-controller/typedoc.json b/packages/accounts-controller/typedoc.json new file mode 100644 index 00000000000..c9da015dbf8 --- /dev/null +++ b/packages/accounts-controller/typedoc.json @@ -0,0 +1,7 @@ +{ + "entryPoints": ["./src/index.ts"], + "excludePrivate": true, + "hideGenerator": true, + "out": "docs", + "tsconfig": "./tsconfig.build.json" +} diff --git a/tsconfig.build.json b/tsconfig.build.json index be01e0c970d..f115d553ebe 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,5 +1,8 @@ { "references": [ + { + "path": "./packages/accounts-controller/tsconfig.build.json" + }, { "path": "./packages/address-book-controller/tsconfig.build.json" }, diff --git a/tsconfig.json b/tsconfig.json index aba4c9ac8fe..15bfa34ee1b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,9 @@ "noEmit": true }, "references": [ + { + "path": "./packages/accounts-controller" + }, { "path": "./packages/address-book-controller" }, diff --git a/yarn.lock b/yarn.lock index b24c132248f..1ac16f5917a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,6 +38,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/code-frame@npm:7.22.10" + dependencies: + "@babel/highlight": ^7.22.10 + chalk: ^2.4.2 + checksum: 89a06534ad19759da6203a71bad120b1d7b2ddc016c8e07d4c56b35dea25e7396c6da60a754e8532a86733092b131ae7f661dbe6ba5d165ea777555daa2ed3c9 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.22.9": version: 7.22.9 resolution: "@babel/compat-data@npm:7.22.9" @@ -68,6 +78,41 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.18.6, @babel/core@npm:^7.20.12": + version: 7.22.11 + resolution: "@babel/core@npm:7.22.11" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.22.10 + "@babel/generator": ^7.22.10 + "@babel/helper-compilation-targets": ^7.22.10 + "@babel/helper-module-transforms": ^7.22.9 + "@babel/helpers": ^7.22.11 + "@babel/parser": ^7.22.11 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.11 + "@babel/types": ^7.22.11 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.3 + semver: ^6.3.1 + checksum: f258b2539ea2e5bfe55a708c2f3e1093a1b4744f12becc35abeb896037b66210de9a8ad6296a706046d5dc3a24e564362b73a9b814e5bfe500c8baab60c22d2e + languageName: node + linkType: hard + +"@babel/generator@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/generator@npm:7.22.10" + dependencies: + "@babel/types": ^7.22.10 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 59a79730abdff9070692834bd3af179e7a9413fa2ff7f83dff3eb888765aeaeb2bfc7b0238a49613ed56e1af05956eff303cc139f2407eda8df974813e486074 + languageName: node + linkType: hard + "@babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": version: 7.22.9 resolution: "@babel/generator@npm:7.22.9" @@ -80,6 +125,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/helper-compilation-targets@npm:7.22.10" + dependencies: + "@babel/compat-data": ^7.22.9 + "@babel/helper-validator-option": ^7.22.5 + browserslist: ^4.21.9 + lru-cache: ^5.1.1 + semver: ^6.3.1 + checksum: f6f1896816392bcff671bbe6e277307729aee53befb4a66ea126e2a91eda78d819a70d06fa384c74ef46c1595544b94dca50bef6c78438d9ffd31776dafbd435 + languageName: node + linkType: hard + "@babel/helper-compilation-targets@npm:^7.22.9": version: 7.22.9 resolution: "@babel/helper-compilation-targets@npm:7.22.9" @@ -191,6 +249,17 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/helpers@npm:7.22.11" + dependencies: + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.11 + "@babel/types": ^7.22.11 + checksum: 93186544228b5e371486466ec3b86a77cce91beeff24a5670ca8ec46d50328f7700dab82d532351286e9d68624dc51d6d71589b051dd9535e44be077a43ec013 + languageName: node + linkType: hard + "@babel/helpers@npm:^7.22.6": version: 7.22.6 resolution: "@babel/helpers@npm:7.22.6" @@ -202,6 +271,17 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.22.10": + version: 7.22.10 + resolution: "@babel/highlight@npm:7.22.10" + dependencies: + "@babel/helper-validator-identifier": ^7.22.5 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: f714a1e1a72dd9d72f6383f4f30fd342e21a8df32d984a4ea8f5eab691bb6ba6db2f8823d4b4cf135d98869e7a98925b81306aa32ee3c429f8cfa52c75889e1b + languageName: node + linkType: hard + "@babel/highlight@npm:^7.22.5": version: 7.22.5 resolution: "@babel/highlight@npm:7.22.5" @@ -222,6 +302,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/parser@npm:7.22.11" + bin: + parser: ./bin/babel-parser.js + checksum: 332079ed09794d3685343e9fc39c6a12dcb6ea589119f2135952cdef2424296786bb609a33f6dfa9be271797bbf8339f1865118418ea50b32a0c701734c96664 + languageName: node + linkType: hard + "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -376,6 +465,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/traverse@npm:7.22.11" + dependencies: + "@babel/code-frame": ^7.22.10 + "@babel/generator": ^7.22.10 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.22.11 + "@babel/types": ^7.22.11 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 4ad62d548ca8b95dbf45bae16cc167428f174f3c837d55a5878b1f17bdbc8b384d6df741dc7c461b62c04d881cf25644d3ab885909ba46e3ac43224e2b15b504 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.7.2": version: 7.22.8 resolution: "@babel/traverse@npm:7.22.8" @@ -405,6 +512,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.18.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.11": + version: 7.22.11 + resolution: "@babel/types@npm:7.22.11" + dependencies: + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 + to-fast-properties: ^2.0.0 + checksum: 431a6446896adb62c876d0fe75263835735d3c974aae05356a87eb55f087c20a777028cf08eadcace7993e058bbafe3b21ce2119363222c6cef9eedd7a204810 + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -421,6 +539,13 @@ __metadata: languageName: node linkType: hard +"@endo/env-options@npm:^0.1.3": + version: 0.1.3 + resolution: "@endo/env-options@npm:0.1.3" + checksum: da8c66865d4d30b0053a00960657dc36f022975a888f0dd6a2f6bb37b9fe731f45a02a2cf263d93b1a40fcb37b25f8ba7076cb8af9e93fd95f496365d9382930 + languageName: node + linkType: hard + "@es-joy/jsdoccomment@npm:~0.36.1": version: 0.36.1 resolution: "@es-joy/jsdoccomment@npm:0.36.1" @@ -1281,6 +1406,34 @@ __metadata: languageName: node linkType: hard +"@metamask/accounts-controller@workspace:packages/accounts-controller": + version: 0.0.0-use.local + resolution: "@metamask/accounts-controller@workspace:packages/accounts-controller" + dependencies: + "@metamask/auto-changelog": ^3.1.0 + "@metamask/base-controller": ^3.2.0 + "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" + "@metamask/keyring-controller": ^7.1.0 + "@metamask/snaps-controllers": ^1.0.1 + "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1" + "@metamask/snaps-utils": ^1.0.1 + "@metamask/utils": ^6.2.0 + "@types/jest": ^27.4.1 + deepmerge: ^4.2.2 + eth-rpc-errors: ^4.0.2 + ethereumjs-util: ^7.1.5 + immer: ^9.0.6 + jest: ^27.5.1 + nanoid: ^3.1.31 + sinon: ^9.2.4 + ts-jest: ^27.1.4 + typedoc: ^0.22.15 + typedoc-plugin-missing-exports: ^0.22.6 + typescript: ~4.6.3 + uuid: ^9.0.0 + languageName: unknown + linkType: soft + "@metamask/action-utils@npm:^1.0.0": version: 1.0.0 resolution: "@metamask/action-utils@npm:1.0.0" @@ -1327,7 +1480,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/approval-controller@^3.5.0, @metamask/approval-controller@workspace:packages/approval-controller": +"@metamask/approval-controller@^3.0.0, @metamask/approval-controller@^3.5.0, @metamask/approval-controller@workspace:packages/approval-controller": version: 0.0.0-use.local resolution: "@metamask/approval-controller@workspace:packages/approval-controller" dependencies: @@ -1409,7 +1562,7 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@^3.2.0, @metamask/base-controller@workspace:packages/base-controller": +"@metamask/base-controller@^3.0.0, @metamask/base-controller@^3.2.0, @metamask/base-controller@workspace:packages/base-controller": version: 0.0.0-use.local resolution: "@metamask/base-controller@workspace:packages/base-controller" dependencies: @@ -1428,7 +1581,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/browser-passworder@npm:^4.1.0": +"@metamask/browser-passworder@npm:^4.0.2, @metamask/browser-passworder@npm:^4.1.0": version: 4.1.0 resolution: "@metamask/browser-passworder@npm:4.1.0" checksum: f1edb3b75594b8e8d075b3134c9ce6c73573160eb48184ef722b9d96a5763db1e2e9acb166fc5c66c7c82936e134a02a3fb4c0022ca9a948857a30181cb84d7e @@ -1694,7 +1847,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^5.0.0, @metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2": +"@metamask/eth-sig-util@npm:^5.0.0, @metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2, @metamask/eth-sig-util@npm:^5.1.0": version: 5.1.0 resolution: "@metamask/eth-sig-util@npm:5.1.0" dependencies: @@ -1734,6 +1887,22 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-snap-keyring@git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0": + version: 0.1.3 + resolution: "@metamask/eth-snap-keyring@https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" + dependencies: + "@ethereumjs/tx": ^4.1.2 + "@metamask/eth-sig-util": ^5.1.0 + "@metamask/keyring-api": ^0.1.3 + "@metamask/snaps-controllers": ^0.35.2-flask.1 + "@metamask/utils": ^6.1.0 + "@types/uuid": ^9.0.1 + superstruct: ^1.0.3 + uuid: ^9.0.0 + checksum: 7af04f704434c08ce57c5c95ed1c493a4373e490e1a96236365886a78972b4c29d1cd7a0548ce4962968459ab8c2597a2e3b81ae08883f88c6f1e2ad15e6799f + languageName: node + linkType: hard + "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller": version: 0.0.0-use.local resolution: "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller" @@ -1765,7 +1934,50 @@ __metadata: languageName: unknown linkType: soft -"@metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/key-tree@npm:^7.1.1": + version: 7.1.1 + resolution: "@metamask/key-tree@npm:7.1.1" + dependencies: + "@metamask/scure-bip39": ^2.1.0 + "@metamask/utils": ^6.0.1 + "@noble/ed25519": ^1.6.0 + "@noble/hashes": ^1.0.0 + "@noble/secp256k1": ^1.5.5 + "@scure/base": ^1.0.0 + checksum: ddab7917e1214c51508f821a680f8e8819a8c866fd91244e0debf28464331dfcbf9a357e01716fc593cf00a8c71f95fd45ed4ff686ee6abc4c4061aed629537c + languageName: node + linkType: hard + +"@metamask/key-tree@npm:^9.0.0": + version: 9.0.0 + resolution: "@metamask/key-tree@npm:9.0.0" + dependencies: + "@metamask/scure-bip39": ^2.1.0 + "@metamask/utils": ^6.0.1 + "@noble/ed25519": ^1.6.0 + "@noble/hashes": ^1.0.0 + "@noble/secp256k1": ^1.5.5 + "@scure/base": ^1.0.0 + checksum: 5c81f07351ca59b37570d52edcc80d60424630b2a8403ed7149c3343c264878ac5d3fc0584a61635ea7ddda4a789295ded1247846606dc529d8e2fd42f6fc61a + languageName: node + linkType: hard + +"@metamask/keyring-api@npm:^0.1.3": + version: 0.1.3 + resolution: "@metamask/keyring-api@npm:0.1.3" + dependencies: + "@metamask/providers": ^11.0.0 + "@metamask/snaps-controllers": ^0.35.2-flask.1 + "@metamask/snaps-utils": ^0.35.2-flask.1 + "@metamask/utils": ^6.0.1 + "@types/uuid": ^9.0.1 + superstruct: ^1.0.3 + uuid: ^9.0.0 + checksum: 2307b5162dbb66d82f7d8ab8e9f1c3a0ef581b915702187b6b8fa5d8e8533838d0539d6f35853ef4f25096f13c9dbf4505fadff247b73f6d489d9c904015d21c + languageName: node + linkType: hard + +"@metamask/keyring-controller@^7.1.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: @@ -1902,6 +2114,17 @@ __metadata: languageName: unknown linkType: soft +"@metamask/object-multiplex@npm:^1.1.0, @metamask/object-multiplex@npm:^1.2.0": + version: 1.2.0 + resolution: "@metamask/object-multiplex@npm:1.2.0" + dependencies: + end-of-stream: ^1.4.4 + once: ^1.4.0 + readable-stream: ^2.3.3 + checksum: 7c622639cc164c3b780294d790311e4bcb327faf14626717728022e95da5834f32fe4e242d8f4e7d9b8c2b83f0c76450922786b2f6ef50e777bfe119b78bdab7 + languageName: node + linkType: hard + "@metamask/obs-store@npm:^7.0.0": version: 7.0.0 resolution: "@metamask/obs-store@npm:7.0.0" @@ -1922,7 +2145,7 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-controller@workspace:packages/permission-controller": +"@metamask/permission-controller@^4.0.0, @metamask/permission-controller@workspace:packages/permission-controller": version: 0.0.0-use.local resolution: "@metamask/permission-controller@workspace:packages/permission-controller" dependencies: @@ -1971,6 +2194,16 @@ __metadata: languageName: unknown linkType: soft +"@metamask/post-message-stream@npm:^6.1.1, @metamask/post-message-stream@npm:^6.1.2": + version: 6.1.2 + resolution: "@metamask/post-message-stream@npm:6.1.2" + dependencies: + "@metamask/utils": ^5.0.0 + readable-stream: 2.3.3 + checksum: 3591ec9b7fd602806b07cbc0fed5075fb7a347c279c43ef1f25fbdd8634dfcad9ce192ae59457fb76554ef0bc15cbf25cfaa5875aee2d72668a273b7a6852c32 + languageName: node + linkType: hard + "@metamask/preferences-controller@^4.3.0, @metamask/preferences-controller@workspace:packages/preferences-controller": version: 0.0.0-use.local resolution: "@metamask/preferences-controller@workspace:packages/preferences-controller" @@ -1988,6 +2221,45 @@ __metadata: languageName: unknown linkType: soft +"@metamask/providers@npm:^10.2.0, @metamask/providers@npm:^10.2.1": + version: 10.2.1 + resolution: "@metamask/providers@npm:10.2.1" + dependencies: + "@metamask/object-multiplex": ^1.1.0 + "@metamask/safe-event-emitter": ^2.0.0 + "@types/chrome": ^0.0.136 + detect-browser: ^5.2.0 + eth-rpc-errors: ^4.0.2 + extension-port-stream: ^2.0.1 + fast-deep-equal: ^2.0.1 + is-stream: ^2.0.0 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.1 + pump: ^3.0.0 + webextension-polyfill-ts: ^0.25.0 + checksum: e88b2db8c4673cc6a7e47d9f0531df3fac73f05f8e9ff6d02c3420dfb3c7a82335d9c44876f2d472c44eac36d66491d2022be4f39600bee561d5de8ad59c5b07 + languageName: node + linkType: hard + +"@metamask/providers@npm:^11.0.0": + version: 11.1.1 + resolution: "@metamask/providers@npm:11.1.1" + dependencies: + "@metamask/object-multiplex": ^1.1.0 + "@metamask/safe-event-emitter": ^3.0.0 + detect-browser: ^5.2.0 + eth-rpc-errors: ^4.0.2 + extension-port-stream: ^2.0.1 + fast-deep-equal: ^3.1.3 + is-stream: ^2.0.0 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.1 + pump: ^3.0.0 + webextension-polyfill: ^0.10.0 + checksum: ca0339e5219ef43bccdf1debbf610f5f06c95d879c7bba124463c4c6e6dd34fae30c0cdf673985d85d9326a87e94a6ee7ad5d29ef154dca85a0c672fdab152d1 + languageName: node + linkType: hard + "@metamask/rate-limit-controller@workspace:packages/rate-limit-controller": version: 0.0.0-use.local resolution: "@metamask/rate-limit-controller@workspace:packages/rate-limit-controller" @@ -2016,6 +2288,60 @@ __metadata: languageName: node linkType: hard +"@metamask/rpc-methods@npm:^0.35.2-flask.1": + version: 0.35.2-flask.1 + resolution: "@metamask/rpc-methods@npm:0.35.2-flask.1" + dependencies: + "@metamask/key-tree": ^7.1.1 + "@metamask/permission-controller": ^4.0.0 + "@metamask/snaps-ui": ^0.35.2-flask.1 + "@metamask/snaps-utils": ^0.35.2-flask.1 + "@metamask/types": ^1.1.0 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + eth-rpc-errors: ^4.0.3 + nanoid: ^3.1.31 + superstruct: ^1.0.3 + checksum: e949da37c7d3099c07fc3f110310a1f86dbfbdf0d3a84a584b697beb64dc4662ba93e843b7a28a41e41b6500128782a07a9884e14252ac98206b8ed58776034b + languageName: node + linkType: hard + +"@metamask/rpc-methods@npm:^0.37.2-flask.1": + version: 0.37.2-flask.1 + resolution: "@metamask/rpc-methods@npm:0.37.2-flask.1" + dependencies: + "@metamask/key-tree": ^9.0.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/snaps-ui": ^0.37.2-flask.1 + "@metamask/snaps-utils": ^0.37.2-flask.1 + "@metamask/types": ^1.1.0 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + eth-rpc-errors: ^4.0.3 + superstruct: ^1.0.3 + checksum: f5b955c3a7b7c042c27aa5d9464ea5d9135104c20af3315ea00d8941c5c6379c12c82c0988bc5c21409f4dd8b740bb93b387e2ee115c914b434e28101cd9d877 + languageName: node + linkType: hard + +"@metamask/rpc-methods@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/rpc-methods@npm:1.0.1" + dependencies: + "@metamask/browser-passworder": ^4.0.2 + "@metamask/key-tree": ^7.1.1 + "@metamask/permission-controller": ^4.0.0 + "@metamask/snaps-ui": ^1.0.1 + "@metamask/snaps-utils": ^1.0.1 + "@metamask/types": ^1.1.0 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.1.3 + eth-rpc-errors: ^4.0.2 + nanoid: ^3.1.31 + superstruct: ^1.0.3 + checksum: 9d5c91f7af7c66b5f7afe23ce08a1d959bec1c7423063e955ae30ca41ed048e793a4e075b0fd23b9a8e54ff3a3fee018994e8d2fa494adebcc236f0bdd484340 + languageName: node + linkType: hard + "@metamask/safe-event-emitter@npm:^2.0.0": version: 2.0.0 resolution: "@metamask/safe-event-emitter@npm:2.0.0" @@ -2066,6 +2392,326 @@ __metadata: languageName: unknown linkType: soft +"@metamask/snaps-controllers-flask@npm:@metamask/snaps-controllers@0.38.0-flask.1": + version: 0.38.0-flask.1 + resolution: "@metamask/snaps-controllers@npm:0.38.0-flask.1" + dependencies: + "@metamask/approval-controller": ^3.0.0 + "@metamask/base-controller": ^3.0.0 + "@metamask/object-multiplex": ^1.2.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/rpc-methods": ^0.37.2-flask.1 + "@metamask/snaps-execution-environments": ^0.38.0-flask.1 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-utils": ^0.38.0-flask.1 + "@metamask/utils": ^6.0.1 + "@xstate/fsm": ^2.0.0 + concat-stream: ^2.0.0 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + gunzip-maybe: ^1.4.2 + immer: ^9.0.6 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + readable-web-to-node-stream: ^3.0.2 + tar-stream: ^2.2.0 + checksum: 538d2fc5295f094d5852c8385b99c60daf3874628f6627926b10412a309d86a7ee3e5bcb61f8c3fdd40b403d6ea08795aa2d338271924c36c6204d65adfcacd6 + languageName: node + linkType: hard + +"@metamask/snaps-controllers@npm:^0.35.2-flask.1": + version: 0.35.2-flask.1 + resolution: "@metamask/snaps-controllers@npm:0.35.2-flask.1" + dependencies: + "@metamask/approval-controller": ^3.0.0 + "@metamask/base-controller": ^3.0.0 + "@metamask/object-multiplex": ^1.2.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/rpc-methods": ^0.35.2-flask.1 + "@metamask/snaps-execution-environments": ^0.35.2-flask.1 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-utils": ^0.35.2-flask.1 + "@metamask/utils": ^6.0.1 + "@xstate/fsm": ^2.0.0 + concat-stream: ^2.0.0 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + gunzip-maybe: ^1.4.2 + immer: ^9.0.6 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + readable-web-to-node-stream: ^3.0.2 + tar-stream: ^2.2.0 + checksum: 8b20ed3176da02c2bb5be642db83912b484dd85a0e9cb36b2d4bca6a0058cd28d578c91c595f20bcc084c73e8802b289191ac41972bc17ae921010e1b05fd307 + languageName: node + linkType: hard + +"@metamask/snaps-controllers@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/snaps-controllers@npm:1.0.1" + dependencies: + "@metamask/approval-controller": ^3.0.0 + "@metamask/base-controller": ^3.0.0 + "@metamask/object-multiplex": ^1.2.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/rpc-methods": ^1.0.1 + "@metamask/snaps-execution-environments": ^1.0.1 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-utils": ^1.0.1 + "@metamask/utils": ^6.0.1 + "@xstate/fsm": ^2.0.0 + concat-stream: ^2.0.0 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.2 + gunzip-maybe: ^1.4.2 + immer: ^9.0.6 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + readable-web-to-node-stream: ^3.0.2 + tar-stream: ^2.2.0 + checksum: 31450c79705786431e76ad20e3ee35294f997a049337729301aac50f7b27aa1b03e617549b62d3eff067b93e0ab51ade883a4f6e9d67a56ff0bba1f2f0b0fbc4 + languageName: node + linkType: hard + +"@metamask/snaps-execution-environments@npm:^0.35.2-flask.1": + version: 0.35.2-flask.1 + resolution: "@metamask/snaps-execution-environments@npm:0.35.2-flask.1" + dependencies: + "@metamask/object-multiplex": ^1.2.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/providers": ^11.0.0 + "@metamask/rpc-methods": ^0.35.2-flask.1 + "@metamask/snaps-utils": ^0.35.2-flask.1 + "@metamask/utils": ^6.0.1 + eth-rpc-errors: ^4.0.3 + json-rpc-engine: ^6.1.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + ses: ^0.18.1 + stream-browserify: ^3.0.0 + superstruct: ^1.0.3 + checksum: b112a25a5cd5574fd84dcc7a62b9076d59d0182d52c37645d44477efcb67d18a4dd40bef3778dcbd67009fe3b261ffe0e247def112737a16bac5e86bf16081e7 + languageName: node + linkType: hard + +"@metamask/snaps-execution-environments@npm:^0.38.0-flask.1": + version: 0.38.0-flask.1 + resolution: "@metamask/snaps-execution-environments@npm:0.38.0-flask.1" + dependencies: + "@metamask/object-multiplex": ^1.2.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/providers": ^11.0.0 + "@metamask/rpc-methods": ^0.37.2-flask.1 + "@metamask/snaps-utils": ^0.38.0-flask.1 + "@metamask/utils": ^6.0.1 + eth-rpc-errors: ^4.0.3 + json-rpc-engine: ^6.1.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + ses: ^0.18.1 + stream-browserify: ^3.0.0 + superstruct: ^1.0.3 + checksum: 725163d03c3c17d0a67ad5829adf69da8bcd2d3890efd984ff5954a19aeeeb4e024a96810d9ae9d5f095f1e39965376f62d41637ff61c59519f46c093f05b0f9 + languageName: node + linkType: hard + +"@metamask/snaps-execution-environments@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/snaps-execution-environments@npm:1.0.1" + dependencies: + "@metamask/object-multiplex": ^1.2.0 + "@metamask/post-message-stream": ^6.1.1 + "@metamask/providers": ^10.2.0 + "@metamask/rpc-methods": ^1.0.1 + "@metamask/snaps-utils": ^1.0.1 + "@metamask/utils": ^6.0.1 + eth-rpc-errors: ^4.0.3 + json-rpc-engine: ^6.1.0 + pump: ^3.0.0 + ses: ^0.18.1 + stream-browserify: ^3.0.0 + superstruct: ^1.0.3 + checksum: 46645edaa0ca6d6188c6c795d1229fb50357d5d6b25d73155de6867d06b9b74b02be76bcd3a4a523b4c0c16a9210c9636fa0204bba347f53c88e4d09ae1f793f + languageName: node + linkType: hard + +"@metamask/snaps-registry@npm:^1.2.1": + version: 1.2.1 + resolution: "@metamask/snaps-registry@npm:1.2.1" + dependencies: + "@metamask/utils": ^6.0.0 + "@noble/secp256k1": ^1.7.1 + superstruct: ^1.0.3 + checksum: d2d5b743a8b55b6f685708b2b694534585329585c6d94819328e270fd77c68c0bede88b866821db9c22a667eca1f4961ed860d83b438cf009bd1c4df6e75b78a + languageName: node + linkType: hard + +"@metamask/snaps-ui@npm:^0.35.2-flask.1": + version: 0.35.2-flask.1 + resolution: "@metamask/snaps-ui@npm:0.35.2-flask.1" + dependencies: + "@metamask/utils": ^6.0.1 + superstruct: ^1.0.3 + checksum: fb272068e04a9d4e0458aecef4df4091a9d9d9c0c3580d40c211a710566eb5610252456f0eb99533379d6d919c0c4156dda1ae6233693723e9227e54a4c5ddbf + languageName: node + linkType: hard + +"@metamask/snaps-ui@npm:^0.37.2-flask.1": + version: 0.37.2-flask.1 + resolution: "@metamask/snaps-ui@npm:0.37.2-flask.1" + dependencies: + "@metamask/utils": ^6.0.1 + superstruct: ^1.0.3 + checksum: a52a887411d689a7a0b017b3741c76819567416c7943ca3c71f5b5f8b623605689f370f70caa8a22531d711d10738c913b0d561fcda09a92c38e288d115e6bac + languageName: node + linkType: hard + +"@metamask/snaps-ui@npm:^0.37.3-flask.1": + version: 0.37.3-flask.1 + resolution: "@metamask/snaps-ui@npm:0.37.3-flask.1" + dependencies: + "@metamask/utils": ^6.0.1 + superstruct: ^1.0.3 + checksum: 0b93f6edeca18afc799f16be8b4e4d758800ea0d68298c70492dc85f74ec9f79c2aa32749f6725bb83a4896fdb1ec95a7d19a10a617484e0a7574ddad7969bd3 + languageName: node + linkType: hard + +"@metamask/snaps-ui@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/snaps-ui@npm:1.0.1" + dependencies: + "@metamask/utils": ^6.0.1 + superstruct: ^1.0.3 + checksum: 71719b47a79d2ff23f0e9f1d2a1a8afd42f4f94891437056241a49090d245ccd086eac5d8fdf7719aaaa3f6bbc16b30f5566ad7bcecaa7b70e00ea638387aaa0 + languageName: node + linkType: hard + +"@metamask/snaps-utils@npm:^0.35.2-flask.1": + version: 0.35.2-flask.1 + resolution: "@metamask/snaps-utils@npm:0.35.2-flask.1" + dependencies: + "@babel/core": ^7.20.12 + "@babel/types": ^7.18.7 + "@metamask/base-controller": ^3.0.0 + "@metamask/key-tree": ^7.1.1 + "@metamask/permission-controller": ^4.0.0 + "@metamask/providers": ^11.0.0 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-ui": ^0.35.2-flask.1 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + "@scure/base": ^1.1.1 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + fast-deep-equal: ^3.1.3 + fast-json-stable-stringify: ^2.1.0 + is-svg: ^4.4.0 + rfdc: ^1.3.0 + semver: ^7.3.7 + ses: ^0.18.1 + superstruct: ^1.0.3 + validate-npm-package-name: ^5.0.0 + checksum: 4a22bf42512e2bdc944042cb05ef8b8f733ce902c0f374f7d36e22344700a3d0674a48ddacbe096a7e253c24f7ba05cff7ff30a05d3e48a840b63cbeeb46f0ce + languageName: node + linkType: hard + +"@metamask/snaps-utils@npm:^0.37.2-flask.1": + version: 0.37.2-flask.1 + resolution: "@metamask/snaps-utils@npm:0.37.2-flask.1" + dependencies: + "@babel/core": ^7.20.12 + "@babel/types": ^7.18.7 + "@metamask/base-controller": ^3.0.0 + "@metamask/key-tree": ^9.0.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/providers": ^11.0.0 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-ui": ^0.37.2-flask.1 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + "@scure/base": ^1.1.1 + chalk: ^4.1.2 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + fast-deep-equal: ^3.1.3 + fast-json-stable-stringify: ^2.1.0 + is-svg: ^4.4.0 + rfdc: ^1.3.0 + semver: ^7.3.7 + ses: ^0.18.1 + superstruct: ^1.0.3 + validate-npm-package-name: ^5.0.0 + checksum: eeb31013a8af39b5488fdb8a6a72e11c54274e423e0f2560fd5cf4ea0278e278aff60be070c1e5b847d84220f15338e01c4725ce7ebb5f3fc614553ae4235f44 + languageName: node + linkType: hard + +"@metamask/snaps-utils@npm:^0.38.0-flask.1": + version: 0.38.0-flask.1 + resolution: "@metamask/snaps-utils@npm:0.38.0-flask.1" + dependencies: + "@babel/core": ^7.20.12 + "@babel/types": ^7.18.7 + "@metamask/base-controller": ^3.0.0 + "@metamask/key-tree": ^9.0.0 + "@metamask/permission-controller": ^4.0.0 + "@metamask/providers": ^11.0.0 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-ui": ^0.37.3-flask.1 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + "@scure/base": ^1.1.1 + chalk: ^4.1.2 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + fast-deep-equal: ^3.1.3 + fast-json-stable-stringify: ^2.1.0 + is-svg: ^4.4.0 + rfdc: ^1.3.0 + semver: ^7.5.4 + ses: ^0.18.1 + superstruct: ^1.0.3 + validate-npm-package-name: ^5.0.0 + checksum: 9b79feabcf3a99f0faa53c87711e0de155807d49dd3a9117933b9636d529fa3f3449bd563535f056fc7cbb3eaffcd9e9703b02985bdb6cfdc090d096d76dad8e + languageName: node + linkType: hard + +"@metamask/snaps-utils@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/snaps-utils@npm:1.0.1" + dependencies: + "@babel/core": ^7.18.6 + "@babel/types": ^7.18.7 + "@metamask/base-controller": ^3.0.0 + "@metamask/key-tree": ^7.1.1 + "@metamask/permission-controller": ^4.0.0 + "@metamask/providers": ^10.2.1 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-ui": ^1.0.1 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.1.3 + "@scure/base": ^1.1.1 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + fast-deep-equal: ^3.1.3 + fast-json-stable-stringify: ^2.1.0 + rfdc: ^1.3.0 + semver: ^7.5.4 + ses: ^0.18.7 + superstruct: ^1.0.3 + validate-npm-package-name: ^5.0.0 + checksum: 4abb2aabd5b6af314338d812f6029cbdecb0953bcaaf8df16e4b3ad65106d934d0c076c4587df032a1adb08409c8ea246f309aa8c2f94e4453e96f8afbb143f3 + languageName: node + linkType: hard + "@metamask/swappable-obj-proxy@npm:^2.1.0": version: 2.1.0 resolution: "@metamask/swappable-obj-proxy@npm:2.1.0" @@ -2110,6 +2756,13 @@ __metadata: languageName: unknown linkType: soft +"@metamask/types@npm:^1.1.0": + version: 1.1.0 + resolution: "@metamask/types@npm:1.1.0" + checksum: 500e8c076a2b0a6d688c8c465101256f090547d99c9a5585efe3d1db7a494b6b2712b127043fb316912caffc4fff78976b9a7780780abb68305e8a3bf812689c + languageName: node + linkType: hard + "@metamask/utils@npm:^4.0.0": version: 4.0.0 resolution: "@metamask/utils@npm:4.0.0" @@ -2135,7 +2788,7 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^6.1.0, @metamask/utils@npm:^6.2.0": +"@metamask/utils@npm:^6.0.0, @metamask/utils@npm:^6.0.1, @metamask/utils@npm:^6.1.0, @metamask/utils@npm:^6.2.0": version: 6.2.0 resolution: "@metamask/utils@npm:6.2.0" dependencies: @@ -2173,6 +2826,13 @@ __metadata: languageName: node linkType: hard +"@noble/ed25519@npm:^1.6.0": + version: 1.7.3 + resolution: "@noble/ed25519@npm:1.7.3" + checksum: 45169927d51de513e47bbeebff3a603433c4ac7579e1b8c5034c380a0afedbe85e6959be3d69584a7a5ed6828d638f8f28879003b9bb2fb5f22d8aa2d88fd5fe + languageName: node + linkType: hard + "@noble/hashes@npm:1.2.0, @noble/hashes@npm:~1.2.0": version: 1.2.0 resolution: "@noble/hashes@npm:1.2.0" @@ -2187,6 +2847,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.1.3": + version: 1.3.2 + resolution: "@noble/hashes@npm:1.3.2" + checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 + languageName: node + linkType: hard + "@noble/hashes@npm:~1.1.1": version: 1.1.5 resolution: "@noble/hashes@npm:1.1.5" @@ -2194,7 +2861,7 @@ __metadata: languageName: node linkType: hard -"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:~1.7.0": +"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.5.5, @noble/secp256k1@npm:^1.7.1, @noble/secp256k1@npm:~1.7.0": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" checksum: d2301f1f7690368d8409a3152450458f27e54df47e3f917292de3de82c298770890c2de7c967d237eff9c95b70af485389a9695f73eb05a43e2bd562d18b18cb @@ -2287,6 +2954,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1": + version: 1.1.2 + resolution: "@scure/base@npm:1.1.2" + checksum: f666b09dbd62ecb5fe6d0e7a629c8a86a972a47dc4f4555ebbbd7b09782b10a5f894fed9c3b8c74fd683b1588c064df079a44e9f695c075ccd98c30a8d3e91f7 + languageName: node + linkType: hard + "@scure/base@npm:~1.1.0": version: 1.1.1 resolution: "@scure/base@npm:1.1.1" @@ -2489,6 +3163,16 @@ __metadata: languageName: node linkType: hard +"@types/chrome@npm:^0.0.136": + version: 0.0.136 + resolution: "@types/chrome@npm:0.0.136" + dependencies: + "@types/filesystem": "*" + "@types/har-format": "*" + checksum: af96fdc79fb019d827fdb6269f831921f8f36215ee05a2624436dd2ad4d84d7be12333cc6f54912fb8bae0ca49cbfde5a78de94723bfbd20d309d2e71e274a1b + languageName: node + linkType: hard + "@types/debug@npm:^4.1.7": version: 4.1.8 resolution: "@types/debug@npm:4.1.8" @@ -2505,6 +3189,22 @@ __metadata: languageName: node linkType: hard +"@types/filesystem@npm:*": + version: 0.0.32 + resolution: "@types/filesystem@npm:0.0.32" + dependencies: + "@types/filewriter": "*" + checksum: 4b9079d200a3b241722b90e1c5806c4b32c4dac87d42a1c7ef76a2c0dafdbe7d5f1a379b873ad5de73622b44de6599e1522908f67b938d54e785bd1c36e302a0 + languageName: node + linkType: hard + +"@types/filewriter@npm:*": + version: 0.0.29 + resolution: "@types/filewriter@npm:0.0.29" + checksum: 0c58aa875c2c245be7dbc42b20212f3203e13d11ec013a4a5cd0febf0e8b87214be5882c05ff9d7bdf0398f145a4fdbc24b7e6cf7b094e134a3b4c7a0598502f + languageName: node + linkType: hard + "@types/graceful-fs@npm:^4.1.2": version: 4.1.6 resolution: "@types/graceful-fs@npm:4.1.6" @@ -2514,6 +3214,13 @@ __metadata: languageName: node linkType: hard +"@types/har-format@npm:*": + version: 1.2.12 + resolution: "@types/har-format@npm:1.2.12" + checksum: f52c1617859983437f4c06d02fda052500c0e5b2e8ffd5deb0172f1adc2222d4569db2df3ca2b99007c2572dbae01ba7a042b51e7b6ec6a4a44d53eea8230895 + languageName: node + linkType: hard + "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.4 resolution: "@types/istanbul-lib-coverage@npm:2.0.4" @@ -2679,6 +3386,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^9.0.1": + version: 9.0.2 + resolution: "@types/uuid@npm:9.0.2" + checksum: 1754bcf3444e1e3aeadd6e774fc328eb53bc956665e2e8fb6ec127aa8e1f43d9a224c3d22a9a6233dca8dd81a12dc7fed4d84b8876dd5ec82d40f574f7ff8b68 + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -2825,6 +3539,13 @@ __metadata: languageName: node linkType: hard +"@xstate/fsm@npm:^2.0.0": + version: 2.1.0 + resolution: "@xstate/fsm@npm:2.1.0" + checksum: 593aa73866d89472f15727872f41cf305388f9b1a34e26e22bff0a062e4dedc8d8615a2c805d76114dc418a635ea0ccfc1d17dd44774e6b7ec1c6fb4a6d1723f + languageName: node + linkType: hard + "abab@npm:^2.0.3, abab@npm:^2.0.5": version: 2.0.6 resolution: "abab@npm:2.0.6" @@ -3323,6 +4044,17 @@ __metadata: languageName: node linkType: hard +"bl@npm:^4.0.3": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: ^5.5.0 + inherits: ^2.0.4 + readable-stream: ^3.4.0 + checksum: 9e8521fa7e83aa9427c6f8ccdcba6e8167ef30cc9a22df26effcc5ab682ef91d2cbc23a239f945d099289e4bbcfae7a192e9c28c84c6202e710a0dfec3722662 + languageName: node + linkType: hard + "blakejs@npm:^1.1.0": version: 1.2.1 resolution: "blakejs@npm:1.2.1" @@ -3416,6 +4148,15 @@ __metadata: languageName: node linkType: hard +"browserify-zlib@npm:^0.1.4": + version: 0.1.4 + resolution: "browserify-zlib@npm:0.1.4" + dependencies: + pako: ~0.2.0 + checksum: abee4cb4349e8a21391fd874564f41b113fe691372913980e6fa06a777e4ea2aad4e942af14ab99bce190d5ac8f5328201432f4ef0eae48c6d02208bc212976f + languageName: node + linkType: hard + "browserslist@npm:^4.21.9": version: 4.21.9 resolution: "browserslist@npm:4.21.9" @@ -3482,7 +4223,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.1.0": +"buffer@npm:^5.1.0, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -3492,7 +4233,7 @@ __metadata: languageName: node linkType: hard -"builtins@npm:^5.0.1": +"builtins@npm:^5.0.0, builtins@npm:^5.0.1": version: 5.0.1 resolution: "builtins@npm:5.0.1" dependencies: @@ -3575,7 +4316,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0": +"chalk@npm:^2.0.0, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -3586,7 +4327,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0": +"chalk@npm:^4.0.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -3755,6 +4496,18 @@ __metadata: languageName: node linkType: hard +"concat-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "concat-stream@npm:2.0.0" + dependencies: + buffer-from: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^3.0.2 + typedarray: ^0.0.6 + checksum: d7f75d48f0ecd356c1545d87e22f57b488172811b1181d96021c7c4b14ab8855f5313280263dca44bb06e5222f274d047da3e290a38841ef87b59719bde967c7 + languageName: node + linkType: hard + "console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -3835,6 +4588,15 @@ __metadata: languageName: node linkType: hard +"cron-parser@npm:^4.5.0": + version: 4.9.0 + resolution: "cron-parser@npm:4.9.0" + dependencies: + luxon: ^3.2.1 + checksum: 3cf248fc5cae6c19ec7124962b1cd84b76f02b9bc4f58976b3bd07624db3ef10aaf1548efcc2d2dcdab0dad4f12029d640a55ecce05ea5e1596af9db585502cf + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -3996,6 +4758,13 @@ __metadata: languageName: node linkType: hard +"detect-browser@npm:^5.2.0": + version: 5.3.0 + resolution: "detect-browser@npm:5.3.0" + checksum: dd6e08d55da1d9e0f22510ac79872078ae03d9dfa13c5e66c96baedc1c86567345a88f96949161f6be8f3e0fafa93bf179bdb1cd311b14f5f163112fcc70ab49 + languageName: node + linkType: hard + "detect-indent@npm:^7.0.1": version: 7.0.1 resolution: "detect-indent@npm:7.0.1" @@ -4081,6 +4850,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^3.5.0, duplexify@npm:^3.6.0": + version: 3.7.1 + resolution: "duplexify@npm:3.7.1" + dependencies: + end-of-stream: ^1.0.0 + inherits: ^2.0.1 + readable-stream: ^2.0.0 + stream-shift: ^1.0.0 + checksum: 3c2ed2223d956a5da713dae12ba8295acb61d9acd966ccbba938090d04f4574ca4dca75cca089b5077c2d7e66101f32e6ea9b36a78ca213eff574e7a8b8accf2 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -4140,6 +4921,15 @@ __metadata: languageName: node linkType: hard +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1, end-of-stream@npm:^1.4.4": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: ^1.4.0 + checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -4730,7 +5520,7 @@ __metadata: languageName: node linkType: hard -"ethereumjs-util@npm:^7.0.10, ethereumjs-util@npm:^7.0.8, ethereumjs-util@npm:^7.1.2": +"ethereumjs-util@npm:^7.0.10, ethereumjs-util@npm:^7.0.8, ethereumjs-util@npm:^7.1.2, ethereumjs-util@npm:^7.1.5": version: 7.1.5 resolution: "ethereumjs-util@npm:7.1.5" dependencies: @@ -5002,6 +5792,22 @@ __metadata: languageName: node linkType: hard +"extension-port-stream@npm:^2.0.1": + version: 2.1.1 + resolution: "extension-port-stream@npm:2.1.1" + dependencies: + webextension-polyfill: ">=0.10.0 <1.0" + checksum: aee8bbeb2ed6f69a62f58a89580e0e9002dadb11062edbaedb7bb04cfc5a5e0b0d3980bfeaa1c3ee7e08dec7e5fac26e25497fc2f82000db7653442bd5eca157 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^2.0.1": + version: 2.0.1 + resolution: "fast-deep-equal@npm:2.0.1" + checksum: b701835a87985e0ec4925bdf1f0c1e7eb56309b5d12d534d5b4b69d95a54d65bb16861c081781ead55f73f12d6c60ba668713391ee7fbf6b0567026f579b7b0b + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -5029,7 +5835,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0": +"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: b191531e36c607977e5b1c47811158733c34ccb3bfde92c44798929e9b4154884378536d26ad90dfecd32e1ffc09c545d23535ad91b3161a27ddbb8ebe0cbecb @@ -5050,6 +5856,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:^4.1.3": + version: 4.2.7 + resolution: "fast-xml-parser@npm:4.2.7" + dependencies: + strnum: ^1.0.5 + bin: + fxparser: src/cli/cli.js + checksum: d8b0c9e04756f6c43fa0399428f30149acadae21350e42e26e8fe98e24e6afa6b9b00aa554453795036b00e9fee974a1b556fe2ba18be391d51a9bf1ab790e7c + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.15.0 resolution: "fastq@npm:1.15.0" @@ -5153,6 +5970,13 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -5438,6 +6262,22 @@ __metadata: languageName: node linkType: hard +"gunzip-maybe@npm:^1.4.2": + version: 1.4.2 + resolution: "gunzip-maybe@npm:1.4.2" + dependencies: + browserify-zlib: ^0.1.4 + is-deflate: ^1.0.0 + is-gzip: ^1.0.0 + peek-stream: ^1.1.0 + pumpify: ^1.3.3 + through2: ^2.0.3 + bin: + gunzip-maybe: bin.js + checksum: bc4d4977c24a2860238df271de75d53dd72a359d19f1248d1c613807dc221d3b8ae09624e3085c8106663e3e1b59db62a85b261d1138c2cc24efad9df577d4e1 + languageName: node + linkType: hard + "has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": version: 1.0.2 resolution: "has-bigints@npm:1.0.2" @@ -5723,7 +6563,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -5827,6 +6667,13 @@ __metadata: languageName: node linkType: hard +"is-deflate@npm:^1.0.0": + version: 1.0.0 + resolution: "is-deflate@npm:1.0.0" + checksum: c2f9f2d3db79ac50c5586697d1e69a55282a2b0cc5e437b3c470dd47f24e40b6216dcd7e024511e21381607bf57afa019343e3bd0e08a119032818b596004262 + languageName: node + linkType: hard + "is-docker@npm:^2.0.0": version: 2.2.1 resolution: "is-docker@npm:2.2.1" @@ -5891,6 +6738,13 @@ __metadata: languageName: node linkType: hard +"is-gzip@npm:^1.0.0": + version: 1.0.0 + resolution: "is-gzip@npm:1.0.0" + checksum: 0d28931c1f445fa29c900cf9f48e06e9d1d477a3bf7bd7332e7ce68f1333ccd8cb381de2f0f62a9a262d9c0912608a9a71b4a40e788e201b3dbd67072bb20d86 + languageName: node + linkType: hard + "is-hex-prefixed@npm:1.0.0": version: 1.0.0 resolution: "is-hex-prefixed@npm:1.0.0" @@ -6012,6 +6866,15 @@ __metadata: languageName: node linkType: hard +"is-svg@npm:^4.4.0": + version: 4.4.0 + resolution: "is-svg@npm:4.4.0" + dependencies: + fast-xml-parser: ^4.1.3 + checksum: cd5a0ba1af653e4897721913b0b80de968fa5b19eb1a592412f4672d3a1203935d183c2a9dbf61d68023739ee43d3761ea795ae1a9f618c6098a9e89eacdd256 + languageName: node + linkType: hard + "is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": version: 1.0.4 resolution: "is-symbol@npm:1.0.4" @@ -6869,6 +7732,16 @@ __metadata: languageName: node linkType: hard +"json-rpc-middleware-stream@npm:^4.2.0, json-rpc-middleware-stream@npm:^4.2.1": + version: 4.2.2 + resolution: "json-rpc-middleware-stream@npm:4.2.2" + dependencies: + "@metamask/safe-event-emitter": ^3.0.0 + readable-stream: ^2.3.3 + checksum: 01ff3a23b501fde5c2abb8c3b4d100c4fd430b41cf5e7750235f860a02d5823f8a43b0e81150c1d3bb196737f2273af1c7a50ff179e95e3d59fb7fe172249de3 + languageName: node + linkType: hard + "json-rpc-random-id@npm:^1.0.0, json-rpc-random-id@npm:^1.0.1": version: 1.0.1 resolution: "json-rpc-random-id@npm:1.0.1" @@ -6897,7 +7770,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:2.x, json5@npm:^2.2.2": +"json5@npm:2.x, json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -7066,6 +7939,13 @@ __metadata: languageName: node linkType: hard +"luxon@npm:^3.2.1": + version: 3.4.2 + resolution: "luxon@npm:3.4.2" + checksum: efefdfaea90f4c8e502db8eb255385e6dac6733b6a9e1372eaf1a866cc1118363e235a3f6df505b837abc8bbcdfd8f0718a8de387b80cef9ee624d8791ca0844 + languageName: node + linkType: hard + "make-dir@npm:^3.0.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -7618,7 +8498,7 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0": +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -7723,6 +8603,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~0.2.0": + version: 0.2.9 + resolution: "pako@npm:0.2.9" + checksum: 055f9487cd57fbb78df84315873bbdd089ba286f3499daed47d2effdc6253e981f5db6898c23486de76d4a781559f890d643bd3a49f70f1b4a18019c98aa5125 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -7825,6 +8712,17 @@ __metadata: languageName: node linkType: hard +"peek-stream@npm:^1.1.0": + version: 1.1.3 + resolution: "peek-stream@npm:1.1.3" + dependencies: + buffer-from: ^1.0.0 + duplexify: ^3.5.0 + through2: ^2.0.3 + checksum: a0e09d6d1a8a01158a3334f20d6b1cdd91747eba24eb06a1d742eefb620385593121a76d4378cc81f77cdce6a66df0575a41041b1189c510254aec91878afc99 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -7931,6 +8829,13 @@ __metadata: languageName: node linkType: hard +"process-nextick-args@npm:~1.0.6": + version: 1.0.7 + resolution: "process-nextick-args@npm:1.0.7" + checksum: 41224fbc803ac6c96907461d4dfc20942efa3ca75f2d521bcf7cf0e89f8dec127fb3fb5d76746b8fb468a232ea02d84824fae08e027aec185fd29049c66d49f8 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -7982,6 +8887,37 @@ __metadata: languageName: node linkType: hard +"pump@npm:^2.0.0": + version: 2.0.1 + resolution: "pump@npm:2.0.1" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: e9f26a17be00810bff37ad0171edb35f58b242487b0444f92fb7d78bc7d61442fa9b9c5bd93a43fd8fd8ddd3cc75f1221f5e04c790f42907e5baab7cf5e2b931 + languageName: node + linkType: hard + +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 + languageName: node + linkType: hard + +"pumpify@npm:^1.3.3": + version: 1.5.1 + resolution: "pumpify@npm:1.5.1" + dependencies: + duplexify: ^3.6.0 + inherits: ^2.0.3 + pump: ^2.0.0 + checksum: 26ca412ec8d665bd0d5e185c1b8f627728eff603440d75d22a58e421e3c66eaf86ec6fc6a6efc54808ecef65979279fa8e99b109a23ec1fa8d79f37e6978c9bd + languageName: node + linkType: hard + "punycode@npm:2.1.0": version: 2.1.0 resolution: "punycode@npm:2.1.0" @@ -8050,18 +8986,22 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.6.0": - version: 3.6.2 - resolution: "readable-stream@npm:3.6.2" +"readable-stream@npm:2.3.3": + version: 2.3.3 + resolution: "readable-stream@npm:2.3.3" dependencies: - inherits: ^2.0.3 - string_decoder: ^1.1.1 - util-deprecate: ^1.0.1 - checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d + core-util-is: ~1.0.0 + inherits: ~2.0.3 + isarray: ~1.0.0 + process-nextick-args: ~1.0.6 + safe-buffer: ~5.1.1 + string_decoder: ~1.0.3 + util-deprecate: ~1.0.1 + checksum: 76f9863065d7edc14abd78e68784048487e83a4b6908336ba3eacb5e9544d642ad60836f91fab16e1dc6ad9e493dfe6c2e5b65f370ec65454d415efa50361a76 languageName: node linkType: hard -"readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.3.3, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -8076,6 +9016,26 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d + languageName: node + linkType: hard + +"readable-web-to-node-stream@npm:^3.0.2": + version: 3.0.2 + resolution: "readable-web-to-node-stream@npm:3.0.2" + dependencies: + readable-stream: ^3.6.0 + checksum: 8c56cc62c68513425ddfa721954875b382768f83fa20e6b31e365ee00cbe7a3d6296f66f7f1107b16cd3416d33aa9f1680475376400d62a081a88f81f0ea7f9c + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.11.0": version: 0.11.1 resolution: "regenerator-runtime@npm:0.11.1" @@ -8185,6 +9145,13 @@ __metadata: languageName: node linkType: hard +"rfdc@npm:^1.3.0": + version: 1.3.0 + resolution: "rfdc@npm:1.3.0" + checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 + languageName: node + linkType: hard + "rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" @@ -8323,7 +9290,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.x, semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0": +"semver@npm:7.x, semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.0, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -8343,6 +9310,15 @@ __metadata: languageName: node linkType: hard +"ses@npm:^0.18.1, ses@npm:^0.18.7": + version: 0.18.7 + resolution: "ses@npm:0.18.7" + dependencies: + "@endo/env-options": ^0.1.3 + checksum: 75ac014771d9bc1f747193c6d0f9e7d2d7700a10311ba8d805d9bc78d4c20d4ef40537f0535b1ea6abf06babf67e70f8bd37b2ad68ad54992a0c5ce842181c87 + languageName: node + linkType: hard + "set-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" @@ -8604,6 +9580,23 @@ __metadata: languageName: node linkType: hard +"stream-browserify@npm:^3.0.0": + version: 3.0.0 + resolution: "stream-browserify@npm:3.0.0" + dependencies: + inherits: ~2.0.4 + readable-stream: ^3.5.0 + checksum: 4c47ef64d6f03815a9ca3874e2319805e8e8a85f3550776c47ce523b6f4c6cd57f40e46ec6a9ab8ad260fde61863c2718f250d3bedb3fe9052444eb9abfd9921 + languageName: node + linkType: hard + +"stream-shift@npm:^1.0.0": + version: 1.0.1 + resolution: "stream-shift@npm:1.0.1" + checksum: 59b82b44b29ec3699b5519a49b3cedcc6db58c72fb40c04e005525dfdcab1c75c4e0c180b923c380f204bed78211b9bad8faecc7b93dece4d004c3f6ec75737b + languageName: node + linkType: hard + "string-length@npm:^4.0.1": version: 4.0.2 resolution: "string-length@npm:4.0.2" @@ -8678,6 +9671,15 @@ __metadata: languageName: node linkType: hard +"string_decoder@npm:~1.0.3": + version: 1.0.3 + resolution: "string_decoder@npm:1.0.3" + dependencies: + safe-buffer: ~5.1.0 + checksum: 57ef02a148fd1ff2f20fe1accd944505ed3703e78bb28d302d940b2ad3dfb469508f79dcd0275ba1960d9675aa206452f76b2416059a6d0b0200bd7e9f552cdb + languageName: node + linkType: hard + "string_decoder@npm:~1.1.1": version: 1.1.1 resolution: "string_decoder@npm:1.1.1" @@ -8749,6 +9751,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 651b2031db5da1bf4a77fdd2f116a8ac8055157c5420f5569f64879133825915ad461513e7202a16d7fec63c54fd822410d0962f8ca12385c4334891b9ae6dd2 + languageName: node + linkType: hard + "superstruct@npm:^1.0.3": version: 1.0.3 resolution: "superstruct@npm:1.0.3" @@ -8817,6 +9826,19 @@ __metadata: languageName: node linkType: hard +"tar-stream@npm:^2.2.0": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: ^4.0.3 + end-of-stream: ^1.4.1 + fs-constants: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^3.1.1 + checksum: 699831a8b97666ef50021c767f84924cfee21c142c2eb0e79c63254e140e6408d6d55a065a2992548e72b06de39237ef2b802b99e3ece93ca3904a37622a66f3 + languageName: node + linkType: hard + "tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.15 resolution: "tar@npm:6.1.15" @@ -9142,6 +10164,13 @@ __metadata: languageName: node linkType: hard +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 33b39f3d0e8463985eeaeeacc3cb2e28bc3dfaf2a5ed219628c0b629d5d7b810b0eb2165f9f607c34871d5daa92ba1dc69f49051cf7d578b4cbd26c340b9d1b1 + languageName: node + linkType: hard + "typedoc-plugin-missing-exports@npm:^0.22.6": version: 0.22.6 resolution: "typedoc-plugin-missing-exports@npm:0.22.6" @@ -9310,6 +10339,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.0": + version: 9.0.0 + resolution: "uuid@npm:9.0.0" + bin: + uuid: dist/bin/uuid + checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -9328,6 +10366,15 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-name@npm:^5.0.0": + version: 5.0.0 + resolution: "validate-npm-package-name@npm:5.0.0" + dependencies: + builtins: ^5.0.0 + checksum: 5342a994986199b3c28e53a8452a14b2bb5085727691ea7aa0d284a6606b127c371e0925ae99b3f1ef7cc7d2c9de75f52eb61a3d1cc45e39bca1e3a9444cbb4e + languageName: node + linkType: hard + "vscode-oniguruma@npm:^1.6.1": version: 1.7.0 resolution: "vscode-oniguruma@npm:1.7.0" @@ -9369,6 +10416,29 @@ __metadata: languageName: node linkType: hard +"webextension-polyfill-ts@npm:^0.25.0": + version: 0.25.0 + resolution: "webextension-polyfill-ts@npm:0.25.0" + dependencies: + webextension-polyfill: ^0.7.0 + checksum: c4dc82c86e34cea717a26af549f2822d63e92af52632f5e909ea13b5e7bd9d6110781f10313e36ada2b54c770ebca018bc3784756d12ba3b0b623d285f1a14a7 + languageName: node + linkType: hard + +"webextension-polyfill@npm:>=0.10.0 <1.0, webextension-polyfill@npm:^0.10.0": + version: 0.10.0 + resolution: "webextension-polyfill@npm:0.10.0" + checksum: 4a59036bda571360c2c0b2fb03fe1dc244f233946bcf9a6766f677956c40fd14d270aaa69cdba95e4ac521014afbe4008bfa5959d0ac39f91c990eb206587f91 + languageName: node + linkType: hard + +"webextension-polyfill@npm:^0.7.0": + version: 0.7.0 + resolution: "webextension-polyfill@npm:0.7.0" + checksum: fb738a5de07feb593875e02f25c3ab4276c8736118929556c8d4bdf965bb0f11c96ea263cd397b9b21259e8faf2dce2eaaa42ce08c922d96de7adb5896ec7d10 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" From 2b66b11e4cd08926baf4ada857603cf4b7b91ef2 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 28 Aug 2023 20:27:10 +0800 Subject: [PATCH 02/54] fix: add eth-snap-keyring dep --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index 1ac16f5917a..98b78e7c533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1516,6 +1516,7 @@ __metadata: "@metamask/contract-metadata": ^2.3.1 "@metamask/controller-utils": ^4.3.1 "@metamask/eth-query": ^3.0.1 + "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" "@metamask/metamask-eth-abis": 3.0.0 "@metamask/network-controller": ^12.1.1 "@metamask/preferences-controller": ^4.3.0 From a03745ead6acbb49954027fbf7ed4fb5f2de2921 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 28 Aug 2023 21:22:21 +0800 Subject: [PATCH 03/54] fix: double negative in legacy account filter --- packages/accounts-controller/src/AccountsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index bdf791ffd9f..6730f12121e 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -251,7 +251,7 @@ export default class AccountsController extends BaseControllerV2< legacyAccounts = legacyAccounts.filter( (account) => !snapAccounts.find( - (snapAccount) => snapAccount.address !== account.address, + (snapAccount) => snapAccount.address === account.address, ), ); From 8f86aa79b88cd5f743428313b23e8d35d6b8224c Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:57:51 +0200 Subject: [PATCH 04/54] fix: fix linting warning --- packages/accounts-controller/src/AccountsController.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 6730f12121e..e35b00a46ea 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -373,7 +373,10 @@ export default class AccountsController extends BaseControllerV2< currentState.internalAccounts.selectedAccount = account.id; }); - this.messagingSystem.publish(`${this.name}:selectedAccountChange`, account); + this.messagingSystem.publish( + `${controllerName}:selectedAccountChange`, + account, + ); } setAccountName(accountId: string, accountName: string): void { @@ -438,8 +441,6 @@ export default class AccountsController extends BaseControllerV2< account.address.toLowerCase() === newAddress!.toLowerCase(), ); - // console.log('new account in onKeyringStateChange', newAccount); - // set the first new account as the selected account // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.setSelectedAccount(newAccount!.id); From 7fccb78dafe93c26912b60e9fe8c91a41039c78a Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 29 Aug 2023 10:59:11 +0200 Subject: [PATCH 05/54] chore: run `yarn dedupe` --- yarn.lock | 180 ++++-------------------------------------------------- 1 file changed, 13 insertions(+), 167 deletions(-) diff --git a/yarn.lock b/yarn.lock index 98b78e7c533..e51055a1fe3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,16 +29,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/code-frame@npm:7.22.5" - dependencies: - "@babel/highlight": ^7.22.5 - checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.22.10": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.10, @babel/code-frame@npm:^7.22.5": version: 7.22.10 resolution: "@babel/code-frame@npm:7.22.10" dependencies: @@ -55,30 +46,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.2, @babel/core@npm:^7.8.0": - version: 7.22.9 - resolution: "@babel/core@npm:7.22.9" - dependencies: - "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.9 - "@babel/helper-compilation-targets": ^7.22.9 - "@babel/helper-module-transforms": ^7.22.9 - "@babel/helpers": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.8 - "@babel/types": ^7.22.5 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.2 - json5: ^2.2.2 - semver: ^6.3.1 - checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 - languageName: node - linkType: hard - -"@babel/core@npm:^7.18.6, @babel/core@npm:^7.20.12": +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.18.6, @babel/core@npm:^7.20.12, @babel/core@npm:^7.7.2, @babel/core@npm:^7.8.0": version: 7.22.11 resolution: "@babel/core@npm:7.22.11" dependencies: @@ -101,7 +69,7 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.22.10": +"@babel/generator@npm:^7.22.10, @babel/generator@npm:^7.7.2": version: 7.22.10 resolution: "@babel/generator@npm:7.22.10" dependencies: @@ -113,18 +81,6 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": - version: 7.22.9 - resolution: "@babel/generator@npm:7.22.9" - dependencies: - "@babel/types": ^7.22.5 - "@jridgewell/gen-mapping": ^0.3.2 - "@jridgewell/trace-mapping": ^0.3.17 - jsesc: ^2.5.1 - checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b - languageName: node - linkType: hard - "@babel/helper-compilation-targets@npm:^7.22.10": version: 7.22.10 resolution: "@babel/helper-compilation-targets@npm:7.22.10" @@ -138,21 +94,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-compilation-targets@npm:7.22.9" - dependencies: - "@babel/compat-data": ^7.22.9 - "@babel/helper-validator-option": ^7.22.5 - browserslist: ^4.21.9 - lru-cache: ^5.1.1 - semver: ^6.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 - languageName: node - linkType: hard - "@babel/helper-environment-visitor@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-environment-visitor@npm:7.22.5" @@ -260,17 +201,6 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.22.6": - version: 7.22.6 - resolution: "@babel/helpers@npm:7.22.6" - dependencies: - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.6 - "@babel/types": ^7.22.5 - checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 - languageName: node - linkType: hard - "@babel/highlight@npm:^7.22.10": version: 7.22.10 resolution: "@babel/highlight@npm:7.22.10" @@ -282,27 +212,7 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/highlight@npm:7.22.5" - dependencies: - "@babel/helper-validator-identifier": ^7.22.5 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": - version: 7.22.7 - resolution: "@babel/parser@npm:7.22.7" - bin: - parser: ./bin/babel-parser.js - checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.22.11": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.11, @babel/parser@npm:^7.22.5": version: 7.22.11 resolution: "@babel/parser@npm:7.22.11" bin: @@ -465,7 +375,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.22.11": +"@babel/traverse@npm:^7.22.11, @babel/traverse@npm:^7.7.2": version: 7.22.11 resolution: "@babel/traverse@npm:7.22.11" dependencies: @@ -483,36 +393,7 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.7.2": - version: 7.22.8 - resolution: "@babel/traverse@npm:7.22.8" - dependencies: - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.7 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/types": ^7.22.5 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 - languageName: node - linkType: hard - -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": - version: 7.22.5 - resolution: "@babel/types@npm:7.22.5" - dependencies: - "@babel/helper-string-parser": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.5 - to-fast-properties: ^2.0.0 - checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 - languageName: node - linkType: hard - -"@babel/types@npm:^7.18.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.11": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.7, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.11, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": version: 7.22.11 resolution: "@babel/types@npm:7.22.11" dependencies: @@ -1516,7 +1397,6 @@ __metadata: "@metamask/contract-metadata": ^2.3.1 "@metamask/controller-utils": ^4.3.1 "@metamask/eth-query": ^3.0.1 - "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" "@metamask/metamask-eth-abis": 3.0.0 "@metamask/network-controller": ^12.1.1 "@metamask/preferences-controller": ^4.3.0 @@ -2841,14 +2721,14 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1": +"@noble/hashes@npm:1.3.1": version: 1.3.1 resolution: "@noble/hashes@npm:1.3.1" checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 languageName: node linkType: hard -"@noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.1.3": +"@noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.1.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 @@ -2955,20 +2835,13 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1": +"@scure/base@npm:^1.0.0, @scure/base@npm:^1.1.1, @scure/base@npm:~1.1.0": version: 1.1.2 resolution: "@scure/base@npm:1.1.2" checksum: f666b09dbd62ecb5fe6d0e7a629c8a86a972a47dc4f4555ebbbd7b09782b10a5f894fed9c3b8c74fd683b1588c064df079a44e9f695c075ccd98c30a8d3e91f7 languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.1 - resolution: "@scure/base@npm:1.1.1" - checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 - languageName: node - linkType: hard - "@scure/bip32@npm:1.1.5": version: 1.1.5 resolution: "@scure/bip32@npm:1.1.5" @@ -4317,7 +4190,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0, chalk@npm:^2.4.2": +"chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -6885,20 +6758,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3": - version: 1.1.10 - resolution: "is-typed-array@npm:1.1.10" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - checksum: aac6ecb59d4c56a1cdeb69b1f129154ef462bbffe434cb8a8235ca89b42f258b7ae94073c41b3cb7bce37f6a1733ad4499f07882d5d5093a7ba84dfc4ebb8017 - languageName: node - linkType: hard - -"is-typed-array@npm:^1.1.9": +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": version: 1.1.12 resolution: "is-typed-array@npm:1.1.12" dependencies: @@ -7771,7 +7631,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:2.x, json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:2.x, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -10518,7 +10378,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11": +"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2": version: 1.1.11 resolution: "which-typed-array@npm:1.1.11" dependencies: @@ -10531,20 +10391,6 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.2": - version: 1.1.10 - resolution: "which-typed-array@npm:1.1.10" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - for-each: ^0.3.3 - gopd: ^1.0.1 - has-tostringtag: ^1.0.0 - is-typed-array: ^1.1.10 - checksum: 149f54f5d11773ce938c60a2c36306720a1824eccb62bda0620170932c2783fa50ad0226254c5741a962e35c7ccba5f4e4c402b8618cb3b4f2cf47bf5e6ade31 - languageName: node - linkType: hard - "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" From a4aa0affca509814ae1182dd2b71066c8bc4e0f0 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 29 Aug 2023 11:51:34 +0200 Subject: [PATCH 06/54] fix: add missing types package --- packages/accounts-controller/package.json | 2 +- .../src/AccountsController.ts | 2 +- packages/accounts-controller/tsconfig.json | 4 +- yarn.lock | 176 ++---------------- 4 files changed, 17 insertions(+), 167 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index f67d93a3aff..ee1174fdc68 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -32,7 +32,6 @@ "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0", "@metamask/keyring-controller": "^7.1.0", "@metamask/snaps-controllers": "^1.0.1", - "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", "eth-rpc-errors": "^4.0.2", @@ -44,6 +43,7 @@ "devDependencies": { "@metamask/auto-changelog": "^3.1.0", "@types/jest": "^27.4.1", + "@types/readable-stream": "^2.3.0", "deepmerge": "^4.2.2", "jest": "^27.5.1", "sinon": "^9.2.4", diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index e35b00a46ea..a6fdaf73e91 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -10,8 +10,8 @@ import type { import type { SnapController, SnapControllerEvents, + SnapControllerState, } from '@metamask/snaps-controllers'; -import type { SnapControllerState } from '@metamask/snaps-controllers-flask'; import type { Snap } from '@metamask/snaps-utils'; import { sha256FromString } from 'ethereumjs-util'; import type { Patch } from 'immer'; diff --git a/packages/accounts-controller/tsconfig.json b/packages/accounts-controller/tsconfig.json index 5b422a56914..da0e84978a4 100644 --- a/packages/accounts-controller/tsconfig.json +++ b/packages/accounts-controller/tsconfig.json @@ -10,7 +10,9 @@ { "path": "../controller-utils" }, - { "path": "../keyring-controller" } + { + "path": "../keyring-controller" + } ], "include": ["../../types", "./src"] } diff --git a/yarn.lock b/yarn.lock index e51055a1fe3..b4b8e504d69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1296,10 +1296,10 @@ __metadata: "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" "@metamask/keyring-controller": ^7.1.0 "@metamask/snaps-controllers": ^1.0.1 - "@metamask/snaps-controllers-flask": "npm:@metamask/snaps-controllers@0.38.0-flask.1" "@metamask/snaps-utils": ^1.0.1 "@metamask/utils": ^6.2.0 "@types/jest": ^27.4.1 + "@types/readable-stream": ^2.3.0 deepmerge: ^4.2.2 eth-rpc-errors: ^4.0.2 ethereumjs-util: ^7.1.5 @@ -1829,20 +1829,6 @@ __metadata: languageName: node linkType: hard -"@metamask/key-tree@npm:^9.0.0": - version: 9.0.0 - resolution: "@metamask/key-tree@npm:9.0.0" - dependencies: - "@metamask/scure-bip39": ^2.1.0 - "@metamask/utils": ^6.0.1 - "@noble/ed25519": ^1.6.0 - "@noble/hashes": ^1.0.0 - "@noble/secp256k1": ^1.5.5 - "@scure/base": ^1.0.0 - checksum: 5c81f07351ca59b37570d52edcc80d60424630b2a8403ed7149c3343c264878ac5d3fc0584a61635ea7ddda4a789295ded1247846606dc529d8e2fd42f6fc61a - languageName: node - linkType: hard - "@metamask/keyring-api@npm:^0.1.3": version: 0.1.3 resolution: "@metamask/keyring-api@npm:0.1.3" @@ -2187,23 +2173,6 @@ __metadata: languageName: node linkType: hard -"@metamask/rpc-methods@npm:^0.37.2-flask.1": - version: 0.37.2-flask.1 - resolution: "@metamask/rpc-methods@npm:0.37.2-flask.1" - dependencies: - "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.0.0 - "@metamask/snaps-ui": ^0.37.2-flask.1 - "@metamask/snaps-utils": ^0.37.2-flask.1 - "@metamask/types": ^1.1.0 - "@metamask/utils": ^6.0.1 - "@noble/hashes": ^1.3.1 - eth-rpc-errors: ^4.0.3 - superstruct: ^1.0.3 - checksum: f5b955c3a7b7c042c27aa5d9464ea5d9135104c20af3315ea00d8941c5c6379c12c82c0988bc5c21409f4dd8b740bb93b387e2ee115c914b434e28101cd9d877 - languageName: node - linkType: hard - "@metamask/rpc-methods@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/rpc-methods@npm:1.0.1" @@ -2273,36 +2242,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/snaps-controllers-flask@npm:@metamask/snaps-controllers@0.38.0-flask.1": - version: 0.38.0-flask.1 - resolution: "@metamask/snaps-controllers@npm:0.38.0-flask.1" - dependencies: - "@metamask/approval-controller": ^3.0.0 - "@metamask/base-controller": ^3.0.0 - "@metamask/object-multiplex": ^1.2.0 - "@metamask/permission-controller": ^4.0.0 - "@metamask/post-message-stream": ^6.1.2 - "@metamask/rpc-methods": ^0.37.2-flask.1 - "@metamask/snaps-execution-environments": ^0.38.0-flask.1 - "@metamask/snaps-registry": ^1.2.1 - "@metamask/snaps-utils": ^0.38.0-flask.1 - "@metamask/utils": ^6.0.1 - "@xstate/fsm": ^2.0.0 - concat-stream: ^2.0.0 - cron-parser: ^4.5.0 - eth-rpc-errors: ^4.0.3 - gunzip-maybe: ^1.4.2 - immer: ^9.0.6 - json-rpc-engine: ^6.1.0 - json-rpc-middleware-stream: ^4.2.0 - nanoid: ^3.1.31 - pump: ^3.0.0 - readable-web-to-node-stream: ^3.0.2 - tar-stream: ^2.2.0 - checksum: 538d2fc5295f094d5852c8385b99c60daf3874628f6627926b10412a309d86a7ee3e5bcb61f8c3fdd40b403d6ea08795aa2d338271924c36c6204d65adfcacd6 - languageName: node - linkType: hard - "@metamask/snaps-controllers@npm:^0.35.2-flask.1": version: 0.35.2-flask.1 resolution: "@metamask/snaps-controllers@npm:0.35.2-flask.1" @@ -2384,27 +2323,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-execution-environments@npm:^0.38.0-flask.1": - version: 0.38.0-flask.1 - resolution: "@metamask/snaps-execution-environments@npm:0.38.0-flask.1" - dependencies: - "@metamask/object-multiplex": ^1.2.0 - "@metamask/post-message-stream": ^6.1.2 - "@metamask/providers": ^11.0.0 - "@metamask/rpc-methods": ^0.37.2-flask.1 - "@metamask/snaps-utils": ^0.38.0-flask.1 - "@metamask/utils": ^6.0.1 - eth-rpc-errors: ^4.0.3 - json-rpc-engine: ^6.1.0 - nanoid: ^3.1.31 - pump: ^3.0.0 - ses: ^0.18.1 - stream-browserify: ^3.0.0 - superstruct: ^1.0.3 - checksum: 725163d03c3c17d0a67ad5829adf69da8bcd2d3890efd984ff5954a19aeeeb4e024a96810d9ae9d5f095f1e39965376f62d41637ff61c59519f46c093f05b0f9 - languageName: node - linkType: hard - "@metamask/snaps-execution-environments@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-execution-environments@npm:1.0.1" @@ -2446,26 +2364,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-ui@npm:^0.37.2-flask.1": - version: 0.37.2-flask.1 - resolution: "@metamask/snaps-ui@npm:0.37.2-flask.1" - dependencies: - "@metamask/utils": ^6.0.1 - superstruct: ^1.0.3 - checksum: a52a887411d689a7a0b017b3741c76819567416c7943ca3c71f5b5f8b623605689f370f70caa8a22531d711d10738c913b0d561fcda09a92c38e288d115e6bac - languageName: node - linkType: hard - -"@metamask/snaps-ui@npm:^0.37.3-flask.1": - version: 0.37.3-flask.1 - resolution: "@metamask/snaps-ui@npm:0.37.3-flask.1" - dependencies: - "@metamask/utils": ^6.0.1 - superstruct: ^1.0.3 - checksum: 0b93f6edeca18afc799f16be8b4e4d758800ea0d68298c70492dc85f74ec9f79c2aa32749f6725bb83a4896fdb1ec95a7d19a10a617484e0a7574ddad7969bd3 - languageName: node - linkType: hard - "@metamask/snaps-ui@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-ui@npm:1.0.1" @@ -2505,66 +2403,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^0.37.2-flask.1": - version: 0.37.2-flask.1 - resolution: "@metamask/snaps-utils@npm:0.37.2-flask.1" - dependencies: - "@babel/core": ^7.20.12 - "@babel/types": ^7.18.7 - "@metamask/base-controller": ^3.0.0 - "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.0.0 - "@metamask/providers": ^11.0.0 - "@metamask/snaps-registry": ^1.2.1 - "@metamask/snaps-ui": ^0.37.2-flask.1 - "@metamask/utils": ^6.0.1 - "@noble/hashes": ^1.3.1 - "@scure/base": ^1.1.1 - chalk: ^4.1.2 - cron-parser: ^4.5.0 - eth-rpc-errors: ^4.0.3 - fast-deep-equal: ^3.1.3 - fast-json-stable-stringify: ^2.1.0 - is-svg: ^4.4.0 - rfdc: ^1.3.0 - semver: ^7.3.7 - ses: ^0.18.1 - superstruct: ^1.0.3 - validate-npm-package-name: ^5.0.0 - checksum: eeb31013a8af39b5488fdb8a6a72e11c54274e423e0f2560fd5cf4ea0278e278aff60be070c1e5b847d84220f15338e01c4725ce7ebb5f3fc614553ae4235f44 - languageName: node - linkType: hard - -"@metamask/snaps-utils@npm:^0.38.0-flask.1": - version: 0.38.0-flask.1 - resolution: "@metamask/snaps-utils@npm:0.38.0-flask.1" - dependencies: - "@babel/core": ^7.20.12 - "@babel/types": ^7.18.7 - "@metamask/base-controller": ^3.0.0 - "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.0.0 - "@metamask/providers": ^11.0.0 - "@metamask/snaps-registry": ^1.2.1 - "@metamask/snaps-ui": ^0.37.3-flask.1 - "@metamask/utils": ^6.0.1 - "@noble/hashes": ^1.3.1 - "@scure/base": ^1.1.1 - chalk: ^4.1.2 - cron-parser: ^4.5.0 - eth-rpc-errors: ^4.0.3 - fast-deep-equal: ^3.1.3 - fast-json-stable-stringify: ^2.1.0 - is-svg: ^4.4.0 - rfdc: ^1.3.0 - semver: ^7.5.4 - ses: ^0.18.1 - superstruct: ^1.0.3 - validate-npm-package-name: ^5.0.0 - checksum: 9b79feabcf3a99f0faa53c87711e0de155807d49dd3a9117933b9636d529fa3f3449bd563535f056fc7cbb3eaffcd9e9703b02985bdb6cfdc090d096d76dad8e - languageName: node - linkType: hard - "@metamask/snaps-utils@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-utils@npm:1.0.1" @@ -3214,6 +3052,16 @@ __metadata: languageName: node linkType: hard +"@types/readable-stream@npm:^2.3.0": + version: 2.3.15 + resolution: "@types/readable-stream@npm:2.3.15" + dependencies: + "@types/node": "*" + safe-buffer: ~5.1.1 + checksum: ec36f525cad09b6c65a1dafcb5ad99b9e2ed824ec49b7aa23180ac427e5d35b8a0706193ecd79ab4253a283ad485ba03d5917a98daaaa144f0ea34f4823e9d82 + languageName: node + linkType: hard + "@types/secp256k1@npm:^4.0.1": version: 4.0.3 resolution: "@types/secp256k1@npm:4.0.3" @@ -4201,7 +4049,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: From 14c98e419718c076d45ac0d096a2e8f4b71b05e3 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:05:49 +0200 Subject: [PATCH 07/54] chore: update CHANGELOG --- packages/accounts-controller/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/accounts-controller/CHANGELOG.md b/packages/accounts-controller/CHANGELOG.md index 11bddf32c5b..98d6e3847af 100644 --- a/packages/accounts-controller/CHANGELOG.md +++ b/packages/accounts-controller/CHANGELOG.md @@ -1,8 +1,11 @@ # Changelog - All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Initial release + +[Unreleased]: https://github.com/MetaMask/core/ From d3eda700e64895919029071ff91ea0cc571e22b8 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Tue, 29 Aug 2023 20:32:41 +0800 Subject: [PATCH 08/54] fix: changelog:validate to accounts-controller --- packages/accounts-controller/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index ee1174fdc68..306422eef28 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -22,7 +22,7 @@ ], "scripts": { "build:docs": "typedoc", - "changelog:validate": "../../scripts/validate-changelog.sh @metamask/approval-controller", + "changelog:validate": "../../scripts/validate-changelog.sh @metamask/accounts-controller", "publish:preview": "yarn npm publish --tag preview", "test": "jest", "test:watch": "jest --watch" From 7c64ed8a6b30e15602eb8ae4565233fc62a0cb9d Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 11:45:14 +0800 Subject: [PATCH 09/54] fix: update accounts controller to not use code fences --- .../src/AccountsController.ts | 201 +++++++++--------- 1 file changed, 102 insertions(+), 99 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index a6fdaf73e91..47dd824daf2 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -8,7 +8,6 @@ import type { KeyringControllerEvents, } from '@metamask/keyring-controller'; import type { - SnapController, SnapControllerEvents, SnapControllerState, } from '@metamask/snaps-controllers'; @@ -80,22 +79,30 @@ export default class AccountsController extends BaseControllerV2< AccountsControllerState, AccountsControllerMessenger > { - #keyringController: KeyringController; + getKeyringForAccount: KeyringController['getKeyringForAccount']; - #snapController: SnapController; + getKeyringByType: KeyringController['getKeyringsByType']; + + getAccounts: KeyringController['getAccounts']; + + keyringApiEnabled: boolean; constructor({ messenger, state, - keyringController, - snapController, + keyringApiEnabled, + getKeyringForAccount, + getKeyringByType, + getAccounts, onSnapStateChange, onKeyringStateChange, }: { messenger: AccountsControllerMessenger; state: AccountsControllerState; - keyringController: KeyringController; - snapController: SnapController; + keyringApiEnabled: boolean; + getKeyringForAccount: KeyringController['getKeyringForAccount']; + getKeyringByType: KeyringController['getKeyringsByType']; + getAccounts: KeyringController['getAccounts']; onKeyringStateChange: ( listener: (keyringState: KeyringControllerState) => void, ) => void; @@ -113,43 +120,43 @@ export default class AccountsController extends BaseControllerV2< }, }); - this.#keyringController = keyringController; - this.#snapController = snapController; + this.getKeyringForAccount = getKeyringForAccount; + this.getKeyringByType = getKeyringByType; + this.getAccounts = getAccounts; + this.keyringApiEnabled = Boolean(keyringApiEnabled); - // eslint-disable-next-line @typescript-eslint/no-misused-promises - onSnapStateChange(async (snapState: SnapControllerState) => { - // only check if snaps changed in status - const { snaps } = snapState; - const accounts = this.listAccounts(); + if (this.keyringApiEnabled) { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + onSnapStateChange(async (snapState: SnapControllerState) => { + // only check if snaps changed in status + const { snaps } = snapState; + const accounts = this.listAccounts(); - const disabledSnaps: Snap[] = []; - for (const snap of Object.values(snaps)) { - if (!(await this.#isSnapEnabled(snap.id))) { - disabledSnaps.push(snap); - } - } + const disabledSnaps: Snap[] = Object.values(snaps).filter( + (snap) => !snap.enabled || !snap.blocked, + ); - const accountsToUpdate = accounts.filter( - (account) => - account.metadata.snap && - disabledSnaps.find((snap) => snap.id === account.metadata.snap?.id), - ); + const accountsToUpdate = accounts.filter( + (account) => + account.metadata.snap && + disabledSnaps.find((snap) => snap.id === account.metadata.snap?.id), + ); - this.update((currentState: AccountsControllerState) => { - accountsToUpdate.forEach((account) => { - if ( - currentState.internalAccounts.accounts[account.id] && - currentState.internalAccounts.accounts[account.id].metadata?.snap - ) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore this account is guaranteed to have snap metadata - currentState.internalAccounts.accounts[ - account.id - ].metadata.snap.enabled = false; - } + this.update((currentState: AccountsControllerState) => { + accountsToUpdate.forEach((account) => { + if ( + currentState.internalAccounts.accounts[account.id]?.metadata?.snap + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this account is guaranteed to have snap metadata + currentState.internalAccounts.accounts[ + account.id + ].metadata.snap.enabled = false; + } + }); }); }); - }); + } onKeyringStateChange( // eslint-disable-next-line @typescript-eslint/no-misused-promises @@ -244,16 +251,54 @@ export default class AccountsController extends BaseControllerV2< return this.getAccountExpect(this.state.internalAccounts.selectedAccount); } + setSelectedAccount(accountId: string): void { + const account = this.getAccountExpect(accountId); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore Type instantiation is excessively deep and possibly infinite. + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts.accounts[account.id].metadata.lastSelected = + Date.now(); + currentState.internalAccounts.selectedAccount = account.id; + }); + + this.messagingSystem.publish(`${this.name}:selectedAccountChange`, account); + } + + setAccountName(accountId: string, accountName: string): void { + const account = this.getAccountExpect(accountId); + + if ( + this.listAccounts().find( + (internalAccount) => + internalAccount.name === accountName && + internalAccount.id !== accountId, + ) + ) { + throw new Error('Account name already exists'); + } + + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts.accounts[accountId] = { + ...account, + name: accountName, + }; + }); + } + async updateAccounts(): Promise { let legacyAccounts = await this.#listLegacyAccounts(); - const snapAccounts = await this.#listSnapAccounts(); - // remove duplicate accounts that are retrieved from the snap keyring. - legacyAccounts = legacyAccounts.filter( - (account) => - !snapAccounts.find( - (snapAccount) => snapAccount.address === account.address, - ), - ); + let snapAccounts: InternalAccount[] = []; + if (this.keyringApiEnabled) { + snapAccounts = await this.#listSnapAccounts(); + // remove duplicate accounts that are retrieved from the snap keyring. + legacyAccounts = legacyAccounts.filter( + (account) => + !snapAccounts.find( + (snapAccount) => snapAccount.address === account.address, + ), + ); + } // keyring type map. const keyringTypes = new Map(); @@ -261,7 +306,7 @@ export default class AccountsController extends BaseControllerV2< const accounts: Record = [ ...legacyAccounts, - ...snapAccounts, + ...(this.keyringApiEnabled ? snapAccounts : []), ].reduce((internalAccountMap, internalAccount) => { const keyringTypeName = keyringTypeToName( internalAccount.metadata.keyring.type, @@ -296,10 +341,14 @@ export default class AccountsController extends BaseControllerV2< }); } + loadBackup(backup: AccountsControllerState): void { + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts = backup.internalAccounts; + }); + } + async #listSnapAccounts(): Promise { - const [snapKeyring] = this.#keyringController.getKeyringsByType( - SnapKeyring.type, - ); + const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); const snapAccounts = (await (snapKeyring as SnapKeyring)?.listAccounts(false)) ?? []; @@ -310,7 +359,7 @@ export default class AccountsController extends BaseControllerV2< account.metadata = { snap: { id: snapId, - enabled: await this.#isSnapEnabled(snapId), + enabled: true, name: account.name, }, keyring: { @@ -324,12 +373,10 @@ export default class AccountsController extends BaseControllerV2< // Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface async #listLegacyAccounts(): Promise[]> { - const addresses = await this.#keyringController.getAccounts(); + const addresses = await this.getAccounts(); const internalAccounts: Omit[] = []; for (const address of addresses) { - const keyring = await this.#keyringController.getKeyringForAccount( - address, - ); + const keyring = await this.getKeyringForAccount(address); // TODO: this is done until the keyrings all implement the InternalAccount interface const v4options = { random: sha256FromString(address).slice(0, 16), @@ -364,50 +411,6 @@ export default class AccountsController extends BaseControllerV2< ); } - setSelectedAccount(accountId: string): void { - const account = this.getAccountExpect(accountId); - - this.update((currentState: AccountsControllerState) => { - currentState.internalAccounts.accounts[account.id].metadata.lastSelected = - Date.now(); - currentState.internalAccounts.selectedAccount = account.id; - }); - - this.messagingSystem.publish( - `${controllerName}:selectedAccountChange`, - account, - ); - } - - setAccountName(accountId: string, accountName: string): void { - const account = this.getAccountExpect(accountId); - - if ( - this.listAccounts().find( - (internalAccount) => - internalAccount.name === accountName && - internalAccount.id !== accountId, - ) - ) { - throw new Error('Account name already exists'); - } - - this.update((currentState: AccountsControllerState) => { - currentState.internalAccounts.accounts[accountId] = { - ...account, - name: accountName, - }; - }); - } - - async #isSnapEnabled(snapId: string): Promise { - const snap = (await this.#snapController.getSnapState(snapId)) as any; - if (!snap) { - return false; - } - return snap?.enabled && !snap?.blocked; - } - #handleSelectedAccountRemoved() { const previousAccount = this.listAccounts() .filter( From 223b38d3066daf55068a71dff0d09e5ae3e138b6 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 11:48:25 +0800 Subject: [PATCH 10/54] feat: add initial test file --- .../src/AccountsController.test.ts | 549 ++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 packages/accounts-controller/src/AccountsController.test.ts diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts new file mode 100644 index 00000000000..fa2e7efdecc --- /dev/null +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -0,0 +1,549 @@ +import { ControllerMessenger } from '@metamask/base-controller'; +import type { InternalAccount } from '@metamask/eth-snap-keyring'; +import type { KeyringController } from '@metamask/keyring-controller'; +import type { SnapController } from '@metamask/snaps-controllers'; + +import type { + AccountsControllerActions, + AccountsControllerEvents, + AccountsControllerState, +} from './AccountsController'; +import AccountsController from './AccountsController'; + +const mockUUIDV4 = jest.fn(); + +jest.mock('uuid', () => { + const actual = jest.requireActual('uuid'); + + return { + ...actual, + v4: () => mockUUIDV4, + }; +}); + +const mockDateNow = jest + .spyOn(Date, 'now') + .mockImplementation(() => 1691565967656); + +const mockKeyringController: jest.Mocked = + {} as jest.Mocked; + +const mockSnapController: jest.Mocked = + {} as jest.Mocked; + +const defaultState: AccountsControllerState = { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, +}; + +const mockAccount = { + name: 'Account 1', + id: 'mock-id', + address: '0x123', + options: {}, + supportedMethods: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v2', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + keyring: { type: 'HD Key Tree' }, + lastSelected: 1691565967656, + }, +}; + +const mockAccount2 = { + name: 'Account 2', + id: 'mock-id2', + address: '0x1234', + options: {}, + supportedMethods: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v2', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + keyring: { type: 'HD Key Tree' }, + lastSelected: 1691565967656, + }, +}; + +/** + * + * @param account + */ +function setLastSelectedAsAny(account: InternalAccount): InternalAccount { + return { + ...account, + metadata: { + ...account.metadata, + lastSelected: expect.any(Number), + }, + }; +} + +/** + * + */ +function buildMessenger() { + return new ControllerMessenger< + AccountsControllerActions, + AccountsControllerEvents + >(); +} + +/** + * Builds a restricted messenger for the AccountsController. + * + * @param {ControllerMessenger} messenger - The messenger to restrict. + * @returns {ControllerMessenger} The restricted messenger. + */ +function buildAccountsControllerMessenger(messenger = buildMessenger()) { + // ... +} +function buildAccountsControllerMessenger(messenger = buildMessenger()) { + return messenger.getRestricted({ + name: 'AccountsController', + allowedEvents: [ + 'SnapController:stateChange', + 'KeyringController:accountRemoved', + 'KeyringController:stateChange', + ], + }); +} + +/** + * + * @param initialState - The initial state to use for the AccountsController. + * @param onKeyringStateChange - A callback to call when the keyring state changes. + * @param onSnapStateChange - A callback to call when the snap state changes. + */ +function setupAccountsController( + // eslint-disable-next-line @typescript-eslint/default-param-last + initialState = {}, + onKeyringStateChange = () => jest.fn(), + onSnapStateChange = () => jest.fn(), +): AccountsController { + const accountsControllerMessenger = buildAccountsControllerMessenger( + new ControllerMessenger(), + ); + + const accountsController = new AccountsController({ + messenger: accountsControllerMessenger, + state: { ...defaultState, ...initialState }, + keyringController: mockKeyringController, + snapController: mockSnapController, + onKeyringStateChange, + onSnapStateChange, + identities: null, + }); + return accountsController; +} + +describe('AccountsController', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + // describe('onSnapStateChange', () => { + // it('should disable snap-enabled accounts when a snap is disabled', () => { + // + // const accountsController = new AccountsController(); + // const snapId = 'snap123'; + // const accountId = '0x123'; + // const account = { + // id: accountId, + // metadata: { snap: { id: snapId, enabled: true } }, + // }; + // accountsController.state.internalAccounts.accounts[accountId] = account; + + // const snapState = { + // snaps: { + // [snapId]: { id: snapId, enabled: false }, + // }, + // }; + + // + // accountsController.onSnapStateChange(snapState); + + // + // expect( + // accountsController.state.internalAccounts.accounts[accountId].metadata + // .snap.enabled, + // ).toBe(false); + // }); + + // it('should not disable snap-disabled accounts when a snap is disabled', () => { + // + // const accountsController = new AccountsController(); + // const snapId = 'snap123'; + // const accountId = '0x123'; + // const account = { + // id: accountId, + // metadata: { snap: { id: snapId, enabled: false } }, + // }; + // accountsController.state.internalAccounts.accounts[accountId] = account; + + // const snapState = { + // snaps: { + // [snapId]: { id: snapId, enabled: false }, + // }, + // }; + + // + // accountsController.onSnapStateChange(snapState); + + // + // expect( + // accountsController.state.internalAccounts.accounts[accountId].metadata + // .snap.enabled, + // ).toBe(false); + // }); + + // it('should not disable accounts when a snap is enabled', () => { + // + // const accountsController = new AccountsController(); + // const snapId = 'snap123'; + // const accountId = '0x123'; + // const account = { + // id: accountId, + // metadata: { snap: { id: snapId, enabled: true } }, + // }; + // accountsController.state.internalAccounts.accounts[accountId] = account; + + // const snapState = { + // snaps: { + // [snapId]: { id: snapId, enabled: true }, + // }, + // }; + + // + // accountsController.onSnapStateChange(snapState); + + // + // expect( + // accountsController.state.internalAccounts.accounts[accountId].metadata + // .snap.enabled, + // ).toBe(true); + // }); + // }); + + // describe('updateAccounts', () => { + // it('should update accounts with legacy accounts', async () => { + // + // const accountsController = new AccountsController(); + // const legacyAccounts = [ + // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + // const snapAccounts = []; + + // + // jest + // .spyOn(accountsController, '#listLegacyAccounts') + // .mockResolvedValue(legacyAccounts); + // jest + // .spyOn(accountsController, '#listSnapAccounts') + // .mockResolvedValue(snapAccounts); + // await accountsController.updateAccounts(); + + // + // expect(accountsController.state.internalAccounts.accounts).toEqual( + // legacyAccounts, + // ); + // }); + + // it('should update accounts with snap accounts', async () => { + // + // const accountsController = new AccountsController(); + // const legacyAccounts = []; + // const snapAccounts = [ + // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0xabc', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + + // + // jest + // .spyOn(accountsController, '#listLegacyAccounts') + // .mockResolvedValue(legacyAccounts); + // jest + // .spyOn(accountsController, '#listSnapAccounts') + // .mockResolvedValue(snapAccounts); + // await accountsController.updateAccounts(); + + // + // expect(accountsController.state.internalAccounts.accounts).toEqual( + // snapAccounts, + // ); + // }); + + // it('should remove duplicate accounts from snap and legacy accounts', async () => { + // + // const accountsController = new AccountsController(); + // const legacyAccounts = [ + // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + // const snapAccounts = [ + // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + // const expectedAccounts = [ + // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + + // + // jest + // .spyOn(accountsController, '#listLegacyAccounts') + // .mockResolvedValue(legacyAccounts); + // jest + // .spyOn(accountsController, '#listSnapAccounts') + // .mockResolvedValue(snapAccounts); + // await accountsController.updateAccounts(); + + // + // expect(accountsController.state.internalAccounts.accounts).toEqual( + // expectedAccounts, + // ); + // }); + + // it('should update keyring types', async () => { + // + // const accountsController = new AccountsController(); + // const legacyAccounts = [ + // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, + // ]; + // const snapAccounts = [ + // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, + // { address: '0xabc', metadata: { keyring: { type: 'Ledger Keyring' } } }, + // ]; + // const expectedKeyringTypes = new Map([ + // ['Simple Keyring', 2], + // ['Ledger Keyring', 1], + // ]); + + // + // jest + // .spyOn(accountsController, '#listLegacyAccounts') + // .mockResolvedValue(legacyAccounts); + // jest + // .spyOn(accountsController, '#listSnapAccounts') + // .mockResolvedValue(snapAccounts); + // await accountsController.updateAccounts(); + + // + // expect(accountsController.state.internalAccounts.keyringTypes).toEqual( + // expectedKeyringTypes, + // ); + // }); + // }); + + describe('getAccount', () => { + it('should return an account by ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + }, + }); + + const result = accountsController.getAccount(mockAccount.id); + + expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); + }); + }); + + it('should return undefined for an unknown account ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + }, + }); + + const result = accountsController.getAccount("I don't exist"); + + expect(result).toBeUndefined(); + }); + + describe('listAccounts', () => { + it('should return a list of accounts', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + }, + }); + + const result = accountsController.listAccounts(); + + expect(result).toEqual([ + setLastSelectedAsAny(mockAccount), + setLastSelectedAsAny(mockAccount2), + ]); + }); + }); + + describe('getAccountExpect', () => { + it('should return an account by ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + }, + }); + const result = accountsController.getAccountExpect(mockAccount.id); + + expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); + }); + + it('should throw an error for an unknown account ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + }, + }); + + expect(() => accountsController.getAccountExpect('unknown id')).toThrow( + `Account Id unknown id not found`, + ); + }); + }); + + describe('getSelectedAccount', () => { + it('should return the selected account', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + const result = accountsController.getAccountExpect(mockAccount.id); + + expect(result).toEqual(setLastSelectedAsAny(mockAccount)); + }); + + it('should throw an error if no account is selected', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: '', + }, + }); + + expect(() => accountsController.getSelectedAccount()).toThrow( + `Account Id not found`, + ); + }); + }); + + describe('setSelectedAccount', () => { + it('should set the selected account', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, + }, + }); + + accountsController.setSelectedAccount(mockAccount2.id); + + expect(accountsController.state.internalAccounts.selectedAccount).toEqual( + mockAccount2.id, + ); + }); + + it('should throw an error for an unknown account ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, + }, + }); + + expect(() => accountsController.setSelectedAccount('unknown id')).toThrow( + `Account Id unknown id not found`, + ); + }); + }); + + describe('setAccountName', () => { + it('should set the name of an existing account', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + + accountsController.setAccountName(mockAccount.id, 'new name'); + + expect(accountsController.getAccountExpect(mockAccount.id).name).toBe( + 'new name', + ); + }); + + it('should throw an error if the account name already exists', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + + expect(() => + accountsController.setAccountName(mockAccount.id, 'Account 1'), + ).toThrow('Account name already exists'); + }); + + it('should throw an error if the account ID is not found', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + expect(() => + accountsController.setAccountName('unknown account', 'new name'), + ).toThrow(`Account Id unknown account not found`); + }); + }); +}); From 5472cce1d673960034f932dc8107de1ea328db3a Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 12:18:37 +0800 Subject: [PATCH 11/54] feat: add setSelectedAccount, getSelectedAccount, setAccountName tests --- .../src/AccountsController.test.ts | 196 +++++++++--------- 1 file changed, 94 insertions(+), 102 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index fa2e7efdecc..3e90f092d8c 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,7 +1,5 @@ import { ControllerMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/eth-snap-keyring'; -import type { KeyringController } from '@metamask/keyring-controller'; -import type { SnapController } from '@metamask/snaps-controllers'; import type { AccountsControllerActions, @@ -21,16 +19,6 @@ jest.mock('uuid', () => { }; }); -const mockDateNow = jest - .spyOn(Date, 'now') - .mockImplementation(() => 1691565967656); - -const mockKeyringController: jest.Mocked = - {} as jest.Mocked; - -const mockSnapController: jest.Mocked = - {} as jest.Mocked; - const defaultState: AccountsControllerState = { internalAccounts: { accounts: {}, @@ -38,6 +26,10 @@ const defaultState: AccountsControllerState = { }, }; +const mockGetKeyringForAccount = jest.fn(); +const mockGetKeyringByType = jest.fn(); +const mockGetAccounts = jest.fn(); + const mockAccount = { name: 'Account 1', id: 'mock-id', @@ -85,8 +77,10 @@ const mockAccount2 = { }; /** + * Sets the `lastSelected` property of the given `account` to `expect.any(Number)`. * - * @param account + * @param account - The account to modify. + * @returns The modified account. */ function setLastSelectedAsAny(account: InternalAccount): InternalAccount { return { @@ -99,7 +93,9 @@ function setLastSelectedAsAny(account: InternalAccount): InternalAccount { } /** + * Builds a new instance of the ControllerMessenger class for the AccountsController. * + * @returns A new instance of the ControllerMessenger class for the AccountsController. */ function buildMessenger() { return new ControllerMessenger< @@ -111,12 +107,9 @@ function buildMessenger() { /** * Builds a restricted messenger for the AccountsController. * - * @param {ControllerMessenger} messenger - The messenger to restrict. - * @returns {ControllerMessenger} The restricted messenger. + * @param messenger - The messenger to restrict. + * @returns The restricted messenger. */ -function buildAccountsControllerMessenger(messenger = buildMessenger()) { - // ... -} function buildAccountsControllerMessenger(messenger = buildMessenger()) { return messenger.getRestricted({ name: 'AccountsController', @@ -134,9 +127,19 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { * @param onKeyringStateChange - A callback to call when the keyring state changes. * @param onSnapStateChange - A callback to call when the snap state changes. */ +/** + * Sets up an instance of the AccountsController class with the given initial state and callbacks. + * + * @param initialState - The initial state to use for the AccountsController. + * @param keyringApiEnabled - Whether or not the keyring API is enabled. + * @param onKeyringStateChange - A callback to call when the keyring state changes. + * @param onSnapStateChange - A callback to call when the snap state changes. + * @returns An instance of the AccountsController class. + */ function setupAccountsController( // eslint-disable-next-line @typescript-eslint/default-param-last initialState = {}, + keyringApiEnabled = true, onKeyringStateChange = () => jest.fn(), onSnapStateChange = () => jest.fn(), ): AccountsController { @@ -147,11 +150,12 @@ function setupAccountsController( const accountsController = new AccountsController({ messenger: accountsControllerMessenger, state: { ...defaultState, ...initialState }, - keyringController: mockKeyringController, - snapController: mockSnapController, + getKeyringForAccount: mockGetKeyringForAccount, + getKeyringByType: mockGetKeyringByType, + getAccounts: mockGetAccounts, onKeyringStateChange, onSnapStateChange, - identities: null, + keyringApiEnabled, }); return accountsController; } @@ -357,80 +361,80 @@ describe('AccountsController', () => { // }); // }); - describe('getAccount', () => { - it('should return an account by ID', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, - }, - }); + // describe('getAccount', () => { + // it('should return an account by ID', () => { + // const accountsController = setupAccountsController({ + // internalAccounts: { + // accounts: { [mockAccount.id]: mockAccount }, + // }, + // }); - const result = accountsController.getAccount(mockAccount.id); + // const result = accountsController.getAccount(mockAccount.id); - expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); - }); - }); + // expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); + // }); + // }); - it('should return undefined for an unknown account ID', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, - }, - }); + // it('should return undefined for an unknown account ID', () => { + // const accountsController = setupAccountsController({ + // internalAccounts: { + // accounts: { [mockAccount.id]: mockAccount }, + // }, + // }); - const result = accountsController.getAccount("I don't exist"); + // const result = accountsController.getAccount("I don't exist"); - expect(result).toBeUndefined(); - }); + // expect(result).toBeUndefined(); + // }); - describe('listAccounts', () => { - it('should return a list of accounts', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockAccount2.id]: mockAccount2, - }, - }, - }); + // describe('listAccounts', () => { + // it('should return a list of accounts', () => { + // const accountsController = setupAccountsController({ + // internalAccounts: { + // accounts: { + // [mockAccount.id]: mockAccount, + // [mockAccount2.id]: mockAccount2, + // }, + // }, + // }); - const result = accountsController.listAccounts(); + // const result = accountsController.listAccounts(); - expect(result).toEqual([ - setLastSelectedAsAny(mockAccount), - setLastSelectedAsAny(mockAccount2), - ]); - }); - }); + // expect(result).toEqual([ + // setLastSelectedAsAny(mockAccount), + // setLastSelectedAsAny(mockAccount2), + // ]); + // }); + // }); - describe('getAccountExpect', () => { - it('should return an account by ID', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, - }, - }); - const result = accountsController.getAccountExpect(mockAccount.id); + // describe('getAccountExpect', () => { + // it('should return an account by ID', () => { + // const accountsController = setupAccountsController({ + // internalAccounts: { + // accounts: { + // [mockAccount.id]: mockAccount, + // }, + // }, + // }); + // const result = accountsController.getAccountExpect(mockAccount.id); - expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); - }); + // expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); + // }); - it('should throw an error for an unknown account ID', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, - }, - }); + // it('should throw an error for an unknown account ID', () => { + // const accountsController = setupAccountsController({ + // internalAccounts: { + // accounts: { + // [mockAccount.id]: mockAccount, + // }, + // }, + // }); - expect(() => accountsController.getAccountExpect('unknown id')).toThrow( - `Account Id unknown id not found`, - ); - }); - }); + // expect(() => accountsController.getAccountExpect('unknown id')).toThrow( + // `Account Id unknown id not found`, + // ); + // }); + // }); describe('getSelectedAccount', () => { it('should return the selected account', () => { @@ -444,21 +448,8 @@ describe('AccountsController', () => { }); const result = accountsController.getAccountExpect(mockAccount.id); - expect(result).toEqual(setLastSelectedAsAny(mockAccount)); - }); - - it('should throw an error if no account is selected', () => { - const accountsController = setupAccountsController({ - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, - selectedAccount: '', - }, - }); - - expect(() => accountsController.getSelectedAccount()).toThrow( - `Account Id not found`, + expect(result).toStrictEqual( + setLastSelectedAsAny(mockAccount as InternalAccount), ); }); }); @@ -477,9 +468,9 @@ describe('AccountsController', () => { accountsController.setSelectedAccount(mockAccount2.id); - expect(accountsController.state.internalAccounts.selectedAccount).toEqual( - mockAccount2.id, - ); + expect( + accountsController.state.internalAccounts.selectedAccount, + ).toStrictEqual(mockAccount2.id); }); it('should throw an error for an unknown account ID', () => { @@ -522,13 +513,14 @@ describe('AccountsController', () => { internalAccounts: { accounts: { [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, }, selectedAccount: mockAccount.id, }, }); expect(() => - accountsController.setAccountName(mockAccount.id, 'Account 1'), + accountsController.setAccountName(mockAccount.id, 'Account 2'), ).toThrow('Account name already exists'); }); From 78115a78d9ffc04a0dcfef791e5f09e29e04ec37 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 12:23:24 +0800 Subject: [PATCH 12/54] fix: lower coverage temporarily --- packages/accounts-controller/jest.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index ca084133399..3ddb5a327d0 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, + branches: 31.14, + functions: 8.24, + lines: 31.4, + statements: 27.02, }, }, }); From b518f7f4790faed971d041429520ce70b1b9b6c5 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 12:34:54 +0800 Subject: [PATCH 13/54] feat: tests for getAccountExpect, listAccounts, getAccount --- packages/accounts-controller/jest.config.js | 4 +- .../src/AccountsController.test.ts | 130 +++++++++--------- 2 files changed, 69 insertions(+), 65 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 3ddb5a327d0..7e0df2a57eb 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,9 +17,9 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 31.14, + branches: 11.34, functions: 8.24, - lines: 31.4, + lines: 29.75, statements: 27.02, }, }, diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 3e90f092d8c..2514f3e3774 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -89,7 +89,7 @@ function setLastSelectedAsAny(account: InternalAccount): InternalAccount { ...account.metadata, lastSelected: expect.any(Number), }, - }; + } as InternalAccount; } /** @@ -361,80 +361,84 @@ describe('AccountsController', () => { // }); // }); - // describe('getAccount', () => { - // it('should return an account by ID', () => { - // const accountsController = setupAccountsController({ - // internalAccounts: { - // accounts: { [mockAccount.id]: mockAccount }, - // }, - // }); + describe('getAccount', () => { + it('should return an account by ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + }, + }); - // const result = accountsController.getAccount(mockAccount.id); + const result = accountsController.getAccount(mockAccount.id); - // expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); - // }); - // }); + expect(result).toStrictEqual( + setLastSelectedAsAny(mockAccount as InternalAccount), + ); + }); + }); - // it('should return undefined for an unknown account ID', () => { - // const accountsController = setupAccountsController({ - // internalAccounts: { - // accounts: { [mockAccount.id]: mockAccount }, - // }, - // }); + it('should return undefined for an unknown account ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + }, + }); - // const result = accountsController.getAccount("I don't exist"); + const result = accountsController.getAccount("I don't exist"); - // expect(result).toBeUndefined(); - // }); + expect(result).toBeUndefined(); + }); - // describe('listAccounts', () => { - // it('should return a list of accounts', () => { - // const accountsController = setupAccountsController({ - // internalAccounts: { - // accounts: { - // [mockAccount.id]: mockAccount, - // [mockAccount2.id]: mockAccount2, - // }, - // }, - // }); + describe('listAccounts', () => { + it('should return a list of accounts', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + }, + }); - // const result = accountsController.listAccounts(); + const result = accountsController.listAccounts(); - // expect(result).toEqual([ - // setLastSelectedAsAny(mockAccount), - // setLastSelectedAsAny(mockAccount2), - // ]); - // }); - // }); + expect(result).toStrictEqual([ + setLastSelectedAsAny(mockAccount as InternalAccount), + setLastSelectedAsAny(mockAccount2 as InternalAccount), + ]); + }); + }); - // describe('getAccountExpect', () => { - // it('should return an account by ID', () => { - // const accountsController = setupAccountsController({ - // internalAccounts: { - // accounts: { - // [mockAccount.id]: mockAccount, - // }, - // }, - // }); - // const result = accountsController.getAccountExpect(mockAccount.id); + describe('getAccountExpect', () => { + it('should return an account by ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + }, + }); + const result = accountsController.getAccountExpect(mockAccount.id); - // expect(result).toStrictEqual(setLastSelectedAsAny(mockAccount)); - // }); + expect(result).toStrictEqual( + setLastSelectedAsAny(mockAccount as InternalAccount), + ); + }); - // it('should throw an error for an unknown account ID', () => { - // const accountsController = setupAccountsController({ - // internalAccounts: { - // accounts: { - // [mockAccount.id]: mockAccount, - // }, - // }, - // }); + it('should throw an error for an unknown account ID', () => { + const accountsController = setupAccountsController({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + }, + }); - // expect(() => accountsController.getAccountExpect('unknown id')).toThrow( - // `Account Id unknown id not found`, - // ); - // }); - // }); + expect(() => accountsController.getAccountExpect('unknown id')).toThrow( + `Account Id unknown id not found`, + ); + }); + }); describe('getSelectedAccount', () => { it('should return the selected account', () => { From 77b05950606036e0ce936bf0ff4d6dcc82139859 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 15:36:50 +0800 Subject: [PATCH 14/54] feat: add tests for updateAccounts, loadBackup --- packages/accounts-controller/jest.config.js | 8 +- .../src/AccountsController.test.ts | 360 ++++++++++++------ .../src/AccountsController.ts | 10 +- 3 files changed, 252 insertions(+), 126 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 7e0df2a57eb..a6a08aa9812 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 11.34, - functions: 8.24, - lines: 29.75, - statements: 27.02, + branches: 23.46, + functions: 43.24, + lines: 53.27, + statements: 52.84, }, }, }); diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 2514f3e3774..26e43d8ab98 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,5 +1,6 @@ import { ControllerMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/eth-snap-keyring'; +import * as uuid from 'uuid'; import type { AccountsControllerActions, @@ -8,16 +9,8 @@ import type { } from './AccountsController'; import AccountsController from './AccountsController'; -const mockUUIDV4 = jest.fn(); - -jest.mock('uuid', () => { - const actual = jest.requireActual('uuid'); - - return { - ...actual, - v4: () => mockUUIDV4, - }; -}); +jest.mock('uuid'); +const mockUUID = jest.spyOn(uuid, 'v4'); const defaultState: AccountsControllerState = { internalAccounts: { @@ -30,7 +23,7 @@ const mockGetKeyringForAccount = jest.fn(); const mockGetKeyringByType = jest.fn(); const mockGetAccounts = jest.fn(); -const mockAccount = { +const mockAccount: InternalAccount = { name: 'Account 1', id: 'mock-id', address: '0x123', @@ -53,7 +46,7 @@ const mockAccount = { }, }; -const mockAccount2 = { +const mockAccount2: InternalAccount = { name: 'Account 2', id: 'mock-id2', address: '0x1234', @@ -76,6 +69,51 @@ const mockAccount2 = { }, }; +/** + * Creates an `InternalAccount` object from the given legacy account properties. + * + * @param props - The properties of the legacy account. + * @param props.id - The ID of the account. + * @param props.name - The name of the account. + * @param props.address - The address of the account. + * @param props.keyringType - The type of the keyring associated with the account. + * @returns The `InternalAccount` object created from the legacy account properties. + */ +function createExpectedInternalAccountFromLegacy({ + id, + name, + address, + keyringType, +}: { + id: string; + name: string; + address: string; + keyringType: string; +}): InternalAccount { + return { + id, + name, + address, + options: {}, + supportedMethods: [ + 'personal_sign', + 'eth_sendTransaction', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signTypedData_v1', + 'eth_signTypedData_v2', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + ], + type: 'eip155:eoa', + metadata: { + keyring: { type: keyringType }, + lastSelected: undefined, + }, + }; +} + /** * Sets the `lastSelected` property of the given `account` to `expect.any(Number)`. * @@ -121,28 +159,28 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { }); } -/** - * - * @param initialState - The initial state to use for the AccountsController. - * @param onKeyringStateChange - A callback to call when the keyring state changes. - * @param onSnapStateChange - A callback to call when the snap state changes. - */ /** * Sets up an instance of the AccountsController class with the given initial state and callbacks. * - * @param initialState - The initial state to use for the AccountsController. - * @param keyringApiEnabled - Whether or not the keyring API is enabled. - * @param onKeyringStateChange - A callback to call when the keyring state changes. - * @param onSnapStateChange - A callback to call when the snap state changes. + * @param options - The options object. + * @param [options.initialState] - The initial state to use for the AccountsController. + * @param [options.keyringApiEnabled] - Whether or not the keyring API is enabled. + * @param [options.onKeyringStateChange] - A callback to call when the keyring state changes. + * @param [options.onSnapStateChange] - A callback to call when the snap state changes. * @returns An instance of the AccountsController class. */ -function setupAccountsController( +function setupAccountsController({ // eslint-disable-next-line @typescript-eslint/default-param-last initialState = {}, keyringApiEnabled = true, onKeyringStateChange = () => jest.fn(), onSnapStateChange = () => jest.fn(), -): AccountsController { +}: { + initialState?: Partial; + keyringApiEnabled?: boolean; + onKeyringStateChange?: () => void; + onSnapStateChange?: () => void; +}): AccountsController { const accountsControllerMessenger = buildAccountsControllerMessenger( new ControllerMessenger(), ); @@ -248,29 +286,109 @@ describe('AccountsController', () => { // }); // }); - // describe('updateAccounts', () => { - // it('should update accounts with legacy accounts', async () => { - // - // const accountsController = new AccountsController(); - // const legacyAccounts = [ - // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; - // const snapAccounts = []; + describe('updateAccounts', () => { + const mockAddress1 = '0x123'; + const mockAddress2 = '0x456'; + const mockSnapAccount = { + ...mockAccount, + metadata: { + ...mockAccount.metadata, + keyring: { + type: 'Snap Keyring', + }, + snap: { + enabled: true, + id: 'mock-snap-id', + }, + lastSelected: undefined, + }, + }; + const mockSnapAccount2 = { + ...mockAccount2, + metadata: { + ...mockAccount2.metadata, + keyring: { + type: 'Snap Keyring', + }, + snap: { + enabled: true, + id: 'mock-snap-id2', + }, + lastSelected: undefined, + }, + }; - // - // jest - // .spyOn(accountsController, '#listLegacyAccounts') - // .mockResolvedValue(legacyAccounts); - // jest - // .spyOn(accountsController, '#listSnapAccounts') - // .mockResolvedValue(snapAccounts); - // await accountsController.updateAccounts(); + it('should update accounts with legacy accounts', async () => { + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([mockAddress1, mockAddress2]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: false, + }); + const expectedAccounts = [ + createExpectedInternalAccountFromLegacy({ + name: 'Account 1', + id: 'mock-id', + address: mockAddress1, + keyringType: 'HD Key Tree', + }), + createExpectedInternalAccountFromLegacy({ + name: 'Account 2', + id: 'mock-id2', + address: mockAddress2, + keyringType: 'HD Key Tree', + }), + ]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); + + it('should update accounts with snap accounts', async () => { + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([]); + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount, mockSnapAccount2], + }, + ]); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + }); + + const expectedAccount1 = { + ...mockSnapAccount, + name: 'Snap Account 1', + }; + + const expectedAccount2 = { + ...mockSnapAccount2, + name: 'Snap Account 2', + }; + + const expectedAccounts = [expectedAccount1, expectedAccount2]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); - // - // expect(accountsController.state.internalAccounts.accounts).toEqual( - // legacyAccounts, - // ); // }); // it('should update accounts with snap accounts', async () => { @@ -297,75 +415,70 @@ describe('AccountsController', () => { // ); // }); - // it('should remove duplicate accounts from snap and legacy accounts', async () => { - // - // const accountsController = new AccountsController(); - // const legacyAccounts = [ - // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; - // const snapAccounts = [ - // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; - // const expectedAccounts = [ - // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; + }); - // - // jest - // .spyOn(accountsController, '#listLegacyAccounts') - // .mockResolvedValue(legacyAccounts); - // jest - // .spyOn(accountsController, '#listSnapAccounts') - // .mockResolvedValue(snapAccounts); - // await accountsController.updateAccounts(); + describe('loadBackup', () => { + it('should load a backup', async () => { + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + }); - // - // expect(accountsController.state.internalAccounts.accounts).toEqual( - // expectedAccounts, - // ); - // }); + accountsController.loadBackup({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); - // it('should update keyring types', async () => { - // - // const accountsController = new AccountsController(); - // const legacyAccounts = [ - // { address: '0x123', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0x456', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; - // const snapAccounts = [ - // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0xabc', metadata: { keyring: { type: 'Ledger Keyring' } } }, - // ]; - // const expectedKeyringTypes = new Map([ - // ['Simple Keyring', 2], - // ['Ledger Keyring', 1], - // ]); + expect(accountsController.state).toStrictEqual({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + }); - // - // jest - // .spyOn(accountsController, '#listLegacyAccounts') - // .mockResolvedValue(legacyAccounts); - // jest - // .spyOn(accountsController, '#listSnapAccounts') - // .mockResolvedValue(snapAccounts); - // await accountsController.updateAccounts(); + it('should not load backup if the data is undefined', () => { + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + }); - // - // expect(accountsController.state.internalAccounts.keyringTypes).toEqual( - // expectedKeyringTypes, - // ); - // }); - // }); + // @ts-expect-error incorrect state + accountsController.loadBackup({}); + + expect(accountsController.state).toStrictEqual({ + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }); + }); + }); describe('getAccount', () => { it('should return an account by ID', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, }, }); @@ -379,8 +492,11 @@ describe('AccountsController', () => { it('should return undefined for an unknown account ID', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, }, }); @@ -392,10 +508,13 @@ describe('AccountsController', () => { describe('listAccounts', () => { it('should return a list of accounts', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount, [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, }, }, }); @@ -412,9 +531,10 @@ describe('AccountsController', () => { describe('getAccountExpect', () => { it('should return an account by ID', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, }, }, }); @@ -427,9 +547,10 @@ describe('AccountsController', () => { it('should throw an error for an unknown account ID', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, }, }, }); @@ -443,11 +564,11 @@ describe('AccountsController', () => { describe('getSelectedAccount', () => { it('should return the selected account', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, + accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, + }, }, }); const result = accountsController.getAccountExpect(mockAccount.id); @@ -461,12 +582,14 @@ describe('AccountsController', () => { describe('setSelectedAccount', () => { it('should set the selected account', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount, [mockAccount2.id]: mockAccount2, }, selectedAccount: mockAccount.id, + }, }, }); @@ -479,12 +602,14 @@ describe('AccountsController', () => { it('should throw an error for an unknown account ID', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount, [mockAccount2.id]: mockAccount2, }, selectedAccount: mockAccount.id, + }, }, }); @@ -497,14 +622,13 @@ describe('AccountsController', () => { describe('setAccountName', () => { it('should set the name of an existing account', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, + accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, + }, }, }); - accountsController.setAccountName(mockAccount.id, 'new name'); expect(accountsController.getAccountExpect(mockAccount.id).name).toBe( @@ -514,12 +638,14 @@ describe('AccountsController', () => { it('should throw an error if the account name already exists', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { accounts: { [mockAccount.id]: mockAccount, [mockAccount2.id]: mockAccount2, }, selectedAccount: mockAccount.id, + }, }, }); @@ -530,11 +656,11 @@ describe('AccountsController', () => { it('should throw an error if the account ID is not found', () => { const accountsController = setupAccountsController({ + initialState: { internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - }, + accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, + }, }, }); expect(() => diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 47dd824daf2..73b824a6843 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -342,9 +342,11 @@ export default class AccountsController extends BaseControllerV2< } loadBackup(backup: AccountsControllerState): void { - this.update((currentState: AccountsControllerState) => { - currentState.internalAccounts = backup.internalAccounts; - }); + if (backup.internalAccounts) { + this.update((currentState: AccountsControllerState) => { + currentState.internalAccounts = backup.internalAccounts; + }); + } } async #listSnapAccounts(): Promise { @@ -360,7 +362,6 @@ export default class AccountsController extends BaseControllerV2< snap: { id: snapId, enabled: true, - name: account.name, }, keyring: { type: (snapKeyring as SnapKeyring).type, @@ -377,7 +378,6 @@ export default class AccountsController extends BaseControllerV2< const internalAccounts: Omit[] = []; for (const address of addresses) { const keyring = await this.getKeyringForAccount(address); - // TODO: this is done until the keyrings all implement the InternalAccount interface const v4options = { random: sha256FromString(address).slice(0, 16), }; From 4fc9f9c3c1411d68e85235df7a72f79d3b58d138 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 19:54:17 +0800 Subject: [PATCH 15/54] feat: add onSnapStateChange tests --- packages/accounts-controller/jest.config.js | 8 +- .../src/AccountsController.test.ts | 422 ++++++++++++++---- .../src/AccountsController.ts | 23 +- 3 files changed, 348 insertions(+), 105 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index a6a08aa9812..11ce7513976 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 23.46, - functions: 43.24, - lines: 53.27, - statements: 52.84, + branches: 67.64, + functions: 67.64, + lines: 77.31, + statements: 59.78, }, }, }); diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 26e43d8ab98..4bbcc315a30 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,5 +1,8 @@ import { ControllerMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/eth-snap-keyring'; +import type { KeyringControllerState } from '@metamask/keyring-controller'; +import type { SnapControllerState } from '@metamask/snaps-controllers'; +import { SnapStatus } from '@metamask/snaps-utils'; import * as uuid from 'uuid'; import type { @@ -7,7 +10,7 @@ import type { AccountsControllerEvents, AccountsControllerState, } from './AccountsController'; -import AccountsController from './AccountsController'; +import AccountsController, { keyringTypeToName } from './AccountsController'; jest.mock('uuid'); const mockUUID = jest.spyOn(uuid, 'v4'); @@ -77,20 +80,26 @@ const mockAccount2: InternalAccount = { * @param props.name - The name of the account. * @param props.address - The address of the account. * @param props.keyringType - The type of the keyring associated with the account. + * @param props.snapId - The id of the snap. + * @param props.snapEnabled * @returns The `InternalAccount` object created from the legacy account properties. */ -function createExpectedInternalAccountFromLegacy({ +function createExpectedInternalAccount({ id, name, address, keyringType, + snapId, + snapEnabled = true, }: { id: string; name: string; address: string; keyringType: string; + snapId?: string; + snapEnabled?: boolean; }): InternalAccount { - return { + const account: InternalAccount = { id, name, address, @@ -112,6 +121,15 @@ function createExpectedInternalAccountFromLegacy({ lastSelected: undefined, }, }; + + if (snapId) { + account.metadata.snap = { + id: snapId, + enabled: Boolean(snapEnabled), + }; + } + + return account; } /** @@ -173,13 +191,15 @@ function setupAccountsController({ // eslint-disable-next-line @typescript-eslint/default-param-last initialState = {}, keyringApiEnabled = true, - onKeyringStateChange = () => jest.fn(), - onSnapStateChange = () => jest.fn(), + onKeyringStateChange = () => undefined, + onSnapStateChange = () => undefined, }: { initialState?: Partial; keyringApiEnabled?: boolean; - onKeyringStateChange?: () => void; - onSnapStateChange?: () => void; + onKeyringStateChange?: ( + listener: (state: KeyringControllerState) => void, + ) => void; + onSnapStateChange?: (listener: (state: SnapControllerState) => void) => void; }): AccountsController { const accountsControllerMessenger = buildAccountsControllerMessenger( new ControllerMessenger(), @@ -203,88 +223,168 @@ describe('AccountsController', () => { jest.clearAllMocks(); }); - // describe('onSnapStateChange', () => { - // it('should disable snap-enabled accounts when a snap is disabled', () => { - // - // const accountsController = new AccountsController(); - // const snapId = 'snap123'; - // const accountId = '0x123'; - // const account = { - // id: accountId, - // metadata: { snap: { id: snapId, enabled: true } }, - // }; - // accountsController.state.internalAccounts.accounts[accountId] = account; - - // const snapState = { - // snaps: { - // [snapId]: { id: snapId, enabled: false }, - // }, - // }; + describe('onSnapStateChange', () => { + it('should not be used when keyringApiEnabled is false', async () => { + const snapStateChangeSpy = jest.fn(); + setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: false, + onSnapStateChange: () => { + snapStateChangeSpy(); + }, + }); - // - // accountsController.onSnapStateChange(snapState); + expect(snapStateChangeSpy).not.toHaveBeenCalled(); + }); - // - // expect( - // accountsController.state.internalAccounts.accounts[accountId].metadata - // .snap.enabled, - // ).toBe(false); - // }); + it('should be used enable an account if the snap is enabled and not blocked', async () => { + const mockSnapAccount = createExpectedInternalAccount({ + id: 'mock-id', + name: 'Snap Account 1', + address: '0x0', + keyringType: 'Snap Keyring', + snapId: 'mock-snap', + snapEnabled: false, + }); + const mockSnapChangeState = { + snaps: { + 'mock-snap': { + enabled: true, + id: 'mock-snap', + blocked: false, + status: SnapStatus.Running, + }, + }, + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockSnapAccount.id]: mockSnapAccount, + }, + selectedAccount: mockSnapAccount.id, + }, + }, + keyringApiEnabled: true, + onSnapStateChange: (listener: (state: any) => void) => { + listener(mockSnapChangeState); + }, + }); - // it('should not disable snap-disabled accounts when a snap is disabled', () => { - // - // const accountsController = new AccountsController(); - // const snapId = 'snap123'; - // const accountId = '0x123'; - // const account = { - // id: accountId, - // metadata: { snap: { id: snapId, enabled: false } }, - // }; - // accountsController.state.internalAccounts.accounts[accountId] = account; - - // const snapState = { - // snaps: { - // [snapId]: { id: snapId, enabled: false }, - // }, - // }; + const updatedAccount = accountsController.getAccountExpect( + mockSnapAccount.id, + ); - // - // accountsController.onSnapStateChange(snapState); + expect(updatedAccount.metadata.snap?.enabled).toBe(true); + }); - // - // expect( - // accountsController.state.internalAccounts.accounts[accountId].metadata - // .snap.enabled, - // ).toBe(false); - // }); + it('should be used disable an account if the snap is disabled', async () => { + const mockSnapAccount = createExpectedInternalAccount({ + id: 'mock-id', + name: 'Snap Account 1', + address: '0x0', + keyringType: 'Snap Keyring', + snapId: 'mock-snap', + }); + const mockSnapChangeState = { + snaps: { + 'mock-snap': { + enabled: false, + id: 'mock-snap', + blocked: false, + status: SnapStatus.Running, + }, + }, + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockSnapAccount.id]: mockSnapAccount, + }, + selectedAccount: mockSnapAccount.id, + }, + }, + keyringApiEnabled: true, + onSnapStateChange: (listener: (state: any) => void) => { + listener(mockSnapChangeState); + }, + }); - // it('should not disable accounts when a snap is enabled', () => { - // - // const accountsController = new AccountsController(); - // const snapId = 'snap123'; - // const accountId = '0x123'; - // const account = { - // id: accountId, - // metadata: { snap: { id: snapId, enabled: true } }, - // }; - // accountsController.state.internalAccounts.accounts[accountId] = account; - - // const snapState = { - // snaps: { - // [snapId]: { id: snapId, enabled: true }, - // }, - // }; + const updatedAccount = accountsController.getAccountExpect( + mockSnapAccount.id, + ); - // - // accountsController.onSnapStateChange(snapState); + expect(updatedAccount.metadata.snap?.enabled).toBe(false); + }); - // - // expect( - // accountsController.state.internalAccounts.accounts[accountId].metadata - // .snap.enabled, - // ).toBe(true); - // }); - // }); + it('should be used disable an account if the snap is blocked', async () => { + const mockSnapAccount = createExpectedInternalAccount({ + id: 'mock-id', + name: 'Snap Account 1', + address: '0x0', + keyringType: 'Snap Keyring', + snapId: 'mock-snap', + }); + const mockSnapChangeState = { + snaps: { + 'mock-snap': { + enabled: true, + id: 'mock-snap', + blocked: true, + status: SnapStatus.Running, + }, + }, + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockSnapAccount.id]: mockSnapAccount, + }, + selectedAccount: mockSnapAccount.id, + }, + }, + keyringApiEnabled: true, + onSnapStateChange: (listener: (state: any) => void) => { + listener(mockSnapChangeState); + }, + }); + + const updatedAccount = accountsController.getAccountExpect( + mockSnapAccount.id, + ); + + expect(updatedAccount.metadata.snap?.enabled).toBe(false); + }); + }); + + describe('constructor', () => { + it('should select next account if selectedAccount is not found', async () => { + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: 'missing account', + }, + }, + keyringApiEnabled: true, + }); + + const selectedAccount = accountsController.getSelectedAccount(); + + expect(setLastSelectedAsAny(selectedAccount)).toStrictEqual( + setLastSelectedAsAny(mockAccount), + ); + }); + }); describe('updateAccounts', () => { const mockAddress1 = '0x123'; @@ -333,13 +433,13 @@ describe('AccountsController', () => { keyringApiEnabled: false, }); const expectedAccounts = [ - createExpectedInternalAccountFromLegacy({ + createExpectedInternalAccount({ name: 'Account 1', id: 'mock-id', address: mockAddress1, keyringType: 'HD Key Tree', }), - createExpectedInternalAccountFromLegacy({ + createExpectedInternalAccount({ name: 'Account 2', id: 'mock-id2', address: mockAddress2, @@ -353,7 +453,6 @@ describe('AccountsController', () => { }); it('should update accounts with snap accounts', async () => { - mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); mockGetAccounts.mockResolvedValue([]); mockGetKeyringByType.mockReturnValueOnce([ { @@ -389,6 +488,132 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); + it('should filter snap accounts from legacyAccounts', async () => { + mockUUID.mockReturnValueOnce('mock-id'); + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount2], + }, + ]); + + // first account will be legacy, second will be a snap account + mockGetAccounts.mockResolvedValue([mockAddress1, '0x1234']); + mockGetKeyringForAccount + .mockResolvedValueOnce({ type: 'HD Key Tree' }) + .mockResolvedValueOnce({ type: 'Snap Keyring' }); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + }); + const expectedAccounts = [ + createExpectedInternalAccount({ + name: 'Account 1', + id: 'mock-id', + address: mockAddress1, + keyringType: 'HD Key Tree', + }), + createExpectedInternalAccount({ + name: 'Snap Account 1', // it is Snap Account 1 because it is the only snap account + id: mockSnapAccount2.id, + address: mockSnapAccount2.address, + keyringType: 'Snap Keyring', + snapId: 'mock-snap-id2', + }), + ]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); + + it('should filter snap accounts from legacyAccounts even if the snap account is listed before legacy accounts', async () => { + mockUUID.mockReturnValue('mock-id'); + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount2], + }, + ]); + + // first account will be legacy, second will be a snap account + mockGetAccounts.mockResolvedValue(['0x1234', mockAddress1]); + mockGetKeyringForAccount + .mockResolvedValueOnce({ type: 'Snap Keyring' }) + .mockResolvedValueOnce({ type: 'HD Key Tree' }); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + }); + const expectedAccounts = [ + createExpectedInternalAccount({ + name: 'Account 1', + id: 'mock-id', + address: mockAddress1, + keyringType: 'HD Key Tree', + }), + createExpectedInternalAccount({ + name: 'Snap Account 1', // it is Snap Account 1 because it is the only snap account + id: mockSnapAccount2.id, + address: mockSnapAccount2.address, + keyringType: 'Snap Keyring', + snapId: 'mock-snap-id2', + }), + ]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); + + it.each([ + 'Simple Key Pair', + 'HD Key Tree', + 'Trezor Hardware', + 'Ledger Hardware', + 'Lattice Hardware', + 'QR Hardware Wallet Device', + 'Custody', + ])('should add accounts for %s type', async (keyringType) => { + mockUUID.mockReturnValue('mock-id'); + mockGetAccounts.mockResolvedValue([mockAddress1]); + mockGetKeyringForAccount.mockResolvedValue({ type: keyringType }); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: false, + }); + + const expectedAccounts = [ + createExpectedInternalAccount({ + name: `${keyringTypeToName(keyringType)} 1`, + id: 'mock-id', + address: mockAddress1, + keyringType, + }), + ]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); // }); // it('should update accounts with snap accounts', async () => { @@ -488,8 +713,6 @@ describe('AccountsController', () => { setLastSelectedAsAny(mockAccount as InternalAccount), ); }); - }); - it('should return undefined for an unknown account ID', () => { const accountsController = setupAccountsController({ initialState: { @@ -503,6 +726,7 @@ describe('AccountsController', () => { const result = accountsController.getAccount("I don't exist"); expect(result).toBeUndefined(); + }); }); describe('listAccounts', () => { @@ -559,6 +783,32 @@ describe('AccountsController', () => { `Account Id unknown id not found`, ); }); + + it('should handle the edge case of undefined accountId during onboarding', async () => { + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + }); + + // @ts-expect-error forcing undefined accountId + expect(accountsController.getAccountExpect(undefined)).toStrictEqual({ + id: '', + name: '', + address: '', + options: {}, + supportedMethods: [], + type: 'eip155:eoa', + metadata: { + keyring: { + type: '', + }, + }, + }); + }); }); describe('getSelectedAccount', () => { diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 73b824a6843..8923a84d6de 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -132,26 +132,18 @@ export default class AccountsController extends BaseControllerV2< const { snaps } = snapState; const accounts = this.listAccounts(); - const disabledSnaps: Snap[] = Object.values(snaps).filter( - (snap) => !snap.enabled || !snap.blocked, - ); - - const accountsToUpdate = accounts.filter( - (account) => - account.metadata.snap && - disabledSnaps.find((snap) => snap.id === account.metadata.snap?.id), - ); - this.update((currentState: AccountsControllerState) => { - accountsToUpdate.forEach((account) => { - if ( - currentState.internalAccounts.accounts[account.id]?.metadata?.snap - ) { + accounts.forEach((account) => { + const currentAccount = + currentState.internalAccounts.accounts[account.id]; + if (currentAccount?.metadata?.snap) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this account is guaranteed to have snap metadata currentState.internalAccounts.accounts[ account.id - ].metadata.snap.enabled = false; + ].metadata.snap.enabled = + snaps[currentAccount.metadata.snap.id].enabled && + !snaps[currentAccount.metadata.snap.id].blocked; } }); }); @@ -483,6 +475,7 @@ export function keyringTypeToName(keyringType: string): string { return 'Custody'; } default: { + console.warn(`Unknown keyring ${keyringType}`); return 'Account'; } } From a59fab568cd6781ecfd29aecdbce23b61d1c8e9e Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 21:15:59 +0800 Subject: [PATCH 16/54] feat: add onKeyringStateChange tests --- packages/accounts-controller/jest.config.js | 8 +- .../src/AccountsController.test.ts | 173 +++++++++++++++++- .../src/AccountsController.ts | 12 +- 3 files changed, 182 insertions(+), 11 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 11ce7513976..2ffea431d23 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 67.64, - functions: 67.64, - lines: 77.31, - statements: 59.78, + branches: 75, + functions: 97.05, + lines: 97.47, + statements: 97.5, }, }, }); diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 4bbcc315a30..5e5d2c6118b 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -81,7 +81,7 @@ const mockAccount2: InternalAccount = { * @param props.address - The address of the account. * @param props.keyringType - The type of the keyring associated with the account. * @param props.snapId - The id of the snap. - * @param props.snapEnabled + * @param props.snapEnabled - The status of the snap * @returns The `InternalAccount` object created from the legacy account properties. */ function createExpectedInternalAccount({ @@ -364,6 +364,177 @@ describe('AccountsController', () => { }); }); + describe('onKeyringStateChange', () => { + it('should only update if the keyring is unlocked', async () => { + const mockNewKeyringState = { + isUnlocked: false, + keyrings: [ + { + accounts: [mockAccount.address, mockAccount2.address], + type: 'HD Key Tree', + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([]); + }); + + it('should add new accounts', async () => { + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [mockAccount.address, mockAccount2.address], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + }, + selectedAccount: mockAccount.id, + }, + }, + keyringApiEnabled: false, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([ + mockAccount, + setLastSelectedAsAny(mockAccount2), + ]); + }); + + it('should handle keyring reinitialization', async () => { + const mockInitialAccount = createExpectedInternalAccount({ + id: 'mock-id', + name: 'Account 1', + address: '0x123', + keyringType: 'HD Key Tree', + }); + const mockReinitialisedAccount = createExpectedInternalAccount({ + id: 'mock-id2', + name: 'Account 1', + address: '0x456', + keyringType: 'HD Key Tree', + }); + mockUUID.mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([mockReinitialisedAccount.address]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [mockReinitialisedAccount.address], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockInitialAccount.id]: mockInitialAccount, + }, + selectedAccount: mockInitialAccount.id, + }, + }, + keyringApiEnabled: false, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const selectedAccount = accountsController.getSelectedAccount(); + const accounts = accountsController.listAccounts(); + const expectedAccount = { + ...setLastSelectedAsAny(mockReinitialisedAccount), + name: 'Account 1', // since there aren't any other accounts it will be account 1 + }; + + expect(selectedAccount).toStrictEqual(expectedAccount); + expect(accounts).toStrictEqual([expectedAccount]); + }); + + it('should delete accounts if its gone from the keyring state', async () => { + mockUUID.mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([mockAccount2.address]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [mockAccount2.address], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, + }, + }, + keyringApiEnabled: false, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([setLastSelectedAsAny(mockAccount2)]); + expect(accountsController.getSelectedAccount()).toStrictEqual( + setLastSelectedAsAny(mockAccount2), + ); + }); + }); + describe('constructor', () => { it('should select next account if selectedAccount is not found', async () => { const accountsController = setupAccountsController({ diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 8923a84d6de..96f16df632d 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -11,7 +11,6 @@ import type { SnapControllerEvents, SnapControllerState, } from '@metamask/snaps-controllers'; -import type { Snap } from '@metamask/snaps-utils'; import { sha256FromString } from 'ethereumjs-util'; import type { Patch } from 'immer'; import { v4 as uuid } from 'uuid'; @@ -136,7 +135,7 @@ export default class AccountsController extends BaseControllerV2< accounts.forEach((account) => { const currentAccount = currentState.internalAccounts.accounts[account.id]; - if (currentAccount?.metadata?.snap) { + if (currentAccount && currentAccount.metadata.snap) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this account is guaranteed to have snap metadata currentState.internalAccounts.accounts[ @@ -298,7 +297,7 @@ export default class AccountsController extends BaseControllerV2< const accounts: Record = [ ...legacyAccounts, - ...(this.keyringApiEnabled ? snapAccounts : []), + ...snapAccounts, ].reduce((internalAccountMap, internalAccount) => { const keyringTypeName = keyringTypeToName( internalAccount.metadata.keyring.type, @@ -344,11 +343,12 @@ export default class AccountsController extends BaseControllerV2< async #listSnapAccounts(): Promise { const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); - const snapAccounts = - (await (snapKeyring as SnapKeyring)?.listAccounts(false)) ?? []; + const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); for (const account of snapAccounts) { - const snapId = account.metadata.snap?.id as string; + // The snap account is guaranteed to have a snap metadata + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const snapId = account.metadata.snap!.id!; account.metadata = { snap: { From cbd11a48ba1a999083b0805c4250240f1a841b88 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 21:20:48 +0800 Subject: [PATCH 17/54] chore: lint --- .../src/AccountsController.test.ts | 110 +++++++----------- 1 file changed, 42 insertions(+), 68 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 5e5d2c6118b..4d27a1a86c2 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -785,32 +785,6 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); - // }); - - // it('should update accounts with snap accounts', async () => { - // - // const accountsController = new AccountsController(); - // const legacyAccounts = []; - // const snapAccounts = [ - // { address: '0x789', metadata: { keyring: { type: 'Simple Keyring' } } }, - // { address: '0xabc', metadata: { keyring: { type: 'Simple Keyring' } } }, - // ]; - - // - // jest - // .spyOn(accountsController, '#listLegacyAccounts') - // .mockResolvedValue(legacyAccounts); - // jest - // .spyOn(accountsController, '#listSnapAccounts') - // .mockResolvedValue(snapAccounts); - // await accountsController.updateAccounts(); - - // - // expect(accountsController.state.internalAccounts.accounts).toEqual( - // snapAccounts, - // ); - // }); - }); describe('loadBackup', () => { @@ -871,8 +845,8 @@ describe('AccountsController', () => { it('should return an account by ID', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, }, }, @@ -884,19 +858,19 @@ describe('AccountsController', () => { setLastSelectedAsAny(mockAccount as InternalAccount), ); }); - it('should return undefined for an unknown account ID', () => { - const accountsController = setupAccountsController({ - initialState: { - internalAccounts: { - accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, + it('should return undefined for an unknown account ID', () => { + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, }, - }, - }); + }); - const result = accountsController.getAccount("I don't exist"); + const result = accountsController.getAccount("I don't exist"); - expect(result).toBeUndefined(); + expect(result).toBeUndefined(); }); }); @@ -904,10 +878,10 @@ describe('AccountsController', () => { it('should return a list of accounts', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockAccount2.id]: mockAccount2, + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, }, selectedAccount: mockAccount.id, }, @@ -927,7 +901,7 @@ describe('AccountsController', () => { it('should return an account by ID', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { + internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, }, @@ -943,7 +917,7 @@ describe('AccountsController', () => { it('should throw an error for an unknown account ID', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { + internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, selectedAccount: mockAccount.id, }, @@ -986,9 +960,9 @@ describe('AccountsController', () => { it('should return the selected account', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { + internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, + selectedAccount: mockAccount.id, }, }, }); @@ -1004,12 +978,12 @@ describe('AccountsController', () => { it('should set the selected account', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockAccount2.id]: mockAccount2, - }, - selectedAccount: mockAccount.id, + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, }, }, }); @@ -1024,12 +998,12 @@ describe('AccountsController', () => { it('should throw an error for an unknown account ID', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockAccount2.id]: mockAccount2, - }, - selectedAccount: mockAccount.id, + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, }, }, }); @@ -1044,9 +1018,9 @@ describe('AccountsController', () => { it('should set the name of an existing account', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { + internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, + selectedAccount: mockAccount.id, }, }, }); @@ -1060,12 +1034,12 @@ describe('AccountsController', () => { it('should throw an error if the account name already exists', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { - accounts: { - [mockAccount.id]: mockAccount, - [mockAccount2.id]: mockAccount2, - }, - selectedAccount: mockAccount.id, + internalAccounts: { + accounts: { + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: mockAccount.id, }, }, }); @@ -1078,9 +1052,9 @@ describe('AccountsController', () => { it('should throw an error if the account ID is not found', () => { const accountsController = setupAccountsController({ initialState: { - internalAccounts: { + internalAccounts: { accounts: { [mockAccount.id]: mockAccount }, - selectedAccount: mockAccount.id, + selectedAccount: mockAccount.id, }, }, }); From 4c272a71bb8892dcdaddddca98e174bc061898e7 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 30 Aug 2023 21:51:49 +0800 Subject: [PATCH 18/54] chore: fix lint in package.json --- packages/accounts-controller/package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 306422eef28..816675f3f54 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -28,17 +28,17 @@ "test:watch": "jest --watch" }, "dependencies": { - "@metamask/base-controller": "^3.2.0", + "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0", - "@metamask/keyring-controller": "^7.1.0", + "@metamask/keyring-controller": "^7.2.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", "eth-rpc-errors": "^4.0.2", - "ethereumjs-util": "^7.1.5", + "ethereumjs-util": "^7.0.10", "immer": "^9.0.6", "nanoid": "^3.1.31", - "uuid": "^9.0.0" + "uuid": "^8.3.2" }, "devDependencies": { "@metamask/auto-changelog": "^3.1.0", @@ -52,6 +52,9 @@ "typedoc-plugin-missing-exports": "^0.22.6", "typescript": "~4.6.3" }, + "peerDependencies": { + "@metamask/keyring-controller": "^7.2.0" + }, "engines": { "node": ">=16.0.0" }, From 4b781ce3028bcf42a3b22f0b488fb24c98dac32a Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 31 Aug 2023 10:00:37 +0800 Subject: [PATCH 19/54] chore: update yarn.lock --- yarn.lock | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index df2396010ee..1d227b611cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,14 +1287,14 @@ __metadata: languageName: node linkType: hard -"@metamask/accounts-controller@workspace:packages/accounts-controller": +"@metamask/accounts-controller@^1.0.0, @metamask/accounts-controller@workspace:packages/accounts-controller": version: 0.0.0-use.local resolution: "@metamask/accounts-controller@workspace:packages/accounts-controller" dependencies: "@metamask/auto-changelog": ^3.1.0 - "@metamask/base-controller": ^3.2.0 + "@metamask/base-controller": ^3.2.1 "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" - "@metamask/keyring-controller": ^7.1.0 + "@metamask/keyring-controller": ^7.2.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 "@metamask/utils": ^6.2.0 @@ -1302,7 +1302,7 @@ __metadata: "@types/readable-stream": ^2.3.0 deepmerge: ^4.2.2 eth-rpc-errors: ^4.0.2 - ethereumjs-util: ^7.1.5 + ethereumjs-util: ^7.0.10 immer: ^9.0.6 jest: ^27.5.1 nanoid: ^3.1.31 @@ -1311,7 +1311,9 @@ __metadata: typedoc: ^0.22.15 typedoc-plugin-missing-exports: ^0.22.6 typescript: ~4.6.3 - uuid: ^9.0.0 + uuid: ^8.3.2 + peerDependencies: + "@metamask/keyring-controller": ^7.2.0 languageName: unknown linkType: soft @@ -1391,12 +1393,14 @@ __metadata: "@ethersproject/contracts": ^5.7.0 "@ethersproject/providers": ^5.7.0 "@metamask/abi-utils": ^2.0.1 + "@metamask/accounts-controller": ^1.0.0 "@metamask/approval-controller": ^3.5.1 "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 "@metamask/contract-metadata": ^2.3.1 "@metamask/controller-utils": ^4.3.2 "@metamask/eth-query": ^3.0.1 + "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" "@metamask/metamask-eth-abis": 3.0.0 "@metamask/network-controller": ^12.1.2 "@metamask/preferences-controller": ^4.4.0 @@ -1423,6 +1427,7 @@ __metadata: typescript: ~4.6.3 uuid: ^8.3.2 peerDependencies: + "@metamask/accounts-controller": ^1.0.0 "@metamask/approval-controller": ^3.5.1 "@metamask/network-controller": ^12.1.2 "@metamask/preferences-controller": ^4.4.0 @@ -1443,7 +1448,7 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@^3.0.0, @metamask/base-controller@^3.2.0, @metamask/base-controller@^3.2.1, @metamask/base-controller@workspace:packages/base-controller": +"@metamask/base-controller@^3.0.0, @metamask/base-controller@^3.2.1, @metamask/base-controller@workspace:packages/base-controller": version: 0.0.0-use.local resolution: "@metamask/base-controller@workspace:packages/base-controller" dependencies: @@ -1844,7 +1849,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@^7.1.0, @metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/keyring-controller@^7.2.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: @@ -5256,7 +5261,7 @@ __metadata: languageName: node linkType: hard -"ethereumjs-util@npm:^7.0.10, ethereumjs-util@npm:^7.0.8, ethereumjs-util@npm:^7.1.2, ethereumjs-util@npm:^7.1.5": +"ethereumjs-util@npm:^7.0.10, ethereumjs-util@npm:^7.0.8, ethereumjs-util@npm:^7.1.2": version: 7.1.5 resolution: "ethereumjs-util@npm:7.1.5" dependencies: From 545dd1738f7a1767ea22f6497f68b506dc30b4f1 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 31 Aug 2023 10:11:04 +0800 Subject: [PATCH 20/54] fix: change from default export and fix version --- packages/accounts-controller/package.json | 2 +- packages/accounts-controller/src/AccountsController.test.ts | 2 +- packages/accounts-controller/src/AccountsController.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 816675f3f54..abfe928ce73 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/accounts-controller", - "version": "3.5.0", + "version": "1.0.0", "description": "Manages internal accounts", "keywords": [ "MetaMask", diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 4d27a1a86c2..1ad345d3dcf 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -10,7 +10,7 @@ import type { AccountsControllerEvents, AccountsControllerState, } from './AccountsController'; -import AccountsController, { keyringTypeToName } from './AccountsController'; +import { AccountsController, keyringTypeToName } from './AccountsController'; jest.mock('uuid'); const mockUUID = jest.spyOn(uuid, 'v4'); diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 96f16df632d..a7711202fec 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -73,7 +73,7 @@ const defaultState: AccountsControllerState = { }, }; -export default class AccountsController extends BaseControllerV2< +export class AccountsController extends BaseControllerV2< typeof controllerName, AccountsControllerState, AccountsControllerMessenger @@ -98,7 +98,7 @@ export default class AccountsController extends BaseControllerV2< }: { messenger: AccountsControllerMessenger; state: AccountsControllerState; - keyringApiEnabled: boolean; + keyringApiEnabled?: boolean; getKeyringForAccount: KeyringController['getKeyringForAccount']; getKeyringByType: KeyringController['getKeyringsByType']; getAccounts: KeyringController['getAccounts']; From 40e814445004aac47b58145b08e571e9beb6c575 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 4 Sep 2023 18:53:18 +0800 Subject: [PATCH 21/54] fix: update tests and bump coverage --- packages/accounts-controller/jest.config.js | 8 +- packages/accounts-controller/package.json | 1 + .../src/AccountsController.test.ts | 189 +++++++++++++++--- .../src/AccountsController.ts | 50 +++-- yarn.lock | 160 ++++++++++++++- 5 files changed, 359 insertions(+), 49 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 2ffea431d23..266acba02b7 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,10 +17,10 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 75, - functions: 97.05, - lines: 97.47, - statements: 97.5, + branches: 92.59, + functions: 100, + lines: 100, + statements: 100, }, }, }); diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index abfe928ce73..6dc13466a13 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -30,6 +30,7 @@ "dependencies": { "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0", + "@metamask/keyring-api": "0.2.1", "@metamask/keyring-controller": "^7.2.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 1ad345d3dcf..2961c096cbe 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,5 +1,5 @@ import { ControllerMessenger } from '@metamask/base-controller'; -import type { InternalAccount } from '@metamask/eth-snap-keyring'; +import type { InternalAccount } from '@metamask/keyring-api'; import type { KeyringControllerState } from '@metamask/keyring-controller'; import type { SnapControllerState } from '@metamask/snaps-controllers'; import { SnapStatus } from '@metamask/snaps-utils'; @@ -27,48 +27,48 @@ const mockGetKeyringByType = jest.fn(); const mockGetAccounts = jest.fn(); const mockAccount: InternalAccount = { - name: 'Account 1', id: 'mock-id', address: '0x123', options: {}, - supportedMethods: [ + methods: [ 'personal_sign', - 'eth_sendTransaction', 'eth_sign', 'eth_signTransaction', 'eth_signTypedData', 'eth_signTypedData_v1', - 'eth_signTypedData_v2', 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], type: 'eip155:eoa', metadata: { + name: 'Account 1', keyring: { type: 'HD Key Tree' }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore lastSelected: 1691565967656, }, }; const mockAccount2: InternalAccount = { - name: 'Account 2', id: 'mock-id2', address: '0x1234', options: {}, - supportedMethods: [ + methods: [ 'personal_sign', - 'eth_sendTransaction', 'eth_sign', 'eth_signTransaction', 'eth_signTypedData', 'eth_signTypedData_v1', - 'eth_signTypedData_v2', 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], type: 'eip155:eoa', metadata: { + name: 'Account 2', keyring: { type: 'HD Key Tree' }, - lastSelected: 1691565967656, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + lastSelected: 1955565967656, }, }; @@ -101,23 +101,23 @@ function createExpectedInternalAccount({ }): InternalAccount { const account: InternalAccount = { id, - name, address, options: {}, - supportedMethods: [ + methods: [ 'personal_sign', - 'eth_sendTransaction', 'eth_sign', 'eth_signTransaction', 'eth_signTypedData', 'eth_signTypedData_v1', - 'eth_signTypedData_v2', 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], type: 'eip155:eoa', metadata: { + name, keyring: { type: keyringType }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore lastSelected: undefined, }, }; @@ -125,6 +125,7 @@ function createExpectedInternalAccount({ if (snapId) { account.metadata.snap = { id: snapId, + name: '', enabled: Boolean(snapEnabled), }; } @@ -484,10 +485,7 @@ describe('AccountsController', () => { const selectedAccount = accountsController.getSelectedAccount(); const accounts = accountsController.listAccounts(); - const expectedAccount = { - ...setLastSelectedAsAny(mockReinitialisedAccount), - name: 'Account 1', // since there aren't any other accounts it will be account 1 - }; + const expectedAccount = setLastSelectedAsAny(mockReinitialisedAccount); expect(selectedAccount).toStrictEqual(expectedAccount); expect(accounts).toStrictEqual([expectedAccount]); @@ -533,6 +531,113 @@ describe('AccountsController', () => { setLastSelectedAsAny(mockAccount2), ); }); + + it('should delete accounts and set the most recent lastSelected account', async () => { + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [mockAccount.address, mockAccount2.address], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + 'missing-account': { + address: '0x999', + } as InternalAccount, + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2, + }, + selectedAccount: 'missing-account', + }, + }, + keyringApiEnabled: false, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([ + setLastSelectedAsAny(mockAccount), + setLastSelectedAsAny(mockAccount2), + ]); + expect(accountsController.getSelectedAccount()).toStrictEqual( + setLastSelectedAsAny(mockAccount2), + ); + }); + + it('should delete accounts and set the most recent lastSelected account when there are accounts that have never been selected', async () => { + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]); + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const mockAccount2WithoutLastSelected = { + ...mockAccount2, + metadata: { + ...mockAccount2.metadata, + lastSelected: undefined, + }, + }; + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [mockAccount.address, mockAccount2.address], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + 'missing-account': { + address: '0x999', + } as InternalAccount, + [mockAccount.id]: mockAccount, + [mockAccount2.id]: mockAccount2WithoutLastSelected, + }, + selectedAccount: 'missing-account', + }, + }, + keyringApiEnabled: false, + onKeyringStateChange: ( + listener: (state: KeyringControllerState) => void, + ) => { + listener(mockNewKeyringState); + }, + }); + + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([ + setLastSelectedAsAny(mockAccount), + mockAccount2WithoutLastSelected, + ]); + expect(accountsController.getSelectedAccount()).toStrictEqual( + setLastSelectedAsAny(mockAccount), + ); + }); }); describe('constructor', () => { @@ -570,6 +675,7 @@ describe('AccountsController', () => { snap: { enabled: true, id: 'mock-snap-id', + name: '', }, lastSelected: undefined, }, @@ -584,6 +690,7 @@ describe('AccountsController', () => { snap: { enabled: true, id: 'mock-snap-id2', + name: '', }, lastSelected: undefined, }, @@ -623,7 +730,7 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); - it('should update accounts with snap accounts', async () => { + it('should update accounts with snap accounts when snap keyring is defined and has accounts', async () => { mockGetAccounts.mockResolvedValue([]); mockGetKeyringByType.mockReturnValueOnce([ { @@ -644,12 +751,18 @@ describe('AccountsController', () => { const expectedAccount1 = { ...mockSnapAccount, - name: 'Snap Account 1', + metadata: { + ...mockSnapAccount.metadata, + name: 'Snap Account 1', + }, }; const expectedAccount2 = { ...mockSnapAccount2, - name: 'Snap Account 2', + metadata: { + ...mockSnapAccount2.metadata, + name: 'Snap Account 2', + }, }; const expectedAccounts = [expectedAccount1, expectedAccount2]; @@ -659,6 +772,27 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); + it('should return an empty array if the snap keyring is not defined', async () => { + mockGetAccounts.mockResolvedValue([]); + mockGetKeyringByType.mockReturnValueOnce([undefined]); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + }); + + const expectedAccounts: InternalAccount[] = []; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); + it('should filter snap accounts from legacyAccounts', async () => { mockUUID.mockReturnValueOnce('mock-id'); mockGetKeyringByType.mockReturnValueOnce([ @@ -757,6 +891,7 @@ describe('AccountsController', () => { 'Lattice Hardware', 'QR Hardware Wallet Device', 'Custody', + 'Unknown', ])('should add accounts for %s type', async (keyringType) => { mockUUID.mockReturnValue('mock-id'); mockGetAccounts.mockResolvedValue([mockAddress1]); @@ -888,6 +1023,8 @@ describe('AccountsController', () => { }, }); + console.log(mockAccount, mockAccount2); + const result = accountsController.listAccounts(); expect(result).toStrictEqual([ @@ -942,12 +1079,12 @@ describe('AccountsController', () => { // @ts-expect-error forcing undefined accountId expect(accountsController.getAccountExpect(undefined)).toStrictEqual({ id: '', - name: '', address: '', options: {}, - supportedMethods: [], + methods: [], type: 'eip155:eoa', metadata: { + name: '', keyring: { type: '', }, @@ -1026,9 +1163,9 @@ describe('AccountsController', () => { }); accountsController.setAccountName(mockAccount.id, 'new name'); - expect(accountsController.getAccountExpect(mockAccount.id).name).toBe( - 'new name', - ); + expect( + accountsController.getAccountExpect(mockAccount.id).metadata.name, + ).toBe('new name'); }); it('should throw an error if the account name already exists', () => { diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index a7711202fec..25b9655bc95 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -1,7 +1,7 @@ import type { RestrictedControllerMessenger } from '@metamask/base-controller'; import { BaseControllerV2 } from '@metamask/base-controller'; -import type { InternalAccount } from '@metamask/eth-snap-keyring'; import { SnapKeyring } from '@metamask/eth-snap-keyring'; +import type { InternalAccount } from '@metamask/keyring-api'; import type { KeyringControllerState, KeyringController, @@ -135,7 +135,7 @@ export class AccountsController extends BaseControllerV2< accounts.forEach((account) => { const currentAccount = currentState.internalAccounts.accounts[account.id]; - if (currentAccount && currentAccount.metadata.snap) { + if (currentAccount?.metadata?.snap) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this account is guaranteed to have snap metadata currentState.internalAccounts.accounts[ @@ -218,12 +218,12 @@ export class AccountsController extends BaseControllerV2< if (!accountId) { return { id: '', - name: '', address: '', options: {}, - supportedMethods: [], + methods: [], type: 'eip155:eoa', metadata: { + name: '', keyring: { type: '', }, @@ -248,6 +248,8 @@ export class AccountsController extends BaseControllerV2< // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Type instantiation is excessively deep and possibly infinite. this.update((currentState: AccountsControllerState) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore lastSelected will be added in 0.2.2 currentState.internalAccounts.accounts[account.id].metadata.lastSelected = Date.now(); currentState.internalAccounts.selectedAccount = account.id; @@ -262,7 +264,7 @@ export class AccountsController extends BaseControllerV2< if ( this.listAccounts().find( (internalAccount) => - internalAccount.name === accountName && + internalAccount.metadata.name === accountName && internalAccount.id !== accountId, ) ) { @@ -272,7 +274,10 @@ export class AccountsController extends BaseControllerV2< this.update((currentState: AccountsControllerState) => { currentState.internalAccounts.accounts[accountId] = { ...account, - name: accountName, + metadata: { + ...account.metadata, + name: accountName, + }, }; }); } @@ -313,11 +318,15 @@ export class AccountsController extends BaseControllerV2< internalAccountMap[internalAccount.id] = { ...internalAccount, - name: existingAccount - ? existingAccount.name - : `${keyringTypeName} ${keyringAccountIndex + 1}`, + metadata: { ...internalAccount.metadata, + name: + existingAccount && existingAccount.metadata.name !== '' + ? existingAccount.metadata.name + : `${keyringTypeName} ${keyringAccountIndex + 1}`, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore lastSelected will be added in 0.2.2 lastSelected: existingAccount?.metadata?.lastSelected, }, }; @@ -342,6 +351,10 @@ export class AccountsController extends BaseControllerV2< async #listSnapAccounts(): Promise { const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); + // snap keyring is not available until the first account is created in the keyring controller + if (!snapKeyring) { + return []; + } const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); @@ -354,20 +367,26 @@ export class AccountsController extends BaseControllerV2< snap: { id: snapId, enabled: true, + name: '', }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore snap keyring not updated yet + name: '', keyring: { type: (snapKeyring as SnapKeyring).type, }, }; } + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore lastSelected will be added in 0.2.2 return snapAccounts; } // Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface - async #listLegacyAccounts(): Promise[]> { + async #listLegacyAccounts(): Promise { const addresses = await this.getAccounts(); - const internalAccounts: Omit[] = []; + const internalAccounts: InternalAccount[] = []; for (const address of addresses) { const keyring = await this.getKeyringForAccount(address); const v4options = { @@ -378,19 +397,18 @@ export class AccountsController extends BaseControllerV2< id: uuid(v4options), address, options: {}, - supportedMethods: [ + methods: [ 'personal_sign', - 'eth_sendTransaction', 'eth_sign', 'eth_signTransaction', 'eth_signTypedData', 'eth_signTypedData_v1', - 'eth_signTypedData_v2', 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], type: 'eip155:eoa', metadata: { + name: '', keyring: { type: (keyring as any).type as string, }, @@ -411,7 +429,11 @@ export class AccountsController extends BaseControllerV2< .sort((accountA, accountB) => { // sort by lastSelected descending return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore (accountB.metadata?.lastSelected ?? 0) - + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore (accountA.metadata?.lastSelected ?? 0) ); })[0]; diff --git a/yarn.lock b/yarn.lock index 1d227b611cb..62116402556 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1294,6 +1294,7 @@ __metadata: "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" + "@metamask/keyring-api": 0.2.1 "@metamask/keyring-controller": ^7.2.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 @@ -1363,7 +1364,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/approval-controller@^3.0.0, @metamask/approval-controller@^3.5.1, @metamask/approval-controller@workspace:packages/approval-controller": +"@metamask/approval-controller@^3.0.0, @metamask/approval-controller@^3.5.0, @metamask/approval-controller@^3.5.1, @metamask/approval-controller@workspace:packages/approval-controller": version: 0.0.0-use.local resolution: "@metamask/approval-controller@workspace:packages/approval-controller" dependencies: @@ -1448,7 +1449,7 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@^3.0.0, @metamask/base-controller@^3.2.1, @metamask/base-controller@workspace:packages/base-controller": +"@metamask/base-controller@^3.0.0, @metamask/base-controller@^3.2.0, @metamask/base-controller@^3.2.1, @metamask/base-controller@workspace:packages/base-controller": version: 0.0.0-use.local resolution: "@metamask/base-controller@workspace:packages/base-controller" dependencies: @@ -1834,6 +1835,36 @@ __metadata: languageName: node linkType: hard +"@metamask/key-tree@npm:^9.0.0": + version: 9.0.0 + resolution: "@metamask/key-tree@npm:9.0.0" + dependencies: + "@metamask/scure-bip39": ^2.1.0 + "@metamask/utils": ^6.0.1 + "@noble/ed25519": ^1.6.0 + "@noble/hashes": ^1.0.0 + "@noble/secp256k1": ^1.5.5 + "@scure/base": ^1.0.0 + checksum: 5c81f07351ca59b37570d52edcc80d60424630b2a8403ed7149c3343c264878ac5d3fc0584a61635ea7ddda4a789295ded1247846606dc529d8e2fd42f6fc61a + languageName: node + linkType: hard + +"@metamask/keyring-api@npm:0.2.1": + version: 0.2.1 + resolution: "@metamask/keyring-api@npm:0.2.1" + dependencies: + "@metamask/providers": ^11.0.0 + "@metamask/rpc-methods": ^0.38.1-flask.1 + "@metamask/snaps-controllers": ^0.38.2-flask.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/utils": ^8.0.0 + "@types/uuid": ^9.0.1 + superstruct: ^1.0.3 + uuid: ^9.0.0 + checksum: 1501d361fa14023f2666575bbde44b37a6ad0fbf95e19d0f956dcbebb83c7fab854fa3b15cdd2c83970540db86bcc7f85d509db1a7cd21b7611579e0da6097c4 + languageName: node + linkType: hard + "@metamask/keyring-api@npm:^0.1.3": version: 0.1.3 resolution: "@metamask/keyring-api@npm:0.1.3" @@ -1861,6 +1892,7 @@ __metadata: "@metamask/base-controller": ^3.2.1 "@metamask/eth-keyring-controller": ^13.0.0 "@metamask/eth-sig-util": ^6.0.0 + "@metamask/keyring-api": 0.2.1 "@metamask/message-manager": ^7.3.1 "@metamask/preferences-controller": ^4.4.0 "@metamask/scure-bip39": ^2.1.0 @@ -2017,7 +2049,7 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-controller@^4.0.0, @metamask/permission-controller@workspace:packages/permission-controller": +"@metamask/permission-controller@^4.0.0, @metamask/permission-controller@^4.1.0, @metamask/permission-controller@workspace:packages/permission-controller": version: 0.0.0-use.local resolution: "@metamask/permission-controller@workspace:packages/permission-controller" dependencies: @@ -2113,7 +2145,7 @@ __metadata: languageName: node linkType: hard -"@metamask/providers@npm:^11.0.0": +"@metamask/providers@npm:^11.0.0, @metamask/providers@npm:^11.1.1": version: 11.1.1 resolution: "@metamask/providers@npm:11.1.1" dependencies: @@ -2178,6 +2210,23 @@ __metadata: languageName: node linkType: hard +"@metamask/rpc-methods@npm:^0.38.1-flask.1": + version: 0.38.1-flask.1 + resolution: "@metamask/rpc-methods@npm:0.38.1-flask.1" + dependencies: + "@metamask/key-tree": ^9.0.0 + "@metamask/permission-controller": ^4.1.0 + "@metamask/snaps-ui": ^0.37.4-flask.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/types": ^1.1.0 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + eth-rpc-errors: ^4.0.3 + superstruct: ^1.0.3 + checksum: 49700a41a1340d36d484086a4f6ca0a3fea27f5f8119c9a9af9c2cd0aeecf7f603f6c89a2f122232c66a238e281023df82d4ad38c799da5a137ccfcf0bca6857 + languageName: node + linkType: hard + "@metamask/rpc-methods@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/rpc-methods@npm:1.0.1" @@ -2277,6 +2326,35 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-controllers@npm:^0.38.2-flask.1": + version: 0.38.2-flask.1 + resolution: "@metamask/snaps-controllers@npm:0.38.2-flask.1" + dependencies: + "@metamask/approval-controller": ^3.5.0 + "@metamask/base-controller": ^3.2.0 + "@metamask/object-multiplex": ^1.2.0 + "@metamask/permission-controller": ^4.1.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/rpc-methods": ^0.38.1-flask.1 + "@metamask/snaps-execution-environments": ^0.38.2-flask.1 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/utils": ^6.0.1 + "@xstate/fsm": ^2.0.0 + concat-stream: ^2.0.0 + eth-rpc-errors: ^4.0.3 + gunzip-maybe: ^1.4.2 + immer: ^9.0.6 + json-rpc-engine: ^6.1.0 + json-rpc-middleware-stream: ^4.2.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + readable-web-to-node-stream: ^3.0.2 + tar-stream: ^2.2.0 + checksum: 738044e70b079fd62195219aa68260b4afae4855826b8d7b94f53edad69d8befaf77c67c2a9dbf2363457223b0560652578eeea438150d37a56a4adcd89e6e43 + languageName: node + linkType: hard + "@metamask/snaps-controllers@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-controllers@npm:1.0.1" @@ -2328,6 +2406,25 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-execution-environments@npm:^0.38.2-flask.1": + version: 0.38.2-flask.1 + resolution: "@metamask/snaps-execution-environments@npm:0.38.2-flask.1" + dependencies: + "@metamask/object-multiplex": ^1.2.0 + "@metamask/post-message-stream": ^6.1.2 + "@metamask/providers": ^11.1.1 + "@metamask/rpc-methods": ^0.38.1-flask.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/utils": ^6.0.1 + eth-rpc-errors: ^4.0.3 + json-rpc-engine: ^6.1.0 + nanoid: ^3.1.31 + pump: ^3.0.0 + superstruct: ^1.0.3 + checksum: 68c1adcb8fe227c989c02ea5f7820f0d58b0e9442b21d0043e2f06a7ea3a6607a5e0bdde82aceecc12e265b27bd1e5a719fb4ec901f19adea3a37d5412ca8415 + languageName: node + linkType: hard + "@metamask/snaps-execution-environments@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-execution-environments@npm:1.0.1" @@ -2369,6 +2466,16 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-ui@npm:^0.37.4-flask.1": + version: 0.37.4-flask.1 + resolution: "@metamask/snaps-ui@npm:0.37.4-flask.1" + dependencies: + "@metamask/utils": ^6.0.1 + superstruct: ^1.0.3 + checksum: d4e0a3c5f82a6edbf7bc82280fab364b93787e7d9995854208dd4200e188f37691216928967a5b75e6be5ebfeb360e638af39ac0043f1bdddb0c2ff7a5cdbce5 + languageName: node + linkType: hard + "@metamask/snaps-ui@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-ui@npm:1.0.1" @@ -2408,6 +2515,35 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-utils@npm:^0.38.2-flask.1": + version: 0.38.2-flask.1 + resolution: "@metamask/snaps-utils@npm:0.38.2-flask.1" + dependencies: + "@babel/core": ^7.20.12 + "@babel/types": ^7.18.7 + "@metamask/base-controller": ^3.2.0 + "@metamask/key-tree": ^9.0.0 + "@metamask/permission-controller": ^4.1.0 + "@metamask/snaps-registry": ^1.2.1 + "@metamask/snaps-ui": ^0.37.4-flask.1 + "@metamask/utils": ^6.0.1 + "@noble/hashes": ^1.3.1 + "@scure/base": ^1.1.1 + chalk: ^4.1.2 + cron-parser: ^4.5.0 + eth-rpc-errors: ^4.0.3 + fast-deep-equal: ^3.1.3 + fast-json-stable-stringify: ^2.1.0 + is-svg: ^4.4.0 + rfdc: ^1.3.0 + semver: ^7.5.4 + ses: ^0.18.7 + superstruct: ^1.0.3 + validate-npm-package-name: ^5.0.0 + checksum: cb95a67a1267299aacb5b85b25a35dbfd14f68060198974cc1a26b9a2e7eff5acd13700b9100e3546dfc7e78ed937b06aecb5c4884c32ff77b2d28c77e7f97b6 + languageName: node + linkType: hard + "@metamask/snaps-utils@npm:^1.0.1": version: 1.0.1 resolution: "@metamask/snaps-utils@npm:1.0.1" @@ -2540,6 +2676,20 @@ __metadata: languageName: node linkType: hard +"@metamask/utils@npm:^8.0.0": + version: 8.1.0 + resolution: "@metamask/utils@npm:8.1.0" + dependencies: + "@ethereumjs/tx": ^4.1.2 + "@noble/hashes": ^1.3.1 + "@types/debug": ^4.1.7 + debug: ^4.3.4 + semver: ^7.5.4 + superstruct: ^1.0.3 + checksum: 4cbee36d0c227f3e528930e83f75a0c6b71b55b332c3e162f0e87f3dd86ae017d0b20405d76ea054ab99e4d924d3d9b8b896ed12a12aae57b090350e5a625999 + languageName: node + linkType: hard + "@ngraveio/bc-ur@npm:^1.1.5": version: 1.1.6 resolution: "@ngraveio/bc-ur@npm:1.1.6" @@ -4068,7 +4218,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0": +"chalk@npm:^4.0.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: From 2c6baae1a2853cf6bd93a4dd05995e4d610c31b2 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 00:23:22 +0800 Subject: [PATCH 22/54] chore: use keyring-api 0.2.2 and eth-snap-keyring 0.2 --- packages/accounts-controller/package.json | 4 +- .../src/AccountsController.ts | 35 ++---------- yarn.lock | 53 ++++++++++++------- 3 files changed, 39 insertions(+), 53 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 6dc13466a13..00d757eaf8e 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -29,8 +29,8 @@ }, "dependencies": { "@metamask/base-controller": "^3.2.1", - "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0", - "@metamask/keyring-api": "0.2.1", + "@metamask/eth-snap-keyring": "^0.2.0", + "@metamask/keyring-api": "^0.2.2", "@metamask/keyring-controller": "^7.2.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 25b9655bc95..30ea573565d 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -129,7 +129,9 @@ export class AccountsController extends BaseControllerV2< onSnapStateChange(async (snapState: SnapControllerState) => { // only check if snaps changed in status const { snaps } = snapState; - const accounts = this.listAccounts(); + const accounts = this.listAccounts().filter( + (account) => account.metadata.snap, + ); this.update((currentState: AccountsControllerState) => { accounts.forEach((account) => { @@ -248,8 +250,6 @@ export class AccountsController extends BaseControllerV2< // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Type instantiation is excessively deep and possibly infinite. this.update((currentState: AccountsControllerState) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore lastSelected will be added in 0.2.2 currentState.internalAccounts.accounts[account.id].metadata.lastSelected = Date.now(); currentState.internalAccounts.selectedAccount = account.id; @@ -325,8 +325,6 @@ export class AccountsController extends BaseControllerV2< existingAccount && existingAccount.metadata.name !== '' ? existingAccount.metadata.name : `${keyringTypeName} ${keyringAccountIndex + 1}`, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore lastSelected will be added in 0.2.2 lastSelected: existingAccount?.metadata?.lastSelected, }, }; @@ -357,29 +355,6 @@ export class AccountsController extends BaseControllerV2< } const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); - - for (const account of snapAccounts) { - // The snap account is guaranteed to have a snap metadata - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const snapId = account.metadata.snap!.id!; - - account.metadata = { - snap: { - id: snapId, - enabled: true, - name: '', - }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore snap keyring not updated yet - name: '', - keyring: { - type: (snapKeyring as SnapKeyring).type, - }, - }; - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore lastSelected will be added in 0.2.2 return snapAccounts; } @@ -429,11 +404,7 @@ export class AccountsController extends BaseControllerV2< .sort((accountA, accountB) => { // sort by lastSelected descending return ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore (accountB.metadata?.lastSelected ?? 0) - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore (accountA.metadata?.lastSelected ?? 0) ); })[0]; diff --git a/yarn.lock b/yarn.lock index 62116402556..5dcd06af8d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1293,8 +1293,8 @@ __metadata: dependencies: "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 - "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" - "@metamask/keyring-api": 0.2.1 + "@metamask/eth-snap-keyring": ^0.2.0 + "@metamask/keyring-api": ^0.2.2 "@metamask/keyring-controller": ^7.2.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 @@ -1790,6 +1790,22 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-snap-keyring@npm:^0.2.0": + version: 0.2.0 + resolution: "@metamask/eth-snap-keyring@npm:0.2.0" + dependencies: + "@ethereumjs/tx": ^4.1.2 + "@metamask/eth-sig-util": ^5.1.0 + "@metamask/keyring-api": ^0.2.2 + "@metamask/snaps-controllers": ^0.38.2-flask.1 + "@metamask/utils": ^8.1.0 + "@types/uuid": ^9.0.1 + superstruct: ^1.0.3 + uuid: ^9.0.0 + checksum: 61383c837a114601a53bc16aa0b4d4db81335c741a890a09fcb68259fccfb3e330c20ef6f5e2c489502eb7350076a5669d313f940862b8f0c985e704ba34dc53 + languageName: node + linkType: hard + "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller": version: 0.0.0-use.local resolution: "@metamask/gas-fee-controller@workspace:packages/gas-fee-controller" @@ -1849,34 +1865,34 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:0.2.1": - version: 0.2.1 - resolution: "@metamask/keyring-api@npm:0.2.1" +"@metamask/keyring-api@npm:^0.1.3": + version: 0.1.3 + resolution: "@metamask/keyring-api@npm:0.1.3" dependencies: "@metamask/providers": ^11.0.0 - "@metamask/rpc-methods": ^0.38.1-flask.1 - "@metamask/snaps-controllers": ^0.38.2-flask.1 - "@metamask/snaps-utils": ^0.38.2-flask.1 - "@metamask/utils": ^8.0.0 + "@metamask/snaps-controllers": ^0.35.2-flask.1 + "@metamask/snaps-utils": ^0.35.2-flask.1 + "@metamask/utils": ^6.0.1 "@types/uuid": ^9.0.1 superstruct: ^1.0.3 uuid: ^9.0.0 - checksum: 1501d361fa14023f2666575bbde44b37a6ad0fbf95e19d0f956dcbebb83c7fab854fa3b15cdd2c83970540db86bcc7f85d509db1a7cd21b7611579e0da6097c4 + checksum: 2307b5162dbb66d82f7d8ab8e9f1c3a0ef581b915702187b6b8fa5d8e8533838d0539d6f35853ef4f25096f13c9dbf4505fadff247b73f6d489d9c904015d21c languageName: node linkType: hard -"@metamask/keyring-api@npm:^0.1.3": - version: 0.1.3 - resolution: "@metamask/keyring-api@npm:0.1.3" +"@metamask/keyring-api@npm:^0.2.2": + version: 0.2.2 + resolution: "@metamask/keyring-api@npm:0.2.2" dependencies: "@metamask/providers": ^11.0.0 - "@metamask/snaps-controllers": ^0.35.2-flask.1 - "@metamask/snaps-utils": ^0.35.2-flask.1 - "@metamask/utils": ^6.0.1 + "@metamask/rpc-methods": ^0.38.1-flask.1 + "@metamask/snaps-controllers": ^0.38.2-flask.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/utils": ^8.0.0 "@types/uuid": ^9.0.1 superstruct: ^1.0.3 uuid: ^9.0.0 - checksum: 2307b5162dbb66d82f7d8ab8e9f1c3a0ef581b915702187b6b8fa5d8e8533838d0539d6f35853ef4f25096f13c9dbf4505fadff247b73f6d489d9c904015d21c + checksum: eefa5408c04e203db4d3a711e0b621193fd5d15c31e155331a0af8da9d150629074ca847aeb55654f39c104eff01060452d7d3ac43b5122f8b565b249581a8df languageName: node linkType: hard @@ -1892,7 +1908,6 @@ __metadata: "@metamask/base-controller": ^3.2.1 "@metamask/eth-keyring-controller": ^13.0.0 "@metamask/eth-sig-util": ^6.0.0 - "@metamask/keyring-api": 0.2.1 "@metamask/message-manager": ^7.3.1 "@metamask/preferences-controller": ^4.4.0 "@metamask/scure-bip39": ^2.1.0 @@ -2676,7 +2691,7 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^8.0.0": +"@metamask/utils@npm:^8.0.0, @metamask/utils@npm:^8.1.0": version: 8.1.0 resolution: "@metamask/utils@npm:8.1.0" dependencies: From 3db13e42387dc54006619f2506c0644ea0a5c942 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 09:51:35 +0800 Subject: [PATCH 23/54] chore: update yarn.lock --- yarn.lock | 144 +----------------------------------------------------- 1 file changed, 1 insertion(+), 143 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5dcd06af8d1..0420a3bafa0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,7 +1287,7 @@ __metadata: languageName: node linkType: hard -"@metamask/accounts-controller@^1.0.0, @metamask/accounts-controller@workspace:packages/accounts-controller": +"@metamask/accounts-controller@workspace:packages/accounts-controller": version: 0.0.0-use.local resolution: "@metamask/accounts-controller@workspace:packages/accounts-controller" dependencies: @@ -1394,14 +1394,12 @@ __metadata: "@ethersproject/contracts": ^5.7.0 "@ethersproject/providers": ^5.7.0 "@metamask/abi-utils": ^2.0.1 - "@metamask/accounts-controller": ^1.0.0 "@metamask/approval-controller": ^3.5.1 "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 "@metamask/contract-metadata": ^2.3.1 "@metamask/controller-utils": ^4.3.2 "@metamask/eth-query": ^3.0.1 - "@metamask/eth-snap-keyring": "git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" "@metamask/metamask-eth-abis": 3.0.0 "@metamask/network-controller": ^12.1.2 "@metamask/preferences-controller": ^4.4.0 @@ -1428,7 +1426,6 @@ __metadata: typescript: ~4.6.3 uuid: ^8.3.2 peerDependencies: - "@metamask/accounts-controller": ^1.0.0 "@metamask/approval-controller": ^3.5.1 "@metamask/network-controller": ^12.1.2 "@metamask/preferences-controller": ^4.4.0 @@ -1774,22 +1771,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-snap-keyring@git+https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0": - version: 0.1.3 - resolution: "@metamask/eth-snap-keyring@https://github.com/MetaMask/eth-snap-keyring.git#commit=543117c98e80c83378dbd8bb2bc87704ee0f96f0" - dependencies: - "@ethereumjs/tx": ^4.1.2 - "@metamask/eth-sig-util": ^5.1.0 - "@metamask/keyring-api": ^0.1.3 - "@metamask/snaps-controllers": ^0.35.2-flask.1 - "@metamask/utils": ^6.1.0 - "@types/uuid": ^9.0.1 - superstruct: ^1.0.3 - uuid: ^9.0.0 - checksum: 7af04f704434c08ce57c5c95ed1c493a4373e490e1a96236365886a78972b4c29d1cd7a0548ce4962968459ab8c2597a2e3b81ae08883f88c6f1e2ad15e6799f - languageName: node - linkType: hard - "@metamask/eth-snap-keyring@npm:^0.2.0": version: 0.2.0 resolution: "@metamask/eth-snap-keyring@npm:0.2.0" @@ -1865,21 +1846,6 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:^0.1.3": - version: 0.1.3 - resolution: "@metamask/keyring-api@npm:0.1.3" - dependencies: - "@metamask/providers": ^11.0.0 - "@metamask/snaps-controllers": ^0.35.2-flask.1 - "@metamask/snaps-utils": ^0.35.2-flask.1 - "@metamask/utils": ^6.0.1 - "@types/uuid": ^9.0.1 - superstruct: ^1.0.3 - uuid: ^9.0.0 - checksum: 2307b5162dbb66d82f7d8ab8e9f1c3a0ef581b915702187b6b8fa5d8e8533838d0539d6f35853ef4f25096f13c9dbf4505fadff247b73f6d489d9c904015d21c - languageName: node - linkType: hard - "@metamask/keyring-api@npm:^0.2.2": version: 0.2.2 resolution: "@metamask/keyring-api@npm:0.2.2" @@ -2207,24 +2173,6 @@ __metadata: languageName: node linkType: hard -"@metamask/rpc-methods@npm:^0.35.2-flask.1": - version: 0.35.2-flask.1 - resolution: "@metamask/rpc-methods@npm:0.35.2-flask.1" - dependencies: - "@metamask/key-tree": ^7.1.1 - "@metamask/permission-controller": ^4.0.0 - "@metamask/snaps-ui": ^0.35.2-flask.1 - "@metamask/snaps-utils": ^0.35.2-flask.1 - "@metamask/types": ^1.1.0 - "@metamask/utils": ^6.0.1 - "@noble/hashes": ^1.3.1 - eth-rpc-errors: ^4.0.3 - nanoid: ^3.1.31 - superstruct: ^1.0.3 - checksum: e949da37c7d3099c07fc3f110310a1f86dbfbdf0d3a84a584b697beb64dc4662ba93e843b7a28a41e41b6500128782a07a9884e14252ac98206b8ed58776034b - languageName: node - linkType: hard - "@metamask/rpc-methods@npm:^0.38.1-flask.1": version: 0.38.1-flask.1 resolution: "@metamask/rpc-methods@npm:0.38.1-flask.1" @@ -2311,36 +2259,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/snaps-controllers@npm:^0.35.2-flask.1": - version: 0.35.2-flask.1 - resolution: "@metamask/snaps-controllers@npm:0.35.2-flask.1" - dependencies: - "@metamask/approval-controller": ^3.0.0 - "@metamask/base-controller": ^3.0.0 - "@metamask/object-multiplex": ^1.2.0 - "@metamask/permission-controller": ^4.0.0 - "@metamask/post-message-stream": ^6.1.2 - "@metamask/rpc-methods": ^0.35.2-flask.1 - "@metamask/snaps-execution-environments": ^0.35.2-flask.1 - "@metamask/snaps-registry": ^1.2.1 - "@metamask/snaps-utils": ^0.35.2-flask.1 - "@metamask/utils": ^6.0.1 - "@xstate/fsm": ^2.0.0 - concat-stream: ^2.0.0 - cron-parser: ^4.5.0 - eth-rpc-errors: ^4.0.3 - gunzip-maybe: ^1.4.2 - immer: ^9.0.6 - json-rpc-engine: ^6.1.0 - json-rpc-middleware-stream: ^4.2.0 - nanoid: ^3.1.31 - pump: ^3.0.0 - readable-web-to-node-stream: ^3.0.2 - tar-stream: ^2.2.0 - checksum: 8b20ed3176da02c2bb5be642db83912b484dd85a0e9cb36b2d4bca6a0058cd28d578c91c595f20bcc084c73e8802b289191ac41972bc17ae921010e1b05fd307 - languageName: node - linkType: hard - "@metamask/snaps-controllers@npm:^0.38.2-flask.1": version: 0.38.2-flask.1 resolution: "@metamask/snaps-controllers@npm:0.38.2-flask.1" @@ -2400,27 +2318,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-execution-environments@npm:^0.35.2-flask.1": - version: 0.35.2-flask.1 - resolution: "@metamask/snaps-execution-environments@npm:0.35.2-flask.1" - dependencies: - "@metamask/object-multiplex": ^1.2.0 - "@metamask/post-message-stream": ^6.1.2 - "@metamask/providers": ^11.0.0 - "@metamask/rpc-methods": ^0.35.2-flask.1 - "@metamask/snaps-utils": ^0.35.2-flask.1 - "@metamask/utils": ^6.0.1 - eth-rpc-errors: ^4.0.3 - json-rpc-engine: ^6.1.0 - nanoid: ^3.1.31 - pump: ^3.0.0 - ses: ^0.18.1 - stream-browserify: ^3.0.0 - superstruct: ^1.0.3 - checksum: b112a25a5cd5574fd84dcc7a62b9076d59d0182d52c37645d44477efcb67d18a4dd40bef3778dcbd67009fe3b261ffe0e247def112737a16bac5e86bf16081e7 - languageName: node - linkType: hard - "@metamask/snaps-execution-environments@npm:^0.38.2-flask.1": version: 0.38.2-flask.1 resolution: "@metamask/snaps-execution-environments@npm:0.38.2-flask.1" @@ -2471,16 +2368,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-ui@npm:^0.35.2-flask.1": - version: 0.35.2-flask.1 - resolution: "@metamask/snaps-ui@npm:0.35.2-flask.1" - dependencies: - "@metamask/utils": ^6.0.1 - superstruct: ^1.0.3 - checksum: fb272068e04a9d4e0458aecef4df4091a9d9d9c0c3580d40c211a710566eb5610252456f0eb99533379d6d919c0c4156dda1ae6233693723e9227e54a4c5ddbf - languageName: node - linkType: hard - "@metamask/snaps-ui@npm:^0.37.4-flask.1": version: 0.37.4-flask.1 resolution: "@metamask/snaps-ui@npm:0.37.4-flask.1" @@ -2501,35 +2388,6 @@ __metadata: languageName: node linkType: hard -"@metamask/snaps-utils@npm:^0.35.2-flask.1": - version: 0.35.2-flask.1 - resolution: "@metamask/snaps-utils@npm:0.35.2-flask.1" - dependencies: - "@babel/core": ^7.20.12 - "@babel/types": ^7.18.7 - "@metamask/base-controller": ^3.0.0 - "@metamask/key-tree": ^7.1.1 - "@metamask/permission-controller": ^4.0.0 - "@metamask/providers": ^11.0.0 - "@metamask/snaps-registry": ^1.2.1 - "@metamask/snaps-ui": ^0.35.2-flask.1 - "@metamask/utils": ^6.0.1 - "@noble/hashes": ^1.3.1 - "@scure/base": ^1.1.1 - cron-parser: ^4.5.0 - eth-rpc-errors: ^4.0.3 - fast-deep-equal: ^3.1.3 - fast-json-stable-stringify: ^2.1.0 - is-svg: ^4.4.0 - rfdc: ^1.3.0 - semver: ^7.3.7 - ses: ^0.18.1 - superstruct: ^1.0.3 - validate-npm-package-name: ^5.0.0 - checksum: 4a22bf42512e2bdc944042cb05ef8b8f733ce902c0f374f7d36e22344700a3d0674a48ddacbe096a7e253c24f7ba05cff7ff30a05d3e48a840b63cbeeb46f0ce - languageName: node - linkType: hard - "@metamask/snaps-utils@npm:^0.38.2-flask.1": version: 0.38.2-flask.1 resolution: "@metamask/snaps-utils@npm:0.38.2-flask.1" From de8a8b5816d259e5b9acb6de80bafa2552c8c9d8 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 10:06:58 +0800 Subject: [PATCH 24/54] chore: bump keyring controller dep in accounts controller --- packages/accounts-controller/package.json | 2 +- yarn.lock | 24 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 00d757eaf8e..554cc4192c5 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -31,7 +31,7 @@ "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "^0.2.0", "@metamask/keyring-api": "^0.2.2", - "@metamask/keyring-controller": "^7.2.0", + "@metamask/keyring-controller": "^7.3.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", diff --git a/yarn.lock b/yarn.lock index 0420a3bafa0..75ae90a00e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1295,7 +1295,7 @@ __metadata: "@metamask/base-controller": ^3.2.1 "@metamask/eth-snap-keyring": ^0.2.0 "@metamask/keyring-api": ^0.2.2 - "@metamask/keyring-controller": ^7.2.0 + "@metamask/keyring-controller": ^7.3.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 "@metamask/utils": ^6.2.0 @@ -1862,7 +1862,27 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@^7.2.0, @metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/keyring-controller@npm:^7.3.0": + version: 7.3.0 + resolution: "@metamask/keyring-controller@npm:7.3.0" + dependencies: + "@keystonehq/metamask-airgapped-keyring": ^0.13.1 + "@metamask/base-controller": ^3.2.1 + "@metamask/eth-keyring-controller": ^13.0.0 + "@metamask/message-manager": ^7.3.1 + "@metamask/preferences-controller": ^4.4.0 + "@metamask/utils": ^6.2.0 + async-mutex: ^0.2.6 + ethereumjs-util: ^7.0.10 + ethereumjs-wallet: ^1.0.1 + immer: ^9.0.6 + peerDependencies: + "@metamask/preferences-controller": ^4.4.0 + checksum: c81d05fcaef6c4ad0959b4483cd4132deb3f28b52da6e847e64465ae6fadf3099adf2bfe422735437014a2ff67b159133e440ed6c1516c6087e458097241d186 + languageName: node + linkType: hard + +"@metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: From 49fb4ca467dc3b75e2ac720747bbb5855b8dc490 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 10:15:10 +0800 Subject: [PATCH 25/54] chore: update yarn.lock after merge --- yarn.lock | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/yarn.lock b/yarn.lock index 37e0dba53cf..bda6900eb6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1862,27 +1862,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@npm:^7.3.0": - version: 7.3.0 - resolution: "@metamask/keyring-controller@npm:7.3.0" - dependencies: - "@keystonehq/metamask-airgapped-keyring": ^0.13.1 - "@metamask/base-controller": ^3.2.1 - "@metamask/eth-keyring-controller": ^13.0.0 - "@metamask/message-manager": ^7.3.1 - "@metamask/preferences-controller": ^4.4.0 - "@metamask/utils": ^6.2.0 - async-mutex: ^0.2.6 - ethereumjs-util: ^7.0.10 - ethereumjs-wallet: ^1.0.1 - immer: ^9.0.6 - peerDependencies: - "@metamask/preferences-controller": ^4.4.0 - checksum: c81d05fcaef6c4ad0959b4483cd4132deb3f28b52da6e847e64465ae6fadf3099adf2bfe422735437014a2ff67b159133e440ed6c1516c6087e458097241d186 - languageName: node - linkType: hard - -"@metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/keyring-controller@^7.3.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: From 008633468df0d86c8fccc194bbe0ed72860efa59 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 12:24:29 +0800 Subject: [PATCH 26/54] fix: update peerdep --- packages/accounts-controller/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 554cc4192c5..7e338c144ae 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -54,7 +54,7 @@ "typescript": "~4.6.3" }, "peerDependencies": { - "@metamask/keyring-controller": "^7.2.0" + "@metamask/keyring-controller": "^7.3.0" }, "engines": { "node": ">=16.0.0" From c338244da8d46af1bc25f4b4450077460248c511 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 13:23:13 +0800 Subject: [PATCH 27/54] chore: update yarn.lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index bda6900eb6f..775fec7d9ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1314,7 +1314,7 @@ __metadata: typescript: ~4.6.3 uuid: ^8.3.2 peerDependencies: - "@metamask/keyring-controller": ^7.2.0 + "@metamask/keyring-controller": ^7.3.0 languageName: unknown linkType: soft From f6a74c51704db2301cde896cd5bacaca051b1c01 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 6 Sep 2023 23:34:40 +0800 Subject: [PATCH 28/54] feat: add jsdocs --- .../src/AccountsController.ts | 89 ++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 30ea573565d..51a9a9a4536 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -73,6 +73,14 @@ const defaultState: AccountsControllerState = { }, }; +/** + * Controller that manages internal accounts. + * The accounts controller is responsible for creating and managing internal accounts. + * It also provides convenience methods for accessing and updating the internal accounts. + * The accounts controller also listens for keyring state changes and updates the internal accounts accordingly. + * The accounts controller also listens for snap state changes and updates the internal accounts accordingly. + * + */ export class AccountsController extends BaseControllerV2< typeof controllerName, AccountsControllerState, @@ -86,6 +94,19 @@ export class AccountsController extends BaseControllerV2< keyringApiEnabled: boolean; + /** + * Constructor for AccountsController. + * + * @param options - The controller options. + * @param options.messenger - The messenger object. + * @param options.state - Initial state to set on this controller + * @param [options.keyringApiEnabled] - The keyring API enabled flag. + * @param options.getKeyringForAccount - Gets the keyring for a given account from the keyring controller. + * @param options.getKeyringByType - Gets the keyring instance for the given type from the keyring controller. + * @param options.getAccounts - Gets all the accounts from the keyring controller. + * @param options.onKeyringStateChange - Allows subscribing to the keyring controller state changes. + * @param options.onSnapStateChange - Allows subscribing to the snap controller state changes. + */ constructor({ messenger, state, @@ -206,14 +227,32 @@ export class AccountsController extends BaseControllerV2< } } + /** + * Returns the internal account object for the given account ID, if it exists. + * + * @param accountId - The ID of the account to retrieve. + * @returns The internal account object, or undefined if the account does not exist. + */ getAccount(accountId: string): InternalAccount | undefined { return this.state.internalAccounts.accounts[accountId]; } + /** + * Returns an array of all internal accounts. + * + * @returns An array of InternalAccount objects. + */ listAccounts(): InternalAccount[] { return Object.values(this.state.internalAccounts.accounts); } + /** + * Returns the internal account object for the given account ID. + * + * @param accountId - The ID of the account to retrieve. + * @returns The internal account object. + * @throws An error if the account ID is not found. + */ getAccountExpect(accountId: string): InternalAccount { // Edge case where the extension is setup but the srp is not yet created // certain ui elements will query the selected address before any accounts are created. @@ -240,10 +279,20 @@ export class AccountsController extends BaseControllerV2< return account; } + /** + * Returns the selected internal account. + * + * @returns The selected internal account. + */ getSelectedAccount(): InternalAccount { return this.getAccountExpect(this.state.internalAccounts.selectedAccount); } + /** + * Sets the selected account by its ID. + * + * @param accountId - The ID of the account to be selected. + */ setSelectedAccount(accountId: string): void { const account = this.getAccountExpect(accountId); @@ -258,6 +307,13 @@ export class AccountsController extends BaseControllerV2< this.messagingSystem.publish(`${this.name}:selectedAccountChange`, account); } + /** + * Sets the name of the account with the given ID. + * + * @param accountId - The ID of the account to set the name for. + * @param accountName - The new name for the account. + * @throws An error if an account with the same name already exists. + */ setAccountName(accountId: string, accountName: string): void { const account = this.getAccountExpect(accountId); @@ -282,6 +338,12 @@ export class AccountsController extends BaseControllerV2< }); } + /** + * Updates the internal accounts list by retrieving legacy and snap accounts, + * removing duplicates, and updating the metadata of each account. + * + * @returns A Promise that resolves when the accounts have been updated. + */ async updateAccounts(): Promise { let legacyAccounts = await this.#listLegacyAccounts(); let snapAccounts: InternalAccount[] = []; @@ -339,6 +401,11 @@ export class AccountsController extends BaseControllerV2< }); } + /** + * Loads the backup state of the accounts controller. + * + * @param backup - The backup state to load. + */ loadBackup(backup: AccountsControllerState): void { if (backup.internalAccounts) { this.update((currentState: AccountsControllerState) => { @@ -347,6 +414,11 @@ export class AccountsController extends BaseControllerV2< } } + /** + * Returns a list of internal accounts created using the SnapKeyring. + * + * @returns A promise that resolves to an array of InternalAccount objects. + */ async #listSnapAccounts(): Promise { const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); // snap keyring is not available until the first account is created in the keyring controller @@ -358,7 +430,13 @@ export class AccountsController extends BaseControllerV2< return snapAccounts; } - // Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface + /** + * Returns a list of legacy accounts. + * Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface. + * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead. + * + * @returns A Promise that resolves to an array of InternalAccount objects. + */ async #listLegacyAccounts(): Promise { const addresses = await this.getAccounts(); const internalAccounts: InternalAccount[] = []; @@ -396,6 +474,9 @@ export class AccountsController extends BaseControllerV2< ); } + /** + * Handles the removal of the currently selected account by selecting the previous account in the list. + */ #handleSelectedAccountRemoved() { const previousAccount = this.listAccounts() .filter( @@ -412,6 +493,12 @@ export class AccountsController extends BaseControllerV2< this.setSelectedAccount(previousAccount.id); } + /** + * Handles the event when a new account is added to the keyring. + * + * @param updatedKeyringAddresses - An array of updated keyring addresses. + * @param previousAccounts - An array of previous internal accounts. + */ #handleNewAccountAdded( updatedKeyringAddresses: string[], previousAccounts: InternalAccount[], From dd5d5780d6bbaf9ed3bd5ee486380185f7ec71dd Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 7 Sep 2023 09:26:40 +0800 Subject: [PATCH 29/54] feat: use messaging system to subscribe to snap and keyring state changes --- .../src/AccountsController.test.ts | 159 +++++++++------- .../src/AccountsController.ts | 169 +++++++++--------- 2 files changed, 171 insertions(+), 157 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 2961c096cbe..b51e04c85ee 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,6 +1,6 @@ import { ControllerMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/keyring-api'; -import type { KeyringControllerState } from '@metamask/keyring-controller'; +import { EthAccountType } from '@metamask/keyring-api'; import type { SnapControllerState } from '@metamask/snaps-controllers'; import { SnapStatus } from '@metamask/snaps-utils'; import * as uuid from 'uuid'; @@ -39,7 +39,7 @@ const mockAccount: InternalAccount = { 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name: 'Account 1', keyring: { type: 'HD Key Tree' }, @@ -62,7 +62,7 @@ const mockAccount2: InternalAccount = { 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name: 'Account 2', keyring: { type: 'HD Key Tree' }, @@ -112,7 +112,7 @@ function createExpectedInternalAccount({ 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name, keyring: { type: keyringType }, @@ -184,27 +184,23 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { * @param options - The options object. * @param [options.initialState] - The initial state to use for the AccountsController. * @param [options.keyringApiEnabled] - Whether or not the keyring API is enabled. - * @param [options.onKeyringStateChange] - A callback to call when the keyring state changes. - * @param [options.onSnapStateChange] - A callback to call when the snap state changes. + * @param [options.messenger] - Messenger to use for the AccountsController. * @returns An instance of the AccountsController class. */ function setupAccountsController({ - // eslint-disable-next-line @typescript-eslint/default-param-last initialState = {}, keyringApiEnabled = true, - onKeyringStateChange = () => undefined, - onSnapStateChange = () => undefined, + messenger = buildMessenger(), }: { initialState?: Partial; keyringApiEnabled?: boolean; - onKeyringStateChange?: ( - listener: (state: KeyringControllerState) => void, - ) => void; - onSnapStateChange?: (listener: (state: SnapControllerState) => void) => void; + messenger?: ControllerMessenger< + AccountsControllerActions, + AccountsControllerEvents + >; }): AccountsController { - const accountsControllerMessenger = buildAccountsControllerMessenger( - new ControllerMessenger(), - ); + const accountsControllerMessenger = + buildAccountsControllerMessenger(messenger); const accountsController = new AccountsController({ messenger: accountsControllerMessenger, @@ -212,8 +208,6 @@ function setupAccountsController({ getKeyringForAccount: mockGetKeyringForAccount, getKeyringByType: mockGetKeyringByType, getAccounts: mockGetAccounts, - onKeyringStateChange, - onSnapStateChange, keyringApiEnabled, }); return accountsController; @@ -226,6 +220,7 @@ describe('AccountsController', () => { describe('onSnapStateChange', () => { it('should not be used when keyringApiEnabled is false', async () => { + const messenger = buildMessenger(); const snapStateChangeSpy = jest.fn(); setupAccountsController({ initialState: { @@ -235,15 +230,20 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onSnapStateChange: () => { - snapStateChangeSpy(); - }, + messenger, }); + messenger.publish( + 'SnapController:stateChange', + {} as any as SnapControllerState, + [], + ); + expect(snapStateChangeSpy).not.toHaveBeenCalled(); }); it('should be used enable an account if the snap is enabled and not blocked', async () => { + const messenger = buildMessenger(); const mockSnapAccount = createExpectedInternalAccount({ id: 'mock-id', name: 'Snap Account 1', @@ -261,7 +261,7 @@ describe('AccountsController', () => { status: SnapStatus.Running, }, }, - }; + } as any as SnapControllerState; const accountsController = setupAccountsController({ initialState: { internalAccounts: { @@ -272,11 +272,11 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, - onSnapStateChange: (listener: (state: any) => void) => { - listener(mockSnapChangeState); - }, + messenger, }); + messenger.publish('SnapController:stateChange', mockSnapChangeState, []); + const updatedAccount = accountsController.getAccountExpect( mockSnapAccount.id, ); @@ -285,6 +285,7 @@ describe('AccountsController', () => { }); it('should be used disable an account if the snap is disabled', async () => { + const messenger = buildMessenger(); const mockSnapAccount = createExpectedInternalAccount({ id: 'mock-id', name: 'Snap Account 1', @@ -301,7 +302,7 @@ describe('AccountsController', () => { status: SnapStatus.Running, }, }, - }; + } as any as SnapControllerState; const accountsController = setupAccountsController({ initialState: { internalAccounts: { @@ -312,11 +313,11 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, - onSnapStateChange: (listener: (state: any) => void) => { - listener(mockSnapChangeState); - }, + messenger, }); + messenger.publish('SnapController:stateChange', mockSnapChangeState, []); + const updatedAccount = accountsController.getAccountExpect( mockSnapAccount.id, ); @@ -325,6 +326,7 @@ describe('AccountsController', () => { }); it('should be used disable an account if the snap is blocked', async () => { + const messenger = buildMessenger(); const mockSnapAccount = createExpectedInternalAccount({ id: 'mock-id', name: 'Snap Account 1', @@ -341,7 +343,7 @@ describe('AccountsController', () => { status: SnapStatus.Running, }, }, - }; + } as any as SnapControllerState; const accountsController = setupAccountsController({ initialState: { internalAccounts: { @@ -352,11 +354,11 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, - onSnapStateChange: (listener: (state: any) => void) => { - listener(mockSnapChangeState); - }, + messenger, }); + messenger.publish('SnapController:stateChange', mockSnapChangeState, []); + const updatedAccount = accountsController.getAccountExpect( mockSnapAccount.id, ); @@ -367,6 +369,8 @@ describe('AccountsController', () => { describe('onKeyringStateChange', () => { it('should only update if the keyring is unlocked', async () => { + const messenger = buildMessenger(); + const mockNewKeyringState = { isUnlocked: false, keyrings: [ @@ -384,19 +388,22 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + const accounts = accountsController.listAccounts(); expect(accounts).toStrictEqual([]); }); it('should add new accounts', async () => { + const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); mockGetAccounts.mockResolvedValue([ mockAccount.address, @@ -422,14 +429,16 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); - await new Promise((resolve) => setTimeout(resolve, 2000)); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); const accounts = accountsController.listAccounts(); @@ -440,6 +449,7 @@ describe('AccountsController', () => { }); it('should handle keyring reinitialization', async () => { + const messenger = buildMessenger(); const mockInitialAccount = createExpectedInternalAccount({ id: 'mock-id', name: 'Account 1', @@ -474,14 +484,16 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); - await new Promise((resolve) => setTimeout(resolve, 2000)); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); const selectedAccount = accountsController.getSelectedAccount(); const accounts = accountsController.listAccounts(); @@ -492,6 +504,7 @@ describe('AccountsController', () => { }); it('should delete accounts if its gone from the keyring state', async () => { + const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id2'); mockGetAccounts.mockResolvedValue([mockAccount2.address]); mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); @@ -515,14 +528,16 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); - await new Promise((resolve) => setTimeout(resolve, 2000)); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); const accounts = accountsController.listAccounts(); @@ -533,6 +548,7 @@ describe('AccountsController', () => { }); it('should delete accounts and set the most recent lastSelected account', async () => { + const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); mockGetAccounts.mockResolvedValue([ mockAccount.address, @@ -562,14 +578,16 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); - await new Promise((resolve) => setTimeout(resolve, 2000)); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); const accounts = accountsController.listAccounts(); @@ -583,6 +601,7 @@ describe('AccountsController', () => { }); it('should delete accounts and set the most recent lastSelected account when there are accounts that have never been selected', async () => { + const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); mockGetAccounts.mockResolvedValue([ mockAccount.address, @@ -619,14 +638,16 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, - onKeyringStateChange: ( - listener: (state: KeyringControllerState) => void, - ) => { - listener(mockNewKeyringState); - }, + messenger, }); - await new Promise((resolve) => setTimeout(resolve, 2000)); + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); const accounts = accountsController.listAccounts(); @@ -1082,7 +1103,7 @@ describe('AccountsController', () => { address: '', options: {}, methods: [], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name: '', keyring: { diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 51a9a9a4536..750c047ac79 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -2,6 +2,7 @@ import type { RestrictedControllerMessenger } from '@metamask/base-controller'; import { BaseControllerV2 } from '@metamask/base-controller'; import { SnapKeyring } from '@metamask/eth-snap-keyring'; import type { InternalAccount } from '@metamask/keyring-api'; +import { EthAccountType } from '@metamask/keyring-api'; import type { KeyringControllerState, KeyringController, @@ -104,8 +105,6 @@ export class AccountsController extends BaseControllerV2< * @param options.getKeyringForAccount - Gets the keyring for a given account from the keyring controller. * @param options.getKeyringByType - Gets the keyring instance for the given type from the keyring controller. * @param options.getAccounts - Gets all the accounts from the keyring controller. - * @param options.onKeyringStateChange - Allows subscribing to the keyring controller state changes. - * @param options.onSnapStateChange - Allows subscribing to the snap controller state changes. */ constructor({ messenger, @@ -114,8 +113,6 @@ export class AccountsController extends BaseControllerV2< getKeyringForAccount, getKeyringByType, getAccounts, - onSnapStateChange, - onKeyringStateChange, }: { messenger: AccountsControllerMessenger; state: AccountsControllerState; @@ -123,12 +120,6 @@ export class AccountsController extends BaseControllerV2< getKeyringForAccount: KeyringController['getKeyringForAccount']; getKeyringByType: KeyringController['getKeyringsByType']; getAccounts: KeyringController['getAccounts']; - onKeyringStateChange: ( - listener: (keyringState: KeyringControllerState) => void, - ) => void; - onSnapStateChange: ( - listener: (snapState: SnapControllerState) => void, - ) => void; }) { super({ messenger, @@ -146,76 +137,17 @@ export class AccountsController extends BaseControllerV2< this.keyringApiEnabled = Boolean(keyringApiEnabled); if (this.keyringApiEnabled) { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - onSnapStateChange(async (snapState: SnapControllerState) => { - // only check if snaps changed in status - const { snaps } = snapState; - const accounts = this.listAccounts().filter( - (account) => account.metadata.snap, - ); - - this.update((currentState: AccountsControllerState) => { - accounts.forEach((account) => { - const currentAccount = - currentState.internalAccounts.accounts[account.id]; - if (currentAccount?.metadata?.snap) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore this account is guaranteed to have snap metadata - currentState.internalAccounts.accounts[ - account.id - ].metadata.snap.enabled = - snaps[currentAccount.metadata.snap.id].enabled && - !snaps[currentAccount.metadata.snap.id].blocked; - } - }); - }); - }); + this.messagingSystem.subscribe( + 'SnapController:stateChange', + async (snapStateState) => + await this.#handleOnSnapStateChange(snapStateState), + ); } - onKeyringStateChange( - // eslint-disable-next-line @typescript-eslint/no-misused-promises - async (keyringState: KeyringControllerState): Promise => { - // check if there are any new accounts added - // TODO: change when accountAdded event is added to the keyring controller - - if (keyringState.isUnlocked) { - // TODO: ACCOUNTS_CONTROLLER keyring will return accounts instead of addresses, remove this flatMap after and just get the latest id - const updatedKeyringAddresses = keyringState.keyrings.flatMap( - (keyring) => keyring.accounts, - ); - const previousAccounts = this.listAccounts(); - - // if there are no overlaps between the addresses in the keyring and previous accounts, - // it means the keyring is being reinitialized because the vault is being restored with the same SRP - const overlaps = updatedKeyringAddresses.filter((address) => - previousAccounts.find( - (account) => - account.address.toLowerCase() === address.toLowerCase(), - ), - ); - - await this.updateAccounts(); - - if (updatedKeyringAddresses.length > previousAccounts.length) { - this.#handleNewAccountAdded( - updatedKeyringAddresses, - previousAccounts, - ); - } else if ( - updatedKeyringAddresses.length > 0 && - overlaps.length === 0 - ) { - // if the keyring is being reinitialized, the selected account will be reset to the first account - this.setSelectedAccount(this.listAccounts()[0].id); - } else if ( - updatedKeyringAddresses.length < previousAccounts.length && - overlaps.length > 0 && - !this.getAccount(this.state.internalAccounts.selectedAccount) - ) { - this.#handleSelectedAccountRemoved(); - } - } - }, + this.messagingSystem.subscribe( + 'KeyringController:stateChange', + async (keyringState) => + await this.#handleOnKeyringStateChange(keyringState), ); // if somehow the selected account becomes lost then select the first account @@ -262,7 +194,7 @@ export class AccountsController extends BaseControllerV2< address: '', options: {}, methods: [], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name: '', keyring: { @@ -459,7 +391,7 @@ export class AccountsController extends BaseControllerV2< 'eth_signTypedData_v3', 'eth_signTypedData_v4', ], - type: 'eip155:eoa', + type: EthAccountType.Eoa, metadata: { name: '', keyring: { @@ -493,6 +425,71 @@ export class AccountsController extends BaseControllerV2< this.setSelectedAccount(previousAccount.id); } + async #handleOnKeyringStateChange( + keyringState: KeyringControllerState, + ): Promise { + // check if there are any new accounts added + // TODO: change when accountAdded event is added to the keyring controller + console.log('running handleOnKeyringStateChange'); + + if (keyringState.isUnlocked) { + // TODO: ACCOUNTS_CONTROLLER keyring will return accounts instead of addresses, remove this flatMap after and just get the latest id + const updatedKeyringAddresses = keyringState.keyrings.flatMap( + (keyring) => keyring.accounts, + ); + const previousAccounts = this.listAccounts(); + + // if there are no overlaps between the addresses in the keyring and previous accounts, + // it means the keyring is being reinitialized because the vault is being restored with the same SRP + const overlaps = updatedKeyringAddresses.filter((address) => + previousAccounts.find( + (account) => account.address.toLowerCase() === address.toLowerCase(), + ), + ); + + await this.updateAccounts(); + + if (updatedKeyringAddresses.length > previousAccounts.length) { + this.#handleNewAccountAdded(updatedKeyringAddresses, previousAccounts); + } else if (updatedKeyringAddresses.length > 0 && overlaps.length === 0) { + // if the keyring is being reinitialized, the selected account will be reset to the first account + this.setSelectedAccount(this.listAccounts()[0].id); + } else if ( + updatedKeyringAddresses.length < previousAccounts.length && + overlaps.length > 0 && + !this.getAccount(this.state.internalAccounts.selectedAccount) + ) { + this.#handleSelectedAccountRemoved(); + } + } + } + + async #handleOnSnapStateChange( + snapState: SnapControllerState, + ): Promise { + // only check if snaps changed in status + const { snaps } = snapState; + const accounts = this.listAccounts().filter( + (account) => account.metadata.snap, + ); + + this.update((currentState: AccountsControllerState) => { + accounts.forEach((account) => { + const currentAccount = + currentState.internalAccounts.accounts[account.id]; + if (currentAccount?.metadata?.snap) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this account is guaranteed to have snap metadata + currentState.internalAccounts.accounts[ + account.id + ].metadata.snap.enabled = + snaps[currentAccount.metadata.snap.id].enabled && + !snaps[currentAccount.metadata.snap.id].blocked; + } + }); + }); + } + /** * Handles the event when a new account is added to the keyring. * @@ -503,22 +500,18 @@ export class AccountsController extends BaseControllerV2< updatedKeyringAddresses: string[], previousAccounts: InternalAccount[], ) { - const newAddress = updatedKeyringAddresses.find( + const [newAddress] = updatedKeyringAddresses.filter( (address) => !previousAccounts.find( (account) => account.address.toLowerCase() === address.toLowerCase(), ), ); - const newAccount = this.listAccounts().find( - (account) => - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - account.address.toLowerCase() === newAddress!.toLowerCase(), + const [newAccount] = this.listAccounts().filter( + (account) => account.address.toLowerCase() === newAddress.toLowerCase(), ); - // set the first new account as the selected account - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.setSelectedAccount(newAccount!.id); + this.setSelectedAccount(newAccount.id); } } From 6681dbe2280eade033a8d437d2f8bc7a4064b878 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 7 Sep 2023 10:43:58 +0800 Subject: [PATCH 30/54] fix: use EthMethod --- .../src/AccountsController.test.ts | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index b51e04c85ee..a1dd1f81d53 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -1,6 +1,6 @@ import { ControllerMessenger } from '@metamask/base-controller'; import type { InternalAccount } from '@metamask/keyring-api'; -import { EthAccountType } from '@metamask/keyring-api'; +import { EthAccountType, EthMethod } from '@metamask/keyring-api'; import type { SnapControllerState } from '@metamask/snaps-controllers'; import { SnapStatus } from '@metamask/snaps-utils'; import * as uuid from 'uuid'; @@ -30,15 +30,7 @@ const mockAccount: InternalAccount = { id: 'mock-id', address: '0x123', options: {}, - methods: [ - 'personal_sign', - 'eth_sign', - 'eth_signTransaction', - 'eth_signTypedData', - 'eth_signTypedData_v1', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - ], + methods: [...Object.values(EthMethod)], type: EthAccountType.Eoa, metadata: { name: 'Account 1', @@ -53,15 +45,7 @@ const mockAccount2: InternalAccount = { id: 'mock-id2', address: '0x1234', options: {}, - methods: [ - 'personal_sign', - 'eth_sign', - 'eth_signTransaction', - 'eth_signTypedData', - 'eth_signTypedData_v1', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - ], + methods: [...Object.values(EthMethod)], type: EthAccountType.Eoa, metadata: { name: 'Account 2', @@ -103,15 +87,7 @@ function createExpectedInternalAccount({ id, address, options: {}, - methods: [ - 'personal_sign', - 'eth_sign', - 'eth_signTransaction', - 'eth_signTypedData', - 'eth_signTypedData_v1', - 'eth_signTypedData_v3', - 'eth_signTypedData_v4', - ], + methods: [...Object.values(EthMethod)], type: EthAccountType.Eoa, metadata: { name, From da0782ed41a7a09d01cc7976dbae2baebe84c8ee Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 7 Sep 2023 20:58:27 +0800 Subject: [PATCH 31/54] chore: remove log --- packages/accounts-controller/src/AccountsController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 750c047ac79..8a46f2ad46d 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -430,7 +430,6 @@ export class AccountsController extends BaseControllerV2< ): Promise { // check if there are any new accounts added // TODO: change when accountAdded event is added to the keyring controller - console.log('running handleOnKeyringStateChange'); if (keyringState.isUnlocked) { // TODO: ACCOUNTS_CONTROLLER keyring will return accounts instead of addresses, remove this flatMap after and just get the latest id From 573305cd970bfdb8f94072b2680cb67531c62db3 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Fri, 8 Sep 2023 17:49:53 +0800 Subject: [PATCH 32/54] fix: lint issue --- packages/accounts-controller/src/AccountsController.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 8a46f2ad46d..6a5a2c67487 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -150,7 +150,6 @@ export class AccountsController extends BaseControllerV2< await this.#handleOnKeyringStateChange(keyringState), ); - // if somehow the selected account becomes lost then select the first account if ( this.state.internalAccounts.selectedAccount !== '' && !this.getAccount(this.state.internalAccounts.selectedAccount) @@ -477,11 +476,7 @@ export class AccountsController extends BaseControllerV2< const currentAccount = currentState.internalAccounts.accounts[account.id]; if (currentAccount?.metadata?.snap) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore this account is guaranteed to have snap metadata - currentState.internalAccounts.accounts[ - account.id - ].metadata.snap.enabled = + currentAccount.metadata.snap.enabled = snaps[currentAccount.metadata.snap.id].enabled && !snaps[currentAccount.metadata.snap.id].blocked; } From e521e20d5a2dc6a979f22ecaccf4f9f184a8894d Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Fri, 8 Sep 2023 17:51:52 +0800 Subject: [PATCH 33/54] fix: add jsdoc for #handleOnSnapStateChange and #handleOnKeyringStateChange --- .../accounts-controller/src/AccountsController.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 6a5a2c67487..693c294e297 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -150,6 +150,7 @@ export class AccountsController extends BaseControllerV2< await this.#handleOnKeyringStateChange(keyringState), ); + // if somehow the selected account becomes lost then select the first account if ( this.state.internalAccounts.selectedAccount !== '' && !this.getAccount(this.state.internalAccounts.selectedAccount) @@ -424,6 +425,12 @@ export class AccountsController extends BaseControllerV2< this.setSelectedAccount(previousAccount.id); } + /** + * Handles changes in the keyring state, specifically when new accounts are added or removed. + * + * @param keyringState - The new state of the keyring controller. + * @returns A Promise that resolves when the function has finished executing. + */ async #handleOnKeyringStateChange( keyringState: KeyringControllerState, ): Promise { @@ -462,6 +469,12 @@ export class AccountsController extends BaseControllerV2< } } + /** + * Handles the change in SnapControllerState by updating the metadata of accounts that have a snap enabled. + * + * @param snapState - The new SnapControllerState. + * @returns A Promise that resolves when the update is complete. + */ async #handleOnSnapStateChange( snapState: SnapControllerState, ): Promise { From e50cea269cba0a86cc55da066ec06cab39949c32 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Fri, 8 Sep 2023 18:00:22 +0800 Subject: [PATCH 34/54] chore: bump keyring dep --- packages/accounts-controller/package.json | 4 ++-- yarn.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 7e338c144ae..91c66acd241 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -31,7 +31,7 @@ "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "^0.2.0", "@metamask/keyring-api": "^0.2.2", - "@metamask/keyring-controller": "^7.3.0", + "@metamask/keyring-controller": "^7.4.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", @@ -54,7 +54,7 @@ "typescript": "~4.6.3" }, "peerDependencies": { - "@metamask/keyring-controller": "^7.3.0" + "@metamask/keyring-controller": "^7.4.0" }, "engines": { "node": ">=16.0.0" diff --git a/yarn.lock b/yarn.lock index 56912d548b6..0a014df46db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1295,7 +1295,7 @@ __metadata: "@metamask/base-controller": ^3.2.1 "@metamask/eth-snap-keyring": ^0.2.0 "@metamask/keyring-api": ^0.2.2 - "@metamask/keyring-controller": ^7.3.0 + "@metamask/keyring-controller": ^7.4.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 "@metamask/utils": ^6.2.0 @@ -1862,7 +1862,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@^7.3.0, @metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/keyring-controller@^7.4.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: From 54478a64399fe421c2feac7b5fa0429e5ea1eca6 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Fri, 8 Sep 2023 18:19:35 +0800 Subject: [PATCH 35/54] chore: update yarn.lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 0a014df46db..87a9cf36221 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1314,7 +1314,7 @@ __metadata: typescript: ~4.6.3 uuid: ^8.3.2 peerDependencies: - "@metamask/keyring-controller": ^7.3.0 + "@metamask/keyring-controller": ^7.4.0 languageName: unknown linkType: soft From 2368fa74f89962c0678f54360d272bd7a7e5231b Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 11 Sep 2023 20:14:50 +0800 Subject: [PATCH 36/54] feat: add check for disabled snap accounts, or missing snap accounts --- packages/accounts-controller/jest.config.js | 2 +- .../src/AccountsController.test.ts | 60 +++++++++++++++++++ .../src/AccountsController.ts | 20 ++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 266acba02b7..75290af4e75 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,7 +17,7 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 92.59, + branches: 92.04, functions: 100, lines: 100, statements: 100, diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index a1dd1f81d53..8a0e51e3e0f 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -769,6 +769,66 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); + it('should not delete snap accounts that are not enabled, or accounts where the communication with the snap is interrupted', async () => { + const mockDisabledSnapAccount = { + ...mockSnapAccount, + id: 'mock-disabled-snap', + metadata: { + ...mockSnapAccount.metadata, + name: 'Snap Account 3', + snap: { + ...mockSnapAccount.metadata.snap, + enabled: false, + }, + }, + }; + mockGetAccounts.mockResolvedValue([]); + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount, mockSnapAccount2], + }, + ]); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + 'mock-disabled-snap': mockDisabledSnapAccount, + }, + selectedAccount: '', + }, + }, + keyringApiEnabled: true, + }); + + const expectedAccount1 = { + ...mockSnapAccount, + metadata: { + ...mockSnapAccount.metadata, + name: 'Snap Account 1', + }, + }; + + const expectedAccount2 = { + ...mockSnapAccount2, + metadata: { + ...mockSnapAccount2.metadata, + name: 'Snap Account 2', + }, + }; + + const expectedAccounts = [ + expectedAccount1, + expectedAccount2, + mockDisabledSnapAccount, + ]; + + await accountsController.updateAccounts(); + + expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); + }); + it('should return an empty array if the snap keyring is not defined', async () => { mockGetAccounts.mockResolvedValue([]); mockGetKeyringByType.mockReturnValueOnce([undefined]); diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 693c294e297..2c8cc37d302 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -359,7 +359,25 @@ export class AccountsController extends BaseControllerV2< } const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); - return snapAccounts; + + // missing accounts should be included as well but with the snap metadata enabled as false + const missingSnapAccounts = Object.values( + this.state.internalAccounts.accounts, + ) + .filter((currentAccount) => + snapAccounts.some( + (account) => + account.id !== currentAccount.id && currentAccount.metadata.snap, + ), + ) + .map((account) => { + if (account.metadata?.snap) { + account.metadata.snap.enabled = false; + } + return account; + }); + + return [...snapAccounts, ...missingSnapAccounts]; } /** From 69c9572e5abaf7b0b8a37b724679c95e790b5f3d Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 18 Sep 2023 13:05:10 +0800 Subject: [PATCH 37/54] feat: use actions instead of importing keyringcontroller methods --- packages/accounts-controller/package.json | 2 +- .../src/AccountsController.test.ts | 279 ++++++++++-------- .../src/AccountsController.ts | 70 ++--- .../src/KeyringController.ts | 20 +- yarn.lock | 61 +++- 5 files changed, 267 insertions(+), 165 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 91c66acd241..4b1b99a5b19 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -30,7 +30,7 @@ "dependencies": { "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "^0.2.0", - "@metamask/keyring-api": "^0.2.2", + "@metamask/keyring-api": "^0.2.4", "@metamask/keyring-controller": "^7.4.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 8a0e51e3e0f..70a206355be 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -101,7 +101,7 @@ function createExpectedInternalAccount({ if (snapId) { account.metadata.snap = { id: snapId, - name: '', + name: 'snap-name', enabled: Boolean(snapEnabled), }; } @@ -151,6 +151,11 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { 'KeyringController:accountRemoved', 'KeyringController:stateChange', ], + allowedActions: [ + 'KeyringController:getAccounts', + 'KeyringController:getKeyringForAccount', + 'KeyringController:getKeyringsByType', + ], }); } @@ -181,9 +186,6 @@ function setupAccountsController({ const accountsController = new AccountsController({ messenger: accountsControllerMessenger, state: { ...defaultState, ...initialState }, - getKeyringForAccount: mockGetKeyringForAccount, - getKeyringByType: mockGetKeyringByType, - getAccounts: mockGetAccounts, keyringApiEnabled, }); return accountsController; @@ -381,11 +383,20 @@ describe('AccountsController', () => { it('should add new accounts', async () => { const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([ - mockAccount.address, - mockAccount2.address, - ]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([ + mockAccount.address, + mockAccount2.address, + ]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -439,8 +450,17 @@ describe('AccountsController', () => { keyringType: 'HD Key Tree', }); mockUUID.mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([mockReinitialisedAccount.address]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([ + mockReinitialisedAccount.address, + ]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -482,8 +502,16 @@ describe('AccountsController', () => { it('should delete accounts if its gone from the keyring state', async () => { const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([mockAccount2.address]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([mockAccount2.address]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -526,11 +554,19 @@ describe('AccountsController', () => { it('should delete accounts and set the most recent lastSelected account', async () => { const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([ - mockAccount.address, - mockAccount2.address, - ]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); + const mockNewKeyringState = { isUnlocked: true, keyrings: [ @@ -579,11 +615,19 @@ describe('AccountsController', () => { it('should delete accounts and set the most recent lastSelected account when there are accounts that have never been selected', async () => { const messenger = buildMessenger(); mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([ - mockAccount.address, - mockAccount2.address, - ]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); + const mockAccount2WithoutLastSelected = { ...mockAccount2, metadata: { @@ -687,7 +731,7 @@ describe('AccountsController', () => { snap: { enabled: true, id: 'mock-snap-id2', - name: '', + name: 'snap-name', }, lastSelected: undefined, }, @@ -695,8 +739,16 @@ describe('AccountsController', () => { it('should update accounts with legacy accounts', async () => { mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); - mockGetAccounts.mockResolvedValue([mockAddress1, mockAddress2]); - mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }); + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([mockAddress1, mockAddress2]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); const accountsController = setupAccountsController({ initialState: { @@ -706,6 +758,7 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, + messenger, }); const expectedAccounts = [ createExpectedInternalAccount({ @@ -728,78 +781,31 @@ describe('AccountsController', () => { }); it('should update accounts with snap accounts when snap keyring is defined and has accounts', async () => { - mockGetAccounts.mockResolvedValue([]); - mockGetKeyringByType.mockReturnValueOnce([ - { - type: 'Snap Keyring', - listAccounts: async () => [mockSnapAccount, mockSnapAccount2], - }, - ]); - - const accountsController = setupAccountsController({ - initialState: { - internalAccounts: { - accounts: {}, - selectedAccount: '', - }, - }, - keyringApiEnabled: true, - }); - - const expectedAccount1 = { - ...mockSnapAccount, - metadata: { - ...mockSnapAccount.metadata, - name: 'Snap Account 1', - }, - }; - - const expectedAccount2 = { - ...mockSnapAccount2, - metadata: { - ...mockSnapAccount2.metadata, - name: 'Snap Account 2', - }, - }; - - const expectedAccounts = [expectedAccount1, expectedAccount2]; - - await accountsController.updateAccounts(); - - expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); - }); + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([]), + ); - it('should not delete snap accounts that are not enabled, or accounts where the communication with the snap is interrupted', async () => { - const mockDisabledSnapAccount = { - ...mockSnapAccount, - id: 'mock-disabled-snap', - metadata: { - ...mockSnapAccount.metadata, - name: 'Snap Account 3', - snap: { - ...mockSnapAccount.metadata.snap, - enabled: false, + messenger.registerActionHandler( + 'KeyringController:getKeyringsByType', + mockGetKeyringByType.mockResolvedValue([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount, mockSnapAccount2], }, - }, - }; - mockGetAccounts.mockResolvedValue([]); - mockGetKeyringByType.mockReturnValueOnce([ - { - type: 'Snap Keyring', - listAccounts: async () => [mockSnapAccount, mockSnapAccount2], - }, - ]); + ]), + ); const accountsController = setupAccountsController({ initialState: { internalAccounts: { - accounts: { - 'mock-disabled-snap': mockDisabledSnapAccount, - }, + accounts: {}, selectedAccount: '', }, }, keyringApiEnabled: true, + messenger, }); const expectedAccount1 = { @@ -818,11 +824,7 @@ describe('AccountsController', () => { }, }; - const expectedAccounts = [ - expectedAccount1, - expectedAccount2, - mockDisabledSnapAccount, - ]; + const expectedAccounts = [expectedAccount1, expectedAccount2]; await accountsController.updateAccounts(); @@ -830,8 +832,16 @@ describe('AccountsController', () => { }); it('should return an empty array if the snap keyring is not defined', async () => { - mockGetAccounts.mockResolvedValue([]); - mockGetKeyringByType.mockReturnValueOnce([undefined]); + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringsByType', + mockGetKeyringByType.mockReturnValueOnce([undefined]), + ); const accountsController = setupAccountsController({ initialState: { @@ -841,6 +851,7 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, + messenger, }); const expectedAccounts: InternalAccount[] = []; @@ -852,18 +863,28 @@ describe('AccountsController', () => { it('should filter snap accounts from legacyAccounts', async () => { mockUUID.mockReturnValueOnce('mock-id'); - mockGetKeyringByType.mockReturnValueOnce([ - { - type: 'Snap Keyring', - listAccounts: async () => [mockSnapAccount2], - }, - ]); + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getKeyringsByType', + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount2], + }, + ]), + ); // first account will be legacy, second will be a snap account - mockGetAccounts.mockResolvedValue([mockAddress1, '0x1234']); - mockGetKeyringForAccount - .mockResolvedValueOnce({ type: 'HD Key Tree' }) - .mockResolvedValueOnce({ type: 'Snap Keyring' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([mockAddress1, '0x1234']), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount + .mockResolvedValueOnce({ type: 'HD Key Tree' }) + .mockResolvedValueOnce({ type: 'Snap Keyring' }), + ); const accountsController = setupAccountsController({ initialState: { @@ -873,6 +894,7 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, + messenger, }); const expectedAccounts = [ createExpectedInternalAccount({ @@ -897,18 +919,28 @@ describe('AccountsController', () => { it('should filter snap accounts from legacyAccounts even if the snap account is listed before legacy accounts', async () => { mockUUID.mockReturnValue('mock-id'); - mockGetKeyringByType.mockReturnValueOnce([ - { - type: 'Snap Keyring', - listAccounts: async () => [mockSnapAccount2], - }, - ]); + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getKeyringsByType', + mockGetKeyringByType.mockReturnValueOnce([ + { + type: 'Snap Keyring', + listAccounts: async () => [mockSnapAccount2], + }, + ]), + ); // first account will be legacy, second will be a snap account - mockGetAccounts.mockResolvedValue(['0x1234', mockAddress1]); - mockGetKeyringForAccount - .mockResolvedValueOnce({ type: 'Snap Keyring' }) - .mockResolvedValueOnce({ type: 'HD Key Tree' }); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue(['0x1234', mockAddress1]), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount + .mockResolvedValueOnce({ type: 'Snap Keyring' }) + .mockResolvedValueOnce({ type: 'HD Key Tree' }), + ); const accountsController = setupAccountsController({ initialState: { @@ -918,6 +950,7 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: true, + messenger, }); const expectedAccounts = [ createExpectedInternalAccount({ @@ -932,6 +965,7 @@ describe('AccountsController', () => { address: mockSnapAccount2.address, keyringType: 'Snap Keyring', snapId: 'mock-snap-id2', + snapEnabled: true, }), ]; @@ -951,8 +985,16 @@ describe('AccountsController', () => { 'Unknown', ])('should add accounts for %s type', async (keyringType) => { mockUUID.mockReturnValue('mock-id'); - mockGetAccounts.mockResolvedValue([mockAddress1]); - mockGetKeyringForAccount.mockResolvedValue({ type: keyringType }); + + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([mockAddress1]), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: keyringType }), + ); const accountsController = setupAccountsController({ initialState: { @@ -962,6 +1004,7 @@ describe('AccountsController', () => { }, }, keyringApiEnabled: false, + messenger, }); const expectedAccounts = [ @@ -1080,8 +1123,6 @@ describe('AccountsController', () => { }, }); - console.log(mockAccount, mockAccount2); - const result = accountsController.listAccounts(); expect(result).toStrictEqual([ diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 2c8cc37d302..2391a4f3e59 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -5,8 +5,10 @@ import type { InternalAccount } from '@metamask/keyring-api'; import { EthAccountType } from '@metamask/keyring-api'; import type { KeyringControllerState, - KeyringController, KeyringControllerEvents, + KeyringControllerGetKeyringForAccountAction, + KeyringControllerGetKeyringsByTypeAction, + KeyringControllerGetAccountsAction, } from '@metamask/keyring-controller'; import type { SnapControllerEvents, @@ -30,7 +32,17 @@ export type AccountsControllerGetStateAction = { handler: () => AccountsControllerState; }; -export type AccountsControllerActions = AccountsControllerGetStateAction; +export type AccountsControllerSetCurrentAccount = { + type: `${typeof controllerName}:setCurrentAccount`; + handler: AccountsController['setSelectedAccount']; +}; + +export type AccountsControllerActions = + | AccountsControllerGetStateAction + | AccountsControllerSetCurrentAccount + | KeyringControllerGetKeyringForAccountAction + | KeyringControllerGetKeyringsByTypeAction + | KeyringControllerGetAccountsAction; export type AccountsControllerChangeEvent = { type: `${typeof controllerName}:stateChange`; @@ -87,12 +99,6 @@ export class AccountsController extends BaseControllerV2< AccountsControllerState, AccountsControllerMessenger > { - getKeyringForAccount: KeyringController['getKeyringForAccount']; - - getKeyringByType: KeyringController['getKeyringsByType']; - - getAccounts: KeyringController['getAccounts']; - keyringApiEnabled: boolean; /** @@ -102,24 +108,15 @@ export class AccountsController extends BaseControllerV2< * @param options.messenger - The messenger object. * @param options.state - Initial state to set on this controller * @param [options.keyringApiEnabled] - The keyring API enabled flag. - * @param options.getKeyringForAccount - Gets the keyring for a given account from the keyring controller. - * @param options.getKeyringByType - Gets the keyring instance for the given type from the keyring controller. - * @param options.getAccounts - Gets all the accounts from the keyring controller. */ constructor({ messenger, state, keyringApiEnabled, - getKeyringForAccount, - getKeyringByType, - getAccounts, }: { messenger: AccountsControllerMessenger; state: AccountsControllerState; keyringApiEnabled?: boolean; - getKeyringForAccount: KeyringController['getKeyringForAccount']; - getKeyringByType: KeyringController['getKeyringsByType']; - getAccounts: KeyringController['getAccounts']; }) { super({ messenger, @@ -131,9 +128,6 @@ export class AccountsController extends BaseControllerV2< }, }); - this.getKeyringForAccount = getKeyringForAccount; - this.getKeyringByType = getKeyringByType; - this.getAccounts = getAccounts; this.keyringApiEnabled = Boolean(keyringApiEnabled); if (this.keyringApiEnabled) { @@ -352,7 +346,11 @@ export class AccountsController extends BaseControllerV2< * @returns A promise that resolves to an array of InternalAccount objects. */ async #listSnapAccounts(): Promise { - const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); + // const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); + const [snapKeyring] = await this.messagingSystem.call( + 'KeyringController:getKeyringsByType', + SnapKeyring.type, + ); // snap keyring is not available until the first account is created in the keyring controller if (!snapKeyring) { return []; @@ -360,24 +358,7 @@ export class AccountsController extends BaseControllerV2< const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); - // missing accounts should be included as well but with the snap metadata enabled as false - const missingSnapAccounts = Object.values( - this.state.internalAccounts.accounts, - ) - .filter((currentAccount) => - snapAccounts.some( - (account) => - account.id !== currentAccount.id && currentAccount.metadata.snap, - ), - ) - .map((account) => { - if (account.metadata?.snap) { - account.metadata.snap.enabled = false; - } - return account; - }); - - return [...snapAccounts, ...missingSnapAccounts]; + return snapAccounts; } /** @@ -388,10 +369,15 @@ export class AccountsController extends BaseControllerV2< * @returns A Promise that resolves to an array of InternalAccount objects. */ async #listLegacyAccounts(): Promise { - const addresses = await this.getAccounts(); + const addresses = await this.messagingSystem.call( + 'KeyringController:getAccounts', + ); const internalAccounts: InternalAccount[] = []; for (const address of addresses) { - const keyring = await this.getKeyringForAccount(address); + const keyring = await this.messagingSystem.call( + 'KeyringController:getKeyringForAccount', + address, + ); const v4options = { random: sha256FromString(address).slice(0, 16), }; @@ -574,7 +560,7 @@ export function keyringTypeToName(keyringType: string): string { } default: { console.warn(`Unknown keyring ${keyringType}`); - return 'Account'; + return 'Unknown'; } } } diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index aff6d0b4dcb..8d31cba77e4 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -90,6 +90,21 @@ export type KeyringControllerGetEncryptionPublicKeyAction = { handler: KeyringController['getEncryptionPublicKey']; }; +export type KeyringControllerGetKeyringsByTypeAction = { + type: `${typeof name}:getKeyringsByType`; + handler: KeyringController['getKeyringsByType']; +}; + +export type KeyringControllerGetKeyringForAccountAction = { + type: `${typeof name}:getKeyringForAccount`; + handler: KeyringController['getKeyringForAccount']; +}; + +export type KeyringControllerGetAccountsAction = { + type: `${typeof name}:getAccounts`; + handler: KeyringController['getAccounts']; +}; + export type KeyringControllerStateChangeEvent = { type: `${typeof name}:stateChange`; payload: [KeyringControllerState, Patch[]]; @@ -116,7 +131,10 @@ export type KeyringControllerActions = | KeyringControllerSignPersonalMessageAction | KeyringControllerSignTypedMessageAction | KeyringControllerDecryptMessageAction - | KeyringControllerGetEncryptionPublicKeyAction; + | KeyringControllerGetEncryptionPublicKeyAction + | KeyringControllerGetAccountsAction + | KeyringControllerGetKeyringsByTypeAction + | KeyringControllerGetKeyringForAccountAction; export type KeyringControllerEvents = | KeyringControllerStateChangeEvent diff --git a/yarn.lock b/yarn.lock index 2a001ac1aba..89b8b5b3119 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1294,7 +1294,7 @@ __metadata: "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 "@metamask/eth-snap-keyring": ^0.2.0 - "@metamask/keyring-api": ^0.2.2 + "@metamask/keyring-api": ^0.2.4 "@metamask/keyring-controller": ^7.4.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 @@ -1818,6 +1818,17 @@ __metadata: languageName: unknown linkType: soft +"@metamask/json-rpc-engine@npm:^7.1.1": + version: 7.1.1 + resolution: "@metamask/json-rpc-engine@npm:7.1.1" + dependencies: + "@metamask/rpc-errors": ^6.0.0 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + checksum: 9dddd9142965ccd86313cda5bf13f15bf99c6c14631f93aab78de353317d548a334b5b125cdc134edd7d54e2f2e4961a0bdcd24fba997b2913083955df8fefa1 + languageName: node + linkType: hard + "@metamask/key-tree@npm:^7.1.1": version: 7.1.1 resolution: "@metamask/key-tree@npm:7.1.1" @@ -1862,6 +1873,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-api@npm:^0.2.4": + version: 0.2.4 + resolution: "@metamask/keyring-api@npm:0.2.4" + dependencies: + "@metamask/providers": ^12.0.0 + "@metamask/rpc-methods": ^0.38.1-flask.1 + "@metamask/snaps-controllers": ^0.38.2-flask.1 + "@metamask/snaps-utils": ^0.38.2-flask.1 + "@metamask/utils": ^8.1.0 + "@types/uuid": ^9.0.1 + superstruct: ^1.0.3 + uuid: ^9.0.0 + checksum: b557321bf85b1d264467cbddf3551942dee882fb544706537fa9dc544ec7f0e6900fe75b5280af0637d47014073ebfe7b9ab68f8e99ce8b31bdd8e3859c883d2 + languageName: node + linkType: hard + "@metamask/keyring-controller@^7.4.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" @@ -2162,6 +2189,26 @@ __metadata: languageName: node linkType: hard +"@metamask/providers@npm:^12.0.0": + version: 12.0.0 + resolution: "@metamask/providers@npm:12.0.0" + dependencies: + "@metamask/json-rpc-engine": ^7.1.1 + "@metamask/object-multiplex": ^1.1.0 + "@metamask/rpc-errors": ^6.0.0 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + detect-browser: ^5.2.0 + extension-port-stream: ^2.1.1 + fast-deep-equal: ^3.1.3 + is-stream: ^2.0.0 + json-rpc-middleware-stream: ^4.2.1 + pump: ^3.0.0 + webextension-polyfill: ^0.10.0 + checksum: 4e466322b3e020607ddb16599555bacc1c4fe8bfe2f781e123c012019f79bbc8206207adc3150d04fb7e36fbd959abf5fc54a6edd072fbed4f4edcabf7dd1ce2 + languageName: node + linkType: hard + "@metamask/rate-limit-controller@workspace:packages/rate-limit-controller": version: 0.0.0-use.local resolution: "@metamask/rate-limit-controller@workspace:packages/rate-limit-controller" @@ -2190,6 +2237,16 @@ __metadata: languageName: node linkType: hard +"@metamask/rpc-errors@npm:^6.0.0": + version: 6.0.0 + resolution: "@metamask/rpc-errors@npm:6.0.0" + dependencies: + "@metamask/utils": ^8.0.0 + fast-safe-stringify: ^2.0.6 + checksum: 7e1ee1a98972266af4a34f0bbc842cdc11dc565056f0b8fbc93aa95663a7027eab8ff1fecbe3e09c38a1dc199f8219a6c69b2237015b2fdb8de0e5b35027c3f8 + languageName: node + linkType: hard + "@metamask/rpc-methods@npm:^0.38.1-flask.1": version: 0.38.1-flask.1 resolution: "@metamask/rpc-methods@npm:0.38.1-flask.1" @@ -5597,7 +5654,7 @@ __metadata: languageName: node linkType: hard -"extension-port-stream@npm:^2.0.1": +"extension-port-stream@npm:^2.0.1, extension-port-stream@npm:^2.1.1": version: 2.1.1 resolution: "extension-port-stream@npm:2.1.1" dependencies: From c7150a9aaa7a31cc6b0713f432b4a631988eaba4 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 18 Sep 2023 13:25:55 +0800 Subject: [PATCH 38/54] feat: add new AccountsController actions --- .../src/AccountsController.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 2391a4f3e59..57ad53281d8 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -37,9 +37,27 @@ export type AccountsControllerSetCurrentAccount = { handler: AccountsController['setSelectedAccount']; }; +export type AccountsControllerSetAccountName = { + type: `${typeof controllerName}:setAccountName`; + handler: AccountsController['setAccountName']; +}; + +export type AccountsControllerListAccounts = { + type: `${typeof controllerName}:listAccounts`; + handler: AccountsController['listAccounts']; +}; + +export type AccountsControllerUpdateAccounts = { + type: `${typeof controllerName}:updateAccounts`; + handler: AccountsController['updateAccounts']; +}; + export type AccountsControllerActions = | AccountsControllerGetStateAction | AccountsControllerSetCurrentAccount + | AccountsControllerListAccounts + | AccountsControllerSetAccountName + | AccountsControllerUpdateAccounts | KeyringControllerGetKeyringForAccountAction | KeyringControllerGetKeyringsByTypeAction | KeyringControllerGetAccountsAction; From 22e8000c416037f9bac50a73375cb8622beba109 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 18 Sep 2023 16:38:42 +0800 Subject: [PATCH 39/54] fix: register the new actions to the messagingSystem --- .../src/AccountsController.ts | 24 +++++++++++++++++++ .../src/KeyringController.ts | 15 ++++++++++++ 2 files changed, 39 insertions(+) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 57ad53281d8..6950d984510 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -162,6 +162,8 @@ export class AccountsController extends BaseControllerV2< await this.#handleOnKeyringStateChange(keyringState), ); + this.#registerMessageHandlers(); + // if somehow the selected account becomes lost then select the first account if ( this.state.internalAccounts.selectedAccount !== '' && @@ -542,6 +544,28 @@ export class AccountsController extends BaseControllerV2< this.setSelectedAccount(newAccount.id); } + + #registerMessageHandlers() { + this.messagingSystem.registerActionHandler( + `${controllerName}:setCurrentAccount`, + this.setSelectedAccount.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:listAccounts`, + this.listAccounts.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:setAccountName`, + this.setAccountName.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:updateAccounts`, + this.updateAccounts.bind(this), + ); + } } /** diff --git a/packages/keyring-controller/src/KeyringController.ts b/packages/keyring-controller/src/KeyringController.ts index 8d31cba77e4..8c2602110dc 100644 --- a/packages/keyring-controller/src/KeyringController.ts +++ b/packages/keyring-controller/src/KeyringController.ts @@ -985,6 +985,21 @@ export class KeyringController extends BaseControllerV2< `${name}:getEncryptionPublicKey`, this.getEncryptionPublicKey.bind(this), ); + + this.messagingSystem.registerActionHandler( + `${name}:getAccounts`, + this.getAccounts.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${name}:getKeyringsByType`, + this.getKeyringsByType.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${name}:getKeyringForAccount`, + this.getKeyringForAccount.bind(this), + ); } /** From 369440a921ea9fe6a11cbf9e75f6dab922b5508b Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 18 Sep 2023 19:14:42 +0800 Subject: [PATCH 40/54] feat: add tests for actions --- .../src/AccountsController.test.ts | 110 ++++++++++++++++++ .../src/AccountsController.ts | 8 +- 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 70a206355be..555d2b8d5f1 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -155,6 +155,10 @@ function buildAccountsControllerMessenger(messenger = buildMessenger()) { 'KeyringController:getAccounts', 'KeyringController:getKeyringForAccount', 'KeyringController:getKeyringsByType', + 'AccountsController:listAccounts', + 'AccountsController:setAccountName', + 'AccountsController:setSelectedAccount', + 'AccountsController:updateAccounts', ], }); } @@ -1298,4 +1302,110 @@ describe('AccountsController', () => { ).toThrow(`Account Id unknown account not found`); }); }); + + describe('actions', () => { + beforeEach(() => { + jest.spyOn(AccountsController.prototype, 'setSelectedAccount'); + jest.spyOn(AccountsController.prototype, 'listAccounts'); + jest.spyOn(AccountsController.prototype, 'setAccountName'); + jest.spyOn(AccountsController.prototype, 'updateAccounts'); + }); + + describe('setSelectedAccount', () => { + it('should set the selected account', async () => { + const messenger = buildMessenger(); + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + messenger, + }); + + await messenger.call( + 'AccountsController:setSelectedAccount', + 'mock-id', + ); + expect(accountsController.setSelectedAccount).toHaveBeenCalledWith( + 'mock-id', + ); + }); + }); + + describe('listAccounts', () => { + it('should retrieve a list of accounts', async () => { + const messenger = buildMessenger(); + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + messenger, + }); + + await messenger.call('AccountsController:listAccounts'); + expect(accountsController.listAccounts).toHaveBeenCalledWith(); + }); + }); + + describe('setAccountName', () => { + it('should set the account name', async () => { + const messenger = buildMessenger(); + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + messenger, + }); + + await messenger.call( + 'AccountsController:setAccountName', + 'mock-id', + 'new name', + ); + expect(accountsController.setAccountName).toHaveBeenCalledWith( + 'mock-id', + 'new name', + ); + }); + }); + + describe('updateAccounts', () => { + it('should update accounts', async () => { + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValueOnce([]), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringsByType', + mockGetKeyringByType.mockResolvedValueOnce([]), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValueOnce([]), + ); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { [mockAccount.id]: mockAccount }, + selectedAccount: mockAccount.id, + }, + }, + messenger, + }); + + await messenger.call('AccountsController:updateAccounts'); + expect(accountsController.updateAccounts).toHaveBeenCalledWith(); + }); + }); + }); }); diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 6950d984510..06c1eba64dc 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -32,8 +32,8 @@ export type AccountsControllerGetStateAction = { handler: () => AccountsControllerState; }; -export type AccountsControllerSetCurrentAccount = { - type: `${typeof controllerName}:setCurrentAccount`; +export type AccountsControllerSetSelectedAccount = { + type: `${typeof controllerName}:setSelectedAccount`; handler: AccountsController['setSelectedAccount']; }; @@ -54,7 +54,7 @@ export type AccountsControllerUpdateAccounts = { export type AccountsControllerActions = | AccountsControllerGetStateAction - | AccountsControllerSetCurrentAccount + | AccountsControllerSetSelectedAccount | AccountsControllerListAccounts | AccountsControllerSetAccountName | AccountsControllerUpdateAccounts @@ -547,7 +547,7 @@ export class AccountsController extends BaseControllerV2< #registerMessageHandlers() { this.messagingSystem.registerActionHandler( - `${controllerName}:setCurrentAccount`, + `${controllerName}:setSelectedAccount`, this.setSelectedAccount.bind(this), ); From ab8224330de9d1ee098ef78ee82e9e9be68c1055 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Mon, 18 Sep 2023 20:25:05 +0800 Subject: [PATCH 41/54] fix: changes accounts with unknown keyring to throw and error --- .../src/AccountsController.test.ts | 30 ++++++++++++++++++- .../src/AccountsController.ts | 3 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 555d2b8d5f1..7a87094ba8c 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -986,7 +986,6 @@ describe('AccountsController', () => { 'Lattice Hardware', 'QR Hardware Wallet Device', 'Custody', - 'Unknown', ])('should add accounts for %s type', async (keyringType) => { mockUUID.mockReturnValue('mock-id'); @@ -1024,6 +1023,35 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); + + it('should throw an error if the keyring type is unknown', async () => { + mockUUID.mockReturnValue('mock-id'); + + const messenger = buildMessenger(); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([mockAddress1]), + ); + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'unknown' }), + ); + + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: {}, + selectedAccount: '', + }, + }, + keyringApiEnabled: false, + messenger, + }); + + await expect(accountsController.updateAccounts()).rejects.toThrow( + 'Unknown keyring unknown', + ); + }); }); describe('loadBackup', () => { diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 06c1eba64dc..43013d72d0a 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -601,8 +601,7 @@ export function keyringTypeToName(keyringType: string): string { return 'Custody'; } default: { - console.warn(`Unknown keyring ${keyringType}`); - return 'Unknown'; + throw new Error(`Unknown keyring ${keyringType}`); } } } From 6d237b48eed08fe3f41145f0881237871d005a62 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Tue, 19 Sep 2023 10:55:59 +0800 Subject: [PATCH 42/54] feat: add tests for new actions in KeyringController --- .../src/KeyringController.test.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/keyring-controller/src/KeyringController.test.ts b/packages/keyring-controller/src/KeyringController.test.ts index 34ddb0c0d3a..c4624a6dba6 100644 --- a/packages/keyring-controller/src/KeyringController.test.ts +++ b/packages/keyring-controller/src/KeyringController.test.ts @@ -2033,6 +2033,67 @@ describe('KeyringController', () => { ); }); }); + + describe('getKeyringsByType', () => { + beforeEach(() => { + jest + .spyOn(KeyringController.prototype, 'getKeyringsByType') + .mockReturnValue([ + { + type: 'HD Key Tree', + accounts: ['0x1234'], + }, + ]); + }); + + it('should return correct keyring by type', async () => { + await withController(async ({ controller, messenger }) => { + await messenger.call( + 'KeyringController:getKeyringsByType', + 'HD Key Tree', + ); + + expect(controller.getKeyringsByType).toHaveBeenCalledWith( + 'HD Key Tree', + ); + }); + }); + }); + + describe('getKeyringForAccount', () => { + beforeEach(() => { + jest + .spyOn(KeyringController.prototype, 'getKeyringForAccount') + .mockResolvedValue({ + type: 'HD Key Tree', + accounts: ['0x1234'], + }); + }); + + it('should return the keyring for the account', async () => { + await withController(async ({ controller, messenger }) => { + await messenger.call('KeyringController:getKeyringForAccount', '0x0'); + + expect(controller.getKeyringForAccount).toHaveBeenCalledWith('0x0'); + }); + }); + }); + + describe('getAccounts', () => { + beforeEach(() => { + jest + .spyOn(KeyringController.prototype, 'getAccounts') + .mockResolvedValue(['0x1234']); + }); + + it('should return all accounts', async () => { + await withController(async ({ controller, messenger }) => { + await messenger.call('KeyringController:getAccounts'); + + expect(controller.getAccounts).toHaveBeenCalledWith(); + }); + }); + }); }); }); @@ -2091,6 +2152,9 @@ function buildKeyringControllerMessenger(messenger = buildMessenger()) { 'KeyringController:signTypedMessage', 'KeyringController:decryptMessage', 'KeyringController:getEncryptionPublicKey', + 'KeyringController:getKeyringsByType', + 'KeyringController:getKeyringForAccount', + 'KeyringController:getAccounts', ], allowedEvents: [ 'KeyringController:stateChange', From c237821dd9f4931117742bbb830347102c89402b Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Tue, 19 Sep 2023 22:59:28 +0800 Subject: [PATCH 43/54] fix: remove comment --- packages/accounts-controller/src/AccountsController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 43013d72d0a..40e8fd25780 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -366,7 +366,6 @@ export class AccountsController extends BaseControllerV2< * @returns A promise that resolves to an array of InternalAccount objects. */ async #listSnapAccounts(): Promise { - // const [snapKeyring] = this.getKeyringByType(SnapKeyring.type); const [snapKeyring] = await this.messagingSystem.call( 'KeyringController:getKeyringsByType', SnapKeyring.type, From c5136a41c23132df3e8c1471ae02c8cc88c1634c Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Tue, 19 Sep 2023 23:01:44 +0800 Subject: [PATCH 44/54] chore: bump dep --- packages/accounts-controller/package.json | 4 +- .../src/AccountsController.ts | 3 +- yarn.lock | 57 +++++++------------ 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 4b1b99a5b19..a2c921f98d9 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -29,8 +29,8 @@ }, "dependencies": { "@metamask/base-controller": "^3.2.1", - "@metamask/eth-snap-keyring": "^0.2.0", - "@metamask/keyring-api": "^0.2.4", + "@metamask/eth-snap-keyring": "^0.2.2", + "@metamask/keyring-api": "^0.2.5", "@metamask/keyring-controller": "^7.4.0", "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 40e8fd25780..36fcc6c8d2e 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -375,7 +375,7 @@ export class AccountsController extends BaseControllerV2< return []; } - const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(false); + const snapAccounts = await (snapKeyring as SnapKeyring).listAccounts(); return snapAccounts; } @@ -409,7 +409,6 @@ export class AccountsController extends BaseControllerV2< 'personal_sign', 'eth_sign', 'eth_signTransaction', - 'eth_signTypedData', 'eth_signTypedData_v1', 'eth_signTypedData_v3', 'eth_signTypedData_v4', diff --git a/yarn.lock b/yarn.lock index 4027eb0673a..0de6b0674ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1293,8 +1293,8 @@ __metadata: dependencies: "@metamask/auto-changelog": ^3.1.0 "@metamask/base-controller": ^3.2.1 - "@metamask/eth-snap-keyring": ^0.2.0 - "@metamask/keyring-api": ^0.2.4 + "@metamask/eth-snap-keyring": ^0.2.2 + "@metamask/keyring-api": ^0.2.5 "@metamask/keyring-controller": ^7.4.0 "@metamask/snaps-controllers": ^1.0.1 "@metamask/snaps-utils": ^1.0.1 @@ -1731,7 +1731,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2, @metamask/eth-sig-util@npm:^5.1.0": +"@metamask/eth-sig-util@npm:^5.0.1, @metamask/eth-sig-util@npm:^5.0.2": version: 5.1.0 resolution: "@metamask/eth-sig-util@npm:5.1.0" dependencies: @@ -1786,19 +1786,19 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-snap-keyring@npm:^0.2.0": - version: 0.2.0 - resolution: "@metamask/eth-snap-keyring@npm:0.2.0" +"@metamask/eth-snap-keyring@npm:^0.2.2": + version: 0.2.2 + resolution: "@metamask/eth-snap-keyring@npm:0.2.2" dependencies: - "@ethereumjs/tx": ^4.1.2 - "@metamask/eth-sig-util": ^5.1.0 - "@metamask/keyring-api": ^0.2.2 + "@ethereumjs/tx": ^4.2.0 + "@metamask/eth-sig-util": ^7.0.0 + "@metamask/keyring-api": ^0.2.3 "@metamask/snaps-controllers": ^0.38.2-flask.1 "@metamask/utils": ^8.1.0 "@types/uuid": ^9.0.1 superstruct: ^1.0.3 uuid: ^9.0.0 - checksum: 61383c837a114601a53bc16aa0b4d4db81335c741a890a09fcb68259fccfb3e330c20ef6f5e2c489502eb7350076a5669d313f940862b8f0c985e704ba34dc53 + checksum: 3a3a3f68a04191ae3961eeb4ca1f7062e592a255a6f1db4a0a63b4f07f9b4ec2c62fe1278b0ea365044cda0748c95402c6c5604e149853b730c79943c835bc06 languageName: node linkType: hard @@ -1872,27 +1872,11 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:^0.2.2": - version: 0.2.2 - resolution: "@metamask/keyring-api@npm:0.2.2" - dependencies: - "@metamask/providers": ^11.0.0 - "@metamask/rpc-methods": ^0.38.1-flask.1 - "@metamask/snaps-controllers": ^0.38.2-flask.1 - "@metamask/snaps-utils": ^0.38.2-flask.1 - "@metamask/utils": ^8.0.0 - "@types/uuid": ^9.0.1 - superstruct: ^1.0.3 - uuid: ^9.0.0 - checksum: eefa5408c04e203db4d3a711e0b621193fd5d15c31e155331a0af8da9d150629074ca847aeb55654f39c104eff01060452d7d3ac43b5122f8b565b249581a8df - languageName: node - linkType: hard - -"@metamask/keyring-api@npm:^0.2.4": - version: 0.2.4 - resolution: "@metamask/keyring-api@npm:0.2.4" +"@metamask/keyring-api@npm:^0.2.3, @metamask/keyring-api@npm:^0.2.5": + version: 0.2.5 + resolution: "@metamask/keyring-api@npm:0.2.5" dependencies: - "@metamask/providers": ^12.0.0 + "@metamask/providers": ^13.0.0 "@metamask/rpc-methods": ^0.38.1-flask.1 "@metamask/snaps-controllers": ^0.38.2-flask.1 "@metamask/snaps-utils": ^0.38.2-flask.1 @@ -1900,7 +1884,7 @@ __metadata: "@types/uuid": ^9.0.1 superstruct: ^1.0.3 uuid: ^9.0.0 - checksum: b557321bf85b1d264467cbddf3551942dee882fb544706537fa9dc544ec7f0e6900fe75b5280af0637d47014073ebfe7b9ab68f8e99ce8b31bdd8e3859c883d2 + checksum: ca567388f5963f7c6adaf9d7bfdfa783f5869ab85d906181cc32d2c3c1328af78c15eea661522d1c5dfb7b23b7dd3c6a9917b4c9eae794d187e03483ba42cd3e languageName: node linkType: hard @@ -2185,7 +2169,7 @@ __metadata: languageName: node linkType: hard -"@metamask/providers@npm:^11.0.0, @metamask/providers@npm:^11.1.1": +"@metamask/providers@npm:^11.1.1": version: 11.1.1 resolution: "@metamask/providers@npm:11.1.1" dependencies: @@ -2204,9 +2188,9 @@ __metadata: languageName: node linkType: hard -"@metamask/providers@npm:^12.0.0": - version: 12.0.0 - resolution: "@metamask/providers@npm:12.0.0" +"@metamask/providers@npm:^13.0.0": + version: 13.0.0 + resolution: "@metamask/providers@npm:13.0.0" dependencies: "@metamask/json-rpc-engine": ^7.1.1 "@metamask/object-multiplex": ^1.1.0 @@ -2218,9 +2202,8 @@ __metadata: fast-deep-equal: ^3.1.3 is-stream: ^2.0.0 json-rpc-middleware-stream: ^4.2.1 - pump: ^3.0.0 webextension-polyfill: ^0.10.0 - checksum: 4e466322b3e020607ddb16599555bacc1c4fe8bfe2f781e123c012019f79bbc8206207adc3150d04fb7e36fbd959abf5fc54a6edd072fbed4f4edcabf7dd1ce2 + checksum: b41748cf179794bf7a68d3028e84234e876498611221ba40846ed0859b4470a806c4cf99ff9fcdc60fde1af2f0d999281e6edbdd42113223d8f7018a009ff0ab languageName: node linkType: hard From d6a4f887395989653f9c00d2a0ede3726f9da2d9 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Wed, 20 Sep 2023 13:45:22 +0800 Subject: [PATCH 45/54] fix: rename legacyAccounts to normalAccounts --- .../src/AccountsController.test.ts | 2 +- .../accounts-controller/src/AccountsController.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 7a87094ba8c..c09d98e3e1a 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -741,7 +741,7 @@ describe('AccountsController', () => { }, }; - it('should update accounts with legacy accounts', async () => { + it('should update accounts with normal accounts', async () => { mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); const messenger = buildMessenger(); messenger.registerActionHandler( diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 36fcc6c8d2e..64c16e98f47 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -285,18 +285,18 @@ export class AccountsController extends BaseControllerV2< } /** - * Updates the internal accounts list by retrieving legacy and snap accounts, + * Updates the internal accounts list by retrieving normal and snap accounts, * removing duplicates, and updating the metadata of each account. * * @returns A Promise that resolves when the accounts have been updated. */ async updateAccounts(): Promise { - let legacyAccounts = await this.#listLegacyAccounts(); + let normalAccounts = await this.#listNormalAccounts(); let snapAccounts: InternalAccount[] = []; if (this.keyringApiEnabled) { snapAccounts = await this.#listSnapAccounts(); // remove duplicate accounts that are retrieved from the snap keyring. - legacyAccounts = legacyAccounts.filter( + normalAccounts = normalAccounts.filter( (account) => !snapAccounts.find( (snapAccount) => snapAccount.address === account.address, @@ -309,7 +309,7 @@ export class AccountsController extends BaseControllerV2< const previousAccounts = this.state.internalAccounts.accounts; const accounts: Record = [ - ...legacyAccounts, + ...normalAccounts, ...snapAccounts, ].reduce((internalAccountMap, internalAccount) => { const keyringTypeName = keyringTypeToName( @@ -381,13 +381,13 @@ export class AccountsController extends BaseControllerV2< } /** - * Returns a list of legacy accounts. - * Note: listLegacyAccounts is a temporary method until the keyrings all implement the InternalAccount interface. + * Returns a list of normal accounts. + * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface. * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead. * * @returns A Promise that resolves to an array of InternalAccount objects. */ - async #listLegacyAccounts(): Promise { + async #listNormalAccounts(): Promise { const addresses = await this.messagingSystem.call( 'KeyringController:getAccounts', ); From ba6f5fe1cf9ca0b7eb35f44ceaa5961d98840b6c Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 15:43:15 +0800 Subject: [PATCH 46/54] fix: bump coverage to 100 --- .../src/AccountsController.test.ts | 80 +++++++++++++++++++ .../src/AccountsController.ts | 11 +-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index c09d98e3e1a..83c417e5985 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -683,6 +683,86 @@ describe('AccountsController', () => { setLastSelectedAsAny(mockAccount), ); }); + + it('should delete the account and select the account with the most recent lastSelected', async () => { + const messenger = buildMessenger(); + mockUUID.mockReturnValueOnce('mock-id').mockReturnValueOnce('mock-id2'); + messenger.registerActionHandler( + 'KeyringController:getAccounts', + mockGetAccounts.mockResolvedValue([ + mockAccount.address, + mockAccount2.address, + ]), + ); + + messenger.registerActionHandler( + 'KeyringController:getKeyringForAccount', + mockGetKeyringForAccount.mockResolvedValue({ type: 'HD Key Tree' }), + ); + + const mockAccountWithoutLastSelected = { + ...mockAccount, + metadata: { + ...mockAccount.metadata, + lastSelected: undefined, + }, + }; + + const mockAccount2WithoutLastSelected = { + ...mockAccount2, + metadata: { + ...mockAccount2.metadata, + lastSelected: undefined, + }, + }; + + const mockNewKeyringState = { + isUnlocked: true, + keyrings: [ + { + type: 'HD Key Tree', + accounts: [ + mockAccountWithoutLastSelected.address, + mockAccount2.address, + ], + }, + ], + }; + const accountsController = setupAccountsController({ + initialState: { + internalAccounts: { + accounts: { + 'missing-account': { + address: '0x999', + } as InternalAccount, + [mockAccount2.id]: mockAccount2WithoutLastSelected, + [mockAccount.id]: mockAccountWithoutLastSelected, + }, + selectedAccount: 'missing-account', + }, + }, + keyringApiEnabled: false, + messenger, + }); + + messenger.publish( + 'KeyringController:stateChange', + mockNewKeyringState, + [], + ); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + const accounts = accountsController.listAccounts(); + + expect(accounts).toStrictEqual([ + setLastSelectedAsAny(mockAccountWithoutLastSelected), + mockAccount2WithoutLastSelected, + ]); + expect(accountsController.getSelectedAccount()).toStrictEqual( + setLastSelectedAsAny(mockAccountWithoutLastSelected), + ); + }); }); describe('constructor', () => { diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 64c16e98f47..c579d094a5b 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -167,9 +167,10 @@ export class AccountsController extends BaseControllerV2< // if somehow the selected account becomes lost then select the first account if ( this.state.internalAccounts.selectedAccount !== '' && - !this.getAccount(this.state.internalAccounts.selectedAccount) + !this.getAccount(this.state.internalAccounts.selectedAccount) && + this.listAccounts().length > 0 ) { - this.setSelectedAccount(this.listAccounts()[0]?.id); + this.setSelectedAccount(this.listAccounts()[0].id); } } @@ -439,8 +440,8 @@ export class AccountsController extends BaseControllerV2< .sort((accountA, accountB) => { // sort by lastSelected descending return ( - (accountB.metadata?.lastSelected ?? 0) - - (accountA.metadata?.lastSelected ?? 0) + (accountB.metadata.lastSelected ?? 0) - + (accountA.metadata.lastSelected ?? 0) ); })[0]; @@ -510,7 +511,7 @@ export class AccountsController extends BaseControllerV2< accounts.forEach((account) => { const currentAccount = currentState.internalAccounts.accounts[account.id]; - if (currentAccount?.metadata?.snap) { + if (currentAccount.metadata.snap) { currentAccount.metadata.snap.enabled = snaps[currentAccount.metadata.snap.id].enabled && !snaps[currentAccount.metadata.snap.id].blocked; From 2c2fa52656e82694c8b4e48440e718cb8d3fd86d Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:06:35 +0800 Subject: [PATCH 47/54] fix: bump jest coverage requirements, and update dependencies --- packages/accounts-controller/jest.config.js | 2 +- packages/accounts-controller/package.json | 11 +++-------- yarn.lock | 4 ---- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/accounts-controller/jest.config.js b/packages/accounts-controller/jest.config.js index 75290af4e75..ca084133399 100644 --- a/packages/accounts-controller/jest.config.js +++ b/packages/accounts-controller/jest.config.js @@ -17,7 +17,7 @@ module.exports = merge(baseConfig, { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 92.04, + branches: 100, functions: 100, lines: 100, statements: 100, diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index a2c921f98d9..654a447b42c 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -1,6 +1,6 @@ { "name": "@metamask/accounts-controller", - "version": "1.0.0", + "version": "0.0.0", "description": "Manages internal accounts", "keywords": [ "MetaMask", @@ -31,8 +31,6 @@ "@metamask/base-controller": "^3.2.1", "@metamask/eth-snap-keyring": "^0.2.2", "@metamask/keyring-api": "^0.2.5", - "@metamask/keyring-controller": "^7.4.0", - "@metamask/snaps-controllers": "^1.0.1", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", "eth-rpc-errors": "^4.0.2", @@ -43,19 +41,16 @@ }, "devDependencies": { "@metamask/auto-changelog": "^3.1.0", + "@metamask/keyring-controller": "^7.4.0", + "@metamask/snaps-controllers": "^1.0.1", "@types/jest": "^27.4.1", "@types/readable-stream": "^2.3.0", - "deepmerge": "^4.2.2", "jest": "^27.5.1", - "sinon": "^9.2.4", "ts-jest": "^27.1.4", "typedoc": "^0.22.15", "typedoc-plugin-missing-exports": "^0.22.6", "typescript": "~4.6.3" }, - "peerDependencies": { - "@metamask/keyring-controller": "^7.4.0" - }, "engines": { "node": ">=16.0.0" }, diff --git a/yarn.lock b/yarn.lock index 0de6b0674ef..f55a35634ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1301,20 +1301,16 @@ __metadata: "@metamask/utils": ^6.2.0 "@types/jest": ^27.4.1 "@types/readable-stream": ^2.3.0 - deepmerge: ^4.2.2 eth-rpc-errors: ^4.0.2 ethereumjs-util: ^7.0.10 immer: ^9.0.6 jest: ^27.5.1 nanoid: ^3.1.31 - sinon: ^9.2.4 ts-jest: ^27.1.4 typedoc: ^0.22.15 typedoc-plugin-missing-exports: ^0.22.6 typescript: ~4.6.3 uuid: ^8.3.2 - peerDependencies: - "@metamask/keyring-controller": ^7.4.0 languageName: unknown linkType: soft From bf9b1017d905fa870936d5bb7bb071ccbb786a49 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:12:13 +0800 Subject: [PATCH 48/54] Update packages/accounts-controller/src/AccountsController.ts Co-authored-by: Michele Esposito <34438276+mikesposito@users.noreply.github.com> --- packages/accounts-controller/src/AccountsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index c579d094a5b..302c5e76e22 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -418,7 +418,7 @@ export class AccountsController extends BaseControllerV2< metadata: { name: '', keyring: { - type: (keyring as any).type as string, + type: (keyring as Keyring).type, }, }, }); From aaaa3ae28ab63baa70ec94027efc8f3d97b8b003 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:13:38 +0800 Subject: [PATCH 49/54] fix: remove unused ts-ignore and update legacy to normal --- .../src/AccountsController.test.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/accounts-controller/src/AccountsController.test.ts b/packages/accounts-controller/src/AccountsController.test.ts index 83c417e5985..b070a7ec550 100644 --- a/packages/accounts-controller/src/AccountsController.test.ts +++ b/packages/accounts-controller/src/AccountsController.test.ts @@ -35,8 +35,6 @@ const mockAccount: InternalAccount = { metadata: { name: 'Account 1', keyring: { type: 'HD Key Tree' }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore lastSelected: 1691565967656, }, }; @@ -50,23 +48,21 @@ const mockAccount2: InternalAccount = { metadata: { name: 'Account 2', keyring: { type: 'HD Key Tree' }, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore lastSelected: 1955565967656, }, }; /** - * Creates an `InternalAccount` object from the given legacy account properties. + * Creates an `InternalAccount` object from the given normal account properties. * - * @param props - The properties of the legacy account. + * @param props - The properties of the normal account. * @param props.id - The ID of the account. * @param props.name - The name of the account. * @param props.address - The address of the account. * @param props.keyringType - The type of the keyring associated with the account. * @param props.snapId - The id of the snap. * @param props.snapEnabled - The status of the snap - * @returns The `InternalAccount` object created from the legacy account properties. + * @returns The `InternalAccount` object created from the normal account properties. */ function createExpectedInternalAccount({ id, @@ -945,7 +941,7 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); - it('should filter snap accounts from legacyAccounts', async () => { + it('should filter snap accounts from normalAccounts', async () => { mockUUID.mockReturnValueOnce('mock-id'); const messenger = buildMessenger(); messenger.registerActionHandler( @@ -958,7 +954,7 @@ describe('AccountsController', () => { ]), ); - // first account will be legacy, second will be a snap account + // first account will be normal, second will be a snap account messenger.registerActionHandler( 'KeyringController:getAccounts', mockGetAccounts.mockResolvedValue([mockAddress1, '0x1234']), @@ -1001,7 +997,7 @@ describe('AccountsController', () => { expect(accountsController.listAccounts()).toStrictEqual(expectedAccounts); }); - it('should filter snap accounts from legacyAccounts even if the snap account is listed before legacy accounts', async () => { + it('should filter snap accounts from normalAccounts even if the snap account is listed before normal accounts', async () => { mockUUID.mockReturnValue('mock-id'); const messenger = buildMessenger(); messenger.registerActionHandler( @@ -1014,7 +1010,7 @@ describe('AccountsController', () => { ]), ); - // first account will be legacy, second will be a snap account + // first account will be normal, second will be a snap account messenger.registerActionHandler( 'KeyringController:getAccounts', mockGetAccounts.mockResolvedValue(['0x1234', mockAddress1]), From 834facc9061ba02c52b3174a27ab836c7fa7202b Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:20:04 +0800 Subject: [PATCH 50/54] fix: add @metamask/util dep --- packages/accounts-controller/package.json | 1 + packages/accounts-controller/src/AccountsController.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 654a447b42c..6dfe5874d4e 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -43,6 +43,7 @@ "@metamask/auto-changelog": "^3.1.0", "@metamask/keyring-controller": "^7.4.0", "@metamask/snaps-controllers": "^1.0.1", + "@metamask/utils": "^6.2.0", "@types/jest": "^27.4.1", "@types/readable-stream": "^2.3.0", "jest": "^27.5.1", diff --git a/packages/accounts-controller/src/AccountsController.ts b/packages/accounts-controller/src/AccountsController.ts index 302c5e76e22..2307d0a4362 100644 --- a/packages/accounts-controller/src/AccountsController.ts +++ b/packages/accounts-controller/src/AccountsController.ts @@ -14,6 +14,7 @@ import type { SnapControllerEvents, SnapControllerState, } from '@metamask/snaps-controllers'; +import type { Keyring, Json } from '@metamask/utils'; import { sha256FromString } from 'ethereumjs-util'; import type { Patch } from 'immer'; import { v4 as uuid } from 'uuid'; From 0414e456ea3740d4634504d93b2eca3fbf20288f Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:22:26 +0800 Subject: [PATCH 51/54] fix: readd deepmerge --- packages/accounts-controller/package.json | 2 +- yarn.lock | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index 6dfe5874d4e..fdae57c7ea1 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -33,6 +33,7 @@ "@metamask/keyring-api": "^0.2.5", "@metamask/snaps-utils": "^1.0.1", "@metamask/utils": "^6.2.0", + "deepmerge": "^4.2.2", "eth-rpc-errors": "^4.0.2", "ethereumjs-util": "^7.0.10", "immer": "^9.0.6", @@ -43,7 +44,6 @@ "@metamask/auto-changelog": "^3.1.0", "@metamask/keyring-controller": "^7.4.0", "@metamask/snaps-controllers": "^1.0.1", - "@metamask/utils": "^6.2.0", "@types/jest": "^27.4.1", "@types/readable-stream": "^2.3.0", "jest": "^27.5.1", diff --git a/yarn.lock b/yarn.lock index f55a35634ab..55b5dbc2ba9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1301,6 +1301,7 @@ __metadata: "@metamask/utils": ^6.2.0 "@types/jest": ^27.4.1 "@types/readable-stream": ^2.3.0 + deepmerge: ^4.3.1 eth-rpc-errors: ^4.0.2 ethereumjs-util: ^7.0.10 immer: ^9.0.6 @@ -4550,7 +4551,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2": +"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 From 8c33befa5969099d620d643e315d5b147f8da000 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:23:58 +0800 Subject: [PATCH 52/54] chore: update yarn.lock --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 55b5dbc2ba9..9fce93ebaf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1301,7 +1301,7 @@ __metadata: "@metamask/utils": ^6.2.0 "@types/jest": ^27.4.1 "@types/readable-stream": ^2.3.0 - deepmerge: ^4.3.1 + deepmerge: ^4.2.2 eth-rpc-errors: ^4.0.2 ethereumjs-util: ^7.0.10 immer: ^9.0.6 @@ -4551,7 +4551,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1": +"deepmerge@npm:^4.2.2": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 From d5e3b8e195f043553b171dcdf8b712d993608a45 Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 17:38:30 +0800 Subject: [PATCH 53/54] fix: roll back peerDep removal --- packages/accounts-controller/package.json | 3 +++ yarn.lock | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/accounts-controller/package.json b/packages/accounts-controller/package.json index fdae57c7ea1..b94e387c4d3 100644 --- a/packages/accounts-controller/package.json +++ b/packages/accounts-controller/package.json @@ -52,6 +52,9 @@ "typedoc-plugin-missing-exports": "^0.22.6", "typescript": "~4.6.3" }, + "peerDependencies": { + "@metamask/keyring-controller": "^7.4.0" + }, "engines": { "node": ">=16.0.0" }, diff --git a/yarn.lock b/yarn.lock index 9fce93ebaf2..04fe5d55079 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1312,6 +1312,8 @@ __metadata: typedoc-plugin-missing-exports: ^0.22.6 typescript: ~4.6.3 uuid: ^8.3.2 + peerDependencies: + "@metamask/keyring-controller": ^7.4.0 languageName: unknown linkType: soft From 27ae5f33b1cf2d44a78c9c0100bc1f529d14086a Mon Sep 17 00:00:00 2001 From: Monte Lai Date: Thu, 21 Sep 2023 18:29:09 +0800 Subject: [PATCH 54/54] fix: remove controller-utils from tsconfig --- packages/accounts-controller/tsconfig.build.json | 3 --- packages/accounts-controller/tsconfig.json | 3 --- 2 files changed, 6 deletions(-) diff --git a/packages/accounts-controller/tsconfig.build.json b/packages/accounts-controller/tsconfig.build.json index 9ea6fa18ca8..a206c483c3f 100644 --- a/packages/accounts-controller/tsconfig.build.json +++ b/packages/accounts-controller/tsconfig.build.json @@ -9,9 +9,6 @@ { "path": "../base-controller/tsconfig.build.json" }, - { - "path": "../controller-utils/tsconfig.build.json" - }, { "path": "../keyring-controller/tsconfig.build.json" } ], "include": ["../../types", "./src"] diff --git a/packages/accounts-controller/tsconfig.json b/packages/accounts-controller/tsconfig.json index da0e84978a4..7263c934b6b 100644 --- a/packages/accounts-controller/tsconfig.json +++ b/packages/accounts-controller/tsconfig.json @@ -7,9 +7,6 @@ { "path": "../base-controller" }, - { - "path": "../controller-utils" - }, { "path": "../keyring-controller" }