Skip to content

Commit

Permalink
feat: Cancel Transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-sanderson committed Jan 23, 2025
1 parent 4b5d56a commit 9b26270
Show file tree
Hide file tree
Showing 23 changed files with 568 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Banner, Button, Column } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { ChainedTransactions } from '@suite-common/wallet-types';

import { Translation } from 'src/components/suite';

import { AffectedTransactionItem } from './AffectedTransactionItem';

type AffectedTransactionsProps = {
chainedTxs?: ChainedTransactions;
showChained: () => void;
};

export const AffectedTransactions = ({ chainedTxs, showChained }: AffectedTransactionsProps) => {
if (chainedTxs === undefined) {
return null;
}

return (
<Column gap={spacings.md}>
<Banner
variant="warning"
rightContent={
<Button
variant="warning"
onClick={showChained}
icon="caretRight"
iconAlignment="right"
>
<Translation id="TR_SEE_DETAILS" />
</Button>
}
>
<Translation id="TR_AFFECTED_TXS" />
</Banner>
<Column alignItems="center">
{chainedTxs.own.map(tx => (
<AffectedTransactionItem key={tx.txid} tx={tx} isAccountOwned />
))}
{chainedTxs.others.map(tx => (
<AffectedTransactionItem key={tx.txid} tx={tx} />
))}
</Column>
</Column>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Card, Column, Divider, InfoItem, Paragraph, Row, Text } from '@trezor/components';
import { spacings } from '@trezor/theme';
import { SelectedAccountLoaded, WalletAccountTransaction } from '@suite-common/wallet-types';
import { getFeeUnits } from '@suite-common/wallet-utils';
import { BigNumber } from '@trezor/utils';

import { Translation } from '../../../../../Translation';
import { FormattedCryptoAmount } from '../../../../../FormattedCryptoAmount';
import { FiatValue } from '../../../../../FiatValue';
import { useCancelTxContext } from '../../../../../../../hooks/wallet/useCancelTxContext';

type CancelTransactionProps = {
tx: WalletAccountTransaction;
selectedAccount: SelectedAccountLoaded;
};

export const CancelTransaction = ({ tx, selectedAccount }: CancelTransactionProps) => {
const { account } = selectedAccount;
const { networkType } = account;

const { composedCancelTx } = useCancelTxContext();

if (composedCancelTx === null) {
return;
}

const { fee, bytes } = composedCancelTx;
const feePerByte = new BigNumber(composedCancelTx.feePerByte);

return (
<Column alignItems="center" gap={spacings.md}>
<Text typographyStyle="highlight" margin={{ vertical: spacings.sm }}>
<Translation id="TR_CANCEL_TX_HEADER" />
</Text>
<Card fillType="none">
<Text variant="tertiary">
<Translation id="TR_CANCEL_TX_NOTICE" />
</Text>
<Divider orientation="horizontal" />

<InfoItem
direction="row"
label={<Translation id="TR_CANCEL_TX_FEE" />}
typographyStyle="body"
>
<Column gap={spacings.md} alignItems="baseline">
<FormattedCryptoAmount
disableHiddenPlaceholder
value={fee ?? undefined}
symbol={tx.symbol}
/>
<Text variant="tertiary" typographyStyle="label">
<FiatValue
disableHiddenPlaceholder
amount={fee ?? '0'}
symbol={tx.symbol}
showApproximationIndicator
/>
</Text>
</Column>
</InfoItem>
<Row justifyContent="start">
<Paragraph variant="tertiary" typographyStyle="hint">
<Translation id="TR_FEE_RATE" />
</Paragraph>
:&nbsp;
<Paragraph typographyStyle="hint">
{feePerByte.toFormat(2)}&nbsp;{getFeeUnits(networkType)} ({bytes}&nbsp;B)
</Paragraph>
</Row>
</Card>
</Column>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NewModal } from '@trezor/components';
import { Account, FormState } from '@suite-common/wallet-types';
import { DEFAULT_PAYMENT } from '@suite-common/wallet-constants';

import { Translation } from 'src/components/suite';
import { useDevice, useDispatch } from 'src/hooks/suite';

import { signAndPushSendFormTransactionThunk } from '../../../../../../../actions/wallet/send/sendFormThunks';
import { useCancelTxContext } from '../../../../../../../hooks/wallet/useCancelTxContext';

type CancelTransactionButtonProps = {
account: Account;
};

export const CancelTransactionButton = ({ account }: CancelTransactionButtonProps) => {
const { device, isLocked } = useDevice();

const dispatch = useDispatch();
const { composedCancelTx } = useCancelTxContext();

const handleCancelTx = () => {
if (composedCancelTx === null) {
return;
}

const formState: FormState = {
feeLimit: '', // Eth only
feePerUnit: composedCancelTx.feePerByte,
hasCoinControlBeenOpened: false,
isCoinControlEnabled: false,
options: ['broadcast'],

outputs: composedCancelTx.outputs.map(output => ({
...DEFAULT_PAYMENT, // Todo: WTF is this sorcery?
...output,
amount: `${output.amount}`, // Todo: WTF is this sorcery?
})),

selectedUtxos: [],
};

return dispatch(
signAndPushSendFormTransactionThunk({
formState,
precomposedTransaction: composedCancelTx,
selectedAccount: account,
}),
).unwrap();
};

const isDisabled = isLocked() || !device || !device?.available || composedCancelTx === null;

return (
<NewModal.Button
data-testid="@send/cancel-tx-button"
isDisabled={isDisabled}
onClick={handleCancelTx}
variant="destructive"
>
<Translation id="TR_CANCEL_TX_BUTTON" />
</NewModal.Button>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Card, Column, Text } from '@trezor/components';
import { HELP_CENTER_CANCEL_TRANSACTION } from '@trezor/urls';
import { spacings } from '@trezor/theme';

import { Translation } from '../../../../../Translation';
import { TrezorLink } from '../../../../../TrezorLink';

export const CancelTransactionFailed = () => (
<Card fillType="none">
<Column gap={spacings.xs}>
<Text typographyStyle="titleSmall">
<Translation id="TR_CANCEL_TX_FAILED" />
</Text>
<Translation id="TR_CANCEL_TX_FAILED_DESCRIPTION" />

<TrezorLink typographyStyle="hint" href={HELP_CENTER_CANCEL_TRANSACTION}>
<Translation id="TR_LEARN_MORE" />
</TrezorLink>
</Column>
</Card>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useEffect, useState } from 'react';

import {
ChainedTransactions,
SelectedAccountLoaded,
WalletAccountTransactionWithRequiredRbfParams,
} from '@suite-common/wallet-types';
import {
composeCancelTransactionThunk,
selectTransactionConfirmations,
} from '@suite-common/wallet-core';
import { PrecomposeResultFinal } from '@trezor/connect';

import { TxDetailModalBase } from '../TxDetailModalBase';
import { Translation } from '../../../../../Translation';
import { CancelTransaction } from './CancelTransaction';
import { CancelTransactionButton } from './CancelTransactionButton';
import { CancelTxContext } from '../../../../../../../hooks/wallet/useCancelTxContext';
import { useDispatch, useSelector } from '../../../../../../../hooks/suite';
import { CancelTransactionFailed } from './CancelTransactionFailed';
import { Column } from '../../../../../../../views/wallet/staking/components/CardanoPrimitives';
import { AffectedTransactions } from '../AffectedTransactions/AffectedTransactions';

type CancelTransactionModalProps = {
tx: WalletAccountTransactionWithRequiredRbfParams;
onCancel: () => void;
onBackClick: () => void;
onShowChained: () => void;
chainedTxs?: ChainedTransactions;
selectedAccount: SelectedAccountLoaded;
};

export const CancelTransactionModal = ({
tx,
onCancel,
onBackClick,
onShowChained,
chainedTxs,
selectedAccount,
}: CancelTransactionModalProps) => {
const { account } = selectedAccount;

const dispatch = useDispatch();
const [composedCancelTx, setComposedCancelTx] = useState<PrecomposeResultFinal | null>(null);

const confirmations = useSelector(state =>
selectTransactionConfirmations(state, tx.txid, account.key),
);

const isTxConfirmed = confirmations > 0;

useEffect(() => {
if (tx.vsize === undefined) {
return;
}

const fetchData = async () => {
if (account.addresses === undefined) {
return;
}

const accountToPleaseTypescriptLords = { ...account, addresses: account.addresses }; // Todo: type-guard this

setComposedCancelTx(
await dispatch(
composeCancelTransactionThunk({ account: accountToPleaseTypescriptLords, tx }),
).unwrap(),
);
};

fetchData();
}, [account, tx, dispatch, setComposedCancelTx]);

return (
<CancelTxContext.Provider value={{ composedCancelTx }}>
<TxDetailModalBase
tx={tx}
onCancel={onCancel}
heading={<Translation id="TR_TRANSACTION_DETAILS" />}
bottomContent={
!isTxConfirmed && <CancelTransactionButton account={selectedAccount.account} />
}
onBackClick={onBackClick}
>
{isTxConfirmed ? (
<CancelTransactionFailed />
) : (
<Column>
<CancelTransaction tx={tx} selectedAccount={selectedAccount} />
<AffectedTransactions showChained={onShowChained} chainedTxs={chainedTxs} />
</Column>
)}
</TxDetailModalBase>
</CancelTxContext.Provider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AccountType, Network } from '@suite-common/wallet-config';
import { TrezorLink, Translation } from 'src/components/suite';
import { TransactionItem } from 'src/components/wallet/TransactionItem/TransactionItem';

import { AffectedTransactionItem } from './ChangeFee/AffectedTransactionItem';
import { AffectedTransactionItem } from './AffectedTransactions/AffectedTransactionItem';

const Wrapper = styled.div`
text-align: left;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useSelector } from 'src/hooks/suite';
import { useRbfContext, UseRbfProps } from 'src/hooks/wallet/useRbfForm';

import { RbfFees } from './RbfFees';
import { AffectedTransactions } from './AffectedTransactions';
import { AffectedTransactions } from '../AffectedTransactions/AffectedTransactions';
import { DecreasedOutputs } from './DecreasedOutputs';

/* children are only for test purposes, this prop is not available in regular build */
Expand All @@ -23,7 +23,9 @@ interface ChangeFeeProps extends UseRbfProps {
const ChangeFeeLoaded = (props: ChangeFeeProps) => {
const { tx, showChained, children } = props;
const {

account: { networkType },
chainedTxs,
} = useRbfContext();

const feeRate =
Expand Down Expand Up @@ -64,7 +66,8 @@ const ChangeFeeLoaded = (props: ChangeFeeProps) => {
<RbfFees />

<DecreasedOutputs />
<AffectedTransactions showChained={showChained} />

<AffectedTransactions chainedTxs={chainedTxs} showChained={showChained} />

{children}
</Card>
Expand Down
Loading

0 comments on commit 9b26270

Please sign in to comment.