YearnV3 Base Operations
Yearn V3's core operations focuses on the logic required to implement the ERC4626 standard.
ERC-4626 Core Requirements
As a reminder, the ERC4626 core requirements are reproduced below:
Asset Operations
// Deposit/Withdraw Interface
function deposit(uint256 assets, address receiver) returns (uint256 shares)
function mint(uint256 shares, address receiver) returns (uint256 assets)
function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)
function redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)
// Max Limits
function maxDeposit(address) returns (uint256)
function maxMint(address) returns (uint256)
function maxWithdraw(address) returns (uint256)
function maxRedeem(address) returns (uint256)
// Preview Functions
function previewDeposit(uint256 assets) returns (uint256 shares)
function previewMint(uint256 shares) returns (uint256 assets)
function previewWithdraw(uint256 assets) returns (uint256 shares)
function previewRedeem(uint256 shares) returns (uint256 assets)
Accounting Views
// Core Accounting
function totalAssets() returns (uint256)
function convertToShares(uint256 assets) returns (uint256)
function convertToAssets(uint256 shares) returns (uint256)
// ERC20 Interface
function totalSupply() returns (uint256)
function balanceOf(address) returns (uint256)
// Asset Info
function asset() returns (address)
YearnV3 Asset Operation
Yearn V3's implementation of ERC-4626 follows a strict operation pattern to ensure safety and efficiency:
Pre-operation Validation: Validates limits and state before executing operations.
Asset Transfers and Conversions: Handles the transfer of assets and conversion to/from shares.
Strategy Hooks: Calls strategy-specific hooks to deploy or withdraw funds.
State Updates and Event Emission: Updates internal state and emits relevant events.
Example implementation of deposit()
:
deposit(uint256 assets, address receiver) external returns (uint256 shares) {
// Check deposit limits
require(assets <= _maxDeposit(receiver), "ERC4626: deposit more than max");
// Calculate shares, revert if zero
require((shares = _convertToShares(assets, Math.Rounding.Down)) != 0, "ZERO_SHARES");
// Safe transfer assets in first (ERC777 reentrancy protection)
asset.safeTransferFrom(msg.sender, address(this), assets);
// Deploy funds via strategy hook
IBaseStrategy(address(this)).deployFunds(asset.balanceOf(address(this)));
// Update total assets
totalAssets += assets;
// Mint shares
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
}
Share Accounting
Yearn V3 tracks ownership and vault holdings using a dynamic price-per-share (PPS) ratio. This system ensures accurate ownership representation while protecting against manipulation.
Primary State
Field
Description
mapping(address => uint256) balances
Tracks share balances for each address.
uint256 totalSupply
Total shares issued by the vault.
uint256 totalAssets
Total assets managed by the vault.
Conversion Functions
Function
Description
_convertToShares(uint256 assets, Math.Rounding rounding)
Converts assets to shares, considering rounding modes.
_convertToAssets(uint256 shares, Math.Rounding rounding)
Converts shares to assets, considering rounding modes.
Key Features
Dynamic PPS calculations based on total assets and supply.
Configurable rounding modes for flexible operations.
First depositor protection.
Share transfer restrictions for security.
Balance and allowance management.
Profit Mechanics
Yearn employs a gradual profit realization system to prevent PPS manipulation. When a keeper calls report()
, profits are calculated and locked, with fees distributed and profits unlocked linearly.
struct StrategyData {
uint256 profitUnlockingRate; // Per-second rate
uint96 fullProfitUnlockDate; // Unlock completion time
uint32 profitMaxUnlockTime; // Default 3 days
uint16 performanceFee; // Default 10%
uint96 lastReport; // Last report timestamp
}
function report() external returns (uint256 profit, uint256 loss) {
// Calculate profit/loss
uint256 newTotalAssets = _harvestAndReport();
uint256 oldTotalAssets = totalAssets;
if (newTotalAssets > oldTotalAssets) {
// Lock profits
profit = newTotalAssets - oldTotalAssets;
sharesToLock = _convertToShares(profit);
// Calculate and distribute fees
totalFees = (profit * performanceFee) / MAX_BPS;
_mint(feeRecipient, feeShares);
// Setup linear unlocking
profitUnlockingRate = sharesToLock / profitMaxUnlockTime;
fullProfitUnlockDate = block.timestamp + profitMaxUnlockTime;
}
totalAssets = newTotalAssets;
lastReport = block.timestamp;
}
Key Features
Linear Unlocking: Term Strategy Vaults default to a
profitMaxUnlockTime
of 3 days, during which profits are unlocked gradually in a linear fashion to ensure smooth realization and prevent manipulation.
Performance Fee Structure
Yearn implements gradual profit realization and transparent fee distribution mechanisms to align incentives and prevent PPS manipulation.
Profit Realization:
Profits are calculated and locked when
report()
is called.Unlocks linearly over a configurable window (default: 3 days for Term Strategy vaults), ensuring smooth price appreciation for depositors.
For example:
performanceFee = 1000 // 10% default
profitMaxUnlockTime = 3 days // Unlock window
managerAddress = 0x0E14e7... // Strategy control
keeperAddress = 0x54Eef1... // Report caller
performanceFeeRecipient = 0x0E14e7... // Fee receiver
Performance Fee Structure:
Fee Parameters:
Default: 10% of realized profits.
80/20 split between vault curator and Yearn protocol.
Fee Distribution:
Charged during the
report()
call.Distributed immediately to
performanceFeeRecipient
.
Profit Unlocking:
Profits locked at report time.
Linear unlocking over the chosen window to ensure fairness and smooth price-per-share increases.
Last updated