Skip to content

Commit f31c4c3

Browse files
feat(packages): show existing vaults in the overview list (#541)
1 parent 2eabad0 commit f31c4c3

File tree

14 files changed

+359
-38
lines changed

14 files changed

+359
-38
lines changed

services/vault/src/components/DepositOverview.tsx renamed to services/vault/src/components/Collateral/Deposit/components/DepositOverview.tsx

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,30 @@ import {
99
VaultDetailCard,
1010
type ColumnProps,
1111
} from "@babylonlabs-io/core-ui";
12-
import { useState } from "react";
12+
import { useWalletConnect } from "@babylonlabs-io/wallet-connector";
13+
import { useMemo, useState } from "react";
1314

14-
import { useBTCWallet, useETHWallet } from "../context/wallet";
15+
import { useBTCWallet, useETHWallet } from "../../../../context/wallet";
16+
import { getPeginState } from "../../../../models/peginStateMachine";
17+
import type { VaultActivity } from "../../../../types/activity";
18+
import type { Deposit } from "../../../../types/vault";
19+
import {
20+
useVaultRedeemState,
21+
VaultRedeemStep,
22+
} from "../../Redeem/state/VaultRedeemState";
23+
import { useVaultDeposits } from "../hooks/useVaultDeposits";
1524
import {
1625
useVaultDepositState,
1726
VaultDepositStep,
1827
} from "../state/VaultDepositState";
19-
import {
20-
useVaultRedeemState,
21-
VaultRedeemStep,
22-
} from "../state/VaultRedeemState";
23-
import type { Deposit } from "../types/vault";
2428

25-
// Hardcoded deposit data
26-
// TODO: Replace with useVaultPositions when wallet providers are integrated
27-
const HARDCODED_DEPOSITS: Deposit[] = [];
28-
29-
function EmptyState({ onDeposit }: { onDeposit: () => void }) {
29+
function EmptyState({
30+
onDeposit,
31+
isConnected,
32+
}: {
33+
onDeposit: () => void;
34+
isConnected: boolean;
35+
}) {
3036
return (
3137
<div className="max-h-[500px] overflow-x-auto overflow-y-auto bg-primary-contrast">
3238
<div className="flex min-h-[200px] items-center justify-center p-6">
@@ -37,7 +43,9 @@ function EmptyState({ onDeposit }: { onDeposit: () => void }) {
3743
Deposit BTC Trustlessly
3844
</h4>
3945
<p className="text-sm text-accent-secondary">
40-
Your deposit will appear here once confirmed.
46+
{isConnected
47+
? "Your deposit will appear here once confirmed."
48+
: "Connect your wallet to start depositing BTC."}
4149
</p>
4250
</div>
4351
<div className="mt-6">
@@ -46,9 +54,9 @@ function EmptyState({ onDeposit }: { onDeposit: () => void }) {
4654
size="medium"
4755
rounded
4856
onClick={onDeposit}
49-
aria-label="Add deposit"
57+
aria-label={isConnected ? "Add deposit" : "Connect wallet"}
5058
>
51-
Deposit
59+
{isConnected ? "Deposit" : "Connect Wallet"}
5260
</Button>
5361
</div>
5462
</div>
@@ -60,18 +68,47 @@ function EmptyState({ onDeposit }: { onDeposit: () => void }) {
6068
export function DepositOverview() {
6169
const isMobile = useIsMobile();
6270
const { connected: btcConnected } = useBTCWallet();
63-
const { connected: ethConnected } = useETHWallet();
71+
const { connected: ethConnected, address: ethAddress } = useETHWallet();
6472
const isConnected = btcConnected && ethConnected;
73+
const { open: openWalletModal } = useWalletConnect();
74+
75+
// Fetch real deposit data
76+
const { activities } = useVaultDeposits(
77+
ethAddress as `0x${string}` | undefined,
78+
);
79+
80+
// Transform VaultActivity to Deposit format
81+
const deposits: Deposit[] = useMemo(() => {
82+
return activities.map((activity: VaultActivity) => {
83+
// Get state from state machine
84+
const state = getPeginState(activity.contractStatus ?? 0);
85+
86+
return {
87+
id: activity.id,
88+
amount: parseFloat(activity.collateral.amount),
89+
vaultProvider: {
90+
name: activity.providers[0]?.name || "Unknown Provider",
91+
icon: activity.providers[0]?.icon || "",
92+
},
93+
status: state.displayLabel as "Available" | "Pending" | "In Use",
94+
};
95+
});
96+
}, [activities]);
6597

66-
const deposits = HARDCODED_DEPOSITS;
6798
const [selectedDepositIds, setSelectedDepositIds] = useState<
6899
Array<string | number>
69100
>([]);
70101
const { goToStep: goToDepositStep } = useVaultDepositState();
71102
const { goToStep: goToRedeemStep, setRedeemData } = useVaultRedeemState();
72103

73104
const handleDeposit = () => {
74-
goToDepositStep(VaultDepositStep.FORM);
105+
if (!isConnected) {
106+
// Open wallet connection modal
107+
openWalletModal();
108+
} else {
109+
// Already connected, open deposit modal directly
110+
goToDepositStep(VaultDepositStep.FORM);
111+
}
75112
};
76113

77114
const handleRedeem = () => {
@@ -83,7 +120,7 @@ export function DepositOverview() {
83120

84121
// Show empty state when not connected OR when connected but no data
85122
if (!isConnected || deposits.length === 0) {
86-
return <EmptyState onDeposit={handleDeposit} />;
123+
return <EmptyState onDeposit={handleDeposit} isConnected={isConnected} />;
87124
}
88125

89126
const columns: ColumnProps<Deposit>[] = [
@@ -143,9 +180,9 @@ export function DepositOverview() {
143180
size="medium"
144181
rounded
145182
onClick={handleDeposit}
146-
aria-label="Deposit BTC"
183+
aria-label={isConnected ? "Deposit BTC" : "Connect wallet to deposit"}
147184
>
148-
Deposit
185+
{isConnected ? "Deposit" : "Connect Wallet"}
149186
</Button>
150187
<Button
151188
variant="outlined"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Fetching and managing pegin request data from smart contracts
3+
* Used in VaultDeposit tab to show deposit/collateral status only (no Morpho data)
4+
*/
5+
6+
import { useQuery } from "@tanstack/react-query";
7+
import { useEffect, useMemo } from "react";
8+
import type { Address } from "viem";
9+
10+
import { CONTRACTS } from "../../../../config/contracts";
11+
import { getPeginRequestsWithDetails } from "../../../../services/vault/vaultQueryService";
12+
import type { VaultActivity } from "../../../../types";
13+
import { transformPeginToActivity } from "../../../../utils/peginTransformers";
14+
15+
/**
16+
* Result interface for usePeginRequests hook
17+
*/
18+
export interface UsePeginRequestsResult {
19+
/** Array of vault activities transformed from pegin requests */
20+
activities: VaultActivity[];
21+
/** Loading state - true while fetching data */
22+
loading: boolean;
23+
/** Error state - contains error if fetch failed */
24+
error: Error | null;
25+
/** Function to manually refetch data */
26+
refetch: () => Promise<void>;
27+
}
28+
29+
/**
30+
* Parameters for usePeginRequests hook
31+
*/
32+
export interface UsePeginRequestsParams {
33+
/** Ethereum address of connected wallet (undefined if not connected) */
34+
connectedAddress: Address | undefined;
35+
}
36+
37+
/**
38+
* Custom hook to fetch pegin requests for a connected wallet address
39+
*
40+
* Fetches pegin/deposit data. The "in use" status is determined from the pegin status itself (status 3 = InPosition).
41+
* Does NOT fetch full Morpho position details (for performance).
42+
* For full position data with Morpho details, use useUserPositions instead.
43+
*
44+
* @param params - Hook parameters
45+
* @returns Object containing activities array, loading state, error state, and refetch function
46+
*/
47+
export function usePeginRequests({
48+
connectedAddress,
49+
}: UsePeginRequestsParams): UsePeginRequestsResult {
50+
// Use React Query to fetch data from service layer
51+
const { data, isLoading, error, refetch } = useQuery({
52+
queryKey: ["peginRequests", connectedAddress, CONTRACTS.BTC_VAULTS_MANAGER],
53+
queryFn: () => {
54+
return getPeginRequestsWithDetails(
55+
connectedAddress!,
56+
CONTRACTS.BTC_VAULTS_MANAGER,
57+
);
58+
},
59+
enabled: !!connectedAddress,
60+
// Refetch when wallet connects to ensure fresh data
61+
refetchOnMount: true,
62+
// Poll every 30 seconds to track peg-in status updates
63+
refetchInterval: 30000,
64+
});
65+
66+
// Trigger refetch when wallet connects (address changes from undefined to a value)
67+
useEffect(() => {
68+
if (connectedAddress) {
69+
refetch();
70+
}
71+
}, [connectedAddress, refetch]);
72+
73+
// Transform pegin requests to vault activities
74+
const activities = useMemo(() => {
75+
if (!data) return [];
76+
77+
const transformed = data.map(({ peginRequest, txHash }) =>
78+
transformPeginToActivity(peginRequest, txHash),
79+
);
80+
return transformed;
81+
}, [data]);
82+
83+
// Wrap refetch to return Promise<void> for backward compatibility
84+
const wrappedRefetch = async () => {
85+
await refetch();
86+
};
87+
88+
return {
89+
activities,
90+
loading: isLoading,
91+
error: error as Error | null,
92+
refetch: wrappedRefetch,
93+
};
94+
}

services/vault/src/hooks/useVaultPositions.ts renamed to services/vault/src/components/Collateral/Deposit/hooks/useVaultDeposits.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { usePeginStorage } from "../storage/usePeginStorage";
55
import { usePeginRequests } from "./usePeginRequests";
66

77
/**
8-
* Hook to manage vault positions data fetching
8+
* Hook to manage vault deposits data fetching
99
* Only responsible for data - UI modal states and action handlers are managed by parent components
1010
* Wallet connections are managed by parent components
1111
*/
12-
export function useVaultPositions(connectedAddress: Hex | undefined) {
12+
export function useVaultDeposits(connectedAddress: Hex | undefined) {
1313
const { activities: confirmedActivities, refetch } = usePeginRequests({
1414
connectedAddress,
1515
});

services/vault/src/state/VaultDepositState.tsx renamed to services/vault/src/components/Collateral/Deposit/state/VaultDepositState.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useCallback, useMemo, useState, type PropsWithChildren } from "react";
22

3-
import { createStateUtils } from "../utils/createStateUtils";
3+
import { createStateUtils } from "../../../../utils/createStateUtils";
44

55
export enum VaultDepositStep {
66
IDLE = "idle",

0 commit comments

Comments
 (0)