Revert "Feat: update fee rate options regularly"

This commit is contained in:
Jiri Zbytovsky
2025-06-03 12:41:36 +02:00
committed by Jiri Zbytovsky
parent 0b8406a03e
commit 26069e28b5
20 changed files with 129 additions and 301 deletions

View File

@@ -1,8 +1,5 @@
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { Note } from '@trezor/components';
import { isApproxEqual } from '@trezor/utils';
import { useSelector } from '../../../hooks/suite';
import { Translation } from '../../suite';
type DustPreventionNoticeProps = {
@@ -18,14 +15,10 @@ export const DustPreventionNotice = ({
baseFee,
feeUnits,
}: DustPreventionNoticeProps) => {
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
const relativeTolerance = 1e-3;
const isComposedFeeRateDifferent =
!areFeesLoading &&
composedFeePerByte !== undefined &&
chosenFeePerByte !== undefined &&
!isApproxEqual(composedFeePerByte, chosenFeePerByte, relativeTolerance);
composedFeePerByte !== '' &&
chosenFeePerByte !== composedFeePerByte;
if (!isComposedFeeRateDifferent) return null;

View File

@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import {
Control,
FieldErrors,
@@ -13,6 +13,7 @@ import { useTheme } from 'styled-components';
import { TranslationKey } from '@suite-common/intl-types';
import { NetworkSymbol, NetworkType } from '@suite-common/wallet-config';
import { updateFeeInfoThunk } from '@suite-common/wallet-core';
import {
FeeInfo,
FormState,
@@ -26,7 +27,7 @@ import { spacings } from '@trezor/theme';
import { HELP_CENTER_TRANSACTION_FEES_URL } from '@trezor/urls';
import { Translation } from 'src/components/suite';
import { useRefetchFees } from 'src/hooks/wallet/useRefetchFees';
import { useDispatch } from 'src/hooks/suite';
import { Account } from 'src/types/wallet';
import { CustomFee } from './CustomFee/CustomFee';
@@ -143,6 +144,7 @@ export const Fees = <TFieldValues extends FormState>({
// Type assertion allowing to make the component reusable, see https://stackoverflow.com/a/73624072.
const { getValues, register, setValue, trigger } = props as unknown as UseFormReturn<FormState>;
const theme = useTheme();
const dispatch = useDispatch();
const selectedOption = getValues('selectedFee') || 'normal';
const isCustomFee = selectedOption === 'custom';
@@ -156,7 +158,9 @@ export const Fees = <TFieldValues extends FormState>({
const supportsCustomFee = networkType !== 'solana';
useRefetchFees({ networkSymbol: symbol });
useEffect(() => {
dispatch(updateFeeInfoThunk({ networkSymbol: symbol }));
}, [dispatch, symbol]);
const feeLabelId = useMemo(() => {
switch (networkType) {

View File

@@ -1,14 +1,13 @@
import { useEffect, useState } from 'react';
import { formatDurationStrict } from '@suite-common/suite-utils';
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { getFeeUnits } from '@suite-common/wallet-utils';
import { Row, Text } from '@trezor/components';
import { FeeRate } from '@trezor/product-components';
import { Translation } from 'src/components/suite';
import { FiatValue } from 'src/components/suite/FiatValue';
import { useLocales, useSelector } from 'src/hooks/suite';
import { useLocales } from 'src/hooks/suite';
import { FeeCard } from './FeeCard';
import { FeeCardsWrapper, StandardFeeProps } from './StandardFee';
@@ -27,7 +26,6 @@ export const BitcoinFeeCards = ({
getValues,
}: StandardFeeProps) => {
const locale = useLocales();
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
const [cachedBytes, setCachedBytes] = useState<number | undefined>(undefined);
@@ -62,7 +60,6 @@ export const BitcoinFeeCards = ({
value={fee.value}
isSelected={selectedLevel.label === fee.value}
changeFeeLevel={changeFeeLevel}
isLoading={areFeesLoading}
topLeftChild={
<span data-testid={`@fee-card/${fee.value}`}>
<Translation id={getFeeLevelTranslationId(fee.value)} />

View File

@@ -1,7 +1,6 @@
import { useEffect, useState } from 'react';
import { formatDurationStrict } from '@suite-common/suite-utils';
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { getFeeUnits, isEip1559 } from '@suite-common/wallet-utils';
import { Badge, Grid, Row, Text } from '@trezor/components';
import { FeeRate } from '@trezor/product-components';
@@ -27,8 +26,6 @@ export const EthereumFeeCards = ({
}: StandardFeeProps) => {
const locale = useLocales();
const isDebug = useSelector(selectIsDebugModeActive);
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
const [cachedGasLimit, setCachedGasLimit] = useState<string | undefined>(undefined);
useEffect(() => {
@@ -61,7 +58,6 @@ export const EthereumFeeCards = ({
key={fee.value}
isSelected={selectedLevel.label === fee.value}
changeFeeLevel={changeFeeLevel}
isLoading={areFeesLoading}
topLeftChild={
<span data-testid={`@fee-card/${fee.value}`}>
<Translation id={getFeeLevelTranslationId(fee.value)} />

View File

@@ -1,11 +1,10 @@
import { ReactNode } from 'react';
import React from 'react';
import {
Box,
Column,
RadioCard,
Row,
SkeletonRectangle,
TOOLTIP_DELAY_NORMAL,
Text,
Tooltip,
@@ -18,12 +17,11 @@ type FeeCardProps = {
value: FeeLevel['label'];
isSelected: boolean;
changeFeeLevel: (level: FeeLevel['label']) => void;
topLeftChild: ReactNode;
topRightChild?: ReactNode;
bottomLeftChild: ReactNode;
bottomRightChild: ReactNode;
tooltipContent?: ReactNode;
isLoading?: boolean;
topLeftChild: React.ReactNode;
topRightChild?: React.ReactNode;
bottomLeftChild: React.ReactNode;
bottomRightChild: React.ReactNode;
tooltipContent?: React.ReactNode;
'data-testid'?: string;
};
@@ -36,7 +34,6 @@ export const FeeCard = ({
bottomLeftChild,
bottomRightChild,
tooltipContent,
isLoading,
'data-testid': dataTestId,
}: FeeCardProps) => (
<Box data-testid={dataTestId} minWidth={FEE_CARD_MIN_WIDTH}>
@@ -46,15 +43,13 @@ export const FeeCard = ({
<Row justifyContent="space-between">
<Text typographyStyle="highlight">{topLeftChild}</Text>
<Text variant="tertiary" typographyStyle="hint">
{isLoading ? <SkeletonRectangle animate={true} /> : topRightChild}
{topRightChild}
</Text>
</Row>
<Row justifyContent="space-between" height={24}>
<Text>
{isLoading ? <SkeletonRectangle animate={true} /> : bottomLeftChild}
</Text>
<Text>{bottomLeftChild}</Text>
<Text variant="tertiary" typographyStyle="hint">
{isLoading ? <SkeletonRectangle animate={true} /> : bottomRightChild}
{bottomRightChild}
</Text>
</Row>
</Column>

View File

@@ -1,9 +1,7 @@
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { getFeeUnits } from '@suite-common/wallet-utils';
import { Text } from '@trezor/components';
import { FiatValue, Translation } from 'src/components/suite';
import { useSelector } from 'src/hooks/suite';
import { FeeCard } from './FeeCard';
import { FeeCardsWrapper, StandardFeeProps } from './StandardFee';
@@ -16,7 +14,6 @@ export const MiscFeeCards = ({
symbol,
changeFeeLevel,
}: StandardFeeProps) => {
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
if (!feeOptions.length) return null;
const isSolanaNetwork = networkType === 'solana';
@@ -31,7 +28,6 @@ export const MiscFeeCards = ({
value={fee.value}
isSelected={true}
changeFeeLevel={changeFeeLevel}
isLoading={areFeesLoading}
topLeftChild={
<span data-testid={`@fee-card/${fee.value}`}>
<Translation id={getFeeLevelTranslationId(fee.value)} />

View File

@@ -1,27 +0,0 @@
import { useEffect } from 'react';
import { useInterval } from 'react-use';
import { NetworkSymbol } from '@suite-common/wallet-config';
import {
FEES_UPDATE_INTERVAL_MILLISECONDS,
delayedUpdateFeeInfoThunk,
updateFeeInfoThunk,
} from '@suite-common/wallet-core';
import { useDispatch } from 'src/hooks/suite';
type UseRefetchFeesProps = { networkSymbol: NetworkSymbol };
export const useRefetchFees = ({ networkSymbol }: UseRefetchFeesProps) => {
const dispatch = useDispatch();
// Initial fetch only when component mounts
useEffect(() => {
dispatch(updateFeeInfoThunk({ networkSymbol }));
}, [dispatch, networkSymbol]);
// Refetch fees periodically incl. loading behavior
useInterval(() => {
dispatch(delayedUpdateFeeInfoThunk({ networkSymbol }));
}, FEES_UPDATE_INTERVAL_MILLISECONDS);
};

View File

@@ -74,6 +74,7 @@ const getStateFromProps = (props: UseSendFormProps) => {
return {
account,
network,
localCurrencyOption,
online: props.online,
metadataEnabled: props.metadataEnabled,
@@ -319,7 +320,7 @@ export const useSendForm = (props: UseSendFormProps): SendContextValues => {
state.network.decimals,
]);
// load draft from reducer and reset current form values, this should be only called once on mount
// load draft from reducer
useEffect(() => {
const loadDraftValues = async () => {
const storedState = await dispatch(getSendFormDraftThunk()).unwrap();
@@ -334,9 +335,7 @@ export const useSendForm = (props: UseSendFormProps): SendContextValues => {
}
};
loadDraftValues();
// composeDraft is excluded because its reference changes with each feeInfo update.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, getLoadedValues, reset]);
}, [dispatch, getLoadedValues, reset, composeDraft]);
// register custom form fields (without HTMLElement)
useEffect(() => {
@@ -349,14 +348,7 @@ export const useSendForm = (props: UseSendFormProps): SendContextValues => {
if (!draft.current) return;
composeDraft(draft.current);
draft.current = undefined;
// composeDraft is excluded because its reference changes with each feeInfo update.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [draft]);
// update composedLevels when feeInfo changes
useEffect(() => {
composeDraft(getValues());
}, [composeDraft, getValues]);
}, [draft, composeDraft]);
// handle draftSaveRequest
useEffect(() => {

View File

@@ -2,13 +2,12 @@ import { useWatch } from 'react-hook-form';
import styled from 'styled-components';
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { isLowAnonymityWarning } from '@suite-common/wallet-utils';
import { Banner, Button, Checkbox, Tooltip, variables } from '@trezor/components';
import { spacingsPx } from '@trezor/theme';
import { Translation } from 'src/components/suite/Translation';
import { useDevice, useSelector } from 'src/hooks/suite';
import { useDevice } from 'src/hooks/suite';
import { useSendFormContext } from 'src/hooks/wallet';
const Container = styled.div`
@@ -69,7 +68,7 @@ export const ReviewButton = () => {
control,
formState: { errors },
online,
isLoading: isSendFormLoading,
isLoading,
signTransaction,
getValues,
getDefaultValue,
@@ -82,8 +81,6 @@ export const ReviewButton = () => {
toggleAnonymityWarning,
},
} = useSendFormContext();
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
const isLoading = isSendFormLoading || areFeesLoading;
const options = useWatch({
name: 'options',

View File

@@ -1,23 +1,16 @@
import { PropsWithChildren, useMemo } from 'react';
import { useMemo } from 'react';
import styled from 'styled-components';
import { selectAreFeesLoading } from '@suite-common/wallet-core';
import { formatAmount, formatNetworkAmount } from '@suite-common/wallet-utils';
import { Card, Column, InfoItem, SkeletonRectangle } from '@trezor/components';
import { Card, Column, InfoItem } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { FiatValue, FormattedCryptoAmount, Translation } from 'src/components/suite';
import { useSelector } from 'src/hooks/suite';
import { useSendFormContext } from 'src/hooks/wallet';
import { ReviewButton } from './ReviewButton';
type ChildOrSkeletonProps = PropsWithChildren<{ isLoading?: boolean }>;
const ChildOrSkeleton = ({ children, isLoading }: ChildOrSkeletonProps) =>
isLoading ? <SkeletonRectangle animate={true} /> : children;
const Container = styled.div`
position: sticky;
top: 80px;
@@ -29,7 +22,6 @@ export const TotalSent = () => {
composedLevels,
getValues,
} = useSendFormContext();
const areFeesLoading = useSelector(state => selectAreFeesLoading(state));
const selectedFee = getValues().selectedFee || 'normal';
const transactionInfo = composedLevels ? composedLevels[selectedFee] : undefined;
@@ -60,49 +52,39 @@ export const TotalSent = () => {
variant="default"
typographyStyle="body"
>
<ChildOrSkeleton isLoading={areFeesLoading}>
{hasTransactionInfo && (
<FormattedCryptoAmount
disableHiddenPlaceholder
value={
tokenInfo
? formatAmount(
transactionInfo.totalSpent,
tokenInfo.decimals,
)
: formatNetworkAmount(
transactionInfo.totalSpent,
symbol,
)
}
symbol={tokenInfo?.symbol ?? symbol}
contractAddress={tokenInfo?.contract}
/>
)}
</ChildOrSkeleton>
{hasTransactionInfo && (
<FormattedCryptoAmount
disableHiddenPlaceholder
value={
tokenInfo
? formatAmount(
transactionInfo.totalSpent,
tokenInfo.decimals,
)
: formatNetworkAmount(transactionInfo.totalSpent, symbol)
}
symbol={tokenInfo?.symbol ?? symbol}
contractAddress={tokenInfo?.contract}
/>
)}
</InfoItem>
<InfoItem label={<Translation id={feeLabelId} />} direction="row">
<ChildOrSkeleton isLoading={areFeesLoading}>
{hasTransactionInfo &&
(tokenInfo ? (
<FormattedCryptoAmount
disableHiddenPlaceholder
value={formatNetworkAmount(transactionInfo.fee, symbol)}
symbol={symbol}
contractAddress={tokenInfo.contract}
/>
) : (
<FiatValue
disableHiddenPlaceholder
amount={formatNetworkAmount(
transactionInfo.totalSpent,
symbol,
)}
symbol={symbol}
/>
))}
</ChildOrSkeleton>
{hasTransactionInfo &&
(tokenInfo ? (
<FormattedCryptoAmount
disableHiddenPlaceholder
value={formatNetworkAmount(transactionInfo.fee, symbol)}
symbol={symbol}
contractAddress={tokenInfo.contract}
/>
) : (
<FiatValue
disableHiddenPlaceholder
amount={formatNetworkAmount(transactionInfo.totalSpent, symbol)}
symbol={symbol}
/>
))}
</InfoItem>
</Column>
<ReviewButton />

View File

@@ -32,7 +32,6 @@ export * from './getWeakRandomNumberInRange';
export * from './hasUppercaseLetter';
export { hexToRgba } from './hexToRgba';
export { hexToRgbaArray } from './hexToRgbaArray';
export { isApproxEqual } from './isApproxEqual';
export * from './isArrayMember';
export * from './isFullPath';
export * from './isHex';

View File

@@ -1,24 +0,0 @@
import { BigNumber, BigNumberValue } from './bigNumber';
/**
* Check if two BigNumber values are approximately equal based on relative difference
* @param value1
* @param value2
* @param relativeTolerance maximum threshold for relative difference of values to declare them approx. equal
*/
export const isApproxEqual = (
value1: BigNumberValue,
value2: BigNumberValue,
relativeTolerance: BigNumberValue,
): boolean => {
value1 = new BigNumber(value1);
value2 = new BigNumber(value2);
relativeTolerance = new BigNumber(relativeTolerance);
// Cannot calculate relative difference if one value is zero, but then they must be equal
if (value1.eq(0)) return value1.eq(value2);
const relativeDifference = value2.minus(value1).abs().dividedBy(value1);
return relativeDifference.lte(relativeTolerance);
};

View File

@@ -1,32 +0,0 @@
import { BigNumber } from '../src/bigNumber';
import { isApproxEqual } from '../src/isApproxEqual';
describe(isApproxEqual.name, () => {
it('always returns true for equal values', () => {
expect(isApproxEqual(100, 100, 0.001)).toBe(true);
expect(isApproxEqual(100, '100', 0.001)).toBe(true);
expect(isApproxEqual('100', '100', 0.001)).toBe(true);
expect(isApproxEqual(new BigNumber(-123), '-123', 0.001)).toBe(true);
expect(isApproxEqual('-123.001', '-123.001000', 0.001)).toBe(true);
});
it('handles zero values correctly', () => {
expect(isApproxEqual(0, 0, 0.01)).toBe(true);
expect(isApproxEqual(0, 1, 0.01)).toBe(false);
});
it('handles relative tolerance', () => {
expect(isApproxEqual(100, 101, 0.005)).toBe(false);
expect(isApproxEqual(100, 101, 0.01)).toBe(true);
expect(isApproxEqual(100, 101, 0.02)).toBe(true);
});
it('handles NaN values', () => {
expect(isApproxEqual(100, NaN, 0.01)).toBe(false);
expect(isApproxEqual(NaN, 100, 0.01)).toBe(false);
expect(isApproxEqual(NaN, NaN, 0.01)).toBe(false);
expect(isApproxEqual('nonsense', '123', 0.01)).toBe(false);
expect(isApproxEqual('123', 'nonsense', 0.01)).toBe(false);
expect(isApproxEqual('nonsense', '', 0.01)).toBe(false);
});
});

View File

@@ -1 +0,0 @@
export const FEES_UPDATE_INTERVAL_MILLISECONDS = 60_000; // interval to refetch estimated fees from backend

View File

@@ -1,7 +1,5 @@
import { createThunk } from '@suite-common/redux-utils';
import { TrezorDevice } from '@suite-common/suite-types';
import {
Network,
NetworkSymbol,
getNetwork,
getNetworkOptional,
@@ -10,13 +8,11 @@ import {
import type { NetworksFees } from '@suite-common/wallet-types';
import { isEip1559 } from '@suite-common/wallet-utils';
import TrezorConnect, { FeeLevel } from '@trezor/connect';
import { BlockchainEstimatedFeeLevel } from '@trezor/connect/src/types/api/blockchainEstimateFee';
import { isNative } from '@trezor/env-utils';
import { FEES_MODULE_PREFIX, feesActions } from './feesActions';
import { selectFees } from './feesReducer';
import { selectNetworkBlockchainInfo } from '../blockchain/blockchainReducer';
import { selectSelectedDevice } from '../device/deviceSelectors';
import { selectFees } from '../fees/feesReducer';
import { selectEnabledNetworks } from '../settings/walletSettingsReducer';
// Conditionally subscribe to blockchain backend
@@ -75,84 +71,84 @@ export const preloadFeeInfoThunk = createThunk(
},
);
type GetNewFeeInfoProps = { network: Network; device?: TrezorDevice };
const getNewFeeInfo = async ({
network,
device,
}: GetNewFeeInfoProps): Promise<BlockchainEstimatedFeeLevel | undefined> => {
if (network.networkType === 'ethereum') {
const result = await TrezorConnect.blockchainEstimateFee({
coin: network.symbol,
request: {
blocks: [2],
feeLevels: 'smart',
specific: {
from: '0x0000000000000000000000000000000000000000',
to: '0x0000000000000000000000000000000000000000',
},
},
});
if (!result.success) return;
const feeLevelBase = result.payload.levels[0];
const isEip1559ActivatedAndAvailable =
getNetwork(network.symbol).features.includes('eip1559') &&
isEip1559(feeLevelBase) &&
!device?.unavailableCapabilities?.['eip1559'] &&
!isNative(); // suite-native does not have eip1559 implementation yet #16372
if (isEip1559ActivatedAndAvailable) return result.payload;
return {
...result.payload,
levels: [
{
...feeLevelBase,
baseFeePerGas: undefined,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
label: 'normal' as const,
},
],
};
}
const result = await TrezorConnect.blockchainEstimateFee({
coin: network.symbol,
request: {
feeLevels: 'smart',
},
});
if (!result.success) return;
return {
...result.payload,
levels: sortLevels(
result.payload.levels
// hack to hide "low" fee option
// (we do not want to change the connect API as it is a potentially breaking change)
.filter(level => level.label !== 'low'),
),
};
};
export const updateFeeInfoThunk = createThunk(
`${FEES_MODULE_PREFIX}/updateFeeInfoThunk`,
async ({ networkSymbol }: { networkSymbol: NetworkSymbol }, { dispatch, getState }) => {
async ({ networkSymbol }: { networkSymbol: NetworkSymbol }, { dispatch, getState, extra }) => {
const network = getNetworkOptional(networkSymbol.toLowerCase());
if (!network) return;
const blockchainInfo = selectNetworkBlockchainInfo(getState(), network.symbol);
const device = selectSelectedDevice(getState());
const newFeeInfo = await getNewFeeInfo({ network, device });
if (newFeeInfo === undefined) return;
const device = extra.selectors.selectDevice(getState());
const partialFees: Partial<NetworksFees> = {};
partialFees[network.symbol] = {
blockHeight: blockchainInfo.blockHeight,
...newFeeInfo,
};
dispatch(feesActions.updateFee(partialFees));
let newFeeInfo;
if (network.networkType === 'ethereum') {
const result = await TrezorConnect.blockchainEstimateFee({
coin: network.symbol,
request: {
blocks: [2],
feeLevels: 'smart',
specific: {
from: '0x0000000000000000000000000000000000000000',
to: '0x0000000000000000000000000000000000000000',
},
},
});
if (result.success) {
const feeLevelBase = result.payload.levels[0];
const isEip1559ActivatedAndAvailable =
getNetwork(network.symbol).features.includes('eip1559') &&
isEip1559(feeLevelBase) &&
!device?.unavailableCapabilities?.['eip1559'] &&
!isNative(); // suite-native does not have eip1559 implementation yet #16372
if (isEip1559ActivatedAndAvailable) {
newFeeInfo = result.payload;
} else {
newFeeInfo = {
...result.payload,
levels: [
{
...feeLevelBase,
baseFeePerGas: undefined,
maxFeePerGas: undefined,
maxPriorityFeePerGas: undefined,
label: 'normal' as const,
},
],
};
}
}
} else {
const result = await TrezorConnect.blockchainEstimateFee({
coin: network.symbol,
request: {
feeLevels: 'smart',
},
});
if (result.success) {
newFeeInfo = {
...result.payload,
levels: sortLevels(
result.payload.levels
// hack to hide "low" fee option
// (we do not want to change the connect API as it is a potentially breaking change)
.filter(level => level.label !== 'low'),
),
};
}
}
if (newFeeInfo) {
const partial: Partial<NetworksFees> = {};
partial[network.symbol] = {
blockHeight: blockchainInfo.blockHeight,
...newFeeInfo,
};
dispatch(feesActions.updateFee(partial));
}
},
);

View File

@@ -23,7 +23,6 @@ export * from './discovery/discoveryThunks';
export * from './discovery/discoverySelectors';
export * from './discovery/discoveryRunningStateLocks';
export * from './fees/feesActions';
export * from './fees/feesConstants';
export * from './fees/feesReducer';
export * from './fees/feesThunks';
export * from './fiat-rates/fiatRatesMiddleware';

View File

@@ -50,11 +50,6 @@ const sendRaw = createAction(`${SEND_MODULE_PREFIX}/sendRaw`, (payload: boolean)
export const dispose = createAction(`${SEND_MODULE_PREFIX}/dispose`);
const setAreFeesLoading = createAction(
`${SEND_MODULE_PREFIX}/setAreFeesLoading`,
(payload: { areFeesLoading: boolean }) => ({ payload }),
);
export const sendFormActions = {
storeDraft,
removeDraft,
@@ -63,5 +58,4 @@ export const sendFormActions = {
discardTransaction,
sendRaw,
dispose,
setAreFeesLoading,
};

View File

@@ -21,7 +21,6 @@ export type SendState = {
precomposedForm?: FormState; // Used to pass the form state to the review modal. Holds similar data as drafts, but drafts are not used in RBF form.
signedTx?: BlockbookTransaction;
serializedTx?: SerializedTx; // Hexadecimal representation of signed transaction (payload for TrezorConnect.pushTransaction).
areFeesLoading: boolean; // visual indication of fees being loaded, independent of the fees state
};
export const initialState: SendState = {
@@ -29,7 +28,6 @@ export const initialState: SendState = {
precomposedTx: undefined,
serializedTx: undefined,
signedTx: undefined,
areFeesLoading: false,
};
export type SendRootState = {
@@ -94,8 +92,5 @@ export const prepareSendFormReducer = createReducerWithExtraDeps(initialState, (
payload.forEach(account => {
delete state.drafts[account.key];
});
})
.addCase(sendFormActions.setAreFeesLoading, (state, { payload: { areFeesLoading } }) => {
state.areFeesLoading = areFeesLoading;
});
});

View File

@@ -73,5 +73,3 @@ export const selectSendFormReviewButtonRequestsCount = (
return isCardano ? sendFormReviewRequest.length - 1 : sendFormReviewRequest.length;
};
export const selectAreFeesLoading = (state: SendRootState) => state.wallet.send.areFeesLoading;

View File

@@ -52,7 +52,6 @@ import {
signRippleStellarSendFormTransactionThunk,
} from './sendFormRippleStellarThunks';
import {
selectAreFeesLoading,
selectSendFormDrafts,
selectSendPrecomposedTx,
selectSendSerializedTx,
@@ -72,7 +71,6 @@ import { accountsActions } from '../accounts/accountsActions';
import { selectAccountByKey } from '../accounts/accountsSelectors';
import { syncAccountsWithBlockchainThunk } from '../blockchain/blockchainThunks';
import { selectSelectedDevice } from '../device/deviceSelectors';
import { updateFeeInfoThunk } from '../fees/feesThunks';
import {
selectAreSatsAmountUnit,
selectBitcoinAmountUnit,
@@ -608,22 +606,3 @@ export const enhancePrecomposedTransactionThunk = createThunk<
return enhancedPrecomposedTransaction;
},
);
const FEE_UPDATE_DELAY_MILLISECONDS = 1000;
/**
* Delay updateFeeInfoThunk with an arbitrary timeout, because backend request is usually very quick.
* This can be used to display loader for a bit longer, to visually draw users attention to the fees which are changing.
*/
export const delayedUpdateFeeInfoThunk = createThunk(
`${SEND_MODULE_PREFIX}/delayedUpdateFeeInfoThunk`,
async ({ networkSymbol }: { networkSymbol: NetworkSymbol }, { dispatch, getState }) => {
if (selectAreFeesLoading(getState())) return;
dispatch(sendFormActions.setAreFeesLoading({ areFeesLoading: true }));
await Promise.all([
dispatch(updateFeeInfoThunk({ networkSymbol })),
new Promise(resolve => setTimeout(resolve, FEE_UPDATE_DELAY_MILLISECONDS)),
]);
dispatch(sendFormActions.setAreFeesLoading({ areFeesLoading: false }));
},
);