From c62435cdcba6d69c04d8ceb568c42fccaf437890 Mon Sep 17 00:00:00 2001 From: Douglas Daniel Date: Mon, 11 Aug 2025 09:49:45 -0500 Subject: [PATCH] feat(wallet): Custom Network Fee Popup (#30427) --- .../browser/brave_wallet_constants.h | 7 + .../common/async/__mocks__/bridge.ts | 2 +- .../custom_network_fee.stories.tsx | 58 +++ .../custom_network_fee.styles.ts | 89 ++++ .../custom_network_fee.test.tsx | 160 ++++++ .../custom_network_fee/custom_network_fee.tsx | 482 ++++++++++++++++++ ...ggested_max_priority_fee_selector.test.tsx | 30 +- components/brave_wallet_ui/stories/locale.ts | 6 + .../mock-data/mock-transaction-info.ts | 8 +- components/resources/wallet_strings.grdp | 6 + 10 files changed, 839 insertions(+), 9 deletions(-) create mode 100644 components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.stories.tsx create mode 100644 components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.styles.ts create mode 100644 components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.test.tsx create mode 100644 components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.tsx diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 9e3893bcc43..6f9192df7bb 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -220,6 +220,13 @@ inline constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletEnableTransactionSimulation", IDS_BRAVE_WALLET_ENABLE_TRANSACTION_SIMULATION}, {"braveWalletNetworkFees", IDS_BRAVE_WALLET_NETWORK_FEES}, + {"braveWalletCustomFeeAmount", IDS_BRAVE_WALLET_CUSTOM_FEE_AMOUNT}, + {"braveWalletGasTipLimit", IDS_BRAVE_WALLET_GAS_TIP_LIMIT}, + {"braveWalletGasPriceLimit", IDS_BRAVE_WALLET_GAS_PRICE_LIMIT}, + {"braveWalletGasPrice", IDS_BRAVE_WALLET_GAS_PRICE}, + {"braveWalletEditGasEstimatedNetworkFee", + IDS_BRAVE_WALLET_EDIT_GAS_ESTIMATED_NETWORK_FEE}, + {"braveWalletUseDefault", IDS_BRAVE_WALLET_USE_DEFAULT}, {"braveWalletNetworkFee", IDS_BRAVE_WALLET_NETWORK_FEE}, {"braveWalletSolanaSysvarRentProgram", IDS_BRAVE_WALLET_SOLANA_SYSVAR_RENT_PROGRAM}, diff --git a/components/brave_wallet_ui/common/async/__mocks__/bridge.ts b/components/brave_wallet_ui/common/async/__mocks__/bridge.ts index 4195b27eb49..e4996874894 100644 --- a/components/brave_wallet_ui/common/async/__mocks__/bridge.ts +++ b/components/brave_wallet_ui/common/async/__mocks__/bridge.ts @@ -718,7 +718,7 @@ export class MockedWalletApiProxy { assetTimeframeChange: '1', fromAsset: fromAssets[0], toAsset: toAssets[0], - price: '1234.56', + price: '3873.78', }, ], } diff --git a/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.stories.tsx b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.stories.tsx new file mode 100644 index 00000000000..1fcf51e5e71 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.stories.tsx @@ -0,0 +1,58 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +import * as React from 'react' + +// Mock Data +import { + mockTransactionInfo, // +} from '../../../../stories/mock-data/mock-transaction-info' +import { mockEthMainnet } from '../../../../stories/mock-data/mock-networks' + +// Utils +import { getLocale } from '../../../../../common/locale' + +// Components +import { CustomNetworkFee } from './custom_network_fee' +import { + WalletPanelStory, // +} from '../../../../stories/wrappers/wallet-panel-story-wrapper' +import { BottomSheet } from '../../../shared/bottom_sheet/bottom_sheet' + +export const _CustomNetworkFee = { + render: () => { + return ( + alert('Close Clicked')} + > + alert('Update Clicked')} + onBack={() => alert('Back Clicked')} + onClose={() => alert('Close Clicked')} + /> + + ) + }, +} + +export default { + title: 'Wallet/Panel/Components/Edit Network Fee', + component: _CustomNetworkFee, + parameters: { + layout: 'centered', + }, + decorators: [ + (Story: any) => ( + + + + ), + ], +} diff --git a/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.styles.ts b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.styles.ts new file mode 100644 index 00000000000..3b938c144f9 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.styles.ts @@ -0,0 +1,89 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +import styled from 'styled-components' +import * as leo from '@brave/leo/tokens/css/variables' + +// Shared Styles +import { Column, Row, Text } from '../../../shared/style' + +export const StyledWrapper = styled(Column)` + overflow: hidden; +` + +export const Card = styled(Column)` + background-color: ${leo.color.container.highlight}; + border-radius: ${leo.radius.xl}; +` + +export const Description = styled(Text)` + font: ${leo.font.small.semibold}; + letter-spacing: ${leo.typography.letterSpacing.small}; +` + +export const SectionLabel = styled(Text)` + font: ${leo.font.small.semibold}; + letter-spacing: ${leo.typography.letterSpacing.small}; +` + +export const InputWrapper = styled(Row)<{ + hasError?: boolean +}>` + cursor: pointer; + background-color: ${leo.color.container.background}; + outline: 1px solid + ${(p) => + p.hasError + ? leo.color.systemfeedback.errorVibrant + : leo.color.divider.subtle}; + transition: + outline 0.1s ease-in-out, + box-shadow 0.1s ease-in-out; + border-radius: ${leo.radius.m}; + :hover { + outline: 1px solid ${leo.color.divider.strong}; + box-shadow: ${leo.effect.elevation['02']}; + } + :focus-within { + outline: 2px solid + ${(p) => + p.hasError + ? leo.color.systemfeedback.errorVibrant + : leo.color.primary[40]}; + } +` + +export const Input = styled.input` + font: ${leo.font.small.regular}; + letter-spacing: ${leo.typography.letterSpacing.small}; + background-color: ${leo.color.container.background}; + color: ${leo.color.text.primary}; + outline: none; + border: none; + width: 100%; + padding: 0px; + text-align: right; + ::placeholder { + font: ${leo.font.small.regular}; + letter-spacing: ${leo.typography.letterSpacing.small}; + color: ${leo.color.text.tertiary}; + } + :focus { + outline: none; + } + ::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + ::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } +` + +export const InputLabel = styled(Text)` + font: ${leo.font.small.regular}; + letter-spacing: ${leo.typography.letterSpacing.small}; +` diff --git a/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.test.tsx b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.test.tsx new file mode 100644 index 00000000000..4c8499b7ce7 --- /dev/null +++ b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.test.tsx @@ -0,0 +1,160 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +import * as React from 'react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import { Provider } from 'react-redux' + +// Utils +import { + // eslint-disable-next-line import/no-named-default + default as BraveCoreThemeProvider, +} from '../../../../../common/BraveCoreThemeProvider' +import { createMockStore } from '../../../../utils/test-utils' + +// Components +import { CustomNetworkFee } from './custom_network_fee' + +// Mock data +import { + mockTransactionInfo, // +} from '../../../../stories/mock-data/mock-transaction-info' +import { mockEthMainnet } from '../../../../stories/mock-data/mock-networks' + +describe('CustomNetworkFee', () => { + const mockBaseFeePerGas = '20000000000' // 20 Gwei in Wei + + const mockOnUpdateCustomNetworkFee = jest.fn() + const mockOnBack = jest.fn() + const mockOnClose = jest.fn() + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('should render custom network fee component correctly', async () => { + const store = createMockStore({}) + const { container } = render( + + + + + , + ) + + await waitFor(() => { + expect(container).toBeVisible() + + // Check for description text + expect( + screen.getByText('braveWalletEditGasDescription'), + ).toBeInTheDocument() + + // Check for gas limit input + expect(screen.getByText('braveWalletEditGasLimit')).toBeInTheDocument() + + // Check for base fee display (for EIP1559 transactions) + expect(screen.getByText('braveWalletEditGasBaseFee')).toBeInTheDocument() + + // Check for gas tip limit (for EIP1559 transactions) + expect(screen.getByText('braveWalletGasTipLimit')).toBeInTheDocument() + + // Check for gas price limit (for EIP1559 transactions) + expect(screen.getByText('braveWalletGasPriceLimit')).toBeInTheDocument() + + // Check for update button + expect(screen.getByText('braveWalletUpdate')).toBeInTheDocument() + + // Check for use default button (for EIP1559 transactions) + expect(screen.getByText('braveWalletUseDefault')).toBeInTheDocument() + + // Check for gas limit error + const gasLimitInput = screen.getByTestId('gas-limit-input') + fireEvent.change(gasLimitInput, { target: { value: '0' } }) + expect(screen.getByText('braveWalletEditGasLimitError')).toBeVisible() + + // Check for gas price limit error + const gasPriceLimitInput = screen.getByTestId('gas-price-limit-input') + fireEvent.change(gasPriceLimitInput, { target: { value: '0' } }) + expect( + screen.getByText('braveWalletGasFeeLimitLowerThanBaseFeeWarning'), + ).toBeVisible() + }) + }) + + it('should render non-EIP1559 transaction correctly', async () => { + const nonEip1559TransactionInfo = { + ...mockTransactionInfo, + txDataUnion: { + ethTxData1559: undefined, + ethTxData: { + nonce: '0x1', + gasPrice: '0x59682f00', + gasLimit: '0x5208', + to: '0x0987654321098765432109876543210987654321', + value: '0x0', + data: [], + signOnly: false, + signedTransaction: undefined, + }, + solanaTxData: undefined, + filTxData: undefined, + btcTxData: undefined, + zecTxData: undefined, + }, + } + + const store = createMockStore({}) + const { container } = render( + + + + + , + ) + + await waitFor(() => { + expect(container).toBeVisible() + + // Check for gas price input (for non-EIP1559 transactions) + expect(screen.getByText('braveWalletGasPrice')).toBeInTheDocument() + + // Should not show EIP1559 specific fields + expect( + screen.queryByText('braveWalletEditGasBaseFee'), + ).not.toBeInTheDocument() + expect( + screen.queryByText('braveWalletGasTipLimit'), + ).not.toBeInTheDocument() + expect( + screen.queryByText('braveWalletGasPriceLimit'), + ).not.toBeInTheDocument() + expect( + screen.queryByText('braveWalletUseDefault'), + ).not.toBeInTheDocument() + + // Check for gas price error + const gasPriceInput = screen.getByTestId('gas-price-input') + fireEvent.change(gasPriceInput, { target: { value: '0' } }) + expect( + screen.getByText('braveWalletEditGasZeroGasPriceWarning'), + ).toBeVisible() + }) + }) +}) diff --git a/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.tsx b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.tsx new file mode 100644 index 00000000000..e909edb80ca --- /dev/null +++ b/components/brave_wallet_ui/components/extension/edit_network_fee/custom_network_fee/custom_network_fee.tsx @@ -0,0 +1,482 @@ +// Copyright (c) 2025 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +import * as React from 'react' +import { skipToken } from '@reduxjs/toolkit/query/react' +import Button from '@brave/leo/react/button' + +// Types +import { + BraveWallet, + SerializableTransactionInfo, +} from '../../../../constants/types' +import { + UpdateUnapprovedTransactionGasFieldsType, // +} from '../../../../common/constants/action_types' + +// Queries +import { + querySubscriptionOptions60s, // +} from '../../../../common/slices/constants' +import { + useGetDefaultFiatCurrencyQuery, + useGetTokenSpotPricesQuery, +} from '../../../../common/slices/api.slice' + +// Utils +import { getLocale } from '../../../../../common/locale' +import { + parseTransactionFeesWithoutPrices, // +} from '../../../../utils/tx-utils' +import { makeNetworkAsset } from '../../../../options/asset-options' +import { + getPriceIdForToken, + getTokenPriceAmountFromRegistry, +} from '../../../../utils/pricing-utils' +import Amount from '../../../../utils/amount' + +// Styled Components +import { Column, Row, VerticalDivider } from '../../../shared/style' +import { + StyledWrapper, + Card, + SectionLabel, + Input, + InputLabel, + InputWrapper, + Description, +} from './custom_network_fee.styles' + +interface Props { + transactionInfo: SerializableTransactionInfo + selectedNetwork: BraveWallet.NetworkInfo + baseFeePerGas: string + onUpdateCustomNetworkFee: ( + payload: UpdateUnapprovedTransactionGasFieldsType, + ) => void + onBack: () => void + onClose: () => void +} + +export function CustomNetworkFee(props: Props) { + const { + transactionInfo, + baseFeePerGas, + selectedNetwork, + onUpdateCustomNetworkFee, + onBack, + onClose, + } = props + + // Memos + const transactionFees = React.useMemo( + () => parseTransactionFeesWithoutPrices(transactionInfo), + [transactionInfo], + ) + const { isEIP1559Transaction } = transactionFees + + const networkAsset = React.useMemo(() => { + return makeNetworkAsset(selectedNetwork) + }, [selectedNetwork]) + + const networkTokenPriceIds = React.useMemo( + () => (networkAsset ? [getPriceIdForToken(networkAsset)] : []), + [networkAsset], + ) + + // Queries + const { data: defaultFiatCurrency } = useGetDefaultFiatCurrencyQuery() + + const { data: spotPriceRegistry } = useGetTokenSpotPricesQuery( + networkTokenPriceIds.length && defaultFiatCurrency + ? { ids: networkTokenPriceIds, toCurrency: defaultFiatCurrency } + : skipToken, + querySubscriptionOptions60s, + ) + + // State + const [gasLimit, setGasLimit] = React.useState( + transactionFees.gasLimit, + ) + const [gasPrice, setGasPrice] = React.useState( + new Amount(transactionFees.gasPrice) + .divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion + .format(), + ) + const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] = + React.useState( + new Amount(transactionFees.maxPriorityFeePerGas) + .divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion + .format(), + ) + const [maxFeePerGas, setMaxFeePerGas] = React.useState( + new Amount(transactionFees.maxFeePerGas) + .divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion + .format(), + ) + + // Methods + const handleGasLimitInputChanged = ( + event: React.ChangeEvent, + ) => { + setGasLimit(event.target.value) + } + + const handleMaxPriorityFeePerGasInputChanged = ( + event: React.ChangeEvent, + ) => { + const value = event.target.value + setMaxPriorityFeePerGas(value) + + // GWei-per-gas → Wei-per-gas conversion + const maxPriorityFeePerGasWei = new Amount(value).multiplyByDecimals(9) + + const computedMaxFeePerGasWei = new Amount(baseFeePerGas).plus( + maxPriorityFeePerGasWei, + ) + + const computedMaxFeePerGasGWei = computedMaxFeePerGasWei + .divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion + .format() + + setMaxFeePerGas(computedMaxFeePerGasGWei) + } + + const handleMaxFeePerGasInputChanged = ( + event: React.ChangeEvent, + ) => { + setMaxFeePerGas(event.target.value) + } + + const handleGasPriceInputChanged = ( + event: React.ChangeEvent, + ) => { + setGasPrice(event.target.value) + } + + const onClickUpdate = React.useCallback(() => { + if (!isEIP1559Transaction) { + onUpdateCustomNetworkFee({ + chainId: transactionInfo.chainId, + txMetaId: transactionInfo.id, + gasPrice: new Amount(gasPrice).multiplyByDecimals(9).toHex(), + gasLimit: new Amount(gasLimit).toHex(), + }) + + onClose() + return + } + + onUpdateCustomNetworkFee({ + chainId: transactionInfo.chainId, + txMetaId: transactionInfo.id, + maxPriorityFeePerGas: new Amount(maxPriorityFeePerGas) + .multiplyByDecimals(9) + .toHex(), + maxFeePerGas: new Amount(maxFeePerGas).multiplyByDecimals(9).toHex(), + gasLimit: new Amount(gasLimit).toHex(), + }) + + onClose() + }, [ + gasPrice, + gasLimit, + maxPriorityFeePerGas, + maxFeePerGas, + transactionInfo, + onClose, + onUpdateCustomNetworkFee, + isEIP1559Transaction, + ]) + + // Computed / Memos + const isCustomGasBelowBaseFee = + isEIP1559Transaction + && new Amount(maxFeePerGas).multiplyByDecimals(9).lt(baseFeePerGas) + + const customEIP1559GasFee = new Amount(maxFeePerGas) + .multiplyByDecimals(9) // GWei-per-gas → Wei-per-gas conversion + .times(gasLimit) // Wei-per-gas → Wei + .divideByDecimals(selectedNetwork.decimals) // Wei → ETH conversion + .format(6) + + const customEIP1559FiatGasFee = + customEIP1559GasFee + && spotPriceRegistry + && new Amount(customEIP1559GasFee) + .times(getTokenPriceAmountFromRegistry(spotPriceRegistry, networkAsset)) + .formatAsFiat(defaultFiatCurrency) + + const isUpdateButtonDisabled = React.useMemo(() => { + if (gasLimit === '') { + return true + } + + if (new Amount(gasLimit).lte(0)) { + return true + } + + if (!isEIP1559Transaction && gasPrice === '') { + return true + } + + if ( + !isEIP1559Transaction + && new Amount(gasPrice).multiplyByDecimals(9).isNegative() + ) { + return true + } + + if (isEIP1559Transaction && maxFeePerGas === '') { + return true + } + + return ( + isEIP1559Transaction + && new Amount(maxPriorityFeePerGas).multiplyByDecimals(9).isNegative() + ) + }, [ + gasLimit, + isEIP1559Transaction, + gasPrice, + maxFeePerGas, + maxPriorityFeePerGas, + ]) + const isZeroGasPrice = React.useMemo(() => { + return ( + !isEIP1559Transaction + && gasPrice !== '' + && new Amount(gasPrice).multiplyByDecimals(9).isZero() + ) + }, [gasPrice, isEIP1559Transaction]) + + // Effects + React.useEffect(() => { + const maxPriorityFeePerGasWei = new Amount( + maxPriorityFeePerGas, + ).multiplyByDecimals(9) // GWei-per-gas → Wei conversion + + const maxFeePerGasWeiValue = new Amount(baseFeePerGas).plus( + maxPriorityFeePerGasWei, + ) + + setMaxFeePerGas( + maxFeePerGasWeiValue + .divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion + .format(), + ) + }, [maxPriorityFeePerGas, baseFeePerGas]) + + // render + return ( + + + {getLocale('braveWalletEditGasDescription')} + + + {isEIP1559Transaction && ( + <> + + + {getLocale('braveWalletEditGasBaseFee')} + + + {new Amount(baseFeePerGas).divideByDecimals(9).format()}{' '} + {getLocale('braveWalletEditGasGwei')} + + + + + )} + + + {getLocale('braveWalletEditGasLimit')} + + + + + + + {gasLimit === '0' && ( + + + {getLocale('braveWalletEditGasLimitError')} + + + )} + + {!isEIP1559Transaction && ( + <> + + + {getLocale('braveWalletGasPrice')} + + + + + {getLocale('braveWalletEditGasGwei')} + + + + {isZeroGasPrice && ( + + + {getLocale('braveWalletEditGasZeroGasPriceWarning')} + + + )} + + )} + {isEIP1559Transaction && ( + <> + + + {getLocale('braveWalletGasTipLimit')} + + + + + {getLocale('braveWalletEditGasGwei')} + + + + + + + {getLocale('braveWalletGasPriceLimit')} + + + + + {getLocale('braveWalletEditGasGwei')} + + + + {isCustomGasBelowBaseFee && ( + + + {getLocale('braveWalletGasFeeLimitLowerThanBaseFeeWarning')} + + + )} + + + + {getLocale('braveWalletEditGasEstimatedNetworkFee')} + + + + ~{customEIP1559FiatGasFee} + + + ~ + {new Amount(customEIP1559GasFee).formatAsAsset( + 6, + networkAsset.symbol, + )} + + + + + + + + + )} + + + + + + ) +} + +export default CustomNetworkFee diff --git a/components/brave_wallet_ui/components/extension/edit_network_fee/suggested_max_priority_fee_selector/suggested_max_priority_fee_selector.test.tsx b/components/brave_wallet_ui/components/extension/edit_network_fee/suggested_max_priority_fee_selector/suggested_max_priority_fee_selector.test.tsx index a2ce9bf6bfe..09e2478b705 100644 --- a/components/brave_wallet_ui/components/extension/edit_network_fee/suggested_max_priority_fee_selector/suggested_max_priority_fee_selector.test.tsx +++ b/components/brave_wallet_ui/components/extension/edit_network_fee/suggested_max_priority_fee_selector/suggested_max_priority_fee_selector.test.tsx @@ -12,6 +12,12 @@ import { SuggestedMaxPriorityFeeSelector, // } from './suggested_max_priority_fee_selector' +// Utils +import Amount from '../../../../utils/amount' +import { + parseTransactionFeesWithoutPrices, // +} from '../../../../utils/tx-utils' + // Mocks import { mockEthMainnet } from '../../../../stories/mock-data/mock-networks' import { @@ -48,6 +54,16 @@ jest.mock('../../../../common/slices/api.slice', () => { }) describe('SuggestedMaxPriorityFeeSelector', () => { + const baseFeePerGas = '0x59682f00' // 1500000000 wei (1.5 gwei) + const transactionFees = parseTransactionFeesWithoutPrices(mockTransactionInfo) + const suggestedFees = mockSuggestedMaxPriorityFeeOptions.map((option) => + new Amount(baseFeePerGas) + .plus(option.fee) + .times(transactionFees.gasLimit) // Wei-per-gas → Wei conversion + .divideByDecimals(mockEthMainnet.decimals) // Wei → ETH conversion + .format(4), + ) + const renderComponent = () => { const store = createMockStore({}) return render( @@ -56,7 +72,7 @@ describe('SuggestedMaxPriorityFeeSelector', () => { {}} @@ -82,9 +98,15 @@ describe('SuggestedMaxPriorityFeeSelector', () => { expect(screen.getByText('1 min')).toBeInTheDocument() // Check if gas fees are displayed - expect(screen.getByText('0.003048 ETH')).toBeInTheDocument() - expect(screen.getByText('0.01171 ETH')).toBeInTheDocument() - expect(screen.getByText('0.02664 ETH')).toBeInTheDocument() + expect( + screen.getByText(suggestedFees[0] + ' ' + mockEthMainnet.symbol), + ).toBeInTheDocument() + expect( + screen.getByText(suggestedFees[1] + ' ' + mockEthMainnet.symbol), + ).toBeInTheDocument() + expect( + screen.getByText(suggestedFees[2] + ' ' + mockEthMainnet.symbol), + ).toBeInTheDocument() // Check if custom button is rendered expect(screen.getByText('braveWalletCustom')).toBeInTheDocument() diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index 7e673e9a14f..29cfc65a587 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -826,6 +826,7 @@ provideStrings({ braveWalletTransactionGasFeeCap: 'Gas Fee Cap', braveWalletNetworkFees: 'Network fees', braveWalletNetworkFee: 'Network fee', + braveWalletCustomFeeAmount: 'Custom fee amount', braveWalletTransactionMayIncludeAccountCreationFee: 'This transaction may include an account creation fee', braveWalletSystemProgramAssignWarningTitle: @@ -1052,6 +1053,11 @@ provideStrings({ braveWalletGasFeeLimitLowerThanBaseFeeWarning: 'Fee limit is set lower than the base fee. ' + 'Your transaction may take a long time or fail.', + braveWalletGasTipLimit: 'Gas tip limit', + braveWalletGasPriceLimit: 'Gas price limit', + braveWalletGasPrice: 'Gas price', + braveWalletEditGasEstimatedNetworkFee: 'Estimated network fee', + braveWalletUseDefault: 'Use default', // Advanced transaction settings braveWalletAdvancedTransactionSettings: 'Advanced settings', diff --git a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts index ec0f97115dc..8017a2f70cd 100644 --- a/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts +++ b/components/brave_wallet_ui/stories/mock-data/mock-transaction-info.ts @@ -37,8 +37,8 @@ export const mockTransactionInfo: SerializableTransactionInfo = { ethTxData1559: { baseData: { nonce: '0x1', - gasPrice: '150', - gasLimit: '21000', + gasPrice: '100000000', + gasLimit: '122665', // wei to: '2', value: '0x15ddf09c97b0000', data: Array.from(new Uint8Array(24)), @@ -46,8 +46,8 @@ export const mockTransactionInfo: SerializableTransactionInfo = { signedTransaction: undefined, }, chainId: '0x0', - maxPriorityFeePerGas: '1', - maxFeePerGas: '1', + maxPriorityFeePerGas: '80410000', // (0.08041 gwei) + maxFeePerGas: '3600000000', // (3.6 gwei) gasEstimation: undefined, }, ethTxData: undefined, diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index 786859c76e0..eb6b5fe5e71 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -1088,6 +1088,12 @@ Refreshing Network fees Network fee + Custom fee amount + Gas tip limit + Gas price limit + Gas price + Estimated network fee + Use default Brave Wallet Receive From