Skip to content

Commit f3d2ee5

Browse files
feat: hook the UI with mint and borrow (#312)
1 parent b206df8 commit f3d2ee5

File tree

8 files changed

+263
-62
lines changed

8 files changed

+263
-62
lines changed

routes/vault/src/components/Activities/Activities.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function Activities() {
1818
handleActivityBorrow,
1919
handleBorrowFlowClose,
2020
isWalletConnected,
21+
refetchActivities,
2122
} = useActivitiesState();
2223

2324
console.log('[Activities] activities:', activities);
@@ -120,6 +121,7 @@ export function Activities() {
120121
activity={selectedActivity}
121122
isOpen={borrowFlowOpen}
122123
onClose={handleBorrowFlowClose}
124+
onBorrowSuccess={refetchActivities}
123125
/>
124126
</>
125127
);

routes/vault/src/components/Activities/useActivitiesState.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function useActivitiesState() {
2828
}, []);
2929

3030
// Fetch pegin requests from blockchain
31-
const { activities } = usePeginRequests(
31+
const { activities, refetch } = usePeginRequests(
3232
connectedAddress,
3333
handleActivityBorrow
3434
);
@@ -55,5 +55,6 @@ export function useActivitiesState() {
5555
handleActivityBorrow,
5656
handleBorrowFlowClose,
5757
isWalletConnected: !!connectedAddress,
58+
refetchActivities: refetch,
5859
};
5960
}

routes/vault/src/components/BorrowFlow/BorrowFlow.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ interface BorrowFlowProps {
77
activity: VaultActivity | null;
88
isOpen: boolean;
99
onClose: () => void;
10+
onBorrowSuccess?: () => void;
1011
}
1112

12-
export function BorrowFlow({ activity, isOpen, onClose }: BorrowFlowProps) {
13+
export function BorrowFlow({ activity, isOpen, onClose, onBorrowSuccess }: BorrowFlowProps) {
1314
const {
1415
modalOpen,
1516
signModalOpen,
@@ -35,8 +36,14 @@ export function BorrowFlow({ activity, isOpen, onClose }: BorrowFlowProps) {
3536
onClose();
3637
};
3738

38-
const handleFinalSuccess = () => {
39+
const handleFinalSuccess = async () => {
3940
handleSuccessClose();
41+
42+
// Refetch activities to show updated vault data before closing
43+
if (onBorrowSuccess) {
44+
await onBorrowSuccess();
45+
}
46+
4047
onClose();
4148
};
4249

@@ -51,6 +58,7 @@ export function BorrowFlow({ activity, isOpen, onClose }: BorrowFlowProps) {
5158
onBorrow={handleBorrowClick}
5259
collateral={activity.collateral}
5360
marketData={activity.marketData}
61+
pegInTxHash={activity.txHash}
5462
/>
5563

5664
{/* Borrow Sign Modal */}
@@ -60,6 +68,7 @@ export function BorrowFlow({ activity, isOpen, onClose }: BorrowFlowProps) {
6068
onSuccess={handleSignSuccess}
6169
borrowAmount={borrowAmount}
6270
collateralAmount={activity.collateral.amount}
71+
pegInTxHash={activity.txHash}
6372
/>
6473

6574
{/* Borrow Success Modal */}

routes/vault/src/components/modals/BorrowModal/BorrowModal.tsx

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import {
1010
AmountItem,
1111
SubSection,
1212
} from "@babylonlabs-io/core-ui";
13-
import { useMemo, type ReactNode } from "react";
13+
import { useMemo, useEffect, useState, type ReactNode } from "react";
1414
import { twMerge } from "tailwind-merge";
1515
import { useBorrowForm } from "./useBorrowForm";
1616
import { usdcIcon } from "../../../assets";
17+
import type { Hex } from "viem";
18+
import { isPeginReadyToMint } from "../../../clients/eth-contract/vault-controller/query";
19+
import { CONTRACTS } from "../../../config/contracts";
1720

1821
type DialogComponentProps = Parameters<typeof Dialog>[0];
1922

@@ -43,9 +46,14 @@ interface BorrowModalProps {
4346
btcPriceUSD: number;
4447
lltvPercent: number;
4548
};
49+
/** Pegin transaction hash to check if ready to mint */
50+
pegInTxHash?: Hex;
4651
}
4752

48-
export function BorrowModal({ open, onClose, onBorrow, collateral, marketData }: BorrowModalProps) {
53+
export function BorrowModal({ open, onClose, onBorrow, collateral, marketData, pegInTxHash }: BorrowModalProps) {
54+
const [isReadyToMint, setIsReadyToMint] = useState<boolean | null>(null);
55+
const [checkingReadiness, setCheckingReadiness] = useState(false);
56+
4957
const collateralBTC = useMemo(
5058
() => parseFloat(collateral.amount || "0"),
5159
[collateral.amount]
@@ -58,6 +66,31 @@ export function BorrowModal({ open, onClose, onBorrow, collateral, marketData }:
5866
return "";
5967
}, [collateral.icon]);
6068

69+
// Check if pegin is ready to mint when modal opens
70+
useEffect(() => {
71+
const checkPeginReadiness = async () => {
72+
if (!open || !pegInTxHash) {
73+
setIsReadyToMint(null);
74+
return;
75+
}
76+
77+
console.log('[BorrowModal] Checking pegin readiness for:', pegInTxHash);
78+
setCheckingReadiness(true);
79+
try {
80+
const ready = await isPeginReadyToMint(CONTRACTS.VAULT_CONTROLLER, pegInTxHash);
81+
console.log('[BorrowModal] Pegin readiness result:', ready);
82+
setIsReadyToMint(ready);
83+
} catch (error) {
84+
console.error('[BorrowModal] Failed to check pegin readiness:', error);
85+
setIsReadyToMint(false);
86+
} finally {
87+
setCheckingReadiness(false);
88+
}
89+
};
90+
91+
checkPeginReadiness();
92+
}, [open, pegInTxHash]);
93+
6194
const {
6295
borrowAmount,
6396
borrowAmountNum,
@@ -230,14 +263,33 @@ export function BorrowModal({ open, onClose, onBorrow, collateral, marketData }:
230263
</div>
231264
</DialogBody>
232265
<DialogFooter className="flex flex-col gap-4 pb-8 pt-0">
266+
{/* Warning message if pegin is not ready */}
267+
{isReadyToMint === false && (
268+
<Text variant="body2" className="text-warning-main text-sm text-center">
269+
Vault is not ready to borrow. Please wait for the vault provider to verify your BTC deposit.
270+
</Text>
271+
)}
272+
233273
<Button
234274
variant="contained"
235275
color="primary"
236276
onClick={handleBorrowClick}
237277
className="w-full"
238-
disabled={!validation.isValid || borrowAmountNum === 0 || processing}
278+
disabled={
279+
!validation.isValid ||
280+
borrowAmountNum === 0 ||
281+
processing ||
282+
checkingReadiness ||
283+
isReadyToMint === false
284+
}
239285
>
240-
{processing ? "Processing..." : "Borrow"}
286+
{checkingReadiness
287+
? "Checking..."
288+
: processing
289+
? "Processing..."
290+
: isReadyToMint === false
291+
? "Not Ready to Borrow"
292+
: "Borrow"}
241293
</Button>
242294
</DialogFooter>
243295
</ResponsiveDialog>

routes/vault/src/components/modals/BorrowSignModal.tsx

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,68 +9,86 @@ import {
99
Text,
1010
} from "@babylonlabs-io/core-ui";
1111
import { useEffect, useState } from "react";
12+
import type { Hex } from "viem";
13+
import { useMintAndBorrow } from "../../hooks/useMintAndBorrow";
1214

1315
interface BorrowSignModalProps {
1416
open: boolean;
1517
onClose: () => void;
1618
onSuccess: () => void;
1719
borrowAmount?: number;
1820
collateralAmount?: string;
21+
/** Pegin transaction hash (vault ID) */
22+
pegInTxHash?: Hex;
1923
}
2024

2125
/**
22-
* BorrowSignModal - Multi-step signing modal for borrow flow
23-
*
24-
* Shows 3 steps that auto-progress with 2-second delays:
25-
* 1. Mint vaultBTC
26-
* 2. Borrow Transaction
27-
* 3. Confirming on Ethereum
28-
*
29-
* After all steps complete, triggers onSuccess callback to show success modal.
30-
*
31-
* Note: This is hardcoded for UI demonstration only.
32-
* Real implementation would integrate with wallet signing.
26+
* BorrowSignModal - Transaction signing modal for borrow flow
27+
*
28+
* The mintAndBorrow transaction atomically:
29+
* 1. Mints vaultBTC from the pegin
30+
* 2. Deposits vaultBTC as collateral to Morpho
31+
* 3. Borrows USDC against the collateral
32+
*
33+
* All three steps happen in a single Ethereum transaction.
3334
*/
3435
export function BorrowSignModal({
3536
open,
3637
onClose,
3738
onSuccess,
39+
borrowAmount,
40+
pegInTxHash,
3841
}: BorrowSignModalProps) {
39-
const [currentStep, setCurrentStep] = useState(1);
40-
const [processing, setProcessing] = useState(false);
41-
42-
// Auto-progress through steps with 2-second delays (hardcoded for UI demo)
43-
useEffect(() => {
44-
if (!open || currentStep > 3) return;
45-
46-
setProcessing(true);
47-
48-
const timer = setTimeout(() => {
49-
if (currentStep === 3) {
50-
// Last step complete, trigger success modal
51-
setProcessing(false);
52-
onSuccess();
53-
} else {
54-
// Move to next step
55-
setCurrentStep((prev) => prev + 1);
56-
}
57-
}, 2000); // 2 second delay per step
58-
59-
return () => clearTimeout(timer);
60-
}, [open, currentStep, onSuccess]);
42+
const [transactionStarted, setTransactionStarted] = useState(false);
43+
const { executeMintAndBorrow, isLoading, error } = useMintAndBorrow();
6144

6245
// Reset state when modal closes
6346
useEffect(() => {
6447
if (!open) {
65-
setCurrentStep(1);
66-
setProcessing(false);
48+
setTransactionStarted(false);
6749
}
6850
}, [open]);
6951

70-
const handleSign = () => {
71-
// In real implementation, this would trigger wallet signing
72-
// For now, auto-progression handles everything
73-
setProcessing(true);
52+
// Show error in console if transaction fails
53+
useEffect(() => {
54+
if (error) {
55+
console.error('[BorrowSignModal] Transaction error:', error);
56+
// TODO: Show error in UI
57+
}
58+
}, [error]);
59+
60+
const handleSign = async () => {
61+
if (!pegInTxHash || !borrowAmount) {
62+
console.error('[BorrowSignModal] Missing required data:', { pegInTxHash, borrowAmount });
63+
return;
64+
}
65+
66+
// Convert USDC amount to bigint with 6 decimals
67+
const borrowAmountBigInt = BigInt(Math.floor(borrowAmount * 1_000_000));
68+
69+
console.log('[BorrowSignModal] Starting transaction:', {
70+
pegInTxHash,
71+
borrowAmount,
72+
borrowAmountBigInt: borrowAmountBigInt.toString(),
73+
});
74+
75+
setTransactionStarted(true);
76+
77+
try {
78+
const result = await executeMintAndBorrow({
79+
pegInTxHash,
80+
borrowAmount: borrowAmountBigInt,
81+
});
82+
83+
if (result) {
84+
// Success - trigger success modal
85+
onSuccess();
86+
}
87+
} catch (err) {
88+
console.error('[BorrowSignModal] Transaction failed:', err);
89+
setTransactionStarted(false);
90+
// Keep modal open to show error
91+
}
7492
};
7593

7694
return (
@@ -83,20 +101,20 @@ export function BorrowSignModal({
83101

84102
<DialogBody className="flex flex-col gap-4 px-4 pb-8 pt-4 text-accent-primary sm:px-6">
85103
<Text variant="body1" className="text-sm text-accent-secondary sm:text-base">
86-
Follow the steps below. Your wallet will prompt you when action is needed.
104+
Sign the transaction in your wallet to mint vaultBTC and borrow USDC atomically.
87105
</Text>
88106

89107
<div className="flex flex-col items-start gap-4 py-4">
90-
<Step step={1} currentStep={currentStep}>
91-
Mint vaultBTC
92-
</Step>
93-
<Step step={2} currentStep={currentStep}>
94-
Borrow Transaction
95-
</Step>
96-
<Step step={3} currentStep={currentStep}>
97-
Confirming on Ethereum
108+
<Step step={1} currentStep={transactionStarted || isLoading ? 1 : 0}>
109+
Mint vaultBTC & Borrow USDC
98110
</Step>
99111
</div>
112+
113+
{error && (
114+
<Text variant="body2" className="text-error-main text-sm">
115+
{error}
116+
</Text>
117+
)}
100118
</DialogBody>
101119

102120
<DialogFooter className="flex gap-4">
@@ -105,17 +123,18 @@ export function BorrowSignModal({
105123
color="primary"
106124
onClick={onClose}
107125
className="flex-1 text-xs sm:text-base"
126+
disabled={isLoading}
108127
>
109128
Cancel
110129
</Button>
111130

112131
<Button
113-
disabled={processing || currentStep > 3}
132+
disabled={isLoading || !pegInTxHash || !borrowAmount}
114133
variant="contained"
115134
className="flex-1 text-xs sm:text-base"
116135
onClick={handleSign}
117136
>
118-
{processing ? (
137+
{isLoading ? (
119138
<Loader size={16} className="text-accent-contrast" />
120139
) : (
121140
"Sign"

0 commit comments

Comments
 (0)