diff --git a/src/app/components/vault/components/vault-progress-bar.tsx b/src/app/components/vault/components/vault-progress-bar.tsx index 5c730861..a3375457 100644 --- a/src/app/components/vault/components/vault-progress-bar.tsx +++ b/src/app/components/vault/components/vault-progress-bar.tsx @@ -1,15 +1,28 @@ import { Box, Progress, Text, VStack } from "@chakra-ui/react"; +import { VaultState } from "@models/vault"; interface VaultProgressBarProps { confirmedBlocks: number; + vaultState: VaultState; } export function VaultProgressBar({ confirmedBlocks, -}: VaultProgressBarProps): React.JSX.Element { + vaultState, +}: VaultProgressBarProps): React.JSX.Element | boolean { + const shouldBeIndeterminate = confirmedBlocks > 6; + + if (vaultState === VaultState.CLOSED && confirmedBlocks > 6) return false; return ( - + - WAITING FOR CONFIRMATIONS: {confirmedBlocks}/6 + {shouldBeIndeterminate + ? "PROCESSING" + : `WAITING FOR CONFIRMATIONS: ${confirmedBlocks}/6`} diff --git a/src/app/components/vault/vault-card.tsx b/src/app/components/vault/vault-card.tsx index e2e3b7e2..7334447f 100644 --- a/src/app/components/vault/vault-card.tsx +++ b/src/app/components/vault/vault-card.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { CustomSkeleton } from "@components/custom-skeleton/custom-skeleton"; +import { useConfirmationChecker } from "@hooks/use-confirmation-checker"; import { Vault, VaultState } from "@models/vault"; import { VaultCardLayout } from "./components/vault-card.layout"; @@ -21,7 +22,11 @@ export function VaultCard({ isSelectable = false, handleSelect, }: VaultBoxProps): React.JSX.Element { - const confirmedBlocks = 3; + const confirmations = useConfirmationChecker( + vault?.state === VaultState.FUNDING ? vault?.fundingTX : vault?.closingTX, + vault?.state, + ); + const [isExpanded, setIsExpanded] = useState(isSelected ? true : false); if (!vault) return ; @@ -48,8 +53,11 @@ export function VaultCard({ isExpanded={isExpanded} /> )} - {[VaultState.FUNDING, VaultState.CLOSING].includes(vault.state) && ( - + {[VaultState.FUNDING, VaultState.CLOSED].includes(vault.state) && ( + )} ); diff --git a/src/app/hooks/use-confirmation-checker.ts b/src/app/hooks/use-confirmation-checker.ts new file mode 100644 index 00000000..b1d78417 --- /dev/null +++ b/src/app/hooks/use-confirmation-checker.ts @@ -0,0 +1,74 @@ +import { useEffect, useMemo, useRef, useState } from "react"; + +import { VaultState } from "@models/vault"; + +export function useConfirmationChecker( + txID: string | undefined, + vaultState: VaultState | undefined, +): number { + const bitcoinExplorerTXURL = `https://devnet.dlc.link/electrs/tx/${txID}`; + const bitcoinExplorerHeightURL = `https://devnet.dlc.link/electrs/blocks/tip/height`; + const fetchInterval = useRef(undefined); + + const [transactionProgress, setTransactionProgress] = useState(0); + + const memoizedTransactionProgress = useMemo( + () => transactionProgress, + [transactionProgress], + ); + + const fetchTransactionDetails = async () => { + if ( + !txID || + (vaultState && + ![VaultState.FUNDING, VaultState.CLOSED].includes(vaultState)) + ) { + clearInterval(fetchInterval.current); + return; + } + + let bitcoinCurrentBlockHeight; + try { + const response = await fetch(bitcoinExplorerHeightURL, { + headers: { Accept: "application/json" }, + }); + bitcoinCurrentBlockHeight = await response.json(); + } catch (error) { + console.error(error); + } + + let bitcoinTransactionBlockHeight; + + try { + const response = await fetch(bitcoinExplorerTXURL, { + headers: { Accept: "application/json" }, + }); + const bitcoinTransactionDetails = await response.json(); + bitcoinTransactionBlockHeight = + bitcoinTransactionDetails.status.block_height; + } catch (error) { + console.error(error); + } + + const difference = + bitcoinCurrentBlockHeight - bitcoinTransactionBlockHeight; + + setTransactionProgress(difference); + + if (difference > 6) { + clearInterval(fetchInterval.current); + } + }; + + fetchTransactionDetails(); + + useEffect(() => { + fetchInterval.current = setInterval( + fetchTransactionDetails, + 10000, + ) as unknown as number; // Cleanup the interval when the component unmounts + return () => clearInterval(fetchInterval.current); + }, []); + + return memoizedTransactionProgress; +}