forked from SmartDropLabs/smartdrop-frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuseLockFlow.ts
More file actions
147 lines (126 loc) · 4.32 KB
/
Copy pathuseLockFlow.ts
File metadata and controls
147 lines (126 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"use client";
/**
* useLockFlow — encapsulates the full simulate → sign → submit → confirm
* deposit state machine for the farm page deposit modal.
*
* Consumers get a single `execute()` function and reactive state that drives
* the step-by-step UI without any business logic leaking into the component.
*/
import { useCallback, useRef, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { lockAssets, type FreighterWalletApi } from "@/lib/soroban";
import { normalizeError } from "@/lib/error-handler";
import { trackEvent } from "@/lib/analytics";
import {
type DepositRecord,
type DepositStep,
isDepositPending,
} from "@/types/farm";
import { QUERY_KEYS } from "@/hooks/useSorobanQuery";
export interface LockFlowParams {
poolId: string;
symbol: string;
publicKey: string;
walletApi: FreighterWalletApi | null;
}
export interface LockFlowState {
step: DepositStep;
record: DepositRecord | null;
error: string | null;
isPending: boolean;
execute: (displayAmount: number) => Promise<void>;
reset: () => void;
}
export function useLockFlow({
poolId,
symbol,
publicKey,
walletApi,
}: LockFlowParams): LockFlowState {
const queryClient = useQueryClient();
const [step, setStep] = useState<DepositStep>("idle");
const [record, setRecord] = useState<DepositRecord | null>(null);
const [error, setError] = useState<string | null>(null);
const walletApiRef = useRef(walletApi);
walletApiRef.current = walletApi;
const reset = useCallback(() => {
setStep("idle");
setRecord(null);
setError(null);
}, []);
const execute = useCallback(
async (displayAmount: number) => {
if (isDepositPending(step)) return;
setError(null);
setRecord(null);
const start = Date.now();
trackEvent("deposit_initiated", { poolId, symbol, displayAmount });
try {
if (!walletApi || !publicKey) {
throw new Error("Wallet not connected. Please connect Freighter before depositing.");
}
setStep("simulating");
const result = await lockAssets({
poolContractId: poolId,
publicKey,
amount: String(displayAmount),
walletApi,
onStep: setStep,
isStillConnected: () => walletApiRef.current === walletApi,
});
if (!result.success) {
throw new Error(result.error ?? "Transaction failed");
}
setStep("submitting");
const txHash = result.hash ?? result.transactionHash ?? "";
const depositRecord: DepositRecord = {
poolId,
symbol,
displayAmount,
txHash,
confirmedAt: Date.now(),
};
setRecord(depositRecord);
setStep("success");
trackEvent("deposit_succeeded", {
poolId,
symbol,
displayAmount,
txHash,
durationMs: Date.now() - start,
});
// Invalidate position and pool caches so the UI reflects the new stake.
// This is the superset of both useLockAssets' onSuccess invalidations
// (#83) and useLockFlow's original set, ensuring no stale data regardless
// of which entry point triggered the deposit.
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.POOLS] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USER_POSITION] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USER_POSITION, poolId] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USER_POSITION, 'all', publicKey] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USER_CREDITS, poolId] });
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.PLATFORM_STATS] });
queryClient.invalidateQueries({ queryKey: ['stellarBalance', publicKey] });
} catch (err) {
const normalized = normalizeError(err, "Deposit");
setError(normalized.userMessage ?? normalized.message);
setStep("error");
trackEvent("deposit_failed", {
poolId,
symbol,
displayAmount,
errorCode: normalized.code,
durationMs: Date.now() - start,
});
}
},
[step, poolId, symbol, publicKey, walletApi, queryClient],
);
return {
step,
record,
error,
isPending: isDepositPending(step),
execute,
reset,
};
}