diff --git a/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx b/packages/suite/src/components/wallet/TransactionItem/TransactionItem.tsx
index 629e7e1e094..19649a8bcf5 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 f6494b9a3ee..0b8439a7819 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 oldest (by nonce) pending transaction in the queue. 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 1eb4d21ed68..92add38607c 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 ef910109009..19161878d7f 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/__tests__/transactionUtils.test.ts b/suite-common/wallet-utils/src/__tests__/transactionUtils.test.ts
index 9aa1a01dc57..f512caa950d 100644
--- a/suite-common/wallet-utils/src/__tests__/transactionUtils.test.ts
+++ b/suite-common/wallet-utils/src/__tests__/transactionUtils.test.ts
@@ -18,6 +18,7 @@ import {
MonthKey,
generateTransactionMonthKey,
groupTokensTransactionsByContractAddress,
+ getTransactionWithLowestNonce,
} from '../transactionUtils';
const { getWalletTransaction } = testMocks;
@@ -101,6 +102,41 @@ describe('transaction utils', () => {
});
});
+ it('getTransactionWithLowestNonce - ethereum network', () => {
+ const transactionGroups = {
+ '2019-10-3': [getWalletTransaction({ ethereumSpecific: { nonce: 1 } as any })],
+ '2019-10-4': [
+ getWalletTransaction({
+ ethereumSpecific: { nonce: 0 },
+ } as any),
+ ],
+ '2019-8-14': [
+ getWalletTransaction({ ethereumSpecific: { nonce: 2 } as any }),
+ getWalletTransaction({ ethereumSpecific: { nonce: 3 } as any }),
+ ],
+ };
+
+ const transactionWithLowestNonce: WalletAccountTransaction | null =
+ getTransactionWithLowestNonce(transactionGroups);
+
+ expect(transactionWithLowestNonce).toStrictEqual(
+ getWalletTransaction({ ethereumSpecific: { nonce: 0 } as any }),
+ );
+ });
+
+ it('getTransactionWithLowestNonce - non ethereum network', () => {
+ const transactionGroups = {
+ '2019-10-4': [getWalletTransaction()],
+ '2019-10-3': [getWalletTransaction()],
+ '2019-8-14': [getWalletTransaction(), getWalletTransaction()],
+ };
+
+ const transactionWithLowestNonce: WalletAccountTransaction | null =
+ getTransactionWithLowestNonce(transactionGroups);
+
+ expect(transactionWithLowestNonce).toStrictEqual(null);
+ });
+
it('groupJointTransactions', () => {
const [j1, r2, j3, j4, s5, s6, j7, f8, j9, j10, j11] = (
[
diff --git a/suite-common/wallet-utils/src/transactionUtils.ts b/suite-common/wallet-utils/src/transactionUtils.ts
index 182db04bbe3..12b0be8287e 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);