Skip to content

Commit

Permalink
feat: disable stake button if utxo not available
Browse files Browse the repository at this point in the history
  • Loading branch information
jrwbabylonlab committed Dec 10, 2024
1 parent 36d0721 commit f070737
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/app/state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const STATE_LIST = [DelegationState, DelegationV2State];

export interface AppState {
availableUTXOs?: UTXO[];
allUTXOs?: UTXO[];
totalBalance: number;
networkInfo?: NetworkInfo;
currentHeight?: number;
Expand Down Expand Up @@ -116,6 +117,7 @@ export function AppState({ children }: PropsWithChildren) {
const context = useMemo(
() => ({
availableUTXOs,
allUTXOs: utxos,
currentHeight: height,
totalBalance,
networkInfo,
Expand All @@ -127,6 +129,7 @@ export function AppState({ children }: PropsWithChildren) {
}),
[
availableUTXOs,
utxos,
height,
totalBalance,
networkInfo,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 (
<button
className="btn btn-outline btn-xs inline-flex text-sm font-normal text-primary-dark"
onClick={() => props.onClick?.(buttonProps.action, props.delegation)}
<span
className="cursor-pointer"
data-tooltip-id={`tooltip-delegation-action-${delegation.stakingTxHashHex}-${buttonProps.action}`}
data-tooltip-content={
isButtonDisabled ? "Please ensure your UTXOs are still available" : ""
}
data-tooltip-place="top"
>
{buttonProps.title}
</button>
<button
className="btn btn-outline btn-xs inline-flex text-sm font-normal text-primary-dark"
onClick={() => props.onClick?.(buttonProps.action, props.delegation)}
disabled={isButtonDisabled}
>
{buttonProps.title}
</button>
<Tooltip
id={`tooltip-delegation-action-${delegation.stakingTxHashHex}-${buttonProps.action}`}
className="tooltip-wrap"
/>
</span>
);
}
21 changes: 21 additions & 0 deletions src/utils/delegations/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UTXO } from "@babylonlabs-io/btc-staking-ts";
import { Transaction } from "bitcoinjs-lib";

import { BtcStakingInputs } from "@/app/hooks/services/useTransactionService";
Expand Down Expand Up @@ -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
);
});
});
};

0 comments on commit f070737

Please sign in to comment.