Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support creation of Bitcoin testnet accounts #25772

Merged
merged 11 commits into from
Jul 16, 2024
9 changes: 9 additions & 0 deletions app/_locales/en/messages.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion app/scripts/controllers/preferences.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -295,14 +296,26 @@ 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({
bitcoinSupportEnabled,
});
}

/**
* Setter for the `bitcoinTestnetSupportEnabled` property.
*
* @param {boolean} bitcoinTestnetSupportEnabled - Whether or not the user wants to
* enable the "Add a new Bitcoin account (Testnet)" button.
*/
setBitcoinTestnetSupportEnabled(bitcoinTestnetSupportEnabled) {
this.store.updateState({
bitcoinTestnetSupportEnabled,
});
}

/**
* Setter for the `useExternalNameSources` property
*
Expand Down
1 change: 1 addition & 0 deletions app/scripts/lib/setupSentry.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3021,6 +3021,10 @@ export default class MetamaskController extends EventEmitter {
preferencesController.setBitcoinSupportEnabled.bind(
preferencesController,
),
setBitcoinTestnetSupportEnabled:
preferencesController.setBitcoinTestnetSupportEnabled.bind(
preferencesController,
),
setUseExternalNameSources:
preferencesController.setUseExternalNameSources.bind(
preferencesController,
Expand Down
1 change: 1 addition & 0 deletions test/data/mock-state.json
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,7 @@
],
"addSnapAccountEnabled": false,
"bitcoinSupportEnabled": false,
"bitcoinTestnetSupportEnabled": false,
"pendingApprovals": {
"testApprovalId": {
"id": "testApprovalId",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
"openSeaEnabled": false,
"securityAlertsEnabled": "boolean",
"bitcoinSupportEnabled": "boolean",
"bitcoinTestnetSupportEnabled": "boolean",
"addSnapAccountEnabled": "boolean",
"advancedGasFee": {},
"featureFlags": {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
"securityAlertsEnabled": "boolean",
"addSnapAccountEnabled": "boolean",
"bitcoinSupportEnabled": "boolean",
"bitcoinTestnetSupportEnabled": "boolean",
"advancedGasFee": {},
"incomingTransactionsPreferences": {},
"identities": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -66,7 +67,11 @@ 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';
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
///: END:ONLY_INCLUDE_IF
import { HiddenAccountList } from './hidden-account-list';

Expand All @@ -80,6 +85,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',
Expand All @@ -99,6 +106,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');
Expand Down Expand Up @@ -156,9 +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,
);
const isBtcTestnetAccountAlreadyCreated = useSelector(
hasCreatedBtcTestnetAccount,
);
///: END:ONLY_INCLUDE_IF

const [searchQuery, setSearchQuery] = useState('');
Expand Down Expand Up @@ -219,10 +234,34 @@ export const AccountListMenu = ({
</Box>
) : null}
{
// Bitcoin mainnet:
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
bitcoinSupportEnabled && actionMode === ACTION_MODES.ADD_BITCOIN ? (
<Box paddingLeft={4} paddingRight={4} paddingBottom={4}>
<CreateBtcAccount
defaultAccountName="Bitcoin Account"
network={MultichainNetworks.BITCOIN}
onActionComplete={(confirmed) => {
if (confirmed) {
onClose();
} else {
setActionMode(ACTION_MODES.LIST);
}
}}
/>
</Box>
) : null
///: END:ONLY_INCLUDE_IF
}
{
// Bitcoin testnet:
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
bitcoinTestnetSupportEnabled &&
actionMode === ACTION_MODES.ADD_BITCOIN_TESTNET ? (
<Box paddingLeft={4} paddingRight={4} paddingBottom={4}>
<CreateBtcAccount
defaultAccountName="Bitcoin Testnet Account"
network={MultichainNetworks.BITCOIN_TESTNET}
onActionComplete={(confirmed) => {
if (confirmed) {
onClose();
Expand Down Expand Up @@ -303,6 +342,25 @@ export const AccountListMenu = ({
) : null
///: END:ONLY_INCLUDE_IF
}
{
///: BEGIN:ONLY_INCLUDE_IF(build-flask)
bitcoinTestnetSupportEnabled ? (
<Box marginTop={4}>
<ButtonLink
disabled={isBtcTestnetAccountAlreadyCreated}
size={Size.SM}
startIconName={IconName.Add}
onClick={() => {
setActionMode(ACTION_MODES.ADD_BITCOIN_TESTNET);
}}
data-testid="multichain-account-menu-popover-add-account-testnet"
>
{t('addNewBitcoinTestnetAccount')}
</ButtonLink>
</Box>
) : null
///: END:ONLY_INCLUDE_IF
}
<Box marginTop={4}>
<ButtonLink
size={Size.SM}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import React from 'react';
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
import { CreateBtcAccount } from '.';

export default {
title: 'Components/Multichain/CreateBtcAccount',
component: CreateBtcAccount,
args: {
defaultAccountName: 'Bitcoin Account',
network: MultichainNetworks.BITCOIN,
},
};

export const DefaultStory = (args) => <CreateBtcAccount {...args} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import { CreateBtcAccount } from '.';

const render = (props = { onActionComplete: jest.fn() }) => {
const store = configureStore(mockState);
return renderWithProvider(<CreateBtcAccount {...props} />, store);
return renderWithProvider(
<CreateBtcAccount
network={MultichainNetworks.BITCOIN}
defaultAccountName="Bitcoin Account"
{...props}
/>,
store,
);
};

const ACCOUNT_NAME = 'Bitcoin Account';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,28 @@ type CreateBtcAccountOptions = {
* Callback called once the account has been created
*/
onActionComplete: (completed: boolean) => Promise<void>;
/**
* CAIP-2 chain ID
*/
network: MultichainNetworks;
/**
* Default account name
*/
defaultAccountName: string;
};

export const CreateBtcAccount = ({
onActionComplete,
defaultAccountName,
network,
}: CreateBtcAccountOptions) => {
const dispatch = useDispatch();

const onCreateAccount = async (name: string) => {
// 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
Expand All @@ -45,7 +55,7 @@ export const CreateBtcAccount = ({
};

const getNextAvailableAccountName = async (_accounts: InternalAccount[]) => {
return 'Bitcoin Account';
return defaultAccountName;
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -241,7 +243,12 @@ export default class ExperimentalTab extends PureComponent<ExperimentalTabProps>
// 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 (
<>
Expand Down Expand Up @@ -272,6 +279,17 @@ export default class ExperimentalTab extends PureComponent<ExperimentalTabProps>
toggleOffLabel: t('off'),
toggleOnLabel: t('on'),
})}
{this.renderToggleSection({
title: t('bitcoinTestnetSupportToggleTitle'),
description: t('bitcoinTestnetSupportToggleDescription'),
toggleValue: bitcoinTestnetSupportEnabled,
toggleCallback: (value) => {
ccharly marked this conversation as resolved.
Show resolved Hide resolved
setBitcoinTestnetSupportEnabled(!value);
},
toggleDataTestId: 'bitcoin-testnet-accounts-toggle',
toggleOffLabel: t('off'),
toggleOnLabel: t('on'),
})}
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +14,7 @@ import {
} from '../../../store/actions';
import {
getIsBitcoinSupportEnabled,
getIsBitcoinTestnetSupportEnabled,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
getIsAddSnapAccountEnabled,
///: END:ONLY_INCLUDE_IF
Expand All @@ -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
Expand All @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
Loading
Loading