Skip to main content

Overview

The Vault contract is the core entry point for users to deposit assets and receive yield. It implements an ERC4626-like vault with share-based accounting, performance fees, and integration with multiple yield strategies through the StrategyRouter. Contract Address: Deployed per-asset basis Inherits: ERC20, Ownable

State Variables

asset
IERC20
required
Immutable. The underlying asset token (e.g., USDC, LINK) that users deposit.
performanceFeeBps
uint256
required
Performance fee in basis points (10000 = 100%). Applied to harvested profits.
feeRecipient
address
required
Address that receives performance and withdrawal fees.
withdrawFeeBps
uint256
required
Withdrawal fee in basis points (10000 = 100%). Applied when users withdraw.
router
address
required
Address of the StrategyRouter contract that manages yield strategies.
netDeposited
mapping(address => uint256)
required
Tracks total deposits per user for growth calculation.
totalWithdrawn
mapping(address => uint256)
required
Tracks total withdrawals per user for growth calculation.

Events

Deposit

event Deposit(address indexed user, uint256 amount, uint256 shares)
Emitted when a user deposits assets.
user
address
Address of the depositor
amount
uint256
Amount of assets deposited
shares
uint256
Amount of vault shares minted

Withdraw

event Withdraw(address indexed user, uint256 amount, uint256 shares)
Emitted when a user withdraws assets.
user
address
Address of the withdrawer
amount
uint256
Amount of assets withdrawn (after fees)
shares
uint256
Amount of vault shares burned

Constructor

constructor(
    address _asset,
    address _feeRecipient,
    uint256 _performanceBps,
    uint256 _withdrawFeeBps
) ERC20("Vault Share Token", "VST") Ownable(msg.sender)
_asset
address
required
Address of the underlying asset token
_feeRecipient
address
required
Address to receive fees
_performanceBps
uint256
required
Performance fee in basis points (e.g., 1000 = 10%)
_withdrawFeeBps
uint256
required
Withdrawal fee in basis points (e.g., 50 = 0.5%)

Public Functions

deposit

function deposit(uint256 amount) external returns (uint256)
Deposit assets into the vault and receive shares.
amount
uint256
required
Amount of assets to deposit
shares
uint256
Amount of vault shares minted to the depositor
Requirements:
  • Caller must have approved the vault to transfer amount of asset tokens
  • amount must be greater than 0
Example:
// Approve vault to spend USDC
usdc.approve(address(vault), 1000e6);

// Deposit 1000 USDC
uint256 shares = vault.deposit(1000e6);

withdraw

function withdraw(uint256 shares) external returns (uint256 assetsOut)
Burn shares and withdraw underlying assets. If vault doesn’t have enough liquidity, pulls from strategies.
shares
uint256
required
Amount of shares to burn
assetsOut
uint256
Amount of assets transferred to user (after withdrawal fee)
Requirements:
  • shares must be greater than 0
  • Caller must have sufficient shares
  • Router must be set if strategies need to be tapped
Example:
// Withdraw all shares
uint256 shares = vault.balanceOf(msg.sender);
uint256 assetsReceived = vault.withdraw(shares);

convertToShares

function convertToShares(uint256 assets) public view returns (uint256)
Convert an asset amount to shares based on current vault state.
assets
uint256
required
Amount of assets
shares
uint256
Equivalent shares amount

convertToAssets

function convertToAssets(uint256 shares) public view returns (uint256)
Convert shares to asset amount based on current vault state.
shares
uint256
required
Amount of shares
assets
uint256
Equivalent assets amount

totalAssets

function totalAssets() public view returns (uint256)
Returns the amount of assets held directly in the vault (not in strategies).
assets
uint256
Amount of assets in vault

totalManagedAssets

function totalManagedAssets() public view returns (uint256)
Returns total assets under management (vault + all strategies).
total
uint256
Total assets in vault and all strategies

getNAV

function getNAV() external view returns (uint256)
Get Net Asset Value - same as totalManagedAssets().
nav
uint256
Total net asset value

availableLiquidity

function availableLiquidity() external view returns (uint256)
Get immediately available liquidity (vault balance only, excluding strategies).
liquidity
uint256
Available liquidity for instant withdrawals

userGrowth

function userGrowth(address user) public view returns (int256)
Calculate absolute profit/loss for a user.
user
address
required
User address to check
pnl
int256
Profit (positive) or loss (negative) in asset units
Formula: (currentValue + totalWithdrawn) - netDeposited

userGrowthPercent

function userGrowthPercent(address user) external view returns (int256)
Calculate percentage growth for a user.
user
address
required
User address to check
growthPercent
int256
Growth percentage scaled by 1e18 (1e18 = 100%)
Example:
int256 growth = vault.userGrowthPercent(msg.sender);
if (growth > 0) {
    // User has profit: growth / 1e18 * 100 = percentage
    // Example: 5e17 = 50% gain
}

Owner Functions

setRouter

function setRouter(address _router) external onlyOwner
Set the StrategyRouter contract address.
_router
address
required
Address of the StrategyRouter contract

Router-Only Functions

moveToStrategy

function moveToStrategy(address strategy, uint256 amount) external onlyRouter
Move funds from vault to a strategy. Called by StrategyRouter during rebalancing.
strategy
address
required
Strategy contract address
amount
uint256
required
Amount of assets to move

receiveFromStrategy

function receiveFromStrategy(uint256 amount) external onlyRouter
Accounting function called when router pulls funds from strategy back to vault.
amount
uint256
required
Amount received from strategy

handleHarvestProfit

function handleHarvestProfit(uint256 profit) external onlyRouter
Process harvested profits and apply performance fee.
profit
uint256
required
Amount of profit harvested
Behavior:
  • Calculates fee: fee = profit * performanceFeeBps / 10000
  • Transfers fee to feeRecipient
  • Remaining profit stays in vault to increase share value

Integration Example

// Deploy vault for USDC
Vault vault = new Vault(
    address(usdc),           // asset
    feeRecipient,            // fee recipient
    1000,                    // 10% performance fee
    50                       // 0.5% withdrawal fee
);

// Set router
vault.setRouter(address(strategyRouter));

// User deposits
usdc.approve(address(vault), 10000e6);
uint256 shares = vault.deposit(10000e6);

// Check user's position value
uint256 value = vault.convertToAssets(vault.balanceOf(msg.sender));

// Check user's profit
int256 profit = vault.userGrowth(msg.sender);
int256 profitPct = vault.userGrowthPercent(msg.sender);

// Withdraw
vault.withdraw(shares);

Security Considerations

  1. Router Trust: The router has privileged access to move funds. Ensure router contract is audited.
  2. Fee Limits: Consider implementing maximum fee caps (e.g., performanceFeeBps 2000 for 20% max).
  3. Reentrancy: Uses SafeERC20 which provides reentrancy protection.
  4. Share Inflation: First depositor should deposit significant amount to prevent share manipulation attacks.

See Also

Build docs developers (and LLMs) love