Skip to content

Commit 700e509

Browse files
feat: hook up pegin data to activity card (#302)
1 parent dbd3415 commit 700e509

File tree

10 files changed

+368
-20
lines changed

10 files changed

+368
-20
lines changed

routes/vault/README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ Vault application for managing BTC staking and ETH interactions under the `/vaul
66

77
```
88
routes/vault/src/
9-
├── VaultLayout.tsx
9+
├── VaultLayout.tsx # Main layout component with wallet connection
1010
├── index.ts
1111
├── clients/ # External API and service clients
1212
│ ├── eth-contract/ # ETH smart contract client for querying data and constructing transactions
13+
│ │ ├── btc-vaults-manager/ # BTCVaultsManager contract interactions
14+
│ │ ├── vault-controller/ # VaultController contract interactions
15+
│ │ ├── morpho/ # Morpho protocol interactions
1316
│ │ └── index.ts
1417
│ ├── morpho-graphql/ # GraphQL client for fetching market, position, and LLTV information from Morpho
1518
│ │ └── index.ts
@@ -18,9 +21,16 @@ routes/vault/src/
1821
│ └── vault-provider/ # RPC-based client for vault provider - querying and posting data
1922
│ └── index.ts
2023
├── components/ # React components
21-
│ └── ui/ # UI components for vault application
22-
│ └── index.ts
24+
│ ├── ui/ # UI components for vault application
25+
│ │ ├── Borrow.tsx # Main borrow interface with activity cards
26+
│ │ └── index.ts
27+
│ ├── modals/ # Modal components for user actions
28+
│ └── examples/ # Example/test components
29+
├── config/ # Configuration files
30+
│ ├── contracts.ts # Smart contract addresses
31+
│ └── index.ts
2332
├── hooks/ # Custom React hooks for vault operations
33+
│ ├── usePeginRequests.ts # Hook for fetching vault data from blockchain
2434
│ └── index.ts
2535
├── services/ # Business logic layer orchestrating clients and transactions
2636
│ └── index.ts
@@ -37,5 +47,6 @@ routes/vault/src/
3747
├── types/ # TypeScript type definitions for vault, morpho, and transaction data
3848
│ └── index.ts
3949
└── utils/ # Utility functions for formatting, validation, and helpers
50+
├── peginTransformers.ts # Transform blockchain data to UI format
4051
└── index.ts
41-
```
52+
```

routes/vault/src/VaultLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { Borrow } from './components/ui';
33
import { PegInTest } from './components/examples/PegInTest';
44

55
// TODO: Uncomment this when we have a way to test the contract queries
6-
// import ContractQueryExample from "./components/examples/ContractQueryExample";
6+
import ContractQueryExample from "./components/examples/ContractQueryExample";
77

88
export default function VaultLayout() {
99
// Initialize AppKit bridge for ETH wallet connection
1010
useAppKitBridge();
1111

1212
return (
1313
<div className="container mx-auto flex max-w-[760px] flex-1 flex-col gap-12 px-4 py-8">
14-
{/* <ContractQueryExample /> */}
14+
<ContractQueryExample />
1515
<PegInTest />
1616
<Borrow />
1717
</div>

routes/vault/src/components/examples/ContractQueryExample.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ export default function ContractQueryExample() {
3131
const connectedAddress = useMemo(() => {
3232
return (
3333
ethConnector?.connectedWallet?.account?.address ||
34-
ethConnector?.connectedWallet?.accounts?.[0]?.address ||
35-
// @ts-expect-error - trying alternative path
36-
ethConnector?.address
34+
(ethConnector as any)?.connectedWallet?.accounts?.[0]?.address || // only this works now
35+
(ethConnector as any)?.address
3736
) as Hex | undefined;
3837
}, [ethConnector]);
3938

routes/vault/src/components/ui/Borrow.tsx

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,24 @@ import {
66
type ActivityCardDetailItem,
77
ProviderItem,
88
} from "@babylonlabs-io/core-ui";
9-
import { useState } from "react";
10-
import { mockVaultActivities, type VaultActivity } from "../../mockData/vaultActivities";
9+
import { useState, useMemo, useCallback } from "react";
10+
import { useChainConnector } from "@babylonlabs-io/wallet-connector";
11+
import type { VaultActivity } from "../../mockData/vaultActivities";
1112
import { BorrowModal, BorrowSignModal, BorrowSuccessModal } from "../modals";
13+
import { usePeginRequests } from "../../hooks/usePeginRequests";
14+
import type { Hex } from "viem";
1215

1316
export function Borrow() {
14-
const [activities] = useState<VaultActivity[]>(mockVaultActivities);
17+
const ethConnector = useChainConnector('ETH');
18+
19+
const connectedAddress = useMemo(() => {
20+
const address = (
21+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
22+
(ethConnector as any)?.connectedWallet?.account?.address ||
23+
(ethConnector as any)?.connectedWallet?.accounts?.[0]?.address
24+
) as Hex | undefined;
25+
return address;
26+
}, [ethConnector]);
1527

1628
// Modal states
1729
const [modalOpen, setModalOpen] = useState(false);
@@ -22,19 +34,24 @@ export function Borrow() {
2234
const [selectedActivity, setSelectedActivity] = useState<VaultActivity | null>(null);
2335
const [borrowAmount, setBorrowAmount] = useState(0);
2436

37+
const handleActivityBorrow = useCallback((activity: VaultActivity) => {
38+
setSelectedActivity(activity);
39+
setModalOpen(true);
40+
}, []);
41+
42+
// Fetch pegin requests from blockchain
43+
const { activities } = usePeginRequests(
44+
connectedAddress,
45+
handleActivityBorrow
46+
);
47+
2548
const handleNewBorrow = () => {
2649
if (activities.length > 0) {
27-
// TODO: getSelectedActivity method should be implemented
2850
setSelectedActivity(activities[0]);
2951
setModalOpen(true);
3052
}
3153
};
3254

33-
const handleActivityBorrow = (activity: VaultActivity) => {
34-
setSelectedActivity(activity);
35-
setModalOpen(true);
36-
};
37-
3855
// Handle modal close
3956
const handleModalClose = () => {
4057
setModalOpen(false);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Smart Contract Addresses Configuration
3+
*
4+
* These addresses are for the local Anvil testnet.
5+
* TODO: Add environment-based configuration for mainnet/testnet
6+
*/
7+
8+
import type { Address } from 'viem';
9+
10+
export const CONTRACTS = {
11+
/**
12+
* BTCVaultsManager contract - Manages vault providers and pegin requests
13+
*/
14+
BTC_VAULTS_MANAGER: '0x0165878A594ca255338adfa4d48449f69242Eb8F' as Address,
15+
16+
/**
17+
* VaultController contract - Controls vault operations and borrowing
18+
*/
19+
VAULT_CONTROLLER: '0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6' as Address,
20+
21+
/**
22+
* Morpho lending protocol contract
23+
*/
24+
MORPHO: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' as Address,
25+
26+
/**
27+
* BTCVault base contract
28+
*/
29+
BTC_VAULT: '0x5FC8d32690cc91D4c39d9d3abcBD16989F875707' as Address,
30+
} as const;
31+
32+
/**
33+
* Morpho Market ID for USDC lending market
34+
*/
35+
export const MORPHO_MARKET_ID = '74452254177513794647796445278347016294878377877693199253750000625994101441252' as const;

routes/vault/src/config/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Configuration exports
3+
*/
4+
5+
export { CONTRACTS, MORPHO_MARKET_ID } from './contracts';

routes/vault/src/hooks/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
// Custom React hooks for vault operations
1+
/**
2+
* Custom React hooks for vault operations
3+
*/
4+
5+
export { usePeginRequests } from './usePeginRequests';
6+
export type { UsePeginRequestsResult } from './usePeginRequests';
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* Fetching and managing pegin request data from smart contracts
3+
*/
4+
5+
import { useState, useEffect, useCallback } from 'react';
6+
import type { Address, Hex } from 'viem';
7+
import { BTCVaultsManager } from '../clients/eth-contract';
8+
import type { PeginRequest } from '../clients/eth-contract';
9+
import { transformPeginToActivity } from '../utils/peginTransformers';
10+
import type { VaultActivity } from '../mockData/vaultActivities';
11+
import { CONTRACTS } from '../config/contracts';
12+
13+
/**
14+
* Result interface for usePeginRequests hook
15+
*/
16+
export interface UsePeginRequestsResult {
17+
/** Array of vault activities transformed from pegin requests */
18+
activities: VaultActivity[];
19+
/** Loading state - true while fetching data */
20+
loading: boolean;
21+
/** Error state - contains error if fetch failed */
22+
error: Error | null;
23+
/** Function to manually refetch data */
24+
refetch: () => Promise<void>;
25+
}
26+
27+
/**
28+
* Custom hook to fetch pegin requests for a connected wallet address
29+
*
30+
* Workflow:
31+
* 1. Calls getDepositorPeginRequests(address) to get array of transaction hashes
32+
* 2. For each hash, calls getPeginRequest(hash) to get detailed pegin data
33+
* 3. Transforms each PeginRequest to VaultActivity format for UI
34+
* 4. Returns activities array with loading and error states
35+
*
36+
* @param connectedAddress - Ethereum address of connected wallet (undefined if not connected)
37+
* @param onBorrowClick - Callback function when user clicks borrow action
38+
* @returns Object containing activities array, loading state, error state, and refetch function
39+
*
40+
* @example
41+
* ```tsx
42+
* function MyComponent() {
43+
* const { address } = useETHWallet();
44+
* const { activities, loading, error, refetch } = usePeginRequests(address, handleBorrow);
45+
*
46+
* if (loading) return <Spinner />;
47+
* if (error) return <Error message={error.message} onRetry={refetch} />;
48+
* return <ActivityList activities={activities} />;
49+
* }
50+
* ```
51+
*/
52+
export function usePeginRequests(
53+
connectedAddress: Address | undefined,
54+
onBorrowClick: (activity: VaultActivity) => void
55+
): UsePeginRequestsResult {
56+
const [activities, setActivities] = useState<VaultActivity[]>([]);
57+
const [loading, setLoading] = useState<boolean>(false);
58+
const [error, setError] = useState<Error | null>(null);
59+
60+
/**
61+
* Fetch pegin requests from contract and transform to activities
62+
*/
63+
const fetchPeginRequests = useCallback(async () => {
64+
// Early return if no wallet connected
65+
if (!connectedAddress) {
66+
setActivities([]);
67+
setLoading(false);
68+
setError(null);
69+
return;
70+
}
71+
72+
try {
73+
setLoading(true);
74+
setError(null);
75+
76+
// Step 1: Get all transaction hashes for this depositor
77+
const txHashes: Hex[] = await BTCVaultsManager.getDepositorPeginRequests(
78+
CONTRACTS.BTC_VAULTS_MANAGER,
79+
connectedAddress
80+
);
81+
82+
// Early return if no vaults found
83+
if (txHashes.length === 0) {
84+
setActivities([]);
85+
setLoading(false);
86+
return;
87+
}
88+
89+
// Step 2: Fetch detailed pegin request data for each transaction hash
90+
// Use Promise.all for parallel fetching to improve performance
91+
const peginRequests: Array<{ peginRequest: PeginRequest; txHash: Hex }> = await Promise.all(
92+
txHashes.map(async (txHash) => {
93+
const peginRequest = await BTCVaultsManager.getPeginRequest(
94+
CONTRACTS.BTC_VAULTS_MANAGER,
95+
txHash
96+
);
97+
return { peginRequest, txHash };
98+
})
99+
);
100+
101+
// Step 3: Transform pegin requests to vault activities
102+
const transformedActivities = peginRequests.map(({ peginRequest, txHash }) =>
103+
transformPeginToActivity(peginRequest, txHash, onBorrowClick)
104+
);
105+
106+
setActivities(transformedActivities);
107+
setLoading(false);
108+
} catch (err) {
109+
const errorMessage = err instanceof Error ? err : new Error('Failed to fetch pegin requests');
110+
setError(errorMessage);
111+
setActivities([]);
112+
setLoading(false);
113+
}
114+
}, [connectedAddress, onBorrowClick]);
115+
116+
/**
117+
* Manual refetch function exposed to component
118+
*/
119+
const refetch = useCallback(async () => {
120+
await fetchPeginRequests();
121+
}, [fetchPeginRequests]);
122+
123+
/**
124+
* Auto-fetch on wallet connection or address change
125+
*/
126+
useEffect(() => {
127+
fetchPeginRequests();
128+
}, [fetchPeginRequests]);
129+
130+
return {
131+
activities,
132+
loading,
133+
error,
134+
refetch,
135+
};
136+
}

routes/vault/src/utils/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,11 @@
1-
// Utility functions for formatting, validation, and helpers
1+
/**
2+
* Utility functions for data transformation and formatting
3+
*/
4+
5+
export {
6+
formatBTCAmount,
7+
getStatusInfo,
8+
formatProviderName,
9+
transformPeginToActivity,
10+
transformPeginRequestsToActivities,
11+
} from './peginTransformers';

0 commit comments

Comments
 (0)