Skip to content

Commit

Permalink
feat(transactions): cancel STX transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
Aman-zishan committed Jun 28, 2024
1 parent 26b6b4f commit 85f293a
Show file tree
Hide file tree
Showing 21 changed files with 463 additions and 200 deletions.
13 changes: 13 additions & 0 deletions src/app/common/utils/get-burn-address.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { StacksNetwork } from '@stacks/network';
import { ChainID } from '@stacks/transactions';

export function getBurnAddress(network: StacksNetwork): string {
switch (network.chainId) {
case ChainID.Mainnet:
return 'SP00000000000003SCNSJTCSE62ZF4MSE';
case ChainID.Testnet:
return 'ST000000000000000000002AMW42H';
default:
return 'ST000000000000000000002AMW42H';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import {
isBitcoinTxInbound,
} from '@app/common/transactions/bitcoin/utils';
import { openInNewTab } from '@app/common/utils/open-in-new-tab';
import { IncreaseFeeButton } from '@app/components/stacks-transaction-item/increase-fee-button';
import { TransactionTitle } from '@app/components/transaction/transaction-title';
import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';

import { TransactionActionButton } from '../stacks-transaction-item/transaction-action-button';
import { TransactionItemLayout } from '../transaction-item/transaction-item.layout';
import { BitcoinTransactionIcon } from './bitcoin-transaction-icon';
import { InscriptionIcon } from './bitcoin-transaction-inscription-icon';
Expand Down Expand Up @@ -74,10 +74,11 @@ export function BitcoinTransactionItem({ transaction }: BitcoinTransactionItemPr

const title = inscriptionData ? `Ordinal inscription #${inscriptionData.number}` : 'Bitcoin';
const increaseFeeButton = (
<IncreaseFeeButton
<TransactionActionButton
label="Increase fee"
isEnabled={isEnabled}
isSelected={pathname === RouteUrls.IncreaseBtcFee}
onIncreaseFee={onIncreaseFee}
onButtonClick={onIncreaseFee}
/>
);

Expand Down
38 changes: 0 additions & 38 deletions src/app/components/stacks-transaction-item/increase-fee-button.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useLocation, useNavigate } from 'react-router-dom';
import { useMatch, useNavigate } from 'react-router-dom';

import { HStack } from 'leather-styles/jsx';

import { StacksTx } from '@leather.io/models';
import { ChevronsRightIcon, CloseIcon } from '@leather.io/ui';

import { RouteUrls } from '@shared/route-urls';
import { analytics } from '@shared/utils/analytics';
Expand All @@ -19,9 +22,21 @@ import { TransactionTitle } from '@app/components/transaction/transaction-title'
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';

import { TransactionItemLayout } from '../transaction-item/transaction-item.layout';
import { IncreaseFeeButton } from './increase-fee-button';
import { StacksTransactionIcon } from './stacks-transaction-icon';
import { StacksTransactionStatus } from './stacks-transaction-status';
import { TransactionActionIconButton } from './transaction-action-button';

type TransactionAction = 'increaseFee' | 'cancel';

interface ActionRouteMap {
increaseFee: keyof typeof RouteUrls;
cancel: keyof typeof RouteUrls;
}

const actionRouteMap: ActionRouteMap = {
increaseFee: 'IncreaseStxFee',
cancel: 'CancelStxTransaction',
};

interface StacksTransactionItemProps {
caption?: string;
Expand All @@ -42,10 +57,13 @@ export function StacksTransactionItem({
const { handleOpenStacksTxLink } = useStacksExplorerLink();
const currentAccount = useCurrentStacksAccount();

const { pathname } = useLocation();
const navigate = useNavigate();
const { whenWallet } = useWalletType();

const cancelTransactionMatch = useMatch('/cancel-transaction/stx/:txid');
const increaseFeeMatch = useMatch('/increase-fee/stx/:txid');
const isTransactionActionRoute = !!cancelTransactionMatch || !!increaseFeeMatch;

const hasTransferDetailsData = !!caption && !!title && !!value && !!link;
if (!transaction && !hasTransferDetailsData) return null;

Expand All @@ -56,10 +74,11 @@ export function StacksTransactionItem({
});
};

const onIncreaseFee = () => {
const handleTransactionAction = (action: TransactionAction): void => {
if (!transaction) return;

const routeUrl = RouteUrls.IncreaseStxFee.replace(':txid', transaction.tx_id);
const routeKey = actionRouteMap[action];
const routeUrl = RouteUrls[routeKey].replace(':txid', transaction.tx_id);

whenWallet({
ledger: () =>
Expand All @@ -78,22 +97,38 @@ export function StacksTransactionItem({
const txIcon = transaction ? <StacksTransactionIcon transaction={transaction} /> : icon;
const txTitle = transaction ? getTxTitle(transaction) : title || '';
const txValue = transaction ? getTxValue(transaction, isOriginator) : value;
const increaseFeeButton = (
<IncreaseFeeButton
isEnabled={isOriginator && isPending}
isSelected={pathname === RouteUrls.IncreaseStxFee}
onIncreaseFee={onIncreaseFee}
/>

const actionButtonGroup = (
<HStack alignItems="center">
<TransactionActionIconButton
key="Cancel"
icon={<CloseIcon />}
isEnabled={isOriginator && isPending}
isSelected={isTransactionActionRoute}
onButtonClick={() => handleTransactionAction('cancel')}
label="Cancel"
/>
<TransactionActionIconButton
key="Increase Fee"
icon={<ChevronsRightIcon />}
isEnabled={isOriginator && isPending}
isSelected={isTransactionActionRoute}
onButtonClick={() => handleTransactionAction('increaseFee')}
label="Increase Fee"
/>
</HStack>
);

const txIsPending = transaction && transaction.tx_status == 'pending';
const txStatus = transaction && <StacksTransactionStatus transaction={transaction} />;

return (
<TransactionItemLayout
openTxLink={openTxLink}
rightElement={isOriginator && isPending ? increaseFeeButton : undefined}
rightElement={isOriginator && isPending ? actionButtonGroup : undefined}
txCaption={txCaption}
txIcon={txIcon}
txStatus={txStatus}
txStatus={!txIsPending && txStatus}
txTitle={<TransactionTitle title={txTitle} />}
txValue={txValue}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IconButton } from '@leather.io/ui';

import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip';

interface ActionButtonProps {
icon: React.ReactElement;
isEnabled?: boolean;
isSelected: boolean;
label: string;
onButtonClick(): void;
}

export function TransactionActionIconButton(props: ActionButtonProps) {
const { isEnabled, isSelected, onButtonClick, label, icon } = props;
const isActive = isEnabled && !isSelected;

if (!isActive) return null;

return (
<BasicTooltip label={label} side="top">
<IconButton
icon={icon}
backgroundColor={'ink.background-primary'}
onClick={e => {
e.stopPropagation();
onButtonClick();
}}
></IconButton>
</BasicTooltip>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RouteUrls } from '@shared/route-urls';

import { IncreaseFeeField } from './components/increase-fee-field';
import { useStxCancelTransaction } from './hooks/use-stx-cancel-transaction';
import { StxTransactionActionDialog } from './stx-transaction-action-dialog';

export function CancelStxTransactionDialog() {
return (
<StxTransactionActionDialog
title="Cancel transaction"
description="Cancelling a transaction isn't guaranteed to work. A higher fee can help replace the old transaction."
routeUrl={RouteUrls.CancelStxTransaction}
useActionHook={useStxCancelTransaction}
FeeComponent={IncreaseFeeField}
isCancel={true}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { Button } from '@leather.io/ui';

import { useWalletType } from '@app/common/use-wallet-type';

interface IncreaseFeeActionsProps {
interface TransactionActionsProps {
isBroadcasting?: boolean;
isDisabled?: boolean;
isLoading?: boolean;
onCancel(): void;
}
export function IncreaseFeeActions(props: IncreaseFeeActionsProps) {
const { isBroadcasting, isDisabled, isLoading, onCancel } = props;
export function TransactionActions(props: TransactionActionsProps) {
const { onCancel, isDisabled, isLoading, isBroadcasting } = props;

const { handleSubmit } = useFormikContext();

const { whenWallet } = useWalletType();

const actionText = whenWallet({ ledger: 'Confirm on Ledger', software: 'Submit' });
Expand Down
Loading

0 comments on commit 85f293a

Please sign in to comment.