diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index e50056a9a41a..65f9c333a4cc 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -3696,6 +3696,9 @@ "nameProvider_token": { "message": "MetaMask" }, + "nameResolutionFailedError": { + "message": "No address resolution found for this name." + }, "nameSetPlaceholder": { "message": "Choose a nickname...", "description": "Placeholder text for name input field in name component modal." diff --git a/app/_locales/en_GB/messages.json b/app/_locales/en_GB/messages.json index e50056a9a41a..65f9c333a4cc 100644 --- a/app/_locales/en_GB/messages.json +++ b/app/_locales/en_GB/messages.json @@ -3696,6 +3696,9 @@ "nameProvider_token": { "message": "MetaMask" }, + "nameResolutionFailedError": { + "message": "No address resolution found for this name." + }, "nameSetPlaceholder": { "message": "Choose a nickname...", "description": "Placeholder text for name input field in name component modal." diff --git a/ui/pages/confirmations/components/send/amount/amount.test.tsx b/ui/pages/confirmations/components/send/amount/amount.test.tsx index 9c584ce16fa1..e6399d38a945 100644 --- a/ui/pages/confirmations/components/send/amount/amount.test.tsx +++ b/ui/pages/confirmations/components/send/amount/amount.test.tsx @@ -62,7 +62,8 @@ describe('Amount', () => { } as unknown as SendContext.SendContextType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -86,7 +87,8 @@ describe('Amount', () => { } as unknown as SendContext.SendContextType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -110,7 +112,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -121,6 +124,7 @@ describe('Amount', () => { expect(getByText('$ 20.00')).toBeInTheDocument(); fireEvent.click(getByTestId('toggle-fiat-mode')); expect(getByRole('textbox')).toHaveValue('20'); + expect(getByText('USD')).toBeInTheDocument(); fireEvent.change(getByRole('textbox'), { target: { value: 100 } }); expect(getByText('0 NEU')).toBeInTheDocument(); }); @@ -136,7 +140,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -172,7 +177,8 @@ describe('Amount', () => { } as unknown as SendContext.SendContextType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -191,7 +197,8 @@ describe('Amount', () => { } as unknown as SendContext.SendContextType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -252,7 +259,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -272,7 +280,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: false, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -292,7 +301,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: false, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -315,7 +325,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', @@ -336,7 +347,8 @@ describe('Amount', () => { } as unknown as ReturnType); jest.spyOn(CurrencyConversions, 'useCurrencyConversions').mockReturnValue({ conversionSupportedForAsset: true, - fiatCurrencySymbol: 'USD', + fiatCurrencySymbol: '$', + fiatCurrencyName: 'usd', getFiatValue: () => '20', getFiatDisplayValue: () => '$ 20.00', getNativeValue: () => '20', diff --git a/ui/pages/confirmations/components/send/amount/amount.tsx b/ui/pages/confirmations/components/send/amount/amount.tsx index 322bc121716e..07f9a88b4d4c 100644 --- a/ui/pages/confirmations/components/send/amount/amount.tsx +++ b/ui/pages/confirmations/components/send/amount/amount.tsx @@ -45,6 +45,7 @@ export const Amount = ({ const [fiatMode, setFiatMode] = useState(false); const { conversionSupportedForAsset, + fiatCurrencyName, getFiatValue, getFiatDisplayValue, getNativeValue, @@ -154,7 +155,9 @@ export const Amount = ({ value={amount} endAccessory={ - {asset?.symbol} + + {fiatMode ? fiatCurrencyName?.toUpperCase() : asset?.symbol} + {conversionSupportedForAsset && ( { asset?.standard !== ERC1155 && asset?.standard !== ERC721, fiatCurrencySymbol: getCurrencySymbol(currentCurrency), + fiatCurrencyName: currentCurrency, getFiatValue, getFiatDisplayValue, getNativeValue, diff --git a/ui/pages/confirmations/hooks/send/useMaxAmount.test.ts b/ui/pages/confirmations/hooks/send/useMaxAmount.test.ts index 67b9c3ef7bf3..91933ac7e66b 100644 --- a/ui/pages/confirmations/hooks/send/useMaxAmount.test.ts +++ b/ui/pages/confirmations/hooks/send/useMaxAmount.test.ts @@ -58,6 +58,32 @@ describe('useMaxAmount', () => { expect(result.getMaxAmount()).toEqual('999.999570668411440000'); }); + it('not throw error if gas fee estimates is not available', () => { + jest.spyOn(SendContext, 'useSendContext').mockReturnValue({ + asset: EVM_NATIVE_ASSET, + chainId: '0x5', + from: MOCK_ADDRESS_1, + } as unknown as SendContext.SendContextType); + useBalanceMock.mockReturnValue({ + balance: '10.00', + decimals: 18, + rawBalanceNumeric: new Numeric('1000000000000000000000', 10), + }); + const result = renderHook({ + ...mockState, + metamask: { + ...mockState.metamask, + gasFeeEstimatesByChainId: { + '0x5': { + gasFeeEstimates: {}, + }, + }, + }, + }); + + expect(result.getMaxAmount()).toEqual('1000'); + }); + it('return 0 if balance of native asset is less than gas needed', () => { jest.spyOn(SendContext, 'useSendContext').mockReturnValue({ asset: EVM_NATIVE_ASSET, diff --git a/ui/pages/confirmations/hooks/send/useMaxAmount.ts b/ui/pages/confirmations/hooks/send/useMaxAmount.ts index ce62033b990d..1293b23c56ee 100644 --- a/ui/pages/confirmations/hooks/send/useMaxAmount.ts +++ b/ui/pages/confirmations/hooks/send/useMaxAmount.ts @@ -28,9 +28,8 @@ export const getEstimatedTotalGas = ( if (!gasFeeEstimates) { return new Numeric('0', 10); } - const { - medium: { suggestedMaxFeePerGas }, - } = gasFeeEstimates; + const { medium: { suggestedMaxFeePerGas } = { suggestedMaxFeePerGas: 0 } } = + gasFeeEstimates; const totalGas = new Numeric( suggestedMaxFeePerGas * NATIVE_TRANSFER_GAS_LIMIT, 10, diff --git a/ui/pages/confirmations/hooks/send/useNameValidation.ts b/ui/pages/confirmations/hooks/send/useNameValidation.ts index 2b8f9278b763..4953116d0cd9 100644 --- a/ui/pages/confirmations/hooks/send/useNameValidation.ts +++ b/ui/pages/confirmations/hooks/send/useNameValidation.ts @@ -18,7 +18,7 @@ export const useNameValidation = () => { if (resolutions.length === 0) { return { - error: 'ensUnknownError', + error: 'nameResolutionFailedError', }; } @@ -33,7 +33,7 @@ export const useNameValidation = () => { } return { - error: 'ensUnknownError', + error: 'nameResolutionFailedError', }; }, [fetchResolutions], diff --git a/ui/pages/confirmations/hooks/send/useRecipientValidation.test.ts b/ui/pages/confirmations/hooks/send/useRecipientValidation.test.ts index b55f9c2a1a3d..4d31718a86b2 100644 --- a/ui/pages/confirmations/hooks/send/useRecipientValidation.test.ts +++ b/ui/pages/confirmations/hooks/send/useRecipientValidation.test.ts @@ -210,7 +210,7 @@ describe('useRecipientValidation', () => { jest.spyOn(NameValidation, 'useNameValidation').mockReturnValue({ validateName: () => Promise.resolve({ - error: 'ensUnknownError', + error: 'nameResolutionFailedError', }), }); @@ -244,7 +244,7 @@ describe('useRecipientValidation', () => { jest.spyOn(NameValidation, 'useNameValidation').mockReturnValue({ validateName: () => Promise.resolve({ - error: 'ensUnknownError', + error: 'nameResolutionFailedError', }), }); diff --git a/ui/pages/confirmations/hooks/send/useRecipients.test.ts b/ui/pages/confirmations/hooks/send/useRecipients.test.ts index cb7ce8ea1241..4f330d81a13f 100644 --- a/ui/pages/confirmations/hooks/send/useRecipients.test.ts +++ b/ui/pages/confirmations/hooks/send/useRecipients.test.ts @@ -46,8 +46,49 @@ describe('useRecipients', () => { const { result } = renderHookWithProvider(() => useRecipients(), mockState); expect(result.current).toEqual([ + ...mockAccountRecipients, ...mockContactRecipients, + ]); + }); + + it('it returns unique recipients', () => { + const mockAccountRecipients: Recipient[] = [ + { + address: '0x5678901234', + walletName: 'Wallet 1', + accountGroupName: 'Account 1', + }, + { + address: '0xfedcba5678', + walletName: 'Wallet 2', + accountGroupName: 'Account 2', + }, + ]; + + const mockContactRecipients: Recipient[] = [ + { address: '0x1234567890', contactName: 'Contact 1' }, + { address: '0xabcdef1234', contactName: 'Contact 2' }, + { + address: '0x1234567890', + walletName: 'Wallet 11', + accountGroupName: 'Account 11', + }, + { + address: '0x5678901234', + walletName: 'Wallet 12', + accountGroupName: 'Account 12', + }, + ]; + + mockUseContactRecipients.mockReturnValue(mockContactRecipients); + mockUseAccountRecipients.mockReturnValue(mockAccountRecipients); + + const { result } = renderHookWithProvider(() => useRecipients(), mockState); + + expect(result.current).toEqual([ ...mockAccountRecipients, + mockContactRecipients[0], + mockContactRecipients[1], ]); }); diff --git a/ui/pages/confirmations/hooks/send/useRecipients.ts b/ui/pages/confirmations/hooks/send/useRecipients.ts index 211da67d8cd7..ff2ba85ff5a1 100644 --- a/ui/pages/confirmations/hooks/send/useRecipients.ts +++ b/ui/pages/confirmations/hooks/send/useRecipients.ts @@ -10,8 +10,20 @@ export type Recipient = { }; export const useRecipients = (): Recipient[] => { - const contactRecipients = useContactRecipients(); const accountRecipients = useAccountRecipients(); + const contactRecipients = useContactRecipients(); + + const recipients = [...accountRecipients]; + + contactRecipients.forEach((recipient) => { + if ( + !recipients.some( + (r) => r.address.toLowerCase() === recipient.address.toLowerCase(), + ) + ) { + recipients.push(recipient); + } + }); - return [...contactRecipients, ...accountRecipients]; + return recipients; };