Skip to content

Commit

Permalink
fixup! feat(suite): add timer to solana tx modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasklim committed Feb 26, 2025
1 parent e68433f commit c892e03
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export const TransactionReviewModal = ({ type, decision }: TransactionReviewModa

return (
<TransactionReviewModalContent
timestamp={txInfoState.precomposedTx?.createdTimestamp}
decision={decision}
txInfoState={txInfoState}
tryAgainSignTx={handleTryAgainSignTx}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SendState,
SerializedTx,
StakeState,
selectAccounts,
selectPrecomposedSendForm,
selectSelectedDevice,
selectSendFormReviewButtonRequestsCount,
Expand Down Expand Up @@ -48,26 +49,30 @@ import { TransactionReviewOutputList } from './TransactionReviewOutputList/Trans
import { TransactionReviewOutputTimer } from './TransactionReviewOutputList/TransactionReviewOutputTimer';
import { TransactionReviewSummary } from './TransactionReviewSummary';
import { ConfirmActionModal } from '../DeviceContextModal/ConfirmActionModal';
import { ExpiredBlockhash } from '../UserContextModal/TxDetailModal/ExpiredBlockhash';
import { ExpiredTxValidity } from '../UserContextModal/TxDetailModal/ExpiredTxValidity';
import { ReplaceByFeeFailedOriginalTxConfirmed } from '../UserContextModal/TxDetailModal/ReplaceByFeeFailedOriginalTxConfirmed';

const SOLANA_TX_TIMEOUT_MS = 40 * 1000;
const getTxValidityTimeoutInMs = (networkType?: NetworkType) => {
if (networkType === 'solana') {
return 40 * 1000; // 40 seconds
}

return 0;
};

const isSolanaBlockhashExpired = (networkType: NetworkType, deadline: number) =>
networkType === 'solana' && deadline <= Date.now();
const hasTxValidityExpired = (deadline: number) => deadline <= Date.now();

const shouldShowSolanaTimer = (
networkType: NetworkType,
const shouldShowTxValidityTimer = (
deadline: number,
outputs: ReviewOutput[],
symbol: NetworkSymbol,
accounts: AccountsState,
buttonRequestsCount: number,
serializedTx: SerializedTx | undefined,
stakeType: StakeType | null,
isTrading: boolean,
shouldCheckTxTimeValidity: boolean,
) => {
if (networkType !== 'solana' || isSolanaBlockhashExpired(networkType, deadline) || isTrading) {
if (!shouldCheckTxTimeValidity || hasTxValidityExpired(deadline)) {
return false;
}

Expand Down Expand Up @@ -96,7 +101,6 @@ const mapRbfTypeToReporting: Record<
};

type TransactionReviewModalContentProps = {
timestamp?: number;
decision: Deferred<boolean, string | number | undefined> | undefined;
txInfoState: SendState | StakeState;
tryAgainSignTx: () => void;
Expand All @@ -105,7 +109,6 @@ type TransactionReviewModalContentProps = {
};

export const TransactionReviewModalContent = ({
timestamp = 0,
decision,
txInfoState,
tryAgainSignTx,
Expand All @@ -114,7 +117,7 @@ export const TransactionReviewModalContent = ({
}: TransactionReviewModalContentProps) => {
const dispatch = useDispatch();
const account = useSelector(selectAccountIncludingChosenInTrading);
const accounts = useSelector(state => state.wallet.accounts);
const accounts = useSelector(selectAccounts);
const device = useSelector(selectSelectedDevice);
const isActionAbortable = useSelector(selectIsActionAbortable);
const [isSending, setIsSending] = useState(false);
Expand All @@ -128,19 +131,24 @@ export const TransactionReviewModalContent = ({
: selectPrecomposedSendForm(state),
);

const isTradingAction = !!precomposedForm?.isTrading;
const shouldCheckTxTimeValidity =
account?.networkType === 'solana' && !precomposedForm?.isTrading;

const deadline = timestamp + SOLANA_TX_TIMEOUT_MS;
const createdTxTimestamp = txInfoState?.precomposedTx?.createdTimestamp || 0;
const deadline = createdTxTimestamp + getTxValidityTimeoutInMs(account?.networkType);

// check if transaction is still valid
useEffect(() => {
if (isTradingAction) {
if (!shouldCheckTxTimeValidity) {
return;
}

const now = Date.now();
const timeLeft = Math.max(deadline - now, 0);
let mounted = true;

console.log(timeLeft);

const timeoutId = setTimeout(() => {
if (mounted && !isSending) {
TrezorConnect.cancel('tx-timeout');
Expand All @@ -151,7 +159,7 @@ export const TransactionReviewModalContent = ({
mounted = false;
clearTimeout(timeoutId);
};
}, [deadline, isSending, isTradingAction]);
}, [deadline, isSending, shouldCheckTxTimeValidity]);

const isBumpFeeRbfAction =
precomposedTx !== undefined && isRbfBumpFeeTransaction(precomposedTx);
Expand Down Expand Up @@ -203,18 +211,17 @@ export const TransactionReviewModalContent = ({

const isCancelRbfAction = isRbfCancelTransaction(precomposedTx);

const hasSolanaBlockhashExpired = isSolanaBlockhashExpired(networkType, deadline);
const isTxExpired = hasTxValidityExpired(deadline);

const showSolanaTimer = shouldShowSolanaTimer(
networkType,
const showTxValidityTimer = shouldShowTxValidityTimer(
deadline,
outputs,
symbol,
accounts,
buttonRequestsCount,
serializedTx,
stakeType,
isTradingAction,
shouldCheckTxTimeValidity,
);

const actionLabel = getTransactionReviewModalActionText({
Expand Down Expand Up @@ -298,7 +305,7 @@ export const TransactionReviewModalContent = ({
);
}

if (hasSolanaBlockhashExpired && !isSending && !isTradingAction) {
if (shouldCheckTxTimeValidity && isTxExpired && !isSending) {
return (
<>
<NewModal.Button variant="primary" onClick={() => handleTryAgain(false)}>
Expand Down Expand Up @@ -362,8 +369,8 @@ export const TransactionReviewModalContent = ({
);
}

if (hasSolanaBlockhashExpired && !isTradingAction) {
return <ExpiredBlockhash symbol={symbol} />;
if (shouldCheckTxTimeValidity && isTxExpired && !isSending) {
return <ExpiredTxValidity symbol={symbol} />;
}

return (
Expand All @@ -375,7 +382,7 @@ export const TransactionReviewModalContent = ({
outputs={outputs}
buttonRequestsCount={buttonRequestsCount}
isRbfAction={isBumpFeeRbfAction}
isTradingAction={isTradingAction}
isTradingAction={!!precomposedForm.isTrading}
isSending={isSending}
stakeType={stakeType || undefined}
deadline={deadline}
Expand Down Expand Up @@ -413,7 +420,7 @@ export const TransactionReviewModalContent = ({
}}
stakeType={stakeType}
/>
{showSolanaTimer && (
{showTxValidityTimer && (
<Row gap={spacings.xs}>
<TransactionReviewOutputTimer
deadline={deadline}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ReactNode } from 'react';
import { TranslationKey } from '@suite-common/intl-types';
import { NetworkSymbol, NetworkType, getNetworkDisplaySymbol } from '@suite-common/wallet-config';
import { BTC_LOCKTIME_VALUE } from '@suite-common/wallet-constants';
import { selectAccounts } from '@suite-common/wallet-core';
import { ReviewOutput, StakeType } from '@suite-common/wallet-types';
import { findAccountsByAddress, isTestnet } from '@suite-common/wallet-utils';
import { BigNumber } from '@trezor/utils/src/bigNumber';
Expand Down Expand Up @@ -245,7 +246,7 @@ export const TransactionReviewOutput = ({
isTrading,
}: TransactionReviewOutputProps) => {
const { networkType, symbol } = account;
const accounts = useSelector(state => state.wallet.accounts);
const accounts = useSelector(selectAccounts);
const { translationString } = useTranslation();
const isFiatVisible =
['fee', 'amount', 'gas', 'fee-replace', 'reduce-output'].includes(type) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ export const TransactionReviewOutputTimer = ({
<CountdownTimer
deadline={deadline}
unitDisplay="narrow"
message="TR_SOLANA_TX_CONFIRMATION_TIMER_SHORT"
message="TR_TX_CONFIRMATION_TIMER_SHORT"
pastDeadlineMessage={
isSending ? 'TR_CONFIRMING_TX' : 'TR_SOLANA_TX_SEND_FAILED_TITLE'
isSending ? 'TR_CONFIRMING_TX' : 'TR_TX_SEND_FAILED_TITLE'
}
/>
</TimerBox>
Expand All @@ -67,8 +67,8 @@ export const TransactionReviewOutputTimer = ({
<CountdownTimer
deadline={deadline}
unitDisplay="long"
message="TR_SOLANA_TX_CONFIRMATION_TIMER"
pastDeadlineMessage="TR_SOLANA_TX_SEND_FAILED_TITLE"
message="TR_TX_CONFIRMATION_TIMER"
pastDeadlineMessage="TR_TX_SEND_FAILED_TITLE"
/>
</Text>
<Translation id="TR_SOLANA_TX_CONFIRMATION_TIMER_DESCRIPTION" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { HELP_CENTER_SOL_SEND } from '@trezor/urls';
import { Translation } from 'src/components/suite/Translation';
import { TrezorLink } from 'src/components/suite/TrezorLink';

type ExpiredBlockhashProps = {
type ExpiredTxValidityProps = {
symbol: NetworkSymbol;
};

export const ExpiredBlockhash = ({ symbol }: ExpiredBlockhashProps) => {
export const ExpiredTxValidity = ({ symbol }: ExpiredTxValidityProps) => {
const networkName = getNetwork(symbol).name;

return (
Expand All @@ -21,9 +21,9 @@ export const ExpiredBlockhash = ({ symbol }: ExpiredBlockhashProps) => {
</Box>

<Text typographyStyle="titleSmall">
<Translation id="TR_SOLANA_TX_SEND_FAILED_TITLE" />
<Translation id="TR_TX_SEND_FAILED_TITLE" />
</Text>
<Translation id="TR_SOLANA_TX_SEND_FAILED_DESCRIPTION" values={{ networkName }} />
<Translation id="TR_TX_SEND_FAILED_DESCRIPTION" values={{ networkName }} />

<TrezorLink typographyStyle="hint" href={HELP_CENTER_SOL_SEND} icon="arrowUpRight">
<Translation id="TR_LEARN_MORE" />
Expand Down
20 changes: 8 additions & 12 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9148,32 +9148,28 @@ export default defineMessages({
id: 'TR_SOLANA_TX_CONFIRMATION_MAY_TAKE_UP_TO_1_MIN',
defaultMessage: 'Transaction confirmation may take up to <nowrap>one (1) minute</nowrap>',
},
TR_SOLANA_TX_SEND_FAILED_TITLE: {
id: 'TR_SOLANA_TX_SEND_FAILED_TITLE',
TR_TX_SEND_FAILED_TITLE: {
id: 'TR_TX_SEND_FAILED_TITLE',
defaultMessage: 'Send transaction failed',
},
TR_SOLANA_TX_SEND_FAILED_DESCRIPTION: {
id: 'TR_SOLANA_TX_SEND_FAILED_DESCRIPTION',
TR_TX_SEND_FAILED_DESCRIPTION: {
id: 'TR_TX_SEND_FAILED_DESCRIPTION',
defaultMessage:
'The time to sign a {networkName} transaction is limited. It could no longer be submitted because it timed out and is no longer valid.',
},
TR_SOLANA_TX_CONFIRMATION_TIMER: {
id: 'TR_SOLANA_TX_CONFIRMATION_TIMER',
TR_TX_CONFIRMATION_TIMER: {
id: 'TR_TX_CONFIRMATION_TIMER',
defaultMessage: '{value} left',
},
TR_SOLANA_TX_CONFIRMATION_TIMER_SHORT: {
id: 'TR_SOLANA_TX_CONFIRMATION_TIMER_SHORT',
TR_TX_CONFIRMATION_TIMER_SHORT: {
id: 'TR_TX_CONFIRMATION_TIMER_SHORT',
defaultMessage: '<strong>{value}</strong> left to confirm',
},
TR_SOLANA_TX_CONFIRMATION_TIMER_DESCRIPTION: {
id: 'TR_SOLANA_TX_CONFIRMATION_TIMER_DESCRIPTION',
defaultMessage:
'Due to Solana network constraints you have <1 minute to confirm and send the transaction before it times out.',
},
TR_SOLANA_TX_CONFIRMATION_TIMER_AGAIN: {
id: 'TR_SOLANA_TX_CONFIRMATION_TIMER_AGAIN',
defaultMessage: 'Start over',
},
TR_VIEW_ONLY_PROMO_YES: {
id: 'TR_VIEW_ONLY_PROMO_YES',
defaultMessage: 'Enable',
Expand Down

0 comments on commit c892e03

Please sign in to comment.