From f070737b943633cc398916581e576a9931fe7f44 Mon Sep 17 00:00:00 2001 From: wjrjerome Date: Tue, 10 Dec 2024 16:18:10 +1100 Subject: [PATCH] feat: disable stake button if utxo not available --- src/app/state/index.tsx | 3 ++ .../components/ActionButton.tsx | 44 ++++++++++++++++--- src/utils/delegations/index.ts | 21 +++++++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/app/state/index.tsx b/src/app/state/index.tsx index d1898a2e..15da12d1 100644 --- a/src/app/state/index.tsx +++ b/src/app/state/index.tsx @@ -21,6 +21,7 @@ const STATE_LIST = [DelegationState, DelegationV2State]; export interface AppState { availableUTXOs?: UTXO[]; + allUTXOs?: UTXO[]; totalBalance: number; networkInfo?: NetworkInfo; currentHeight?: number; @@ -116,6 +117,7 @@ export function AppState({ children }: PropsWithChildren) { const context = useMemo( () => ({ availableUTXOs, + allUTXOs: utxos, currentHeight: height, totalBalance, networkInfo, @@ -127,6 +129,7 @@ export function AppState({ children }: PropsWithChildren) { }), [ availableUTXOs, + utxos, height, totalBalance, networkInfo, diff --git a/src/components/delegations/DelegationList/components/ActionButton.tsx b/src/components/delegations/DelegationList/components/ActionButton.tsx index 0c3098f9..079ab307 100644 --- a/src/components/delegations/DelegationList/components/ActionButton.tsx +++ b/src/components/delegations/DelegationList/components/ActionButton.tsx @@ -1,9 +1,14 @@ +import { Transaction } from "bitcoinjs-lib"; +import { Tooltip } from "react-tooltip"; + import { DELEGATION_ACTIONS as ACTIONS } from "@/app/constants"; import { ActionType } from "@/app/hooks/services/useDelegationService"; +import { useAppState } from "@/app/state"; import { DelegationV2, DelegationV2StakingState as State, } from "@/app/types/delegationsV2"; +import { isTransactionInputAvailable } from "@/utils/delegations"; interface ActionButtonProps { delegation: DelegationV2; @@ -42,16 +47,43 @@ const ACTION_BUTTON_PROPS: Record< }; export function ActionButton(props: ActionButtonProps) { + const { allUTXOs } = useAppState(); const buttonProps = ACTION_BUTTON_PROPS[props.state]; - if (!buttonProps) return null; + if (!buttonProps || !allUTXOs) return null; + const delegation = props.delegation; + + // For verifed delegation where user will perform stake action, + // we need to verify that the UTXOs are still available + // to avoid the user to perform the action on a spent UTXO + let isButtonDisabled = false; + if (buttonProps.action === ACTIONS.STAKE) { + isButtonDisabled = !isTransactionInputAvailable( + Transaction.fromHex(delegation.stakingTxHex), + allUTXOs, + ); + } return ( - + + + ); } diff --git a/src/utils/delegations/index.ts b/src/utils/delegations/index.ts index 46a432fb..49d6eb30 100644 --- a/src/utils/delegations/index.ts +++ b/src/utils/delegations/index.ts @@ -1,3 +1,4 @@ +import { UTXO } from "@babylonlabs-io/btc-staking-ts"; import { Transaction } from "bitcoinjs-lib"; import { BtcStakingInputs } from "@/app/hooks/services/useTransactionService"; @@ -58,3 +59,23 @@ export const validateStakingInput = (stakingInput: BtcStakingInputs) => { if (!stakingInput.stakingAmountSat) throw new Error("Staking amount not set"); if (!stakingInput.stakingTimelock) throw new Error("Staking time not set"); }; + +/** + * Verifies that the transaction inputs are still available from the UTXOs set. + * @param tx - The transaction to verify. + * @param allUTXOs - The UTXOs set. + * @returns True if the transaction inputs are still available, false otherwise. + */ +export const isTransactionInputAvailable = ( + tx: Transaction, + allUTXOs: UTXO[], +) => { + return tx.ins.every((input) => { + return allUTXOs.find((utxo) => { + return ( + utxo.txid === Buffer.from(input.hash).reverse().toString("hex") && + utxo.vout === input.index + ); + }); + }); +};