-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(btc): add BTC account creation menu entry (#25625)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** This adds a new menu entry "Create a new Bitcoin account" to create a Bitcoin account using the Bitcoin Manager Snap. > [!NOTE] > This new feature is only available on Flask for now. > [!WARNING] > Some code logic of this PR might change depending if we are able to reduce the e2e test flakiness from this PR: > - #25191 [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/25625?quickstart=1) ## **Related issues** Fixes: MetaMask/accounts-planning#451 ## **Manual testing steps** ### Main build 1. `yarn start` 2. Click on the account (in the app header) 3. Click "Add account or hardware wallet" 3. **You should not see anything related to Bitcoin account creation** ![Screenshot 2024-07-02 at 11 45 22](https://github.com/MetaMask/metamask-extension/assets/569258/477c9527-475f-4a30-a550-968c93c1ec8e) ### Flask build 1. `yarn start` 2. Click on the account (in the app header) 3. Click "Add account or hardware wallet" 4. Click "Create a new Bitcoin account" 5. Input your account name 6. You should now see your new Bitcoin account in the extension ![Screenshot 2024-07-02 at 11 53 17](https://github.com/MetaMask/metamask-extension/assets/569258/069c4466-fb18-43b1-b483-f72f6141ad65) ![Screenshot 2024-07-02 at 11 54 12](https://github.com/MetaMask/metamask-extension/assets/569258/1577981a-db62-4ee3-b6b6-2679028e23d3) ![Screenshot 2024-07-02 at 11 54 25](https://github.com/MetaMask/metamask-extension/assets/569258/18cec646-8c3c-495c-adef-f8983b48c828) ![Screenshot 2024-07-02 at 11 54 31](https://github.com/MetaMask/metamask-extension/assets/569258/2fbf99e5-b386-481f-905f-700612361eb2) ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** ![Screenshot 2024-07-02 at 11 45 22](https://github.com/MetaMask/metamask-extension/assets/569258/477c9527-475f-4a30-a550-968c93c1ec8e) ![Screenshot 2024-07-02 at 11 53 17](https://github.com/MetaMask/metamask-extension/assets/569258/069c4466-fb18-43b1-b483-f72f6141ad65) ![Screenshot 2024-07-02 at 11 54 12](https://github.com/MetaMask/metamask-extension/assets/569258/1577981a-db62-4ee3-b6b6-2679028e23d3) ![Screenshot 2024-07-02 at 11 54 25](https://github.com/MetaMask/metamask-extension/assets/569258/18cec646-8c3c-495c-adef-f8983b48c828) ![Screenshot 2024-07-02 at 11 54 31](https://github.com/MetaMask/metamask-extension/assets/569258/2fbf99e5-b386-481f-905f-700612361eb2) ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.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. --------- Co-authored-by: Daniel Rocha <[email protected]>
- Loading branch information
Showing
18 changed files
with
350 additions
and
29 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { SnapId } from '@metamask/snaps-sdk'; | ||
import { Sender } from '@metamask/keyring-api'; | ||
import { HandlerType } from '@metamask/snaps-utils'; | ||
import { Json, JsonRpcRequest } from '@metamask/utils'; | ||
// This dependency is still installed as part of the `package.json`, however | ||
// the Snap is being pre-installed only for Flask build (for the moment). | ||
import BitcoinWalletSnap from '@metamask/bitcoin-wallet-snap/dist/preinstalled-snap.json'; | ||
import { handleSnapRequest } from '../../../../ui/store/actions'; | ||
|
||
export const BITCOIN_WALLET_SNAP_ID: SnapId = | ||
BitcoinWalletSnap.snapId as SnapId; | ||
|
||
export class BitcoinWalletSnapSender implements Sender { | ||
send = async (request: JsonRpcRequest): Promise<Json> => { | ||
// We assume the caller of this module is aware of this. If we try to use this module | ||
// without having the pre-installed Snap, this will likely throw an error in | ||
// the `handleSnapRequest` action. | ||
return (await handleSnapRequest({ | ||
origin: 'metamask', | ||
snapId: BITCOIN_WALLET_SNAP_ID, | ||
handler: HandlerType.OnKeyringRequest, | ||
request, | ||
})) as Json; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
ui/components/multichain/create-btc-account/create-btc-account.stories.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React from 'react'; | ||
import { CreateBtcAccount } from '.'; | ||
|
||
export default { | ||
title: 'Components/Multichain/CreateBtcAccount', | ||
component: CreateBtcAccount, | ||
}; | ||
|
||
export const DefaultStory = (args) => <CreateBtcAccount {...args} />; | ||
DefaultStory.storyName = 'Default'; |
100 changes: 100 additions & 0 deletions
100
ui/components/multichain/create-btc-account/create-btc-account.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* eslint-disable jest/require-top-level-describe */ | ||
import React from 'react'; | ||
import { JsonRpcRequest } from '@metamask/utils'; | ||
import { BtcAccountType, BtcMethod } from '@metamask/keyring-api'; | ||
import { fireEvent, renderWithProvider, waitFor } from '../../../../test/jest'; | ||
import configureStore from '../../../store/store'; | ||
import mockState from '../../../../test/data/mock-state.json'; | ||
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks'; | ||
import { CreateBtcAccount } from '.'; | ||
|
||
const render = (props = { onActionComplete: jest.fn() }) => { | ||
const store = configureStore(mockState); | ||
return renderWithProvider(<CreateBtcAccount {...props} />, store); | ||
}; | ||
|
||
const ACCOUNT_NAME = 'Bitcoin Account'; | ||
|
||
const mockBtcAccount = { | ||
type: BtcAccountType.P2wpkh, | ||
id: '8a323a0b-9ff5-4ab6-95e0-d51ec7e09763', | ||
address: 'bc1qwl8399fz829uqvqly9tcatgrgtwp3udnhxfq4k', | ||
options: { | ||
scope: MultichainNetworks.BITCOIN, | ||
index: 0, | ||
}, | ||
methods: [BtcMethod.SendMany], | ||
}; | ||
const mockBitcoinWalletSnapSend = jest.fn().mockReturnValue(mockBtcAccount); | ||
const mockSetAccountLabel = jest.fn().mockReturnValue({ type: 'TYPE' }); | ||
|
||
jest.mock('../../../store/actions', () => ({ | ||
forceUpdateMetamaskState: jest.fn(), | ||
setAccountLabel: (address: string, label: string) => | ||
mockSetAccountLabel(address, label), | ||
})); | ||
|
||
jest.mock( | ||
'../../../../app/scripts/lib/snap-keyring/bitcoin-wallet-snap', | ||
() => ({ | ||
BitcoinWalletSnapSender: jest.fn().mockImplementation(() => { | ||
return { | ||
send: (_request: JsonRpcRequest) => { | ||
return mockBitcoinWalletSnapSend(); | ||
}, | ||
}; | ||
}), | ||
}), | ||
); | ||
|
||
describe('CreateBtcAccount', () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('displays account name input and suggests name', async () => { | ||
const { getByPlaceholderText } = render(); | ||
|
||
await waitFor(() => | ||
expect(getByPlaceholderText(ACCOUNT_NAME)).toBeInTheDocument(), | ||
); | ||
}); | ||
|
||
it('fires onActionComplete when clicked', async () => { | ||
const onActionComplete = jest.fn(); | ||
const { getByText, getByPlaceholderText } = render({ onActionComplete }); | ||
|
||
const input = await waitFor(() => getByPlaceholderText(ACCOUNT_NAME)); | ||
const newAccountName = 'New Account Name'; | ||
|
||
fireEvent.change(input, { | ||
target: { value: newAccountName }, | ||
}); | ||
fireEvent.click(getByText('Create')); | ||
|
||
await waitFor(() => | ||
expect(mockSetAccountLabel).toHaveBeenCalledWith( | ||
mockBtcAccount.address, | ||
newAccountName, | ||
), | ||
); | ||
await waitFor(() => expect(onActionComplete).toHaveBeenCalled()); | ||
}); | ||
|
||
it(`doesn't allow duplicate account names`, async () => { | ||
const { getByText, getByPlaceholderText } = render(); | ||
|
||
const input = await waitFor(() => getByPlaceholderText(ACCOUNT_NAME)); | ||
const usedAccountName = | ||
mockState.metamask.internalAccounts.accounts[ | ||
'07c2cfec-36c9-46c4-8115-3836d3ac9047' | ||
].metadata.name; | ||
|
||
fireEvent.change(input, { | ||
target: { value: usedAccountName }, | ||
}); | ||
|
||
const submitButton = getByText('Create'); | ||
expect(submitButton).toHaveAttribute('disabled'); | ||
}); | ||
}); |
Oops, something went wrong.