VaultCoreLib
Title: VaultCoreLib
Authors: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80), Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC4626.sol)
Library extending ERC-4626 vaults with strategy hooks for yield aggregation.
All logic lives here; the VaultCore facet is a pure delegator. Storage is accessed via ERC-7201 namespaced slot to avoid collisions in the Diamond proxy.
Architecture:
- The vault holds "idle" assets (its own ERC-20 balance of the underlying).
- When a StrategyManager is configured, it can direct the vault to PUSH assets to
external strategies via
allocateToStrategy. The strategy later PUSHES back via its ownwithdrawcall. totalAssets()is overridden to include both idle and allocated assets. ERC4626Lib prices shares by self-staticcalling the diamond'stotalAssets(), so conversions, previews and mutators all use this full NAV, whilemaxWithdraw/maxRedeemstay capped at idle assets.- After a strategy force removal the manager latches deposits closed (depositsLatched): deposit/mint
revert and
maxDeposit/maxMintreturn 0 until the manager admin clears the latch (#270). - A strategy-manager swap that may strand funds (the old manager is latched, still reports allocations, or cannot answer) latches deposits on the vault itself until the vault admin clears it (#305).
Functions
vaultCoreStorage
function vaultCoreStorage() internal pure returns (VaultCoreStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | VaultCoreStorage |
vaultCoreRecoveryStorage
function vaultCoreRecoveryStorage() internal pure returns (VaultCoreRecoveryStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | VaultCoreRecoveryStorage |
__VaultCore_init
Initializes the VaultCore module.
Must be called inside a pre/postInitializer block, after ERC4626Lib.__ERC4626_init. AccessControl must already be initialized to support the DEFAULT_ADMIN_ROLE check.
function __VaultCore_init() internal;registerInterfaces
Registers support for the IVaultCore and IVaultCoreRecovery interfaces via ERC-165.
function registerInterfaces() internal;strategyManager
Returns the configured strategy manager address, or address(0) if not set.
function strategyManager() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
idleAssets
Returns the vault's current idle asset balance.
Defined as the ERC-20 balance of the underlying asset held by this contract.
function idleAssets() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
totalAssets
Returns the total assets held by the vault, including strategy allocations.
Backs the diamond's totalAssets() selector (replacing ERC4626Lib.totalAssets), which
ERC4626Lib reads for all share pricing. When a manager is set, adds the manager's
totalAllocated() view (which sums each strategy's self-reported balance).
Trust assumption: strategy balance reports are accurate.
Fails closed: if the manager read reverts or returns malformed data, this reverts with
IVaultCore.VaultCoreStrategyNavUnavailable rather than under-reporting the NAV as idle, which
would let deposits mint shares cheaply and exits redeem at a discount. allocatedAssets() reverts too.
function totalAssets() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
allocatedAssets
Returns total assets allocated to strategies (totalAssets - idleAssets).
function allocatedAssets() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
setStrategyManager
Sets the strategy manager address. Restricted to DEFAULT_ADMIN_ROLE.
Latches deposits on the vault when the swap may strand funds (see _setStrategyManager).
function setStrategyManager(address manager) internal;Parameters
| Name | Type | Description |
|---|---|---|
| manager | address | The new strategy manager address. |
_setStrategyManager
Inner logic for setStrategyManager (no auth check — auth in outer).
Swapping away from a manager drops its strategies from the NAV and its own deposit latch with it, so
funds those strategies return later would move value to whoever deposited after the swap (#305).
Unless the previous manager verifiably releases cleanly (_releasesCleanly), the swap sets the
vault-side latch and emits IVaultCoreRecovery.ManagerSwapLatchSet. The reads never revert the swap:
it is the vault admin's last-resort recovery when the old manager's NAV read is broken. Setting the
first manager, or the same manager again, strands nothing and latches nothing.
function _setStrategyManager(address manager) internal;Parameters
| Name | Type | Description |
|---|---|---|
| manager | address |
clearManagerSwapLatch
Clears the manager-swap latch, reopening deposits unless the configured manager's own latch is set. Restricted to DEFAULT_ADMIN_ROLE (the role that gates setStrategyManager).
function clearManagerSwapLatch() internal;_clearManagerSwapLatch
Inner logic for clearManagerSwapLatch (no auth check).
function _clearManagerSwapLatch() internal;allocateToStrategy
Transfers idle assets to a strategy. Only callable by the strategy manager.
Empty return data counts as success only when the asset has code (matches OpenZeppelin SafeERC20).
function allocateToStrategy(address strategy, uint256 amount) internal;Parameters
| Name | Type | Description |
|---|---|---|
| strategy | address | Destination strategy address. |
| amount | uint256 | Amount of underlying asset to transfer. |
recallFromStrategy
Acknowledges a recall event from a strategy.
The strategy itself is responsible for pushing assets back to the vault. This function exists to emit the event and allow the StrategyManager to coordinate the accounting.
function recallFromStrategy(address strategy, uint256 amount) internal;Parameters
| Name | Type | Description |
|---|---|---|
| strategy | address | Source strategy address. |
| amount | uint256 | Amount expected to be returned by the strategy. |
_checkManager
Reverts with VaultCoreUnauthorizedManager if the caller is not the strategy manager.
function _checkManager() internal view;requireManagerNotRebalancing
Reverts if the configured strategy manager is currently executing rebalance().
During a rebalance the vault's idle balance and the strategies' reported balances are
transiently inconsistent, so totalAssets() (and therefore the share price) cannot be
trusted. The vault's deposit/mint/withdraw/redeem entry points call this to reject
read-only reentrancy via a strategy callback. The manager is an external contract, so we
query its reentrancy status by staticcall — mirroring how totalAssets() reads it.
function requireManagerNotRebalancing() internal view;depositsLatched
Returns true while deposits are latched closed, by the vault (managerSwapLatched) or by the configured strategy manager (managerDepositsLatched).
function depositsLatched() internal view returns (bool);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
managerSwapLatched
Returns true while a strategy-manager swap keeps the vault's deposits closed (#305).
function managerSwapLatched() internal view returns (bool);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
managerDepositsLatched
Returns true while the configured strategy manager latches deposits closed.
Reads the manager's depositsLatched() (IStrategyManagerRecovery), which a strategy force removal
sets (#270). No manager, or a manager whose read fails or returns short data (one without the
latch selector), counts as unlatched, mirroring requireManagerNotRebalancing. Any nonzero word
counts as latched, so a malformed answer keeps deposits closed rather than reverting the views.
A swap away from such a manager is stricter: there a failed read latches (_releasesCleanly).
function managerDepositsLatched() internal view returns (bool);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
requireDepositsOpen
Reverts with IVaultCore.VaultCoreDepositsLatched while deposits are latched closed.
Called by the deposit/mint entry points. A force removal or a manager swap drops funds from the NAV, so a depositor entering at the lower NAV would capture part of any funds that later return; the latch keeps entries closed until an admin clears it. The error names the latch holder whose clear is due: this vault for the manager-swap latch (checked first), else the strategy manager. Exits are not gated.
function requireDepositsOpen() internal view;_releasesCleanly
True only when manager verifiably strands nothing on a swap: both its depositsLatched() and its
totalAllocated() read succeed with a full word, and they report false and 0. Any other answer,
including a revert, short return data or running out of gas, returns false, so the swap latches. The
old manager cannot then show that its strategies, or the strategies it force-removed, hold nothing.
On the last-resort route (a broken NAV read) the old manager still reports allocations or its own
sum overflows, so it latches either way. A false latch costs one admin call; a missed one reopens
the #270 donation capture.
function _releasesCleanly(address manager) private view returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| manager | address |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
_readWord
Staticcalls selector on target and returns the first word of its answer. ok is false when the
call reverts or returns fewer than 32 bytes. At most 32 bytes of return data are copied, so an
oversized answer cannot exhaust the caller's gas.
function _readWord(address target, bytes4 selector) private view returns (bool ok, uint256 word);Parameters
| Name | Type | Description |
|---|---|---|
| target | address | |
| selector | bytes4 |
Returns
| Name | Type | Description |
|---|---|---|
| ok | bool | |
| word | uint256 |