haedal-liquidity-layer-scripts
v2.0.6
Published
本文对应 `hearn/move/liquidity_layer/sdk`,面向前端/客户端调用。
Readme
Liquidity Layer Frontend SDK 使用说明
本文对应 hearn/move/liquidity_layer/sdk,面向前端/客户端调用。
- 用户侧主类:
LLVClient - 管理侧主类:
LLVAdminClient
1. 初始化
import { LLVClient, LLVAdminClient } from "haedal-liquidity-layer-scripts";
const llvClient = new LLVClient(provider, {
packageId: "0x<llv_core_pkg>",
globalId: "0x<llv_global>",
clockId: "0x6",
shareTokenType: "0x<share_token_type>",
// 方案C拆包时建议都传
vaultPackageId: "0x<llv_ext_vault_pkg>",
suilendPackageId: "0x<llv_ext_suilend_pkg>",
scallopPackageId: "0x<llv_ext_scallop_pkg>",
naviPackageId: "0x<llv_ext_navi_pkg>",
cetusPackageId: "0x<llv_ext_cetus_pkg>",
alphalendPackageId: "0x<llv_ext_alphalend_pkg>",
});
const adminClient = new LLVAdminClient({
packageId: "0x<llv_core_pkg>",
globalId: "0x<llv_global>",
clockId: "0x6",
shareTokenType: "0x<share_token_type>",
globalAdminCapId: "0x<global_admin_cap>",
naviPackageId: "0x<llv_ext_navi_pkg>",
alphalendPackageId: "0x<llv_ext_alphalend_pkg>",
vaultPackageId: "0x<llv_ext_vault_pkg>",
suilendPackageId: "0x<llv_ext_suilend_pkg>",
scallopPackageId: "0x<llv_ext_scallop_pkg>",
cetusPackageId: "0x<llv_ext_cetus_pkg>",
});2. 管理端配置池子(必须先完成)
在 clm-auth 下,推荐顺序:
createAndSharePoolconfigureProtocolinitProtocolCap(仅NAVI / AlphaLend)addProtocolregisterProtocolLegAuth(所有协议都需要)setSupplyQueue/setWithdrawQueue
2.1 一步法(推荐)
可直接使用:configureInitAddAndRegisterProtocol(...)
它会按顺序串联:
configure_protocol -> init_*_cap(按需) -> add_protocol -> register_*_leg_auth
3. 仓位查询(新增,Phase 1)
合约新增了不会 abort 的查询函数,前端应优先使用。
3.0.1 一键获取提款展示信息(推荐)
// getWithdrawDisplayQuote: 聚合查询,返回前端展示所需的全部信息
const quote = await llvClient.getWithdrawDisplayQuote(sender, poolId, userShares);
// quote.positionValue — 仓位总价值(会计值,含禁用协议)
// quote.actualWithdrawable — 当前实际可提取金额
// quote.recallPlan — 参考分配计划(展示"从哪些协议提取")
// quote.isPartiallyLocked — 是否部分锁定(positionValue > actualWithdrawable)
if (quote.isPartiallyLocked) {
// 显示提示:"部分资金所在协议流动性受限"
}3.0.2 查询仓位价值(会计值)
const positionValue = await llvClient.estimateSharesValue(sender, poolId, userShares);
// 返回 bigint: 纯 NAV 换算,忽略方向标志和流动性3.0.3 查询可提取金额(操作值)
const result = await llvClient.previewWithdrawBySharesSafe(sender, poolId, userShares);
// result.navValue — 仓位总价值(会计值)
// result.actualWithdrawable — 当前实际可提取金额
// result.recallPlan — 参考分配计划3.0.4 按金额查询可提取信息
const result = await llvClient.previewWithdrawByAssetsSafe(sender, poolId, 800_000_000n);
// result.requestedShares — 请求金额对应的 shares(ceil,会计值)
// result.maxSafeShares — 可安全提交 begin_withdraw 的 shares 上限(floor)
// result.actualWithdrawable — 当前实际可提取金额
// result.recallPlan — 参考分配计划3.0.5 查询用户最大可提取 shares(推荐用于 max 按钮)
// 用户级上限 = min(wallet + staked shares, 池级可提取上限)
// 传入 suiClient 以包含 farming staked shares
import { SuiClient } from "@mysten/sui/client";
const suiClient = new SuiClient({ url: "..." });
const userMax = await llvClient.getUserMaxWithdrawableShares(sender, poolId, { suiClient });
// 不传 suiClient 时,仅统计 wallet 内的 shares
// (farming staked shares 不计入;RPC 读取失败也静默降级为 wallet-only)
const walletOnlyMax = await llvClient.getUserMaxWithdrawableShares(sender, poolId);如果只需要池级上限(不关心用户持仓):
// 池级上限(注意:不是用户级上限)
const poolMax = await llvClient.queryMaxWithdrawableShares(sender, poolId);3.0.6 前端展示建议
总仓位价值: 110 SUI ← quote.positionValue
当前可提取: 60 SUI ← quote.actualWithdrawable当 quote.isPartiallyLocked === true 时,显示提示:"部分资金所在协议流动性受限或方向控制已禁用"
3.0.7 新旧 SDK 方法对比
| SDK 方法 | 协议禁用时行为 | 适用场景 |
|---------|--------------|---------|
| previewWithdrawByShares()(旧) | throw | SDK 内部执行路径,不推荐前端直接调用 |
| previewWithdrawBySharesSafe()(新) | 正常返回 | 前端查询展示 |
| previewWithdrawByAssets()(旧) | throw | SDK 内部执行路径,不推荐前端直接调用 |
| previewWithdrawByAssetsSafe()(新) | 正常返回 | 前端查询展示 |
| estimateSharesValue()(新) | 正常返回 | 仓位价值展示 |
| queryMaxWithdrawableShares()(新) | 正常返回 | 池级可提取上限(不含用户持仓信息) |
| getUserMaxWithdrawableShares()(新) | 正常返回 | 用户级 max = min(wallet+staked, 池级上限);传入 suiClient 支持 farming |
| getWithdrawDisplayQuote()(新) | 正常返回(shares=0 返回全 0) | 一键获取展示所需全部信息 |
3.1 存款(原有流程不变)
3.1 存款
const tx = await llvClient.deposit(
poolId,
sender,
10n, // amount(按你传入 decimal 解释)
assetType, // 可不传,不传则从池子配置解析
{ slippageBps: 100n }, // 可选,默认 1%
9, // decimal,可选;SUI 常用 9
);
await signAndExecuteTransaction({ transaction: tx });SDK 内部会自动走:
preview_deposit(devInspect)begin_deposit- 按分配计划调用各协议
deposit_to_* complete_deposit
3.2 赎回
推荐流程:先查后取
// Step 1: 查询可提取信息(使用 safe 版本,不会 throw)
const quote = await llvClient.getWithdrawDisplayQuote(sender, poolId, userShares);
// Step 2: 前端展示
// “仓位价值: {quote.positionValue} SUI | 可提取: {quote.actualWithdrawable} SUI”
// if (quote.isPartiallyLocked) 显示流动性受限提示
// Step 3: 用户确认提取金额后,构造交易
const tx = await llvClient.withdraw(
poolId,
sender,
sharesToWithdraw, // 不超过 query_max_withdrawable_shares 的返回值
0n, // minAssetsOut(可为 0,交给 slippage 自动推导)
assetType,
{ slippageBps: 100n },
);
await signAndExecuteTransaction({ transaction: tx });SDK 内部会自动走:
- 自动选择用户持有的
Coin<ShareToken>,能直接复用就不额外 merge/split - final PTB dry-run quote
begin_withdraw- 按 recall plan 调用各协议
withdraw_from_* complete_withdraw_return(此处传入min_assets_out做最终安全检查)
如果前端是按”要拿多少资产”来发起赎回,推荐直接使用:
// 先查询:确认请求金额是否可执行
const preview = await llvClient.previewWithdrawByAssetsSafe(sender, poolId, assetsToWithdraw);
// 如果 preview.actualWithdrawable < assetsToWithdraw,提示用户:
// “请求提取 {assetsToWithdraw},但当前最多可提取 {preview.actualWithdrawable}”
// 用户确认后执行(使用实际可提取金额)
const tx = await llvClient.withdrawByAssets(
poolId,
sender,
preview.actualWithdrawable, // 使用实际可提取金额
0n, // minAssetsOut(可为 0,交给 slippage 自动推导)
assetType,
{ slippageBps: 100n },
);
await signAndExecuteTransaction({ transaction: tx });withdrawByAssets 会先通过 preview_withdraw_by_assets 求出应烧毁的 shares,再沿用同一套 final quote 流程构造单次签名交易。
注意:
withdraw/withdrawByAssets内部仍使用旧的preview_withdraw_by_shares(会 abort)。如果传入的 shares/assets 超过可提取上限,交易构造阶段就会失败。前端应先通过_safe版本查询,确保用户输入不超过actual_withdrawable。
4. 注意事项(高频坑位)
- 在
register_*_leg_auth之前,协议腿调用会因为 witness 鉴权失败而中止。 NAVI / AlphaLend未先执行init_*_cap,会在 ext 调用阶段失败。- 拆包部署必须传对各 ext package id,否则
moveCall target会指向错误包。 withdraw/withdrawByAssets都会自动解析用户持有的Coin<ShareToken>;如果已有单枚 coin 刚好够用,SDK 会直接复用它,避免额外对象抖动。- 非 SUI 资产不要依赖 gas coin,SDK 会从用户资产币里 split;余额不足会直接报错。
assetType可不传,但前提是池子里protocol_configs.type_bytes[1]已正确配置。slippageBps默认 100(1%),可按产品需求调大/调小。configure_protocol的ids/values顺序必须严格匹配 Move 约束,否则会 abort。- 禁用协议后查询仓位:当某协议
can_withdraw=false时,旧的preview_withdraw_by_shares会 abort。前端必须使用preview_withdraw_by_shares_safe查询,它永远不会 abort。 actual_withdrawable是参考上限:查询时返回的actual_withdrawable基于链上记录的current_balance和方向标志,不含第三方协议的真实流动性(如 Suilend 借贷池利用率)。实际执行时,complete_withdraw_return的min_assets_out参数提供最终安全兜底。recall_plan是参考值:_safe函数返回的recall_plan仅供前端展示"大约从哪些协议提取"。实际执行时begin_withdraw会重新计算 plan,可能因状态变化而略有不同。max_safe_shares的语义:preview_withdraw_by_assets_safe返回的max_safe_shares是"可安全提交给 begin_withdraw 的 shares 上限"(floor 取整),不是"精确可执行的 shares"。执行时begin_withdraw会用该 shares 数重算assets_to_receive,结果 ≤actual_withdrawable。
5. AlphaLend 特别说明
PROTOCOL_ALPHALEND = 5- 配置时:
ids = [lending_protocol_id],values = [market_id] - 然后必须执行:
init_alphalend_position_cap - 最后记得:
add_protocol+register_alphalend_leg_auth
