Skip to content

Commit

Permalink
fix: rework stacking flow, closes #335
Browse files Browse the repository at this point in the history
  • Loading branch information
kyranjamie committed Dec 1, 2020
1 parent e70a34e commit d3eb79b
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ export const TransactionListItemPending: FC<TransactionListItemPendingProps> = p
<TransactionIcon variant="pending" mr="base-loose" />
<Box flex={1}>
<Text textStyle="body.large.medium" display="block">
Sending
{tx.txType === 'token_transfer' ? 'Sending' : 'Initiating Stacking…'}
</Text>
<Text textStyle="body.small" color="ink.600">
{tx.tx_id.substr(0, 28)}
</Text>
</Box>
<Box textAlign="right">
<Text textStyle="body.large" color="ink.900" display="block">
{toHumanReadableStx(tx.amount)}
{tx.txType === 'token_transfer' ? '−' : ''}
{toHumanReadableStx(tx.amount)}
</Text>
<Text textStyle="body.small" color="ink.600">
Pending
Expand Down
14 changes: 10 additions & 4 deletions app/components/home/transaction-list/transaction-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { sumStxTxTotal } from '@utils/sum-stx-tx-total';
import { TransactionIcon, TransactionIconVariants } from './transaction-icon';
import { toHumanReadableStx } from '@utils/unit-convert';
import { makeExplorerLink } from '@utils/external-links';
import { getRecipientAddress, isLockTx } from '@utils/tx-utils';
import { getRecipientAddress, isStackingTx } from '@utils/tx-utils';

import {
createTxListContextMenu,
Expand Down Expand Up @@ -61,18 +61,24 @@ export const TransactionListItem: FC<TransactionListItemProps> = props => {
if (tx.tx_type === 'token_transfer') {
return direction;
}
if (tx.tx_type === 'contract_call' && isLockTx(tx, poxInfo?.contract_id)) {
if (tx.tx_type === 'contract_call' && isStackingTx(tx, poxInfo?.contract_id)) {
return 'locked';
}
return 'default';
}, [direction, poxInfo?.contract_id, tx]);

const transactionTitle = useCallback(() => {
if (tx.tx_type === 'token_transfer') return capitalize(direction);
if (tx.tx_type === 'contract_call' && isStackingTx(tx, poxInfo?.contract_id)) {
if (tx.tx_status === 'pending') return 'Initiating Stacking';
if (tx.tx_status === 'abort_by_response' || tx.tx_status === 'abort_by_post_condition')
return 'Stacking initiation failed';
return 'Stacking initiated successfully';
}
return capitalize(tx.tx_type).replace('_', ' ');
}, [direction, tx.tx_type]);
}, [direction, poxInfo?.contract_id, tx]);

const sumPrefix = direction === 'sent' ? '−' : '';
const sumPrefix = direction === 'sent' && !isStackingTx(tx, poxInfo?.contract_id) ? '−' : '';
const memo =
tx.tx_type === 'token_transfer' &&
Buffer.from(
Expand Down
50 changes: 9 additions & 41 deletions app/modals/stacking/stacking-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { BigNumber } from 'bignumber.js';

import { RootState } from '@store/index';
import { watchContractExecution } from '@api/watch-contract-execution';
import routes from '@constants/routes.json';
import {
selectAddress,
selectPublicKey,
selectEncryptedMnemonic,
selectSalt,
decryptSoftwareWallet,
selectWalletType,
} from '@store/keys';
import { fetchStackerInfo, selectCoreNodeInfo, selectPoxInfo } from '@store/stacking';
import { selectCoreNodeInfo, selectPoxInfo } from '@store/stacking';
import {
makeUnsignedContractCall,
StacksTransaction,
Expand All @@ -43,12 +41,10 @@ import { DecryptWalletForm } from './steps/decrypt-wallet-form';
import { SignTxWithLedger } from './steps/sign-tx-with-ledger';
import { FailedBroadcastError } from './steps/failed-broadcast-error';
import { StackingSuccess } from './steps/stacking-success';
import { PendingContractCall } from './steps/pending-contract-call';

enum StackingModalStep {
DecryptWalletAndSend,
SignWithLedgerAndSend,
PendingTransaction,
StackingSuccess,
FailedContractCall,
}
Expand All @@ -72,12 +68,10 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA
const [decryptionError, setDecryptionError] = useState<string | null>(null);
const [isDecrypting, setIsDecrypting] = useState(false);
const [blockstackApp, setBlockstackApp] = useState<null | BlockstackApp>(null);
const [contractError, setContractError] = useState<null | string>(null);

const {
encryptedMnemonic,
salt,
address,
walletType,
publicKey,
poxInfo,
Expand All @@ -87,7 +81,6 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA
} = useSelector((state: RootState) => ({
salt: selectSalt(state),
encryptedMnemonic: selectEncryptedMnemonic(state),
address: selectAddress(state),
walletType: selectWalletType(state),
publicKey: selectPublicKey(state),
poxInfo: selectPoxInfo(state),
Expand Down Expand Up @@ -181,20 +174,7 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA

const broadcastActions = {
amount: new BigNumber(balance),
onBroadcastSuccess: async (txId: string) => {
setStep(StackingModalStep.PendingTransaction);
const [error, success] = await safeAwait(
watchContractExecution({ txId, nodeUrl: node.url })
);
if (error) {
setContractError(error.message);
setStep(StackingModalStep.FailedContractCall);
}
if (success) {
setStep(StackingModalStep.StackingSuccess);
if (address) dispatch(fetchStackerInfo(address));
}
},
onBroadcastSuccess: () => history.push(routes.HOME),
onBroadcastFail: () => setStep(StackingModalStep.FailedContractCall),
};

Expand All @@ -212,7 +192,9 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA

if (transaction) {
setIsDecrypting(false);
dispatch(broadcastTransaction({ ...broadcastActions, transaction }));
dispatch(
broadcastTransaction({ ...broadcastActions, txType: 'contract_call', transaction })
);
}
}

Expand All @@ -230,7 +212,9 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA
}

if (transaction) {
dispatch(broadcastTransaction({ ...broadcastActions, transaction }));
dispatch(
broadcastTransaction({ ...broadcastActions, txType: 'contract_call', transaction })
);
}
}
};
Expand Down Expand Up @@ -306,18 +290,6 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA
),
}),

[StackingModalStep.PendingTransaction]: () => ({
header: <StackingModalHeader onSelectClose={onClose} />,
body: <PendingContractCall />,
footer: (
<StackingModalFooter>
<StackingModalButton onClick={() => (onClose(), history.push(routes.HOME))}>
Close
</StackingModalButton>
</StackingModalFooter>
),
}),

[StackingModalStep.StackingSuccess]: () => ({
header: <StackingModalHeader onSelectClose={onClose} />,
body: <StackingSuccess cycles={numCycles} />,
Expand All @@ -332,11 +304,7 @@ export const StackingModal: FC<StackingModalProps> = ({ onClose, numCycles, poxA

[StackingModalStep.FailedContractCall]: () => ({
header: <StackingModalHeader onSelectClose={onClose} />,
body: (
<FailedBroadcastError>
{contractError || 'Failed to call stacking contract'}
</FailedBroadcastError>
),
body: <FailedBroadcastError>{'Failed to call stacking contract'}</FailedBroadcastError>,
footer: (
<StackingModalFooter>
<StackingModalButton mode="tertiary" onClick={onClose}>
Expand Down
11 changes: 0 additions & 11 deletions app/modals/stacking/steps/pending-contract-call.tsx

This file was deleted.

8 changes: 6 additions & 2 deletions app/modals/transaction/transaction-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {

if (transaction) {
setIsDecrypting(false);
dispatch(broadcastTransaction({ ...broadcastActions, transaction }));
dispatch(
broadcastTransaction({ ...broadcastActions, txType: 'token_transfer', transaction })
);
}
}

Expand All @@ -190,7 +192,9 @@ export const TransactionModal: FC<TxModalProps> = ({ balance, address }) => {
}

if (transaction) {
dispatch(broadcastTransaction({ ...broadcastActions, transaction }));
dispatch(
broadcastTransaction({ ...broadcastActions, txType: 'token_transfer', transaction })
);
}
}
};
Expand Down
7 changes: 4 additions & 3 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
"license": "MIT",
"dependencies": {
"@ledgerhq/hw-transport-node-hid": "5.23.2",
"@stacks/keychain": "0.16.1",
"@stacks/transactions": "1.0.0-beta.9",
"bitcoinjs-lib": "^5.2.0"
"@stacks/keychain": "1.0.0-beta.10",
"@stacks/transactions": "1.0.0-beta.10",
"bitcoinjs-lib": "^5.2.0",
"blockstack": "21.1.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Transaction } from '@blockstack/stacks-blockchain-api-types';
export interface PendingTransaction extends Pick<Transaction, 'tx_id'> {
amount: string;
time: number;
txType: Transaction['tx_type'];
}

export type PendingTransactionState = EntityState<PendingTransaction>;
Expand Down
4 changes: 3 additions & 1 deletion app/store/transaction/transaction.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ export const broadcastTxFail = createAction<BroadcastTxFail>(

interface BroadcastTransactionArgs {
transaction: StacksTransaction;
txType: Transaction['tx_type'];
amount: BigNumber;
onBroadcastSuccess(txId: string): void;
onBroadcastFail(): void;
}
export function broadcastTransaction(args: BroadcastTransactionArgs) {
const { amount, transaction, onBroadcastSuccess, onBroadcastFail } = args;
const { amount, transaction, txType, onBroadcastSuccess, onBroadcastFail } = args;
return async (dispatch: Dispatch, getState: GetState) => {
dispatch(broadcastTx());

Expand All @@ -86,6 +87,7 @@ export function broadcastTransaction(args: BroadcastTransactionArgs) {
tx_id: safelyFormatHexTxid(blockchainResponse),
amount: amount.toString(),
time: +new Date(),
txType,
})
);
return blockchainResponse;
Expand Down
6 changes: 3 additions & 3 deletions app/utils/sum-stx-tx-total.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Transaction, TransactionEvent } from '@blockstack/stacks-blockchain-api-types';
import BigNumber from 'bignumber.js';
import { getStxTxDirection } from './get-stx-transfer-direction';
import { isLockTx } from './tx-utils';
import { isStackingTx } from './tx-utils';

export function sumStxTxTotal(address: string, tx: Transaction, poxContractID?: string) {
export function sumStxTxTotal(address: string, tx: Transaction, poxContractId?: string) {
const dir = getStxTxDirection(address, tx);
if (tx.tx_type === 'token_transfer') {
return new BigNumber(tx.token_transfer.amount).plus(dir === 'sent' ? tx.fee_rate : 0);
Expand All @@ -12,7 +12,7 @@ export function sumStxTxTotal(address: string, tx: Transaction, poxContractID?:
return new BigNumber(tx.fee_rate);
}

if (isLockTx(tx, poxContractID) && tx.tx_result?.repr) {
if (isStackingTx(tx, poxContractId) && tx.tx_result?.repr) {
// We get the amount stacked from the tx_result
const matcher = /\(tuple \(lock-amount\su(\d+)/;
const matches = matcher.exec(tx.tx_result.repr);
Expand Down
4 changes: 2 additions & 2 deletions app/utils/tx-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export function getRecipientAddress(tx: Transaction) {
return tx.token_transfer.recipient_address;
}

export function isLockTx(tx: Transaction, poxContractID?: string): boolean {
return tx.tx_type === 'contract_call' && tx.contract_call.contract_id === poxContractID;
export function isStackingTx(tx: Transaction, poxContractId?: string): boolean {
return tx.tx_type === 'contract_call' && tx.contract_call.contract_id === poxContractId;
}
Loading

0 comments on commit d3eb79b

Please sign in to comment.