diff --git a/ui/pages/confirmations/components/UI/account-type-label/account-type-label.test.tsx b/ui/pages/confirmations/components/UI/account-type-label/account-type-label.test.tsx new file mode 100644 index 000000000000..0fb65719908d --- /dev/null +++ b/ui/pages/confirmations/components/UI/account-type-label/account-type-label.test.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { AccountTypeLabel } from './account-type-label'; + +describe('AccountTypeLabel', () => { + it('should return null when label is undefined', () => { + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); + + it('should render Tag component when label is provided', () => { + const { container, getByText } = render( + , + ); + + expect(container.firstChild).not.toBeNull(); + expect(getByText('Native SegWit')).toBeInTheDocument(); + }); +}); diff --git a/ui/pages/confirmations/components/UI/account-type-label/account-type-label.tsx b/ui/pages/confirmations/components/UI/account-type-label/account-type-label.tsx new file mode 100644 index 000000000000..0fa4c390a8db --- /dev/null +++ b/ui/pages/confirmations/components/UI/account-type-label/account-type-label.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { Tag } from '../../../../../components/component-library'; + +export const AccountTypeLabel = ({ label }: { label?: string }) => { + if (!label) { + return null; + } + + return ; +}; diff --git a/ui/pages/confirmations/components/UI/account-type-label/index.ts b/ui/pages/confirmations/components/UI/account-type-label/index.ts new file mode 100644 index 000000000000..8688e4c5ff25 --- /dev/null +++ b/ui/pages/confirmations/components/UI/account-type-label/index.ts @@ -0,0 +1 @@ +export { AccountTypeLabel } from './account-type-label'; diff --git a/ui/pages/confirmations/components/UI/asset/asset.test.tsx b/ui/pages/confirmations/components/UI/asset/asset.test.tsx index 7c26db8040fa..0dc78c5c6901 100644 --- a/ui/pages/confirmations/components/UI/asset/asset.test.tsx +++ b/ui/pages/confirmations/components/UI/asset/asset.test.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; +import { BtcAccountType } from '@metamask/keyring-api'; import createMockStore from 'redux-mock-store'; import { renderWithProvider } from '../../../../../../test/jest'; @@ -184,4 +185,14 @@ describe('NFTAsset', () => { const image = getByAltText('Test NFT'); expect(image).toHaveAttribute('src', 'https://example.com/collection.png'); }); + + it('renders account type label when account type is provided', () => { + const assetWithAccountType = { + ...mockTokenAsset, + accountType: BtcAccountType.P2wpkh, + }; + const { getByText } = render(); + + expect(getByText('Native SegWit')).toBeInTheDocument(); + }); }); diff --git a/ui/pages/confirmations/components/UI/asset/asset.tsx b/ui/pages/confirmations/components/UI/asset/asset.tsx index dde6dc764680..1d770580e99d 100644 --- a/ui/pages/confirmations/components/UI/asset/asset.tsx +++ b/ui/pages/confirmations/components/UI/asset/asset.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { KeyringAccountType } from '@metamask/keyring-api'; import { AvatarToken, AvatarNetwork, @@ -22,7 +23,9 @@ import { NFT_STANDARDS, } from '../../../types/send'; import { useNftImageUrl } from '../../../hooks/useNftImageUrl'; +import { accountTypeLabel } from '../../../constants/network'; import { useFormatters } from '../../../../../hooks/useFormatters'; +import { AccountTypeLabel } from '../account-type-label'; type AssetProps = { asset: AssetType; @@ -119,6 +122,8 @@ const TokenAsset = ({ asset, onClick, isSelected }: AssetProps) => { const { formatCurrencyWithMinThreshold, formatTokenQuantity } = useFormatters(); + const typeLabel = accountTypeLabel[asset.accountType as KeyringAccountType]; + return ( { flexDirection={FlexDirection.Column} style={{ flex: 1, overflow: 'hidden' }} > - - {name} - + + + {name} + + + { expect(getByText('0x12345...45678')).toBeInTheDocument(); }); + + it('renders account type label when account type is provided', () => { + const { getByText } = render( + , + ); + + expect(getByText('Native SegWit')).toBeInTheDocument(); + }); }); diff --git a/ui/pages/confirmations/components/UI/recipient/recipient.tsx b/ui/pages/confirmations/components/UI/recipient/recipient.tsx index 4a7d7840d586..22394b6caa87 100644 --- a/ui/pages/confirmations/components/UI/recipient/recipient.tsx +++ b/ui/pages/confirmations/components/UI/recipient/recipient.tsx @@ -1,5 +1,7 @@ import React from 'react'; import { AvatarAccountSize } from '@metamask/design-system-react'; +import { KeyringAccountType } from '@metamask/keyring-api'; + import { PreferredAvatar } from '../../../../../components/app/preferred-avatar'; import { Box, Text } from '../../../../../components/component-library'; import { @@ -12,6 +14,8 @@ import { } from '../../../../../helpers/constants/design-system'; import { type Recipient as RecipientType } from '../../../hooks/send/useRecipients'; import { shortenAddress } from '../../../../../helpers/utils/util'; +import { accountTypeLabel } from '../../../constants/network'; +import { AccountTypeLabel } from '../account-type-label'; export const Recipient = ({ isAccount, @@ -28,6 +32,8 @@ export const Recipient = ({ const recipientName = isAccount ? recipient.accountGroupName : recipient.contactName; + const typeLabel = + accountTypeLabel[recipient.accountType as KeyringAccountType]; return ( {recipientName} - - {shortenAddress(address)} - + + {shortenAddress(address)} + + + ); diff --git a/ui/pages/confirmations/constants/network.ts b/ui/pages/confirmations/constants/network.ts new file mode 100644 index 000000000000..7fe9b4981057 --- /dev/null +++ b/ui/pages/confirmations/constants/network.ts @@ -0,0 +1,10 @@ +import { BtcAccountType, KeyringAccountType } from '@metamask/keyring-api'; + +// This map is used to display the account type label next to the token / account name +// Add more account type here to support labels in case we need them for other networks +export const accountTypeLabel: Partial> = { + [BtcAccountType.P2pkh]: 'Legacy', + [BtcAccountType.P2sh]: 'Nested SegWit', + [BtcAccountType.P2wpkh]: 'Native SegWit', + [BtcAccountType.P2tr]: 'Taproot', +}; diff --git a/ui/pages/confirmations/hooks/send/useAccountRecipients.test.ts b/ui/pages/confirmations/hooks/send/useAccountRecipients.test.ts index e90d20798a45..eada460ce506 100644 --- a/ui/pages/confirmations/hooks/send/useAccountRecipients.test.ts +++ b/ui/pages/confirmations/hooks/send/useAccountRecipients.test.ts @@ -1,3 +1,6 @@ +import { BtcAccountType } from '@metamask/keyring-api'; + +import { cloneDeep } from 'lodash'; import { renderHookWithProvider } from '../../../../../test/lib/render-helpers'; import mockState from '../../../../../test/data/mock-state.json'; import { ConsolidatedWallets } from '../../../../selectors/multichain-accounts/account-tree.types'; @@ -141,6 +144,20 @@ describe('useAccountRecipients', () => { isSolanaSendType: false, isBitcoinSendType: true, } as unknown as ReturnType); + const accountsWithAccountType = cloneDeep(mockWalletsWithAccounts); + ( + accountsWithAccountType.wallet1.groups.group1.accounts[0] as { + address: string; + type: BtcAccountType; + } + ).type = BtcAccountType.P2wpkh; + ( + accountsWithAccountType.wallet1.groups.group1.accounts[1] as { + address: string; + type: BtcAccountType; + } + ).type = BtcAccountType.P2sh; + mockGetWalletsWithAccounts.mockReturnValue(accountsWithAccountType); mockIsEVMAccountForSend.mockReturnValue(false); mockIsSolanaAccountForSend.mockReturnValue(false); mockIsBitcoinAccountForSend.mockReturnValue(true); @@ -153,11 +170,13 @@ describe('useAccountRecipients', () => { expect(result.current).toEqual([ { accountGroupName: 'Account Group 1', + accountType: BtcAccountType.P2wpkh, address: '0x1234567890abcdef1234567890abcdef12345678', walletName: 'MetaMask Wallet', }, { accountGroupName: 'Account Group 1', + accountType: BtcAccountType.P2sh, address: '0xabcdef1234567890abcdef1234567890abcdef12', walletName: 'MetaMask Wallet', }, diff --git a/ui/pages/confirmations/hooks/send/useAccountRecipients.ts b/ui/pages/confirmations/hooks/send/useAccountRecipients.ts index 91efd2a156be..9bd94b456420 100644 --- a/ui/pages/confirmations/hooks/send/useAccountRecipients.ts +++ b/ui/pages/confirmations/hooks/send/useAccountRecipients.ts @@ -39,6 +39,7 @@ export const useAccountRecipients = (): Recipient[] => { if (shouldInclude) { recipients.push({ accountGroupName, + accountType: account.type, address: account.address, walletName, }); diff --git a/ui/pages/confirmations/hooks/send/useRecipients.ts b/ui/pages/confirmations/hooks/send/useRecipients.ts index 211da67d8cd7..6d7183a13319 100644 --- a/ui/pages/confirmations/hooks/send/useRecipients.ts +++ b/ui/pages/confirmations/hooks/send/useRecipients.ts @@ -1,8 +1,10 @@ +import { KeyringAccountType } from '@metamask/keyring-api'; import { useContactRecipients } from './useContactRecipients'; import { useAccountRecipients } from './useAccountRecipients'; export type Recipient = { accountGroupName?: string; + accountType?: KeyringAccountType; address: string; contactName?: string; isContact?: boolean; diff --git a/ui/pages/confirmations/types/send.ts b/ui/pages/confirmations/types/send.ts index 141bfd7730b9..c0be43c53578 100644 --- a/ui/pages/confirmations/types/send.ts +++ b/ui/pages/confirmations/types/send.ts @@ -1,3 +1,4 @@ +import { KeyringAccountType } from '@metamask/keyring-api'; import { Hex } from '@metamask/utils'; export enum AssetStandard { @@ -12,6 +13,7 @@ export const NFT_STANDARDS = [AssetStandard.ERC721, AssetStandard.ERC1155]; export type Asset = { accountAddress?: string; accountId?: string; + accountType?: KeyringAccountType; address?: string; assetId?: string; balance?: string | number | undefined;