11 // SPDX-License-Identifier: MIT
2-pragma solidity 0.8.28;
2+pragma solidity 0.8.34;
33
4-import {Errors} from "./libraries/Errors.sol";
5-import {IYoVault} from "./interfaces/IYoVault.sol";
6-import {IYoOracle} from "./interfaces/IYoOracle.sol";
4+import { Errors } from "./libraries/Errors.sol";
5+import { IYoVault } from "./interfaces/IYoVault.sol";
6+import { IYoOracle } from "./interfaces/IYoOracle.sol";
7+import { IYoApprovalRegistry } from "./interfaces/IYoApprovalRegistry.sol";
78
8-import {Compatible} from "./base/Compatible.sol";
9-import {AuthUpgradeable, Authority} from "./base/AuthUpgradeable.sol";
9+import { Compatible } from "./base/Compatible.sol";
10+import { AuthUpgradeable } from "./base/AuthUpgradeable.sol";
11+import { IAuthority } from "./interfaces/IAuthority.sol";
1012
11-import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
12-import {Address} from "@openzeppelin/contracts/utils/Address.sol";
13-import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
14-import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
15-import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
16-import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
13+import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
14+import { Address } from "@openzeppelin/contracts/utils/Address.sol";
15+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
16+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
17+import { ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
18+import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
1719
1820 // __ __ ____ _ _
1921 // \ \ / /__ | _ \ _ __ ___ | |_ ___ ___ ___ | |
2022 // \ V / _ \| |_) | '__/ _ \| __/ _ \ / __/ _ \| |
2123 // | | (_) | __/| | | (_) | || (_) | (_| (_) | |
2224 // |_|\___/|_| |_| \___/ \__\___/ \___\___/|_|
23-/// @title yoVault_V2 - A simple vault contract that allows for an operator to manage the vault.
24-/// @dev This contract is based on the ERC4626 standard and uses the Auth contract for access control.
25-/// It provides an asynchronous redeem mechanism that allows users to request a redeem and the operator to fulfill it.
26-/// This would allow the operator to move funds to a different chain or strategy before the user can claim the assets.
27-/// If the vault has enough assets to fulfill the request, the assets are withdrawn and returned to the owner
28-/// immediately. Otherwise, the assets are transferred to the vault and the request is stored until the operator
29-/// fulfills it.
25+/// @title YoVault - Operator-managed ERC4626 vault with asynchronous redemptions.
26+/// @dev Extends ERC4626 with role-based access control and an async redeem mechanism.
27+/// Users call `requestRedeem` — if the vault holds enough assets the withdrawal is instant,
28+/// otherwise shares are escrowed until the operator calls `fulfillRedeem`.
29+/// Oracle-driven pricing: share conversions query `_oracleAsset()` via `ORACLE_ADDRESS`,
30+/// which subclasses can override to price against a different asset.
3031
31-contract YoVault_V2 is ERC4626Upgradeable, Compatible, IYoVault, AuthUpgradeable, PausableUpgradeable {
32+contract YoVault is ERC4626Upgradeable, Compatible, IYoVault, AuthUpgradeable, PausableUpgradeable {
3233 using Math for uint256;
3334 using Address for address;
3435 using SafeERC20 for IERC20;
3536
36- /// @dev A slot for storing an address value.
37+ /// @dev Helper struct for reading an address from a storage slot.
3738 struct AddressSlot {
3839 address value;
3940 }
4041
41- /// @dev The slot for the implementation contract.
42+ /// @dev ERC-1967 implementation slot.
4243 bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
4344
44- /// @dev Assume requests are non-fungible and all have ID = 0, so we can differentiate between a request ID and the
45- /// assets amount.
45+ /// @dev Non-fungible request sentinel — all pending redeems share ID 0.
4646 uint256 internal constant REQUEST_ID = 0;
47- /// @dev The denominator used for precision calculations.
47+ /// @dev 1e18 precision denominator for fee and percentage calculations.
4848 uint256 internal constant DENOMINATOR = 1e18;
49- /// @dev The maximum fee that can be set for the vault operations. 1e17 = 10%.
49+ /// @dev Maximum allowed fee (10%).
5050 uint256 internal constant MAX_FEE = 1e17;
51- /// @dev the address of the oracle contract
51+ /// @dev YoOracle contract used for share-price lookups.
5252 address public constant ORACLE_ADDRESS = 0x6E879d0CcC85085A709eBf5539224f53d0D396B0;
5353
54- /// @dev the aggregated underlying balances across all strategies/chains, reported by an oracle
54+ /// @dev Deprecated — preserved for storage-layout compatibility.
5555 uint256 private deprecated_aggregatedUnderlyingBalances;
56- /// @dev the last block number when the aggregated underlying balances were updated
56+ /// @dev Deprecated — preserved for storage-layout compatibility.
5757 uint256 private deprecated_lastBlockUpdated;
58- /// @dev the last price per share calculated after the aggregated underlying balances are reported
58+ /// @dev Deprecated — preserved for storage-layout compatibility.
5959 uint256 private deprecated_lastPricePerShare;
60- /// @dev the total amount of assets that are pending redemption
60+ /// @notice Total assets locked in pending redemption requests.
6161 uint256 public totalPendingAssets;
62- /// @dev the maximum percentage change allowed before the vault is paused. It can be updated by the owner.
63- /// 1e18 = 100%. It's value depends on the frequency of the oracle updates.
62+ /// @dev Deprecated — preserved for storage-layout compatibility.
6463 uint256 private deprecated_maxPercentageChange;
65- /// @dev the fee charged for the withdraws, it's a percentage of the assets redeemed
64+ /// @notice Withdrawal fee as an 18-decimal fraction (e.g. 1e16 = 1%).
6665 uint256 public feeOnWithdraw;
67- /// @dev the fee charged for the deposits, it's a percentage of the assets deposited
66+ /// @notice Deposit fee as an 18-decimal fraction (e.g. 1e16 = 1%).
6867 uint256 public feeOnDeposit;
69- /// @dev the address that receives the fees for the vault operations, if it's zero, no fees are charged
68+ /// @notice Recipient of collected fees. No fees are collected when zero.
7069 address public feeRecipient;
7170
72- /// @dev used to store the amount of shares that are pending redemption, it must be fulfilled by the vault operator
71+ /// @dev Per-user pending redemption state, fulfilled by the vault operator.
7372 mapping(address user => PendingRedeem redeem) internal _pendingRedeem;
7473
74+ /// @notice Approval registry consulted by `approveToken`. Set after upgrade via
75+ /// `setApprovalRegistry`. Reads return `address(0)` until configured.
76+ IYoApprovalRegistry public approvalRegistry;
77+
7578 //============================== CONSTRUCTOR ===============================
7679
7780 /// @custom:oz-upgrades-unsafe-allow constructor