← Documentation home
Build

Quickstart

Read state, deposit, and withdraw against a pooled tranched metavault. Examples in TypeScript (viem), JavaScript (ethers), and Python (web3.py).

Install

npm install viem

Read state via the Lens

The Lens is one stateless contract per chain. One snapshot(vault) call returns every field a UI needs, atomic to a single block.

import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
 
const LENS = "0x..." as const;   // see /deployments
const VAULT = "0x..." as const;  // pooled metavault address
 
const client = createPublicClient({ chain: baseSepolia, transport: http() });
 
const snapshot = await client.readContract({
  address: LENS,
  abi: lensAbi,
  functionName: "snapshot",
  args: [VAULT],
});
 
console.log({
  asset: snapshot.asset,
  protectionPremiumBps: snapshot.protectionPremiumBps,
  seniorNav: snapshot.seniorNav,
  juniorNav: snapshot.juniorNav,
  seniorSharePrice: snapshot.seniorSharePrice,
  juniorSharePrice: snapshot.juniorSharePrice,
  maxJuniorWithdrawAssets: snapshot.maxJuniorWithdrawAssets,
});

Read a user's position

The Lens returns the user's lcSEN / lcJUN balances plus any outstanding withdrawal notice on each side.

const position = await client.readContract({
  address: LENS,
  abi: lensAbi,
  functionName: "userPosition",
  args: [VAULT, userAddress],
});
 
console.log({
  seniorShares: position.seniorShares,
  seniorAssets: position.seniorAssets,
  seniorNotice: position.seniorNotice, // { shares, executableAt, snapshotAssets, executableAssets }
 
  juniorShares: position.juniorShares,
  juniorAssets: position.juniorAssets,
  juniorNotice: position.juniorNotice,
});

A user has at most one outstanding notice per (address, tranche) pair. notice.shares > 0 means a notice is currently queued.

Deposit (senior)

Approve the metavault to pull the underlying asset, then call depositSenior. lcSEN is minted to receiver at the current senior unit price.

import { parseUnits, maxUint256 } from "viem";
 
// 1. Approve the metavault to pull USDC.
await walletClient.writeContract({
  address: USDC,
  abi: erc20Abi,
  functionName: "approve",
  args: [VAULT, maxUint256],
});
 
// 2. Deposit. lcSEN shares are minted to `receiver` at the current senior unit price.
const amount = parseUnits("1000", 6); // 1000 USDC
const shares = await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "depositSenior",
  args: [amount, userAddress],
});

Reverts with:

Deposit (junior)

Same shape as senior — only the function name changes. Junior deposits don't have the leverage-cap revert (more junior is always fine). Blocked only by DepositsPaused.

const shares = await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "depositJunior",
  args: [amount, userAddress],
});

Withdraw (senior)

Senior withdrawals are gated by a 5-day notice. Approve the metavault to pull lcSEN, file the notice, wait, then execute. The redemption is priced at the lower of execution-time NAV and the file-time snapshot value.

const SENIOR_TOKEN = snapshot.seniorToken;
 
// 1. Approve the metavault to pull lcSEN, then file the notice.
await walletClient.writeContract({
  address: SENIOR_TOKEN,
  abi: erc20Abi,
  functionName: "approve",
  args: [VAULT, shares],
});
 
await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "requestSeniorWithdrawal",
  args: [shares],
});
 
// 2. Wait 5 days (SENIOR_WITHDRAWAL_NOTICE).
//    Cancel any time with cancelSeniorWithdrawal(); file again only after the
//    active notice is cancelled or fully executed.
 
// 3. Execute within the 3-day window after maturity.
const assets = await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "withdrawSenior",
  args: [shares, userAddress, userAddress],
});

Reverts with:

To preview the expected payout without executing:

const expectedAssets = await client.readContract({
  address: LENS,
  abi: lensAbi,
  functionName: "previewWithdrawSenior",
  args: [VAULT, shares],
});

The loss waterfall is applied to NAV continuously, so the senior unit price already reflects whatever protection has been consumed — there is no separate "claim" path on senior exit.

Withdraw (junior)

Junior withdrawals are gated by a 10-day notice. Same shape as senior, including the tranche-token approval before filing, with an additional check at execution: the redemption must not push senior past 4× junior NAV.

// 1. File the notice.
await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "requestJuniorWithdrawal",
  args: [shares],
});
 
// 2. Wait 10 days (JUNIOR_WITHDRAWAL_NOTICE).
 
// 3. Execute within the 3-day window after maturity.
await walletClient.writeContract({
  address: VAULT,
  abi: trancheVaultAbi,
  functionName: "withdrawJunior",
  args: [shares, userAddress, userAddress],
});

Additional revert (on top of the senior list above):

Tranche-token surface

The two PooledTrancheToken instances (lcSEN, lcJUN) are plain ERC20Permit receipt tokens. The metavault is the only minter/burner; holder-side transfers (including notice locks) go through the standard ERC20 surface, plus EIP-2612 permit so a holder can authorise the metavault to pull shares for a withdrawal notice without a separate approve tx.

const seniorToken = "0x..." as const; // metavault.seniorToken()
 
await walletClient.writeContract({
  address: seniorToken,
  abi: erc20PermitAbi,
  functionName: "permit",
  args: [holder, metavault, value, deadline, v, r, s],
});

Pricing and capacity views live on the metavault (or the Lens), not on the receipt tokens. There is no ERC4626 deposit / redeem on the tranche tokens themselves. Always go through TranchedMetaVault for deposits, notices, and withdrawals.

Where to next