How the Vault Works
The PoolVault is an ERC-4626 vault. You deposit USDC and
receive pool shares in return. Shares represent your proportional claim on the pool’s total assets.
The pool is the counterparty to every trader position:
Trader losses flow into the pool, increasing share price
Trader profits are paid from the pool, decreasing share price
Trading fees accrue to the pool on every position open
Over time, if the pool collects more in fees and trader losses than it pays in trader profits, the
share price increases and your shares become worth more USDC than you deposited.
Share Price
sharePrice = totalAssets / totalSupply
Term Description totalAssetsUSDC held by the vault (6 decimals) totalSupplyPool shares outstanding (6 decimals)
Onchain : both values are stored in the PoolVault contract. See Share Price
for the full formula including unrealized PnL adjustments.
Deposit USDC
Depositing requires two transactions: approve the PoolVault to spend your USDC, then call deposit.
Approve USDC spending
import { createPublicClient , createWalletClient , http , parseUnits } from "viem" ;
import { sepolia } from "viem/chains" ;
import { privateKeyToAccount } from "viem/accounts" ;
import { mockUsdcAbi } from "@nile-markets/sdk" ;
const account = privateKeyToAccount ( "0xYOUR_PRIVATE_KEY" );
const publicClient = createPublicClient ({
chain: sepolia ,
transport: http ( "YOUR_RPC_URL" ),
});
const walletClient = createWalletClient ({
account ,
chain: sepolia ,
transport: http ( "YOUR_RPC_URL" ),
});
const USDC = "0x..." ; // See Contract Addresses page
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const amount = parseUnits ( "1000" , 6 ); // 1,000 USDC
const approveHash = await walletClient . writeContract ({
address: USDC ,
abi: mockUsdcAbi ,
functionName: "approve" ,
args: [ POOL_VAULT , amount ],
});
await publicClient . waitForTransactionReceipt ({ hash: approveHash });
console . log ( "USDC approved for PoolVault" );
nile token approve --spender poolVault --amount 1000.0
Deposit into the vault
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { parseUnits , formatUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const amount = parseUnits ( "1000" , 6 ); // 1,000 USDC
const depositHash = await walletClient . writeContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "deposit" ,
args: [ amount , account . address ], // assets, receiver
});
const receipt = await publicClient . waitForTransactionReceipt ({ hash: depositHash });
console . log ( `Deposited in tx: ${ receipt . transactionHash } ` );
Check how many shares you received: const shares = await publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "balanceOf" ,
args: [ account . address ],
});
console . log ( `Your shares: ${ formatUnits ( shares , 6 ) } ` );
nile pool deposit --amount 1000.0
The number of shares you receive depends on the current share price. If the share price is 1.05,
depositing 1,000 USDC yields approximately 952.38 shares (1000 / 1.05).
Preview Deposit
Before depositing, you can preview how many shares you will receive:
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { parseUnits , formatUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const previewShares = await publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "previewDeposit" ,
args: [ parseUnits ( "1000" , 6 )],
});
console . log ( `1,000 USDC → ${ formatUnits ( previewShares , 6 ) } shares` );
Withdraw USDC
You can withdraw by specifying either a USDC amount (withdraw) or a share amount (redeem).
Withdraw by USDC Amount
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { parseUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const amount = parseUnits ( "500" , 6 ); // 500 USDC
const withdrawHash = await walletClient . writeContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "withdraw" ,
args: [ amount , account . address , account . address ], // assets, receiver, owner
});
await publicClient . waitForTransactionReceipt ({ hash: withdrawHash });
console . log ( "Withdrawn 500 USDC" );
nile pool withdraw --amount 500.0
Redeem by Share Amount
To redeem a specific number of shares instead of a USDC amount:
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { parseUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const shares = parseUnits ( "500" , 6 ); // 500 shares
const redeemHash = await walletClient . writeContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "redeem" ,
args: [ shares , account . address , account . address ], // shares, receiver, owner
});
await publicClient . waitForTransactionReceipt ({ hash: redeemHash });
Check Your LP Position
Query your current share balance and its USDC value:
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { formatUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const [ shares , totalAssets , totalSupply ] = await Promise . all ([
publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "balanceOf" ,
args: [ account . address ],
}),
publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "totalAssets" ,
}),
publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "totalSupply" ,
}),
]);
const sharePrice = Number ( totalAssets ) / Number ( totalSupply );
const usdcValue = Number ( shares ) * sharePrice ;
console . log ( `Shares: ${ formatUnits ( shares , 6 ) } ` );
console . log ( `Share price: ${ sharePrice . toFixed ( 6 ) } ` );
console . log ( `Value: ${ ( usdcValue / 1e6 ). toFixed ( 2 ) } USDC` );
The pool state output includes totalAssets, totalSupply, and the current share price.
Withdrawal Restrictions
Withdrawals may be restricted when pool utilization is high.
Condition Effect Utilization below cap Full withdrawal available Utilization near cap Partial withdrawal — only the amount that keeps utilization below the cap Utilization at cap No withdrawals until positions close and free up capital
Check the maximum withdrawable amount before submitting:
import { poolVaultAbi } from "@nile-markets/sdk" ;
import { formatUnits } from "viem" ;
const POOL_VAULT = "0x..." ; // See Contract Addresses page
const [ maxWithdraw , utilization ] = await Promise . all ([
publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "maxWithdrawable" ,
}),
publicClient . readContract ({
address: POOL_VAULT ,
abi: poolVaultAbi ,
functionName: "utilization" ,
}),
]);
console . log ( `Max withdrawable: ${ formatUnits ( maxWithdraw , 6 ) } USDC` );
console . log ( `Current utilization: ${ Number ( utilization ) / 100 } %` );
High utilization means the pool’s capital is backing open trader positions. Withdrawing too much
would leave the pool unable to pay trader profits. The utilization cap protects both LPs and traders.
Pool Transaction History
View your deposit and withdrawal history via the subgraph:
{
vaultEvents (
where : { account : "0xYOUR_ADDRESS" }
orderBy : timestamp
orderDirection : desc
first : 25
) {
id
type
assets
shares
timestamp
txHash
}
}
Or via CLI:
Related Pages
Vault Mechanics How the ERC-4626 vault manages LP capital and counterparty exposure.
Share Price Detailed share price formula including unrealized PnL adjustments.
Pool Utilization Utilization calculation, caps, and impact on withdrawals.
Pool Vault Contract Full contract reference with all read and write functions.