@oydual31/more-vaults-sdk 0.2.0 → 0.2.1
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/README.md +163 -75
- package/dist/ethers/index.cjs +62 -0
- package/dist/ethers/index.cjs.map +1 -1
- package/dist/ethers/index.d.cts +55 -1
- package/dist/ethers/index.d.ts +55 -1
- package/dist/ethers/index.js +60 -1
- package/dist/ethers/index.js.map +1 -1
- package/dist/react/index.cjs +22 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +42 -1
- package/dist/react/index.d.ts +42 -1
- package/dist/react/index.js +22 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ethers/chains.ts +19 -0
- package/src/ethers/index.ts +3 -1
- package/src/ethers/redeemFlows.ts +109 -0
- package/src/react/index.ts +1 -0
- package/src/react/useSmartRedeem.ts +70 -0
package/package.json
CHANGED
package/src/ethers/chains.ts
CHANGED
|
@@ -37,3 +37,22 @@ export const CHAIN_ID_TO_EID: Record<number, number> = {
|
|
|
37
37
|
[CHAIN_IDS.base]: LZ_EIDS.base,
|
|
38
38
|
[CHAIN_IDS.ethereum]: LZ_EIDS.ethereum,
|
|
39
39
|
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Recommended timeouts for cross-chain operations (milliseconds).
|
|
43
|
+
* UIs should show a progress indicator and NOT timeout before these values.
|
|
44
|
+
*/
|
|
45
|
+
export const LZ_TIMEOUTS = {
|
|
46
|
+
/** Poll interval between balance/event checks */
|
|
47
|
+
POLL_INTERVAL: 30_000,
|
|
48
|
+
/** Standard OFT bridge (shares or assets, non-Stargate) */
|
|
49
|
+
OFT_BRIDGE: 900_000, // 15 min
|
|
50
|
+
/** Stargate bridge (USDC, USDT, WETH) — slower due to pool mechanics */
|
|
51
|
+
STARGATE_BRIDGE: 1_800_000, // 30 min
|
|
52
|
+
/** LZ Read callback (async vault actions) */
|
|
53
|
+
LZ_READ_CALLBACK: 900_000, // 15 min
|
|
54
|
+
/** Compose delivery to hub (deposit from spoke) */
|
|
55
|
+
COMPOSE_DELIVERY: 2_700_000, // 45 min
|
|
56
|
+
/** Full spoke→hub→spoke redeem (all steps combined) */
|
|
57
|
+
FULL_SPOKE_REDEEM: 3_600_000, // 60 min
|
|
58
|
+
} as const;
|
package/src/ethers/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Barrel export for all flows and utilities.
|
|
3
3
|
|
|
4
4
|
// --- Chain constants ---
|
|
5
|
-
export { CHAIN_IDS, LZ_EIDS, EID_TO_CHAIN_ID, CHAIN_ID_TO_EID } from "./chains";
|
|
5
|
+
export { CHAIN_IDS, LZ_EIDS, EID_TO_CHAIN_ID, CHAIN_ID_TO_EID, LZ_TIMEOUTS } from "./chains";
|
|
6
6
|
|
|
7
7
|
// --- Types ---
|
|
8
8
|
export type {
|
|
@@ -69,7 +69,9 @@ export {
|
|
|
69
69
|
requestRedeem,
|
|
70
70
|
getWithdrawalRequest,
|
|
71
71
|
redeemAsync,
|
|
72
|
+
smartRedeem,
|
|
72
73
|
bridgeSharesToHub,
|
|
74
|
+
bridgeAssetsToSpoke,
|
|
73
75
|
} from "./redeemFlows";
|
|
74
76
|
|
|
75
77
|
// --- Utilities ---
|
|
@@ -15,6 +15,7 @@ import type { ContractTransactionReceipt } from "ethers";
|
|
|
15
15
|
import { preflightAsync, preflightRedeemLiquidity } from "./preflight";
|
|
16
16
|
import { EscrowNotConfiguredError } from "./errors";
|
|
17
17
|
import { validateWalletChain } from "./chainValidation";
|
|
18
|
+
import { getVaultStatus, quoteLzFee } from "./utils";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Ensure `spender` has at least `amount` allowance from `owner`.
|
|
@@ -298,3 +299,111 @@ export async function bridgeSharesToHub(
|
|
|
298
299
|
|
|
299
300
|
return { receipt };
|
|
300
301
|
}
|
|
302
|
+
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Smart redeem -- auto-detect vault type
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Smart redeem — auto-selects the correct flow based on vault configuration.
|
|
309
|
+
*
|
|
310
|
+
* Detects the vault mode and dispatches to:
|
|
311
|
+
* - Sync vaults (local / cross-chain-oracle): `redeemShares`
|
|
312
|
+
* - Async vaults (cross-chain, oracle OFF): `redeemAsync` (quotes LZ fee automatically)
|
|
313
|
+
*
|
|
314
|
+
* @param signer Wallet signer with account attached
|
|
315
|
+
* @param addresses Vault address set (`escrow` required for async vaults)
|
|
316
|
+
* @param shares Amount of shares to redeem
|
|
317
|
+
* @param receiver Address that will receive the underlying assets
|
|
318
|
+
* @param owner Owner of the shares being redeemed
|
|
319
|
+
* @param extraOptions Optional LZ extra options (only used for async vaults)
|
|
320
|
+
* @returns RedeemResult or AsyncRequestResult depending on vault mode
|
|
321
|
+
*/
|
|
322
|
+
export async function smartRedeem(
|
|
323
|
+
signer: Signer,
|
|
324
|
+
addresses: VaultAddresses,
|
|
325
|
+
shares: bigint,
|
|
326
|
+
receiver: string,
|
|
327
|
+
owner: string,
|
|
328
|
+
extraOptions: string = "0x"
|
|
329
|
+
): Promise<RedeemResult | AsyncRequestResult> {
|
|
330
|
+
const provider = signer.provider!;
|
|
331
|
+
const vault = addresses.vault;
|
|
332
|
+
const status = await getVaultStatus(provider, vault);
|
|
333
|
+
|
|
334
|
+
if (status.mode === "paused") {
|
|
335
|
+
throw new Error(`[MoreVaults] Vault ${vault} is paused. Cannot redeem.`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (status.recommendedDepositFlow === "depositAsync") {
|
|
339
|
+
// Async vault — use redeemAsync
|
|
340
|
+
const lzFee = await quoteLzFee(provider, vault, extraOptions);
|
|
341
|
+
return redeemAsync(signer, addresses, shares, receiver, owner, lzFee, extraOptions);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Sync vault — direct redeem
|
|
345
|
+
return redeemShares(signer, addresses, shares, receiver, owner);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
// R7 -- Bridge assets from hub back to spoke
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Bridge underlying assets from hub back to spoke chain via OFT.
|
|
354
|
+
*
|
|
355
|
+
* Step 3 of the full spoke redeem flow:
|
|
356
|
+
* 1. bridgeSharesToHub() — shares spoke->hub
|
|
357
|
+
* 2. smartRedeem() — redeem on hub
|
|
358
|
+
* 3. bridgeAssetsToSpoke() — assets hub->spoke
|
|
359
|
+
*
|
|
360
|
+
* @param signer Wallet signer on the HUB chain
|
|
361
|
+
* @param assetOFT OFT address for the underlying asset on hub
|
|
362
|
+
* @param spokeChainEid LayerZero EID for the spoke (destination) chain
|
|
363
|
+
* @param amount Amount of underlying assets to bridge
|
|
364
|
+
* @param receiver Receiver address on the spoke chain
|
|
365
|
+
* @param lzFee OFT send fee (quote via OFT.quoteSend)
|
|
366
|
+
* @param isStargate Whether this is a Stargate OFT (uses TAXI mode)
|
|
367
|
+
* @returns Transaction receipt
|
|
368
|
+
*/
|
|
369
|
+
export async function bridgeAssetsToSpoke(
|
|
370
|
+
signer: Signer,
|
|
371
|
+
assetOFT: string,
|
|
372
|
+
spokeChainEid: number,
|
|
373
|
+
amount: bigint,
|
|
374
|
+
receiver: string,
|
|
375
|
+
lzFee: bigint,
|
|
376
|
+
isStargate: boolean = true
|
|
377
|
+
): Promise<{ receipt: ContractTransactionReceipt }> {
|
|
378
|
+
const oft = new Contract(assetOFT, OFT_ABI, signer);
|
|
379
|
+
|
|
380
|
+
// Read underlying token and approve
|
|
381
|
+
const token: string = await oft.token();
|
|
382
|
+
if (token.toLowerCase() !== assetOFT.toLowerCase()) {
|
|
383
|
+
await ensureAllowance(signer, token, assetOFT, amount);
|
|
384
|
+
} else {
|
|
385
|
+
await ensureAllowance(signer, assetOFT, assetOFT, amount);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const refundAddress = await signer.getAddress();
|
|
389
|
+
const toBytes32 = zeroPadValue(receiver, 32);
|
|
390
|
+
|
|
391
|
+
const sendParam = {
|
|
392
|
+
dstEid: spokeChainEid,
|
|
393
|
+
to: toBytes32,
|
|
394
|
+
amountLD: amount,
|
|
395
|
+
minAmountLD: amount * 99n / 100n, // 1% slippage for Stargate
|
|
396
|
+
extraOptions: "0x",
|
|
397
|
+
composeMsg: "0x",
|
|
398
|
+
oftCmd: isStargate ? "0x01" : "0x",
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const msgFee = { nativeFee: lzFee, lzTokenFee: 0n };
|
|
402
|
+
|
|
403
|
+
const tx = await oft.send(sendParam, msgFee, refundAddress, {
|
|
404
|
+
value: lzFee,
|
|
405
|
+
});
|
|
406
|
+
const receipt = await tx.wait();
|
|
407
|
+
|
|
408
|
+
return { receipt };
|
|
409
|
+
}
|
package/src/react/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { useVaultDistribution } from './useVaultDistribution.js'
|
|
|
30
30
|
|
|
31
31
|
// --- Smart (auto-routing) hooks ---
|
|
32
32
|
export { useSmartDeposit } from './useSmartDeposit.js'
|
|
33
|
+
export { useSmartRedeem } from './useSmartRedeem.js'
|
|
33
34
|
|
|
34
35
|
// --- Inbound Routes ---
|
|
35
36
|
export { useInboundRoutes, getRouteTokenDecimals } from './useInboundRoutes.js'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { AsyncRequestStatusInfo } from '../viem/index.js'
|
|
2
|
+
import { useVaultStatus } from './useVaultStatus.js'
|
|
3
|
+
import { useOmniRedeem } from './useOmniRedeem.js'
|
|
4
|
+
import { useRedeemShares } from './useRedeemShares.js'
|
|
5
|
+
|
|
6
|
+
interface UseSmartRedeemReturn {
|
|
7
|
+
/**
|
|
8
|
+
* Execute redeem using the correct flow for this vault's mode.
|
|
9
|
+
* For async vaults: wraps redeemAsync (R5) — returns guid for tracking.
|
|
10
|
+
* For local/oracle vaults: wraps redeemShares (R1) — returns assets.
|
|
11
|
+
*/
|
|
12
|
+
redeem: (sharesInWei: bigint, receiver: `0x${string}`, owner: `0x${string}`) => Promise<void>
|
|
13
|
+
isLoading: boolean
|
|
14
|
+
txHash: `0x${string}` | undefined
|
|
15
|
+
/** Assets received (available for R1 vaults after confirmation, undefined for R5). */
|
|
16
|
+
assets: bigint | undefined
|
|
17
|
+
/** GUID for cross-chain tracking (R5 vaults only). */
|
|
18
|
+
guid: `0x${string}` | undefined
|
|
19
|
+
/** Cross-chain request status (R5 vaults only). */
|
|
20
|
+
requestStatus: AsyncRequestStatusInfo | undefined
|
|
21
|
+
/** true when the wallet is connected to the wrong chain (R5 vaults only). */
|
|
22
|
+
wrongChain: boolean
|
|
23
|
+
/** Vault mode loaded from getVaultStatus. undefined while loading. */
|
|
24
|
+
vaultMode: 'local' | 'cross-chain-oracle' | 'cross-chain-async' | 'paused' | 'full' | undefined
|
|
25
|
+
error: Error | undefined
|
|
26
|
+
reset: () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Auto-selects the correct redeem flow based on vault mode.
|
|
31
|
+
* Best for frontends that support multiple vault types.
|
|
32
|
+
*
|
|
33
|
+
* Internally uses useVaultStatus to detect the mode, then delegates to:
|
|
34
|
+
* - useOmniRedeem (R5) for 'cross-chain-async' vaults
|
|
35
|
+
* - useRedeemShares (R1) for 'local' and 'cross-chain-oracle' vaults
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const { redeem, isLoading, guid, requestStatus, vaultMode } = useSmartRedeem('0xVAULT', 8453)
|
|
39
|
+
*
|
|
40
|
+
* if (vaultMode === 'paused') return <PausedBadge />
|
|
41
|
+
*
|
|
42
|
+
* await redeem(sharesInWei, userAddress, userAddress)
|
|
43
|
+
* // For async vaults: poll requestStatus until 'completed'
|
|
44
|
+
* // For sync vaults: txHash + assets are available immediately
|
|
45
|
+
*/
|
|
46
|
+
export function useSmartRedeem(
|
|
47
|
+
vault: `0x${string}` | undefined,
|
|
48
|
+
hubChainId: number,
|
|
49
|
+
): UseSmartRedeemReturn {
|
|
50
|
+
const { data: status } = useVaultStatus(vault, hubChainId)
|
|
51
|
+
const omni = useOmniRedeem(vault, hubChainId)
|
|
52
|
+
const simple = useRedeemShares(vault, hubChainId)
|
|
53
|
+
|
|
54
|
+
const isAsync = status?.mode === 'cross-chain-async'
|
|
55
|
+
|
|
56
|
+
const redeem = isAsync ? omni.redeem : simple.redeem
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
redeem,
|
|
60
|
+
isLoading: isAsync ? omni.isLoading : simple.isLoading,
|
|
61
|
+
txHash: isAsync ? omni.txHash : simple.txHash,
|
|
62
|
+
assets: isAsync ? undefined : simple.assets,
|
|
63
|
+
guid: isAsync ? omni.guid : undefined,
|
|
64
|
+
requestStatus: isAsync ? omni.requestStatus : undefined,
|
|
65
|
+
wrongChain: isAsync ? omni.wrongChain : false,
|
|
66
|
+
vaultMode: status?.mode,
|
|
67
|
+
error: isAsync ? omni.error : simple.error,
|
|
68
|
+
reset: isAsync ? omni.reset : simple.reset,
|
|
69
|
+
}
|
|
70
|
+
}
|