From 16cc0736984791e4e66af3af5daaf4094b9d84b8 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Tue, 9 Jul 2024 16:06:39 +0200 Subject: [PATCH 01/11] feat(btc): add bitcoin testnet account creation menu entry --- app/_locales/en/messages.json | 3 ++ .../account-list-menu/account-list-menu.js | 48 +++++++++++++++++++ .../create-btc-account.test.tsx | 2 +- .../create-btc-account/create-btc-account.tsx | 15 +++++- 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8683dc1bb47d..aa37bd15e041 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -283,6 +283,9 @@ "addNewBitcoinAccount": { "message": "Add a new Bitcoin account (Beta)" }, + "addNewBitcoinTestnetAccount": { + "message": "Add a new Bitcoin account (testnet)" + }, "addNewToken": { "message": "Add new token" }, diff --git a/ui/components/multichain/account-list-menu/account-list-menu.js b/ui/components/multichain/account-list-menu/account-list-menu.js index 6e4c7490541c..48d4c669f357 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.js +++ b/ui/components/multichain/account-list-menu/account-list-menu.js @@ -69,6 +69,7 @@ import { getAccountLabel } from '../../../helpers/utils/accounts'; import { hasCreatedBtcMainnetAccount } from '../../../selectors/accounts'; ///: END:ONLY_INCLUDE_IF import { HiddenAccountList } from './hidden-account-list'; +import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; const ACTION_MODES = { // Displays the search box and account list @@ -80,6 +81,8 @@ const ACTION_MODES = { ///: BEGIN:ONLY_INCLUDE_IF(build-flask) // Displays the add account form controls (for bitcoin account) ADD_BITCOIN: 'add-bitcoin', + // Same but for testnet + ADD_BITCOIN_TESTNET: 'add-bitcoin-testnet', ///: END:ONLY_INCLUDE_IF // Displays the import account form controls IMPORT: 'import', @@ -99,6 +102,8 @@ export const getActionTitle = (t, actionMode) => { ///: BEGIN:ONLY_INCLUDE_IF(build-flask) case ACTION_MODES.ADD_BITCOIN: return t('addAccount'); + case ACTION_MODES.ADD_BITCOIN_TESTNET: + return t('addAccount'); ///: END:ONLY_INCLUDE_IF case ACTION_MODES.MENU: return t('addAccount'); @@ -161,6 +166,10 @@ export const AccountListMenu = ({ ); ///: END:ONLY_INCLUDE_IF + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + const isBtcTestnetEnabled = true; // TODO: Use a feature flag for this? + ///: END:ONLY_INCLUDE_IF + const [searchQuery, setSearchQuery] = useState(''); const [actionMode, setActionMode] = useState(ACTION_MODES.LIST); @@ -219,10 +228,33 @@ export const AccountListMenu = ({ ) : null} { + // Bitcoin mainnet: ///: BEGIN:ONLY_INCLUDE_IF(build-flask) bitcoinSupportEnabled && actionMode === ACTION_MODES.ADD_BITCOIN ? ( { + if (confirmed) { + onClose(); + } else { + setActionMode(ACTION_MODES.LIST); + } + }} + /> + + ) : null + ///: END:ONLY_INCLUDE_IF + } + { + // Bitcoin testnet: + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + isBtcTestnetEnabled && actionMode === ACTION_MODES.ADD_BITCOIN_TESTNET ? ( + + { if (confirmed) { onClose(); @@ -303,6 +335,22 @@ export const AccountListMenu = ({ ) : null ///: END:ONLY_INCLUDE_IF } + { + ///: BEGIN:ONLY_INCLUDE_IF(build-flask) + + { + setActionMode(ACTION_MODES.ADD_BITCOIN_TESTNET); + }} + data-testid="multichain-account-menu-popover-add-account-testnet" + > + {t('addNewBitcoinTestnetAccount')} + + + ///: END:ONLY_INCLUDE_IF + } { const store = configureStore(mockState); - return renderWithProvider(, store); + return renderWithProvider(, store); }; const ACCOUNT_NAME = 'Bitcoin Account'; diff --git a/ui/components/multichain/create-btc-account/create-btc-account.tsx b/ui/components/multichain/create-btc-account/create-btc-account.tsx index 40c8bdb169e7..f5220c41e389 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.tsx +++ b/ui/components/multichain/create-btc-account/create-btc-account.tsx @@ -8,16 +8,27 @@ import { setAccountLabel, forceUpdateMetamaskState, } from '../../../store/actions'; +import { CaipChainId } from '@metamask/utils'; type CreateBtcAccountOptions = { /** * Callback called once the account has been created */ onActionComplete: (completed: boolean) => Promise; + /** + * CAIP-2 chain ID + */ + network: MultichainNetworks, + /** + * Default account name + */ + defaultAccountName?: string, }; export const CreateBtcAccount = ({ onActionComplete, + defaultAccountName, + network, }: CreateBtcAccountOptions) => { const dispatch = useDispatch(); @@ -25,7 +36,7 @@ export const CreateBtcAccount = ({ // Trigger the Snap account creation flow const client = new KeyringClient(new BitcoinWalletSnapSender()); const account = await client.createAccount({ - scope: MultichainNetworks.BITCOIN, + scope: network, }); // TODO: Use the new Snap account creation flow that also include account renaming @@ -45,7 +56,7 @@ export const CreateBtcAccount = ({ }; const getNextAvailableAccountName = async (_accounts: InternalAccount[]) => { - return 'Bitcoin Account'; + return defaultAccountName ?? 'Bitcoin Account'; }; return ( From 2e3cd9a0512c0a9e534b2292d9ab722fb8ffeea1 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Thu, 11 Jul 2024 16:23:25 +0200 Subject: [PATCH 02/11] feat(btc): add bitcoin testnet account creation feature flag --- app/_locales/en/messages.json | 11 +++- app/scripts/controllers/preferences.js | 15 +++++- app/scripts/lib/setupSentry.js | 1 + app/scripts/metamask-controller.js | 4 ++ test/data/mock-state.json | 1 + .../account-list-menu/account-list-menu.js | 52 +++++++++++-------- .../create-btc-account.test.tsx | 5 +- .../create-btc-account/create-btc-account.tsx | 5 +- .../experimental-tab.component.tsx | 20 ++++++- .../experimental-tab.container.ts | 5 ++ ui/selectors/accounts.test.ts | 37 +++++++++++++ ui/selectors/accounts.ts | 22 ++++++-- ui/selectors/selectors.js | 10 ++++ ui/store/actions.ts | 8 +++ 14 files changed, 163 insertions(+), 33 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index aa37bd15e041..17f02584d172 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -284,7 +284,7 @@ "message": "Add a new Bitcoin account (Beta)" }, "addNewBitcoinTestnetAccount": { - "message": "Add a new Bitcoin account (testnet)" + "message": "Add a new Bitcoin account (Testnet)" }, "addNewToken": { "message": "Add new token" @@ -771,6 +771,15 @@ "bitcoinSupportToggleTitle": { "message": "Enable \"Add a new Bitcoin account (Beta)\"" }, + "bitcoinTestnetSupportSectionTitle": { + "message": "Bitcoin" + }, + "bitcoinTestnetSupportToggleDescription": { + "message": "Turning on this feature will give you the option to add a Bitcoin Account for test network. " + }, + "bitcoinTestnetSupportToggleTitle": { + "message": "Enable \"Add a new Bitcoin account (Testnet)\"" + }, "blockExplorerAccountAction": { "message": "Account", "description": "This is used with viewOnEtherscan and viewInExplorer e.g View Account in Explorer" diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index aa14bc84a1a4..6342dfd88d29 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -62,6 +62,7 @@ export default class PreferencesController { openSeaEnabled: true, // todo set this to true securityAlertsEnabled: true, bitcoinSupportEnabled: false, + bitcoinTestnetSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: false, ///: END:ONLY_INCLUDE_IF @@ -295,7 +296,7 @@ export default class PreferencesController { * Setter for the `bitcoinSupportEnabled` property. * * @param {boolean} bitcoinSupportEnabled - Whether or not the user wants to - * enable the "Add a new Bitcoin account" button. + * enable the "Add a new Bitcoin account (Beta)" button. */ setBitcoinSupportEnabled(bitcoinSupportEnabled) { this.store.updateState({ @@ -303,6 +304,18 @@ export default class PreferencesController { }); } + /** + * Setter for the `bitcoinTestnetSupportEnabled` property. + * + * @param {boolean} bitcoinTestnetSupportEnabled - Whether or not the user wants to + * enable the "Add a new Bitcoin testnet account (Beta)" button. + */ + setBitcoinTestnetSupportEnabled(bitcoinTestnetSupportEnabled) { + this.store.updateState({ + bitcoinTestnetSupportEnabled, + }); + } + /** * Setter for the `useExternalNameSources` property * diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index 2d0df8f78994..b9687cd2cad5 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -425,6 +425,7 @@ export const SENTRY_UI_STATE = { confirmationExchangeRates: true, useSafeChainsListValidation: true, bitcoinSupportEnabled: false, + bitcoinTestnetSupportEnabled: false, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: false, snapsAddSnapAccountModalDismissed: false, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index e24d140b5575..a12932bffa9b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3021,6 +3021,10 @@ export default class MetamaskController extends EventEmitter { preferencesController.setBitcoinSupportEnabled.bind( preferencesController, ), + setBitcoinTestnetSupportEnabled: + preferencesController.setBitcoinTestnetSupportEnabled.bind( + preferencesController, + ), setUseExternalNameSources: preferencesController.setUseExternalNameSources.bind( preferencesController, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 6ec7a271f2c1..ca7bccddb53f 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -1881,6 +1881,7 @@ ], "addSnapAccountEnabled": false, "bitcoinSupportEnabled": false, + "bitcoinTestnetSupportEnabled": false, "pendingApprovals": { "testApprovalId": { "id": "testApprovalId", diff --git a/ui/components/multichain/account-list-menu/account-list-menu.js b/ui/components/multichain/account-list-menu/account-list-menu.js index 48d4c669f357..70555bb0f0ea 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.js +++ b/ui/components/multichain/account-list-menu/account-list-menu.js @@ -48,6 +48,7 @@ import { ///: END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(build-flask) getIsBitcoinSupportEnabled, + getIsBitcoinTestnetSupportEnabled, ///: END:ONLY_INCLUDE_IF } from '../../../selectors'; import { setSelectedAccount } from '../../../store/actions'; @@ -66,10 +67,13 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { getAccountLabel } from '../../../helpers/utils/accounts'; ///: BEGIN:ONLY_INCLUDE_IF(build-flask) -import { hasCreatedBtcMainnetAccount } from '../../../selectors/accounts'; +import { + hasCreatedBtcMainnetAccount, + hasCreatedBtcTestnetAccount, +} from '../../../selectors/accounts'; ///: END:ONLY_INCLUDE_IF -import { HiddenAccountList } from './hidden-account-list'; import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; +import { HiddenAccountList } from './hidden-account-list'; const ACTION_MODES = { // Displays the search box and account list @@ -161,13 +165,15 @@ export const AccountListMenu = ({ ///: END:ONLY_INCLUDE_IF ///: BEGIN:ONLY_INCLUDE_IF(build-flask) const bitcoinSupportEnabled = useSelector(getIsBitcoinSupportEnabled); + const bitcoinTestnetSupportEnabled = useSelector( + getIsBitcoinTestnetSupportEnabled, + ); const isBtcMainnetAccountAlreadyCreated = useSelector( hasCreatedBtcMainnetAccount, ); - ///: END:ONLY_INCLUDE_IF - - ///: BEGIN:ONLY_INCLUDE_IF(build-flask) - const isBtcTestnetEnabled = true; // TODO: Use a feature flag for this? + const isBtcTestnetAccountAlreadyCreated = useSelector( + hasCreatedBtcTestnetAccount, + ); ///: END:ONLY_INCLUDE_IF const [searchQuery, setSearchQuery] = useState(''); @@ -233,7 +239,7 @@ export const AccountListMenu = ({ bitcoinSupportEnabled && actionMode === ACTION_MODES.ADD_BITCOIN ? ( { if (confirmed) { @@ -250,10 +256,11 @@ export const AccountListMenu = ({ { // Bitcoin testnet: ///: BEGIN:ONLY_INCLUDE_IF(build-flask) - isBtcTestnetEnabled && actionMode === ACTION_MODES.ADD_BITCOIN_TESTNET ? ( + bitcoinTestnetSupportEnabled && + actionMode === ACTION_MODES.ADD_BITCOIN_TESTNET ? ( { if (confirmed) { @@ -337,18 +344,21 @@ export const AccountListMenu = ({ } { ///: BEGIN:ONLY_INCLUDE_IF(build-flask) - - { - setActionMode(ACTION_MODES.ADD_BITCOIN_TESTNET); - }} - data-testid="multichain-account-menu-popover-add-account-testnet" - > - {t('addNewBitcoinTestnetAccount')} - - + bitcoinTestnetSupportEnabled ? ( + + { + setActionMode(ACTION_MODES.ADD_BITCOIN_TESTNET); + }} + data-testid="multichain-account-menu-popover-add-account-testnet" + > + {t('addNewBitcoinTestnetAccount')} + + + ) : null ///: END:ONLY_INCLUDE_IF } diff --git a/ui/components/multichain/create-btc-account/create-btc-account.test.tsx b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx index 6439ad6e430a..9c21744188d6 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.test.tsx +++ b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx @@ -10,7 +10,10 @@ import { CreateBtcAccount } from '.'; const render = (props = { onActionComplete: jest.fn() }) => { const store = configureStore(mockState); - return renderWithProvider(, store); + return renderWithProvider( + , + store, + ); }; const ACCOUNT_NAME = 'Bitcoin Account'; diff --git a/ui/components/multichain/create-btc-account/create-btc-account.tsx b/ui/components/multichain/create-btc-account/create-btc-account.tsx index f5220c41e389..524eac24a368 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.tsx +++ b/ui/components/multichain/create-btc-account/create-btc-account.tsx @@ -8,7 +8,6 @@ import { setAccountLabel, forceUpdateMetamaskState, } from '../../../store/actions'; -import { CaipChainId } from '@metamask/utils'; type CreateBtcAccountOptions = { /** @@ -18,11 +17,11 @@ type CreateBtcAccountOptions = { /** * CAIP-2 chain ID */ - network: MultichainNetworks, + network: MultichainNetworks; /** * Default account name */ - defaultAccountName?: string, + defaultAccountName?: string; }; export const CreateBtcAccount = ({ diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx index 04f4de68f608..61bd3b4b0a90 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx @@ -32,6 +32,8 @@ import { type ExperimentalTabProps = { bitcoinSupportEnabled: boolean; setBitcoinSupportEnabled: (value: boolean) => void; + bitcoinTestnetSupportEnabled: boolean; + setBitcoinTestnetSupportEnabled: (value: boolean) => void; ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: boolean; setAddSnapAccountEnabled: (value: boolean) => void; @@ -241,7 +243,12 @@ export default class ExperimentalTab extends PureComponent // we should remove it for the feature release renderBitcoinSupport() { const { t, trackEvent } = this.context; - const { bitcoinSupportEnabled, setBitcoinSupportEnabled } = this.props; + const { + bitcoinSupportEnabled, + setBitcoinSupportEnabled, + bitcoinTestnetSupportEnabled, + setBitcoinTestnetSupportEnabled, + } = this.props; return ( <> @@ -272,6 +279,17 @@ export default class ExperimentalTab extends PureComponent toggleOffLabel: t('off'), toggleOnLabel: t('on'), })} + {this.renderToggleSection({ + title: t('bitcoinTestnetSupportToggleTitle'), + description: t('bitcoinTestnetSupportToggleDescription'), + toggleValue: bitcoinTestnetSupportEnabled, + toggleCallback: (value) => { + setBitcoinTestnetSupportEnabled(!value); + }, + toggleDataTestId: 'bitcoin-testnet-accounts-toggle', + toggleOffLabel: t('off'), + toggleOnLabel: t('on'), + })} ); } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.ts b/ui/pages/settings/experimental-tab/experimental-tab.container.ts index 7137d1665692..ae2c34f46f7e 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.ts +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.ts @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { setBitcoinSupportEnabled, + setBitcoinTestnetSupportEnabled, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) setAddSnapAccountEnabled, ///: END:ONLY_INCLUDE_IF @@ -13,6 +14,7 @@ import { } from '../../../store/actions'; import { getIsBitcoinSupportEnabled, + getIsBitcoinTestnetSupportEnabled, ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) getIsAddSnapAccountEnabled, ///: END:ONLY_INCLUDE_IF @@ -32,6 +34,7 @@ const mapStateToProps = (state: MetaMaskReduxState) => { const featureNotificationsEnabled = getFeatureNotificationsEnabled(state); return { bitcoinSupportEnabled: getIsBitcoinSupportEnabled(state), + bitcoinTestnetSupportEnabled: getIsBitcoinTestnetSupportEnabled(state), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) addSnapAccountEnabled: getIsAddSnapAccountEnabled(state), ///: END:ONLY_INCLUDE_IF @@ -46,6 +49,8 @@ const mapDispatchToProps = (dispatch: MetaMaskReduxDispatch) => { return { setBitcoinSupportEnabled: (value: boolean) => setBitcoinSupportEnabled(value), + setBitcoinTestnetSupportEnabled: (value: boolean) => + setBitcoinTestnetSupportEnabled(value), ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) setAddSnapAccountEnabled: (value: boolean) => setAddSnapAccountEnabled(value), diff --git a/ui/selectors/accounts.test.ts b/ui/selectors/accounts.test.ts index 911cc0f48c1e..7b25234ab783 100644 --- a/ui/selectors/accounts.test.ts +++ b/ui/selectors/accounts.test.ts @@ -3,12 +3,14 @@ import { MOCK_ACCOUNT_EOA, MOCK_ACCOUNT_ERC4337, MOCK_ACCOUNT_BIP122_P2WPKH, + MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET, } from '../../test/data/mock-accounts'; import { AccountsState, isSelectedInternalAccountEth, isSelectedInternalAccountBtc, hasCreatedBtcMainnetAccount, + hasCreatedBtcTestnetAccount, } from './accounts'; const MOCK_STATE: AccountsState = { @@ -107,4 +109,39 @@ describe('Accounts Selectors', () => { expect(isSelectedInternalAccountBtc(state)).toBe(false); }); }); + + describe('hasCreatedBtcTestnetAccount', () => { + it('returns true if the BTC testnet account has been created', () => { + const state: AccountsState = { + metamask: { + // No-op for this test, but might be required in the future: + ...MOCK_STATE.metamask, + internalAccounts: { + selectedAccount: MOCK_ACCOUNT_BIP122_P2WPKH.id, + accounts: [ + MOCK_ACCOUNT_BIP122_P2WPKH, + MOCK_ACCOUNT_BIP122_P2WPKH_TESTNET, + ], + }, + }, + }; + + expect(hasCreatedBtcTestnetAccount(state)).toBe(true); + }); + + it('returns false if the BTC testnet account has not been created yet', () => { + const state: AccountsState = { + metamask: { + // No-op for this test, but might be required in the future: + ...MOCK_STATE.metamask, + internalAccounts: { + selectedAccount: MOCK_ACCOUNT_BIP122_P2WPKH.id, + accounts: [MOCK_ACCOUNT_BIP122_P2WPKH], + }, + }, + }; + + expect(isSelectedInternalAccountBtc(state)).toBe(false); + }); + }); }); diff --git a/ui/selectors/accounts.ts b/ui/selectors/accounts.ts index 9500f4e5ff3d..6f915858624c 100644 --- a/ui/selectors/accounts.ts +++ b/ui/selectors/accounts.ts @@ -4,7 +4,10 @@ import { InternalAccount, } from '@metamask/keyring-api'; import { AccountsControllerState } from '@metamask/accounts-controller'; -import { isBtcMainnetAddress } from '../../shared/lib/multichain'; +import { + isBtcMainnetAddress, + isBtcTestnetAddress, +} from '../../shared/lib/multichain'; import { getSelectedInternalAccount, getInternalAccounts } from './selectors'; export type AccountsState = { @@ -28,11 +31,20 @@ export function isSelectedInternalAccountBtc(state: AccountsState) { return isBtcAccount(getSelectedInternalAccount(state)); } -export function hasCreatedBtcMainnetAccount(state: AccountsState) { +function hasCreatedBtcAccount( + state: AccountsState, + isAddress: (address: string) => boolean, +) { const accounts = getInternalAccounts(state); return accounts.some((account) => { - // Since we might wanna support testnet accounts later, we do - // want to make this one very explicit and check for mainnet addresses! - return isBtcAccount(account) && isBtcMainnetAddress(account.address); + return isBtcAccount(account) && isAddress(account.address); }); } + +export function hasCreatedBtcMainnetAccount(state: AccountsState) { + return hasCreatedBtcAccount(state, isBtcMainnetAddress); +} + +export function hasCreatedBtcTestnetAccount(state: AccountsState) { + return hasCreatedBtcAccount(state, isBtcTestnetAddress); +} diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index f28d0507265b..36c14b4f77cd 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -2303,6 +2303,16 @@ export function getIsBitcoinSupportEnabled(state) { return state.metamask.bitcoinSupportEnabled; } +/** + * Get the state of the `bitcoinTestnetSupportEnabled` flag. + * + * @param {*} state + * @returns The state of the `bitcoinTestnetSupportEnabled` flag. + */ +export function getIsBitcoinTestnetSupportEnabled(state) { + return state.metamask.bitcoinTestnetSupportEnabled; +} + export function getIsCustomNetwork(state) { const chainId = getCurrentChainId(state); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 4997a6ff8819..7493ee03ba3f 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -4971,6 +4971,14 @@ export async function setBitcoinSupportEnabled(value: boolean) { } } +export async function setBitcoinTestnetSupportEnabled(value: boolean) { + try { + await submitRequestToBackground('setBitcoinTestnetSupportEnabled', [value]); + } catch (error) { + logErrorWithMessage(error); + } +} + ///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps) export async function setAddSnapAccountEnabled(value: boolean): Promise { try { From 7ef4f82136aaf2b6a086130f435f49255299ab64 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Thu, 11 Jul 2024 16:51:10 +0200 Subject: [PATCH 03/11] chore: fix misplaced code fence --- ui/components/multichain/account-list-menu/account-list-menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/multichain/account-list-menu/account-list-menu.js b/ui/components/multichain/account-list-menu/account-list-menu.js index 70555bb0f0ea..6e27dd7a6187 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.js +++ b/ui/components/multichain/account-list-menu/account-list-menu.js @@ -71,8 +71,8 @@ import { hasCreatedBtcMainnetAccount, hasCreatedBtcTestnetAccount, } from '../../../selectors/accounts'; -///: END:ONLY_INCLUDE_IF import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; +///: END:ONLY_INCLUDE_IF import { HiddenAccountList } from './hidden-account-list'; const ACTION_MODES = { From 80729665049ebdffc2cd801311a4e7debef70c4a Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Thu, 11 Jul 2024 17:50:39 +0200 Subject: [PATCH 04/11] test: fix experimental-tab test (now had an additional toggle) --- ui/pages/settings/experimental-tab/experimental-tab.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/settings/experimental-tab/experimental-tab.test.js b/ui/pages/settings/experimental-tab/experimental-tab.test.js index ef519bca6df2..3abefb3a58df 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.test.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.test.js @@ -30,7 +30,7 @@ describe('ExperimentalTab', () => { const { getAllByRole } = render(); const toggle = getAllByRole('checkbox'); - expect(toggle).toHaveLength(5); + expect(toggle).toHaveLength(6); }); it('enables add account snap', async () => { From 87fba4ab38d15068b60c0542b14d9374d840f2ab Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Fri, 12 Jul 2024 15:35:16 +0200 Subject: [PATCH 05/11] chore: remove unused "en" message --- app/_locales/en/messages.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 17f02584d172..c9f60b6f8ee5 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -771,9 +771,6 @@ "bitcoinSupportToggleTitle": { "message": "Enable \"Add a new Bitcoin account (Beta)\"" }, - "bitcoinTestnetSupportSectionTitle": { - "message": "Bitcoin" - }, "bitcoinTestnetSupportToggleDescription": { "message": "Turning on this feature will give you the option to add a Bitcoin Account for test network. " }, From 805ac7ba46d2ad0ffdbcfdef81f15f8f91848f3e Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Fri, 12 Jul 2024 17:26:57 +0200 Subject: [PATCH 06/11] fix: fix e2e metrics tests --- .../errors-after-init-opt-in-background-state.json | 1 + .../state-snapshots/errors-after-init-opt-in-ui-state.json | 1 + 2 files changed, 2 insertions(+) diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index eb815b7727eb..3bf1a277a21f 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -185,6 +185,7 @@ "openSeaEnabled": false, "securityAlertsEnabled": "boolean", "bitcoinSupportEnabled": "boolean", + "bitcoinTestnetSupportEnabled": "boolean", "addSnapAccountEnabled": "boolean", "advancedGasFee": {}, "featureFlags": {}, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 5f9666cb6702..9a827b723392 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -123,6 +123,7 @@ "securityAlertsEnabled": "boolean", "addSnapAccountEnabled": "boolean", "bitcoinSupportEnabled": "boolean", + "bitcoinTestnetSupportEnabled": "boolean", "advancedGasFee": {}, "incomingTransactionsPreferences": {}, "identities": "object", From 3ec87a1b7ab5b6b95ecfea7302a0a547c480c8d2 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 15 Jul 2024 12:11:14 +0200 Subject: [PATCH 07/11] chore(btc): fix wording Co-authored-by: Daniel Rocha --- app/_locales/en/messages.json | 2 +- app/scripts/controllers/preferences.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c9f60b6f8ee5..7081e0c95c31 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -772,7 +772,7 @@ "message": "Enable \"Add a new Bitcoin account (Beta)\"" }, "bitcoinTestnetSupportToggleDescription": { - "message": "Turning on this feature will give you the option to add a Bitcoin Account for test network. " + "message": "Turning on this feature will give you the option to add a Bitcoin Account for the test network." }, "bitcoinTestnetSupportToggleTitle": { "message": "Enable \"Add a new Bitcoin account (Testnet)\"" diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 6342dfd88d29..e4de5521d3ad 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -308,7 +308,7 @@ export default class PreferencesController { * Setter for the `bitcoinTestnetSupportEnabled` property. * * @param {boolean} bitcoinTestnetSupportEnabled - Whether or not the user wants to - * enable the "Add a new Bitcoin testnet account (Beta)" button. + * enable the "Add a new Bitcoin account (Testnet)" button. */ setBitcoinTestnetSupportEnabled(bitcoinTestnetSupportEnabled) { this.store.updateState({ From d124883978930dd1262259d34ecf5fb3bfd50eb9 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 15 Jul 2024 12:15:16 +0200 Subject: [PATCH 08/11] refactor(btc): makes defaultAccountName required --- .../create-btc-account/create-btc-account.stories.js | 5 +++++ .../create-btc-account/create-btc-account.test.tsx | 6 +++++- .../multichain/create-btc-account/create-btc-account.tsx | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/components/multichain/create-btc-account/create-btc-account.stories.js b/ui/components/multichain/create-btc-account/create-btc-account.stories.js index 71c537420eed..58415f189f53 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.stories.js +++ b/ui/components/multichain/create-btc-account/create-btc-account.stories.js @@ -1,9 +1,14 @@ import React from 'react'; import { CreateBtcAccount } from '.'; +import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; export default { title: 'Components/Multichain/CreateBtcAccount', component: CreateBtcAccount, + args: { + defaultAccountName: 'Bitcoin Account', + network: MultichainNetworks.BITCOIN, + }, }; export const DefaultStory = (args) => ; diff --git a/ui/components/multichain/create-btc-account/create-btc-account.test.tsx b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx index 9c21744188d6..23fbe9226707 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.test.tsx +++ b/ui/components/multichain/create-btc-account/create-btc-account.test.tsx @@ -11,7 +11,11 @@ import { CreateBtcAccount } from '.'; const render = (props = { onActionComplete: jest.fn() }) => { const store = configureStore(mockState); return renderWithProvider( - , + , store, ); }; diff --git a/ui/components/multichain/create-btc-account/create-btc-account.tsx b/ui/components/multichain/create-btc-account/create-btc-account.tsx index 524eac24a368..29c7b8f345b3 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.tsx +++ b/ui/components/multichain/create-btc-account/create-btc-account.tsx @@ -21,7 +21,7 @@ type CreateBtcAccountOptions = { /** * Default account name */ - defaultAccountName?: string; + defaultAccountName: string; }; export const CreateBtcAccount = ({ @@ -55,7 +55,7 @@ export const CreateBtcAccount = ({ }; const getNextAvailableAccountName = async (_accounts: InternalAccount[]) => { - return defaultAccountName ?? 'Bitcoin Account'; + return defaultAccountName; }; return ( From bf2d25f34b24636194e5ce9e598ff83bd7f8e870 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 15 Jul 2024 12:17:59 +0200 Subject: [PATCH 09/11] refactor(btc): isAddress -> isAddressCallback --- ui/selectors/accounts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/selectors/accounts.ts b/ui/selectors/accounts.ts index 6f915858624c..bd33d5af1f89 100644 --- a/ui/selectors/accounts.ts +++ b/ui/selectors/accounts.ts @@ -33,11 +33,11 @@ export function isSelectedInternalAccountBtc(state: AccountsState) { function hasCreatedBtcAccount( state: AccountsState, - isAddress: (address: string) => boolean, + isAddressCallback: (address: string) => boolean, ) { const accounts = getInternalAccounts(state); return accounts.some((account) => { - return isBtcAccount(account) && isAddress(account.address); + return isBtcAccount(account) && isAddressCallback(account.address); }); } From 4763e46fc202b0a77f05943ff32d50dc60af20c0 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 15 Jul 2024 14:59:49 +0200 Subject: [PATCH 10/11] chore: lint --- .../multichain/create-btc-account/create-btc-account.stories.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/multichain/create-btc-account/create-btc-account.stories.js b/ui/components/multichain/create-btc-account/create-btc-account.stories.js index 58415f189f53..15f93f16ce26 100644 --- a/ui/components/multichain/create-btc-account/create-btc-account.stories.js +++ b/ui/components/multichain/create-btc-account/create-btc-account.stories.js @@ -1,6 +1,6 @@ import React from 'react'; -import { CreateBtcAccount } from '.'; import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; +import { CreateBtcAccount } from '.'; export default { title: 'Components/Multichain/CreateBtcAccount', From 42ebe440e41bc9e8f868d92a1af8b974a94d9d70 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Mon, 15 Jul 2024 16:36:22 +0200 Subject: [PATCH 11/11] feat(btc): track bitcoin testnet support event --- shared/constants/metametrics.ts | 1 + .../experimental-tab/experimental-tab.component.tsx | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 7a83ec4f61e2..9f8de615691f 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -513,6 +513,7 @@ export enum MetaMetricsEventName { AppWindowExpanded = 'App Window Expanded', BridgeLinkClicked = 'Bridge Link Clicked', BitcoinSupportToggled = 'Bitcoin Support Toggled', + BitcoinTestnetSupportToggled = 'Bitcoin Testnet Support Toggled', DappViewed = 'Dapp Viewed', DecryptionApproved = 'Decryption Approved', DecryptionRejected = 'Decryption Rejected', diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx index 61bd3b4b0a90..ef03a23c09b8 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.tsx +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.tsx @@ -284,6 +284,13 @@ export default class ExperimentalTab extends PureComponent description: t('bitcoinTestnetSupportToggleDescription'), toggleValue: bitcoinTestnetSupportEnabled, toggleCallback: (value) => { + trackEvent({ + event: MetaMetricsEventName.BitcoinTestnetSupportToggled, + category: MetaMetricsEventCategory.Settings, + properties: { + enabled: !value, + }, + }); setBitcoinTestnetSupportEnabled(!value); }, toggleDataTestId: 'bitcoin-testnet-accounts-toggle',