feat(wallet): Custom Network Fee Popup (#30427)

This commit is contained in:
Douglas Daniel
2025-08-11 09:49:45 -05:00
committed by GitHub
parent e4306188af
commit c62435cdcb
10 changed files with 839 additions and 9 deletions
@@ -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},
@@ -718,7 +718,7 @@ export class MockedWalletApiProxy {
assetTimeframeChange: '1',
fromAsset: fromAssets[0],
toAsset: toAssets[0],
price: '1234.56',
price: '3873.78',
},
],
}
@@ -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 (
<BottomSheet
isOpen={true}
title={getLocale('braveWalletCustomFeeAmount')}
onClose={() => alert('Close Clicked')}
>
<CustomNetworkFee
transactionInfo={mockTransactionInfo}
selectedNetwork={mockEthMainnet}
baseFeePerGas='3641000000' // (3.641 gwei)
onUpdateCustomNetworkFee={() => alert('Update Clicked')}
onBack={() => alert('Back Clicked')}
onClose={() => alert('Close Clicked')}
/>
</BottomSheet>
)
},
}
export default {
title: 'Wallet/Panel/Components/Edit Network Fee',
component: _CustomNetworkFee,
parameters: {
layout: 'centered',
},
decorators: [
(Story: any) => (
<WalletPanelStory>
<Story />
</WalletPanelStory>
),
],
}
@@ -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};
`
@@ -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(
<Provider store={store}>
<BraveCoreThemeProvider>
<CustomNetworkFee
transactionInfo={mockTransactionInfo}
selectedNetwork={mockEthMainnet}
baseFeePerGas={mockBaseFeePerGas}
onUpdateCustomNetworkFee={mockOnUpdateCustomNetworkFee}
onBack={mockOnBack}
onClose={mockOnClose}
/>
</BraveCoreThemeProvider>
</Provider>,
)
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(
<Provider store={store}>
<BraveCoreThemeProvider>
<CustomNetworkFee
transactionInfo={nonEip1559TransactionInfo}
selectedNetwork={mockEthMainnet}
baseFeePerGas={mockBaseFeePerGas}
onUpdateCustomNetworkFee={mockOnUpdateCustomNetworkFee}
onBack={mockOnBack}
onClose={mockOnClose}
/>
</BraveCoreThemeProvider>
</Provider>,
)
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()
})
})
})
@@ -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<string>(
transactionFees.gasLimit,
)
const [gasPrice, setGasPrice] = React.useState<string>(
new Amount(transactionFees.gasPrice)
.divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion
.format(),
)
const [maxPriorityFeePerGas, setMaxPriorityFeePerGas] =
React.useState<string>(
new Amount(transactionFees.maxPriorityFeePerGas)
.divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion
.format(),
)
const [maxFeePerGas, setMaxFeePerGas] = React.useState<string>(
new Amount(transactionFees.maxFeePerGas)
.divideByDecimals(9) // Wei-per-gas → GWei-per-gas conversion
.format(),
)
// Methods
const handleGasLimitInputChanged = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
setGasLimit(event.target.value)
}
const handleMaxPriorityFeePerGasInputChanged = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
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<HTMLInputElement>,
) => {
setMaxFeePerGas(event.target.value)
}
const handleGasPriceInputChanged = (
event: React.ChangeEvent<HTMLInputElement>,
) => {
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 (
<StyledWrapper
width='100%'
height='100%'
justifyContent='space-between'
padding='16px'
gap='16px'
>
<Description
textColor='tertiary'
textAlign='left'
>
{getLocale('braveWalletEditGasDescription')}
</Description>
<Card
width='100%'
padding={isEIP1559Transaction ? '16px 16px 8px 16px' : '16px'}
gap='8px'
>
{isEIP1559Transaction && (
<>
<Row
justifyContent='space-between'
alignItems='flex-start'
padding='8px 0px'
>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletEditGasBaseFee')}
</SectionLabel>
<SectionLabel textColor='primary'>
{new Amount(baseFeePerGas).divideByDecimals(9).format()}{' '}
{getLocale('braveWalletEditGasGwei')}
</SectionLabel>
</Row>
<VerticalDivider />
</>
)}
<Row justifyContent='space-between'>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletEditGasLimit')}
</SectionLabel>
<InputWrapper
width='140px'
padding='8px 12px'
hasError={gasLimit === '0'}
>
<Input
placeholder='0'
type='number'
min={0}
value={gasLimit}
onChange={handleGasLimitInputChanged}
data-testid='gas-limit-input'
/>
</InputWrapper>
</Row>
{gasLimit === '0' && (
<Row justifyContent='flex-start'>
<InputLabel
textColor='error'
textAlign='left'
>
{getLocale('braveWalletEditGasLimitError')}
</InputLabel>
</Row>
)}
<VerticalDivider />
{!isEIP1559Transaction && (
<>
<Row justifyContent='space-between'>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletGasPrice')}
</SectionLabel>
<InputWrapper
width='140px'
gap='6px'
padding='8px 12px'
hasError={isZeroGasPrice}
>
<Input
placeholder='0'
type='number'
value={gasPrice}
onChange={handleGasPriceInputChanged}
data-testid='gas-price-input'
/>
<InputLabel textColor='tertiary'>
{getLocale('braveWalletEditGasGwei')}
</InputLabel>
</InputWrapper>
</Row>
{isZeroGasPrice && (
<Row justifyContent='flex-start'>
<InputLabel
textColor='error'
textAlign='left'
>
{getLocale('braveWalletEditGasZeroGasPriceWarning')}
</InputLabel>
</Row>
)}
</>
)}
{isEIP1559Transaction && (
<>
<Row justifyContent='space-between'>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletGasTipLimit')}
</SectionLabel>
<InputWrapper
width='140px'
gap='6px'
padding='8px 12px'
hasError={isCustomGasBelowBaseFee}
>
<Input
placeholder='0'
type='number'
min={0}
value={maxPriorityFeePerGas}
onChange={handleMaxPriorityFeePerGasInputChanged}
/>
<InputLabel textColor='tertiary'>
{getLocale('braveWalletEditGasGwei')}
</InputLabel>
</InputWrapper>
</Row>
<VerticalDivider />
<Row justifyContent='space-between'>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletGasPriceLimit')}
</SectionLabel>
<InputWrapper
width='140px'
gap='6px'
padding='8px 12px'
hasError={isCustomGasBelowBaseFee}
>
<Input
placeholder='0'
min={0}
type='number'
value={maxFeePerGas}
onChange={handleMaxFeePerGasInputChanged}
data-testid='gas-price-limit-input'
/>
<InputLabel textColor='tertiary'>
{getLocale('braveWalletEditGasGwei')}
</InputLabel>
</InputWrapper>
</Row>
{isCustomGasBelowBaseFee && (
<Row justifyContent='flex-start'>
<InputLabel
textColor='error'
textAlign='left'
>
{getLocale('braveWalletGasFeeLimitLowerThanBaseFeeWarning')}
</InputLabel>
</Row>
)}
<VerticalDivider />
<Row
justifyContent='space-between'
alignItems='flex-start'
padding='8px 0px'
>
<SectionLabel textColor='secondary'>
{getLocale('braveWalletEditGasEstimatedNetworkFee')}
</SectionLabel>
<Column alignItems='flex-end'>
<SectionLabel textColor='primary'>
~{customEIP1559FiatGasFee}
</SectionLabel>
<SectionLabel textColor='primary'>
~
{new Amount(customEIP1559GasFee).formatAsAsset(
6,
networkAsset.symbol,
)}
</SectionLabel>
</Column>
</Row>
<VerticalDivider />
<Row width='unset'>
<Button
kind='plain-faint'
size='tiny'
onClick={onBack}
>
{getLocale('braveWalletUseDefault')}
</Button>
</Row>
</>
)}
</Card>
<Row>
<Button
isDisabled={isUpdateButtonDisabled}
onClick={onClickUpdate}
>
{getLocale('braveWalletUpdate')}
</Button>
</Row>
</StyledWrapper>
)
}
export default CustomNetworkFee
@@ -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', () => {
<SuggestedMaxPriorityFeeSelector
transactionInfo={mockTransactionInfo}
selectedNetwork={mockEthMainnet}
baseFeePerGas={'0x59682f00'} // 1500000000 wei (1.5 gwei)
baseFeePerGas={baseFeePerGas}
suggestedMaxPriorityFee='average'
suggestedMaxPriorityFeeOptions={mockSuggestedMaxPriorityFeeOptions}
setSuggestedMaxPriorityFee={() => {}}
@@ -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()
@@ -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',
@@ -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,
+6
View File
@@ -1088,6 +1088,12 @@
<message name="IDS_BRAVE_WALLET_AUTO_DISCOVERY_EMPTY_STATE_REFRESH" desc="NFTs tab empty state refreshing text">Refreshing</message>
<message name="IDS_BRAVE_WALLET_NETWORK_FEES" desc="Label for blockchain network fees">Network fees</message>
<message name="IDS_BRAVE_WALLET_NETWORK_FEE" desc="Label for blockchain network fee">Network fee</message>
<message name="IDS_BRAVE_WALLET_CUSTOM_FEE_AMOUNT" desc="Label for custom fee amount">Custom fee amount</message>
<message name="IDS_BRAVE_WALLET_GAS_TIP_LIMIT" desc="Label for gas tip limit">Gas tip limit</message>
<message name="IDS_BRAVE_WALLET_GAS_PRICE_LIMIT" desc="Label for gas price limit">Gas price limit</message>
<message name="IDS_BRAVE_WALLET_GAS_PRICE" desc="Label for gas price">Gas price</message>
<message name="IDS_BRAVE_WALLET_EDIT_GAS_ESTIMATED_NETWORK_FEE" desc="Label for estimated network fee">Estimated network fee</message>
<message name="IDS_BRAVE_WALLET_USE_DEFAULT" desc="Label for use default">Use default</message>
<message name="IDS_WALLET_EIP6963_PROVIDER_NAME" desc="Provider name which will be return when announce ethereum provider">Brave Wallet</message>
<message name="IDS_BRAVE_WALLET_RECEIVE" desc="Receive asset event title">Receive</message>
<message name="IDS_BRAVE_WALLET_FROM" desc="From address label">From</message>