|
| 1 | +# Venus Protocol's ERC-4626 Implementation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Venus Protocol likes to introduce **native ERC-4626 vaults**, bringing standardized, composable yield vaults to the Venus ecosystem. This integration represents a significant advancement in making Venus's yield-bearing markets more accessible and composable within the broader DeFi ecosystem. |
| 6 | + |
| 7 | +### Key Benefits: |
| 8 | + |
| 9 | +- **Full ERC-4626 Compliance** – Interoperable with DeFi primitives (DAOs, aggregators, etc.) |
| 10 | +- **Native Venus Yield Integration** – Auto-compounding via vTokens |
| 11 | +- **Gas-Optimized Architecture** – Beacon proxy pattern for efficient deployments, to keep all vaults implementation contract the same. |
| 12 | +- **Secure & Upgradeable** – Governance-controlled upgrades and reward management |
| 13 | + |
| 14 | +## Understanding ERC-4626 |
| 15 | + |
| 16 | +ERC-4626 is a tokenized vault standard designed to unify how yield-bearing assets are deposited, managed, and withdrawn in DeFi protocols. It builds on the ERC-20 token standard and introduces a consistent interface for vaults that accept a specific asset (like USDC) and issue shares representing ownership in the vault. |
| 17 | + |
| 18 | +The primary goal of ERC-4626 is **standardization**—allowing developers to integrate with vaults without needing to understand their internal mechanics. Functions like deposit, withdraw, mint, and redeem follow predictable behaviors across all compliant contracts. |
| 19 | + |
| 20 | +In essence, ERC-4626 makes it easier for users to earn yield on their assets, and for protocols to plug into vaults in a reliable, composable way—enhancing both usability and interoperability across the DeFi ecosystem. |
| 21 | + |
| 22 | +#### Reference: https://eips.ethereum.org/EIPS/eip-4626 |
| 23 | + |
| 24 | +## The implementation of the Venus4626 vaults consists of two core smart contracts: |
| 25 | + |
| 26 | +#### **1\. VenusERC4626Factory.sol - The factory contract for deploying standardized vaults** |
| 27 | + |
| 28 | +- Deploys individual vaults for individual vTokens via **BeaconProxy** |
| 29 | +- Ensures deterministic addresses using **CREATE2** |
| 30 | +- Managed by Venus Governance |
| 31 | + |
| 32 | +#### **2\. VenusERC4626.sol - The vault logic implementing ERC-4626 functionality** |
| 33 | + |
| 34 | +- ERC-4626-compliant mint, deposit, redeem and withdraw functions |
| 35 | +- Integrates with Venus’s **vToken** interest accrual |
| 36 | +- Handles reward distribution (XVS, etc.) |
| 37 | + |
| 38 | +## Architecture |
| 39 | + |
| 40 | +<figure><img src="../../.gitbook/assets/erc-4626-flow-diagram.png" alt="Flow of funds related to Prime"><figcaption></figcaption></figure> |
| 41 | + |
| 42 | +## **VenusERC4626Factory.sol: The Vault Factory** |
| 43 | + |
| 44 | +### **Architecture Overview** |
| 45 | + |
| 46 | +The factory contract implements a sophisticated deployment system using OpenZeppelin's upgradeability patterns: |
| 47 | + |
| 48 | +```jsx |
| 49 | +contract VenusERC4626Factory is AccessControlledV8, MaxLoopsLimitHelper { |
| 50 | + UpgradeableBeacon public beacon; |
| 51 | + mapping(address vToken => ERC4626Upgradeable vault) public createdVaults; |
| 52 | + |
| 53 | + function createERC4626(address vToken) external returns (ERC4626Upgradeable) { |
| 54 | + // Deployment logic... |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +### **Core Components** |
| 60 | + |
| 61 | +### **Beacon Proxy System** |
| 62 | + |
| 63 | +- **UpgradeableBeacon**: Stores the current implementation address |
| 64 | +- **BeaconProxy**: Proxy delegates to beacon implementation |
| 65 | +- **CREATE2**: Deterministic deployment with fixed salt for beacon proxies |
| 66 | + |
| 67 | +#### Benefits: |
| 68 | + |
| 69 | +- Single implementation contract shared by all vaults |
| 70 | +- Gas-efficient deployments |
| 71 | +- Centralized upgrade capability |
| 72 | + |
| 73 | +### **PoolRegistry Integration** |
| 74 | + |
| 75 | +```jsx |
| 76 | +function createERC4626(address vToken) external { |
| 77 | + // Validate vToken is registered in PoolRegistry |
| 78 | + if (vToken != poolRegistry.getVTokenForAsset(comptroller, underlying)) { |
| 79 | + revert VenusERC4626Factory__InvalidVToken(); |
| 80 | + } |
| 81 | + // Proceed with deployment... |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +This ensures: |
| 86 | + |
| 87 | +- Only legitimate Venus vTokens can create vaults |
| 88 | +- Proper asset/vToken pairing |
| 89 | +- Compliance with Venus's risk parameters |
| 90 | + |
| 91 | +### Key Features |
| 92 | + |
| 93 | +- **Deterministic Deployment**: Uses a constant salt to enable deterministic address generation for ERC-4626 vaults(Proxies). |
| 94 | +- **Upgradeable Architecture**: Utilizes a beacon proxy pattern to support upgradeability of all deployed vaults via a single beacon. |
| 95 | +- **Vault Tracking**: Maintains a mapping `createdVaults` of vTokens to their corresponding deployed ERC-4626 vaults. |
| 96 | +- **Reward Routing**: Allows configuration of a centralized reward recipient for all vaults, supporting liquidity mining incentives. |
| 97 | +- **Permissioned Admin**: Restricted administrative operations (e.g., setting reward recipient, max loops) via Access Control Manager (ACM). |
| 98 | + |
| 99 | +### Events |
| 100 | + |
| 101 | +- **`CreateERC4626 (event)`**: Emitted when a new ERC-4626 vault is created for a vToken. |
| 102 | +- **`RewardRecipientUpdated (event)`**: Emitted when the reward recipient address is updated. |
| 103 | + |
| 104 | +### Constants |
| 105 | + |
| 106 | +- **`SALT (bytes32)`**: Constant salt used for deterministic deployment of vaults. |
| 107 | + |
| 108 | +### State variables |
| 109 | + |
| 110 | +- **`beacon (UpgradeableBeacon)`**: Stores the address of the beacon contract holding the ERC-4626 vault implementation. |
| 111 | +- **`poolRegistry (PoolRegistryInterface)`**: Reference to the Venus Pool Registry contract. |
| 112 | +- **`rewardRecipient (address)`**: Address designated to receive liquidity mining rewards. |
| 113 | +- **`createdVaults (mapping(address => ERC4626Upgradeable))`**: Maps vTokens to their deployed ERC-4626 vault instances. |
| 114 | + |
| 115 | +### Functions |
| 116 | + |
| 117 | +- **`initialize()`**: Initializes the factory with core configuration. |
| 118 | +- **`createERC4626(address vToken)`**: Creates a new ERC-4626 vault for the specified vToken. |
| 119 | +- **`computeVaultAddress(address vToken)`**: Returns the predicted address of a vault for a specific vToken. |
| 120 | +- **`setRewardRecipient(address newRecipient)`**: Updates the address receiving reward distributions (ACM-restricted). |
| 121 | +- **`setMaxLoopsLimit(uint256 loopsLimit)`**: Configures the maximum allowed loop iterations (ACM-restricted). |
| 122 | + |
| 123 | +### **Security Considerations** |
| 124 | + |
| 125 | +### **1\. Access Controls** |
| 126 | + |
| 127 | +- **Admin-Only Functions** (via **AccessControlledV8**): |
| 128 | + |
| 129 | + - **setRewardRecipient()** |
| 130 | + - **setMaxLoopsLimit()** |
| 131 | + - **upgradeBeacon()** (for emergency fixes) |
| 132 | + |
| 133 | +### **2\. Input Validation** |
| 134 | + |
| 135 | +- **ensureNonzeroAddress()** prevents invalid configurations. |
| 136 | + |
| 137 | +### **Attack Surface Mitigation** |
| 138 | + |
| 139 | +| **Threat Vector** | **Mitigation Strategy** | |
| 140 | +| ------------------ | ------------------------- | |
| 141 | +| Reentrancy | `nonReentrant` modifiers | |
| 142 | +| Invalid vTokens | PoolRegistry validation | |
| 143 | +| Governance attacks | ACM with timelocks | |
| 144 | +| Upgrade risks | Beacon ownership controls | |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## VenusERC4626.sol: The Vault Implementation |
| 149 | + |
| 150 | +### Core Architecture |
| 151 | + |
| 152 | +The **VenusERC4626** contract serves as an ERC-4626 compliant wrapper around Venus's yield-bearing vTokens. It inherits from multiple OpenZeppelin and Venus-specific base contracts to provide a secure, feature-rich implementation: |
| 153 | + |
| 154 | +```jsx |
| 155 | +contract VenusERC4626 is |
| 156 | + ERC4626Upgradeable, |
| 157 | + AccessControlledV8, |
| 158 | + MaxLoopsLimitHelper, |
| 159 | + ReentrancyGuardUpgradeable { |
| 160 | + // Implementation... |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +### Key Features |
| 165 | + |
| 166 | +- **ERC-4626 Compliant**: Fully compliant with the ERC-4626 Tokenized Vault standard, enabling integrations with yield aggregators and frontends. |
| 167 | +- **vToken Wrapping**: Provides tokenized access to underlying vTokens with proportional interest accrual. |
| 168 | +- **Dual-Stage Initialization**: Separates base contract setup and access/reward configuration for modular deployment. |
| 169 | +- **Reward Claiming**: Allows vaults to claim accrued rewards and direct them to a predefined recipient. |
| 170 | +- **Failsafes and Admin Tools**: Includes recovery mechanisms like `sweepToken` and loop control for security and operational safety. |
| 171 | + |
| 172 | +### Key Inherited Functionality: |
| 173 | + |
| 174 | +- **ERC4626Upgradeable**: Used Openzeppelin's 4626 contract as base implementation of the ERC-4626 standard. |
| 175 | +- **AccessControlledV8**: Venus role-based access control system. |
| 176 | +- **ReentrancyGuardUpgradeable**: Protection against reentrancy attacks. |
| 177 | +- **MaxLoopsLimitHelper**: Prevents gas exhaustion in loops. |
| 178 | + |
| 179 | +### Events |
| 180 | + |
| 181 | +- **`ClaimRewards (event)`**: Emitted when rewards are claimed and distributed. |
| 182 | +- **`RewardRecipientUpdated (event)`**: Emitted when the reward recipient address is updated. |
| 183 | +- **`SweepToken (event)`**: Emitted when ERC-20 tokens are swept from the contract. |
| 184 | + |
| 185 | +### State variables |
| 186 | + |
| 187 | +- **`vToken (VToken)`**: The underlying Venus vToken being wrapped. |
| 188 | +- **`comptroller (IComptroller)`**: The Comptroller contract associated with the vToken. |
| 189 | +- **`rewardRecipient (address)`**: Address designated to receive reward tokens. |
| 190 | + |
| 191 | +### Functions |
| 192 | + |
| 193 | +#### Core ERC-4626 Functions |
| 194 | + |
| 195 | +- **`deposit(uint256 assets, address receiver)`**: Deposits assets and mints shares to the receiver. |
| 196 | +- **`mint(uint256 shares, address receiver)`**: Mints exact shares by depositing required assets. |
| 197 | +- **`withdraw(uint256 assets, address receiver, address owner)`**: Withdraws exact assets and burns shares from the owner. |
| 198 | +- **`redeem(uint256 shares, address receiver, address owner)`**: Redeems exact shares and transfers assets to the receiver. |
| 199 | +- **`totalAssets()`**: Returns the total underlying assets held by the vault. |
| 200 | + |
| 201 | +#### Initialization |
| 202 | + |
| 203 | +- **`initialize(address vToken_)`**: Initializes the vault with the target vToken (first-stage init). |
| 204 | +- **`initialize2(address accessControlManager_, address rewardRecipient_, uint256 loopsLimit_)`**: Second-stage initialization with access control, reward recipient, and loop limit. |
| 205 | + |
| 206 | +#### Reward Management |
| 207 | + |
| 208 | +- **`claimRewards()`**: Claims all available rewards and sends them to the recipient. |
| 209 | +- **`setRewardRecipient(address newRecipient)`**: Updates the reward recipient address (ACM-restricted). |
| 210 | + |
| 211 | +#### Admin Functions |
| 212 | + |
| 213 | +- **`sweepToken(IERC20Upgradeable token)`**: Allows the owner to recover any ERC-20 tokens mistakenly sent to the vault. |
| 214 | +- **`setMaxLoopsLimit(uint256 loopsLimit)`**: Configures the maximum loop iterations (ACM-restricted). |
| 215 | + |
| 216 | +### Error Codes |
| 217 | + |
| 218 | +- **`VenusError(uint256 errorCode)`**: Generic error returned from Venus protocol operations. |
| 219 | +- **`ERC4626__ZeroAmount(string operation)`**: Thrown when a zero amount is provided during an operation. |
| 220 | +- **`ERC4626__DepositMoreThanMax()`**: Error triggered when a deposit exceeds the maximum limit. |
| 221 | +- **`ERC4626__MintMoreThanMax()`**: Error triggered when a mint exceeds the maximum limit. |
| 222 | +- **`ERC4626__WithdrawMoreThanMax()`**: Error triggered when a withdrawal exceeds the maximum limit. |
| 223 | +- **`ERC4626__RedeemMoreThanMax()`**: Error triggered when a redeem exceeds the maximum limit. |
| 224 | + |
| 225 | +### **Deposit Flow** |
| 226 | + |
| 227 | +```jsx |
| 228 | +function deposit(uint256 assets, address receiver) |
| 229 | + public |
| 230 | + override |
| 231 | + nonReentrant |
| 232 | + returns (uint256) |
| 233 | +{ |
| 234 | + // Input validation |
| 235 | + if (assets == 0) revert ERC4626__ZeroAmount("deposit"); |
| 236 | + if (assets > maxDeposit(receiver)) revert ERC4626__DepositMoreThanMax(); |
| 237 | + |
| 238 | + // Process deposit |
| 239 | + uint256 shares = previewDeposit(assets); |
| 240 | + _deposit(_msgSender(), receiver, assets, shares); |
| 241 | + |
| 242 | + return shares; |
| 243 | +} |
| 244 | +``` |
| 245 | + |
| 246 | +1. Validates input parameters |
| 247 | +2. Calculates shares to mint |
| 248 | +3. Transfers assets from user |
| 249 | +4. Mints vTokens via Venus Protocol |
| 250 | +5. Issues vault shares to receiver |
| 251 | + |
| 252 | +#### Example |
| 253 | + |
| 254 | +**Scenario**: Alice deposits 100 USDC |
| 255 | + |
| 256 | +<figure><img src="../../.gitbook/assets/erc4626-deposit.png" alt="Flow of funds related to Prime"><figcaption></figcaption></figure> |
| 257 | + |
| 258 | +#### Result: |
| 259 | + |
| 260 | +- Alice gets 100 vault shares |
| 261 | +- Vault holds 100 vUSDC (earning yield) |
| 262 | + |
| 263 | +### **Withdrawal Flow** |
| 264 | + |
| 265 | +```jsx |
| 266 | +function withdraw(uint256 assets, address receiver, address owner) |
| 267 | + public |
| 268 | + override |
| 269 | + nonReentrant |
| 270 | + returns (uint256) |
| 271 | +{ |
| 272 | + // Input validation |
| 273 | + if (assets == 0) revert ERC4626__ZeroAmount("withdraw"); |
| 274 | + if (assets > maxWithdraw(owner)) revert ERC4626__WithdrawMoreThanMax(); |
| 275 | + |
| 276 | + // Process withdrawal |
| 277 | + uint256 shares = previewWithdraw(assets); |
| 278 | + beforeWithdraw(assets); // Redeems from Venus |
| 279 | + _withdraw(_msgSender(), receiver, owner, assets, shares); |
| 280 | + |
| 281 | + return shares; |
| 282 | +} |
| 283 | +``` |
| 284 | + |
| 285 | +1. Validates input parameters |
| 286 | +2. Calculates shares to burn |
| 287 | +3. Redeems underlying assets from Venus |
| 288 | +4. Transfers assets to receiver |
| 289 | +5. Burns vault shares |
| 290 | + |
| 291 | +#### Example |
| 292 | + |
| 293 | +#### Scenario: Alice withdraws 50 USDC (after interest accrual) |
| 294 | + |
| 295 | +<figure><img src="../../.gitbook/assets/erc4626-withdraw.png" alt="Flow of funds related to Prime"><figcaption></figcaption></figure> |
| 296 | + |
| 297 | +#### Result |
| 298 | + |
| 299 | +Alice receives 50 USDC |
| 300 | + |
| 301 | +- Vault burns shares adjusted for interest (e.g., 48.54 shares at 1.03 exchange rate) |
| 302 | + |
| 303 | +### **Security Features** |
| 304 | + |
| 305 | +1. **Reentrancy Protection**: |
| 306 | + |
| 307 | + - All state-changing functions use **nonReentrant** modifier |
| 308 | + - Critical Venus operations (mint/redeem) are atomic |
| 309 | + |
| 310 | +2. **Input Validation**: |
| 311 | + |
| 312 | + - Zero-address checks via **ensureNonzeroAddress** |
| 313 | + - Zero-amount validation for all operations |
| 314 | + - Explicit error codes for Venus operations |
| 315 | + |
| 316 | +3. **Access Control**: |
| 317 | + |
| 318 | + - Sensitive functions protected by Venus's ACM |
| 319 | + - Reward recipient can only be changed by authorized accounts |
| 320 | + |
| 321 | +--- |
0 commit comments