YDS: Mental Model & Lifecycle
Strategy Lifecycle (Direct Deposit)
This page explains the full lifecycle of Yield Donating Strategies (YDS) and their interaction with the MultiStrategyVault. YDS strategies are ERC-4626 contracts where profits are donated by minting new shares to a donation address, and losses burn those donation shares first. Users can interact directly with standalone strategies, or indirectly via a vault that aggregates multiple strategies.
Every Yield Donating Strategy (YDS) is an ERC-4626 vault that manages a single ERC-20 asset. Users only interact through the ERC-4626 surface (deposit, mint, withdraw, redeem, and conversion/limit views) .
Strategy Lifecycle Summary
The minimal lifecycle for a YDS strategy is built on just three functions: _deployFunds, _freeFunds, and _harvestAndReport. Together, these cover deposits, withdrawals, and reports with donation-via-shares handled automatically by the base contracts. For strategy authors, the guidance is simple: implement the three required overrides with care, keep permissionless paths boring and deterministic, and make _harvestAndReport return an honest, totalAssets number. Then add optional hooks and configuration like availableDepositLimit, availableWithdrawLimit, profitLimitRatio  and lossLimitRatio if your strategy’s external yield source demands them.
- 
Deposit → Shares. Before shares are minted, the vault calls availableDepositLimit(owner). If permitted, shares are minted and_deployFundsinvests the idle balance.
- 
Withdraw → Assets. Before assets are returned, the vault calls availableWithdrawLimit(owner). If allowed, shares are burned,_freeFundsis called to unlock capital if needed, and assets are returned. Any shortfall is a realized loss for that user.
- 
Tend (Optional). If _tendTrigger()returns true, a keeper may calltend(). The strategy deploys idle capital or rebalances positions. PPS is unchanged until the next report.
- 
Report → Donation Adjustment. A keeper or manager calls report()._harvestAndReportcomputes total assets. BaseHealthCheck validates the reported delta against configured thresholds. If checks pass, the base mints or burns donation shares accordingly. PPS remains constant until the donation buffer is exhausted.
- 
Emergency Shutdown. Management may set the shutdown flag, which blocks deposits but still allows withdrawals. _emergencyWithdrawcan be called to recover funds into idle.
Required Overrides
The strategist’s responsibility is to implement three required overrides and optionally additional hooks to control deposits, withdrawals, maintenance, and reporting safety through out the vault lifecycle.
- _harvestAndReport() returns (uint256 totalAssets)
Called by management or a keeper during the trusted report() step. This function should harvest pending rewards, convert them into the underlying with bounded slippage, and return the total assets managed by the strategy (both idle and deployed). The base contract compares this figure to the last report:
- If profit is detected, new shares are minted to the donation address, keeping user PPS constant.
- If a loss is detected, donation shares are burned first, and only if the buffer is exhausted does PPS fall for all holders.
- _deployFunds(uint256 amount)
Called at the end of each deposit() or mint(). The amount parameter represents idle underlying now available to deploy. The implementation should transfer this into the external yield source. Because deposits are permissionless and can be front-run, this function must be simple and deterministic, without swaps or reliance on manipulable oracles. If the yield source is unavailable, leaving funds idle until the next maintenance or report is acceptable.
- _freeFunds(uint256 amount)
Called during withdraw() or redeem() when idle balances are insufficient. The strategy should attempt to unwind positions to free up the requested amount. Any shortfall between the request and what is returned is treated as a realized loss and charged to the withdrawing user. Strategies with illiquid assets should either override withdrawal limits or revert to avoid recognizing incorrect losses.
Optional Overrides
Beyond the required overrides, the base exposes optional hooks for strategists who need more control:
- _tend(uint256 totalIdle)/- _tendTrigger()
_tend() allows keepers or management to perform mid-cycle maintenance, such as deploying idle capital once above a threshold or rebalancing collateral. It does not change PPS until the next report. _tendTrigger() defines the conditions under which a keeper should call _tend().
- _emergencyWithdraw(uint256 amount)
Used after shutdown, callable only by management. This should attempt to pull back capital from the yield source into idle balance without realizing profit or loss. A subsequent report() will finalize accounting.
- availableDepositLimit(address owner)
By default returns unlimited. Override to impose global caps, whitelist logic, or conditional pauses. Called before deposit() or mint().
- availableWithdrawLimit(address owner)
By default returns unlimited. Override to throttle withdrawals for illiquid positions. Called before withdraw() or redeem().
Optional (but strongly recommended) Configuration
Strategies may also inherit from BaseHealthCheck.sol to add automated sanity checks during the reporting process. When enabled, health checks run immediately after _harvestAndReport(). These checks verify that reported profits don't exceed the profit limit and losses don't exceed the loss limit - if they do, the report reverts before finalization, otherwise if values are within the configured thresholds, the report proceeds normally. This module adds:
- bool public doHealthCheck— a toggle to enable or disable health checks.
- profitLimitRatioand- lossLimitRatio— thresholds (in basis points) defining the maximum acceptable profit or loss relative to total assets in a single report.
Vault Lifecycle (Strategy Aggregator)
While YDS strategies are standalone ERC‑4626 vaults, many projects may choose to deploy them within a Yearn V3 [multi‑strategy] vault. In addition to standalone strategies, YDS supports vaults that can aggregate across multiple strategies. The MultiStrategyVault is our Solidity implementation of Yearn V3 vault semantics. It is also an ERC-4626 contract. Users interact with the vault by calling deposit, withdraw, mint, or redeem. Managers configure which strategies the vault uses and how much capital is allocated to each one.
When a user deposits into a vault, they receive vault shares. The vault then allocates those assets into its attached strategies. Each strategy runs its own lifecycle. YDS strategies donate profits by minting or burning donation shares at each report, while compounding strategies increase their PPS by reinvesting yield. Vault managers can adjust allocations dynamically. For example, after a socialized loss in a YDS strategy, a manager may allocate more capital to a compounding strategy to help rebuild the vault’s PPS. On withdrawal, the vault sources underlying from its idle balance or by pulling back debt from strategies, and transfers it to the user.
The key distinction is that in a standalone YDS strategy, profits are always represented as donation shares and user PPS never increases. In a multi-strategy vault, managers can influence vault-level PPS through allocation.
Vault Lifecycle Summary
The lifecycle of a MultiStrategyVault consists of both user-facing flows (deposits, withdrawals, and redemptions) and strategy lifecycle events (reports and debt updates). The vault is not just a passive container. It actively allocates capital, records strategy performance, and integrates strategy-level reports into vault-level accounting.
- Add strategies. Managers attach new strategies with addStrategy(address,bool). Newly added strategies start with zero debt and may optionally be placed in the withdrawal queue.
- Set ceilings. Governance or a manager defines the maximum debt a strategy can receive with updateMaxDebtForStrategy.
- Deposit → Allocation.
- When a user deposits into the vault, the vault mints new vault shares and its idle balance increases.
- updateDebtis called to move idle funds into attached strategies, up to their ceilings id auto allocate. Other wise vault management can allocate.
- The vault deposits underlying into each strategy’s ERC-4626 interface, receives strategy shares in return, and the strategy executes _deployFundsto put the capital to work.
- Thus, a single user deposit into the vault flows through to vault shares minted, strategy shares minted, and ultimately capital deployed into external yield sources.
 
- Withdraw → Deallocation.
- When a user redeems vault shares, the vault first uses idle to satisfy the request. If idle is insufficient, the vault lowers strategy debts via updateDebt.
- The vault withdraws assets from the strategy, burns its strategy shares, and the strategy executes _freeFunds, if necessary, to release underlying .
- The vault transfers the withdrawn underlying back to the user. Any shortfall realized at the strategy level flows upward as a vault-level loss.
 
- When a user redeems vault shares, the vault first uses idle to satisfy the request. If idle is insufficient, the vault lowers strategy debts via 
- Report (Strategy Lifecycle Event).
- A keeper or manager must call report()directly on each strategy.
- This triggers the strategy’s _harvestAndReport(), which harvests rewards, converts them to underlying, and finalizes profit or loss.
- For compounding strategies, this increases PPS by raising the strategy’s exchange rate.
- For YDS strategies, profits are donated by minting new shares to the donation address, and losses are absorbed by burning donation shares before PPS falls.
 
- A keeper or manager must call 
- Process Report (Vault Lifecycle Event).
- After a strategy has reported, the vault calls processReport(strategy).
- The vault queries the strategy’s updated convertToAssets()value, compares it against the last recorded value, and books the profit or loss into its own accounting.
- For YDS strategies, the vault does not mint or burn donation shares itself; instead, it consumes the results of the strategy’s report, where donation shares were already minted or burned.
 
- After a strategy has reported, the vault calls 
- Rebalance. Over time, the DebtAllocatoror managers invokeupdateDebtto reallocate capital between strategies. This may be used to optimize yield, maintain liquidity buffers, or offset YDS socialized losses by increasing allocations to compounding strategies.
- Revoke or force revoke. Strategies are removed cleanly with revokeStrategyonce debt has been fully withdrawn. If a strategy cannot repay its debt,forceRevokeStrategyforcibly detaches it and records unrepaid balances as vault-level losses.
- Emergency handling. Governance can halt deposits globally. Withdrawals and reports remain available. At the strategy level, management can use _emergencyWithdrawto retrieve funds without realizing P/L, leaving them idle until accounted for in a future report.
Key Point: Pass-Through Lifecycle
When a vault is used as an allocator, user actions and keeper actions flow directly through to strategy lifecycles:
- Vault deposit → vault shares minted → updateDebt → strategy deposit → _deployFunds.
- Vault withdrawal → vault shares burned → updateDebt down → strategy withdraw → _freeFunds.
- Strategy report (keeper-driven) → _harvestAndReport→ donation shares minted/burned (YDS) or PPS increase (compounding).
- Vault processReport → consumes updated convertToAssets→ records profit/loss at the vault level.
Managing Strategies
- addStrategy(address newStrategy_, bool addToQueue_) external override
Adds a new strategy. If addToQueue_ is true, it is included in the withdrawal queue. New strategies start with max debt set to zero.
- updateMaxDebtForStrategy(address strategy_, uint256 newMaxDebt_) external override
Sets the maximum capital allocation allowed for a strategy.
- updateDebt(address strategy_, uint256 targetDebt_, uint256 maxLoss_) external override nonReentrant returns (uint256)
Adjusts a strategy’s debt to targetDebt_. Increasing debt pushes idle funds into the strategy; decreasing pulls funds back, tolerating losses up to maxLoss_. This is the primary function used to rebalance allocations.
- revokeStrategy(address strategy_) external override
Removes a strategy after its debt has been fully withdrawn.
- forceRevokeStrategy(address strategy_) external override
Removes a strategy immediately, even if it still holds debt. Any unrepaid balance is recorded as a vault-level loss, lowering PPS.
Reporting Strategy Results
- processReport(address strategy_) external nonReentrant returns (uint256 profit, uint256 loss)
Called by a keeper or manager to update accounting. The vault queries convertToAssets() on the strategy, calculates profit or loss since the last report, and updates internal state.
– For YDS strategies, profit appears as minted donation shares and losses burn donation instead of a PPS increase.
– For compounding strategies, profit increases PPS directly.
It is best practice to process a report before reducing or revoking a strategy’s debt to ensure all results are captured.
Debt Updates and Automation
Debt allocation is done by the DebtAllocator, which holds the DEBT_MANAGER role. All debt movements are denominated in the vault’s underlying asset. Updates must respect each strategy’s maxDebt, availableDepositLimit, and availableWithdrawLimit. The vault also enforces a minimumTotalIdle buffer to preserve withdrawal liquidity. A strategy with unrealized losses cannot have its debt reduced until those losses are reported.