Skip to content

Commit ce21ae4

Browse files
feat: deposit redeem flow (#555)
* feat(packages): deposit redeem flow
1 parent 413127f commit ce21ae4

File tree

9 files changed

+530
-246
lines changed

9 files changed

+530
-246
lines changed

services/vault/src/components/Overview/Deposits/DepositOverview/index.tsx

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type ColumnProps,
1010
} from "@babylonlabs-io/core-ui";
1111
import { useWalletConnect } from "@babylonlabs-io/wallet-connector";
12-
import { useMemo } from "react";
12+
import { useCallback, useMemo, useState } from "react";
1313
import type { Hex } from "viem";
1414

1515
import { useBTCWallet, useETHWallet } from "../../../../context/wallet";
@@ -19,6 +19,8 @@ import { getPeginState } from "../../../../models/peginStateMachine";
1919
import { usePeginStorage } from "../../../../storage/usePeginStorage";
2020
import type { VaultActivity } from "../../../../types/activity";
2121
import type { Deposit } from "../../../../types/vault";
22+
import { BroadcastSignModal } from "../BroadcastSignModal";
23+
import { BroadcastSuccessModal } from "../BroadcastSuccessModal";
2224
import { DepositTableRowActions } from "../DepositTableRow";
2325
import { useDepositRowPolling } from "../hooks/useDepositRowPolling";
2426
import { usePayoutSignModal } from "../hooks/usePayoutSignModal";
@@ -27,6 +29,10 @@ import {
2729
useVaultDepositState,
2830
VaultDepositStep,
2931
} from "../state/VaultDepositState";
32+
import {
33+
useVaultRedeemState,
34+
VaultRedeemStep,
35+
} from "../state/VaultRedeemState";
3036

3137
function EmptyState({
3238
onDeposit,
@@ -109,12 +115,14 @@ function ActionCell({
109115
btcPublicKey,
110116
pendingPegins,
111117
onSignClick,
118+
onBroadcastClick,
112119
}: {
113120
activity: VaultActivity;
114121
deposit: Deposit;
115122
btcPublicKey?: string;
116123
pendingPegins: any[];
117124
onSignClick: (depositId: string, transactions: any[]) => void;
125+
onBroadcastClick: (depositId: string) => void;
118126
}) {
119127
const pendingPegin = pendingPegins.find((p) => p.id === deposit.id);
120128

@@ -125,6 +133,7 @@ function ActionCell({
125133
btcPublicKey={btcPublicKey}
126134
pendingPegin={pendingPegin}
127135
onSignClick={onSignClick}
136+
onBroadcastClick={onBroadcastClick}
128137
/>
129138
);
130139
}
@@ -136,20 +145,26 @@ function DepositMobileCard({
136145
btcPublicKey,
137146
pendingPegins,
138147
onSignClick,
148+
onBroadcastClick,
139149
}: {
140150
deposit: Deposit;
141151
activity: VaultActivity;
142152
btcPublicKey?: string;
143153
pendingPegins: any[];
144154
onSignClick: (depositId: string, transactions: any[]) => void;
155+
onBroadcastClick: (depositId: string) => void;
145156
}) {
146157
const pendingPegin = pendingPegins.find((p) => p.id === deposit.id);
147-
const { peginState, shouldShowSignButton, transactions } =
148-
useDepositRowPolling({
149-
activity,
150-
btcPublicKey,
151-
pendingPegin,
152-
});
158+
const {
159+
peginState,
160+
shouldShowSignButton,
161+
shouldShowBroadcastButton,
162+
transactions,
163+
} = useDepositRowPolling({
164+
activity,
165+
btcPublicKey,
166+
pendingPegin,
167+
});
153168
const status = peginState.displayLabel;
154169

155170
const statusMap: Record<string, "inactive" | "pending" | "active"> = {
@@ -194,11 +209,17 @@ function DepositMobileCard({
194209
},
195210
]}
196211
actions={
197-
shouldShowSignButton ? [{ name: "Sign", action: "sign" }] : undefined
212+
shouldShowSignButton
213+
? [{ name: "Sign", action: "sign" }]
214+
: shouldShowBroadcastButton
215+
? [{ name: "Sign & Broadcast", action: "broadcast" }]
216+
: undefined
198217
}
199218
onAction={(id, action) => {
200219
if (action === "sign" && transactions) {
201220
onSignClick(id, transactions);
221+
} else if (action === "broadcast") {
222+
onBroadcastClick(id);
202223
}
203224
}}
204225
/>
@@ -239,7 +260,38 @@ export function DepositOverview() {
239260
onSuccess: refetchActivities,
240261
});
241262

263+
// Manage broadcast modal state
264+
const [broadcastingActivity, setBroadcastingActivity] =
265+
useState<VaultActivity | null>(null);
266+
const [broadcastSuccessOpen, setBroadcastSuccessOpen] = useState(false);
267+
268+
// Broadcast modal handlers
269+
const handleBroadcastClick = useCallback(
270+
(depositId: string) => {
271+
const activity = allActivities.find((a) => a.id === depositId);
272+
if (activity) {
273+
setBroadcastingActivity(activity);
274+
}
275+
},
276+
[allActivities],
277+
);
278+
279+
const handleBroadcastClose = useCallback(() => {
280+
setBroadcastingActivity(null);
281+
}, []);
282+
283+
const handleBroadcastSuccess = useCallback(() => {
284+
setBroadcastingActivity(null);
285+
setBroadcastSuccessOpen(true);
286+
refetchActivities(); // Trigger refetch to update status
287+
}, [refetchActivities]);
288+
289+
const handleBroadcastSuccessClose = useCallback(() => {
290+
setBroadcastSuccessOpen(false);
291+
}, []);
292+
242293
const { goToStep: goToDepositStep } = useVaultDepositState();
294+
const { goToStep: goToRedeemStep } = useVaultRedeemState();
243295

244296
const handleDeposit = () => {
245297
if (!isConnected) {
@@ -251,6 +303,12 @@ export function DepositOverview() {
251303
}
252304
};
253305

306+
const handleRedeem = () => {
307+
if (isConnected) {
308+
goToRedeemStep(VaultRedeemStep.FORM);
309+
}
310+
};
311+
254312
// Transform VaultActivity to Deposit format for table
255313
const deposits: Deposit[] = useMemo(() => {
256314
return allActivities.map((activity: VaultActivity) => {
@@ -264,7 +322,7 @@ export function DepositOverview() {
264322
name: activity.providers[0]?.name || "Unknown Provider",
265323
icon: activity.providers[0]?.icon || "",
266324
},
267-
status: state.displayLabel as "Available" | "Pending" | "In Use",
325+
status: state.displayLabel,
268326
};
269327
});
270328
}, [allActivities]);
@@ -333,6 +391,7 @@ export function DepositOverview() {
333391
btcPublicKey={btcPublicKey}
334392
pendingPegins={pendingPegins}
335393
onSignClick={handleSignClick}
394+
onBroadcastClick={handleBroadcastClick}
336395
/>
337396
);
338397
},
@@ -341,7 +400,7 @@ export function DepositOverview() {
341400

342401
return (
343402
<div className="relative">
344-
{/* Header with Deposit button */}
403+
{/* Header with Deposit and Redeem buttons */}
345404
<div className="mb-4 flex items-center justify-end gap-2">
346405
<Button
347406
variant="outlined"
@@ -352,6 +411,16 @@ export function DepositOverview() {
352411
>
353412
{isConnected ? "Deposit" : "Connect Wallet"}
354413
</Button>
414+
<Button
415+
variant="outlined"
416+
size="medium"
417+
rounded
418+
onClick={handleRedeem}
419+
aria-label="Redeem BTC"
420+
disabled={!isConnected}
421+
>
422+
Redeem
423+
</Button>
355424
</div>
356425

357426
{/* Desktop: Deposits Table, Mobile: Deposit Cards */}
@@ -367,6 +436,7 @@ export function DepositOverview() {
367436
btcPublicKey={btcPublicKey}
368437
pendingPegins={pendingPegins}
369438
onSignClick={handleSignClick}
439+
onBroadcastClick={handleBroadcastClick}
370440
/>
371441
);
372442
})}
@@ -389,6 +459,24 @@ export function DepositOverview() {
389459
onSuccess={handlePayoutSignSuccess}
390460
/>
391461
)}
462+
463+
{/* Broadcast Sign Modal - Opens when clicking Sign & Broadcast button */}
464+
{broadcastingActivity && ethAddress && (
465+
<BroadcastSignModal
466+
open={!!broadcastingActivity}
467+
onClose={handleBroadcastClose}
468+
activity={broadcastingActivity}
469+
depositorEthAddress={ethAddress}
470+
onSuccess={handleBroadcastSuccess}
471+
/>
472+
)}
473+
474+
{/* Broadcast Success Modal - Shows after successful broadcast */}
475+
<BroadcastSuccessModal
476+
open={broadcastSuccessOpen}
477+
onClose={handleBroadcastSuccessClose}
478+
amount={broadcastingActivity?.collateral.amount || "0"}
479+
/>
392480
</div>
393481
);
394482
}

0 commit comments

Comments
 (0)