diff --git a/src/app/app.tsx b/src/app/app.tsx
index b1ca9705..af4b38fd 100644
--- a/src/app/app.tsx
+++ b/src/app/app.tsx
@@ -9,7 +9,7 @@ import { MyVaults } from '@pages/my-vaults/my-vaults';
import { PointsPage } from '@pages/points/points-page';
import { ProofOfReservePage } from '@pages/proof-of-reserve/proof-of-reserve-page';
import { BalanceContextProvider } from '@providers/balance-context-provider';
-import { BlockchainHeightContextProvider } from '@providers/bitcoin-query-provider';
+import { BitcoinTransactionConfirmationsProvider } from '@providers/bitcoin-query-provider';
import { BitcoinWalletContextProvider } from '@providers/bitcoin-wallet-context-provider';
import { EthereumNetworkConfigurationContextProvider } from '@providers/ethereum-network-configuration.provider';
import { EthereumObserverProvider } from '@providers/ethereum-observer-provider';
@@ -32,7 +32,7 @@ export function App(): React.JSX.Element {
-
+
@@ -50,7 +50,7 @@ export function App(): React.JSX.Element {
-
+
diff --git a/src/app/components/mint-unmint/components/mint-unmint.layout.tsx b/src/app/components/mint-unmint/components/mint-unmint.layout.tsx
index cf80537d..47b5a423 100644
--- a/src/app/components/mint-unmint/components/mint-unmint.layout.tsx
+++ b/src/app/components/mint-unmint/components/mint-unmint.layout.tsx
@@ -9,7 +9,7 @@ export function MintUnmintLayout({ children }: MintUnmintLayoutProps): React.JSX
return (
}
{flowPropertyMap[flow][currentStep[0]].title}:
-
- {currentStep[0] === 2 && (
+ {currentVault && (
<>
-
- b) {flowPropertyMap[flow][currentStep[0]].subtitle}:
-
-
+
+ {currentStep[0] === 2 && (
+ <>
+
+ b) {flowPropertyMap[flow][currentStep[0]].subtitle}:
+
+
+ >
+ )}
>
)}
diff --git a/src/app/components/mint-unmint/components/unmint/components/unmint-vault-selector.tsx b/src/app/components/mint-unmint/components/unmint/components/unmint-vault-selector.tsx
index 9dedaa97..e23fae37 100644
--- a/src/app/components/mint-unmint/components/unmint/components/unmint-vault-selector.tsx
+++ b/src/app/components/mint-unmint/components/unmint/components/unmint-vault-selector.tsx
@@ -49,11 +49,6 @@ export function UnmintVaultSelector({
const { ethereumNetworkConfiguration } = useContext(EthereumNetworkConfigurationContext);
- function handleSelect(uuid: string): void {
- const vault = fundedVaults.find(vault => vault.uuid === uuid);
- if (vault) setSelectedVault(vault);
- }
-
useEffect(() => {
setSelectedVault(fundedVaults.find(vault => vault.uuid === unmintStep[1]));
}, [fundedVaults, unmintStep]);
@@ -124,11 +119,7 @@ export function UnmintVaultSelector({
Select vault to withdraw Bitcoin:
-
+
)}
diff --git a/src/app/components/my-vaults-small/components/my-vaults-small.layout.tsx b/src/app/components/my-vaults-small/components/my-vaults-small.layout.tsx
index a3d76fca..e41b63db 100644
--- a/src/app/components/my-vaults-small/components/my-vaults-small.layout.tsx
+++ b/src/app/components/my-vaults-small/components/my-vaults-small.layout.tsx
@@ -6,7 +6,7 @@ export function MyVaultsSmallLayout({ children }: HasChildren): React.JSX.Elemen
void;
-}
-
-export function VaultCardLayout({
- children,
- isSelectable = false,
- handleClick,
-}: VaultCardLayoutProps): React.JSX.Element {
- return (
- {}}
- >
- {children}
-
- );
-}
diff --git a/src/app/components/vault/components/vault-expand-button.tsx b/src/app/components/vault/components/vault-expand-button.tsx
deleted file mode 100644
index 9b6fa91f..00000000
--- a/src/app/components/vault/components/vault-expand-button.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
-import { Button, Text } from '@chakra-ui/react';
-
-interface VaultExpandButtonProps {
- isExpanded: boolean;
- handleClick: () => void;
-}
-
-export function VaultExpandButton({
- isExpanded,
- handleClick,
-}: VaultExpandButtonProps): React.JSX.Element {
- return (
-
- );
-}
diff --git a/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-row.tsx b/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-row.tsx
deleted file mode 100644
index 7ed35398..00000000
--- a/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-row.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { CheckCircleIcon, CopyIcon } from '@chakra-ui/icons';
-import { Button, HStack, Text, Tooltip } from '@chakra-ui/react';
-import { useCopyToClipboard } from '@hooks/use-copy-to-clipboard';
-import { truncateAddress } from 'dlc-btc-lib/utilities';
-
-interface VaultExpandedInformationUUIDRowProps {
- uuid: string;
-}
-
-export function VaultExpandedInformationUUIDRow({
- uuid,
-}: VaultExpandedInformationUUIDRowProps): React.JSX.Element {
- const { hasCopied, copyToClipboard } = useCopyToClipboard();
-
- return (
-
-
-
- UUID
-
-
-
-
-
-
- {truncateAddress(uuid)}
-
-
- );
-}
diff --git a/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-transaction-row.tsx b/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-transaction-row.tsx
deleted file mode 100644
index 73230440..00000000
--- a/src/app/components/vault/components/vault-expanded-information/components/vault-expanded-information-transaction-row.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { HStack, Text } from '@chakra-ui/react';
-
-interface VaultExpandedInformationTransactionRowProps {
- label: string;
- value: string;
-}
-
-export function VaultExpandedInformationTransactionRow({
- label,
- value,
-}: VaultExpandedInformationTransactionRowProps): React.JSX.Element {
- return (
-
-
- {label}
-
-
- window.open(`${appConfiguration.bitcoinBlockchainExplorerURL}/tx/${value}`, '_blank')
- }
- _hover={{ cursor: 'pointer' }}
- >
- View in TX explorer
-
-
- );
-}
diff --git a/src/app/components/vault/components/vault-expanded-information/vault-expanded-information.tsx b/src/app/components/vault/components/vault-expanded-information/vault-expanded-information.tsx
deleted file mode 100644
index 16c92588..00000000
--- a/src/app/components/vault/components/vault-expanded-information/vault-expanded-information.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import { useDispatch } from 'react-redux';
-import { useNavigate } from 'react-router-dom';
-
-import { Button, Divider, SlideFade, Stack, VStack } from '@chakra-ui/react';
-import { Vault } from '@models/vault';
-import { mintUnmintActions } from '@store/slices/mintunmint/mintunmint.actions';
-import { VaultState } from 'dlc-btc-lib/models';
-
-import { VaultExpandedInformationUUIDRow } from './components/vault-expanded-information-row';
-import { VaultExpandedInformationTransactionRow } from './components/vault-expanded-information-transaction-row';
-
-interface VaultExpandedInformationProps {
- vault: Vault;
- isExpanded: boolean;
- isSelected?: boolean;
- close: () => void;
-}
-
-export function VaultExpandedInformation({
- vault,
- isExpanded,
- isSelected,
- close,
-}: VaultExpandedInformationProps): React.JSX.Element {
- const navigate = useNavigate();
- const dispatch = useDispatch();
-
- const { fundingTX, withdrawDepositTX, state, uuid } = vault;
-
- function handleWithdraw() {
- navigate('/mint-withdraw');
- if (vault.valueLocked === vault.valueMinted) {
- dispatch(mintUnmintActions.setUnmintStep([0, uuid]));
- } else {
- dispatch(mintUnmintActions.setUnmintStep([1, uuid]));
- }
- close();
- }
-
- return (
-
-
-
-
-
-
-
- {!!fundingTX && (
-
- )}
- {!!withdrawDepositTX && (
-
- )}
-
- {[VaultState.READY, VaultState.FUNDED].includes(state) && !isSelected && (
-
- )}
- {state === VaultState.FUNDED && !isSelected && (
-
- )}
-
-
-
-
- );
-}
diff --git a/src/app/components/vault/components/vault-information.tsx b/src/app/components/vault/components/vault-information.tsx
deleted file mode 100644
index 6dbcc1b2..00000000
--- a/src/app/components/vault/components/vault-information.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { CheckIcon } from '@chakra-ui/icons';
-import { HStack, Image, Text, VStack } from '@chakra-ui/react';
-import { VaultState } from 'dlc-btc-lib/models';
-
-import { VaultExpandButton } from './vault-expand-button';
-
-const getAssetLogo = (state: VaultState) => {
- return [VaultState.FUNDED, VaultState.CLOSING, VaultState.CLOSED, VaultState.PENDING].includes(
- state
- )
- ? '/images/logos/dlc-btc-logo.svg'
- : '/images/logos/bitcoin-logo.svg';
-};
-
-interface VaultInformationProps {
- collateral: number;
- state: VaultState;
- timestamp: number;
- isExpanded: boolean;
- isSelectable?: boolean;
- isSelected?: boolean;
- handleClick: () => void;
-}
-
-export function VaultInformation({
- state,
- collateral,
- timestamp,
- isExpanded,
- isSelectable,
- isSelected,
- handleClick,
-}: VaultInformationProps): React.JSX.Element {
- const date = new Date(timestamp * 1000).toLocaleDateString('en-US');
-
- return (
-
-
-
-
-
- {collateral}
-
-
-
- {date}
-
-
- {isSelectable ? (
-
- {isSelected && }
-
- ) : (
- handleClick()} />
- )}
-
- );
-}
diff --git a/src/app/components/vault/components/vault-progress-bar.tsx b/src/app/components/vault/components/vault-progress-bar.tsx
deleted file mode 100644
index 211d3103..00000000
--- a/src/app/components/vault/components/vault-progress-bar.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import { Box, Progress, Text, VStack } from '@chakra-ui/react';
-import { VaultState } from 'dlc-btc-lib/models';
-
-interface VaultProgressBarProps {
- confirmedBlocks: number;
- vaultState: VaultState;
-}
-
-export function VaultProgressBar({
- confirmedBlocks,
- vaultState,
-}: VaultProgressBarProps): React.JSX.Element | boolean {
- const shouldBeIndeterminate = confirmedBlocks > 6 || Number.isNaN(confirmedBlocks);
-
- if (vaultState === VaultState.CLOSED && confirmedBlocks > 6) return false;
- return (
-
-
-
-
- {shouldBeIndeterminate ? 'PROCESSING' : `WAITING FOR CONFIRMATIONS: ${confirmedBlocks}/6`}
-
-
-
- );
-}
diff --git a/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/components/vault.details.button-group.button.tsx b/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/components/vault.details.button-group.button.tsx
new file mode 100644
index 00000000..756d683a
--- /dev/null
+++ b/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/components/vault.details.button-group.button.tsx
@@ -0,0 +1,29 @@
+import { Button } from '@chakra-ui/react';
+
+interface VaultExpandedInformationButtonProps {
+ label: string;
+ onClick: () => void;
+ isDisabled?: boolean;
+}
+
+export function VaultExpandedInformationButton({
+ label,
+ onClick,
+ isDisabled,
+}: VaultExpandedInformationButtonProps): React.JSX.Element {
+ return (
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/vault.details.button-group.tsx b/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/vault.details.button-group.tsx
new file mode 100644
index 00000000..0fad1c04
--- /dev/null
+++ b/src/app/components/vault/components/vault.detaills/components/vault.details.button-group/vault.details.button-group.tsx
@@ -0,0 +1,31 @@
+import { HStack } from '@chakra-ui/react';
+import { VaultState } from 'dlc-btc-lib/models';
+
+import { VaultExpandedInformationButton } from './components/vault.details.button-group.button';
+
+interface VaultExpandedInformationButtonGroupProps {
+ vaultState: VaultState;
+ vaultTotalLockedValue: number;
+ handleDepositClick: () => void;
+ handleWithdrawClick: () => void;
+}
+
+export function VaultExpandedInformationButtonGroup({
+ vaultState,
+ vaultTotalLockedValue,
+ handleDepositClick,
+ handleWithdrawClick,
+}: VaultExpandedInformationButtonGroupProps): React.JSX.Element {
+ const isButtonDisabled = vaultState === VaultState.READY || vaultTotalLockedValue === 0;
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/components/vault.details.transaction-stack.transaction-row.tsx b/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/components/vault.details.transaction-stack.transaction-row.tsx
new file mode 100644
index 00000000..0c45488c
--- /dev/null
+++ b/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/components/vault.details.transaction-stack.transaction-row.tsx
@@ -0,0 +1,36 @@
+import { HStack, Text } from '@chakra-ui/react';
+
+interface VaultTransactionRowProps {
+ label: string;
+ value?: string;
+}
+
+export function VaultTransactionRow({
+ label,
+ value,
+}: VaultTransactionRowProps): React.JSX.Element | false {
+ if (!value) return false;
+
+ return (
+
+
+
+ {label}
+
+
+
+
+ window.open(`${appConfiguration.bitcoinBlockchainExplorerURL}/tx/${value}`, '_blank')
+ }
+ _hover={{ cursor: 'pointer' }}
+ >
+ View in TX explorer
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/vault.details.transaction-stack.tsx b/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/vault.details.transaction-stack.tsx
new file mode 100644
index 00000000..b0c30999
--- /dev/null
+++ b/src/app/components/vault/components/vault.detaills/components/vault.details.transaction-stack/vault.details.transaction-stack.tsx
@@ -0,0 +1,23 @@
+import { Divider, VStack } from '@chakra-ui/react';
+
+import { VaultTransactionRow } from './components/vault.details.transaction-stack.transaction-row';
+
+interface VaultTransactionStackProps {
+ vaultFundingTX?: string;
+ vaultWithdrawDepositTX?: string;
+}
+
+export function VaultTransactionStack({
+ vaultFundingTX,
+ vaultWithdrawDepositTX,
+}: VaultTransactionStackProps): React.JSX.Element | false {
+ if (!vaultFundingTX && !vaultWithdrawDepositTX) return false;
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.detaills/vault.details.tsx b/src/app/components/vault/components/vault.detaills/vault.details.tsx
new file mode 100644
index 00000000..21351ae2
--- /dev/null
+++ b/src/app/components/vault/components/vault.detaills/vault.details.tsx
@@ -0,0 +1,72 @@
+import { useDispatch } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import { Collapse, Divider, Stack, VStack } from '@chakra-ui/react';
+import { mintUnmintActions } from '@store/slices/mintunmint/mintunmint.actions';
+import { VaultState } from 'dlc-btc-lib/models';
+
+import { VaultExpandedInformationButtonGroup } from './components/vault.details.button-group/vault.details.button-group';
+import { VaultTransactionStack } from './components/vault.details.transaction-stack/vault.details.transaction-stack';
+
+interface VaultDetailsProps {
+ vaultUUID: string;
+ vaultState: VaultState;
+ vaultTotalLockedValue: number;
+ vaultTotalMintedValue: number;
+ isVaultExpanded: boolean;
+ vaultFundingTX?: string;
+ vaultWithdrawDepositTX?: string;
+}
+
+export function VaultDetails({
+ vaultUUID,
+ vaultState,
+ vaultFundingTX,
+ vaultWithdrawDepositTX,
+ vaultTotalLockedValue,
+ vaultTotalMintedValue,
+ isVaultExpanded,
+}: VaultDetailsProps): React.JSX.Element {
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+
+ function handleDepositClick() {
+ navigate('/mint-withdraw');
+ dispatch(mintUnmintActions.setMintStep([1, vaultUUID]));
+ close();
+ }
+
+ function handleWithdrawClick() {
+ navigate('/mint-withdraw');
+ if (vaultTotalLockedValue === vaultTotalMintedValue) {
+ dispatch(mintUnmintActions.setUnmintStep([0, vaultUUID]));
+ } else {
+ dispatch(mintUnmintActions.setUnmintStep([1, vaultUUID]));
+ }
+ close();
+ }
+
+ return (
+
+
+
+
+
+
+ {vaultState !== VaultState.PENDING && (
+
+ )}
+
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.header/components/vault.header.copy-button.tsx b/src/app/components/vault/components/vault.header/components/vault.header.copy-button.tsx
new file mode 100644
index 00000000..dd0acb9b
--- /dev/null
+++ b/src/app/components/vault/components/vault.header/components/vault.header.copy-button.tsx
@@ -0,0 +1,41 @@
+import { CheckCircleIcon, CopyIcon } from '@chakra-ui/icons';
+import { HStack, IconButton, Tooltip } from '@chakra-ui/react';
+
+interface VaultCopyButtonProps {
+ onCopyUUID: () => void;
+ hasCopiedUUID: boolean;
+}
+
+export function VaultCopyButton({
+ onCopyUUID,
+ hasCopiedUUID,
+}: VaultCopyButtonProps): React.JSX.Element {
+ return (
+
+
+
+ ) : (
+
+ )
+ }
+ />
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.header/vault.header.tsx b/src/app/components/vault/components/vault.header/vault.header.tsx
new file mode 100644
index 00000000..dfc067b8
--- /dev/null
+++ b/src/app/components/vault/components/vault.header/vault.header.tsx
@@ -0,0 +1,48 @@
+import { HStack, Stack, Text, useClipboard } from '@chakra-ui/react';
+import { truncateAddress } from 'dlc-btc-lib/utilities';
+
+import { VaultCopyButton } from './components/vault.header.copy-button';
+
+interface VaultHeaderProps {
+ vaultUUID: string;
+ vaultCreationTimestamp: number;
+}
+
+export function VaultHeader({
+ vaultUUID,
+ vaultCreationTimestamp,
+}: VaultHeaderProps): React.JSX.Element {
+ const { onCopy, hasCopied } = useClipboard(vaultUUID);
+
+ const vaultTruncatedUUID = truncateAddress(vaultUUID);
+ const vaultCreationDate = new Date(vaultCreationTimestamp * 1000).toLocaleDateString('en-US');
+
+ return (
+
+
+
+
+ Vault
+
+
+
+
+ {vaultTruncatedUUID}
+
+
+
+
+
+
+ {vaultCreationDate}
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.layout.tsx b/src/app/components/vault/components/vault.layout.tsx
new file mode 100644
index 00000000..f3c28fd0
--- /dev/null
+++ b/src/app/components/vault/components/vault.layout.tsx
@@ -0,0 +1,23 @@
+import { ReactNode } from 'react';
+
+import { VStack } from '@chakra-ui/react';
+
+interface VaultLayoutProps {
+ children: ReactNode;
+}
+
+export function VaultLayout({ children }: VaultLayoutProps): React.JSX.Element {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.action-button.tsx b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.action-button.tsx
new file mode 100644
index 00000000..483d3afb
--- /dev/null
+++ b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.action-button.tsx
@@ -0,0 +1,39 @@
+import { ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons';
+import { Button, HStack, Text } from '@chakra-ui/react';
+
+interface VaultActionButtonProps {
+ isExpanded: boolean;
+ handleClick: () => void;
+ variant?: 'select';
+}
+
+export function VaultActionButton({
+ isExpanded,
+ handleClick,
+ variant,
+}: VaultActionButtonProps): React.JSX.Element {
+ return (
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/components/vault.main-stack.asset-information-stack.asset-row.tsx b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/components/vault.main-stack.asset-information-stack.asset-row.tsx
new file mode 100644
index 00000000..7df15cf4
--- /dev/null
+++ b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/components/vault.main-stack.asset-information-stack.asset-row.tsx
@@ -0,0 +1,29 @@
+import { HStack, Image, Text } from '@chakra-ui/react';
+
+interface VaultAssetRowProps {
+ assetLogo: string;
+ assetValue: number;
+ assetSymbol: string;
+}
+
+export function VaultAssetRow({
+ assetLogo,
+ assetValue,
+ assetSymbol,
+}: VaultAssetRowProps): React.JSX.Element {
+ return (
+
+
+
+
+ {assetValue}
+
+
+
+
+ {assetSymbol}
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/vault.main-stack.asset-information-stack.tsx b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/vault.main-stack.asset-information-stack.tsx
new file mode 100644
index 00000000..6983d49b
--- /dev/null
+++ b/src/app/components/vault/components/vault.main-stack/components/vault.main-stack.asset-information-stack/vault.main-stack.asset-information-stack.tsx
@@ -0,0 +1,27 @@
+import { VStack } from '@chakra-ui/react';
+
+import { VaultAssetRow } from './components/vault.main-stack.asset-information-stack.asset-row';
+
+interface VaultAssetInformationStackProps {
+ vaultTotalLockedValue: number;
+ vaultTotalMintedValue: number;
+}
+export function VaultAssetInformationStack({
+ vaultTotalLockedValue,
+ vaultTotalMintedValue,
+}: VaultAssetInformationStackProps): React.JSX.Element {
+ return (
+
+
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.main-stack/vault-main-stack.tsx b/src/app/components/vault/components/vault.main-stack/vault-main-stack.tsx
new file mode 100644
index 00000000..b081dc82
--- /dev/null
+++ b/src/app/components/vault/components/vault.main-stack/vault-main-stack.tsx
@@ -0,0 +1,37 @@
+import { Divider, HStack } from '@chakra-ui/react';
+
+import { VaultActionButton } from './components/vault.main-stack.action-button';
+import { VaultAssetInformationStack } from './components/vault.main-stack.asset-information-stack/vault.main-stack.asset-information-stack';
+
+interface VaultMainStackProps {
+ vaultTotalLockedValue: number;
+ vaultTotalMintedValue: number;
+ isVaultExpanded: boolean;
+ handleButtonClick: () => void;
+ variant?: 'select';
+}
+
+export function VaultMainStack({
+ vaultTotalLockedValue,
+ vaultTotalMintedValue,
+ isVaultExpanded,
+ handleButtonClick,
+ variant,
+}: VaultMainStackProps): React.JSX.Element {
+ return (
+
+
+
+
+ handleButtonClick()}
+ variant={variant}
+ />
+
+
+ );
+}
diff --git a/src/app/components/vault/components/vault.progress-bar.tsx b/src/app/components/vault/components/vault.progress-bar.tsx
new file mode 100644
index 00000000..8ff912e0
--- /dev/null
+++ b/src/app/components/vault/components/vault.progress-bar.tsx
@@ -0,0 +1,50 @@
+import { Box, Progress, Text, VStack } from '@chakra-ui/react';
+import { VaultState } from 'dlc-btc-lib/models';
+
+import { BITCOIN_BLOCK_CONFIRMATIONS } from '@shared/constants/bitcoin.constants';
+
+interface VaultProgressBarProps {
+ bitcoinTransactionConfirmations?: number;
+ vaultState: VaultState;
+}
+
+export function VaultProgressBar({
+ bitcoinTransactionConfirmations,
+ vaultState,
+}: VaultProgressBarProps): React.JSX.Element | false {
+ if (vaultState !== VaultState.PENDING) return false;
+
+ const showProcessing =
+ bitcoinTransactionConfirmations === undefined ||
+ bitcoinTransactionConfirmations === 0 ||
+ bitcoinTransactionConfirmations >= BITCOIN_BLOCK_CONFIRMATIONS;
+ return (
+
+
+
+
+ {showProcessing
+ ? 'PROCESSING'
+ : `WAITING FOR CONFIRMATIONS: ${bitcoinTransactionConfirmations}/${BITCOIN_BLOCK_CONFIRMATIONS}`}
+
+
+
+ );
+}
diff --git a/src/app/components/vault/vault-card.tsx b/src/app/components/vault/vault-card.tsx
deleted file mode 100644
index 2fd824cd..00000000
--- a/src/app/components/vault/vault-card.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import React, { useState } from 'react';
-
-import { CustomSkeleton } from '@components/custom-skeleton/custom-skeleton';
-import { useConfirmationChecker } from '@hooks/use-confirmation-checker';
-import { Vault } from '@models/vault';
-import { VaultState } from 'dlc-btc-lib/models';
-
-import { VaultCardLayout } from './components/vault-card.layout';
-import { VaultExpandedInformation } from './components/vault-expanded-information/vault-expanded-information';
-import { VaultInformation } from './components/vault-information';
-import { VaultProgressBar } from './components/vault-progress-bar';
-
-interface VaultCardProps {
- vault?: Vault;
- isSelected?: boolean;
- isSelectable?: boolean;
- handleSelect?: () => void;
-}
-
-export function VaultCard({
- vault,
- isSelected = false,
- isSelectable = false,
- handleSelect,
-}: VaultCardProps): React.JSX.Element {
- const [isExpanded, setIsExpanded] = useState(isSelected ? true : false);
-
- const confirmations = useConfirmationChecker(vault);
-
- if (!vault) return ;
-
- return (
- handleSelect && handleSelect()}>
- setIsExpanded(!isExpanded)}
- />
- {isExpanded && (
- setIsExpanded(false)}
- />
- )}
- {vault.state === VaultState.PENDING && (
-
- )}
-
- );
-}
diff --git a/src/app/components/vault/vault.tsx b/src/app/components/vault/vault.tsx
new file mode 100644
index 00000000..46c6a6c1
--- /dev/null
+++ b/src/app/components/vault/vault.tsx
@@ -0,0 +1,57 @@
+import React, { useContext, useState } from 'react';
+import { useDispatch } from 'react-redux';
+
+import { Vault as VaultModel } from '@models/vault';
+import { BitcoinTransactionConfirmationsContext } from '@providers/bitcoin-query-provider';
+import { mintUnmintActions } from '@store/slices/mintunmint/mintunmint.actions';
+
+import { VaultDetails } from './components/vault.detaills/vault.details';
+import { VaultHeader } from './components/vault.header/vault.header';
+import { VaultLayout } from './components/vault.layout';
+import { VaultMainStack } from './components/vault.main-stack/vault-main-stack';
+import { VaultProgressBar } from './components/vault.progress-bar';
+
+interface VaultProps {
+ vault: VaultModel;
+ variant?: 'select';
+}
+
+export function Vault({ vault, variant }: VaultProps): React.JSX.Element {
+ const dispatch = useDispatch();
+ const [isVaultExpanded, setIsVaultExpanded] = useState(false);
+
+ function handleMainButtonClick() {
+ if (variant === 'select') {
+ dispatch(mintUnmintActions.setUnmintStep([0, vault.uuid]));
+ } else {
+ setIsVaultExpanded(!isVaultExpanded);
+ }
+ }
+
+ const confirmations = useContext(
+ BitcoinTransactionConfirmationsContext
+ ).bitcoinTransactionConfirmations.find(v => v[0] === vault.uuid)?.[1];
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/components/vaults-list/components/vaults-list-group-container.tsx b/src/app/components/vaults-list/components/vaults-list-group-container.tsx
index ae979fc2..ea4d1f60 100644
--- a/src/app/components/vaults-list/components/vaults-list-group-container.tsx
+++ b/src/app/components/vaults-list/components/vaults-list-group-container.tsx
@@ -1,11 +1,12 @@
import { Button, HStack, Image, Spinner, Text, VStack } from '@chakra-ui/react';
-import { VaultCard } from '@components/vault/vault-card';
+import { Vault } from '@components/vault/vault';
import { useAddToken } from '@hooks/use-add-token';
-import { Vault } from '@models/vault';
+import { Vault as VaultModel } from '@models/vault';
interface VaultsListGroupContainerProps {
label?: string;
- vaults: Vault[];
+ variant?: 'select';
+ vaults: VaultModel[];
selectedVaultUUID?: string;
isSelectable?: boolean;
handleSelect?: (uuid: string) => void;
@@ -13,10 +14,8 @@ interface VaultsListGroupContainerProps {
export function VaultsListGroupContainer({
label,
+ variant,
vaults,
- selectedVaultUUID,
- isSelectable = false,
- handleSelect,
}: VaultsListGroupContainerProps): React.JSX.Element | boolean {
const addToken = useAddToken();
@@ -49,13 +48,7 @@ export function VaultsListGroupContainer({
)}
{vaults.map((vault, index) => (
- handleSelect && handleSelect(vault.uuid)}
- />
+
))}
);
diff --git a/src/app/hooks/use-blockchain-height-query.ts b/src/app/hooks/use-blockchain-height-query.ts
index 1e8e925d..3d204876 100644
--- a/src/app/hooks/use-blockchain-height-query.ts
+++ b/src/app/hooks/use-blockchain-height-query.ts
@@ -4,9 +4,14 @@ export function useBlockchainHeightQuery(): number | undefined {
const bitcoinExplorerHeightURL = `${appConfiguration.bitcoinBlockchainURL}/blocks/tip/height`;
async function getBlockchainHeight() {
- const response = await fetch(bitcoinExplorerHeightURL);
- if (!response.ok) throw new Error('Network response was not ok');
- return response.json();
+ try {
+ const response = await fetch(bitcoinExplorerHeightURL);
+ if (!response.ok) throw new Error('Network response was not ok');
+ return response.json();
+ } catch (error) {
+ console.error('Error fetching blockchain height', error);
+ return undefined;
+ }
}
const { data: blockHeight } = useQuery({
diff --git a/src/app/hooks/use-confirmation-checker.ts b/src/app/hooks/use-confirmation-checker.ts
index 66100033..62054ee9 100644
--- a/src/app/hooks/use-confirmation-checker.ts
+++ b/src/app/hooks/use-confirmation-checker.ts
@@ -1,49 +1,62 @@
-import { useContext, useEffect, useState } from 'react';
+import { useContext } from 'react';
import { Vault } from '@models/vault';
-import { BlockchainHeightContext } from '@providers/bitcoin-query-provider';
+import { VaultContext } from '@providers/vault-context-provider';
import { useQuery } from '@tanstack/react-query';
-import { VaultState } from 'dlc-btc-lib/models';
-export function useConfirmationChecker(vault?: Vault): number {
- const { blockHeight } = useContext(BlockchainHeightContext);
+import { useBlockchainHeightQuery } from './use-blockchain-height-query';
- const [blockHeightAtBroadcast, setBlockHeightAtBroadcast] = useState(
- undefined
- );
- const [transactionProgress, setTransactionProgress] = useState(0);
+export function useConfirmationChecker(): [string, number][] {
+ const blockHeight = useBlockchainHeightQuery();
+ const { pendingVaults } = useContext(VaultContext);
- const bitcoinExplorerTXURL = `${appConfiguration.bitcoinBlockchainURL}/tx/${vault?.withdrawDepositTX}`;
+ async function fetchBitcoinTransactionBlockHeight(vault: Vault): Promise {
+ try {
+ const bitcoinExplorerTXURL = `${appConfiguration.bitcoinBlockchainURL}/tx/${vault?.withdrawDepositTX}`;
- async function fetchTransactionDetails() {
- const response = await fetch(bitcoinExplorerTXURL);
- if (!response.ok) throw new Error('Network response was not ok');
- const transactionDetails = await response.json();
- return transactionDetails.status.block_height;
- }
+ const bitcoinTransactionResponse = await fetch(bitcoinExplorerTXURL);
+ const bitcoinTransaction = await bitcoinTransactionResponse.json();
+ const bitcoinTransactionBlockHeight: number = bitcoinTransaction.status.block_height;
- const { data: txBlockHeightAtBroadcast } = useQuery({
- queryKey: ['transactionDetails', vault?.withdrawDepositTX],
- queryFn: () => fetchTransactionDetails(),
- enabled:
- !!vault?.withdrawDepositTX && vault?.state === VaultState.PENDING && !blockHeightAtBroadcast,
- refetchInterval: 10000,
- });
+ if (!bitcoinTransactionBlockHeight)
+ throw new Error('Could not fetch Bitcoin Transaction Block Height');
- useEffect(() => {
- if (txBlockHeightAtBroadcast && typeof txBlockHeightAtBroadcast === 'number') {
- setBlockHeightAtBroadcast(txBlockHeightAtBroadcast);
+ return bitcoinTransactionBlockHeight;
+ } catch (error) {
+ throw new Error('Error fetching Bitcoin Transaction Block Height');
}
- }, [txBlockHeightAtBroadcast]);
+ }
- useEffect(() => {
- if (vault?.state != VaultState.PENDING || transactionProgress > 6) return;
+ async function fetchBitcoinTransactionConfirmations(
+ vault: Vault,
+ blockHeight: number
+ ): Promise {
+ try {
+ const bitcoinTransactionBlockHeight = await fetchBitcoinTransactionBlockHeight(vault);
- const blockHeightDifference = (blockHeight as number) + 1 - (blockHeightAtBroadcast as number);
- if (typeof blockHeightDifference === 'number' && blockHeightDifference >= 0) {
- setTransactionProgress(blockHeightDifference);
+ return blockHeight - bitcoinTransactionBlockHeight;
+ } catch (error) {
+ return 0;
}
- }, [blockHeightAtBroadcast, blockHeight, vault?.state, transactionProgress]);
+ }
+
+ async function fetchAllBitcoinTransactionConfirmations(): Promise<[string, number][]> {
+ if (!blockHeight) throw new Error('Block Height is not available');
+ return await Promise.all(
+ pendingVaults.map(async vault => {
+ const confirmations = await fetchBitcoinTransactionConfirmations(vault, blockHeight + 1);
+ return [vault.uuid, confirmations] as [string, number];
+ })
+ );
+ }
+
+ const { data: bitcoinTransactionConfirmations } = useQuery({
+ queryKey: ['transactionDetails'],
+ initialData: [],
+ queryFn: () => fetchAllBitcoinTransactionConfirmations(),
+ enabled: pendingVaults.length > 0,
+ refetchInterval: 10000,
+ });
- return transactionProgress;
+ return bitcoinTransactionConfirmations;
}
diff --git a/src/app/providers/bitcoin-query-provider.tsx b/src/app/providers/bitcoin-query-provider.tsx
index 97a56fa4..06b1164c 100644
--- a/src/app/providers/bitcoin-query-provider.tsx
+++ b/src/app/providers/bitcoin-query-provider.tsx
@@ -1,22 +1,25 @@
import { createContext } from 'react';
-import { useBlockchainHeightQuery } from '@hooks/use-blockchain-height-query';
+import { useConfirmationChecker } from '@hooks/use-confirmation-checker';
import { HasChildren } from '@models/has-children';
-interface BlockchainHeightContextProviderType {
- blockHeight: number | undefined;
+interface BitcoinTransactionConfirmationsProviderType {
+ bitcoinTransactionConfirmations: [string, number][];
}
-export const BlockchainHeightContext = createContext({
- blockHeight: undefined,
-});
+export const BitcoinTransactionConfirmationsContext =
+ createContext({
+ bitcoinTransactionConfirmations: [],
+ });
-export function BlockchainHeightContextProvider({ children }: HasChildren): React.JSX.Element {
- const blockHeight = useBlockchainHeightQuery();
+export function BitcoinTransactionConfirmationsProvider({
+ children,
+}: HasChildren): React.JSX.Element {
+ const bitcoinTransactionConfirmations = useConfirmationChecker();
return (
-
+
{children}
-
+
);
}
diff --git a/src/shared/constants/bitcoin.constants.ts b/src/shared/constants/bitcoin.constants.ts
index 740a83b3..87baea6e 100644
--- a/src/shared/constants/bitcoin.constants.ts
+++ b/src/shared/constants/bitcoin.constants.ts
@@ -5,3 +5,5 @@ export const BITCOIN_NETWORK_MAP = {
testnet: testnet,
regtest: regtest,
};
+
+export const BITCOIN_BLOCK_CONFIRMATIONS = 6;
diff --git a/src/styles/app-theme.ts b/src/styles/app-theme.ts
index fa0e69fb..645f493c 100644
--- a/src/styles/app-theme.ts
+++ b/src/styles/app-theme.ts
@@ -23,10 +23,10 @@ export const appTheme = extendTheme({
Progress: {
baseStyle: {
track: {
- bg: 'white.03',
+ bg: 'white.04',
},
filledTrack: {
- bg: 'border.lightBlue.01',
+ bg: 'orange.01',
},
},
},
@@ -43,15 +43,19 @@ export const appTheme = extendTheme({
}),
},
colors: {
+ 'purple.01': 'rgba(104, 24, 173, 1)',
+ 'orange.01': 'rgba(255, 168, 0, 1)',
'background.website.01': 'rgba(0, 0, 0, 1)',
'background.container.01': 'rgba(18, 18, 18, 1)',
'background.content.01': 'rgba(51, 51, 51, 1)',
+ 'grey.01': 'rgba(181, 182, 187, 1)',
'border.lightBlue.01': 'rgba(50, 201, 247,0.75)',
'border.white.01': 'rgba(255,255,255,0.25)',
'accent.lightBlue.01': 'rgba(50, 201, 247, 1)',
'white.01': 'rgba(255,255,255,1)',
'white.02': 'rgba(255,255,255,0.75)',
'white.03': 'rgba(255,255,255,0.35)',
+ 'white.04': 'rgba(255,255,255,0.10)',
'warning.01': 'rgba(255,204,85, 1)',
'error.01': 'rgba(255,51,102, 1)',
'table.background.green': 'rgba(50, 201, 247, 0.1)',