Stateless read aggregator. One call returns every field the UI needs; one call per user position. Use this instead of N view calls.
The Lens provides a simple way of querying the smart contracts, by packing data into a single struct-returning view function. One RPC, one block, every field consistent.
function snapshot(address vault) external view returns (VaultSnapshot memory);
function snapshots(address[] vaults) external view returns (VaultSnapshot[] memory);
function userPosition(address vault, address user) external view returns (UserPosition memory);
function userPositions(address[] vaults, address user) external view returns (UserPosition[] memory);The plural variants batch across multiple vaults, which is useful for a homepage table or a portfolio dashboard.
struct VaultSnapshot {
// ---- identity ----
address vault; // the metavault itself
address morphoVault; // the wrapped ERC4626 (legacy field name; holds whatever ERC4626 the metavault wraps — Morpho V2, Euler V2, etc.)
address asset; // underlying ERC20
string assetSymbol;
uint8 assetDecimals;
address seniorToken; // lcSEN PooledTrancheToken
address juniorToken; // lcJUN PooledTrancheToken
// ---- protocol parameters ----
// (Constant values — SENIOR_PROTECTION_BPS, MAX_CORRELATED_SENIOR_LEVERAGE_BPS,
// SENIOR/JUNIOR_WITHDRAWAL_NOTICE, MAX_PERFORMANCE_FEE_BPS — are defined once
// on the metavault page; this struct just mirrors them for the UI.)
uint256 protectionPremiumBps;
uint256 seniorProtectionBps;
uint256 maxCorrelatedSeniorLeverageBps;
uint256 seniorWithdrawalNotice;
uint256 juniorWithdrawalNotice;
// ---- performance fee ----
uint256 performanceFeeBps; // active rate, ≤ MAX_PERFORMANCE_FEE_BPS
uint256 maxPerformanceFeeBps; // hard cap exposed for UI
address feeRecipient; // recipient of newly-minted junior fee shares
// ---- live state ----
bool depositsPaused;
uint64 lastPremiumAccrual;
// ---- pooled accounting ----
uint256 seniorNav;
uint256 juniorNav;
uint256 totalAssets; // vault.previewRedeem(totalVaultShares)
uint256 totalVaultShares;
uint256 seniorTotalSupply; // lcSEN.totalSupply()
uint256 juniorTotalSupply; // lcJUN.totalSupply()
uint256 seniorSharePrice; // 1e18-scaled
uint256 juniorSharePrice; // 1e18-scaled
// ---- derived headroom ----
uint256 requiredJuniorCollateralAssets; // seniorNav / 4 at 20% protection
uint256 maxJuniorWithdrawAssets; // junior NAV currently free to exit
uint256 maxSeniorDepositAssets; // headroom before the 4× cap binds
uint256 seniorLeverageBps; // current senior / junior, in bps
uint256 juniorCushionBps; // junior / senior, in bps; uint256.max if no senior
}There is no on-chain rate scheduling on the metavault: setProtectionPremiumRate / setPerformanceFeeRate / setFeeRecipient apply atomically (role-gated, see TranchedMetaVault). Deployments that need a delay-on-governance route the relevant role through an external TimelockController and observe its CallScheduled events directly. The lens does not surface a pending-rate field.
The pooled model means a user has at most one balance per tranche and at most one outstanding notice per tranche. The Lens returns both, plus the asset-equivalent at current NAV.
struct UserPosition {
// Free balances (transferable, redeemable after notice).
uint256 seniorShares;
uint256 juniorShares;
// Asset-equivalent value of the free balance at current NAV.
// Computed as `shares × trancheSharePrice / 1e18`.
uint256 seniorAssets;
uint256 juniorAssets;
// Notice state. Zero `executableAt` ⇒ no notice on file.
WithdrawalNoticeView seniorNotice;
WithdrawalNoticeView juniorNotice;
}
struct WithdrawalNoticeView {
uint256 shares; // shares currently locked under notice
uint64 executableAt; // 0 if no notice
uint256 assetsAtCurrentNav; // shares × sharePrice / 1e18
}A user with no senior position has seniorShares == 0 and a zeroed seniorNotice. Same for junior.
The assetsAtCurrentNav field is purely informational. It doesn't account for the underlying-vault liquidity gate or the junior leverage cap. For senior, assetsAtCurrentNav is the actual amount the user would receive on a successful withdrawSenior. For junior, the actual receivable is min(assetsAtCurrentNav, vaultSnapshot.maxJuniorWithdrawAssets).