From 466f57c08115056a9663e3e73b984147f8b3770d Mon Sep 17 00:00:00 2001 From: Owen Craston Date: Wed, 3 Jul 2024 16:33:31 -0700 Subject: [PATCH] feat: remove `selectIdentities` in favour of `selectInternalAccounts` (#9724) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** 1. What is the reason for the change? In an effort to make MetaMask multi chain we are migrating from an address based accounts to account id based account. In https://github.com/MetaMask/metamask-mobile/pull/8759 previous PR we integrated the accounts controller which stores metadata like the account name, address and supported methods. This pr migrates all the use of `selectIdentities` to use the accounts controller as the source of truth for all accounts information (address, name etc). 2. What is the improvement/solution? - find all use of `selectIdentities` and replace it with `selectInternalAccounts` and/or `selectSelectedInternalAccountChecksummedAddress` - These selectors are already in use in `main` and are defined [here](https://github.com/MetaMask/metamask-mobile/blob/main/app/selectors/accountsController.ts#L11-L32). - tests for these utils can be found [here](https://github.com/MetaMask/metamask-mobile/blob/main/app/selectors/accountsController.test.ts#L86). - Note that ENS resolution in the send flow is not working but has been broken in production for a while. ENS resolution in the accounts list and dapp transactions are working as expected. ## **Related issues** Fixes: https://github.com/MetaMask/accounts-planning/issues/410 ## Q.A/Review check list - [QA builds](https://app.bitrise.io/app/be69d4368ee7e86d/pipelines/a4fca9c3-11d5-4654-80ce-4cdedd638fe6?tab=artifacts) - [ ] Verify that there is no use of `selectIdentities` in the app - [ ] Verify that no functions are expecting `identities` as params ## **Manual testing steps** #### Fresh install 1. Launch wallet 2. create a new account 4. once on the wallet view, add a new account 5. create a custom name for your account(s) 6. swipe away the app and re open 7. login 8. the selected account should be same one that was previously selected 9. all addresses should be in [checksum format](https://coincodex.com/article/2078/ethereum-address-checksum-explained/) 10. connect the portfolio dapp via the portfolio button on the home screen. 11. Click connect on the portfolio dapp 12. The first address in the address picker should be match the selected address on the wallet view and be in checksum format #### Upgrade path 1. Use an existing wallet that has multiple accounts created/imported 2. Change the names of your accounts so that they have custom names 3. "upgrade" your wallet to this branch 4. the selected account should be the same as it was on the previous branch 5. all of the account data should remain the same (names and balances) #### Send flow 1. Start a send transaction 2. Verify that the account names and addresses are consistent in the relevant screens of the flow #### Hardware wallet connection and confirmation 1. Connect a hardware wallet device 2. Go to the MetaMask test dapp and try to connect an account 3. Verify that the account names and addresses are consistent ## **Screenshots/Recordings** ### **After** #### Basic usage https://github.com/MetaMask/metamask-mobile/assets/22918444/c5df59e1-05e6-43de-9efd-9a479aa342f7 #### Hardware account name preserved when connecting and confirming with dapps https://github.com/MetaMask/metamask-mobile/assets/22918444/2931b041-7e69-43ff-acb1-c4405e5b526b #### Complex usage - Ledger account connected - Imported account with ens (orangefox.eth) - custom account name - derived account - Tested.... - send flow has the correct names ✅ - wallet list has the correct names, even after a force reload (and correct selected account) ✅ - dapp connection request has the correct names for all accounts including hardware and ENS account ✅ - dapp transactions have correct names for all account types https://github.com/MetaMask/metamask-mobile/assets/22918444/254aeb44-6301-463e-86b6-01a12d56d967 ## **Pre-merge author checklist** - [ ] I’ve followed [MetaMask Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../__snapshots__/index.test.tsx.snap | 2 +- .../AccountFromToInfoCard.test.tsx | 13 - .../AccountFromToInfoCard.tsx | 109 +- .../AccountFromToInfoCard.types.tsx | 9 +- .../__snapshots__/index.test.tsx.snap | 2 +- app/components/UI/AccountInfoCard/index.js | 12 +- app/components/UI/AccountOverview/index.js | 33 +- .../AccountSelector.test.tsx | 25 +- .../AccountSelector.test.tsx.snap | 980 +++++++++--------- .../AddToAddressBookWrapper.test.tsx | 33 +- app/components/UI/AddressCopy/AddressCopy.tsx | 28 +- app/components/UI/AddressInputs/index.js | 4 +- .../UI/AddressInputs/index.test.jsx | 11 - app/components/UI/AssetIcon/index.test.tsx | 14 - app/components/UI/DrawerView/index.js | 68 +- app/components/UI/DrawerView/index.test.tsx | 14 +- app/components/UI/EthereumAddress/index.js | 2 +- .../UI/NavbarBrowserTitle/index.test.tsx | 6 - .../__snapshots__/OrderDetails.test.tsx.snap | 9 + .../Ramp/Views/OrdersList/OrdersList.test.tsx | 9 - app/components/UI/Ramp/components/Account.tsx | 22 +- .../UI/SearchTokenAutocomplete/index.test.tsx | 7 - app/components/UI/TransactionElement/index.js | 29 +- .../Views/AccountConnect/AccountConnect.tsx | 30 +- .../AccountPermissions/AccountPermissions.tsx | 20 +- app/components/Views/Asset/index.js | 44 +- .../NetworkSelector/NetworkSelector.test.tsx | 8 - .../Settings/Contacts/ContactForm/index.js | 32 +- .../Views/TransactionsView/index.js | 28 +- app/components/Views/Wallet/index.test.tsx | 18 - .../Views/confirmations/Send/index.js | 25 +- .../Views/confirmations/Send/index.test.tsx | 16 +- .../SendFlow/AddressList/AddressList.test.tsx | 20 +- .../SendFlow/AddressList/AddressList.tsx | 13 +- .../SendFlow/Confirm/index.test.tsx | 3 - .../confirmations/SendFlow/SendTo/index.js | 44 +- .../ApproveTransactionHeader.test.tsx | 13 - .../ApproveTransactionHeader.tsx | 8 +- .../AddNickname/index.tsx | 26 +- .../AddNickname/types.ts | 23 +- .../ApproveTransactionReview/index.js | 4 +- .../SignatureRequest/Root/Root.test.tsx | 9 - .../TransactionReview/index.test.tsx | 6 - .../__snapshots__/index.test.tsx.snap | 4 +- .../hooks/useExistingAddress.test.ts | 45 +- app/components/hooks/useExistingAddress.ts | 51 +- .../approveTransaction.test.ts | 31 +- .../TransactionManager/approveTransaction.ts | 7 +- app/selectors/preferencesController.ts | 5 +- app/util/address/index.test.ts | 25 +- app/util/address/{index.js => index.ts} | 187 ++-- .../trackDappViewedEvent/index.test.ts | 41 +- .../metrics/trackDappViewedEvent/index.ts | 6 +- app/util/transactions/index.js | 16 +- e2e/fixtures/fixture-builder.js | 7 +- yarn.lock | 12 +- 56 files changed, 1141 insertions(+), 1127 deletions(-) rename app/util/address/{index.js => index.ts} (74%) diff --git a/app/components/UI/AccountApproval/__snapshots__/index.test.tsx.snap b/app/components/UI/AccountApproval/__snapshots__/index.test.tsx.snap index 9864f17e99f..c5704972d10 100644 --- a/app/components/UI/AccountApproval/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/AccountApproval/__snapshots__/index.test.tsx.snap @@ -334,7 +334,7 @@ exports[`AccountApproval should render correctly 1`] = ` } } > - 0xC496...a756 + Account 2 { const { - identities, + internalAccounts, chainId, onPressFromAddressIcon, ticker, @@ -33,7 +35,6 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => { const { transaction: { from: rawFromAddress, data, to }, transactionTo, - transactionToName, transactionFromName, selectedAsset, ensRecipient, @@ -56,50 +57,88 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => { ); useEffect(() => { - if (!fromAddress) { - return; - } - if (transactionFromName) { - setFromAccountName(transactionFromName); - return; - } - (async () => { + const fetchFromAccountDetails = async () => { + if (!fromAddress) { + return; + } + + if (transactionFromName) { + if (fromAccountName !== transactionFromName) { + setFromAccountName(transactionFromName); + } + return; + } + const fromEns = await doENSReverseLookup(fromAddress, chainId); if (fromEns) { - setFromAccountName(fromEns); + if (fromAccountName !== fromEns) { + setFromAccountName(fromEns); + } } else { - const { name: fromName } = identities[fromAddress]; - setFromAccountName(fromName); + const accountWithMatchingFromAddress = internalAccounts.find( + (account) => toLowerCaseEquals(account.address, fromAddress), + ); + + const newName = accountWithMatchingFromAddress + ? accountWithMatchingFromAddress.metadata.name + : fromAddress; + + if (fromAccountName !== newName) { + setFromAccountName(newName); + } } - })(); - }, [fromAddress, identities, transactionFromName, chainId]); + }; + + fetchFromAccountDetails(); + }, [ + fromAddress, + transactionFromName, + chainId, + internalAccounts, + fromAccountName, + ]); useEffect(() => { - if (existingToAddress) { - setToAccountName(existingToAddress?.name); - return; - } - (async () => { + const fetchAccountDetails = async () => { + if (existingToAddress) { + if (toAccountName !== existingToAddress.name) { + setToAccountName(existingToAddress.name); + } + return; + } + const toEns = await doENSReverseLookup(toAddress, chainId); if (toEns) { - setToAccountName(toEns); - } else if (identities[toAddress]) { - const { name: toName } = identities[toAddress]; - setToAccountName(toName); + if (toAccountName !== toEns) { + setToAccountName(toEns); + } + } else { + const accountWithMatchingToAddress = internalAccounts.find((account) => + toLowerCaseEquals(account.address, toAddress), + ); + + const newName = accountWithMatchingToAddress + ? accountWithMatchingToAddress.metadata.name + : toAddress; + + if (toAccountName !== newName) { + setToAccountName(newName); + } } - })(); - }, [existingToAddress, identities, chainId, toAddress, transactionToName]); + }; + + fetchAccountDetails(); + }, [existingToAddress, chainId, toAddress, internalAccounts, toAccountName]); useEffect(() => { - const accountNames = - (identities && - Object.keys(identities).map((hash) => identities[hash].name)) || - []; + const accountNames = internalAccounts.map( + (account) => account.metadata.name, + ); const isOwnAccount = ensRecipient && accountNames.includes(ensRecipient); if (ensRecipient && !isOwnAccount) { setConfusableCollection(collectConfusables(ensRecipient)); } - }, [identities, ensRecipient]); + }, [internalAccounts, ensRecipient]); useEffect(() => { let toAddr; @@ -172,10 +211,8 @@ const AccountFromToInfoCard = (props: AccountFromToInfoCardProps) => { ); }; -// TODO: Replace "any" with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapStateToProps = (state: any) => ({ - identities: selectIdentities(state), +const mapStateToProps = (state: RootState) => ({ + internalAccounts: selectInternalAccounts(state), chainId: selectChainId(state), ticker: selectTicker(state), }); diff --git a/app/components/UI/AccountFromToInfoCard/AccountFromToInfoCard.types.tsx b/app/components/UI/AccountFromToInfoCard/AccountFromToInfoCard.types.tsx index ece567c7bdd..84690a6c732 100644 --- a/app/components/UI/AccountFromToInfoCard/AccountFromToInfoCard.types.tsx +++ b/app/components/UI/AccountFromToInfoCard/AccountFromToInfoCard.types.tsx @@ -1,9 +1,4 @@ -interface Identity { - address: string; - name: string; -} - -type Identities = Record; +import { InternalAccount } from '@metamask/keyring-api'; interface SelectedAsset { isETH: boolean; @@ -26,7 +21,7 @@ export interface Transaction { } export interface AccountFromToInfoCardProps { - identities: Identities; + internalAccounts: InternalAccount[]; chainId: string; onPressFromAddressIcon?: () => void; ticker?: string; diff --git a/app/components/UI/AccountInfoCard/__snapshots__/index.test.tsx.snap b/app/components/UI/AccountInfoCard/__snapshots__/index.test.tsx.snap index b8b3d0ae551..6a4a79370c7 100644 --- a/app/components/UI/AccountInfoCard/__snapshots__/index.test.tsx.snap +++ b/app/components/UI/AccountInfoCard/__snapshots__/index.test.tsx.snap @@ -232,7 +232,7 @@ exports[`AccountInfoCard should match snapshot 1`] = ` } } > - 0xC495...D272 + Account 1 StyleSheet.create({ @@ -111,9 +111,9 @@ class AccountInfoCard extends PureComponent { */ accounts: PropTypes.object, /** - * List of accounts from the PreferencesController + * List of accounts from the AccountsController */ - identities: PropTypes.object, + internalAccounts: PropTypes.array, /** * A number that specifies the ETH/USD conversion rate */ @@ -142,7 +142,7 @@ class AccountInfoCard extends PureComponent { render() { const { accounts, - identities, + internalAccounts, conversionRate, currentCurrency, operation, @@ -162,7 +162,7 @@ class AccountInfoCard extends PureComponent { ? hexToBN(accounts[fromAddress].balance) : 0; const balance = `${renderFromWei(weiBalance)} ${getTicker(ticker)}`; - const accountLabel = renderAccountName(fromAddress, identities); + const accountLabel = renderAccountName(fromAddress, internalAccounts); const address = renderShortAddress(fromAddress); const dollarBalance = showFiatBalance ? weiToFiat(weiBalance, conversionRate, currentCurrency, 2)?.toUpperCase() @@ -248,7 +248,7 @@ class AccountInfoCard extends PureComponent { const mapStateToProps = (state) => ({ accounts: selectAccounts(state), - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), conversionRate: selectConversionRate(state), currentCurrency: selectCurrentCurrency(state), ticker: selectTicker(state), diff --git a/app/components/UI/AccountOverview/index.js b/app/components/UI/AccountOverview/index.js index c65706e35a0..db383ab313f 100644 --- a/app/components/UI/AccountOverview/index.js +++ b/app/components/UI/AccountOverview/index.js @@ -41,14 +41,17 @@ import AppConstants from '../../../core/AppConstants'; import Engine from '../../../core/Engine'; import { selectChainId } from '../../../selectors/networkController'; import { selectCurrentCurrency } from '../../../selectors/currencyRateController'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { + selectInternalAccounts, + selectSelectedInternalAccountChecksummedAddress, +} from '../../../selectors/accountsController'; import { createAccountSelectorNavDetails } from '../../Views/AccountSelector'; import Text, { TextVariant, } from '../../../component-library/components/Texts/Text'; import { withMetricsAwareness } from '../../../components/hooks/useMetrics'; import { isPortfolioUrl } from '../../../util/url'; +import { toLowerCaseEquals } from '../../../util/general'; const createStyles = (colors) => StyleSheet.create({ @@ -157,9 +160,9 @@ class AccountOverview extends PureComponent { */ selectedAddress: PropTypes.string, /** - /* Identities object required to get account name + /* InternalAccounts object required to get account name */ - identities: PropTypes.object, + internalAccounts: PropTypes.object, /** * Object that represents the selected account */ @@ -234,8 +237,8 @@ class AccountOverview extends PureComponent { input = React.createRef(); componentDidMount = () => { - const { identities, selectedAddress, onRef } = this.props; - const accountLabel = renderAccountName(selectedAddress, identities); + const { internalAccounts, selectedAddress, onRef } = this.props; + const accountLabel = renderAccountName(selectedAddress, internalAccounts); this.setState({ accountLabel }); onRef && onRef(this); InteractionManager.runAfterInteractions(() => { @@ -259,16 +262,18 @@ class AccountOverview extends PureComponent { } setAccountLabel = () => { - const { selectedAddress, identities } = this.props; + const { selectedAddress, internalAccounts } = this.props; const { accountLabel } = this.state; - const lastAccountLabel = identities[selectedAddress].name; + const accountWithMatchingToAddress = internalAccounts.find((account) => + toLowerCaseEquals(account.address, selectedAddress), + ); Engine.setAccountLabel( selectedAddress, this.isAccountLabelDefined(accountLabel) ? accountLabel - : lastAccountLabel, + : accountWithMatchingToAddress.metadata.name, ); this.setState({ accountLabelEditable: false }); }; @@ -278,8 +283,8 @@ class AccountOverview extends PureComponent { }; setAccountLabelEditable = () => { - const { identities, selectedAddress } = this.props; - const accountLabel = renderAccountName(selectedAddress, identities); + const { internalAccounts, selectedAddress } = this.props; + const accountLabel = renderAccountName(selectedAddress, internalAccounts); this.setState({ accountLabelEditable: true, accountLabel }); setTimeout(() => { this.input && this.input.current && this.input.current.focus(); @@ -287,8 +292,8 @@ class AccountOverview extends PureComponent { }; cancelAccountLabelEdition = () => { - const { identities, selectedAddress } = this.props; - const accountLabel = renderAccountName(selectedAddress, identities); + const { internalAccounts, selectedAddress } = this.props; + const accountLabel = renderAccountName(selectedAddress, internalAccounts); this.setState({ accountLabelEditable: false, accountLabel }); }; @@ -461,7 +466,7 @@ class AccountOverview extends PureComponent { const mapStateToProps = (state) => ({ selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), currentCurrency: selectCurrentCurrency(state), chainId: selectChainId(state), browserTabs: state.browser.tabs, diff --git a/app/components/UI/AccountSelectorList/AccountSelector.test.tsx b/app/components/UI/AccountSelectorList/AccountSelector.test.tsx index 8961cd92278..b5a679c3bbf 100644 --- a/app/components/UI/AccountSelectorList/AccountSelector.test.tsx +++ b/app/components/UI/AccountSelectorList/AccountSelector.test.tsx @@ -48,16 +48,6 @@ const initialState = { PreferencesController: { isMultiAccountBalancesEnabled: true, selectedAddress: BUSINESS_ACCOUNT, - identities: { - [BUSINESS_ACCOUNT]: { - address: BUSINESS_ACCOUNT, - name: 'Business Account', - }, - [PERSONAL_ACCOUNT]: { - address: PERSONAL_ACCOUNT, - name: 'Personal Account', - }, - }, }, CurrencyRateController: { currentCurrency: 'usd', @@ -120,12 +110,12 @@ describe('AccountSelectorList', () => { onRemoveImportedAccount.mockClear(); }); - it('should render correctly', async () => { + it('renders correctly', async () => { const { toJSON } = renderComponent(initialState); await waitFor(() => expect(toJSON()).toMatchSnapshot()); }); - it('should render all accounts with balances', async () => { + it('renders all accounts with balances', async () => { const { queryByTestId, getAllByTestId, toJSON } = renderComponent(initialState); @@ -186,7 +176,7 @@ describe('AccountSelectorList', () => { }); }); - it('should render all accounts with right acessory', async () => { + it('renders all accounts with right accessory', async () => { const { getAllByTestId, toJSON } = renderComponent( initialState, AccountSelectorListRightAccessoryUseAccounts, @@ -199,4 +189,13 @@ describe('AccountSelectorList', () => { expect(toJSON()).toMatchSnapshot(); }); }); + it('renders correct account names', async () => { + const { getAllByTestId } = renderComponent(initialState); + + await waitFor(() => { + const accountNameItems = getAllByTestId('cellbase-avatar-title'); + expect(within(accountNameItems[0]).getByText('Account 1')).toBeDefined(); + expect(within(accountNameItems[1]).getByText('Account 2')).toBeDefined(); + }); + }); }); diff --git a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap index 8b7c6c08b72..1e4cbf6b45b 100644 --- a/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap +++ b/app/components/UI/AccountSelectorList/__snapshots__/AccountSelector.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`AccountSelectorList should render all accounts but only the balance for selected account 1`] = ` +exports[`AccountSelectorList renders all accounts with balances 1`] = ` - + > + + + + + + + + - - - - - Account 2 - - - 0xd018...78E7 - - - - - - - - -`; - -exports[`AccountSelectorList should render all accounts with balances 1`] = ` - + + + + + + + + + + + Account 2 + + + 0xd018...78E7 + + + + + + $6400.00 +2 ETH + + + + + + + + + +`; + +exports[`AccountSelectorList renders all accounts with right accessory 1`] = ` + - - $3200.00 -1 ETH - + 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1 + + + + - - - - - - - - - $6400.00 -2 ETH - + 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2 @@ -937,7 +1139,7 @@ exports[`AccountSelectorList should render all accounts with balances 1`] = ` `; -exports[`AccountSelectorList should render all accounts with right acessory 1`] = ` +exports[`AccountSelectorList renders correctly 1`] = ` - 0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272 - Account 1 + + $3200.00 +1 ETH + + + + - 0xd018538C87232FF95acbCe4870629b75640a78E7 - Account 2 + + $6400.00 +2 ETH + @@ -1473,7 +1742,7 @@ exports[`AccountSelectorList should render all accounts with right acessory 1`] `; -exports[`AccountSelectorList should render correctly 1`] = ` +exports[`AccountSelectorList should render all accounts but only the balance for selected account 1`] = ` - - - - - - - - + } + /> - - - - - - - - + } + /> - - - - $6400.00 -2 ETH - - - diff --git a/app/components/UI/AddToAddressBookWrapper/AddToAddressBookWrapper.test.tsx b/app/components/UI/AddToAddressBookWrapper/AddToAddressBookWrapper.test.tsx index b2ce68ace60..19d111bea87 100644 --- a/app/components/UI/AddToAddressBookWrapper/AddToAddressBookWrapper.test.tsx +++ b/app/components/UI/AddToAddressBookWrapper/AddToAddressBookWrapper.test.tsx @@ -6,35 +6,32 @@ import AddToAddressBookWrapper from './AddToAddressBookWrapper'; import { AddAddressModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AddAddressModal.selectors'; import renderWithProvider from '../../../util/test/renderWithProvider'; import initialBackgroundState from '../../../util/test/initial-background-state.json'; +import { createMockAccountsControllerState } from '../../../util/test/accountsControllerTestUtils'; + +const MOCK_ADDRESS_1 = '0x0'; +const MOCK_ADDRESS_2 = '0x1'; + +const MOCK_ACCOUNTS_CONTROLLER_STATE = createMockAccountsControllerState([ + MOCK_ADDRESS_1, + MOCK_ADDRESS_2, +]); const initialState = { settings: {}, engine: { backgroundState: { ...initialBackgroundState, - PreferencesController: { - selectedAddress: '0x0', - identities: { - '0x0': { - address: '0x0', - name: 'Account 1', - }, - '0x1': { - address: '0x1', - name: 'Account 2', - }, - }, - }, AddressBookController: { addressBook: { - '0x1': { - '0x1': { - address: '0x1', + [MOCK_ADDRESS_2]: { + [MOCK_ADDRESS_2]: { + address: MOCK_ADDRESS_2, name: 'Account 2', }, }, }, }, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, }, }, }; @@ -66,7 +63,7 @@ describe('AddToAddressBookWrapper', () => { }); it('should not render touchable wrapper if address is already saved', async () => { const { queryByText } = renderWithProvider( - + DUMMY , { state: initialState }, @@ -78,7 +75,7 @@ describe('AddToAddressBookWrapper', () => { }); it('should return null if address is already saved and defaultNull is true', async () => { const { queryByText } = renderWithProvider( - + DUMMY , { state: initialState }, diff --git a/app/components/UI/AddressCopy/AddressCopy.tsx b/app/components/UI/AddressCopy/AddressCopy.tsx index 80af16dcbeb..4aa60e4b880 100644 --- a/app/components/UI/AddressCopy/AddressCopy.tsx +++ b/app/components/UI/AddressCopy/AddressCopy.tsx @@ -26,9 +26,9 @@ import generateTestId from '../../../../wdio/utils/generateTestId'; // Internal dependencies import styleSheet from './AddressCopy.styles'; import { AddressCopyProps } from './AddressCopy.types'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; import { useMetrics } from '../../../components/hooks/useMetrics'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => { const { styles } = useStyles(styleSheet, {}); @@ -49,22 +49,14 @@ const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => { /** * A string that represents the selected address */ - const selectedAddress = useSelector( - selectSelectedInternalAccountChecksummedAddress, - ); - - /** - * An object containing each identity in the format address => account - */ - const identities = useSelector(selectIdentities); - - const account = { - ...identities[selectedAddress], - address: selectedAddress, - }; + const selectedInternalAccount = useSelector(selectSelectedInternalAccount); const copyAccountToClipboard = async () => { - await ClipboardManager.setString(selectedAddress); + if (selectedInternalAccount?.address) { + await ClipboardManager.setString( + toChecksumHexAddress(selectedInternalAccount.address), + ); + } handleShowAlert({ isVisible: true, autodismiss: 1500, @@ -90,7 +82,9 @@ const AddressCopy = ({ formatAddressType = 'full' }: AddressCopyProps) => { variant={TextVariant.BodySM} {...generateTestId(Platform, 'wallet-account-address')} > - {formatAddress(account.address, formatAddressType)} + {selectedInternalAccount + ? formatAddress(selectedInternalAccount.address, formatAddressType) + : null} { - {renderSlightlyLongAddress(toSelectedAddress, 4, 9)} + {toSelectedAddress + ? renderSlightlyLongAddress(toSelectedAddress, 4, 9) + : ''} diff --git a/app/components/UI/AddressInputs/index.test.jsx b/app/components/UI/AddressInputs/index.test.jsx index cc3a6d2bfc5..1cd5add8cd7 100644 --- a/app/components/UI/AddressInputs/index.test.jsx +++ b/app/components/UI/AddressInputs/index.test.jsx @@ -12,17 +12,6 @@ const initialState = { backgroundState: { ...initialBackgroundState, PreferencesController: { - selectedAddress: '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A', - identities: { - '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A': { - address: '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A', - name: 'Account 1', - }, - '0x519d2CE57898513F676a5C3b66496c3C394c9CC7': { - address: '0x519d2CE57898513F676a5C3b66496c3C394c9CC7', - name: 'Account 2', - }, - }, useTokenDetection: false, }, AddressBookController: { diff --git a/app/components/UI/AssetIcon/index.test.tsx b/app/components/UI/AssetIcon/index.test.tsx index fd256ddb41e..7ceedd57a90 100644 --- a/app/components/UI/AssetIcon/index.test.tsx +++ b/app/components/UI/AssetIcon/index.test.tsx @@ -10,13 +10,6 @@ const mockInitialState = { ...initialBackgroundState, PreferencesController: { featureFlags: {}, - identities: { - '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3': { - address: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', - name: 'Account 1', - importTime: 1684232000456, - }, - }, ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', lostIdentities: {}, selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', @@ -34,13 +27,6 @@ const mockInitialState = { _W: { featureFlags: {}, frequentRpcList: [], - identities: { - '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3': { - address: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', - name: 'Account 1', - importTime: 1684232000456, - }, - }, ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', lostIdentities: {}, selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', diff --git a/app/components/UI/DrawerView/index.js b/app/components/UI/DrawerView/index.js index 82963e61ae0..f33822e83f3 100644 --- a/app/components/UI/DrawerView/index.js +++ b/app/components/UI/DrawerView/index.js @@ -81,12 +81,12 @@ import { selectCurrentCurrency } from '../../../selectors/currencyRateController import { selectTokens } from '../../../selectors/tokensController'; import { selectAccounts } from '../../../selectors/accountTrackerController'; import { selectContractBalances } from '../../../selectors/tokenBalancesController'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; import { createAccountSelectorNavDetails } from '../../Views/AccountSelector'; import NetworkInfo from '../NetworkInfo'; import { withMetricsAwareness } from '../../../components/hooks/useMetrics'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; const createStyles = (colors) => StyleSheet.create({ @@ -340,18 +340,14 @@ class DrawerView extends PureComponent { * Object representing the configuration of the current selected network */ providerConfig: PropTypes.object.isRequired, - /** - * Selected address as string - */ - selectedAddress: PropTypes.string, /** * List of accounts from the AccountTrackerController */ accounts: PropTypes.object, /** - * List of accounts from the PreferencesController + * Currently selected account */ - identities: PropTypes.object, + selectedInternalAccount: PropTypes.object, /** /* Selected currency */ @@ -474,16 +470,19 @@ class DrawerView extends PureComponent { previousBalance = null; processedNewBalance = false; animatingNetworksModal = false; + selectedChecksummedAddress = toChecksumHexAddress( + this.props.selectedInternalAccount.address, + ); isCurrentAccountImported() { let ret = false; - const { keyrings, selectedAddress } = this.props; + const { keyrings } = this.props; const allKeyrings = keyrings && keyrings.length ? keyrings : Engine.context.KeyringController.state.keyrings; for (const keyring of allKeyrings) { - if (keyring.accounts.includes(selectedAddress)) { + if (keyring.accounts.includes(this.selectedChecksummedAddress)) { ret = keyring.type !== 'HD Key Tree'; break; } @@ -495,7 +494,7 @@ class DrawerView extends PureComponent { renderTag() { const colors = this.context.colors || mockTheme.colors; const styles = createStyles(colors); - const label = getLabelTextByAddress(this.props.selectedAddress); + const label = getLabelTextByAddress(this.selectedChecksummedAddress); return label ? ( @@ -579,16 +578,16 @@ class DrawerView extends PureComponent { } updateAccountInfo = async () => { - const { identities, providerConfig, selectedAddress } = this.props; + const { providerConfig, selectedInternalAccount } = this.props; const { currentChainId, address, name } = this.state.account; - const accountName = identities[selectedAddress]?.name; + const accountName = selectedInternalAccount.metadata.name; if ( currentChainId !== providerConfig.chainId || - address !== selectedAddress || + address !== this.selectedChecksummedAddress || name !== accountName ) { const ens = await doENSReverseLookup( - selectedAddress, + this.selectedChecksummedAddress, providerConfig.chainId, ); this.setState((state) => ({ @@ -596,7 +595,7 @@ class DrawerView extends PureComponent { ens, name: accountName, currentChainId: providerConfig.chainId, - address: selectedAddress, + address: this.selectedChecksummedAddress, }, })); } @@ -697,18 +696,20 @@ class DrawerView extends PureComponent { }; viewInEtherscan = () => { - const { selectedAddress, providerConfig, networkConfigurations } = - this.props; + const { providerConfig, networkConfigurations } = this.props; if (providerConfig.type === RPC) { const blockExplorer = findBlockExplorerForRpc( providerConfig.rpcUrl, networkConfigurations, ); - const url = `${blockExplorer}/address/${selectedAddress}`; + const url = `${blockExplorer}/address/${this.selectedChecksummedAddress}`; const title = new URL(blockExplorer).hostname; this.goToBrowserUrl(url, title); } else { - const url = getEtherscanAddressUrl(providerConfig.type, selectedAddress); + const url = getEtherscanAddressUrl( + providerConfig.type, + this.selectedChecksummedAddress, + ); const etherscan_url = getEtherscanBaseUrl(providerConfig.type).replace( 'https://', '', @@ -892,8 +893,7 @@ class DrawerView extends PureComponent { }; copyAccountToClipboard = async () => { - const { selectedAddress } = this.props; - await ClipboardManager.setString(selectedAddress); + await ClipboardManager.setString(this.selectedChecksummedAddress); this.toggleReceiveModal(); InteractionManager.runAfterInteractions(() => { this.props.showAlert({ @@ -906,9 +906,8 @@ class DrawerView extends PureComponent { }; onShare = () => { - const { selectedAddress } = this.props; Share.open({ - message: selectedAddress, + message: this.selectedChecksummedAddress, }) .then(() => { this.props.protectWalletModalVisible(); @@ -996,8 +995,7 @@ class DrawerView extends PureComponent { const { providerConfig, accounts, - identities, - selectedAddress, + selectedInternalAccount, currentCurrency, seedphraseBackedUp, currentRoute, @@ -1011,16 +1009,16 @@ class DrawerView extends PureComponent { } = this.state; const account = { - address: selectedAddress, + address: this.selectedChecksummedAddress, name: nameFromState, ens: ensFromState, - ...identities[selectedAddress], - ...accounts[selectedAddress], + ...selectedInternalAccount, + ...accounts[this.selectedChecksummedAddress], }; const { name, ens } = account; account.balance = - (accounts[selectedAddress] && - renderFromWei(accounts[selectedAddress].balance)) || + (accounts[this.selectedChecksummedAddress] && + renderFromWei(accounts[this.selectedChecksummedAddress].balance)) || 0; const fiatBalance = Engine.getTotalFiatAccountBalance(); const totalFiatBalance = fiatBalance.ethFiat + fiatBalance.tokenFiat; @@ -1056,7 +1054,10 @@ class DrawerView extends PureComponent { testID={'navbar-account-identicon'} > - + ({ providerConfig: selectProviderConfig(state), accounts: selectAccounts(state), - selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), - identities: selectIdentities(state), + selectedInternalAccount: selectSelectedInternalAccount(state), networkConfigurations: selectNetworkConfigurations(state), currentCurrency: selectCurrentCurrency(state), keyrings: state.engine.backgroundState.KeyringController.keyrings, diff --git a/app/components/UI/DrawerView/index.test.tsx b/app/components/UI/DrawerView/index.test.tsx index 9d06c981241..f395b99955d 100644 --- a/app/components/UI/DrawerView/index.test.tsx +++ b/app/components/UI/DrawerView/index.test.tsx @@ -4,10 +4,7 @@ import DrawerView from './'; import initialBackgroundState from '../../../util/test/initial-background-state.json'; import Engine from '../../../core/Engine'; -import { - MOCK_ACCOUNTS_CONTROLLER_STATE, - MOCK_ADDRESS_1, -} from '../../../util/test/accountsControllerTestUtils'; +import { MOCK_ACCOUNTS_CONTROLLER_STATE } from '../../../util/test/accountsControllerTestUtils'; const mockedEngine = Engine; @@ -15,15 +12,6 @@ const mockInitialState = { engine: { backgroundState: { ...initialBackgroundState, - PreferencesController: { - selectedAddress: MOCK_ADDRESS_1, - identities: { - [MOCK_ADDRESS_1]: { - name: 'Account 1', - address: MOCK_ADDRESS_1, - }, - }, - }, AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, }, }, diff --git a/app/components/UI/EthereumAddress/index.js b/app/components/UI/EthereumAddress/index.js index b522b2bf0cd..82b2ce368a8 100644 --- a/app/components/UI/EthereumAddress/index.js +++ b/app/components/UI/EthereumAddress/index.js @@ -21,7 +21,7 @@ class EthereumAddress extends PureComponent { address: PropTypes.string, /** * Type of formatting for the address - * can be "short" or "full" + * can be "short", "mid" or "full" */ type: PropTypes.string, }; diff --git a/app/components/UI/NavbarBrowserTitle/index.test.tsx b/app/components/UI/NavbarBrowserTitle/index.test.tsx index 65a46192a79..9bc8a67c0b2 100644 --- a/app/components/UI/NavbarBrowserTitle/index.test.tsx +++ b/app/components/UI/NavbarBrowserTitle/index.test.tsx @@ -10,12 +10,6 @@ const mockInitialState = { engine: { backgroundState: { ...initialBackgroundState, - PreferencesController: { - selectedAddress: '0x', - identities: { - '0x': { name: 'Account 1', address: '0x' }, - }, - }, }, }, }; diff --git a/app/components/UI/Ramp/Views/OrderDetails/__snapshots__/OrderDetails.test.tsx.snap b/app/components/UI/Ramp/Views/OrderDetails/__snapshots__/OrderDetails.test.tsx.snap index dd227168f98..e7db500ff72 100644 --- a/app/components/UI/Ramp/Views/OrderDetails/__snapshots__/OrderDetails.test.tsx.snap +++ b/app/components/UI/Ramp/Views/OrderDetails/__snapshots__/OrderDetails.test.tsx.snap @@ -863,6 +863,7 @@ exports[`OrderDetails renders a cancelled order 1`] = ` ] } > + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + Account 1 ( + toLowerCaseEquals(account.address, address), + ) + : internalAccounts.find((account) => + toLowerCaseEquals(account.address, selectedAddress), + ); + + const accountName = selectedInternalAccount?.metadata?.name || ''; return ( - {identities[address || selectedAddress]?.name} ( + {accountName} ( ) diff --git a/app/components/UI/SearchTokenAutocomplete/index.test.tsx b/app/components/UI/SearchTokenAutocomplete/index.test.tsx index 8ea3e4bd99f..cb2002f59f9 100644 --- a/app/components/UI/SearchTokenAutocomplete/index.test.tsx +++ b/app/components/UI/SearchTokenAutocomplete/index.test.tsx @@ -9,13 +9,6 @@ const mockInitialState = { ...initialBackgroundState, PreferencesController: { useTokenDetection: true, - selectedAddress: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', - identities: { - '0x0': { - address: '0x76cf1CdD1fcC252442b50D6e97207228aA4aefC3', - name: 'Account 1', - }, - }, }, }, }, diff --git a/app/components/UI/TransactionElement/index.js b/app/components/UI/TransactionElement/index.js index 2801450fe07..9d78b4f1243 100644 --- a/app/components/UI/TransactionElement/index.js +++ b/app/components/UI/TransactionElement/index.js @@ -33,8 +33,7 @@ import { selectChainId, selectTicker, } from '../../../selectors/networkController'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; const createStyles = (colors, typography) => StyleSheet.create({ @@ -129,13 +128,9 @@ class TransactionElement extends PureComponent { */ tx: PropTypes.object, /** - * String of selected address - */ - selectedAddress: PropTypes.string, - /** - /* Identities object required to get import time name + /* InternalAccount object required to get import time name */ - identities: PropTypes.object, + selectedInternalAccount: PropTypes.object, /** * Current element of the list index */ @@ -216,7 +211,10 @@ class TransactionElement extends PureComponent { }; renderTxTime = () => { - const { tx, selectedAddress } = this.props; + const { tx, selectedInternalAccount } = this.props; + const selectedAddress = safeToChecksumAddress( + selectedInternalAccount?.address, + ); const incoming = safeToChecksumAddress(tx.txParams.to) === selectedAddress; const selfSent = incoming && safeToChecksumAddress(tx.txParams.from) === selectedAddress; @@ -238,11 +236,10 @@ class TransactionElement extends PureComponent { * @returns Account added to wallet view */ renderImportTime = () => { - const { tx, identities, selectedAddress } = this.props; + const { tx, selectedInternalAccount } = this.props; const { colors, typography } = this.context || mockTheme; const styles = createStyles(colors, typography); - - const accountImportTime = identities[selectedAddress]?.importTime; + const accountImportTime = selectedInternalAccount?.metadata.importTime; if (tx.insertImportTime && accountImportTime) { return ( <> @@ -312,9 +309,8 @@ class TransactionElement extends PureComponent { */ renderTxElement = (transactionElement) => { const { - identities, + selectedInternalAccount, chainId, - selectedAddress, isQRHardwareAccount, isLedgerAccount, tx: { time, status, isSmartTransaction }, @@ -329,7 +325,7 @@ class TransactionElement extends PureComponent { const renderUnsignedQRActions = status === 'approved' && isQRHardwareAccount; const renderLedgerActions = status === 'approved' && isLedgerAccount; - const accountImportTime = identities[selectedAddress]?.importTime; + const accountImportTime = selectedInternalAccount?.metadata.importTime; return ( <> {accountImportTime > time && this.renderImportTime()} @@ -605,9 +601,8 @@ class TransactionElement extends PureComponent { const mapStateToProps = (state) => ({ ticker: selectTicker(state), chainId: selectChainId(state), - identities: selectIdentities(state), + selectedInternalAccount: selectSelectedInternalAccount(state), primaryCurrency: state.settings.primaryCurrency, - selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), swapsTransactions: state.engine.backgroundState.TransactionController.swapsTransactions || {}, swapsTokens: state.engine.backgroundState.SwapsController.tokens, diff --git a/app/components/Views/AccountConnect/AccountConnect.tsx b/app/components/Views/AccountConnect/AccountConnect.tsx index 2e20f151bcf..2f2612c0d2e 100644 --- a/app/components/Views/AccountConnect/AccountConnect.tsx +++ b/app/components/Views/AccountConnect/AccountConnect.tsx @@ -27,8 +27,10 @@ import { USER_INTENT } from '../../../constants/permissions'; import { MetaMetricsEvents } from '../../../core/Analytics'; import UntypedEngine from '../../../core/Engine'; import { selectAccountsLength } from '../../../selectors/accountTrackerController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; -import { selectIdentities } from '../../../selectors/preferencesController'; +import { + selectInternalAccounts, + selectSelectedInternalAccountChecksummedAddress, +} from '../../../selectors/accountsController'; import { isDefaultAccountName } from '../../../util/ENSUtils'; import Logger from '../../../util/Logger'; import getAccountNameWithENS from '../../../util/accounts'; @@ -94,9 +96,9 @@ const AccountConnect = (props: AccountConnectProps) => { const selectedWalletAddress = useSelector( selectSelectedInternalAccountChecksummedAddress, ); - const [selectedAddresses, setSelectedAddresses] = useState([ - selectedWalletAddress, - ]); + const [selectedAddresses, setSelectedAddresses] = useState( + selectedWalletAddress ? [selectedWalletAddress] : [], + ); const sheetRef = useRef(null); const [screen, setScreen] = useState( AccountConnectScreens.SingleConnect, @@ -105,7 +107,7 @@ const AccountConnect = (props: AccountConnectProps) => { isLoading, }); const previousIdentitiesListSize = useRef(); - const identitiesMap = useSelector(selectIdentities); + const internalAccounts = useSelector(selectInternalAccounts); const [showPhishingModal, setShowPhishingModal] = useState(false); const [userIntent, setUserIntent] = useState(USER_INTENT.None); @@ -267,16 +269,20 @@ const AccountConnect = (props: AccountConnectProps) => { // Refreshes selected addresses based on the addition and removal of accounts. useEffect(() => { - const identitiesAddressList = Object.keys(identitiesMap); - if (previousIdentitiesListSize.current !== identitiesAddressList.length) { - // Clean up selected addresses that are no longer part of identities. + // Extract the address list from the internalAccounts array + const accountsAddressList = internalAccounts.map((account) => + account.address.toLowerCase(), + ); + + if (previousIdentitiesListSize.current !== accountsAddressList.length) { + // Clean up selected addresses that are no longer part of accounts. const updatedSelectedAddresses = selectedAddresses.filter((address) => - identitiesAddressList.includes(address), + accountsAddressList.includes(address.toLowerCase()), ); setSelectedAddresses(updatedSelectedAddresses); - previousIdentitiesListSize.current = identitiesAddressList.length; + previousIdentitiesListSize.current = accountsAddressList.length; } - }, [identitiesMap, selectedAddresses]); + }, [internalAccounts, selectedAddresses]); const cancelPermissionRequest = useCallback( (requestId) => { diff --git a/app/components/Views/AccountPermissions/AccountPermissions.tsx b/app/components/Views/AccountPermissions/AccountPermissions.tsx index c6a51d2a392..d450e93dffa 100755 --- a/app/components/Views/AccountPermissions/AccountPermissions.tsx +++ b/app/components/Views/AccountPermissions/AccountPermissions.tsx @@ -36,7 +36,6 @@ import { getActiveTabUrl } from '../../../util/transactions'; import { strings } from '../../../../locales/i18n'; import { AvatarAccountType } from '../../../component-library/components/Avatars/Avatar/variants/AvatarAccount'; import { selectAccountsLength } from '../../../selectors/accountTrackerController'; -import { selectIdentities } from '../../../selectors/preferencesController'; import { selectNetworkConfigurations } from '../../../selectors/networkController'; // Internal dependencies. @@ -50,6 +49,7 @@ import { USER_INTENT } from '../../../constants/permissions'; import useFavicon from '../../hooks/useFavicon/useFavicon'; import URLParse from 'url-parse'; import { useMetrics } from '../../../components/hooks/useMetrics'; +import { selectInternalAccounts } from '../../../selectors/accountsController'; const AccountPermissions = (props: AccountPermissionsProps) => { const navigation = useNavigation(); @@ -112,7 +112,7 @@ const AccountPermissions = (props: AccountPermissionsProps) => { }); const previousPermittedAccounts = useRef(); const previousIdentitiesListSize = useRef(); - const identitiesMap = useSelector(selectIdentities); + const internalAccounts = useSelector(selectInternalAccounts); const activeAddress: string = permittedAccountsByHostname[0]; const [userIntent, setUserIntent] = useState(USER_INTENT.None); @@ -142,16 +142,20 @@ const AccountPermissions = (props: AccountPermissionsProps) => { // Refreshes selected addresses based on the addition and removal of accounts. useEffect(() => { - const identitiesAddressList = Object.keys(identitiesMap); - if (previousIdentitiesListSize.current !== identitiesAddressList.length) { - // Clean up selected addresses that are no longer part of identities. + // Extract the address list from the internalAccounts array + const accountsAddressList = internalAccounts.map((account) => + account.address.toLowerCase(), + ); + + if (previousIdentitiesListSize.current !== accountsAddressList.length) { + // Clean up selected addresses that are no longer part of accounts. const updatedSelectedAddresses = selectedAddresses.filter((address) => - identitiesAddressList.includes(address), + accountsAddressList.includes(address.toLowerCase()), ); setSelectedAddresses(updatedSelectedAddresses); - previousIdentitiesListSize.current = identitiesAddressList.length; + previousIdentitiesListSize.current = accountsAddressList.length; } - }, [identitiesMap, selectedAddresses]); + }, [internalAccounts, selectedAddresses]); const accountsFilteredByPermissions = useMemo(() => { const accountsByPermittedStatus: Record< diff --git a/app/components/Views/Asset/index.js b/app/components/Views/Asset/index.js index bd1cb81b684..63e700030b9 100644 --- a/app/components/Views/Asset/index.js +++ b/app/components/Views/Asset/index.js @@ -54,8 +54,7 @@ import { selectConversionRate, selectCurrentCurrency, } from '../../../selectors/currencyRateController'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; import { TOKEN_OVERVIEW_BUY_BUTTON, TOKEN_OVERVIEW_SWAP_BUTTON, @@ -63,6 +62,7 @@ import { import { updateIncomingTransactions } from '../../../util/transaction-controller'; import { withMetricsAwareness } from '../../../components/hooks/useMetrics'; import { store } from '../../../store'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; const createStyles = (colors) => StyleSheet.create({ @@ -138,13 +138,9 @@ class Asset extends PureComponent { */ currentCurrency: PropTypes.string, /** - /* Identities object required to get account name + /* InternalAccount object required to get account name */ - identities: PropTypes.object, - /** - * A string that represents the selected address - */ - selectedAddress: PropTypes.string, + selectedInternalAccount: PropTypes.object, /** * The chain ID for the current selected network */ @@ -192,6 +188,9 @@ class Asset extends PureComponent { filter = undefined; navSymbol = undefined; navAddress = undefined; + selectedAddress = toChecksumHexAddress( + this.props.selectedInternalAccount?.address, + ); updateNavBar = (contentOffset = 0) => { const { navigation, route, chainId, rpcUrl, networkConfigurations } = @@ -251,7 +250,8 @@ class Asset extends PureComponent { componentDidUpdate(prevProps) { if ( prevProps.chainId !== this.props.chainId || - prevProps.selectedAddress !== this.props.selectedAddress + prevProps.selectedInternalAccount.address !== + this.props.selectedInternalAccount?.address ) { this.showLoaderAndNormalize(); } else { @@ -274,7 +274,7 @@ class Asset extends PureComponent { ethFilter = (tx) => { const { networkId } = store.getState().inpageProvider; - const { selectedAddress, chainId } = this.props; + const { chainId } = this.props; const { txParams: { from, to }, isTransfer, @@ -282,8 +282,8 @@ class Asset extends PureComponent { } = tx; if ( - (safeToChecksumAddress(from) === selectedAddress || - safeToChecksumAddress(to) === selectedAddress) && + (safeToChecksumAddress(from) === this.selectedAddress || + safeToChecksumAddress(to) === this.selectedAddress) && (chainId === tx.chainId || (!tx.chainId && networkId === tx.networkID)) && tx.status !== 'unapproved' ) { @@ -299,15 +299,15 @@ class Asset extends PureComponent { noEthFilter = (tx) => { const { networkId } = store.getState().inpageProvider; - const { chainId, swapsTransactions, selectedAddress } = this.props; + const { chainId, swapsTransactions } = this.props; const { txParams: { to, from }, isTransfer, transferInformation, } = tx; if ( - (safeToChecksumAddress(from) === selectedAddress || - safeToChecksumAddress(to) === selectedAddress) && + (safeToChecksumAddress(from) === this.selectedAddress || + safeToChecksumAddress(to) === this.selectedAddress) && (chainId === tx.chainId || (!tx.chainId && networkId === tx.networkID)) && tx.status !== 'unapproved' ) { @@ -334,8 +334,8 @@ class Asset extends PureComponent { normalizeTransactions() { if (this.isNormalizing) return; let accountAddedTimeInsertPointFound = false; - const { selectedAddress } = this.props; - const addedAccountTime = this.props.identities[selectedAddress]?.importTime; + const { selectedInternalAccount } = this.props; + const addedAccountTime = selectedInternalAccount?.metadata.importTime; this.isNormalizing = true; let submittedTxs = []; @@ -376,7 +376,7 @@ class Asset extends PureComponent { }); submittedTxs = submittedTxs.filter(({ txParams: { from, nonce } }) => { - if (!toLowerCaseEquals(from, selectedAddress)) { + if (!toLowerCaseEquals(from, this.selectedAddress)) { return false; } const alreadySubmitted = submittedNonces.includes(nonce); @@ -384,7 +384,7 @@ class Asset extends PureComponent { (confirmedTransaction) => toLowerCaseEquals( safeToChecksumAddress(confirmedTransaction.txParams.from), - selectedAddress, + this.selectedAddress, ) && confirmedTransaction.txParams.nonce === nonce, ); if (alreadyConfirmed) { @@ -461,7 +461,6 @@ class Asset extends PureComponent { navigation, conversionRate, currentCurrency, - selectedAddress, chainId, } = this.props; const colors = this.context.colors || mockTheme.colors; @@ -522,7 +521,7 @@ class Asset extends PureComponent { transactions={transactions} submittedTransactions={submittedTxs} confirmedTransactions={confirmedTxs} - selectedAddress={selectedAddress} + selectedAddress={this.selectedAddress} conversionRate={conversionRate} currentCurrency={currentCurrency} networkType={chainId} @@ -580,8 +579,7 @@ const mapStateToProps = (state) => ({ state.engine.backgroundState.TransactionController.swapsTransactions || {}, conversionRate: selectConversionRate(state), currentCurrency: selectCurrentCurrency(state), - selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), - identities: selectIdentities(state), + selectedInternalAccount: selectSelectedInternalAccount(state), chainId: selectChainId(state), tokens: selectTokens(state), transactions: state.engine.backgroundState.TransactionController.transactions, diff --git a/app/components/Views/NetworkSelector/NetworkSelector.test.tsx b/app/components/Views/NetworkSelector/NetworkSelector.test.tsx index 36feaf8de3a..7f866b2f94f 100644 --- a/app/components/Views/NetworkSelector/NetworkSelector.test.tsx +++ b/app/components/Views/NetworkSelector/NetworkSelector.test.tsx @@ -25,10 +25,6 @@ jest.mock('../../../core/Engine', () => ({ context: { NetworkController: { setActiveNetwork: jest.fn() }, PreferencesController: { - selectedAddress: '0x', - identities: { - '0x': { name: 'Account 1', address: '0x' }, - }, setShowTestNetworks: jest.fn(), }, CurrencyRateController: { updateExchangeRate: jest.fn() }, @@ -105,10 +101,6 @@ const initialState = { }, PreferencesController: { showTestNetworks: false, - selectedAddress: '0x', - identities: { - '0x': { name: 'Account 1', address: '0x' }, - }, }, NftController: { allNfts: { '0x': { '0x1': [] } }, diff --git a/app/components/Views/Settings/Contacts/ContactForm/index.js b/app/components/Views/Settings/Contacts/ContactForm/index.js index b31aff1da3e..8fadd5ac7bd 100644 --- a/app/components/Views/Settings/Contacts/ContactForm/index.js +++ b/app/components/Views/Settings/Contacts/ContactForm/index.js @@ -32,8 +32,9 @@ import { import Routes from '../../../../../constants/navigation/Routes'; import { createQRScannerNavDetails } from '../../../QRScanner'; import { selectChainId } from '../../../../../selectors/networkController'; -import { selectIdentities } from '../../../../../selectors/preferencesController'; import { AddContactViewSelectorsIDs } from '../../../../../../e2e/selectors/Settings/Contacts/AddContactView.selectors'; +import { selectInternalAccounts } from '../../../../../selectors/accountsController'; +import { toLowerCaseEquals } from '../../../../../util/general'; const createStyles = (colors) => StyleSheet.create({ @@ -121,9 +122,9 @@ class ContactForm extends PureComponent { */ navigation: PropTypes.object, /** - * An object containing each identity in the format address => account + * An array containing each account with metadata */ - identities: PropTypes.object, + internalAccounts: PropTypes.array, /** * Map representing the address book */ @@ -178,14 +179,19 @@ class ContactForm extends PureComponent { this.setState({ inputWidth: '100%' }); }, 100); if (mode === EDIT) { - const { addressBook, chainId, identities } = this.props; + const { addressBook, chainId, internalAccounts } = this.props; const networkAddressBook = addressBook[chainId] || {}; const address = this.props.route.params?.address ?? ''; - const contact = networkAddressBook[address] || identities[address]; + const contact = + networkAddressBook[address] || + (address && + internalAccounts.find((account) => + toLowerCaseEquals(account.address, address), + )); this.setState({ address, - name: contact.name, - memo: contact.memo, + name: contact?.name ?? '', + memo: contact?.memo ?? '', addressReady: true, editable: false, }); @@ -216,7 +222,7 @@ class ContactForm extends PureComponent { }; validateAddressOrENSFromInput = async (address) => { - const { addressBook, identities, chainId } = this.props; + const { addressBook, internalAccounts, chainId } = this.props; const { addressError, @@ -224,12 +230,12 @@ class ContactForm extends PureComponent { addressReady, toEnsAddress, errorContinue, - } = await validateAddressOrENS({ - toAccount: address, + } = await validateAddressOrENS( + address, addressBook, - identities, + internalAccounts, chainId, - }); + ); this.setState({ addressError, @@ -499,7 +505,7 @@ ContactForm.contextType = ThemeContext; const mapStateToProps = (state) => ({ addressBook: state.engine.backgroundState.AddressBookController.addressBook, - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), chainId: selectChainId(state), }); diff --git a/app/components/Views/TransactionsView/index.js b/app/components/Views/TransactionsView/index.js index df07b80907f..9f6ece59d3f 100644 --- a/app/components/Views/TransactionsView/index.js +++ b/app/components/Views/TransactionsView/index.js @@ -29,12 +29,12 @@ import { } from '../../../selectors/currencyRateController'; import { selectTokens } from '../../../selectors/tokensController'; import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors'; -import { selectIdentities } from '../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../selectors/accountsController'; +import { selectSelectedInternalAccount } from '../../../selectors/accountsController'; import { store } from '../../../store'; import { NETWORK_ID_LOADING } from '../../../core/redux/slices/inpageProvider'; import { selectPendingSmartTransactionsBySender } from '../../../selectors/smartTransactionsController'; import { selectNonReplacedTransactions } from '../../../selectors/transactionController'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; const styles = StyleSheet.create({ wrapper: { @@ -45,8 +45,7 @@ const styles = StyleSheet.create({ const TransactionsView = ({ navigation, conversionRate, - selectedAddress, - identities, + selectedInternalAccount, networkType, currentCurrency, transactions, @@ -58,12 +57,16 @@ const TransactionsView = ({ const [confirmedTxs, setConfirmedTxs] = useState([]); const [loading, setLoading] = useState(); + const selectedAddress = toChecksumHexAddress( + selectedInternalAccount?.address, + ); + const filterTransactions = useCallback( (networkId) => { if (networkId === NETWORK_ID_LOADING) return; let accountAddedTimeInsertPointFound = false; - const addedAccountTime = identities[selectedAddress]?.importTime; + const addedAccountTime = selectedInternalAccount?.metadata.importTime; const submittedTxs = []; const confirmedTxs = []; @@ -141,7 +144,7 @@ const TransactionsView = ({ setConfirmedTxs(confirmedTxs); setLoading(false); }, - [transactions, identities, selectedAddress, tokens, chainId], + [transactions, selectedInternalAccount, selectedAddress, tokens, chainId], ); useEffect(() => { @@ -188,17 +191,13 @@ TransactionsView.propTypes = { */ currentCurrency: PropTypes.string, /** - /* Identities object required to get account name + /* InternalAccount object required to get account name, address and import time */ - identities: PropTypes.object, + selectedInternalAccount: PropTypes.object, /** /* navigation object required to push new views */ navigation: PropTypes.object, - /** - * A string that represents the selected address - */ - selectedAddress: PropTypes.string, /** * An array that represents the user transactions */ @@ -218,8 +217,6 @@ TransactionsView.propTypes = { }; const mapStateToProps = (state) => { - const selectedAddress = - selectSelectedInternalAccountChecksummedAddress(state); const chainId = selectChainId(state); // Remove duplicate confirmed STX @@ -233,8 +230,7 @@ const mapStateToProps = (state) => { conversionRate: selectConversionRate(state), currentCurrency: selectCurrentCurrency(state), tokens: selectTokens(state), - selectedAddress, - identities: selectIdentities(state), + selectedInternalAccount: selectSelectedInternalAccount(state), transactions: [ ...nonReplacedTransactions, ...pendingSmartTransactions, diff --git a/app/components/Views/Wallet/index.test.tsx b/app/components/Views/Wallet/index.test.tsx index ad0765d26c4..1a2b4fa3701 100644 --- a/app/components/Views/Wallet/index.test.tsx +++ b/app/components/Views/Wallet/index.test.tsx @@ -23,15 +23,6 @@ jest.mock('../../../core/Engine', () => ({ init: () => mockEngine.init({}), getTotalFiatAccountBalance: jest.fn(), context: { - PreferencesController: { - selectedAddress: MOCK_ADDRESS, - identities: { - [MOCK_ADDRESS]: { - name: 'Account 1', - address: MOCK_ADDRESS, - }, - }, - }, NftController: { allNfts: { [MOCK_ADDRESS]: { @@ -94,15 +85,6 @@ const mockInitialState = { engine: { backgroundState: { ...initialBackgroundState, - PreferencesController: { - selectedAddress: MOCK_ADDRESS, - identities: { - [MOCK_ADDRESS]: { - name: 'Account 1', - address: MOCK_ADDRESS, - }, - }, - }, AccountsController: { ...MOCK_ACCOUNTS_CONTROLLER_STATE, }, diff --git a/app/components/Views/confirmations/Send/index.js b/app/components/Views/confirmations/Send/index.js index 6b2bf18baa5..2b98788ba80 100644 --- a/app/components/Views/confirmations/Send/index.js +++ b/app/components/Views/confirmations/Send/index.js @@ -57,12 +57,15 @@ import { selectTokenList } from '../../../../selectors/tokenListController'; import { selectTokens } from '../../../../selectors/tokensController'; import { selectAccounts } from '../../../../selectors/accountTrackerController'; import { selectContractBalances } from '../../../../selectors/tokenBalancesController'; -import { selectIdentities } from '../../../../selectors/preferencesController'; -import { selectSelectedInternalAccountChecksummedAddress } from '../../../../selectors/accountsController'; +import { + selectInternalAccounts, + selectSelectedInternalAccountChecksummedAddress, +} from '../../../../selectors/accountsController'; import { providerErrors } from '@metamask/rpc-errors'; import { withMetricsAwareness } from '../../../../components/hooks/useMetrics'; import { selectShouldUseSmartTransaction } from '../../../../selectors/smartTransactionsController'; import { STX_NO_HASH_ERROR } from '../../../../util/smart-transactions/smart-publish-hook'; +import { toLowerCaseEquals } from '../../../../util/general'; const REVIEW = 'review'; const EDIT = 'edit'; @@ -124,9 +127,9 @@ class Send extends PureComponent { */ chainId: PropTypes.string, /** - * List of accounts from the PreferencesController + * List of accounts from the AccountsController */ - identities: PropTypes.object, + internalAccounts: PropTypes.array, /** * Selected address as string */ @@ -315,7 +318,8 @@ class Send extends PureComponent { function_name = null, // eslint-disable-line no-unused-vars parameters = null, }) => { - const { addressBook, chainId, identities, selectedAddress } = this.props; + const { addressBook, chainId, internalAccounts, selectedAddress } = + this.props; let newTxMeta = {}; let txRecipient; @@ -342,7 +346,7 @@ class Send extends PureComponent { addressBook, chainId, toAddress: newTxMeta.to, - identities, + internalAccounts, ensRecipient: newTxMeta.ensRecipient, }); @@ -382,7 +386,7 @@ class Send extends PureComponent { addressBook, chainId, toAddress: to, - identities, + internalAccounts, ensRecipient, }); break; @@ -416,7 +420,10 @@ class Send extends PureComponent { } newTxMeta.from = selectedAddress; - newTxMeta.transactionFromName = identities[selectedAddress].name; + const fromAccount = internalAccounts.find((account) => + toLowerCaseEquals(account.address, selectedAddress), + ); + newTxMeta.transactionFromName = fromAccount.metadata.name; this.props.setTransactionObject(newTxMeta); this.mounted && this.setState({ ready: true, transactionKey: Date.now() }); }; @@ -781,7 +788,7 @@ const mapStateToProps = (state) => ({ networkType: selectProviderType(state), tokens: selectTokens(state), chainId: selectChainId(state), - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), dappTransactionModalVisible: state.modals.dappTransactionModalVisible, tokenList: selectTokenList(state), diff --git a/app/components/Views/confirmations/Send/index.test.tsx b/app/components/Views/confirmations/Send/index.test.tsx index 309da7b89ec..b7b9012493c 100644 --- a/app/components/Views/confirmations/Send/index.test.tsx +++ b/app/components/Views/confirmations/Send/index.test.tsx @@ -4,6 +4,7 @@ import { MOCK_ACCOUNTS_CONTROLLER_STATE, MOCK_ADDRESS_1, } from '../../../../util/test/accountsControllerTestUtils'; +import { MOCK_KEYRING_CONTROLLER } from '../../../../selectors/keyringController/testUtils'; const initialState = { transaction: { @@ -55,13 +56,6 @@ const initialState = { }, PreferencesController: { featureFlags: {}, - identities: { - [MOCK_ADDRESS_1]: { - address: MOCK_ADDRESS_1, - name: 'Account 1', - importTime: 1684232000456, - }, - }, ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', lostIdentities: {}, selectedAddress: MOCK_ADDRESS_1, @@ -79,13 +73,6 @@ const initialState = { _W: { featureFlags: {}, frequentRpcList: [], - identities: { - [MOCK_ADDRESS_1]: { - address: MOCK_ADDRESS_1, - name: 'Account 1', - importTime: 1684232000456, - }, - }, ipfsGateway: 'https://cloudflare-ipfs.com/ipfs/', lostIdentities: {}, selectedAddress: MOCK_ADDRESS_1, @@ -123,6 +110,7 @@ const initialState = { _X: null, }, AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, + KeyringController: MOCK_KEYRING_CONTROLLER, NetworkController: { network: '1', providerConfig: { diff --git a/app/components/Views/confirmations/SendFlow/AddressList/AddressList.test.tsx b/app/components/Views/confirmations/SendFlow/AddressList/AddressList.test.tsx index b011a3c69c0..12d550b0154 100644 --- a/app/components/Views/confirmations/SendFlow/AddressList/AddressList.test.tsx +++ b/app/components/Views/confirmations/SendFlow/AddressList/AddressList.test.tsx @@ -2,6 +2,13 @@ import React from 'react'; import renderWithProvider from '../../../../../util/test/renderWithProvider'; import initialBackgroundState from '../../../../../util/test/initial-background-state.json'; import AddressList from '.'; +import { createMockAccountsControllerState } from '../../../../../util/test/accountsControllerTestUtils'; + +const MOCK_ADDRESS = '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272'; + +const MOCK_ACCOUNTS_CONTROLLER_STATE = createMockAccountsControllerState([ + MOCK_ADDRESS, +]); jest.mock('../../../../../core/Engine', () => ({ context: { @@ -20,8 +27,8 @@ const initialState = { AddressBookController: { addressBook: { '0x1': { - '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272': { - address: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272', + [MOCK_ADDRESS]: { + address: MOCK_ADDRESS, chainId: '0x1', isEns: false, memo: '', @@ -30,14 +37,7 @@ const initialState = { }, }, }, - PreferencesController: { - identities: { - '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272': { - address: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272', - name: 'Account 1', - }, - }, - }, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, }, }, }; diff --git a/app/components/Views/confirmations/SendFlow/AddressList/AddressList.tsx b/app/components/Views/confirmations/SendFlow/AddressList/AddressList.tsx index bb993718ad5..8022c337dc8 100644 --- a/app/components/Views/confirmations/SendFlow/AddressList/AddressList.tsx +++ b/app/components/Views/confirmations/SendFlow/AddressList/AddressList.tsx @@ -16,13 +16,14 @@ import { useTheme } from '../../../../../util/theme'; import Text from '../../../../../component-library/components/Texts/Text/Text'; import { TextVariant } from '../../../../../component-library/components/Texts/Text'; import { selectChainId } from '../../../../../selectors/networkController'; -import { selectIdentities } from '../../../../../selectors/preferencesController'; import { regex } from '../../../../../util/regex'; import { SendViewSelectorsIDs } from '../../../../../../e2e/selectors/SendView.selectors'; +import { selectInternalAccounts } from '../../../../../selectors/accountsController'; // Internal dependencies import { AddressListProps, Contact } from './AddressList.types'; import styleSheet from './AddressList.styles'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -49,7 +50,7 @@ const AddressList: React.FC = ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any const [fuse, setFuse] = useState(undefined); const chainId = useSelector(selectChainId); - const identities = useSelector(selectIdentities); + const internalAccounts = useSelector(selectInternalAccounts); const addressBook = useSelector( // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -183,11 +184,11 @@ const AddressList: React.FC = ({ > {strings('onboarding_wizard.step2.title')} - {Object.keys(identities).map((address) => ( + {internalAccounts.map((account) => ( true; @@ -87,9 +90,9 @@ class SendFlow extends PureComponent { */ selectedAddress: PropTypes.string, /** - * List of accounts from the PreferencesController + * List of accounts from the AccountsController */ - identities: PropTypes.object, + internalAccounts: PropTypes.array, /** * Current provider ticker */ @@ -217,11 +220,14 @@ class SendFlow extends PureComponent { isAddressSaved = () => { const { toAccount } = this.state; - const { addressBook, chainId, identities } = this.props; + const { addressBook, chainId, internalAccounts } = this.props; const networkAddressBook = addressBook[chainId] || {}; const checksummedAddress = toChecksumAddress(toAccount); return !!( - networkAddressBook[checksummedAddress] || identities[checksummedAddress] + networkAddressBook[checksummedAddress] || + internalAccounts.find((account) => + toLowerCaseEquals(account.address, checksummedAddress), + ) ); }; @@ -354,23 +360,26 @@ class SendFlow extends PureComponent { this.setState({ fromSelectedAddress: address }); }; - getAddressNameFromBookOrIdentities = (toAccount) => { - const { addressBook, identities, chainId } = this.props; + getAddressNameFromBookOrInternalAccounts = (toAccount) => { + const { addressBook, internalAccounts, chainId } = this.props; if (!toAccount) return; const networkAddressBook = addressBook[chainId] || {}; const checksummedAddress = toChecksumAddress(toAccount); + const matchingAccount = internalAccounts.find((account) => + toLowerCaseEquals(account.address, checksummedAddress), + ); return networkAddressBook[checksummedAddress] ? networkAddressBook[checksummedAddress].name - : identities[checksummedAddress] - ? identities[checksummedAddress].name + : matchingAccount + ? matchingAccount.metadata.name : null; }; validateAddressOrENSFromInput = async (toAccount) => { - const { addressBook, identities, chainId } = this.props; + const { addressBook, internalAccounts, chainId } = this.props; const { addressError, toEnsName, @@ -381,12 +390,12 @@ class SendFlow extends PureComponent { errorContinue, isOnlyWarning, confusableCollection, - } = await validateAddressOrENS({ + } = await validateAddressOrENS( toAccount, addressBook, - identities, + internalAccounts, chainId, - }); + ); this.setState({ addressError, @@ -415,7 +424,8 @@ class SendFlow extends PureComponent { }, ); } - const addressName = this.getAddressNameFromBookOrIdentities(toAccount); + const addressName = + this.getAddressNameFromBookOrInternalAccounts(toAccount); /** * If the address is from addressBook or identities @@ -471,7 +481,7 @@ class SendFlow extends PureComponent { const styles = createStyles(colors); const checksummedAddress = toAccount && toChecksumAddress(toAccount); - const existingAddressName = this.getAddressNameFromBookOrIdentities( + const existingAddressName = this.getAddressNameFromBookOrInternalAccounts( toEnsAddressResolved || toAccount, ); const existingContact = @@ -661,7 +671,7 @@ const mapStateToProps = (state) => ({ chainId: selectChainId(state), selectedAddress: selectSelectedInternalAccountChecksummedAddress(state), selectedAsset: state.transaction.selectedAsset, - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), ticker: selectTicker(state), providerType: selectProviderType(state), isPaymentRequest: state.transaction.paymentRequest, diff --git a/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.test.tsx b/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.test.tsx index dd20029ead9..61191c26327 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.test.tsx +++ b/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.test.tsx @@ -42,19 +42,6 @@ const mockInitialState = { }, }, }, - PreferencesController: { - selectedAddress: MOCK_ADDRESS_1, - identities: { - [MOCK_ADDRESS_1]: { - address: MOCK_ADDRESS_1, - name: 'Account 1', - }, - [MOCK_ADDRESS_2]: { - address: MOCK_ADDRESS_2, - name: 'Account 2', - }, - }, - }, AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, NetworkController: { providerConfig: { diff --git a/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx b/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx index 26e616e27bd..b0749ac8469 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx +++ b/app/components/Views/confirmations/components/ApproveTransactionHeader/ApproveTransactionHeader.tsx @@ -14,7 +14,6 @@ import { selectNetworkImageSource, selectNetworkName, } from '../../../../../selectors/networkInfos'; -import { selectIdentities } from '../../../../../selectors/preferencesController'; import { getLabelTextByAddress, renderAccountName, @@ -30,6 +29,7 @@ import { } from './ApproveTransactionHeader.constants'; import stylesheet from './ApproveTransactionHeader.styles'; import { ApproveTransactionHeaderI } from './ApproveTransactionHeader.types'; +import { selectInternalAccounts } from '../../../../../selectors/accountsController'; const ApproveTransactionHeader = ({ from, @@ -51,7 +51,7 @@ const ApproveTransactionHeader = ({ const accountsByChainId = useSelector(selectAccountsByChainId); - const identities = useSelector(selectIdentities); + const internalAccounts = useSelector(selectInternalAccounts); const activeAddress = toChecksumAddress(from); const networkName = useSelector(selectNetworkName); @@ -64,7 +64,7 @@ const ApproveTransactionHeader = ({ useEffect(() => { const accountNameVal = activeAddress - ? renderAccountName(activeAddress, identities) + ? renderAccountName(activeAddress, internalAccounts) : ''; const isOriginDeepLinkVal = @@ -84,7 +84,7 @@ const ApproveTransactionHeader = ({ setIsOriginMMSDKRemoteConn( origin.startsWith(AppConstants.MM_SDK.SDK_REMOTE_ORIGIN), ); - }, [accountsByChainId, identities, activeAddress, origin]); + }, [accountsByChainId, internalAccounts, activeAddress, origin]); const networkImage = useSelector(selectNetworkImageSource); diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx index 74f7fdde669..d28f4d7e06c 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx +++ b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/index.tsx @@ -36,9 +36,10 @@ import { selectProviderType, selectRpcUrl, } from '../../../../../../selectors/networkController'; -import { selectIdentities } from '../../../../../../selectors/preferencesController'; import { ContractNickNameViewSelectorsIDs } from '../../../../../../../e2e/selectors/ContractNickNameView.selectors'; import { useMetrics } from '../../../../../../components/hooks/useMetrics'; +import { selectInternalAccounts } from '../../../../../../selectors/accountsController'; +import { RootState } from '../../../../../../reducers'; const getAnalyticsParams = () => ({}); @@ -52,7 +53,7 @@ const AddNickname = (props: AddNicknameProps) => { providerChainId, providerRpcTarget, addressBook, - identities, + internalAccounts, networkConfigurations, } = props; @@ -73,18 +74,17 @@ const AddNickname = (props: AddNicknameProps) => { }; const validateAddressOrENSFromInput = useCallback(async () => { - const { addressError, errorContinue } = await validateAddressOrENS({ - toAccount: address, + const { addressError, errorContinue } = await validateAddressOrENS( + address, addressBook, - identities, - // TODO: This parameters is effectively ignored, it should be named `chainId` + internalAccounts, providerChainId, - }); + ); setAddressErr(addressError); setErrContinue(errorContinue); setAddressHasError(addressError); - }, [address, addressBook, identities, providerChainId]); + }, [address, addressBook, internalAccounts, providerChainId]); useEffect(() => { validateAddressOrENSFromInput(); @@ -155,11 +155,11 @@ const AddNickname = (props: AddNicknameProps) => { return errorMessage; }; - const hasBlockExplorer = shouldShowBlockExplorer({ + const hasBlockExplorer = shouldShowBlockExplorer( providerType, providerRpcTarget, networkConfigurations, - }); + ); return ( @@ -263,14 +263,12 @@ const AddNickname = (props: AddNicknameProps) => { ); }; -// TODO: Replace "any" with type -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: RootState) => ({ providerType: selectProviderType(state), providerRpcTarget: selectRpcUrl(state), providerChainId: selectChainId(state), addressBook: state.engine.backgroundState.AddressBookController.addressBook, - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), networkConfigurations: selectNetworkConfigurations(state), }); diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts index 3bc8fa80f17..4e7da1e8f0b 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts +++ b/app/components/Views/confirmations/components/ApproveTransactionReview/AddNickname/types.ts @@ -1,4 +1,8 @@ +import { AddressBookState } from '@metamask/address-book-controller'; +import { NetworkType } from '@metamask/controller-utils'; +import { InternalAccount } from '@metamask/keyring-api'; import type { NetworkState } from '@metamask/network-controller'; +import { Hex } from '@metamask/utils'; export interface AddNicknameProps { closeModal: () => void; @@ -9,19 +13,10 @@ export interface AddNicknameProps { // TODO: Replace "any" with type // eslint-disable-next-line @typescript-eslint/no-explicit-any showModalAlert: (config: any) => void; - providerType: string; - providerChainId: string; + providerType: NetworkType; + providerChainId: Hex; providerNetwork: string; - providerRpcTarget?: string; - addressBook: { - [key: string]: { - address: string; - chainId: string; - memo: string; - name: string; - }; - }; - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - identities: any; + providerRpcTarget: string; + addressBook: AddressBookState['addressBook']; + internalAccounts: InternalAccount[]; } diff --git a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js index b5523c828f1..4ae7f5e81f0 100644 --- a/app/components/Views/confirmations/components/ApproveTransactionReview/index.js +++ b/app/components/Views/confirmations/components/ApproveTransactionReview/index.js @@ -815,11 +815,11 @@ class ApproveTransactionReview extends PureComponent { gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET || gasEstimateType === GAS_ESTIMATE_TYPES.NONE; - const hasBlockExplorer = shouldShowBlockExplorer({ + const hasBlockExplorer = shouldShowBlockExplorer( providerType, providerRpcTarget, networkConfigurations, - }); + ); const tokenLabel = `${ tokenName || tokenSymbol || strings(`spend_limit_edition.nft`) diff --git a/app/components/Views/confirmations/components/SignatureRequest/Root/Root.test.tsx b/app/components/Views/confirmations/components/SignatureRequest/Root/Root.test.tsx index c326484baba..9cd090bbff5 100644 --- a/app/components/Views/confirmations/components/SignatureRequest/Root/Root.test.tsx +++ b/app/components/Views/confirmations/components/SignatureRequest/Root/Root.test.tsx @@ -75,15 +75,6 @@ const initialState = { }, }, }, - PreferencesController: { - selectedAddress: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272', - identities: { - '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272': { - address: '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272', - name: 'Account 1', - }, - }, - }, CurrencyRateController: { currentCurrency: 'usd', currencyRates: { diff --git a/app/components/Views/confirmations/components/TransactionReview/index.test.tsx b/app/components/Views/confirmations/components/TransactionReview/index.test.tsx index 469860d2308..02993b26192 100644 --- a/app/components/Views/confirmations/components/TransactionReview/index.test.tsx +++ b/app/components/Views/confirmations/components/TransactionReview/index.test.tsx @@ -95,12 +95,6 @@ const mockState = { }, }, PreferencesController: { - selectedAddress: MOCK_ADDRESS_3, - identities: { - [MOCK_ADDRESS_1]: { name: 'Account 1' }, - [MOCK_ADDRESS_2]: { name: 'Account 2' }, - [MOCK_ADDRESS_3]: { name: 'Account 3' }, - }, securityAlertsEnabled: true, }, AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, diff --git a/app/components/Views/confirmations/components/TypedSign/__snapshots__/index.test.tsx.snap b/app/components/Views/confirmations/components/TypedSign/__snapshots__/index.test.tsx.snap index cbe02c8942f..112dd02ee61 100644 --- a/app/components/Views/confirmations/components/TypedSign/__snapshots__/index.test.tsx.snap +++ b/app/components/Views/confirmations/components/TypedSign/__snapshots__/index.test.tsx.snap @@ -393,7 +393,7 @@ exports[`TypedSign onConfirm signs message 1`] = ` } } > - 0xC495...D272 + Account 1 @@ -1064,7 +1064,7 @@ exports[`TypedSign onReject rejects message 1`] = ` } } > - 0xC495...D272 + Account 1 diff --git a/app/components/hooks/useExistingAddress.test.ts b/app/components/hooks/useExistingAddress.test.ts index aad6c1c2736..9e034a7cd63 100644 --- a/app/components/hooks/useExistingAddress.test.ts +++ b/app/components/hooks/useExistingAddress.test.ts @@ -1,21 +1,21 @@ import { renderHookWithProvider } from '../../util/test/renderWithProvider'; import useExistingAddress from './useExistingAddress'; import initialBackgroundState from '../../util/test/initial-background-state.json'; +import { createMockAccountsControllerState } from '../../util/test/accountsControllerTestUtils'; + +const MOCK_ADDRESS_1 = '0x0'; +const MOCK_ADDRESS_2 = '0x1'; + +const MOCK_ACCOUNTS_CONTROLLER_STATE = createMockAccountsControllerState([ + MOCK_ADDRESS_1, + MOCK_ADDRESS_2, +]); const mockInitialState = { settings: {}, engine: { backgroundState: { ...initialBackgroundState, - PreferencesController: { - selectedAddress: '0x0', - identities: { - '0x0': { - address: '0x0', - name: 'Account 1', - }, - }, - }, NetworkController: { providerConfig: { chainId: '0x1', @@ -23,14 +23,15 @@ const mockInitialState = { }, AddressBookController: { addressBook: { - '0x1': { - '0x1': { - address: '0x1', + [MOCK_ADDRESS_2]: { + [MOCK_ADDRESS_2]: { + address: MOCK_ADDRESS_2, name: 'Account 2', }, }, }, }, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE, }, }, }; @@ -43,16 +44,22 @@ jest.mock('react-redux', () => ({ })); describe('useExistingAddress', () => { - it('should return existing address from identities', async () => { - const { result } = renderHookWithProvider(() => useExistingAddress('0x0'), { - state: mockInitialState, - }); + it('should return existing address from accounts controller', async () => { + const { result } = renderHookWithProvider( + () => useExistingAddress(MOCK_ADDRESS_1), + { + state: mockInitialState, + }, + ); expect(result?.current?.name).toEqual('Account 1'); }); it('should return existing address from address book', async () => { - const { result } = renderHookWithProvider(() => useExistingAddress('0x1'), { - state: mockInitialState, - }); + const { result } = renderHookWithProvider( + () => useExistingAddress(MOCK_ADDRESS_2), + { + state: mockInitialState, + }, + ); expect(result?.current?.name).toEqual('Account 2'); }); it('should return undefined address not in identities or address book', async () => { diff --git a/app/components/hooks/useExistingAddress.ts b/app/components/hooks/useExistingAddress.ts index 297863dac0e..77b75ef7771 100644 --- a/app/components/hooks/useExistingAddress.ts +++ b/app/components/hooks/useExistingAddress.ts @@ -2,24 +2,18 @@ import { toChecksumAddress } from 'ethereumjs-util'; import { useSelector } from 'react-redux'; import { selectChainId } from '../../selectors/networkController'; -import { selectIdentities } from '../../selectors/preferencesController'; - -export interface Address { - address: string; - chainId: string; - isEns: boolean; - isSmartContract: boolean; - memo: string; - name: string; -} - -const useExistingAddress = (address?: string): Address | undefined => { +import { selectInternalAccounts } from '../../selectors/accountsController'; +import { toLowerCaseEquals } from '../../util/general'; +import { AddressBookEntry } from '@metamask/address-book-controller'; +import { RootState } from '../../reducers'; + +type AccountInfo = Pick; + +const useExistingAddress = (address?: string): AccountInfo | undefined => { const chainId = useSelector(selectChainId); - // TODO: Replace "any" with type - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { addressBook, identities } = useSelector((state: any) => ({ + const { addressBook, internalAccounts } = useSelector((state: RootState) => ({ addressBook: state.engine.backgroundState.AddressBookController.addressBook, - identities: selectIdentities(state), + internalAccounts: selectInternalAccounts(state), })); if (!address) return; @@ -27,11 +21,28 @@ const useExistingAddress = (address?: string): Address | undefined => { const networkAddressBook = addressBook[chainId] || {}; const checksummedAddress = toChecksumAddress(address); - return ( - networkAddressBook?.[checksummedAddress] ?? - identities?.[checksummedAddress] ?? - undefined + const matchingAddressBookEntry: AddressBookEntry | undefined = + networkAddressBook?.[checksummedAddress]; + + if (matchingAddressBookEntry) { + return { + name: matchingAddressBookEntry.name, + address: matchingAddressBookEntry.address, + }; + } + + const accountWithMatchingAddress = internalAccounts.find((account) => + toLowerCaseEquals(account.address, address), ); + + if (accountWithMatchingAddress) { + return { + address: accountWithMatchingAddress.address, + name: accountWithMatchingAddress.metadata.name, + }; + } + + return undefined; }; export default useExistingAddress; diff --git a/app/core/DeeplinkManager/TransactionManager/approveTransaction.test.ts b/app/core/DeeplinkManager/TransactionManager/approveTransaction.test.ts index 757fe2518a6..7bc79fc7750 100644 --- a/app/core/DeeplinkManager/TransactionManager/approveTransaction.test.ts +++ b/app/core/DeeplinkManager/TransactionManager/approveTransaction.test.ts @@ -6,6 +6,15 @@ import Engine from '../../Engine'; import NotificationManager from '../../NotificationManager'; import approveTransaction from './approveTransaction'; import { addTransaction } from '../../../util/transaction-controller'; +import { createMockInternalAccount } from '../../../util/test/accountsControllerTestUtils'; + +const MOCK_SENDER_ADDRESS = '0xMockSenderAddress'; +const MOCK_TARGET_ADDRESS = '0xTargetAddress'; + +const MOCK_INTERNAL_ACCOUNT = createMockInternalAccount( + MOCK_SENDER_ADDRESS, + 'Account 1', +); jest.mock('../../../util/networks'); jest.mock('../../../util/transactions'); @@ -14,11 +23,15 @@ jest.mock('../../Engine', () => ({ context: { NetworkController: { setProviderType: jest.fn() }, PreferencesController: { - state: { selectedAddress: '0xMockSenderAddress' }, + state: { selectedAddress: MOCK_SENDER_ADDRESS }, }, TransactionController: { addTransaction: jest.fn(), }, + AccountsController: { + internalAccounts: MOCK_INTERNAL_ACCOUNT, + getSelectedAccount: jest.fn(() => MOCK_INTERNAL_ACCOUNT), + }, }, })); jest.mock('../../NotificationManager'); @@ -32,7 +45,7 @@ jest.mock('../../../util/transaction-controller', () => ({ const mockEthUrl = { parameters: { uint256: '123', address: '0xMockAddress' }, - target_address: '0xTargetAddress', + target_address: MOCK_TARGET_ADDRESS, chain_id: '1', }; const mockDeeplinkManager = { @@ -96,7 +109,7 @@ describe('approveTransaction', () => { }); it('calls generateApprovalData with the correct parameters', async () => { - spyGetAddress.mockReturnValue('0xMockAddress'); + spyGetAddress.mockResolvedValue('0xMockAddress'); await approveTransaction({ // TODO: Replace "any" with type @@ -127,8 +140,8 @@ describe('approveTransaction', () => { expect(spyAddTransaction).toHaveBeenCalledWith( { - to: '0xTargetAddress', - from: '0xMockSenderAddress', + to: MOCK_TARGET_ADDRESS, + from: MOCK_SENDER_ADDRESS, value: '0x0', data: 'fakeApproveData', }, @@ -157,7 +170,7 @@ describe('approveTransaction', () => { }); it('should call showSimpleNotification with the correct parameters if the spender address is invalid', async () => { - spyGetAddress.mockReturnValue(''); + spyGetAddress.mockResolvedValue(''); await approveTransaction({ // TODO: Replace "any" with type @@ -178,7 +191,7 @@ describe('approveTransaction', () => { }); it('should call navigate with the correct parameters if the spender address is invalid', async () => { - spyGetAddress.mockReturnValue(''); + spyGetAddress.mockResolvedValue(''); await approveTransaction({ // TODO: Replace "any" with type @@ -196,7 +209,7 @@ describe('approveTransaction', () => { }); it('should not call showSimpleNotification if the spender address is valid', async () => { - spyGetAddress.mockReturnValue('0xMockAddress'); + spyGetAddress.mockResolvedValue('0xMockAddress'); await approveTransaction({ // TODO: Replace "any" with type @@ -212,7 +225,7 @@ describe('approveTransaction', () => { }); it('should not call navigate if the spender address is valid', async () => { - spyGetAddress.mockReturnValue('0xMockAddress'); + spyGetAddress.mockResolvedValue('0xMockAddress'); await approveTransaction({ // TODO: Replace "any" with type diff --git a/app/core/DeeplinkManager/TransactionManager/approveTransaction.ts b/app/core/DeeplinkManager/TransactionManager/approveTransaction.ts index e2e37266059..72b5307fd8d 100644 --- a/app/core/DeeplinkManager/TransactionManager/approveTransaction.ts +++ b/app/core/DeeplinkManager/TransactionManager/approveTransaction.ts @@ -8,6 +8,7 @@ import DeeplinkManager from '../DeeplinkManager'; import Engine from '../../Engine'; import NotificationManager from '../../NotificationManager'; import { WalletDevice } from '@metamask/transaction-controller'; +import { toChecksumHexAddress } from '@metamask/controller-utils'; async function approveTransaction({ deeplinkManager, @@ -19,7 +20,7 @@ async function approveTransaction({ origin: string; }) { const { parameters, target_address, chain_id } = ethUrl; - const { PreferencesController, NetworkController } = Engine.context; + const { AccountsController, NetworkController } = Engine.context; if (chain_id) { const newNetworkType = getNetworkTypeById(chain_id); @@ -50,9 +51,11 @@ async function approveTransaction({ deeplinkManager.navigation.navigate('WalletView'); } + const selectedAccount = AccountsController.getSelectedAccount(); + const txParams = { to: target_address.toString(), - from: PreferencesController.state.selectedAddress.toString(), + from: toChecksumHexAddress(selectedAccount.address), value: '0x0', data: generateApprovalData({ spender: spenderAddress, value }), }; diff --git a/app/selectors/preferencesController.ts b/app/selectors/preferencesController.ts index 83dae910b82..7d1b6b9a6dd 100644 --- a/app/selectors/preferencesController.ts +++ b/app/selectors/preferencesController.ts @@ -5,6 +5,9 @@ import { RootState } from '../reducers'; const selectPreferencesControllerState = (state: RootState) => state.engine.backgroundState.PreferencesController; +/** + * @deprecated use selectInternalAccounts rom selectors/accountsController.ts instead + */ export const selectIdentities = createSelector( selectPreferencesControllerState, (preferencesControllerState: PreferencesState) => @@ -18,7 +21,7 @@ export const selectIpfsGateway = createSelector( ); /** - * @deprecated use selectSelectedInternal or selectSelectedInternalAccountChecksummedAddress account from selectors/accountsController.ts + * @deprecated use selectSelectedInternal or selectSelectedInternalAccountChecksummedAddress from selectors/accountsController.ts */ export const selectSelectedAddress = createSelector( selectPreferencesControllerState, diff --git a/app/util/address/index.test.ts b/app/util/address/index.test.ts index a0b6499b17f..e19035c34d2 100644 --- a/app/util/address/index.test.ts +++ b/app/util/address/index.test.ts @@ -1,3 +1,4 @@ +import { NetworkState } from '@metamask/network-controller'; import { isENS, renderSlightlyLongAddress, @@ -35,9 +36,6 @@ describe('isENS', () => { describe('renderSlightlyLongAddress', () => { const mockAddress = '0xC4955C0d639D99699Bfd7Ec54d9FaFEe40e4D272'; - it('should return the address when the address do not exist', () => { - expect(renderSlightlyLongAddress(null)).toBeNull(); - }); it('should return 5 characters before ellipsis and 4 final characters of the address after the ellipsis', () => { expect(renderSlightlyLongAddress(mockAddress).split('.')[0].length).toBe( 24, @@ -171,12 +169,13 @@ describe('getAddress', () => { }); describe('shouldShowBlockExplorer', () => { - const networkConfigurations = { + const networkConfigurations: NetworkState['networkConfigurations'] = { networkId1: { - chainId: '1', + id: 'networkId1', + chainId: '0x1', nickname: 'Main Ethereum Network', + ticker: 'USD', rpcUrl: 'https://mainnet.infura.io/v3/123', - rpcPrefs: {}, }, }; @@ -184,11 +183,11 @@ describe('shouldShowBlockExplorer', () => { const providerType = 'mainnet'; const providerRpcTarget = networkConfigurations.networkId1.rpcUrl; - const result = shouldShowBlockExplorer({ + const result = shouldShowBlockExplorer( providerType, providerRpcTarget, networkConfigurations, - }); + ); expect(result).toBe(true); }); @@ -199,11 +198,11 @@ describe('shouldShowBlockExplorer', () => { const blockExplorerUrl = 'https://rpc.testnet.fantom.network'; networkConfigurations.networkId1.rpcPrefs = { blockExplorerUrl }; - const result = shouldShowBlockExplorer({ + const result = shouldShowBlockExplorer( providerType, providerRpcTarget, networkConfigurations, - }); + ); expect(result).toBe(blockExplorerUrl); }); @@ -211,13 +210,13 @@ describe('shouldShowBlockExplorer', () => { it('returns undefined if block explorer URL is not defined', () => { const providerType = 'rpc'; const providerRpcTarget = networkConfigurations.networkId1.rpcUrl; - networkConfigurations.networkId1.rpcPrefs = {}; + networkConfigurations.networkId1.rpcPrefs = undefined; - const result = shouldShowBlockExplorer({ + const result = shouldShowBlockExplorer( providerType, providerRpcTarget, networkConfigurations, - }); + ); expect(result).toBe(undefined); }); diff --git a/app/util/address/index.js b/app/util/address/index.ts similarity index 74% rename from app/util/address/index.js rename to app/util/address/index.ts index 08aa9c22fb3..010dcebbfe9 100644 --- a/app/util/address/index.js +++ b/app/util/address/index.ts @@ -1,17 +1,16 @@ import { toChecksumAddress, isValidAddress, - isHexString, addHexPrefix, isValidChecksumAddress, + //@ts-expect-error - This error is expected, but ethereumjs-util exports this function isHexPrefixed, } from 'ethereumjs-util'; -import URL from 'url-parse'; import punycode from 'punycode/punycode'; import ExtendedKeyringTypes from '../../constants/keyringTypes'; import Engine from '../../core/Engine'; import { strings } from '../../../locales/i18n'; -import { tlc } from '../general'; +import { tlc, toLowerCaseEquals } from '../general'; import { doENSLookup, doENSReverseLookup, @@ -33,6 +32,12 @@ import TransactionTypes from '../../core/TransactionTypes'; import { selectChainId } from '../../selectors/networkController'; import { store } from '../../store'; import { regex } from '../../../app/util/regex'; +import { InternalAccount } from '@metamask/keyring-api'; +import { AddressBookState } from '@metamask/address-book-controller'; +import { NetworkType, toChecksumHexAddress } from '@metamask/controller-utils'; +import { NetworkState } from '@metamask/network-controller'; +import { AccountImportStrategy } from '@metamask/keyring-controller'; +import { Hex, isHexString } from '@metamask/utils'; const { ASSET: { ERC721, ERC1155 }, @@ -43,7 +48,7 @@ const { * @param {String} address - String corresponding to an address * @returns {String} - String corresponding to full checksummed address */ -export function renderFullAddress(address) { +export function renderFullAddress(address: string) { return address ? toChecksumAddress(address) : strings('transactions.tx_details_not_available'); @@ -55,7 +60,8 @@ export function renderFullAddress(address) { * @param {String} type - Format type * @returns {String} Formatted address */ -export const formatAddress = (rawAddress, type) => { +type FormatAddressType = 'short' | 'mid'; +export const formatAddress = (rawAddress: string, type: FormatAddressType) => { let formattedAddress = rawAddress; if (!isValidAddress(rawAddress)) { @@ -81,7 +87,7 @@ export const formatAddress = (rawAddress, type) => { * Defaults to 4. * @returns {String} - String corresponding to short address format */ -export function renderShortAddress(address, chars = 4) { +export function renderShortAddress(address: string, chars = 4) { if (!address) return address; const checksummedAddress = toChecksumAddress(address); return `${checksummedAddress.substr( @@ -91,11 +97,10 @@ export function renderShortAddress(address, chars = 4) { } export function renderSlightlyLongAddress( - address, + address: string, chars = 4, initialChars = 20, ) { - if (!address) return address; const checksummedAddress = toChecksumAddress(address); return `${checksummedAddress.slice( 0, @@ -104,33 +109,40 @@ export function renderSlightlyLongAddress( } /** - * Returns address name if it's in known identities + * Returns address name if it's in known InternalAccounts * * @param {String} address - String corresponding to an address - * @param {Object} identities - Identities object + * @param {Array} internalAccounts - Array of InternalAccounts objects * @returns {String} - String corresponding to account name. If there is no name, returns the original short format address */ -export function renderAccountName(address, identities) { +export function renderAccountName( + address: string, + internalAccounts: InternalAccount[], +) { const chainId = selectChainId(store.getState()); - address = safeToChecksumAddress(address); - if (identities && address && address in identities) { - const identityName = identities[address].name; + address = toChecksumHexAddress(address); + const account = internalAccounts.find((acc) => + toLowerCaseEquals(acc.address, address), + ); + if (account) { + const identityName = account.metadata.name; const ensName = getCachedENSName(address, chainId) || ''; return isDefaultAccountName(identityName) && ensName ? ensName : identityName; } + return renderShortAddress(address); } /** - * Imports a an account from a private key + * Imports an account from a private key * * @param {String} private_key - String corresponding to a private key * @returns {Promise} - Returns a promise */ -export async function importAccountFromPrivateKey(private_key) { +export async function importAccountFromPrivateKey(private_key: string) { const { KeyringController } = Engine.context; // Import private key let pkey = private_key; @@ -139,8 +151,11 @@ export async function importAccountFromPrivateKey(private_key) { pkey = pkey.substr(2); } const importedAccountAddress = - await KeyringController.importAccountWithStrategy('privateKey', [pkey]); - const checksummedAddress = safeToChecksumAddress(importedAccountAddress); + await KeyringController.importAccountWithStrategy( + AccountImportStrategy.privateKey, + [pkey], + ); + const checksummedAddress = toChecksumHexAddress(importedAccountAddress); return Engine.setSelectedAddress(checksummedAddress); } @@ -150,7 +165,7 @@ export async function importAccountFromPrivateKey(private_key) { * @param {String} address - String corresponding to an address * @returns {Boolean} - Returns a boolean */ -export function isQRHardwareAccount(address) { +export function isQRHardwareAccount(address: string) { if (!isValidHexAddress(address)) return false; const { KeyringController } = Engine.context; @@ -158,7 +173,7 @@ export function isQRHardwareAccount(address) { const qrKeyrings = keyrings.filter( (keyring) => keyring.type === ExtendedKeyringTypes.qr, ); - let qrAccounts = []; + let qrAccounts: string[] = []; for (const qrKeyring of qrKeyrings) { qrAccounts = qrAccounts.concat( qrKeyring.accounts.map((account) => account.toLowerCase()), @@ -173,7 +188,7 @@ export function isQRHardwareAccount(address) { * @param {String} address - String corresponding to an address * @returns {Keyring | undefined} - Returns the keyring of the provided address if keyring found, otherwise returns undefined */ -export function getKeyringByAddress(address) { +export function getKeyringByAddress(address: string) { if (!isValidHexAddress(address)) { return undefined; } @@ -194,11 +209,11 @@ export function getKeyringByAddress(address) { * @returns {Boolean} - Returns a boolean */ export function isHardwareAccount( - address, + address: string, accountTypes = [ExtendedKeyringTypes.qr, ExtendedKeyringTypes.ledger], ) { const keyring = getKeyringByAddress(address); - return keyring && accountTypes.includes(keyring.type); + return keyring && accountTypes.includes(keyring.type as ExtendedKeyringTypes); } /** @@ -207,7 +222,7 @@ export function isHardwareAccount( * @param {String} address - String corresponding to an address * @returns {Boolean} - Returns a boolean */ -export function isExternalHardwareAccount(address) { +export function isExternalHardwareAccount(address: string) { return isHardwareAccount(address, [ExtendedKeyringTypes.ledger]); } @@ -217,7 +232,7 @@ export function isExternalHardwareAccount(address) { * @param {String} address - String corresponding to an address * @returns {String} - Returns address's i18n label text */ -export function getLabelTextByAddress(address) { +export function getLabelTextByAddress(address: string) { if (!address) return null; const keyring = getKeyringByAddress(address); if (keyring) { @@ -239,7 +254,7 @@ export function getLabelTextByAddress(address) { * @param {String} address - String corresponding to an address * @returns {String} - Returns address's account type */ -export function getAddressAccountType(address) { +export function getAddressAccountType(address: string) { if (!isValidHexAddress(address)) { throw new Error(`Invalid address: ${address}`); } @@ -272,7 +287,7 @@ export function getAddressAccountType(address) { * @param {String} name - String corresponding to an ENS name * @returns {boolean} - Returns a boolean indicating if it is valid */ -export function isENS(name = undefined) { +export function isENS(name: string | undefined = undefined) { if (!name) return false; // Checks that the domain consists of at least one valid domain pieces separated by periods, followed by a tld @@ -281,7 +296,7 @@ export function isENS(name = undefined) { const match = punycode.toASCII(name).toLowerCase().match(regex.ensName); const OFFSET = 1; - const index = name && name.lastIndexOf('.'); + const index = name?.lastIndexOf('.'); const tld = index && index >= OFFSET && @@ -296,11 +311,11 @@ export function isENS(name = undefined) { * @param {string} address The 42 character Ethereum address composed of: * 2 ('0x': 2 char hex prefix) + 20 (last 20 bytes of public key) * 2 (as each byte is 2 chars in ascii) */ -export function resemblesAddress(address) { +export function resemblesAddress(address: string) { return address && address.length === 2 + 20 * 2; } -export function safeToChecksumAddress(address) { +export function safeToChecksumAddress(address: string) { if (!address) return undefined; return toChecksumAddress(address); } @@ -317,14 +332,14 @@ export function safeToChecksumAddress(address) { * @param {string} possibleAddress - Input parameter to check against * @param {Object} [options] - options bag * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' - * is prepended to the string + * is prepended to the string * @param {boolean} [options.mixedCaseUseChecksum] - If true will treat mixed - * case addresses as checksum addresses and validate that proper checksum - * format is used + * case addresses as checksum addresses and validate that proper checksum + * format is used * @returns {boolean} whether or not the input is a valid hex address */ export function isValidHexAddress( - possibleAddress, + possibleAddress: string, { allowNonPrefixed = false, mixedCaseUseChecksum = false } = {}, ) { const addressToCheck = allowNonPrefixed @@ -351,21 +366,27 @@ export function isValidHexAddress( * @param {Object} params - Contains multiple variables that are needed to * check if the address is already saved in our contact list or in our accounts * Variables: - * address (String) - Represents the address of the account - * addressBook (Object) - Represents all the contacts that we have saved on the address book - * identities (Object) - Represents our accounts on the current network of the wallet - * chainId (string) - The chain ID for the current selected network + * address (String) - Represents the address of the account + * addressBook (Object) - Represents all the contacts that we have saved on the address book + * internalAccounts (Array) InternalAccount - Represents our accounts on the current network of the wallet + * chainId (string) - The chain ID for the current selected network * @returns String | undefined - When it is saved returns a string "contactAlreadySaved" if it's not reutrn undefined */ -function checkIfAddressAlreadySaved(params) { - const { address, addressBook, chainId, identities } = params; +function checkIfAddressAlreadySaved( + address: string, + addressBook: AddressBookState['addressBook'], + chainId: Hex, + internalAccounts: InternalAccount[], +) { if (address) { const networkAddressBook = addressBook[chainId] || {}; const checksummedResolvedAddress = toChecksumAddress(address); if ( networkAddressBook[checksummedResolvedAddress] || - identities[checksummedResolvedAddress] + internalAccounts.find((account) => + toLowerCaseEquals(account.address, checksummedResolvedAddress), + ) ) { return CONTACT_ALREADY_SAVED; } @@ -379,25 +400,29 @@ function checkIfAddressAlreadySaved(params) { * This function is needed in two place of the app, SendTo of SendFlow in order to send tokes and * is present in ContactForm of Contatcs, in order to add a new contact * Variables: - * toAccount (String) - Represents the account address or ens - * chainId (String) - Represents the current chain ID - * addressBook (Object) - Represents all the contacts that we have saved on the address book - * identities (Object) - Represents our accounts on the current network of the wallet - * providerType (String) - Represents the network name + * toAccount (String) - Represents the account address or ens + * chainId (Hex String) - Represents the current chain ID + * addressBook (Object) - Represents all the contacts that we have saved on the address book + * internalAccounts (Array) InternalAccount - Represents our accounts on the current network of the wallet + * providerType (String) - Represents the network name * @returns the variables that are needed for updating the state of the two flows metioned above * Variables: - * addressError (String) - Contains the message or the error - * toEnsName (String) - Represents the ens name of the destination account - * addressReady (Bollean) - Represents if the address is validated or not - * toEnsAddress (String) - Represents the address of the ens inserted - * addToAddressToAddressBook (Boolean) - Represents if the address it can be add to the address book - * toAddressName (String) - Represents the address of the destination account - * errorContinue (Boolean) - Represents if with one error we can proceed or not to the next step if we wish - * confusableCollection (Object) - Represents one array with the confusable characters of the ens + * addressError (String) - Contains the message or the error + * toEnsName (String) - Represents the ens name of the destination account + * addressReady (Bollean) - Represents if the address is validated or not + * toEnsAddress (String) - Represents the address of the ens inserted + * addToAddressToAddressBook (Boolean) - Represents if the address it can be add to the address book + * toAddressName (String) - Represents the address of the destination account + * errorContinue (Boolean) - Represents if with one error we can proceed or not to the next step if we wish + * confusableCollection (Object) - Represents one array with the confusable characters of the ens * */ -export async function validateAddressOrENS(params) { - const { toAccount, addressBook, identities, chainId } = params; +export async function validateAddressOrENS( + toAccount: string, + addressBook: AddressBookState['addressBook'], + internalAccounts: InternalAccount[], + chainId: Hex, +) { const { AssetsContractController } = Engine.context; let addressError, @@ -410,15 +435,20 @@ export async function validateAddressOrENS(params) { let [addressReady, addToAddressToAddressBook] = [false, false]; if (isValidHexAddress(toAccount, { mixedCaseUseChecksum: true })) { - const contactAlreadySaved = checkIfAddressAlreadySaved({ - address: toAccount, + const contactAlreadySaved = checkIfAddressAlreadySaved( + toAccount, addressBook, chainId, - identities, - }); + internalAccounts, + ); if (contactAlreadySaved) { - addressError = checkIfAddressAlreadySaved(toAccount); + addressError = checkIfAddressAlreadySaved( + toAccount, + addressBook, + chainId, + internalAccounts, + ); } const checksummedAddress = toChecksumAddress(toAccount); addressReady = true; @@ -467,12 +497,12 @@ export async function validateAddressOrENS(params) { toEnsName = toAccount; confusableCollection = collectConfusables(toEnsName); const resolvedAddress = await doENSLookup(toAccount, chainId); - const contactAlreadySaved = checkIfAddressAlreadySaved({ - address: resolvedAddress, + const contactAlreadySaved = checkIfAddressAlreadySaved( + resolvedAddress, addressBook, chainId, - identities, - }); + internalAccounts, + ); if (resolvedAddress) { if (!contactAlreadySaved) { @@ -508,10 +538,10 @@ export async function validateAddressOrENS(params) { * @param {string} input - a random string. * @returns {boolean} indicates if the string is a valid input. */ -export function isValidAddressInputViaQRCode(input) { +export function isValidAddressInputViaQRCode(input: string) { if (input.includes(PROTOCOLS.ETHEREUM)) { const { pathname } = new URL(input); - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [address, _] = pathname.split('@'); return isValidHexAddress(address); } @@ -523,7 +553,7 @@ export function isValidAddressInputViaQRCode(input) { * @param {string} str * @returns {string} */ -export const stripHexPrefix = (str) => { +export const stripHexPrefix = (str: string) => { if (typeof str !== 'string') { return str; } @@ -537,7 +567,10 @@ export const stripHexPrefix = (str) => { * @param {String} chainId - The chain ID for the given address * @returns {String} - Address or null */ -export async function getAddress(toAccount, chainId) { +export async function getAddress( + toAccount: string, + chainId: string, +): Promise { if (isENS(toAccount)) { return await doENSLookup(toAccount, chainId); } @@ -547,7 +580,11 @@ export async function getAddress(toAccount, chainId) { return null; } -export const getTokenDetails = async (tokenAddress, userAddress, tokenId) => { +export const getTokenDetails = async ( + tokenAddress: string, + userAddress: string, + tokenId: string, +) => { const { AssetsContractController } = Engine.context; const tokenData = await AssetsContractController.getTokenStandardAndDetails( tokenAddress, @@ -569,11 +606,11 @@ export const getTokenDetails = async (tokenAddress, userAddress, tokenId) => { }; }; -export const shouldShowBlockExplorer = ({ - providerType, - providerRpcTarget, - networkConfigurations, -}) => { +export const shouldShowBlockExplorer = ( + providerType: NetworkType, + providerRpcTarget: string, + networkConfigurations: NetworkState['networkConfigurations'], +) => { if (providerType === RPC) { return findBlockExplorerForRpc(providerRpcTarget, networkConfigurations); } diff --git a/app/util/metrics/trackDappViewedEvent/index.test.ts b/app/util/metrics/trackDappViewedEvent/index.test.ts index f40d0796841..6eada90e61b 100644 --- a/app/util/metrics/trackDappViewedEvent/index.test.ts +++ b/app/util/metrics/trackDappViewedEvent/index.test.ts @@ -1,5 +1,16 @@ import trackDappViewedEvent from './index'; import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics'; +import { createMockAccountsControllerState } from '../../test/accountsControllerTestUtils'; +import { MOCK_KEYRING_CONTROLLER } from '../../../selectors/keyringController/testUtils'; + +const MOCK_ADDRESS_1 = '0xe64dD0AB5ad7e8C5F2bf6Ce75C34e187af8b920A'; +const MOCK_ADDRESS_2 = '0x519d2CE57898513F676a5C3b66496c3C394c9CC7'; + +const MOCK_DEFAULT_ACCOUNTS_CONTROLLER_STATE = + createMockAccountsControllerState([MOCK_ADDRESS_1, MOCK_ADDRESS_2]); + +const MOCK_ACCOUNTS_CONTROLLER_STATE_WITH_ONE_ACCOUNT = + createMockAccountsControllerState([MOCK_ADDRESS_1]); jest.mock('../../../core/Analytics/MetaMetrics'); // Need to mock this module since it uses store.getState, which interferes with the mocks from this test file. @@ -24,9 +35,8 @@ jest.mock('../../../store', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true, '0x2': true }, - }, + AccountsController: MOCK_DEFAULT_ACCOUNTS_CONTROLLER_STATE, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); @@ -51,9 +61,8 @@ describe('trackDappViewedEvent', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true, '0x2': true }, - }, + AccountsController: MOCK_DEFAULT_ACCOUNTS_CONTROLLER_STATE, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); @@ -84,9 +93,8 @@ describe('trackDappViewedEvent', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true, '0x2': true }, - }, + AccountsController: MOCK_DEFAULT_ACCOUNTS_CONTROLLER_STATE, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); @@ -117,9 +125,8 @@ describe('trackDappViewedEvent', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true, '0x2': true }, - }, + AccountsController: MOCK_DEFAULT_ACCOUNTS_CONTROLLER_STATE, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); @@ -150,9 +157,8 @@ describe('trackDappViewedEvent', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true }, - }, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE_WITH_ONE_ACCOUNT, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); @@ -183,9 +189,8 @@ describe('trackDappViewedEvent', () => { }, engine: { backgroundState: { - PreferencesController: { - identities: { '0x1': true }, - }, + AccountsController: MOCK_ACCOUNTS_CONTROLLER_STATE_WITH_ONE_ACCOUNT, + KeyringController: MOCK_KEYRING_CONTROLLER, }, }, })); diff --git a/app/util/metrics/trackDappViewedEvent/index.ts b/app/util/metrics/trackDappViewedEvent/index.ts index 20e1dccd67d..ebdf2bb4634 100644 --- a/app/util/metrics/trackDappViewedEvent/index.ts +++ b/app/util/metrics/trackDappViewedEvent/index.ts @@ -1,8 +1,8 @@ import { store } from '../../../store'; -import { selectIdentities } from '../../../selectors/preferencesController'; import { addToViewedDapp } from '../../../actions/browser'; import { MetaMetrics, MetaMetricsEvents } from '../../../core/Analytics'; import { prefixUrlWithProtocol } from '../../browser'; +import { selectInternalAccounts } from '../../../selectors/accountsController'; /** * Tracks Dapp viewed event @@ -23,8 +23,8 @@ const trackDappViewedEvent = ({ const visitedDappsByHostname = store.getState().browser.visitedDappsByHostname; const isFirstVisit = !visitedDappsByHostname[hostname]; - const accountByAddress = selectIdentities(store.getState()); - const numberOfWalletAccounts = Object.keys(accountByAddress).length; + const internalAccounts = selectInternalAccounts(store.getState()); + const numberOfWalletAccounts = Object.keys(internalAccounts).length; // Add Dapp hostname to viewed dapps store.dispatch(addToViewedDapp(hostname)); diff --git a/app/util/transactions/index.js b/app/util/transactions/index.js index 670b57c606f..7ce75b038c7 100644 --- a/app/util/transactions/index.js +++ b/app/util/transactions/index.js @@ -217,7 +217,7 @@ export function isApprovalTransaction(data) { * Generates ERC20 approval data * * @param {object} opts - Object containing spender address, value and data - * @param {string} opts.spender - The address of the spender + * @param {string | null} opts.spender - The address of the spender * @param {string} opts.value - The amount of tokens to be approved or increased * @param {string} [opts.data] - The data of the transaction * @returns {String} - String containing the generated data, by default for approve method @@ -517,7 +517,7 @@ export function getEther(ticker) { * @param {object} config.addressBook - Object of address book entries * @param {string} config.chainId - network id * @param {string} config.toAddress - hex address of tx recipient - * @param {object} config.identities - object of identities + * @param {array} config.identities - array of accounts objects from AccountsController * @param {string} config.ensRecipient - name of ens recipient * @returns {string} - recipient name */ @@ -525,7 +525,7 @@ export function getTransactionToName({ addressBook, chainId, toAddress, - identities, + internalAccounts, ensRecipient, }) { if (ensRecipient) { @@ -535,11 +535,19 @@ export function getTransactionToName({ const networkAddressBook = addressBook[chainId]; const checksummedToAddress = toChecksumAddress(toAddress); + // Convert internalAccounts array to a map for quick lookup + const internalAccountsMap = internalAccounts.reduce((acc, account) => { + acc[toChecksumAddress(account.address)] = account; + return acc; + }, {}); + + const matchingAccount = internalAccountsMap[checksummedToAddress]; + const transactionToName = (networkAddressBook && networkAddressBook[checksummedToAddress] && networkAddressBook[checksummedToAddress].name) || - (identities[checksummedToAddress] && identities[checksummedToAddress].name); + (matchingAccount && matchingAccount.metadata.name); return transactionToName; } diff --git a/e2e/fixtures/fixture-builder.js b/e2e/fixtures/fixture-builder.js index 5f81a06d709..3e35dc36b61 100644 --- a/e2e/fixtures/fixture-builder.js +++ b/e2e/fixtures/fixture-builder.js @@ -208,11 +208,12 @@ class FixtureBuilder { AccountsController: { internalAccounts: { accounts: { - 1: { + '4d7a5e0b-b261-4aed-8126-43972b0fa0a1': { address: '0x76cf1cdd1fcc252442b50d6e97207228aa4aefc3', - id: '1', + id: '4d7a5e0b-b261-4aed-8126-43972b0fa0a1', metadata: { name: 'Account 1', + importTime: 1684232000456, keyring: { type: 'HD Key Tree', }, @@ -229,7 +230,7 @@ class FixtureBuilder { type: 'eip155:eoa', }, }, - selectedAccount: 1, + selectedAccount: '4d7a5e0b-b261-4aed-8126-43972b0fa0a1', }, }, PreferencesController: { diff --git a/yarn.lock b/yarn.lock index a90c025785b..876c7fdca9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9057,9 +9057,9 @@ integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/punycode@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.0.tgz#89e4f3d09b3f92e87a80505af19be7e0c31d4e83" - integrity sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g== + version "2.1.4" + resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.4.tgz#96f8a47f1ee9fb0d0def5557fe80fac532f966fa" + integrity sha512-trzh6NzBnq8yw5e35f8xe8VTYjqM3NE7bohBtvDVf/dtUer3zYTLK1Ka3DG3p7bdtoaOHZucma6FfVKlQ134pQ== "@types/puppeteer-core@^5.4.0": version "5.4.0" @@ -23907,9 +23907,9 @@ punycode@^1.3.2, punycode@^1.4.1: integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== puppeteer-core@13.1.3: version "13.1.3"