Skip to main content
Liquidity providers interact with the PoolVault through standard ERC-4626 deposit and withdrawal functions. Deposits mint share tokens proportional to the current share price. Withdrawals burn shares and return the corresponding USDC, subject to a utilization cap that ensures the pool maintains sufficient liquidity for open positions.
On Sepolia testnet, the vault uses MockUSDC (a test ERC-20 with 6 decimals). You can mint test tokens from the MockUSDC contract. All USDC values in this guide refer to raw 6-decimal units unless otherwise noted.

Depositing

There are two ways to deposit into the vault:
Specify the exact USDC amount to deposit. The vault calculates and mints the proportional number of shares based on the current share price.Parameters:
  • assets — USDC amount in raw units (e.g., 1000000000 for 1,000 USDC)
  • receiver — address that receives the minted share tokens
Returns: Number of shares minted.

Prerequisites

Before depositing, you must approve the PoolVault to spend your USDC:
import { erc20Abi } from "viem";
import { poolVaultAbi } from "@nile-markets/sdk";

// Step 1: Approve USDC spending
await walletClient.writeContract({
  address: USDC_ADDRESS,
  abi: erc20Abi,
  functionName: "approve",
  args: [POOL_VAULT_ADDRESS, depositAmount],
});

// Step 2: Deposit USDC into the vault
const shares = await walletClient.writeContract({
  address: POOL_VAULT_ADDRESS,
  abi: poolVaultAbi,
  functionName: "deposit",
  args: [depositAmount, userAddress],
});
If the LP allowlist is enabled, only allowlisted addresses can deposit. Attempting to deposit from a non-allowlisted address will revert. Check the current allowlist status by reading allowlistEnabled() on the PoolVault contract.

How Shares Are Calculated

When you deposit, the number of shares minted is determined by the current pool equity and total supply:
sharesReceived = depositAmount * totalSupply / poolEquity
If totalSupply is zero (first deposit), shares are minted 1:1 with the deposit amount.
Your deposit does not change the share price. Both the numerator (pool equity) and denominator (total supply) increase proportionally. Other LPs are not diluted by new deposits.

Withdrawing

There are two ways to withdraw from the vault:
Specify the exact USDC amount to withdraw. The vault calculates and burns the required shares.Parameters:
  • assets — USDC amount to withdraw in raw units
  • owner — address whose shares will be burned
  • receiver — address that receives the USDC
Returns: Number of shares burned.

Utilization Restriction

Withdrawals are restricted by the pool’s utilization cap. You cannot withdraw if doing so would push the pool’s notional utilization above maxUtilizationBps (default: 80%).
maxWithdrawable = totalAssets - (grossNotional * BPS_DENOMINATOR / maxUtilizationBps)
If the pool is already at or above the utilization cap, maxWithdrawable returns 0 and no withdrawals are possible until positions close and utilization decreases.
The utilization cap prevents bank-run dynamics. Without it, LPs could withdraw all liquidity during adverse market conditions, leaving the pool unable to settle matured positions or absorb trader losses. The cap ensures a minimum reserve ratio is always maintained relative to open exposure.
You can always withdraw up to the maxWithdrawable amount. If utilization is high and your full balance exceeds the withdrawable amount, you can make partial withdrawals as positions close and utilization decreases. There is no lock-up period — only the utilization constraint.
No. The LP allowlist only restricts deposits. Any shareholder can always withdraw their shares, subject only to the utilization cap.

Checking Withdrawable Amount

Before initiating a withdrawal, query the maximum withdrawable amount:
import { poolVaultAbi } from "@nile-markets/sdk";

// Check max withdrawable for your address
const maxAssets = await publicClient.readContract({
  address: POOL_VAULT_ADDRESS,
  abi: poolVaultAbi,
  functionName: "maxWithdraw",
  args: [userAddress],
});

// Check max redeemable shares for your address
const maxShares = await publicClient.readContract({
  address: POOL_VAULT_ADDRESS,
  abi: poolVaultAbi,
  functionName: "maxRedeem",
  args: [userAddress],
});

ERC-4626 Rounding

The ERC-4626 standard specifies that redeem rounds down when converting shares to assets. This means the actual USDC received may be up to 1 wei less than the theoretical value.
When building integrations that assert exact withdrawal amounts, account for a 1-wei rounding difference. In Foundry tests, use assertApproxEqAbs(actual, expected, 1) instead of assertEq.

Full Deposit and Withdraw Flow

1

Approve USDC

Call USDC.approve(poolVault, amount) to allow the vault to pull your USDC.
2

Deposit USDC

Call poolVault.deposit(amount, yourAddress) to deposit and receive share tokens.
3

Hold shares

Your shares represent proportional ownership of pool equity. Share price changes as traders trade, positions settle, and fees are collected.
4

Check withdrawable amount

Call poolVault.maxWithdraw(yourAddress) to see how much you can currently withdraw given the utilization cap.
5

Redeem shares

Call poolVault.redeem(shares, yourAddress, yourAddress) to burn shares and receive USDC.

ERC-4626 Vault

Understand how the vault operates as counterparty to all trader positions.

Pool Utilization

Deep dive into utilization measurement and withdrawal restrictions.