@earnforge/react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.d.mts +313 -0
- package/dist/esm/index.d.mts.map +1 -0
- package/dist/esm/index.mjs +629 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { AllowanceResult, ApprovalTx, ApyDataPoint, DepositQuoteResult, EarnForge, PortfolioResponse, PreflightReport, RedeemQuoteResult, RiskScore, StrategyConfig, StrategyPreset, SuggestResult, Vault } from "@earnforge/sdk";
|
|
3
|
+
|
|
4
|
+
//#region src/context.d.ts
|
|
5
|
+
declare const EarnForgeContext: any;
|
|
6
|
+
interface EarnForgeProviderProps {
|
|
7
|
+
sdk: EarnForge;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Wrap your application with `EarnForgeProvider` to make the SDK
|
|
12
|
+
* instance available to all hooks via React context.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const forge = createEarnForge({ composerApiKey: '...' });
|
|
16
|
+
* <EarnForgeProvider sdk={forge}>
|
|
17
|
+
* <App />
|
|
18
|
+
* </EarnForgeProvider>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function EarnForgeProvider({
|
|
22
|
+
sdk,
|
|
23
|
+
children
|
|
24
|
+
}: EarnForgeProviderProps): any;
|
|
25
|
+
/**
|
|
26
|
+
* Internal helper — returns the SDK instance from context
|
|
27
|
+
* or throws if used outside an `EarnForgeProvider`.
|
|
28
|
+
*/
|
|
29
|
+
declare function useEarnForge(): EarnForge;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/hooks/useVaults.d.ts
|
|
32
|
+
interface UseVaultsParams {
|
|
33
|
+
chainId?: number;
|
|
34
|
+
asset?: string;
|
|
35
|
+
minTvl?: number;
|
|
36
|
+
sortBy?: string;
|
|
37
|
+
limit?: number;
|
|
38
|
+
strategy?: StrategyPreset;
|
|
39
|
+
}
|
|
40
|
+
interface UseVaultsReturn {
|
|
41
|
+
data: Vault[] | undefined;
|
|
42
|
+
isLoading: boolean;
|
|
43
|
+
error: Error | null;
|
|
44
|
+
fetchMore: () => void;
|
|
45
|
+
hasMore: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Fetch a paginated list of vaults with optional filters.
|
|
49
|
+
* Resets accumulated data when params change.
|
|
50
|
+
*
|
|
51
|
+
* ```tsx
|
|
52
|
+
* const { data, isLoading, fetchMore, hasMore } = useVaults({ chainId: 8453 });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare function useVaults(params?: UseVaultsParams): UseVaultsReturn;
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/hooks/useVault.d.ts
|
|
58
|
+
interface UseVaultReturn {
|
|
59
|
+
data: Vault | undefined;
|
|
60
|
+
isLoading: boolean;
|
|
61
|
+
error: Error | null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Fetch a single vault by its slug.
|
|
65
|
+
*
|
|
66
|
+
* ```tsx
|
|
67
|
+
* const { data: vault, isLoading } = useVault('aave-v3-usdc-base');
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function useVault(slug: string | undefined): UseVaultReturn;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/hooks/useEarnTopYield.d.ts
|
|
73
|
+
interface UseEarnTopYieldParams {
|
|
74
|
+
asset?: string;
|
|
75
|
+
chainId?: number;
|
|
76
|
+
limit?: number;
|
|
77
|
+
strategy?: StrategyPreset;
|
|
78
|
+
minTvl?: number;
|
|
79
|
+
}
|
|
80
|
+
interface UseEarnTopYieldReturn {
|
|
81
|
+
data: Vault[] | undefined;
|
|
82
|
+
isLoading: boolean;
|
|
83
|
+
error: Error | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Fetch top-yielding vaults sorted by APY.
|
|
87
|
+
*
|
|
88
|
+
* ```tsx
|
|
89
|
+
* const { data } = useEarnTopYield({ asset: 'USDC', limit: 5 });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
declare function useEarnTopYield(params?: UseEarnTopYieldParams): UseEarnTopYieldReturn;
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/hooks/usePortfolio.d.ts
|
|
95
|
+
interface UsePortfolioReturn {
|
|
96
|
+
data: PortfolioResponse | undefined;
|
|
97
|
+
isLoading: boolean;
|
|
98
|
+
error: Error | null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Fetch portfolio positions for a wallet address.
|
|
102
|
+
*
|
|
103
|
+
* ```tsx
|
|
104
|
+
* const { data } = usePortfolio('0xabc...');
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
declare function usePortfolio(wallet: string | undefined): UsePortfolioReturn;
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/hooks/useRiskScore.d.ts
|
|
110
|
+
interface UseRiskScoreReturn {
|
|
111
|
+
data: RiskScore | undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Compute a risk score for a vault. This is a synchronous computation
|
|
115
|
+
* wrapped in `useMemo` for referential stability.
|
|
116
|
+
*
|
|
117
|
+
* ```tsx
|
|
118
|
+
* const { data: risk } = useRiskScore(vault);
|
|
119
|
+
* // risk.score, risk.label, risk.breakdown
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function useRiskScore(vault: Vault | undefined): UseRiskScoreReturn;
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/hooks/useStrategy.d.ts
|
|
125
|
+
interface UseStrategyReturn {
|
|
126
|
+
data: StrategyConfig | undefined;
|
|
127
|
+
filters: StrategyConfig['filters'] | undefined;
|
|
128
|
+
sort: StrategyConfig['sort'] | undefined;
|
|
129
|
+
sortDirection: StrategyConfig['sortDirection'] | undefined;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve a strategy preset into its filter configuration.
|
|
133
|
+
* Use the returned `filters` to pass into `useVaults` or `useEarnTopYield`.
|
|
134
|
+
*
|
|
135
|
+
* ```tsx
|
|
136
|
+
* const { filters } = useStrategy('conservative');
|
|
137
|
+
* const { data } = useVaults({ ...filters });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function useStrategy(preset: StrategyPreset | undefined): UseStrategyReturn;
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/hooks/useSuggest.d.ts
|
|
143
|
+
interface UseSuggestParams {
|
|
144
|
+
amount: number;
|
|
145
|
+
asset?: string;
|
|
146
|
+
maxChains?: number;
|
|
147
|
+
strategy?: StrategyPreset;
|
|
148
|
+
}
|
|
149
|
+
interface UseSuggestReturn {
|
|
150
|
+
data: SuggestResult | undefined;
|
|
151
|
+
isLoading: boolean;
|
|
152
|
+
error: Error | null;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get a suggested portfolio allocation based on the given parameters.
|
|
156
|
+
*
|
|
157
|
+
* ```tsx
|
|
158
|
+
* const { data } = useSuggest({ amount: 10_000, asset: 'USDC' });
|
|
159
|
+
* // data.allocations — the recommended split
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
declare function useSuggest(params: UseSuggestParams | undefined): UseSuggestReturn;
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/hooks/useApyHistory.d.ts
|
|
165
|
+
interface UseApyHistoryReturn {
|
|
166
|
+
data: ApyDataPoint[] | undefined;
|
|
167
|
+
isLoading: boolean;
|
|
168
|
+
error: Error | null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Fetch 30-day APY history for a vault from DeFiLlama.
|
|
172
|
+
*
|
|
173
|
+
* Accepts a full Vault object for accurate matching (uses protocol name,
|
|
174
|
+
* chain, underlying tokens, symbol, and TVL proximity). Falls back to
|
|
175
|
+
* the less accurate address+chainId matching when only primitives are given.
|
|
176
|
+
*
|
|
177
|
+
* ```tsx
|
|
178
|
+
* // Preferred: pass the full vault object for accurate DeFiLlama matching
|
|
179
|
+
* const { data: history } = useApyHistory(vault);
|
|
180
|
+
*
|
|
181
|
+
* // Legacy: address + chainId (less accurate matching)
|
|
182
|
+
* const { data: history } = useApyHistory('0xabc...', 8453);
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
declare function useApyHistory(vault: Vault | undefined): UseApyHistoryReturn;
|
|
186
|
+
declare function useApyHistory(vaultAddress: string | undefined, chainId: number | undefined): UseApyHistoryReturn;
|
|
187
|
+
//#endregion
|
|
188
|
+
//#region src/hooks/useEarnDeposit.d.ts
|
|
189
|
+
/**
|
|
190
|
+
* Deposit state machine:
|
|
191
|
+
*
|
|
192
|
+
* idle --> preflight --> checking-allowance --> approving --> quoting --> ready --> sending --> success
|
|
193
|
+
* \ | | | | | |
|
|
194
|
+
* \________|_______________|___________________|____________|__________|__________|--> error
|
|
195
|
+
*
|
|
196
|
+
* The "checking-allowance" phase verifies the ERC-20 allowance for the fromToken.
|
|
197
|
+
* If allowance is insufficient, "approving" sends an approval tx before quoting.
|
|
198
|
+
*/
|
|
199
|
+
type DepositPhase = 'idle' | 'preflight' | 'checking-allowance' | 'approving' | 'quoting' | 'ready' | 'sending' | 'success' | 'error';
|
|
200
|
+
interface DepositState {
|
|
201
|
+
phase: DepositPhase;
|
|
202
|
+
preflightReport: PreflightReport | null;
|
|
203
|
+
allowance: AllowanceResult | null;
|
|
204
|
+
approvalTx: ApprovalTx | null;
|
|
205
|
+
quote: DepositQuoteResult | null;
|
|
206
|
+
txHash: string | null;
|
|
207
|
+
error: Error | null;
|
|
208
|
+
}
|
|
209
|
+
interface UseEarnDepositParams {
|
|
210
|
+
vault: Vault | undefined;
|
|
211
|
+
amount: string;
|
|
212
|
+
wallet: string;
|
|
213
|
+
fromToken?: string;
|
|
214
|
+
fromChain?: number;
|
|
215
|
+
slippage?: number;
|
|
216
|
+
/** JSON-RPC URL for the source chain — needed for allowance checking */
|
|
217
|
+
rpcUrl?: string;
|
|
218
|
+
/** wagmi's sendTransactionAsync function — pass from useSendTransaction() */
|
|
219
|
+
sendTransactionAsync?: (params: {
|
|
220
|
+
to: `0x${string}`;
|
|
221
|
+
data: `0x${string}`;
|
|
222
|
+
value: bigint;
|
|
223
|
+
chainId: number;
|
|
224
|
+
}) => Promise<`0x${string}`>;
|
|
225
|
+
}
|
|
226
|
+
interface UseEarnDepositReturn {
|
|
227
|
+
state: DepositState;
|
|
228
|
+
/** Kick off the preflight -> quote -> ready flow */
|
|
229
|
+
prepare: () => Promise<void>;
|
|
230
|
+
/** Execute the deposit transaction (requires sendTransactionAsync or sendTransaction) */
|
|
231
|
+
execute: () => Promise<void>;
|
|
232
|
+
/** Reset back to idle */
|
|
233
|
+
reset: () => void;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Deposit state machine hook.
|
|
237
|
+
*
|
|
238
|
+
* Flow: idle -> preflight -> quoting -> ready
|
|
239
|
+
* Then call `execute()` to send: ready -> sending -> success
|
|
240
|
+
*
|
|
241
|
+
* ```tsx
|
|
242
|
+
* const { state, prepare, execute, reset } = useEarnDeposit({
|
|
243
|
+
* vault,
|
|
244
|
+
* amount: '100',
|
|
245
|
+
* wallet: address,
|
|
246
|
+
* sendTransactionAsync,
|
|
247
|
+
* });
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
declare function useEarnDeposit(params: UseEarnDepositParams): UseEarnDepositReturn;
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/hooks/useEarnRedeem.d.ts
|
|
253
|
+
/**
|
|
254
|
+
* Redeem state machine:
|
|
255
|
+
*
|
|
256
|
+
* idle --> preflight --> quoting --> ready --> sending --> success
|
|
257
|
+
* \ | | | |
|
|
258
|
+
* \__________|______________|____________|____________|--> error
|
|
259
|
+
*/
|
|
260
|
+
type RedeemPhase = 'idle' | 'preflight' | 'quoting' | 'ready' | 'sending' | 'success' | 'error';
|
|
261
|
+
interface RedeemState {
|
|
262
|
+
phase: RedeemPhase;
|
|
263
|
+
preflightReport: PreflightReport | null;
|
|
264
|
+
quote: RedeemQuoteResult | null;
|
|
265
|
+
txHash: string | null;
|
|
266
|
+
error: Error | null;
|
|
267
|
+
}
|
|
268
|
+
interface UseEarnRedeemParams {
|
|
269
|
+
vault: Vault | undefined;
|
|
270
|
+
/** Amount of vault share tokens to redeem (human-readable) */
|
|
271
|
+
amount: string;
|
|
272
|
+
wallet: string;
|
|
273
|
+
/** Token to receive. Defaults to underlying token on vault chain. */
|
|
274
|
+
toToken?: string;
|
|
275
|
+
/** Destination chain. Defaults to vault chain. */
|
|
276
|
+
toChain?: number;
|
|
277
|
+
slippage?: number;
|
|
278
|
+
/** wagmi's sendTransactionAsync function — pass from useSendTransaction() */
|
|
279
|
+
sendTransactionAsync?: (params: {
|
|
280
|
+
to: `0x${string}`;
|
|
281
|
+
data: `0x${string}`;
|
|
282
|
+
value: bigint;
|
|
283
|
+
chainId: number;
|
|
284
|
+
}) => Promise<`0x${string}`>;
|
|
285
|
+
}
|
|
286
|
+
interface UseEarnRedeemReturn {
|
|
287
|
+
state: RedeemState;
|
|
288
|
+
/** Kick off the preflight -> quote -> ready flow */
|
|
289
|
+
prepare: () => Promise<void>;
|
|
290
|
+
/** Execute the redeem transaction (requires sendTransactionAsync) */
|
|
291
|
+
execute: () => Promise<void>;
|
|
292
|
+
/** Reset back to idle */
|
|
293
|
+
reset: () => void;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Withdrawal/redeem state machine hook.
|
|
297
|
+
*
|
|
298
|
+
* Flow: idle -> preflight -> quoting -> ready
|
|
299
|
+
* Then call `execute()` to send: ready -> sending -> success
|
|
300
|
+
*
|
|
301
|
+
* ```tsx
|
|
302
|
+
* const { state, prepare, execute, reset } = useEarnRedeem({
|
|
303
|
+
* vault,
|
|
304
|
+
* amount: '100',
|
|
305
|
+
* wallet: address,
|
|
306
|
+
* sendTransactionAsync,
|
|
307
|
+
* });
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
declare function useEarnRedeem(params: UseEarnRedeemParams): UseEarnRedeemReturn;
|
|
311
|
+
//#endregion
|
|
312
|
+
export { type DepositPhase, type DepositState, EarnForgeContext, EarnForgeProvider, type EarnForgeProviderProps, type RedeemPhase, type RedeemState, type UseApyHistoryReturn, type UseEarnDepositParams, type UseEarnDepositReturn, type UseEarnRedeemParams, type UseEarnRedeemReturn, type UseEarnTopYieldParams, type UseEarnTopYieldReturn, type UsePortfolioReturn, type UseRiskScoreReturn, type UseStrategyReturn, type UseSuggestParams, type UseSuggestReturn, type UseVaultReturn, type UseVaultsParams, type UseVaultsReturn, useApyHistory, useEarnDeposit, useEarnForge, useEarnRedeem, useEarnTopYield, usePortfolio, useRiskScore, useStrategy, useSuggest, useVault, useVaults };
|
|
313
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/context.ts","../../src/hooks/useVaults.ts","../../src/hooks/useVault.ts","../../src/hooks/useEarnTopYield.ts","../../src/hooks/usePortfolio.ts","../../src/hooks/useRiskScore.ts","../../src/hooks/useStrategy.ts","../../src/hooks/useSuggest.ts","../../src/hooks/useApyHistory.ts","../../src/hooks/useEarnDeposit.ts","../../src/hooks/useEarnRedeem.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { MAX_UINT256, buildApprovalTx, checkAllowance, getStrategy, toSmallestUnit } from "@earnforge/sdk";
|
|
4
|
+
//#region src/context.ts
|
|
5
|
+
const EarnForgeContext = createContext(null);
|
|
6
|
+
/**
|
|
7
|
+
* Wrap your application with `EarnForgeProvider` to make the SDK
|
|
8
|
+
* instance available to all hooks via React context.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const forge = createEarnForge({ composerApiKey: '...' });
|
|
12
|
+
* <EarnForgeProvider sdk={forge}>
|
|
13
|
+
* <App />
|
|
14
|
+
* </EarnForgeProvider>
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function EarnForgeProvider({ sdk, children }) {
|
|
18
|
+
return createElement(EarnForgeContext.Provider, { value: sdk }, children);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Internal helper — returns the SDK instance from context
|
|
22
|
+
* or throws if used outside an `EarnForgeProvider`.
|
|
23
|
+
*/
|
|
24
|
+
function useEarnForge() {
|
|
25
|
+
const ctx = useContext(EarnForgeContext);
|
|
26
|
+
if (!ctx) throw new Error("useEarnForge must be used within an <EarnForgeProvider>. Wrap your component tree with <EarnForgeProvider sdk={forge}>.");
|
|
27
|
+
return ctx;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/hooks/useVaults.ts
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a paginated list of vaults with optional filters.
|
|
33
|
+
* Resets accumulated data when params change.
|
|
34
|
+
*
|
|
35
|
+
* ```tsx
|
|
36
|
+
* const { data, isLoading, fetchMore, hasMore } = useVaults({ chainId: 8453 });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function useVaults(params = {}) {
|
|
40
|
+
const sdk = useEarnForge();
|
|
41
|
+
const queryClient = useQueryClient();
|
|
42
|
+
const cursorRef = useRef(null);
|
|
43
|
+
const accumulatedRef = useRef([]);
|
|
44
|
+
const paramsKey = useMemo(() => JSON.stringify({
|
|
45
|
+
chainId: params.chainId,
|
|
46
|
+
asset: params.asset,
|
|
47
|
+
minTvl: params.minTvl,
|
|
48
|
+
sortBy: params.sortBy,
|
|
49
|
+
strategy: params.strategy
|
|
50
|
+
}), [
|
|
51
|
+
params.chainId,
|
|
52
|
+
params.asset,
|
|
53
|
+
params.minTvl,
|
|
54
|
+
params.sortBy,
|
|
55
|
+
params.strategy
|
|
56
|
+
]);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
cursorRef.current = null;
|
|
59
|
+
accumulatedRef.current = [];
|
|
60
|
+
}, [paramsKey]);
|
|
61
|
+
const queryKey = useMemo(() => [
|
|
62
|
+
"earnforge",
|
|
63
|
+
"vaults",
|
|
64
|
+
paramsKey,
|
|
65
|
+
cursorRef.current
|
|
66
|
+
], [paramsKey]);
|
|
67
|
+
const query = useQuery({
|
|
68
|
+
queryKey,
|
|
69
|
+
queryFn: async () => {
|
|
70
|
+
const result = await sdk.vaults.list({
|
|
71
|
+
chainId: params.chainId,
|
|
72
|
+
asset: params.asset,
|
|
73
|
+
minTvl: params.minTvl,
|
|
74
|
+
sortBy: params.sortBy,
|
|
75
|
+
strategy: params.strategy,
|
|
76
|
+
cursor: cursorRef.current ?? void 0
|
|
77
|
+
});
|
|
78
|
+
cursorRef.current = result.nextCursor;
|
|
79
|
+
if (accumulatedRef.current.length === 0) accumulatedRef.current = result.data;
|
|
80
|
+
else accumulatedRef.current = [...accumulatedRef.current, ...result.data];
|
|
81
|
+
return {
|
|
82
|
+
...result,
|
|
83
|
+
data: params.limit ? accumulatedRef.current.slice(0, params.limit) : accumulatedRef.current
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const fetchMore = useCallback(() => {
|
|
88
|
+
if (cursorRef.current) queryClient.invalidateQueries({ queryKey });
|
|
89
|
+
}, [queryClient, queryKey]);
|
|
90
|
+
return {
|
|
91
|
+
data: query.data?.data,
|
|
92
|
+
isLoading: query.isLoading,
|
|
93
|
+
error: query.error,
|
|
94
|
+
fetchMore,
|
|
95
|
+
hasMore: cursorRef.current !== null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/hooks/useVault.ts
|
|
100
|
+
/**
|
|
101
|
+
* Fetch a single vault by its slug.
|
|
102
|
+
*
|
|
103
|
+
* ```tsx
|
|
104
|
+
* const { data: vault, isLoading } = useVault('aave-v3-usdc-base');
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function useVault(slug) {
|
|
108
|
+
const sdk = useEarnForge();
|
|
109
|
+
const query = useQuery({
|
|
110
|
+
queryKey: [
|
|
111
|
+
"earnforge",
|
|
112
|
+
"vault",
|
|
113
|
+
slug
|
|
114
|
+
],
|
|
115
|
+
queryFn: () => sdk.vaults.get(slug),
|
|
116
|
+
enabled: !!slug
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
data: query.data,
|
|
120
|
+
isLoading: query.isLoading,
|
|
121
|
+
error: query.error
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/hooks/useEarnTopYield.ts
|
|
126
|
+
/**
|
|
127
|
+
* Fetch top-yielding vaults sorted by APY.
|
|
128
|
+
*
|
|
129
|
+
* ```tsx
|
|
130
|
+
* const { data } = useEarnTopYield({ asset: 'USDC', limit: 5 });
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
function useEarnTopYield(params = {}) {
|
|
134
|
+
const sdk = useEarnForge();
|
|
135
|
+
const query = useQuery({
|
|
136
|
+
queryKey: [
|
|
137
|
+
"earnforge",
|
|
138
|
+
"topYield",
|
|
139
|
+
params
|
|
140
|
+
],
|
|
141
|
+
queryFn: () => sdk.vaults.top({
|
|
142
|
+
asset: params.asset,
|
|
143
|
+
chainId: params.chainId,
|
|
144
|
+
limit: params.limit,
|
|
145
|
+
strategy: params.strategy,
|
|
146
|
+
minTvl: params.minTvl
|
|
147
|
+
})
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
data: query.data,
|
|
151
|
+
isLoading: query.isLoading,
|
|
152
|
+
error: query.error
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/hooks/usePortfolio.ts
|
|
157
|
+
/**
|
|
158
|
+
* Fetch portfolio positions for a wallet address.
|
|
159
|
+
*
|
|
160
|
+
* ```tsx
|
|
161
|
+
* const { data } = usePortfolio('0xabc...');
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
function usePortfolio(wallet) {
|
|
165
|
+
const sdk = useEarnForge();
|
|
166
|
+
const query = useQuery({
|
|
167
|
+
queryKey: [
|
|
168
|
+
"earnforge",
|
|
169
|
+
"portfolio",
|
|
170
|
+
wallet
|
|
171
|
+
],
|
|
172
|
+
queryFn: () => sdk.portfolio.get(wallet),
|
|
173
|
+
enabled: !!wallet
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
data: query.data,
|
|
177
|
+
isLoading: query.isLoading,
|
|
178
|
+
error: query.error
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/hooks/useRiskScore.ts
|
|
183
|
+
/**
|
|
184
|
+
* Compute a risk score for a vault. This is a synchronous computation
|
|
185
|
+
* wrapped in `useMemo` for referential stability.
|
|
186
|
+
*
|
|
187
|
+
* ```tsx
|
|
188
|
+
* const { data: risk } = useRiskScore(vault);
|
|
189
|
+
* // risk.score, risk.label, risk.breakdown
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
function useRiskScore(vault) {
|
|
193
|
+
const sdk = useEarnForge();
|
|
194
|
+
return { data: useMemo(() => {
|
|
195
|
+
if (!vault) return void 0;
|
|
196
|
+
return sdk.riskScore(vault);
|
|
197
|
+
}, [sdk, vault]) };
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/hooks/useStrategy.ts
|
|
201
|
+
/**
|
|
202
|
+
* Resolve a strategy preset into its filter configuration.
|
|
203
|
+
* Use the returned `filters` to pass into `useVaults` or `useEarnTopYield`.
|
|
204
|
+
*
|
|
205
|
+
* ```tsx
|
|
206
|
+
* const { filters } = useStrategy('conservative');
|
|
207
|
+
* const { data } = useVaults({ ...filters });
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
function useStrategy(preset) {
|
|
211
|
+
const data = useMemo(() => {
|
|
212
|
+
if (!preset) return void 0;
|
|
213
|
+
return getStrategy(preset);
|
|
214
|
+
}, [preset]);
|
|
215
|
+
return {
|
|
216
|
+
data,
|
|
217
|
+
filters: data?.filters,
|
|
218
|
+
sort: data?.sort,
|
|
219
|
+
sortDirection: data?.sortDirection
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/hooks/useSuggest.ts
|
|
224
|
+
/**
|
|
225
|
+
* Get a suggested portfolio allocation based on the given parameters.
|
|
226
|
+
*
|
|
227
|
+
* ```tsx
|
|
228
|
+
* const { data } = useSuggest({ amount: 10_000, asset: 'USDC' });
|
|
229
|
+
* // data.allocations — the recommended split
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
function useSuggest(params) {
|
|
233
|
+
const sdk = useEarnForge();
|
|
234
|
+
const query = useQuery({
|
|
235
|
+
queryKey: [
|
|
236
|
+
"earnforge",
|
|
237
|
+
"suggest",
|
|
238
|
+
params
|
|
239
|
+
],
|
|
240
|
+
queryFn: () => sdk.suggest({
|
|
241
|
+
amount: params.amount,
|
|
242
|
+
asset: params.asset,
|
|
243
|
+
maxChains: params.maxChains,
|
|
244
|
+
strategy: params.strategy
|
|
245
|
+
}),
|
|
246
|
+
enabled: !!params && params.amount > 0
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
data: query.data,
|
|
250
|
+
isLoading: query.isLoading,
|
|
251
|
+
error: query.error
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/hooks/useApyHistory.ts
|
|
256
|
+
function useApyHistory(vaultOrAddress, chainId) {
|
|
257
|
+
const sdk = useEarnForge();
|
|
258
|
+
const isVaultObject = typeof vaultOrAddress === "object" && vaultOrAddress !== null;
|
|
259
|
+
const vault = isVaultObject ? vaultOrAddress : void 0;
|
|
260
|
+
const address = typeof vaultOrAddress === "string" ? vaultOrAddress : void 0;
|
|
261
|
+
const enabled = isVaultObject ? !!vault : !!address && !!chainId;
|
|
262
|
+
const query = useQuery({
|
|
263
|
+
queryKey: isVaultObject ? [
|
|
264
|
+
"earnforge",
|
|
265
|
+
"apyHistory",
|
|
266
|
+
vault?.slug,
|
|
267
|
+
vault?.chainId
|
|
268
|
+
] : [
|
|
269
|
+
"earnforge",
|
|
270
|
+
"apyHistory",
|
|
271
|
+
address,
|
|
272
|
+
chainId
|
|
273
|
+
],
|
|
274
|
+
queryFn: () => {
|
|
275
|
+
if (vault) return sdk.getApyHistory(vault);
|
|
276
|
+
return sdk.getApyHistory(address, chainId);
|
|
277
|
+
},
|
|
278
|
+
enabled
|
|
279
|
+
});
|
|
280
|
+
return {
|
|
281
|
+
data: query.data,
|
|
282
|
+
isLoading: query.isLoading,
|
|
283
|
+
error: query.error
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/hooks/useEarnDeposit.ts
|
|
288
|
+
const INITIAL_STATE$1 = {
|
|
289
|
+
phase: "idle",
|
|
290
|
+
preflightReport: null,
|
|
291
|
+
allowance: null,
|
|
292
|
+
approvalTx: null,
|
|
293
|
+
quote: null,
|
|
294
|
+
txHash: null,
|
|
295
|
+
error: null
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Deposit state machine hook.
|
|
299
|
+
*
|
|
300
|
+
* Flow: idle -> preflight -> quoting -> ready
|
|
301
|
+
* Then call `execute()` to send: ready -> sending -> success
|
|
302
|
+
*
|
|
303
|
+
* ```tsx
|
|
304
|
+
* const { state, prepare, execute, reset } = useEarnDeposit({
|
|
305
|
+
* vault,
|
|
306
|
+
* amount: '100',
|
|
307
|
+
* wallet: address,
|
|
308
|
+
* sendTransactionAsync,
|
|
309
|
+
* });
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
function useEarnDeposit(params) {
|
|
313
|
+
const sdk = useEarnForge();
|
|
314
|
+
const [state, setState] = useState(INITIAL_STATE$1);
|
|
315
|
+
const abortRef = useRef(false);
|
|
316
|
+
return {
|
|
317
|
+
state,
|
|
318
|
+
prepare: useCallback(async () => {
|
|
319
|
+
if (!params.vault || !params.wallet || !params.amount) {
|
|
320
|
+
setState({
|
|
321
|
+
...INITIAL_STATE$1,
|
|
322
|
+
phase: "error",
|
|
323
|
+
error: /* @__PURE__ */ new Error("Missing vault, wallet, or amount")
|
|
324
|
+
});
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
abortRef.current = false;
|
|
328
|
+
try {
|
|
329
|
+
setState({
|
|
330
|
+
...INITIAL_STATE$1,
|
|
331
|
+
phase: "preflight"
|
|
332
|
+
});
|
|
333
|
+
const report = sdk.preflight(params.vault, params.wallet);
|
|
334
|
+
if (abortRef.current) return;
|
|
335
|
+
if (!report.ok) {
|
|
336
|
+
setState({
|
|
337
|
+
...INITIAL_STATE$1,
|
|
338
|
+
phase: "error",
|
|
339
|
+
preflightReport: report,
|
|
340
|
+
error: /* @__PURE__ */ new Error(`Preflight failed: ${report.issues.map((i) => i.message).join("; ")}`)
|
|
341
|
+
});
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const fromToken = params.fromToken ?? params.vault.underlyingTokens[0]?.address;
|
|
345
|
+
let allowanceResult = null;
|
|
346
|
+
let approval = null;
|
|
347
|
+
if (params.rpcUrl && fromToken) {
|
|
348
|
+
setState({
|
|
349
|
+
...INITIAL_STATE$1,
|
|
350
|
+
phase: "checking-allowance",
|
|
351
|
+
preflightReport: report
|
|
352
|
+
});
|
|
353
|
+
const decimals = params.vault.underlyingTokens[0]?.decimals ?? 18;
|
|
354
|
+
const requiredAmount = BigInt(toSmallestUnit(params.amount, decimals));
|
|
355
|
+
allowanceResult = await checkAllowance(params.rpcUrl, fromToken, params.wallet, params.vault.address, requiredAmount);
|
|
356
|
+
if (abortRef.current) return;
|
|
357
|
+
if (!allowanceResult.sufficient) {
|
|
358
|
+
approval = buildApprovalTx(fromToken, params.vault.address, MAX_UINT256, params.vault.chainId);
|
|
359
|
+
setState({
|
|
360
|
+
...INITIAL_STATE$1,
|
|
361
|
+
phase: "approving",
|
|
362
|
+
preflightReport: report,
|
|
363
|
+
allowance: allowanceResult,
|
|
364
|
+
approvalTx: approval
|
|
365
|
+
});
|
|
366
|
+
const sendFn = params.sendTransactionAsync;
|
|
367
|
+
if (sendFn) {
|
|
368
|
+
await sendFn({
|
|
369
|
+
to: approval.to,
|
|
370
|
+
data: approval.data,
|
|
371
|
+
value: 0n,
|
|
372
|
+
chainId: approval.chainId
|
|
373
|
+
});
|
|
374
|
+
if (abortRef.current) return;
|
|
375
|
+
} else return;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
setState({
|
|
379
|
+
...INITIAL_STATE$1,
|
|
380
|
+
phase: "quoting",
|
|
381
|
+
preflightReport: report,
|
|
382
|
+
allowance: allowanceResult,
|
|
383
|
+
approvalTx: approval
|
|
384
|
+
});
|
|
385
|
+
const quote = await sdk.buildDepositQuote(params.vault, {
|
|
386
|
+
fromAmount: params.amount,
|
|
387
|
+
wallet: params.wallet,
|
|
388
|
+
fromToken: params.fromToken,
|
|
389
|
+
fromChain: params.fromChain,
|
|
390
|
+
slippage: params.slippage
|
|
391
|
+
});
|
|
392
|
+
if (abortRef.current) return;
|
|
393
|
+
setState({
|
|
394
|
+
...INITIAL_STATE$1,
|
|
395
|
+
phase: "ready",
|
|
396
|
+
preflightReport: report,
|
|
397
|
+
allowance: allowanceResult,
|
|
398
|
+
approvalTx: approval,
|
|
399
|
+
quote
|
|
400
|
+
});
|
|
401
|
+
} catch (err) {
|
|
402
|
+
if (abortRef.current) return;
|
|
403
|
+
setState({
|
|
404
|
+
...INITIAL_STATE$1,
|
|
405
|
+
phase: "error",
|
|
406
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}, [
|
|
410
|
+
sdk,
|
|
411
|
+
params.vault,
|
|
412
|
+
params.wallet,
|
|
413
|
+
params.amount,
|
|
414
|
+
params.fromToken,
|
|
415
|
+
params.fromChain,
|
|
416
|
+
params.slippage,
|
|
417
|
+
params.rpcUrl,
|
|
418
|
+
params.sendTransactionAsync
|
|
419
|
+
]),
|
|
420
|
+
execute: useCallback(async () => {
|
|
421
|
+
if (state.phase !== "ready" || !state.quote) {
|
|
422
|
+
setState((prev) => ({
|
|
423
|
+
...prev,
|
|
424
|
+
phase: "error",
|
|
425
|
+
error: /* @__PURE__ */ new Error("Cannot execute: not in ready state. Call prepare() first.")
|
|
426
|
+
}));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const sendFn = params.sendTransactionAsync;
|
|
430
|
+
if (!sendFn) {
|
|
431
|
+
setState((prev) => ({
|
|
432
|
+
...prev,
|
|
433
|
+
phase: "error",
|
|
434
|
+
error: /* @__PURE__ */ new Error("No sendTransactionAsync provided. Pass it from wagmi useSendTransaction().")
|
|
435
|
+
}));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
setState((prev) => ({
|
|
440
|
+
...prev,
|
|
441
|
+
phase: "sending"
|
|
442
|
+
}));
|
|
443
|
+
const tx = state.quote.quote.transactionRequest;
|
|
444
|
+
const hash = await sendFn({
|
|
445
|
+
to: tx.to,
|
|
446
|
+
data: tx.data,
|
|
447
|
+
value: BigInt(tx.value),
|
|
448
|
+
chainId: tx.chainId
|
|
449
|
+
});
|
|
450
|
+
setState((prev) => ({
|
|
451
|
+
...prev,
|
|
452
|
+
phase: "success",
|
|
453
|
+
txHash: hash
|
|
454
|
+
}));
|
|
455
|
+
} catch (err) {
|
|
456
|
+
setState((prev) => ({
|
|
457
|
+
...prev,
|
|
458
|
+
phase: "error",
|
|
459
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
}, [
|
|
463
|
+
state.phase,
|
|
464
|
+
state.quote,
|
|
465
|
+
params.sendTransactionAsync
|
|
466
|
+
]),
|
|
467
|
+
reset: useCallback(() => {
|
|
468
|
+
abortRef.current = true;
|
|
469
|
+
setState(INITIAL_STATE$1);
|
|
470
|
+
}, [])
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/hooks/useEarnRedeem.ts
|
|
475
|
+
const INITIAL_STATE = {
|
|
476
|
+
phase: "idle",
|
|
477
|
+
preflightReport: null,
|
|
478
|
+
quote: null,
|
|
479
|
+
txHash: null,
|
|
480
|
+
error: null
|
|
481
|
+
};
|
|
482
|
+
/**
|
|
483
|
+
* Withdrawal/redeem state machine hook.
|
|
484
|
+
*
|
|
485
|
+
* Flow: idle -> preflight -> quoting -> ready
|
|
486
|
+
* Then call `execute()` to send: ready -> sending -> success
|
|
487
|
+
*
|
|
488
|
+
* ```tsx
|
|
489
|
+
* const { state, prepare, execute, reset } = useEarnRedeem({
|
|
490
|
+
* vault,
|
|
491
|
+
* amount: '100',
|
|
492
|
+
* wallet: address,
|
|
493
|
+
* sendTransactionAsync,
|
|
494
|
+
* });
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
function useEarnRedeem(params) {
|
|
498
|
+
const sdk = useEarnForge();
|
|
499
|
+
const [state, setState] = useState(INITIAL_STATE);
|
|
500
|
+
const abortRef = useRef(false);
|
|
501
|
+
return {
|
|
502
|
+
state,
|
|
503
|
+
prepare: useCallback(async () => {
|
|
504
|
+
if (!params.vault || !params.wallet || !params.amount) {
|
|
505
|
+
setState({
|
|
506
|
+
...INITIAL_STATE,
|
|
507
|
+
phase: "error",
|
|
508
|
+
error: /* @__PURE__ */ new Error("Missing vault, wallet, or amount")
|
|
509
|
+
});
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
if (!params.vault.isRedeemable) {
|
|
513
|
+
setState({
|
|
514
|
+
...INITIAL_STATE,
|
|
515
|
+
phase: "error",
|
|
516
|
+
error: /* @__PURE__ */ new Error(`Vault ${params.vault.slug} is not redeemable — withdrawals are not supported.`)
|
|
517
|
+
});
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
abortRef.current = false;
|
|
521
|
+
try {
|
|
522
|
+
setState({
|
|
523
|
+
...INITIAL_STATE,
|
|
524
|
+
phase: "preflight"
|
|
525
|
+
});
|
|
526
|
+
const report = sdk.preflight(params.vault, params.wallet);
|
|
527
|
+
if (abortRef.current) return;
|
|
528
|
+
if (!report.ok) {
|
|
529
|
+
setState({
|
|
530
|
+
...INITIAL_STATE,
|
|
531
|
+
phase: "error",
|
|
532
|
+
preflightReport: report,
|
|
533
|
+
error: /* @__PURE__ */ new Error(`Preflight failed: ${report.issues.map((i) => i.message).join("; ")}`)
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
setState({
|
|
538
|
+
...INITIAL_STATE,
|
|
539
|
+
phase: "quoting",
|
|
540
|
+
preflightReport: report
|
|
541
|
+
});
|
|
542
|
+
const quote = await sdk.buildRedeemQuote(params.vault, {
|
|
543
|
+
fromAmount: params.amount,
|
|
544
|
+
wallet: params.wallet,
|
|
545
|
+
toToken: params.toToken,
|
|
546
|
+
toChain: params.toChain,
|
|
547
|
+
slippage: params.slippage
|
|
548
|
+
});
|
|
549
|
+
if (abortRef.current) return;
|
|
550
|
+
setState({
|
|
551
|
+
...INITIAL_STATE,
|
|
552
|
+
phase: "ready",
|
|
553
|
+
preflightReport: report,
|
|
554
|
+
quote
|
|
555
|
+
});
|
|
556
|
+
} catch (err) {
|
|
557
|
+
if (abortRef.current) return;
|
|
558
|
+
setState({
|
|
559
|
+
...INITIAL_STATE,
|
|
560
|
+
phase: "error",
|
|
561
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}, [
|
|
565
|
+
sdk,
|
|
566
|
+
params.vault,
|
|
567
|
+
params.wallet,
|
|
568
|
+
params.amount,
|
|
569
|
+
params.toToken,
|
|
570
|
+
params.toChain,
|
|
571
|
+
params.slippage
|
|
572
|
+
]),
|
|
573
|
+
execute: useCallback(async () => {
|
|
574
|
+
if (state.phase !== "ready" || !state.quote) {
|
|
575
|
+
setState((prev) => ({
|
|
576
|
+
...prev,
|
|
577
|
+
phase: "error",
|
|
578
|
+
error: /* @__PURE__ */ new Error("Cannot execute: not in ready state. Call prepare() first.")
|
|
579
|
+
}));
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const sendFn = params.sendTransactionAsync;
|
|
583
|
+
if (!sendFn) {
|
|
584
|
+
setState((prev) => ({
|
|
585
|
+
...prev,
|
|
586
|
+
phase: "error",
|
|
587
|
+
error: /* @__PURE__ */ new Error("No sendTransactionAsync provided. Pass it from wagmi useSendTransaction().")
|
|
588
|
+
}));
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
try {
|
|
592
|
+
setState((prev) => ({
|
|
593
|
+
...prev,
|
|
594
|
+
phase: "sending"
|
|
595
|
+
}));
|
|
596
|
+
const tx = state.quote.quote.transactionRequest;
|
|
597
|
+
const hash = await sendFn({
|
|
598
|
+
to: tx.to,
|
|
599
|
+
data: tx.data,
|
|
600
|
+
value: BigInt(tx.value),
|
|
601
|
+
chainId: tx.chainId
|
|
602
|
+
});
|
|
603
|
+
setState((prev) => ({
|
|
604
|
+
...prev,
|
|
605
|
+
phase: "success",
|
|
606
|
+
txHash: hash
|
|
607
|
+
}));
|
|
608
|
+
} catch (err) {
|
|
609
|
+
setState((prev) => ({
|
|
610
|
+
...prev,
|
|
611
|
+
phase: "error",
|
|
612
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
613
|
+
}));
|
|
614
|
+
}
|
|
615
|
+
}, [
|
|
616
|
+
state.phase,
|
|
617
|
+
state.quote,
|
|
618
|
+
params.sendTransactionAsync
|
|
619
|
+
]),
|
|
620
|
+
reset: useCallback(() => {
|
|
621
|
+
abortRef.current = true;
|
|
622
|
+
setState(INITIAL_STATE);
|
|
623
|
+
}, [])
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
//#endregion
|
|
627
|
+
export { EarnForgeContext, EarnForgeProvider, useApyHistory, useEarnDeposit, useEarnForge, useEarnRedeem, useEarnTopYield, usePortfolio, useRiskScore, useStrategy, useSuggest, useVault, useVaults };
|
|
628
|
+
|
|
629
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["INITIAL_STATE"],"sources":["../../src/context.ts","../../src/hooks/useVaults.ts","../../src/hooks/useVault.ts","../../src/hooks/useEarnTopYield.ts","../../src/hooks/usePortfolio.ts","../../src/hooks/useRiskScore.ts","../../src/hooks/useStrategy.ts","../../src/hooks/useSuggest.ts","../../src/hooks/useApyHistory.ts","../../src/hooks/useEarnDeposit.ts","../../src/hooks/useEarnRedeem.ts"],"sourcesContent":["// SPDX-License-Identifier: Apache-2.0\nimport { createContext, useContext, createElement, type ReactNode } from 'react';\nimport type { EarnForge } from '@earnforge/sdk';\n\nconst EarnForgeContext = createContext<EarnForge | null>(null);\n\nexport interface EarnForgeProviderProps {\n sdk: EarnForge;\n children: ReactNode;\n}\n\n/**\n * Wrap your application with `EarnForgeProvider` to make the SDK\n * instance available to all hooks via React context.\n *\n * ```tsx\n * const forge = createEarnForge({ composerApiKey: '...' });\n * <EarnForgeProvider sdk={forge}>\n * <App />\n * </EarnForgeProvider>\n * ```\n */\nexport function EarnForgeProvider({ sdk, children }: EarnForgeProviderProps) {\n return createElement(EarnForgeContext.Provider, { value: sdk }, children);\n}\n\n/**\n * Internal helper — returns the SDK instance from context\n * or throws if used outside an `EarnForgeProvider`.\n */\nexport function useEarnForge(): EarnForge {\n const ctx = useContext(EarnForgeContext);\n if (!ctx) {\n throw new Error(\n 'useEarnForge must be used within an <EarnForgeProvider>. ' +\n 'Wrap your component tree with <EarnForgeProvider sdk={forge}>.',\n );\n }\n return ctx;\n}\n\nexport { EarnForgeContext };\n","// SPDX-License-Identifier: Apache-2.0\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\nimport type { Vault, VaultListResponse } from '@earnforge/sdk';\nimport type { StrategyPreset } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseVaultsParams {\n chainId?: number;\n asset?: string;\n minTvl?: number;\n sortBy?: string;\n limit?: number;\n strategy?: StrategyPreset;\n}\n\nexport interface UseVaultsReturn {\n data: Vault[] | undefined;\n isLoading: boolean;\n error: Error | null;\n fetchMore: () => void;\n hasMore: boolean;\n}\n\n/**\n * Fetch a paginated list of vaults with optional filters.\n * Resets accumulated data when params change.\n *\n * ```tsx\n * const { data, isLoading, fetchMore, hasMore } = useVaults({ chainId: 8453 });\n * ```\n */\nexport function useVaults(params: UseVaultsParams = {}): UseVaultsReturn {\n const sdk = useEarnForge();\n const queryClient = useQueryClient();\n const cursorRef = useRef<string | null>(null);\n const accumulatedRef = useRef<Vault[]>([]);\n\n // Stable serialized params key for comparison\n const paramsKey = useMemo(\n () => JSON.stringify({ chainId: params.chainId, asset: params.asset, minTvl: params.minTvl, sortBy: params.sortBy, strategy: params.strategy }),\n [params.chainId, params.asset, params.minTvl, params.sortBy, params.strategy],\n );\n\n // Reset accumulated data and cursor when params change\n useEffect(() => {\n cursorRef.current = null;\n accumulatedRef.current = [];\n }, [paramsKey]);\n\n const queryKey = useMemo(\n () => ['earnforge', 'vaults', paramsKey, cursorRef.current] as const,\n [paramsKey],\n );\n\n const query = useQuery<VaultListResponse, Error>({\n queryKey,\n queryFn: async () => {\n const result = await sdk.vaults.list({\n chainId: params.chainId,\n asset: params.asset,\n minTvl: params.minTvl,\n sortBy: params.sortBy,\n strategy: params.strategy,\n cursor: cursorRef.current ?? undefined,\n });\n cursorRef.current = result.nextCursor;\n\n if (accumulatedRef.current.length === 0) {\n accumulatedRef.current = result.data;\n } else {\n accumulatedRef.current = [...accumulatedRef.current, ...result.data];\n }\n\n return {\n ...result,\n data: params.limit\n ? accumulatedRef.current.slice(0, params.limit)\n : accumulatedRef.current,\n };\n },\n });\n\n const fetchMore = useCallback(() => {\n if (cursorRef.current) {\n queryClient.invalidateQueries({ queryKey });\n }\n }, [queryClient, queryKey]);\n\n return {\n data: query.data?.data,\n isLoading: query.isLoading,\n error: query.error,\n fetchMore,\n hasMore: cursorRef.current !== null,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useQuery } from '@tanstack/react-query';\nimport type { Vault } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseVaultReturn {\n data: Vault | undefined;\n isLoading: boolean;\n error: Error | null;\n}\n\n/**\n * Fetch a single vault by its slug.\n *\n * ```tsx\n * const { data: vault, isLoading } = useVault('aave-v3-usdc-base');\n * ```\n */\nexport function useVault(slug: string | undefined): UseVaultReturn {\n const sdk = useEarnForge();\n\n const query = useQuery<Vault, Error>({\n queryKey: ['earnforge', 'vault', slug],\n queryFn: () => sdk.vaults.get(slug!),\n enabled: !!slug,\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading,\n error: query.error,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useQuery } from '@tanstack/react-query';\nimport type { Vault } from '@earnforge/sdk';\nimport type { StrategyPreset } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseEarnTopYieldParams {\n asset?: string;\n chainId?: number;\n limit?: number;\n strategy?: StrategyPreset;\n minTvl?: number;\n}\n\nexport interface UseEarnTopYieldReturn {\n data: Vault[] | undefined;\n isLoading: boolean;\n error: Error | null;\n}\n\n/**\n * Fetch top-yielding vaults sorted by APY.\n *\n * ```tsx\n * const { data } = useEarnTopYield({ asset: 'USDC', limit: 5 });\n * ```\n */\nexport function useEarnTopYield(params: UseEarnTopYieldParams = {}): UseEarnTopYieldReturn {\n const sdk = useEarnForge();\n\n const query = useQuery<Vault[], Error>({\n queryKey: ['earnforge', 'topYield', params],\n queryFn: () =>\n sdk.vaults.top({\n asset: params.asset,\n chainId: params.chainId,\n limit: params.limit,\n strategy: params.strategy,\n minTvl: params.minTvl,\n }),\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading,\n error: query.error,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useQuery } from '@tanstack/react-query';\nimport type { PortfolioResponse } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UsePortfolioReturn {\n data: PortfolioResponse | undefined;\n isLoading: boolean;\n error: Error | null;\n}\n\n/**\n * Fetch portfolio positions for a wallet address.\n *\n * ```tsx\n * const { data } = usePortfolio('0xabc...');\n * ```\n */\nexport function usePortfolio(wallet: string | undefined): UsePortfolioReturn {\n const sdk = useEarnForge();\n\n const query = useQuery<PortfolioResponse, Error>({\n queryKey: ['earnforge', 'portfolio', wallet],\n queryFn: () => sdk.portfolio.get(wallet!),\n enabled: !!wallet,\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading,\n error: query.error,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useMemo } from 'react';\nimport type { Vault, RiskScore } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseRiskScoreReturn {\n data: RiskScore | undefined;\n}\n\n/**\n * Compute a risk score for a vault. This is a synchronous computation\n * wrapped in `useMemo` for referential stability.\n *\n * ```tsx\n * const { data: risk } = useRiskScore(vault);\n * // risk.score, risk.label, risk.breakdown\n * ```\n */\nexport function useRiskScore(vault: Vault | undefined): UseRiskScoreReturn {\n const sdk = useEarnForge();\n\n const data = useMemo(() => {\n if (!vault) return undefined;\n return sdk.riskScore(vault);\n }, [sdk, vault]);\n\n return { data };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useMemo } from 'react';\nimport { getStrategy, type StrategyPreset, type StrategyConfig } from '@earnforge/sdk';\n\nexport interface UseStrategyReturn {\n data: StrategyConfig | undefined;\n filters: StrategyConfig['filters'] | undefined;\n sort: StrategyConfig['sort'] | undefined;\n sortDirection: StrategyConfig['sortDirection'] | undefined;\n}\n\n/**\n * Resolve a strategy preset into its filter configuration.\n * Use the returned `filters` to pass into `useVaults` or `useEarnTopYield`.\n *\n * ```tsx\n * const { filters } = useStrategy('conservative');\n * const { data } = useVaults({ ...filters });\n * ```\n */\nexport function useStrategy(preset: StrategyPreset | undefined): UseStrategyReturn {\n const data = useMemo(() => {\n if (!preset) return undefined;\n return getStrategy(preset);\n }, [preset]);\n\n return {\n data,\n filters: data?.filters,\n sort: data?.sort,\n sortDirection: data?.sortDirection,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useQuery } from '@tanstack/react-query';\nimport type { SuggestResult, StrategyPreset } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseSuggestParams {\n amount: number;\n asset?: string;\n maxChains?: number;\n strategy?: StrategyPreset;\n}\n\nexport interface UseSuggestReturn {\n data: SuggestResult | undefined;\n isLoading: boolean;\n error: Error | null;\n}\n\n/**\n * Get a suggested portfolio allocation based on the given parameters.\n *\n * ```tsx\n * const { data } = useSuggest({ amount: 10_000, asset: 'USDC' });\n * // data.allocations — the recommended split\n * ```\n */\nexport function useSuggest(params: UseSuggestParams | undefined): UseSuggestReturn {\n const sdk = useEarnForge();\n\n const query = useQuery<SuggestResult, Error>({\n queryKey: ['earnforge', 'suggest', params],\n queryFn: () =>\n sdk.suggest({\n amount: params!.amount,\n asset: params!.asset,\n maxChains: params!.maxChains,\n strategy: params!.strategy,\n }),\n enabled: !!params && params.amount > 0,\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading,\n error: query.error,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useQuery } from '@tanstack/react-query';\nimport type { ApyDataPoint, Vault } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\nexport interface UseApyHistoryReturn {\n data: ApyDataPoint[] | undefined;\n isLoading: boolean;\n error: Error | null;\n}\n\n/**\n * Fetch 30-day APY history for a vault from DeFiLlama.\n *\n * Accepts a full Vault object for accurate matching (uses protocol name,\n * chain, underlying tokens, symbol, and TVL proximity). Falls back to\n * the less accurate address+chainId matching when only primitives are given.\n *\n * ```tsx\n * // Preferred: pass the full vault object for accurate DeFiLlama matching\n * const { data: history } = useApyHistory(vault);\n *\n * // Legacy: address + chainId (less accurate matching)\n * const { data: history } = useApyHistory('0xabc...', 8453);\n * ```\n */\nexport function useApyHistory(vault: Vault | undefined): UseApyHistoryReturn;\nexport function useApyHistory(vaultAddress: string | undefined, chainId: number | undefined): UseApyHistoryReturn;\nexport function useApyHistory(\n vaultOrAddress: Vault | string | undefined,\n chainId?: number | undefined,\n): UseApyHistoryReturn {\n const sdk = useEarnForge();\n\n // Determine if we received a Vault object or address+chainId\n const isVaultObject = typeof vaultOrAddress === 'object' && vaultOrAddress !== null;\n const vault = isVaultObject ? (vaultOrAddress as Vault) : undefined;\n const address = typeof vaultOrAddress === 'string' ? vaultOrAddress : undefined;\n\n const enabled = isVaultObject\n ? !!vault\n : !!address && !!chainId;\n\n // Use vault slug + chainId as cache key for vault objects, address + chainId for legacy\n const queryKey = isVaultObject\n ? ['earnforge', 'apyHistory', vault?.slug, vault?.chainId]\n : ['earnforge', 'apyHistory', address, chainId];\n\n const query = useQuery<ApyDataPoint[], Error>({\n queryKey,\n queryFn: () => {\n if (vault) {\n // Full vault object — accurate DeFiLlama matching via protocol+chain+tokens\n return sdk.getApyHistory(vault);\n }\n // Legacy: address + chainId\n return sdk.getApyHistory(address!, chainId!);\n },\n enabled,\n });\n\n return {\n data: query.data,\n isLoading: query.isLoading,\n error: query.error,\n };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useState, useCallback, useRef } from 'react';\nimport type {\n Vault,\n PreflightReport,\n DepositQuoteResult,\n AllowanceResult,\n ApprovalTx,\n} from '@earnforge/sdk';\nimport { checkAllowance, buildApprovalTx, MAX_UINT256, toSmallestUnit } from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\n/**\n * Deposit state machine:\n *\n * idle --> preflight --> checking-allowance --> approving --> quoting --> ready --> sending --> success\n * \\ | | | | | |\n * \\________|_______________|___________________|____________|__________|__________|--> error\n *\n * The \"checking-allowance\" phase verifies the ERC-20 allowance for the fromToken.\n * If allowance is insufficient, \"approving\" sends an approval tx before quoting.\n */\nexport type DepositPhase =\n | 'idle'\n | 'preflight'\n | 'checking-allowance'\n | 'approving'\n | 'quoting'\n | 'ready'\n | 'sending'\n | 'success'\n | 'error';\n\nexport interface DepositState {\n phase: DepositPhase;\n preflightReport: PreflightReport | null;\n allowance: AllowanceResult | null;\n approvalTx: ApprovalTx | null;\n quote: DepositQuoteResult | null;\n txHash: string | null;\n error: Error | null;\n}\n\nexport interface UseEarnDepositParams {\n vault: Vault | undefined;\n amount: string;\n wallet: string;\n fromToken?: string;\n fromChain?: number;\n slippage?: number;\n /** JSON-RPC URL for the source chain — needed for allowance checking */\n rpcUrl?: string;\n /** wagmi's sendTransactionAsync function — pass from useSendTransaction() */\n sendTransactionAsync?: (params: { to: `0x${string}`; data: `0x${string}`; value: bigint; chainId: number }) => Promise<`0x${string}`>;\n}\n\nexport interface UseEarnDepositReturn {\n state: DepositState;\n /** Kick off the preflight -> quote -> ready flow */\n prepare: () => Promise<void>;\n /** Execute the deposit transaction (requires sendTransactionAsync or sendTransaction) */\n execute: () => Promise<void>;\n /** Reset back to idle */\n reset: () => void;\n}\n\nconst INITIAL_STATE: DepositState = {\n phase: 'idle',\n preflightReport: null,\n allowance: null,\n approvalTx: null,\n quote: null,\n txHash: null,\n error: null,\n};\n\n/**\n * Deposit state machine hook.\n *\n * Flow: idle -> preflight -> quoting -> ready\n * Then call `execute()` to send: ready -> sending -> success\n *\n * ```tsx\n * const { state, prepare, execute, reset } = useEarnDeposit({\n * vault,\n * amount: '100',\n * wallet: address,\n * sendTransactionAsync,\n * });\n * ```\n */\nexport function useEarnDeposit(params: UseEarnDepositParams): UseEarnDepositReturn {\n const sdk = useEarnForge();\n const [state, setState] = useState<DepositState>(INITIAL_STATE);\n const abortRef = useRef(false);\n\n const prepare = useCallback(async () => {\n if (!params.vault || !params.wallet || !params.amount) {\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n error: new Error('Missing vault, wallet, or amount'),\n });\n return;\n }\n\n abortRef.current = false;\n\n try {\n // Phase: preflight\n setState({ ...INITIAL_STATE, phase: 'preflight' });\n\n const report = sdk.preflight(params.vault, params.wallet);\n if (abortRef.current) return;\n\n if (!report.ok) {\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n preflightReport: report,\n error: new Error(\n `Preflight failed: ${report.issues.map((i) => i.message).join('; ')}`,\n ),\n });\n return;\n }\n\n // Phase: checking-allowance (if rpcUrl and fromToken are provided)\n const fromToken = params.fromToken ?? params.vault.underlyingTokens[0]?.address;\n let allowanceResult: AllowanceResult | null = null;\n let approval: ApprovalTx | null = null;\n\n if (params.rpcUrl && fromToken) {\n setState({\n ...INITIAL_STATE,\n phase: 'checking-allowance',\n preflightReport: report,\n });\n\n const decimals = params.vault.underlyingTokens[0]?.decimals ?? 18;\n const requiredAmount = BigInt(toSmallestUnit(params.amount, decimals));\n\n // Spender = vault address (Composer routes through vault contract)\n allowanceResult = await checkAllowance(\n params.rpcUrl,\n fromToken,\n params.wallet,\n params.vault.address,\n requiredAmount,\n );\n if (abortRef.current) return;\n\n // If allowance insufficient, build approval tx and wait for it\n if (!allowanceResult.sufficient) {\n approval = buildApprovalTx(\n fromToken,\n params.vault.address,\n MAX_UINT256,\n params.vault.chainId,\n );\n\n setState({\n ...INITIAL_STATE,\n phase: 'approving',\n preflightReport: report,\n allowance: allowanceResult,\n approvalTx: approval,\n });\n\n // Send approval tx if sendTransactionAsync is available\n const sendFn = params.sendTransactionAsync;\n if (sendFn) {\n await sendFn({\n to: approval.to as `0x${string}`,\n data: approval.data as `0x${string}`,\n value: 0n,\n chainId: approval.chainId,\n });\n if (abortRef.current) return;\n } else {\n // Cannot auto-approve without sendTransactionAsync — expose the tx for manual sending\n // The caller should check state.approvalTx and handle it\n return;\n }\n }\n }\n\n // Phase: quoting\n setState({\n ...INITIAL_STATE,\n phase: 'quoting',\n preflightReport: report,\n allowance: allowanceResult,\n approvalTx: approval,\n });\n\n const quote = await sdk.buildDepositQuote(params.vault, {\n fromAmount: params.amount,\n wallet: params.wallet,\n fromToken: params.fromToken,\n fromChain: params.fromChain,\n slippage: params.slippage,\n });\n if (abortRef.current) return;\n\n // Phase: ready\n setState({\n ...INITIAL_STATE,\n phase: 'ready',\n preflightReport: report,\n allowance: allowanceResult,\n approvalTx: approval,\n quote,\n });\n } catch (err) {\n if (abortRef.current) return;\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, [sdk, params.vault, params.wallet, params.amount, params.fromToken, params.fromChain, params.slippage, params.rpcUrl, params.sendTransactionAsync]);\n\n const execute = useCallback(async () => {\n if (state.phase !== 'ready' || !state.quote) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: new Error('Cannot execute: not in ready state. Call prepare() first.'),\n }));\n return;\n }\n\n const sendFn = params.sendTransactionAsync;\n if (!sendFn) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: new Error('No sendTransactionAsync provided. Pass it from wagmi useSendTransaction().'),\n }));\n return;\n }\n\n try {\n setState((prev) => ({ ...prev, phase: 'sending' }));\n\n const tx = state.quote.quote.transactionRequest;\n const hash = await sendFn({\n to: tx.to as `0x${string}`,\n data: tx.data as `0x${string}`,\n value: BigInt(tx.value),\n chainId: tx.chainId,\n });\n\n setState((prev) => ({\n ...prev,\n phase: 'success',\n txHash: hash,\n }));\n } catch (err) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: err instanceof Error ? err : new Error(String(err)),\n }));\n }\n }, [state.phase, state.quote, params.sendTransactionAsync]);\n\n const reset = useCallback(() => {\n abortRef.current = true;\n setState(INITIAL_STATE);\n }, []);\n\n return { state, prepare, execute, reset };\n}\n","// SPDX-License-Identifier: Apache-2.0\nimport { useState, useCallback, useRef } from 'react';\nimport type {\n Vault,\n PreflightReport,\n RedeemQuoteResult,\n} from '@earnforge/sdk';\nimport { useEarnForge } from '../context.js';\n\n/**\n * Redeem state machine:\n *\n * idle --> preflight --> quoting --> ready --> sending --> success\n * \\ | | | |\n * \\__________|______________|____________|____________|--> error\n */\nexport type RedeemPhase =\n | 'idle'\n | 'preflight'\n | 'quoting'\n | 'ready'\n | 'sending'\n | 'success'\n | 'error';\n\nexport interface RedeemState {\n phase: RedeemPhase;\n preflightReport: PreflightReport | null;\n quote: RedeemQuoteResult | null;\n txHash: string | null;\n error: Error | null;\n}\n\nexport interface UseEarnRedeemParams {\n vault: Vault | undefined;\n /** Amount of vault share tokens to redeem (human-readable) */\n amount: string;\n wallet: string;\n /** Token to receive. Defaults to underlying token on vault chain. */\n toToken?: string;\n /** Destination chain. Defaults to vault chain. */\n toChain?: number;\n slippage?: number;\n /** wagmi's sendTransactionAsync function — pass from useSendTransaction() */\n sendTransactionAsync?: (params: { to: `0x${string}`; data: `0x${string}`; value: bigint; chainId: number }) => Promise<`0x${string}`>;\n}\n\nexport interface UseEarnRedeemReturn {\n state: RedeemState;\n /** Kick off the preflight -> quote -> ready flow */\n prepare: () => Promise<void>;\n /** Execute the redeem transaction (requires sendTransactionAsync) */\n execute: () => Promise<void>;\n /** Reset back to idle */\n reset: () => void;\n}\n\nconst INITIAL_STATE: RedeemState = {\n phase: 'idle',\n preflightReport: null,\n quote: null,\n txHash: null,\n error: null,\n};\n\n/**\n * Withdrawal/redeem state machine hook.\n *\n * Flow: idle -> preflight -> quoting -> ready\n * Then call `execute()` to send: ready -> sending -> success\n *\n * ```tsx\n * const { state, prepare, execute, reset } = useEarnRedeem({\n * vault,\n * amount: '100',\n * wallet: address,\n * sendTransactionAsync,\n * });\n * ```\n */\nexport function useEarnRedeem(params: UseEarnRedeemParams): UseEarnRedeemReturn {\n const sdk = useEarnForge();\n const [state, setState] = useState<RedeemState>(INITIAL_STATE);\n const abortRef = useRef(false);\n\n const prepare = useCallback(async () => {\n if (!params.vault || !params.wallet || !params.amount) {\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n error: new Error('Missing vault, wallet, or amount'),\n });\n return;\n }\n\n if (!params.vault.isRedeemable) {\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n error: new Error(`Vault ${params.vault.slug} is not redeemable — withdrawals are not supported.`),\n });\n return;\n }\n\n abortRef.current = false;\n\n try {\n // Phase: preflight\n setState({ ...INITIAL_STATE, phase: 'preflight' });\n\n const report = sdk.preflight(params.vault, params.wallet);\n if (abortRef.current) return;\n\n if (!report.ok) {\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n preflightReport: report,\n error: new Error(\n `Preflight failed: ${report.issues.map((i) => i.message).join('; ')}`,\n ),\n });\n return;\n }\n\n // Phase: quoting\n setState({\n ...INITIAL_STATE,\n phase: 'quoting',\n preflightReport: report,\n });\n\n const quote = await sdk.buildRedeemQuote(params.vault, {\n fromAmount: params.amount,\n wallet: params.wallet,\n toToken: params.toToken,\n toChain: params.toChain,\n slippage: params.slippage,\n });\n if (abortRef.current) return;\n\n // Phase: ready\n setState({\n ...INITIAL_STATE,\n phase: 'ready',\n preflightReport: report,\n quote,\n });\n } catch (err) {\n if (abortRef.current) return;\n setState({\n ...INITIAL_STATE,\n phase: 'error',\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, [sdk, params.vault, params.wallet, params.amount, params.toToken, params.toChain, params.slippage]);\n\n const execute = useCallback(async () => {\n if (state.phase !== 'ready' || !state.quote) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: new Error('Cannot execute: not in ready state. Call prepare() first.'),\n }));\n return;\n }\n\n const sendFn = params.sendTransactionAsync;\n if (!sendFn) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: new Error('No sendTransactionAsync provided. Pass it from wagmi useSendTransaction().'),\n }));\n return;\n }\n\n try {\n setState((prev) => ({ ...prev, phase: 'sending' }));\n\n const tx = state.quote.quote.transactionRequest;\n const hash = await sendFn({\n to: tx.to as `0x${string}`,\n data: tx.data as `0x${string}`,\n value: BigInt(tx.value),\n chainId: tx.chainId,\n });\n\n setState((prev) => ({\n ...prev,\n phase: 'success',\n txHash: hash,\n }));\n } catch (err) {\n setState((prev) => ({\n ...prev,\n phase: 'error',\n error: err instanceof Error ? err : new Error(String(err)),\n }));\n }\n }, [state.phase, state.quote, params.sendTransactionAsync]);\n\n const reset = useCallback(() => {\n abortRef.current = true;\n setState(INITIAL_STATE);\n }, []);\n\n return { state, prepare, execute, reset };\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@earnforge/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"description": "React hooks for the LI.FI Earn API — vault discovery, risk scoring, yield strategies, and deposits",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/esm/index.js",
|
|
10
|
+
"types": "./dist/esm/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsdown src/index.ts --format esm --dts --out-dir dist/esm",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:unit": "vitest run",
|
|
19
|
+
"clean": "rm -rf dist .turbo"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@earnforge/sdk": "workspace:*"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@tanstack/react-query": ">=5.90.0",
|
|
26
|
+
"react": ">=18",
|
|
27
|
+
"viem": "^2.47.11",
|
|
28
|
+
"wagmi": ">=2.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@tanstack/react-query": "^5.90.0",
|
|
32
|
+
"@testing-library/react": "^16.3.0",
|
|
33
|
+
"jsdom": "^26.1.0",
|
|
34
|
+
"react": "^19.2.5",
|
|
35
|
+
"react-dom": "^19.2.5",
|
|
36
|
+
"tsdown": "^0.21.7",
|
|
37
|
+
"typescript": "^5.9.3",
|
|
38
|
+
"vitest": "^4.1.4"
|
|
39
|
+
}
|
|
40
|
+
}
|