Skip to main content

MultistrategyVault

Git Source

Inherits: IMultistrategyVault

Author: yearn.finance; port maintained by Golem Foundation

This MultistrategyVault is based on the original VaultV3.vy Vyper implementation that has been ported to Solidity. It is designed as a non-opinionated system to distribute funds of depositors for a specific asset into different opportunities (aka Strategies) and manage accounting in a robust way. Depositors receive shares (aka vaults tokens) proportional to their deposit amount. Vault tokens are yield-bearing and can be redeemed at any time to get back deposit plus any yield generated. Addresses that are given different permissioned roles by the roleManager are then able to allocate funds as they best see fit to different strategies and adjust the strategies and allocations as needed, as well as reporting realized profits or losses. Strategies are any ERC-4626 compliant contracts that use the same underlying asset as the vault. The vault provides no assurances as to the safety of any strategy and it is the responsibility of those that hold the corresponding roles to choose and fund strategies that best fit their desired specifications. Those holding vault tokens are able to redeem the tokens for the corresponding amount of underlying asset based on any reported profits or losses since their initial deposit. The vault is built to be customized by the management to be able to fit their specific desired needs. Including the customization of strategies, accountants, ownership etc.

*Security considerations (summary):

  • Roles: privileged functions gated via Roles; improper assignment can lead to fund mismanagement.
  • Reentrancy: mutating flows guarded by nonReentrant.
  • Precision/rounding: use preview/convert helpers; PPS exposed has limited precision.
  • Withdrawal queue: incorrect ordering/duplicates can distort maxWithdraw/maxRedeem views.
  • Strategy trust: vault does not attest to strategy safety; management must curate strategies.*

Notes:

State Variables

MAX_QUEUE

Maximum number of strategies allowed in the withdrawal queue

Prevents excessive gas costs during withdrawal iterations and queue management

uint256 public constant MAX_QUEUE = 10;

MAX_BPS

Maximum basis points representing 100%

Used for all percentage calculations (fees, losses, etc.) 10,000 basis points = 100%

uint256 public constant MAX_BPS = 10_000;

MAX_BPS_EXTENDED

Extended precision for profit unlocking rate calculations

Used to maintain precision when calculating per-second profit unlock rates 1,000,000,000,000 = 1e12 for high precision time-weighted calculations

uint256 public constant MAX_BPS_EXTENDED = 1_000_000_000_000;

API_VERSION

API version of this vault implementation

Based on Yearn V3 vault version

string public constant API_VERSION = "3.0.4";

DOMAIN_TYPE_HASH

EIP-712 domain type hash for signature verification

Used in permit() function for gasless approvals

bytes32 private constant DOMAIN_TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

PERMIT_TYPE_HASH

EIP-712 permit type hash for signature verification

Used in permit() function for gasless approvals

bytes32 private constant PERMIT_TYPE_HASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

asset

Address of the underlying ERC20 asset token

Must be ERC20-compliant. Set during initialization and cannot be changed

address public override asset;

decimals

Decimal places for vault shares, matching the underlying asset

Inherited from the asset token to maintain 1:1 precision

uint8 public override decimals;

_factory

Address of the factory contract that deployed this vault

Used to retrieve protocol fee configuration and maintain vault registry

address private _factory;

_strategies

Mapping of strategy addresses to their parameters and accounting

Only strategies with activation != 0 are considered active Stores: activation timestamp, lastReport, currentDebt, maxDebt

mapping(address => StrategyParams) internal _strategies;

_defaultQueue

Array of strategy addresses used as the default withdrawal queue

Maximum length of MAX_QUEUE (10). Order determines withdrawal priority Strategies are attempted in array order during withdrawals

address[] internal _defaultQueue;

useDefaultQueue

Whether to force use of default queue for all withdrawals

When true, custom withdrawal queues passed to withdraw/redeem are ignored

bool public useDefaultQueue;

autoAllocate

Whether to automatically allocate deposited funds to strategies

When true, deposits are automatically sent to _defaultQueue[0] if queue is not empty Requires a non-empty default queue or deposits will fail

bool public autoAllocate;

_balanceOf

Mapping of account addresses to their vault share balances

ERC20-compliant balance tracking

mapping(address => uint256) private _balanceOf;

allowance

Nested mapping of owner to spender to approved share amounts

ERC20-compliant allowance tracking for transferFrom operations

mapping(address => mapping(address => uint256)) public override allowance;

_totalSupplyValue

Total supply of vault shares including locked shares

Includes both circulating shares and shares locked for profit unlocking Actual circulating supply = _totalSupplyValue - _unlockedShares()

uint256 private _totalSupplyValue;

_totalDebt

Total amount of assets currently deployed across all strategies

In asset base units (typically 18 decimals). Updated via updateDebt() and processReport() Invariant: _totalAssets = _totalIdle + _totalDebt

uint256 private _totalDebt;

_totalIdle

Amount of underlying asset held idle in the vault contract

In asset base units. Used instead of balanceOf(this) to prevent PPS manipulation via direct transfers Acts as buffer for cheap withdrawals without touching strategies

uint256 private _totalIdle;

minimumTotalIdle

Minimum amount of assets to maintain idle in the vault

In asset base units. Set by MINIMUM_IDLE_MANAGER role Helps ensure gas-efficient withdrawals by maintaining a buffer Value of 0 means no minimum is enforced

uint256 public override minimumTotalIdle;

depositLimit

Maximum total assets the vault can hold

In asset base units. When totalAssets >= depositLimit, deposits revert Can be overridden by depositLimitModule if set to type(uint256).max

uint256 public override depositLimit;

accountant

Address of accountant contract for fee assessment

Charges fees on profits and can issue refunds. Set to address(0) to disable fees Called during processReport() to calculate fees and refunds

address public override accountant;

depositLimitModule

Address of deposit limit module contract

When set (non-zero), overrides the standard depositLimit Must implement IDepositLimitModule.availableDepositLimit()

address public override depositLimitModule;

withdrawLimitModule

Address of withdraw limit module contract

When set (non-zero), overrides the standard maxWithdraw calculation Must implement IWithdrawLimitModule.availableWithdrawLimit()

address public override withdrawLimitModule;

roles

Mapping of addresses to their role bitmasks

Each bit represents a different role (see Roles enum) Multiple roles can be assigned by combining bits with OR

mapping(address => uint256) public roles;

roleManager

Address with authority to manage all role assignments

Can add/remove roles for any address including itself Transfer requires two-step process via transferRoleManager() and acceptRoleManager()

address public override roleManager;

futureRoleManager

Pending role manager address during transfer

Set by transferRoleManager(), cleared after acceptRoleManager() Two-step process prevents accidental transfer to wrong address

address public override futureRoleManager;

name

Human-readable name of the vault token

ERC20 standard. Can be updated by roleManager via setName()

string public override name;

symbol

Symbol ticker of the vault token

ERC20 standard. Can be updated by roleManager via setSymbol()

string public override symbol;

_shutdown

Whether the vault has been shutdown

When true, only withdrawals are allowed. Cannot be reversed once set Triggered by EMERGENCY_MANAGER role via shutdownVault()

bool private _shutdown;

_profitMaxUnlockTime

Duration over which profits are gradually unlocked

In seconds. Maximum 31,556,952 (1 year) Set to 0 to disable profit locking (profits unlock immediately) Prevents PPS manipulation by locking profits as vault shares

uint256 private _profitMaxUnlockTime;

_fullProfitUnlockDate

Timestamp when all currently locked profits will be fully unlocked

Unix timestamp in seconds. Set to 0 when no profits are locked Updated after each profitable report

uint256 private _fullProfitUnlockDate;

_profitUnlockingRate

Rate at which locked profit shares unlock per second

In MAX_BPS_EXTENDED precision (1e12) Formula: (totalLockedShares * MAX_BPS_EXTENDED) / unlockingPeriod

uint256 private _profitUnlockingRate;

_lastProfitUpdate

Timestamp of the last update to profit locking state

Unix timestamp in seconds. Used to calculate _unlockedShares() Updated whenever shares are locked (during processReport)

uint256 private _lastProfitUpdate;

nonces

Mapping of addresses to their current nonce for EIP-2612 permit

Incremented each time permit() is successfully called Prevents replay attacks on signed approvals

mapping(address => uint256) public override nonces;

_locked

Reentrancy guard lock state

True when a nonReentrant function is executing

bool private _locked;

Functions

nonReentrant

Prevents reentrancy attacks on state-changing functions

Uses simple lock flag. Applied to deposit, withdraw, redeem, updateDebt, and processReport Reverts with Reentrancy() error if reentrant call is detected

modifier nonReentrant();

constructor

Constructor that prevents reinitialization

Sets asset to a non-zero value (this contract address) to prevent initialize() from being called Actual initialization happens via initialize() when deployed through factory

constructor();

initialize

Initialize a new vault with core parameters

Can only be called once per deployment (checked via asset == address(0)) Called by factory immediately after deployment. Sets all critical vault parameters

Note: security: Only callable once due to asset check. All parameters are immutable except profitMaxUnlockTime

function initialize(
address asset_,
string memory name_,
string memory symbol_,
address roleManager_,
uint256 profitMaxUnlockTime_
) public virtual override;

Parameters

NameTypeDescription
asset_addressAddress of the underlying ERC20 asset token (cannot be zero address)
name_stringHuman-readable name for the vault token (e.g., "Octant ETH Vault")
symbol_stringToken symbol for the vault token (e.g., "ovETH")
roleManager_addressAddress that can manage role assignments (cannot be zero address)
profitMaxUnlockTime_uint256Duration for profit unlocking in seconds (0-31556952, max 1 year)

setName

Updates the vault token name

ERC20 metadata update. Does not affect existing approvals or balances

Note: security: Only callable by roleManager

function setName(string memory name_) external override;

Parameters

NameTypeDescription
name_stringNew name for the vault token

setSymbol

Updates the vault token symbol

ERC20 metadata update. Does not affect existing approvals or balances

Note: security: Only callable by roleManager

function setSymbol(string memory symbol_) external override;

Parameters

NameTypeDescription
symbol_stringNew symbol ticker for the vault token

setAccountant

Sets the accountant contract for fee assessment

Accountant is called during processReport() to calculate fees and refunds Set to address(0) to disable fee assessment

Note: security: Only callable by ACCOUNTANT_MANAGER role

function setAccountant(address newAccountant_) external override;

Parameters

NameTypeDescription
newAccountant_addressAddress of the new accountant contract (or address(0) to disable)

setDefaultQueue

Sets the default withdrawal queue

Validates that all strategies are active but does NOT check for duplicates WARNING: Adding the same strategy twice will cause incorrect maxRedeem/maxWithdraw values Queue order determines withdrawal priority (index 0 = highest priority)

Note: security: Only callable by QUEUE_MANAGER role

function setDefaultQueue(address[] calldata newDefaultQueue_) external override;

Parameters

NameTypeDescription
newDefaultQueue_address[]Array of strategy addresses (maximum length MAX_QUEUE = 10)

setUseDefaultQueue

Sets whether to force use of default withdrawal queue

When true, custom withdrawal queues passed to withdraw/redeem are ignored Useful for ensuring consistent withdrawal behavior across all users

Note: security: Only callable by QUEUE_MANAGER role

function setUseDefaultQueue(bool useDefaultQueue_) external override;

Parameters

NameTypeDescription
useDefaultQueue_boolTrue to force default queue, false to allow custom queues

setAutoAllocate

Sets whether to automatically allocate deposits to strategies

When true, deposits are automatically sent to _defaultQueue[0] via updateDebt() WARNING: Requires non-empty default queue or all deposits will fail Deposits become atomic (deposit + allocation) which increases gas cost

Note: security: Only callable by DEBT_MANAGER role

function setAutoAllocate(bool autoAllocate_) external override;

Parameters

NameTypeDescription
autoAllocate_boolTrue to enable auto-allocation, false to keep deposits idle

setDepositLimit

Sets the maximum total assets the vault can hold

Cannot be changed if depositLimitModule is set unless shouldOverride_ is true Reverts if vault is shutdown

Notes:

  • security: Only callable by DEPOSIT_LIMIT_MANAGER role

  • security: Reverts if vault is shutdown

function setDepositLimit(uint256 depositLimit_, bool shouldOverride_) external override;

Parameters

NameTypeDescription
depositLimit_uint256New maximum total assets (use type(uint256).max for unlimited)
shouldOverride_boolIf true, clears depositLimitModule to allow setting depositLimit

setDepositLimitModule

Sets a module contract to dynamically control deposit limits

Module overrides static depositLimit. Requires depositLimit = type(uint256).max or shouldOverride_ = true. Reverts if vault is shutdown

Notes:

  • security: Only callable by DEPOSIT_LIMIT_MANAGER role

  • security: Reverts if vault is shutdown

function setDepositLimitModule(address depositLimitModule_, bool shouldOverride_) external override;

Parameters

NameTypeDescription
depositLimitModule_addressAddress of IDepositLimitModule contract (or address(0) to disable)
shouldOverride_boolIf true, automatically sets depositLimit to type(uint256).max

setWithdrawLimitModule

Sets a module contract to dynamically control withdraw limits

Module overrides standard maxWithdraw() calculation Module must implement IWithdrawLimitModule.availableWithdrawLimit()

Note: security: Only callable by WITHDRAW_LIMIT_MANAGER role

function setWithdrawLimitModule(address withdrawLimitModule_) external override;

Parameters

NameTypeDescription
withdrawLimitModule_addressAddress of IWithdrawLimitModule contract (or address(0) to disable)

setMinimumTotalIdle

Sets the minimum amount of assets to keep idle in the vault

Acts as buffer for cheap withdrawals. updateDebt() maintains this minimum Set to 0 to disable minimum idle requirement

Note: security: Only callable by MINIMUM_IDLE_MANAGER role

function setMinimumTotalIdle(uint256 minimumTotalIdle_) external override;

Parameters

NameTypeDescription
minimumTotalIdle_uint256Minimum idle assets (0 = no minimum)

setProfitMaxUnlockTime

Sets the duration over which profits are gradually unlocked

*Prevents PPS manipulation by time-locking profit shares IMPORTANT BEHAVIORS:

  • Setting to 0: Instantly unlocks all locked profits (immediate PPS increase)
  • Setting to non-zero: Next report will use new duration, current lock continues with old rate CONSTRAINTS:
  • Maximum value: 31,556,952 seconds (1 year)
  • When set to 0, burns all locked shares and resets unlocking variables*

Notes:

  • security: Only callable by PROFIT_UNLOCK_MANAGER role

  • security: Setting to 0 causes immediate PPS change

function setProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime_) external override;

Parameters

NameTypeDescription
newProfitMaxUnlockTime_uint256New unlock duration in seconds (0-31556952)

_enforceRole

Enforces that an account has the required role

Note: security: Reverts with NotAllowed() if account lacks the role

function _enforceRole(address account_, Roles role_) internal view;

Parameters

NameTypeDescription
account_addressAddress to check role for
role_RolesRequired role enum value

setRole

Sets the complete role bitmask for an account

OVERWRITES all existing roles - must include all desired roles in bitmask Use addRole() or removeRole() to modify individual roles Bitmask calculated as: (1 << role1) | (1 << role2) | ...

Note: security: Only callable by roleManager

function setRole(address account_, uint256 rolesBitmask_) external override;

Parameters

NameTypeDescription
account_addressAddress to set roles for
rolesBitmask_uint256Complete role bitmask (overwrites existing)

addRole

Adds a role to an account without affecting existing roles

Uses bitwise OR to add role while preserving others Account can hold multiple roles simultaneously

Note: security: Only callable by roleManager

function addRole(address account_, Roles role_) external override;

Parameters

NameTypeDescription
account_addressAddress to grant role to
role_RolesRole enum value to add (single role only)

removeRole

Removes a role from an account without affecting other roles

Uses bitwise AND with NOT to remove role while preserving others Does not revert if account doesn't have the role

Note: security: Only callable by roleManager

function removeRole(address account_, Roles role_) external override;

Parameters

NameTypeDescription
account_addressAddress to revoke role from
role_RolesRole enum value to remove (single role only)

transferRoleManager

Initiates role manager transfer (step 1 of 2)

Two-step process prevents accidental transfer to wrong/inaccessible address Sets futureRoleManager which must then call acceptRoleManager()

Note: security: Only callable by current roleManager

function transferRoleManager(address roleManager_) external override;

Parameters

NameTypeDescription
roleManager_addressAddress of the new role manager

acceptRoleManager

Completes role manager transfer (step 2 of 2)

Caller must be the futureRoleManager set by transferRoleManager() Clears futureRoleManager and updates roleManager to caller

Note: security: Only callable by futureRoleManager address

function acceptRoleManager() external override;

isShutdown

Returns whether the vault has been shutdown

Shutdown is permanent and cannot be reversed When shutdown, only withdrawals are allowed

function isShutdown() external view override returns (bool);

Returns

NameTypeDescription
&lt;none&gt;boolTrue if vault is shutdown, false otherwise

unlockedShares

Returns the amount of profit shares that have unlocked since last update

Calculates time-weighted unlocking based on _profitUnlockingRate and elapsed time Returns total locked shares if unlock period has ended

function unlockedShares() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256Amount of shares unlocked, typically 18 decimals)

pricePerShare

Returns the current price per share (PPS)

PRECISION WARNING: Limited precision due to division. For exact calculations use convertToAssets() or convertToShares() instead Formula: (10^decimals * totalAssets) / totalSupply Returns 10^decimals when totalSupply = 0 (1:1 initial ratio)

function pricePerShare() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256Price per share (e.g., 1.05e18 = 1.05 assets per share)

getDefaultQueue

Returns the default withdrawal queue array

Array of strategy addresses in withdrawal priority order Maximum length is MAX_QUEUE (10)

function getDefaultQueue() external view returns (address[] memory);

Returns

NameTypeDescription
&lt;none&gt;address[]Array of strategy addresses

processReport

Processes a strategy's performance report and updates vault accounting

*CRITICAL FUNCTION for vault accounting. Updates gains/losses, assesses fees, and locks profits PROCESS OVERVIEW:

  1. Calculate gain/loss by comparing strategy assets vs recorded debt
  2. Assess fees and refunds via accountant (if set)
  3. Calculate shares to burn (for losses/fees) and shares to lock (for profits)
  4. Update share supply and locked shares accordingly
  5. Pull refunds from accountant if any
  6. Update strategy debt tracking
  7. Issue fee shares to accountant and protocol
  8. Update profit unlocking rate and schedule IMPORTANT NOTES:
  • Strategy's convertToAssets() MUST NOT be manipulable or vault reports incorrect gains/losses
  • Pass address(this) as strategy_ to accrue airdrops into totalIdle
  • Profit locking prevents PPS manipulation by gradually releasing profits over time
  • Fees are taken as shares, reducing profit for existing shareholders ACCOUNTING INVARIANTS:
  • totalAssets = totalIdle + totalDebt
  • totalSupply = circulating shares + locked shares*

Notes:

  • security: Only callable by REPORTING_MANAGER role

  • security: Reentrancy protected

  • security: Strategy convertToAssets() must be manipulation-resistant

function processReport(address strategy_) external nonReentrant returns (uint256, uint256);

Parameters

NameTypeDescription
strategy_addressAddress of strategy to report (or address(this) to report vault airdrops)

Returns

NameTypeDescription
&lt;none&gt;uint256gain Amount of profit generated since last report
&lt;none&gt;uint256loss Amount of loss incurred since last report

buyDebt

Emergency function to purchase bad debt from a strategy

*EMERGENCY USE ONLY: Alternative to force revoking to avoid reporting losses MECHANISM:

  • Transfers assets from caller to vault
  • Transfers proportional strategy shares from vault to caller
  • Reduces strategy debt and converts to idle assets USE CASES:
  • Strategy is underwater but shares still have value
  • Governance wants to absorb loss without affecting vault PPS
  • Allows time to recover or liquidate strategy position separately WARNINGS:
  • Does not rely on strategy's conversion rates (assumes issues)
  • Caller receives strategy shares at recorded debt ratio
  • Caller assumes all risk of strategy position*

Notes:

  • security: Only callable by DEBT_PURCHASER role

  • security: Reentrancy protected

function buyDebt(address strategy_, uint256 amount_) external override nonReentrant;

Parameters

NameTypeDescription
strategy_addressAddress of the strategy to buy debt from (must be active)
amount_uint256Amount of debt to purchase (capped at current debt)

addStrategy

Adds a new strategy to the vault

*Validates strategy compatibility and initializes tracking Strategy MUST be ERC4626-compliant with matching asset REQUIREMENTS:

  • Strategy address cannot be zero or vault address
  • Strategy asset must match vault asset
  • Strategy cannot already be active INITIALIZATION:
  • Sets activation timestamp to current block
  • Initializes currentDebt and maxDebt to 0
  • Optionally adds to default queue if space available (max 10)*

Note: security: Only callable by ADD_STRATEGY_MANAGER role

function addStrategy(address newStrategy_, bool addToQueue_) external override;

Parameters

NameTypeDescription
newStrategy_addressAddress of the ERC4626 strategy to add
addToQueue_boolIf true, adds to default queue (if queue has space)

revokeStrategy

Revokes a strategy (soft revoke)

Removes strategy from vault. REQUIRES strategy to have zero debt Use updateDebt() to withdraw all funds before revoking Strategy is removed from default queue if present Strategy can be re-added later if needed

Note: security: Only callable by REVOKE_STRATEGY_MANAGER role

function revokeStrategy(address strategy_) external override;

Parameters

NameTypeDescription
strategy_addressAddress of the strategy to revoke (must have currentDebt = 0)

forceRevokeStrategy

Force revokes a strategy and realizes any outstanding debt as loss

*DANGEROUS OPERATION: Immediately writes off all strategy debt as vault loss WARNING: Use as last resort only!

  • Reduces totalDebt by strategy's currentDebt
  • Does NOT attempt to withdraw funds
  • Loss affects all vault shareholders
  • Strategy is permanently removed from vault BEFORE USING:
  1. Try updateDebt() to withdraw all possible funds
  2. Consider buyDebt() if strategy shares have value
  3. Only force revoke if strategy is completely unrecoverable RECOVERY:
  • If removed erroneously, strategy can be re-added
  • Any recovered funds will be credited as profit (with fees)*

Notes:

  • security: Only callable by FORCE_REVOKE_MANAGER role

  • security: Realizes immediate loss for all shareholders

function forceRevokeStrategy(address strategy_) external override;

Parameters

NameTypeDescription
strategy_addressAddress of the strategy to force revoke

updateMaxDebtForStrategy

Sets the maximum debt allowed for a strategy

Controls how much of vault's assets can be allocated to this strategy updateDebt() will respect this cap when allocating funds Set to 0 to prevent new allocations (existing debt remains)

Note: security: Only callable by MAX_DEBT_MANAGER role

function updateMaxDebtForStrategy(address strategy_, uint256 newMaxDebt_) external override;

Parameters

NameTypeDescription
strategy_addressAddress of the strategy (must be active)
newMaxDebt_uint256Maximum debt (0 = no new allocations)

updateDebt

Rebalances the debt allocation for a strategy

*Moves funds between vault and strategy to reach target debt level OPERATIONS:

  • If targetDebt_ > currentDebt: Deposits idle assets to strategy
  • If targetDebt_ < currentDebt: Withdraws assets from strategy
  • Respects strategy's maxDebt limit
  • Maintains vault's minimumTotalIdle if configured SPECIAL VALUES:
  • targetDebt_ = type(uint256).max: Deposit all available idle (up to maxDebt)
  • targetDebt_ = 0: Withdraw all assets from strategy LOSS HANDLING:
  • maxLoss_ parameter caps acceptable loss in basis points (0-10000)
  • Reverts if realized loss exceeds maxLoss_
  • Common values: 0 (no loss), 100 (1%), 10000 (100% - accept any loss)*

Notes:

  • security: Only callable by DEBT_MANAGER role

  • security: Reentrancy protected

function updateDebt(address strategy_, uint256 targetDebt_, uint256 maxLoss_)
external
override
nonReentrant
returns (uint256);

Parameters

NameTypeDescription
strategy_addressAddress of the strategy to rebalance (must be active)
targetDebt_uint256Target debt (or type(uint256).max for max allocation)
maxLoss_uint256Maximum acceptable loss in basis points (0-10000, where 10000 = 100%)

Returns

NameTypeDescription
&lt;none&gt;uint256newDebt The new current debt after rebalancing

_updateDebt

function _updateDebt(address strategy_, uint256 targetDebt_, uint256 maxLoss_) internal returns (uint256);

shutdownVault

Permanently shuts down the vault

*IRREVERSIBLE OPERATION - cannot be undone EFFECTS:

  • Sets _shutdown = true permanently
  • Disables all deposits (depositLimit = 0)
  • Clears depositLimitModule
  • Grants DEBT_MANAGER role to caller for emergency withdrawals
  • Withdrawals remain available USE CASES:
  • Critical vulnerability discovered
  • Strategy failures requiring user protection
  • Planned vault migration/deprecation POST-SHUTDOWN:
  • Users can withdraw funds
  • No new deposits accepted
  • Debt manager can rebalance strategies
  • Vault continues operating in withdrawal-only mode*

Notes:

  • security: Only callable by EMERGENCY_MANAGER role

  • security: IRREVERSIBLE - use with extreme caution

function shutdownVault() external override;

deposit

Deposits assets into the vault and mints shares to receiver

*ERC4626-compliant deposit function. Converts assets to shares using current PPS CONVERSION:

  • Uses ROUND_DOWN when calculating shares (favors vault)
  • shares = (assets * totalSupply) / totalAssets
  • First deposit: 1:1 ratio (1 asset = 1 share) SPECIAL VALUES:
  • assets_ = type(uint256).max: Deposits caller's full asset balance BEHAVIOR:
  • Transfers assets from msg.sender to vault
  • Increases totalIdle
  • Mints shares to receiver
  • If autoAllocate is true, automatically allocates to defaultQueue[0] REQUIREMENTS:
  • Vault not shutdown
  • Amount > 0
  • Amount <= maxDeposit(receiver)
  • If autoAllocate, defaultQueue must not be empty*

Note: security: Reentrancy protected

function deposit(uint256 assets_, address receiver_) external virtual nonReentrant returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to deposit (or type(uint256).max for full balance)
receiver_addressAddress to receive the minted vault shares

Returns

NameTypeDescription
&lt;none&gt;uint256shares Amount of shares minted to receiver

mint

Mints exact amount of shares by depositing required assets

*ERC4626-compliant mint function. Calculates assets needed for exact share amount CONVERSION:

  • Uses ROUND_UP when calculating assets (favors vault)
  • assets = (shares * totalAssets) / totalSupply + 1
  • First deposit: 1:1 ratio (1 share = 1 asset) BEHAVIOR:
  • Calculates assets required for shares amount
  • Transfers calculated assets from msg.sender
  • Mints exact shares_ amount to receiver
  • If autoAllocate is true, automatically allocates to defaultQueue[0]*

Note: security: Reentrancy protected

function mint(uint256 shares_, address receiver_) external virtual nonReentrant returns (uint256);

Parameters

NameTypeDescription
shares_uint256Exact amount of shares to mint
receiver_addressAddress to receive the minted vault shares

Returns

NameTypeDescription
&lt;none&gt;uint256assets Amount of assets deposited from caller

withdraw

Withdraws assets from the vault by burning owner's shares

*ERC4626-extended withdraw function with loss tolerance and custom queue CONVERSION:

  • Uses ROUND_UP when calculating shares to burn (favors vault)
  • shares = (assets * totalSupply) / totalAssets + 1 WITHDRAWAL FLOW:
  1. Burns calculated shares from owner
  2. Pulls from idle assets first
  3. If insufficient idle, withdraws from strategies in queue order
  4. Handles unrealized losses according to maxLoss parameter
  5. Transfers assets to receiver QUEUE BEHAVIOR:
  • Empty array + useDefaultQueue=false: Uses default queue
  • Empty array + useDefaultQueue=true: Uses default queue
  • Custom array + useDefaultQueue=false: Uses custom queue
  • Custom array + useDefaultQueue=true: Ignores custom, uses default LOSS HANDLING:
  • maxLoss_ = 0: No loss accepted (default, reverts on any loss)
  • maxLoss_ = 100: Accept up to 1% loss
  • maxLoss_ = 10000: Accept any loss (100%)
  • Users receive proportional share of unrealized losses ALLOWANCE:
  • If msg.sender != owner, requires sufficient allowance
  • Spends allowance (unless type(uint256).max)*

Note: security: Reentrancy protected

function withdraw(
uint256 assets_,
address receiver_,
address owner_,
uint256 maxLoss_,
address[] calldata strategiesArray_
) public virtual override nonReentrant returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to withdraw
receiver_addressAddress to receive the withdrawn assets
owner_addressAddress whose shares will be burned
maxLoss_uint256Maximum acceptable loss in basis points (0-10000, default 0 = no loss)
strategiesArray_address[]Optional custom withdrawal queue (empty = use default)

Returns

NameTypeDescription
&lt;none&gt;uint256shares Amount of shares actually burned from owner

redeem

Redeems exact amount of shares for assets

*ERC4626-extended redeem function with loss tolerance and custom queue CONVERSION:

  • Uses ROUND_DOWN when calculating assets (favors vault)
  • assets = (shares * totalAssets) / totalSupply BEHAVIOR:
  • Burns exact shares_ amount from owner
  • Calculates assets to withdraw based on shares
  • Follows same withdrawal flow as withdraw()
  • May return less assets than expected if losses occur DIFFERENCE FROM WITHDRAW:
  • withdraw(): User specifies assets, function calculates shares
  • redeem(): User specifies shares, function calculates assets
  • redeem() may return less assets than preview if losses occur*

Note: security: Reentrancy protected

function redeem(
uint256 shares_,
address receiver_,
address owner_,
uint256 maxLoss_,
address[] calldata strategiesArray_
) public virtual override nonReentrant returns (uint256);

Parameters

NameTypeDescription
shares_uint256Exact amount of shares to burn
receiver_addressAddress to receive the withdrawn assets
owner_addressAddress whose shares will be burned
maxLoss_uint256Maximum acceptable loss in basis points (0-10000, default 10000 = accept all)
strategiesArray_address[]Optional custom withdrawal queue (empty = use default)

Returns

NameTypeDescription
&lt;none&gt;uint256assets Amount of assets actually withdrawn and sent to receiver

approve

Approves spender to transfer shares on behalf of caller

ERC20-compliant approve function

function approve(address spender_, uint256 amount_) external override returns (bool);

Parameters

NameTypeDescription
spender_addressAddress authorized to spend shares
amount_uint256Amount of shares to approve (type(uint256).max for unlimited)

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True (reverts on failure)

transfer

Transfers shares from caller to receiver

ERC20-compliant transfer function Prevents transfers to vault address or zero address

function transfer(address receiver_, uint256 amount_) external override returns (bool);

Parameters

NameTypeDescription
receiver_addressAddress to receive shares (cannot be vault or zero address)
amount_uint256Amount of shares to transfer

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True (reverts on failure)

transferFrom

Transfers shares from sender to receiver using allowance

ERC20-compliant transferFrom function Requires sufficient allowance from sender to caller Prevents transfers to vault address or zero address

function transferFrom(address sender_, address receiver_, uint256 amount_) external override returns (bool);

Parameters

NameTypeDescription
sender_addressAddress to transfer shares from (requires allowance)
receiver_addressAddress to receive shares (cannot be vault or zero address)
amount_uint256Amount of shares to transfer

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True (reverts on failure)

permit

Approves spender using EIP-2612 signature (gasless approval)

Allows approval without on-chain transaction from owner Uses EIP-712 structured data signing Increments nonce to prevent replay attacks

Note: security: Validates signature against owner and increments nonce

function permit(address owner_, address spender_, uint256 amount_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_)
external
override
returns (bool);

Parameters

NameTypeDescription
owner_addressAddress of the share owner granting approval
spender_addressAddress authorized to spend shares
amount_uint256Amount of shares to approve
deadline_uint256Unix timestamp after which signature expires
v_uint8ECDSA signature v component
r_bytes32ECDSA signature r component
s_bytes32ECDSA signature s component

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True (reverts if signature invalid or expired)

balanceOf

Returns the share balance of an account

For vault address, excludes shares that have unlocked from profit locking For all other addresses, returns full balance

function balanceOf(address addr_) public view override returns (uint256);

Parameters

NameTypeDescription
addr_addressAddress to query balance for

Returns

NameTypeDescription
&lt;none&gt;uint256balance Share balance, typically 18 decimals)

totalSupply

Returns the circulating supply of vault shares

Excludes locked shares that haven't unlocked yet Formula: _totalSupplyValue - _unlockedShares()

function totalSupply() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256supply Total circulating shares

totalAssets

Returns total assets under management

Formula: totalIdle + totalDebt Includes both idle assets and assets deployed to strategies

Note: invariant: totalAssets = totalIdle + totalDebt

function totalAssets() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256assets Total assets

totalIdle

Returns amount of assets held idle in the vault

Assets available for immediate withdrawal without touching strategies

function totalIdle() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256idle Idle assets

totalDebt

Returns total assets deployed across all strategies

Sum of currentDebt for all active strategies

function totalDebt() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256debt Total debt

convertToShares

Converts asset amount to equivalent shares

Uses ROUND_DOWN (favors vault) Formula: (assets * totalSupply) / totalAssets

function convertToShares(uint256 assets_) external view override returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to convert

Returns

NameTypeDescription
&lt;none&gt;uint256shares Equivalent amount of shares

previewDeposit

Previews shares that would be minted for a deposit

Identical to convertToShares() for this implementation

function previewDeposit(uint256 assets_) external view override returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to deposit

Returns

NameTypeDescription
&lt;none&gt;uint256shares Amount of shares that would be minted

previewMint

Previews assets required to mint exact share amount

Uses ROUND_UP (favors vault)

function previewMint(uint256 shares_) external view override returns (uint256);

Parameters

NameTypeDescription
shares_uint256Amount of shares to mint

Returns

NameTypeDescription
&lt;none&gt;uint256assets Amount of assets required

convertToAssets

Converts share amount to equivalent assets

Uses ROUND_DOWN (favors vault) Formula: (shares * totalAssets) / totalSupply

function convertToAssets(uint256 shares_) external view override returns (uint256);

Parameters

NameTypeDescription
shares_uint256Amount of shares to convert

Returns

NameTypeDescription
&lt;none&gt;uint256assets Equivalent amount of assets

defaultQueue

Returns the default withdrawal queue

Same as getDefaultQueue()

function defaultQueue() external view override returns (address[] memory);

Returns

NameTypeDescription
&lt;none&gt;address[]queue Array of strategy addresses in withdrawal priority order

maxDeposit

Returns maximum assets that can be deposited for a receiver

Checks against depositLimit or depositLimitModule (if set) Returns 0 if receiver is vault address or zero address

function maxDeposit(address receiver_) external view override returns (uint256);

Parameters

NameTypeDescription
receiver_addressAddress that would receive the shares

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum deposit amount (0 if deposits disabled)

maxMint

Returns maximum shares that can be minted for a receiver

Converts maxDeposit to shares using current PPS

function maxMint(address receiver_) external view override returns (uint256);

Parameters

NameTypeDescription
receiver_addressAddress that would receive the shares

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum mint amount (0 if deposits disabled)

maxWithdraw

Returns maximum assets that owner can withdraw

*ERC4626-extended with custom loss tolerance and withdrawal queue CALCULATION:

  1. Calculates owner's share value in assets
  2. Checks idle assets availability
  3. Simulates withdrawal through strategy queue
  4. Accounts for unrealized losses per maxLoss tolerance
  5. Checks withdrawLimitModule if set WARNING: Incorrect queue ordering may return inaccurate values Use default queue or ensure custom queue is properly ordered*
function maxWithdraw(address owner_, uint256 maxLoss_, address[] calldata strategiesArray_)
external
view
virtual
override
returns (uint256);

Parameters

NameTypeDescription
owner_addressAddress that owns the shares
maxLoss_uint256Maximum acceptable loss in basis points (0-10000)
strategiesArray_address[]Custom withdrawal queue (empty = use default)

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum withdrawable assets

maxRedeem

Returns maximum shares that owner can redeem

*ERC4626-extended with custom loss tolerance and withdrawal queue Returns minimum of:

  • Shares equivalent of maxWithdraw
  • Owner's full share balance WARNING: Incorrect queue ordering may return inaccurate values*
function maxRedeem(address owner_, uint256 maxLoss_, address[] calldata strategiesArray_)
external
view
virtual
override
returns (uint256);

Parameters

NameTypeDescription
owner_addressAddress that owns the shares
maxLoss_uint256Maximum acceptable loss in basis points (0-10000)
strategiesArray_address[]Custom withdrawal queue (empty = use default)

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum redeemable shares

previewWithdraw

Previews shares that would be burned for an asset withdrawal

Uses ROUND_UP (favors vault, user burns slightly more shares)

function previewWithdraw(uint256 assets_) external view override returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to withdraw

Returns

NameTypeDescription
&lt;none&gt;uint256shares Amount of shares that would be burned

previewRedeem

Previews assets that would be withdrawn for a share redemption

Uses ROUND_DOWN (favors vault, user receives slightly less assets) Actual withdrawal may be less if losses occur

function previewRedeem(uint256 shares_) external view override returns (uint256);

Parameters

NameTypeDescription
shares_uint256Amount of shares to redeem

Returns

NameTypeDescription
&lt;none&gt;uint256assets Amount of assets that would be withdrawn

FACTORY

Returns the factory address that deployed this vault

Used to query protocol fee configuration

function FACTORY() external view override returns (address);

Returns

NameTypeDescription
&lt;none&gt;addressfactory Address of MultistrategyVaultFactory

apiVersion

Returns the API version of this vault implementation

Based on Yearn V3 vault versioning

function apiVersion() external pure override returns (string memory);

Returns

NameTypeDescription
&lt;none&gt;stringversion API version string (e.g., "3.0.4")

assessShareOfUnrealisedLosses

Calculates the unrealized losses for a withdrawal from strategy

Compares strategy's actual asset value vs recorded debt If strategy is underwater, user takes proportional share of loss Formula: loss = assetsNeeded - (assetsNeeded * strategyAssets / currentDebt)

function assessShareOfUnrealisedLosses(address strategy_, uint256 assetsNeeded_) external view returns (uint256);

Parameters

NameTypeDescription
strategy_addressAddress of the strategy
assetsNeeded_uint256Amount of assets to withdraw from strategy

Returns

NameTypeDescription
&lt;none&gt;uint256loss User's share of unrealized losses

profitMaxUnlockTime

Returns the configured profit unlocking duration

Time period over which new profits are gradually released

function profitMaxUnlockTime() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256duration Unlock duration in seconds (0-31556952, max 1 year)

fullProfitUnlockDate

Returns when all currently locked profits will be fully unlocked

Unix timestamp. Returns 0 if no profits are locked

function fullProfitUnlockDate() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256timestamp Unix timestamp in seconds (0 = no active unlocking)

profitUnlockingRate

Returns the per-second profit unlock rate

Denominated in MAX_BPS_EXTENDED precision (1e12) Rate = (totalLockedShares * MAX_BPS_EXTENDED) / unlockPeriod

function profitUnlockingRate() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256rate Shares unlocked per second (in 1e12 precision)

lastProfitUpdate

Returns when profit unlocking was last updated

Updated whenever shares are locked (during processReport)

function lastProfitUpdate() external view override returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256timestamp Unix timestamp in seconds

assessShareOfUnrealisedLosses

Assess the share of unrealised losses that a strategy has.

function assessShareOfUnrealisedLosses(address strategy, uint256 currentDebt, uint256 assetsNeeded)
external
view
returns (uint256);

Parameters

NameTypeDescription
strategyaddressThe address of the strategy.
currentDebtuint256The current debt of the strategy
assetsNeededuint256The amount of assets needed to be withdrawn

Returns

NameTypeDescription
&lt;none&gt;uint256The share of unrealised losses that the strategy has

DOMAIN_SEPARATOR

Get the domain separator for EIP-712.

function DOMAIN_SEPARATOR() public view override returns (bytes32);

Returns

NameTypeDescription
&lt;none&gt;bytes32The domain separator.

strategies

Returns the parameters for a specific strategy

Returns full StrategyParams struct including activation, lastReport, currentDebt, maxDebt

function strategies(address strategy_) external view returns (StrategyParams memory);

Parameters

NameTypeDescription
strategy_addressAddress of the strategy to query

Returns

NameTypeDescription
&lt;none&gt;StrategyParamsparams Strategy parameters struct

_spendAllowance

Spends allowance from owner to spender

function _spendAllowance(address owner_, address spender_, uint256 amount_) internal;

Parameters

NameTypeDescription
owner_addressOwner of the shares
spender_addressAddress spending the shares
amount_uint256Amount of allowance to spend

_transfer

Transfers shares from sender to receiver

function _transfer(address sender_, address receiver_, uint256 amount_) internal virtual;

Parameters

NameTypeDescription
sender_addressAddress sending shares
receiver_addressAddress receiving shares
amount_uint256Amount of shares to transfer

_approve

Sets approval of spender for owner's shares

function _approve(address owner_, address spender_, uint256 amount_) internal returns (bool);

Parameters

NameTypeDescription
owner_addressOwner granting approval
spender_addressAddress being approved
amount_uint256Amount of shares approved

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True on success

_permit

Implementation of the permit function (EIP-2612)

function _permit(address owner_, address spender_, uint256 amount_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_)
internal
returns (bool);

Parameters

NameTypeDescription
owner_addressOwner granting approval
spender_addressAddress being approved
amount_uint256Amount of shares approved
deadline_uint256Expiration timestamp
v_uint8Signature v component
r_bytes32Signature r component
s_bytes32Signature s component

Returns

NameTypeDescription
&lt;none&gt;boolsuccess True on success

_burnShares

Burns shares from an account

function _burnShares(uint256 shares_, address owner_) internal;

Parameters

NameTypeDescription
shares_uint256Amount of shares to burn
owner_addressAddress to burn shares from

_unlockedShares

Calculates amount of profit shares that have unlocked since last update

function _unlockedShares() internal view returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256unlockedSharesAmount Amount of shares unlocked

_totalSupply

Returns circulating supply excluding locked shares

function _totalSupply() internal view returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256supply Total supply minus unlocked shares

_totalAssets

Returns total assets under management

function _totalAssets() internal view returns (uint256);

Returns

NameTypeDescription
&lt;none&gt;uint256assets Sum of idle and debt

_convertToAssets

Converts shares to assets with rounding control

function _convertToAssets(uint256 shares_, Rounding rounding_) internal view returns (uint256);

Parameters

NameTypeDescription
shares_uint256Amount of shares to convert
rounding_RoundingRounding direction (ROUND_UP or ROUND_DOWN)

Returns

NameTypeDescription
&lt;none&gt;uint256assets Equivalent amount of assets

_convertToShares

Converts assets to shares with rounding control

function _convertToShares(uint256 assets_, Rounding rounding_) internal view returns (uint256);

Parameters

NameTypeDescription
assets_uint256Amount of assets to convert
rounding_RoundingRounding direction (ROUND_UP or ROUND_DOWN)

Returns

NameTypeDescription
&lt;none&gt;uint256shares Equivalent amount of shares

_issueShares

Mints shares to a recipient

function _issueShares(uint256 shares_, address recipient_) internal;

Parameters

NameTypeDescription
shares_uint256Amount of shares to mint
recipient_addressAddress to receive shares

_maxDeposit

Calculates maximum deposit possible for a receiver

function _maxDeposit(address receiver_) internal view returns (uint256);

Parameters

NameTypeDescription
receiver_addressAddress that would receive shares

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum deposit amount

_maxWithdraw

Calculates maximum withdrawal possible for an owner

function _maxWithdraw(address owner_, uint256 maxLoss_, address[] memory strategiesParam_)
internal
view
returns (uint256);

Parameters

NameTypeDescription
owner_addressAddress that owns shares
maxLoss_uint256Maximum acceptable loss in basis points
strategiesParam_address[]Custom withdrawal queue

Returns

NameTypeDescription
&lt;none&gt;uint256max Maximum withdrawable assets

_deposit

Handles deposit logic with idle tracking and auto-allocation

function _deposit(address recipient_, uint256 assets_, uint256 shares_) internal;

Parameters

NameTypeDescription
recipient_addressAddress to receive shares
assets_uint256Amount of assets to deposit
shares_uint256Amount of shares to mint

_assessShareOfUnrealisedLosses

Calculates user's share of unrealized losses from a strategy

function _assessShareOfUnrealisedLosses(address strategy_, uint256 strategyCurrentDebt_, uint256 assetsNeeded_)
internal
view
returns (uint256);

Parameters

NameTypeDescription
strategy_addressStrategy address
strategyCurrentDebt_uint256Strategy's recorded debt
assetsNeeded_uint256Amount to withdraw

Returns

NameTypeDescription
&lt;none&gt;uint256loss User's proportional share of losses

_addStrategy

Adds and initializes a new strategy

function _addStrategy(address newStrategy_, bool addToQueue_) internal;

Parameters

NameTypeDescription
newStrategy_addressStrategy address to add
addToQueue_boolWhether to add to default queue

_redeem

Handles redemption logic including strategy withdrawals

function _redeem(
address sender_,
address receiver_,
address owner_,
uint256 assets_,
uint256 shares_,
uint256 maxLoss_,
address[] memory strategiesParam_
) internal returns (uint256);

Parameters

NameTypeDescription
sender_addressCaller initiating redemption
receiver_addressAddress to receive assets
owner_addressAddress whose shares are burned
assets_uint256Target amount of assets
shares_uint256Amount of shares to burn
maxLoss_uint256Maximum acceptable loss in basis points
strategiesParam_address[]Custom withdrawal queue

Returns

NameTypeDescription
&lt;none&gt;uint256withdrawn Actual amount of assets withdrawn

_revokeStrategy

Revokes a strategy and handles loss accounting if forced

function _revokeStrategy(address strategy, bool force) internal;

Parameters

NameTypeDescription
strategyaddressStrategy address to revoke
forceboolWhether to force revoke with debt

_safeTransferFrom

Safely transfers ERC20 tokens handling non-standard implementations

Handles tokens that don't return bool (e.g., USDT)

function _safeTransferFrom(address token, address sender, address receiver, uint256 amount) internal;

Parameters

NameTypeDescription
tokenaddressToken address
senderaddressAddress to transfer from
receiveraddressAddress to transfer to
amountuint256Amount to transfer

_safeTransfer

Safely transfers ERC20 tokens handling non-standard implementations

Handles tokens that don't return bool (e.g., USDT)

function _safeTransfer(address token, address receiver, uint256 amount) internal;

Parameters

NameTypeDescription
tokenaddressToken address
receiveraddressAddress to transfer to
amountuint256Amount to transfer