From da997b211cf07670e2625650d9c619e8aed7f32d Mon Sep 17 00:00:00 2001 From: Albina Nikiforova Date: Wed, 29 Jan 2025 15:03:21 +0100 Subject: [PATCH] feat(suite): Fees redesign feat(components): disable vertical align of SelectBar on demand --- .../components/form/SelectBar/SelectBar.tsx | 67 ++- .../form/SelectBar/breakpointUtils.ts | 2 + .../ClaimModal/ClaimModal.tsx | 2 + .../StakeModal/StakeEthForm/StakeEthForm.tsx | 2 + .../TxDetailModal/ChangeFee/RbfFees.tsx | 2 + .../UnstakeEthForm/UnstakeEthForm.tsx | 2 + .../src/components/wallet/Fees/CustomFee.tsx | 58 ++- .../src/components/wallet/Fees/FeeDetails.tsx | 258 +++++++++--- .../suite/src/components/wallet/Fees/Fees.tsx | 386 ++++++++++++------ .../src/components/wallet/Fees/consts.ts | 5 + packages/suite/src/support/messages.ts | 22 + .../suite/src/views/wallet/send/SendFees.tsx | 2 + .../common/TradingForm/TradingFormInputs.tsx | 65 ++- 13 files changed, 625 insertions(+), 248 deletions(-) create mode 100644 packages/components/src/components/form/SelectBar/breakpointUtils.ts create mode 100644 packages/suite/src/components/wallet/Fees/consts.ts diff --git a/packages/components/src/components/form/SelectBar/SelectBar.tsx b/packages/components/src/components/form/SelectBar/SelectBar.tsx index f87e6b6a5a7c..e8c1710e8822 100644 --- a/packages/components/src/components/form/SelectBar/SelectBar.tsx +++ b/packages/components/src/components/form/SelectBar/SelectBar.tsx @@ -26,17 +26,26 @@ import { useElevation } from '../../ElevationContext/ElevationContext'; export const allowedSelectBarFrameProps = ['margin', 'width'] as const satisfies FramePropsKeys[]; type AllowedFrameProps = Pick; -const Wrapper = styled.div & { $isFullWidth?: boolean }>` +const Wrapper = styled.div< + TransientProps & { + $isFullWidth?: boolean; + $shouldRealignToVertical?: boolean; + } +>` display: flex; align-items: center; gap: ${spacingsPx.sm}; width: ${({ $isFullWidth }) => ($isFullWidth ? '100%' : 'auto')}; - ${breakpointMediaQueries.below_sm} { - flex-direction: column; - align-items: flex-start; - width: 100%; - } + ${({ $shouldRealignToVertical }) => + $shouldRealignToVertical && + css` + @container ${breakpointMediaQueries.below_sm} { + flex-direction: column; + align-items: flex-start; + width: 100%; + } + `} ${withFrameProps} `; @@ -63,6 +72,7 @@ const Options = styled.div<{ $optionsCount: number; $isFullWidth?: boolean; $elevation: Elevation; + $shouldRealignToColumn?: boolean; }>` position: relative; display: grid; @@ -74,14 +84,23 @@ const Options = styled.div<{ border-radius: ${borders.radii.full}; width: ${({ $isFullWidth }) => ($isFullWidth ? '100%' : 'auto')}; - ${breakpointMediaQueries.below_sm} { - grid-auto-flow: row; - width: 100%; - border-radius: ${borders.radii.lg}; - } + ${({ $shouldRealignToColumn }) => + $shouldRealignToColumn && + css` + @container ${breakpointMediaQueries.below_sm} { + grid-auto-flow: row; + width: 100%; + border-radius: ${borders.radii.lg}; + } + `} `; -const Puck = styled.div<{ $optionsCount: number; $selectedIndex: number; $elevation: Elevation }>` +const Puck = styled.div<{ + $optionsCount: number; + $selectedIndex: number; + $elevation: Elevation; + $shouldRealignToVertical?: boolean; +}>` position: absolute; left: 4px; top: 4px; @@ -98,15 +117,18 @@ const Puck = styled.div<{ $optionsCount: number; $selectedIndex: number; $elevat ${getFocusShadowStyle()} - ${breakpointMediaQueries.below_sm} { - left: 4px; - right: 4px; - top: 4px; - width: auto; - height: ${({ $optionsCount }) => getPuckWidth($optionsCount)}; - transform: ${({ $selectedIndex: selectedIndex }) => - `translateY(${getTranslateValue(selectedIndex)})`}; - } + ${({ $shouldRealignToVertical, $optionsCount, $selectedIndex }) => + $shouldRealignToVertical && + css` + @container ${breakpointMediaQueries.below_sm} { + left: 4px; + right: 4px; + top: 4px; + width: auto; + height: ${getPuckWidth($optionsCount)}; + transform: translateY(${getTranslateValue($selectedIndex)}); + } + `} `; const WidthMock = styled.span` @@ -166,6 +188,7 @@ export type SelectBarProps = { isDisabled?: boolean; isFullWidth?: boolean; className?: string; + shouldRealignToVertical?: boolean; 'data-testid'?: string; } & AllowedFrameProps; @@ -178,6 +201,7 @@ export const SelectBar = ({ isDisabled = false, isFullWidth, className, + shouldRealignToVertical = true, 'data-testid': dataTest, ...rest }: SelectBarProps) => { @@ -238,6 +262,7 @@ export const SelectBar = ({ diff --git a/packages/components/src/components/form/SelectBar/breakpointUtils.ts b/packages/components/src/components/form/SelectBar/breakpointUtils.ts new file mode 100644 index 000000000000..5848fee22df2 --- /dev/null +++ b/packages/components/src/components/form/SelectBar/breakpointUtils.ts @@ -0,0 +1,2 @@ +export const constructContainerQueryMaxWidth = (breakpoint: number) => + `@container (max-width: ${breakpoint}px)`; diff --git a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/ClaimModal/ClaimModal.tsx b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/ClaimModal/ClaimModal.tsx index 0e11083d351b..33a8834f5e90 100644 --- a/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/ClaimModal/ClaimModal.tsx +++ b/packages/suite/src/components/suite/modals/ReduxModal/UserContextModal/ClaimModal/ClaimModal.tsx @@ -26,6 +26,7 @@ export const ClaimModal = ({ onCancel }: ClaimModalModalProps) => { const { account, + network, formState: { errors, isSubmitting }, register, control, @@ -113,6 +114,7 @@ export const ClaimModal = ({ onCancel }: ClaimModalModalProps) => { { const { account, + network, isConfirmModalOpen, closeConfirmModal, signTx, @@ -45,6 +46,7 @@ export const StakeEthForm = () => { { account, feeInfo, composedLevels, + network, } = useRbfContext(); return ( { control, setValue, getValues, + network, changeFeeLevel, feeInfo, composedLevels, @@ -76,6 +77,7 @@ export const UnstakeEthForm = () => { { control: Control; setValue: UseFormSetValue; getValues: UseFormGetValues; - changeFeeLimit?: (value: string) => void; composedFeePerByte: string; } +const getAverageFee = (levels: FeeLevel[]) => { + if (levels.length > 2) { + return `${levels[1].feePerUnit}`; + } + + return `${levels[0].feePerUnit}`; +}; + export const CustomFee = ({ networkType, feeInfo, register, control, - changeFeeLimit, composedFeePerByte, ...props }: CustomFeeProps) => { @@ -66,6 +85,7 @@ export const CustomFee = ({ const feePerUnitError = errors.feePerUnit; const feeLimitError = errors.feeLimit; + const { elevation } = useElevation(); const useFeeLimit = networkType === 'ethereum'; const isComposedFeeRateDifferent = @@ -146,7 +166,7 @@ export const CustomFee = ({ feeLimitError?.type === 'feeLimit' ? feeLimitValidationProps : undefined; return ( - + ({ > + + + + + : + + + + {getAverageFee(feeInfo.levels)} {getFeeUnits(networkType)} + + + + + {useFeeLimit ? ( ({ inputState={getInputState(feeLimitError)} name={FEE_LIMIT} data-testid={FEE_LIMIT} - onChange={changeFeeLimit} bottomText={ feeLimitError?.message ? ( void; + changeFeeLevel: (level: FeeLevel['label']) => void; transactionInfo?: PrecomposedTransaction | PrecomposedTransactionCardano; showFee: boolean; }; -type ItemProps = { - label: React.ReactNode; - children: React.ReactNode; +export const InlineSizeWrapper = styled.div` + container-type: inline-size; + width: 100%; +`; + +const FeeCardsWrapper = styled.div<{ $cardsAmount: number }>` + display: grid; + grid-template-columns: repeat(${({ $cardsAmount }) => $cardsAmount}, 1fr); + + @container ${collapsedFeeGridQuery} { + grid-template-columns: repeat(1, 1fr); + } +`; + +type FeeCardProps = { + value: FeeLevel['label']; + setSelectedLevelOption: (option: string) => void; + isSelected: boolean; + changeFeeLevel: (level: FeeLevel['label']) => void; + topLeftChild: React.ReactNode; + topRightChild: React.ReactNode; + bottomLeftChild: React.ReactNode; + bottomRightChild: React.ReactNode; }; -const Item = ({ label, children }: ItemProps) => ( - - {label}: - {children} - +const FeeCard = ({ + value, + setSelectedLevelOption, + isSelected, + changeFeeLevel, + topLeftChild, + topRightChild, + bottomLeftChild, + bottomRightChild, +}: FeeCardProps) => ( + + { + setSelectedLevelOption(value); + changeFeeLevel(value); + }} + isActive={isSelected} + > + + + {topLeftChild} + {topRightChild} + + + {bottomLeftChild} + + {bottomRightChild} + + + + + ); const BitcoinDetails = ({ networkType, feeInfo, - selectedLevel, transactionInfo, + feeOptions, showFee, + setSelectedLevelOption, + selectedLevelOption, + changeFeeLevel, + symbol, }: DetailsProps) => { const hasInfo = transactionInfo && transactionInfo.type !== 'error'; return ( showFee && ( - <> - }> - {formatDuration(feeInfo.blockTime * selectedLevel.blocks * 60)} - - - }> - {hasInfo ? transactionInfo.feePerByte : selectedLevel.feePerUnit}{' '} - {getFeeUnits(networkType)} - {hasInfo ? ` (${transactionInfo.bytes} B)` : ''} - - + + + {feeOptions && + feeOptions.map((fee, index) => ( + + ~ + {formatDuration( + feeInfo.blockTime * (fee?.blocks || 0) * 60, + )} + + } + bottomLeftChild={ + + } + bottomRightChild={ + <> + {fee?.feePerUnit} {getFeeUnits(networkType)} + {hasInfo ? ` (${transactionInfo.bytes} B)` : ''} + + } + /> + ))} + + ) ); }; const EthereumDetails = ({ - networkType, - selectedLevel, - transactionInfo, showFee, + feeOptions, + setSelectedLevelOption, + selectedLevelOption, + changeFeeLevel, + symbol, }: DetailsProps) => { - // States to remember the last known values of feeLimit and feePerByte when isComposedTx was true. - const [lastKnownFeeLimit, setLastKnownFeeLimit] = useState(''); - const [lastKnownFeePerByte, setLastKnownFeePerByte] = useState(''); - - const isComposedTx = transactionInfo && transactionInfo.type !== 'error'; - - useEffect(() => { - if (isComposedTx && transactionInfo.feeLimit) { - setLastKnownFeeLimit(transactionInfo.feeLimit); - setLastKnownFeePerByte(transactionInfo.feePerByte); - } - }, [isComposedTx, transactionInfo]); - - const gasLimit = isComposedTx - ? transactionInfo.feeLimit - : lastKnownFeeLimit || selectedLevel.feeLimit; - const gasPrice = isComposedTx - ? transactionInfo.feePerByte - : lastKnownFeePerByte || selectedLevel.feePerUnit; + const isBelowTablet = useMediaQuery(`(max-width: ${variables.SCREEN_SIZE.MD})`); return ( showFee && ( - <> - }>{gasLimit} - - }> - {gasPrice} {getFeeUnits(networkType)} - - + + + {feeOptions && + feeOptions.map((fee, index) => ( + + } + bottomRightChild={ + + } + /> + ))} + + ) ); }; -const RippleDetails = ({ networkType, selectedLevel, showFee }: DetailsProps) => - showFee && {selectedLevel.feePerUnit}; +// Solana, Ripple, Cardano and other networks with only one option +const MiscDetails = ({ networkType, showFee, feeOptions, symbol }: DetailsProps) => + showFee && ( + + {}} + isSelected={true} + changeFeeLevel={() => {}} + topLeftChild={feeOptions[0].label} + topRightChild="" + bottomLeftChild={ + + } + bottomRightChild={ + + {feeOptions[0].feePerUnit} {getFeeUnits(networkType)} + + } + /> + + ); export const FeeDetails = (props: DetailsProps) => { const { networkType } = props; return ( - - + + {networkType === 'bitcoin' && } {networkType === 'ethereum' && } - {networkType === 'ripple' && } + {networkType !== 'bitcoin' && networkType !== 'ethereum' && ( + + )} ); diff --git a/packages/suite/src/components/wallet/Fees/Fees.tsx b/packages/suite/src/components/wallet/Fees/Fees.tsx index 86b69817217f..658e91abbf6f 100644 --- a/packages/suite/src/components/wallet/Fees/Fees.tsx +++ b/packages/suite/src/components/wallet/Fees/Fees.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { Control, FieldErrors, @@ -8,8 +9,10 @@ import { } from 'react-hook-form'; import { AnimatePresence, motion } from 'framer-motion'; +import styled from 'styled-components'; import { TranslationKey } from '@suite-common/intl-types'; +import { Network, NetworkSymbol, NetworkType } from '@suite-common/wallet-config'; import { FeeInfo, FormState, @@ -19,6 +22,7 @@ import { } from '@suite-common/wallet-types'; import { formatNetworkAmount } from '@suite-common/wallet-utils'; import { + Badge, Banner, Column, InfoItem, @@ -28,15 +32,24 @@ import { Text, Tooltip, motionEasing, + useElevation, } from '@trezor/components'; import { FeeLevel } from '@trezor/connect'; -import { spacings } from '@trezor/theme'; +import { + Colors, + Elevation, + borders, + mapElevationToBorder, + spacings, + spacingsPx, +} from '@trezor/theme'; import { FiatValue, FormattedCryptoAmount, Translation } from 'src/components/suite'; import { Account } from 'src/types/wallet'; import { CustomFee } from './CustomFee'; -import { FeeDetails } from './FeeDetails'; +import { FeeDetails, InlineSizeWrapper } from './FeeDetails'; +import { collapsedFeeHeaderQuery } from './consts'; const FEE_LEVELS_TRANSLATIONS: Record = { custom: 'FEE_LEVEL_CUSTOM', @@ -46,11 +59,82 @@ const FEE_LEVELS_TRANSLATIONS: Record = { low: 'FEE_LEVEL_LOW', } as const; -const buildFeeOptions = (levels: FeeLevel[]) => - levels.map(({ label }) => ({ - label: , - value: label, - })); +export const BorderTopWrapper = styled.div<{ theme: Colors; $elevation: Elevation }>` + width: 100%; + padding-top: ${spacingsPx.sm}; + border-top: ${borders.widths.small} solid + ${({ theme, $elevation }) => mapElevationToBorder({ theme, $elevation })}; +`; + +export const buildFeeOptions = ( + levels: FeeLevel[], + networkType: NetworkType, + symbol: NetworkSymbol, + composedLevels?: PrecomposedLevels | PrecomposedLevelsCardano, +) => { + const filteredLevels = levels.filter(level => level.label !== 'custom'); + + if (networkType === 'ethereum') { + //legacy fee format + return filteredLevels.map(level => { + const transactionInfo = composedLevels?.[level.label]; + const hasTransactionInfo = + transactionInfo !== undefined && transactionInfo.type !== 'error'; + const networkAmount = hasTransactionInfo + ? formatNetworkAmount(transactionInfo.fee, symbol) + : null; + + return { + label: , + value: level.label, + networkAmount, + }; + }); + } + + if (networkType === 'bitcoin') { + return filteredLevels.map(level => { + const transactionInfo = composedLevels?.[level.label]; + const hasTransactionInfo = + transactionInfo !== undefined && transactionInfo.type !== 'error'; + const networkAmount = hasTransactionInfo + ? formatNetworkAmount(transactionInfo.fee, symbol) + : null; + + return { + label: , + value: level.label, + blocks: level.blocks, + feePerUnit: level.feePerUnit, + networkAmount, + }; + }); + } + + return filteredLevels.map(level => { + const transactionInfo = composedLevels?.[level.label]; + const hasTransactionInfo = + transactionInfo !== undefined && transactionInfo.type !== 'error'; + const networkAmount = hasTransactionInfo + ? formatNetworkAmount(transactionInfo.fee, symbol) + : null; + + return { + label: , + value: level.label, + feePerUnit: level.feePerUnit, + networkAmount, + }; + }); +}; + +export type FeeOption = { + label: React.ReactNode; + value: FeeLevel['label']; + blocks?: number; + feePerUnit?: string; + networkAmount?: string | null; +}; export interface FeesProps { account: Account; @@ -61,11 +145,11 @@ export interface FeesProps { getValues: UseFormGetValues; errors: FieldErrors; changeFeeLevel: (level: FeeLevel['label']) => void; - changeFeeLimit?: (value: string) => void; composedLevels?: PrecomposedLevels | PrecomposedLevelsCardano; label?: TranslationKey; rbfForm?: boolean; helperText?: React.ReactNode; + network: Network; } export const Fees = ({ @@ -73,143 +157,203 @@ export const Fees = ({ feeInfo, control, changeFeeLevel, - changeFeeLimit, composedLevels, label, rbfForm, helperText, + network, ...props }: FeesProps) => { // Type assertion allowing to make the component reusable, see https://stackoverflow.com/a/73624072. const { getValues, register, setValue } = props as unknown as UseFormReturn; + const [isCustomFee, setIsCustomFee] = useState(false); + const [selectedLevelOption, setSelectedLevelOption] = useState('normal'); const errors = props.errors as unknown as FieldErrors; - const selectedOption = getValues('selectedFee') || 'normal'; - const isCustomLevel = selectedOption === 'custom'; - + const selectedOption = isCustomFee ? 'custom' : selectedLevelOption; const error = errors.selectedFee; const selectedLevel = feeInfo.levels.find(level => level.label === selectedOption)!; const transactionInfo = composedLevels?.[selectedOption]; - const hasTransactionInfo = transactionInfo !== undefined && transactionInfo.type !== 'error'; - // Solana has only `normal` fee level, so we do not display any feeOptions since there is nothing to choose from - const feeOptions = networkType === 'solana' ? [] : buildFeeOptions(feeInfo.levels); - - const shouldAnimateNormalFee = !isCustomLevel; + const feeOptions = buildFeeOptions(feeInfo.levels, networkType, symbol, composedLevels); + const hasTransactionInfo = transactionInfo !== undefined && transactionInfo.type !== 'error'; const networkAmount = hasTransactionInfo ? formatNetworkAmount(transactionInfo.fee, symbol) : null; + const FeeHeaderWrapper = styled.div` + display: grid; + + grid-template-columns: repeat(2, 1fr); + @container ${collapsedFeeHeaderQuery} { + grid-template-columns: repeat(1, 1fr); + } + `; + + const SelectBarWrapper = styled.div` + width: 239px; + justify-self: end; + @container ${collapsedFeeHeaderQuery} { + margin-top: ${spacingsPx.sm}; + width: 100%; + } + `; + const { elevation } = useElevation(); + return ( - - } - > - - - ) : ( - - ) - } - > - {networkAmount && ( - - + + + + + + + ) : ( + + ) + } + > + + + + + + } /> - - - - )} - - - {feeOptions.length > 0 && ( - <> - - - {shouldAnimateNormalFee && ( - + { + changeFeeLevel(!isCustomFee ? 'custom' : 'normal'); + setIsCustomFee(!isCustomFee); }} - > - - - )} - - - - {isCustomLevel && ( - - - - )} - - - {error && ( - - {error.message} - + isFullWidth + /> + )} + + + + <> + + {!isCustomFee && ( + + + + )} + + + + {isCustomFee && ( + + + {networkAmount && ( + + + + + : + + + + + + + + + + + )} + + )} + + {error && ( + + {error.message} + + )} - {helperText && {helperText}} - - )} + {helperText && {helperText}} + ); }; diff --git a/packages/suite/src/components/wallet/Fees/consts.ts b/packages/suite/src/components/wallet/Fees/consts.ts new file mode 100644 index 000000000000..0f988ec30361 --- /dev/null +++ b/packages/suite/src/components/wallet/Fees/consts.ts @@ -0,0 +1,5 @@ +export const COLLAPSED_FEE_GRID_WIDTH = 530; +export const COLLAPSED_FEE_HEADER_WIDTH = 414; + +export const collapsedFeeGridQuery = `(max-width: ${COLLAPSED_FEE_GRID_WIDTH}px)`; +export const collapsedFeeHeaderQuery = `(max-width: ${COLLAPSED_FEE_HEADER_WIDTH}px)`; diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index dfd9e2eb0374..ac8ed0944a71 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -3433,6 +3433,14 @@ export default defineMessages({ id: 'TR_GAS_PRICE', defaultMessage: 'Gas price', }, + TR_AVERAGE_FEE: { + id: 'TR_AVERAGE_FEE', + defaultMessage: 'Average fee', + }, + TR_AVERAGE_GAS_PRICE: { + id: 'TR_AVERAGE_GAS_PRICE', + defaultMessage: 'Average gas price', + }, TR_GAS_LIMIT: { id: 'TR_GAS_LIMIT', defaultMessage: 'Gas limit', @@ -5594,6 +5602,10 @@ export default defineMessages({ description: 'Label in Send form for Ethereum network type', id: 'MAX_FEE', }, + WHY_FEES: { + defaultMessage: 'Why fees?', + id: 'WHY_FEES', + }, EXPECTED_FEE: { defaultMessage: 'Expected fee', description: 'Label in Send form for Solana network type', @@ -8789,6 +8801,16 @@ export default defineMessages({ defaultMessage: 'Maximum fee is the network transaction fee that you’re willing to pay on the network to ensure your transaction gets processed.', }, + TR_EVM_MAX_FEE_DESC: { + id: 'TR_EVM_MAX_FEE_DESC', + defaultMessage: + 'Set how much you’re willing to pay to process your transaction on the blockchain. A higher maximum fee may speed up your transaction confirmation.', + }, + TR_TRANSACTION_FEE_DESC: { + id: 'TR_TRANSACTION_FEE_DESC', + defaultMessage: + 'The highest transaction fee you’re willing to pay to ensure your transaction gets processed on the blockchain. A higher fee can speed up confirmation times.', + }, TR_STAKE_MAX: { id: 'TR_STAKE_MAX', defaultMessage: 'Max', diff --git a/packages/suite/src/views/wallet/send/SendFees.tsx b/packages/suite/src/views/wallet/send/SendFees.tsx index aa1a0d63d19c..7931482cb172 100644 --- a/packages/suite/src/views/wallet/send/SendFees.tsx +++ b/packages/suite/src/views/wallet/send/SendFees.tsx @@ -14,6 +14,7 @@ export const SendFees = () => { account, feeInfo, composedLevels, + network, } = useSendFormContext(); return ( @@ -28,6 +29,7 @@ export const SendFees = () => { account={account} composedLevels={composedLevels} changeFeeLevel={changeFeeLevel} + network={network} /> ); diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInputs.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInputs.tsx index 56877369cd32..6c594ea36db9 100644 --- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInputs.tsx +++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormInputs.tsx @@ -1,13 +1,6 @@ import { TokenAddress } from '@suite-common/wallet-types'; import { formatAmount } from '@suite-common/wallet-utils'; -import { - Card, - Column, - ElevationContext, - FractionButton, - FractionButtonProps, - Row, -} from '@trezor/components'; +import { Column, FractionButton, FractionButtonProps, Row } from '@trezor/components'; import { hasBitcoinOnlyFirmware } from '@trezor/device-utils/src/firmwareUtils'; import { spacings } from '@trezor/theme'; @@ -78,6 +71,7 @@ export const TradingFormInputs = () => { control, feeInfo, account, + network, composedLevels, formState: { errors }, form: { helpers }, @@ -131,21 +125,18 @@ export const TradingFormInputs = () => { )} - - - - - + @@ -157,6 +148,7 @@ export const TradingFormInputs = () => { control, feeInfo, account, + network, composedLevels, formState: { errors }, form: { helpers }, @@ -218,21 +210,18 @@ export const TradingFormInputs = () => { supportedCryptoCurrencies={supportedCryptoCurrencies} methods={{ ...context }} /> - - - - - + );