diff --git a/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx b/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx index 629e7e1e0942..19649a8bcf54 100644 --- a/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx +++ b/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx @@ -2,7 +2,7 @@ import { memo, useMemo, useState } from 'react'; import styled, { css } from 'styled-components'; import { AnimatePresence } from 'framer-motion'; import { selectIsPhishingTransaction } from '@suite-common/wallet-core'; -import { variables, Button, Card } from '@trezor/components'; +import { variables, Button, Card, Link } from '@trezor/components'; import { Translation } from 'src/components/suite'; import { useDispatch, useSelector } from 'src/hooks/suite'; import { openModal } from 'src/actions/suite/modalActions'; @@ -35,6 +35,8 @@ import { BlurWrapper } from './TransactionItemBlurWrapper'; import { selectSelectedAccount } from 'src/reducers/wallet/selectedAccountReducer'; import { getInstantStakeType } from 'src/utils/suite/stake'; import { isStakeTypeTx } from '@suite-common/suite-utils'; +import { Tooltip } from '@trezor/components'; +import { HELP_CENTER_REPLACE_BY_FEE } from '@trezor/urls'; // eslint-disable-next-line local-rules/no-override-ds-component const Wrapper = styled(Card)<{ @@ -88,6 +90,7 @@ interface TransactionItemProps { network: Network; accountType: AccountType; className?: string; + disableBumpFee?: boolean; index: number; } @@ -101,6 +104,7 @@ export const TransactionItem = memo( network, accountType, className, + disableBumpFee, index, }: TransactionItemProps) => { const [limit, setLimit] = useState(0); @@ -183,6 +187,42 @@ export const TransactionItem = memo( transaction.deadline ? '/prepending' : '' }`; + const BumpFeeButton = ({ isDisabled }: { isDisabled: boolean }) => ( + + ); + + const DisabledBumpFeeButtonWithTooltip = () => ( + + ( + + {chunks} + + ), + }} + /> + + } + > + + + ); + // we are using slightly different layout for 1 targets txs to better match the design // the only difference is that crypto amount is in the same row as tx heading/description // fiat amount is in the second row along with address @@ -418,12 +458,11 @@ export const TransactionItem = memo( networkFeatures?.includes('rbf') && !transaction?.deadline && ( - + {disableBumpFee ? ( + + ) : ( + + )} )} diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts index f6494b9a3ee3..66300a036002 100644 --- a/packages/suite/src/support/messages.ts +++ b/packages/suite/src/support/messages.ts @@ -9157,4 +9157,9 @@ export default defineMessages({ defaultMessage: 'Setting a low fee might cause your transaction to fail or experience significant delays.', }, + TR_BUMP_FEE_DISABLED_TOOLTIP: { + id: 'TR_BUMP_FEE_DISABLED_TOOLTIP', + defaultMessage: + 'To speed up your transactions, increase the fee on the first pending transaction in the queue (the one processed first). Transactions must be confirmed in order. Learn more', + }, }); diff --git a/packages/suite/src/views/wallet/transactions/TransactionList/TransactionGroupedList.tsx b/packages/suite/src/views/wallet/transactions/TransactionList/TransactionGroupedList.tsx index 1eb4d21ed68d..92add38607cb 100644 --- a/packages/suite/src/views/wallet/transactions/TransactionList/TransactionGroupedList.tsx +++ b/packages/suite/src/views/wallet/transactions/TransactionList/TransactionGroupedList.tsx @@ -1,4 +1,8 @@ -import { GroupedTransactionsByDate, groupJointTransactions } from '@suite-common/wallet-utils'; +import { + getTransactionWithLowestNonce, + GroupedTransactionsByDate, + groupJointTransactions, +} from '@suite-common/wallet-utils'; import { getNetwork } from '@suite-common/wallet-config'; import { CoinjoinBatchItem } from 'src/components/wallet/TransactionItem/CoinjoinBatchItem'; import { useSelector } from 'src/hooks/suite'; @@ -25,6 +29,9 @@ export const TransactionGroupedList = ({ const accountMetadata = useSelector(state => selectLabelingDataForAccount(state, account.key)); const network = getNetwork(symbol); + const transactionWithLowestNonce: WalletAccountTransaction | null = + getTransactionWithLowestNonce(transactionGroups); + return Object.entries(transactionGroups).map(([dateKey, value], groupIndex) => ( ), )} diff --git a/packages/urls/src/urls.ts b/packages/urls/src/urls.ts index ef9101090092..19161878d7f9 100644 --- a/packages/urls/src/urls.ts +++ b/packages/urls/src/urls.ts @@ -103,6 +103,8 @@ export const HELP_CENTER_EVM_ADDRESS_CHECKSUM: Url = 'https://trezor.io/learn/a/evm-address-checksum-in-trezor-suite'; export const HELP_CENTER_FIRMWARE_REVISION_CHECK: Url = 'https://trezor.io/learn/a/trezor-firmware-revision-check'; +export const HELP_CENTER_REPLACE_BY_FEE: Url = + 'https://trezor.io/learn/a/replace-by-fee-rbf-ethereum'; export const INVITY_URL: Url = 'https://invity.io/'; export const INVITY_SCHEDULE_OF_FEES: Url = 'https://blog.invity.io/schedule-of-fees'; diff --git a/suite-common/wallet-utils/src/transactionUtils.ts b/suite-common/wallet-utils/src/transactionUtils.ts index 182db04bbe34..12b0be8287e9 100644 --- a/suite-common/wallet-utils/src/transactionUtils.ts +++ b/suite-common/wallet-utils/src/transactionUtils.ts @@ -1034,3 +1034,21 @@ export const groupTokensTransactionsByContractAddress = ( return groupedTokensTxs; }; + +export const getTransactionWithLowestNonce = ( + transactionGroups: GroupedTransactionsByDate, +): WalletAccountTransaction | null => + Object.values(transactionGroups) + .flat() + .reduce((lowestNonceTransaction, transaction) => { + if (!transaction.ethereumSpecific) return null; + const currentNonce = Number(transaction.ethereumSpecific?.nonce); + if ( + lowestNonceTransaction === null || + currentNonce < Number(lowestNonceTransaction.ethereumSpecific?.nonce) + ) { + lowestNonceTransaction = transaction; + } + + return lowestNonceTransaction; + }, null);