Vault Reference
MoolahVault is the ERC-4626 layer over Moolah: suppliers deposit one loan asset, a curator allocates it across markets, and shares accrue the blended yield. Call a vault directly — from Solidity, or from any stack that cannot use the SDK — with the surface below.
For deployed vault addresses see Smart Contract. For the market-level contract see Contract & Interface Reference; for the read-side REST API see Vault API.
Before you deposit: three things that can block you
These are the gates that make a deposit fail for reasons a plain ERC-4626 client would not expect.
1. The vault may have a whitelist. deposit and mint both require isWhiteList(receiver). The check is whiteList.length() == 0 || whiteList.contains(account) — an empty list means open to everyone, which is the common case. Once any address is added, only listed receivers can be minted to. Read it before assuming:
function isWhiteList(address account) external view returns (bool);
function getWhiteList() external view returns (address[] memory);A blocked deposit reverts NotWhiteList(). Note the gate is on the receiver, not the caller, so depositing on someone else's behalf is constrained by their status.
2. Capacity is finite and is not totalAssets. Each market in the supply queue has a cap, and the vault can only absorb what the queue can still take. maxDeposit walks the supply queue and sums the remaining room:
function maxDeposit(address) external view returns (uint256);
function maxMint(address) external view returns (uint256);Call maxDeposit before sizing a deposit; exceeding it reverts AllCapsReached(). Both maxDeposit and maxMint can over-report when the supply queue contains the same market twice — that market's headroom gets counted once per entry. Treat either as an upper bound rather than a promise, and leave margin: a deposit sized at exactly maxDeposit can still revert.
3. Withdrawal is bounded by reachable liquidity, not by your balance. A vault withdraw pulls from markets in withdraw-queue order, and a market whose liquidity is fully borrowed cannot be pulled from. So a holder's shares are not always redeemable in full at that moment:
function maxWithdraw(address owner) external view returns (uint256);
function maxRedeem(address owner) external view returns (uint256);Exceeding the reachable amount reverts NotEnoughLiquidity(). Both views can under-report because of share/asset rounding, so treat them as a safe lower bound. Do not present a user's full share balance as withdrawable — quote maxWithdraw instead.
ERC-4626 surface
Standard, with the usual semantics:
Approve the vault itself for deposit / mint — it pulls the asset with transferFrom. withdraw and redeem from an owner other than the caller consume the ERC-20 allowance on the shares, per the standard.
The convertTo* / preview* conversions account for the not-yet-minted performance-fee shares, so a quote taken before a fee accrual can differ slightly from what settles. For an exact figure, simulate the call.
totalAssets does not net out the fee — the fee is taken by minting shares to the fee recipient, not by reducing assets. It does subtract the broker interest lock buffer's currentLocked(), so it is not simply the sum of the vault's market positions.
Two non-standard helpers
These exist for provider-routed flows — see Providers, which is how native BNB reaches a WBNB vault.
Reading a vault's allocation
The supply queue determines where a deposit lands; the withdraw queue determines what a withdrawal can reach, in order. Both are allocator-managed (setSupplyQueue, updateWithdrawQueue, and reallocate — the last also callable by a bot role), while the per-market caps behind them are curator-managed (setCap, setMarketRemoval). All are role-gated, and all change without notice — read the queues rather than caching them.
Per-market caps and queue changes are observable as events; see Events & Callbacks for the vault event set, including SetCap, SetSupplyQueue and SetWithdrawQueue.
Reverts
NotWhiteList()
The receiver is not on a non-empty vault whitelist.
AllCapsReached()
Every market in the supply queue is at its cap.
SupplyCapExceeded(Id)
A reallocation would push one market past its cap — an allocator error, not a deposit one. A deposit clamps to the cap instead and reverts AllCapsReached().
NotEnoughLiquidity()
The withdraw queue cannot reach enough liquidity right now.
MarketNotEnabled(Id) · UnauthorizedMarket(Id)
The market is not enabled for this vault — relevant to curator calls, not to a plain deposit.
InconsistentAsset(Id)
A market's loan token does not match the vault's asset().
Last updated
Was this helpful?