diff --git a/src/contracts/Vault.sol b/src/contracts/Vault.sol index f0d54c4b..8dc91bfd 100644 --- a/src/contracts/Vault.sol +++ b/src/contracts/Vault.sol @@ -7,8 +7,7 @@ import {IRegistry} from "src/interfaces/IRegistry.sol"; import {IMiddlewarePlugin} from "src/interfaces/plugins/IMiddlewarePlugin.sol"; import {INetworkOptInPlugin} from "src/interfaces/plugins/INetworkOptInPlugin.sol"; -import {MigratableEntity} from "./base/MigratableEntity.sol"; -import {ERC6372} from "./utils/ERC6372.sol"; +import {VaultStorage} from "./VaultStorage.sol"; import {Checkpoints} from "./libraries/Checkpoints.sol"; import {ERC4626Math} from "./libraries/ERC4626Math.sol"; @@ -16,216 +15,33 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {MulticallUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; -import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; - -contract Vault is - MigratableEntity, - ERC6372, - ReentrancyGuardUpgradeable, - MulticallUpgradeable, - AccessControlUpgradeable, - IVault -{ - using Checkpoints for Checkpoints.Trace208; + +contract Vault is VaultStorage, MulticallUpgradeable, IVault { using Checkpoints for Checkpoints.Trace256; - using EnumerableSet for EnumerableSet.UintSet; using SafeERC20 for IERC20; using Math for uint256; using Strings for string; - /** - * @dev Some dead address to transfer slashed tokens to. - */ - address private constant DEAD = address(0xdEaD); - - /** - * @inheritdoc IVault - */ - uint256 public constant ADMIN_FEE_BASE = 10_000; - - /** - * @inheritdoc IVault - */ - bytes32 public constant NETWORK_LIMIT_SET_ROLE = keccak256("NETWORK_LIMIT_SET_ROLE"); - - /** - * @inheritdoc IVault - */ - bytes32 public constant OPERATOR_LIMIT_SET_ROLE = keccak256("OPERATOR_LIMIT_SET_ROLE"); - - /** - * @inheritdoc IVault - */ - bytes32 public constant ADMIN_FEE_SET_ROLE = keccak256("ADMIN_FEE_SET_ROLE"); - - /** - * @inheritdoc IVault - */ - bytes32 public constant DEPOSIT_WHITELIST_SET_ROLE = keccak256("DEPOSIT_WHITELIST_SET_ROLE"); - /** - * @inheritdoc IVault - */ - bytes32 public constant DEPOSITOR_WHITELIST_ROLE = keccak256("DEPOSITOR_WHITELIST_ROLE"); - - /** - * @inheritdoc IVault - */ - address public immutable NETWORK_REGISTRY; - - /** - * @inheritdoc IVault - */ - address public immutable OPERATOR_REGISTRY; - - /** - * @inheritdoc IVault - */ - address public immutable NETWORK_MIDDLEWARE_PLUGIN; - - /** - * @inheritdoc IVault - */ - address public immutable NETWORK_OPT_IN_PLUGIN; - - /** - * @inheritdoc IVault - */ - string public metadataURL; - - /** - * @inheritdoc IVault - */ - address public collateral; - - /** - * @inheritdoc IVault - */ - uint48 public epochDurationInit; - - /** - * @inheritdoc IVault - */ - uint48 public epochDuration; - - /** - * @inheritdoc IVault - */ - uint48 public vetoDuration; - - /** - * @inheritdoc IVault - */ - uint48 public slashDuration; - - /** - * @inheritdoc IVault - */ - uint256 public adminFee; - - /** - * @inheritdoc IVault - */ - mapping(address token => uint256 amount) public claimableAdminFee; - - /** - * @inheritdoc IVault - */ - bool public depositWhitelist; - - /** - * @inheritdoc IVault - */ - mapping(address account => bool value) public isDepositorWhitelisted; - - /** - * @inheritdoc IVault - */ - mapping(address account => uint48 timestamp) public firstDepositAt; - - /** - * @inheritdoc IVault - */ - mapping(uint256 epoch => uint256 amount) public withdrawals; - - /** - * @inheritdoc IVault - */ - mapping(uint256 epoch => uint256 amount) public withdrawalsShares; - - /** - * @inheritdoc IVault - */ - mapping(uint256 epoch => mapping(address account => uint256 amount)) public withdrawalsSharesOf; - - /** - * @inheritdoc IVault - */ - SlashRequest[] public slashRequests; - - /** - * @inheritdoc IVault - */ - mapping(address token => RewardDistribution[] rewards_) public rewards; - - /** - * @inheritdoc IVault - */ - mapping(address account => mapping(address token => uint256 rewardIndex)) public lastUnclaimedReward; - - /** - * @inheritdoc IVault - */ - mapping(address network => mapping(address resolver => bool value)) public isNetworkOptedIn; - - /** - * @inheritdoc IVault - */ - mapping(address operator => uint48 timestamp) public operatorOptOutAt; - - /** - * @inheritdoc IVault - */ - mapping(address network => mapping(address resolver => uint256 amount)) public maxNetworkLimit; + constructor( + address networkRegistry, + address operatorRegistry, + address networkMiddlewarePlugin, + address networkOptInPlugin + ) VaultStorage(networkRegistry, operatorRegistry, networkMiddlewarePlugin, networkOptInPlugin) {} /** * @inheritdoc IVault */ - mapping(address network => mapping(address resolver => DelayedLimit)) public nextNetworkLimit; + function currentEpoch() public view returns (uint256) { + return (clock() - epochDurationInit) / epochDuration; + } /** * @inheritdoc IVault */ - mapping(address operator => mapping(address network => DelayedLimit)) public nextOperatorLimit; - - Checkpoints.Trace256 private _activeShares; - - Checkpoints.Trace256 private _activeSupplies; - - mapping(address account => Checkpoints.Trace256 shares) private _activeSharesOf; - - mapping(uint48 timestamp => uint256 amount) private _activeSharesCache; - - mapping(uint48 timestamp => uint256 amount) private _activeSuppliesCache; - - mapping(address operator => bool value) private _isOperatorOptedIn; - - mapping(address network => mapping(address resolver => Limit limit)) private _networkLimit; - - mapping(address operator => mapping(address network => Limit limit)) private _operatorLimit; - - constructor( - address networkRegistry, - address operatorRegistry, - address networkMiddlewarePlugin, - address networkOptInPlugin - ) { - NETWORK_REGISTRY = networkRegistry; - OPERATOR_REGISTRY = operatorRegistry; - NETWORK_MIDDLEWARE_PLUGIN = networkMiddlewarePlugin; - NETWORK_OPT_IN_PLUGIN = networkOptInPlugin; + function currentEpochStart() public view returns (uint48) { + return uint48(epochDurationInit + currentEpoch() * epochDuration); } /** @@ -335,15 +151,8 @@ contract Vault is /** * @inheritdoc IVault */ - function currentEpoch() public view returns (uint256) { - return (clock() - epochDurationInit) / epochDuration; - } - - /** - * @inheritdoc IVault - */ - function currentEpochStart() public view returns (uint48) { - return uint48(epochDurationInit + currentEpoch() * epochDuration); + function isNetworkOptedIn(address network, address resolver) public view returns (bool) { + return _isNetworkOptedIn[network][resolver]; } /** @@ -363,83 +172,14 @@ contract Vault is * @inheritdoc IVault */ function networkLimit(address network, address resolver) public view returns (uint256) { - DelayedLimit storage nextLimit = nextNetworkLimit[network][resolver]; - if (nextLimit.timestamp == 0) { - return _networkLimit[network][resolver].amount; - } - if (clock() < nextLimit.timestamp) { - return _networkLimit[network][resolver].amount; - } - return nextLimit.amount; + return _getLimit(_networkLimit[network][resolver], nextNetworkLimit[network][resolver]); } /** * @inheritdoc IVault */ function operatorLimit(address operator, address network) public view returns (uint256) { - DelayedLimit storage nextLimit = nextOperatorLimit[operator][network]; - if (nextLimit.timestamp == 0) { - return _operatorLimit[operator][network].amount; - } - if (clock() < nextLimit.timestamp) { - return _operatorLimit[operator][network].amount; - } - return nextLimit.amount; - } - - /** - * @inheritdoc MigratableEntity - */ - function initialize(uint64 version, bytes memory data) public override reinitializer(version) { - (IVault.InitParams memory params) = abi.decode(data, (IVault.InitParams)); - - if (params.epochDuration == 0) { - revert InvalidEpochDuration(); - } - - if (params.vetoDuration + params.slashDuration > params.epochDuration) { - revert InvalidSlashDuration(); - } - - if (params.adminFee > ADMIN_FEE_BASE) { - revert InvalidAdminFee(); - } - - __ReentrancyGuard_init(); - - _initialize(params.owner); - - metadataURL = params.metadataURL; - collateral = params.collateral; - - epochDurationInit = clock(); - epochDuration = params.epochDuration; - - vetoDuration = params.vetoDuration; - slashDuration = params.slashDuration; - - adminFee = params.adminFee; - depositWhitelist = params.depositWhitelist; - - _grantRole(DEFAULT_ADMIN_ROLE, params.owner); - _grantRole(NETWORK_LIMIT_SET_ROLE, params.owner); - _grantRole(OPERATOR_LIMIT_SET_ROLE, params.owner); - if (params.depositWhitelist) { - _grantRole(DEPOSITOR_WHITELIST_ROLE, params.owner); - } - } - - /** - * @inheritdoc IVault - */ - function setMetadataURL(string calldata metadataURL_) external onlyOwner { - if (metadataURL.equal(metadataURL_)) { - revert AlreadySet(); - } - - metadataURL = metadataURL_; - - emit SetMetadataURL(metadataURL_); + return _getLimit(_operatorLimit[operator][network], nextOperatorLimit[operator][network]); } /** @@ -546,10 +286,14 @@ contract Vault is revert InsufficientSlash(); } - if (!isNetworkOptedIn[network][resolver]) { + if (!isNetworkOptedIn(network, resolver)) { revert NetworkNotOptedIn(); } + if (!isOperatorOptedIn(operator)) { + revert OperatorNotOptedInVault(); + } + if ( !INetworkOptInPlugin(NETWORK_OPT_IN_PLUGIN).isOperatorOptedIn(operator, network) && INetworkOptInPlugin(NETWORK_OPT_IN_PLUGIN).lastOperatorOptOut(operator, network) @@ -558,10 +302,6 @@ contract Vault is revert OperatorNotOptedInNetwork(); } - if (!isOperatorOptedIn(operator)) { - revert OperatorNotOptedInVault(); - } - uint256 slashAmount = Math.min(amount, maxSlash_); uint48 vetoDeadline = clock() + vetoDuration; uint48 slashDeadline = vetoDeadline + slashDuration; @@ -813,7 +553,7 @@ contract Vault is revert NotNetwork(); } - if (isNetworkOptedIn[msg.sender][resolver]) { + if (isNetworkOptedIn(msg.sender, resolver)) { revert NetworkAlreadyOptedIn(); } @@ -821,7 +561,7 @@ contract Vault is revert InvalidMaxNetworkLimit(); } - isNetworkOptedIn[msg.sender][resolver] = true; + _isNetworkOptedIn[msg.sender][resolver] = true; _networkLimit[msg.sender][resolver].amount = 0; nextNetworkLimit[msg.sender][resolver].timestamp = 0; @@ -835,13 +575,13 @@ contract Vault is * @inheritdoc IVault */ function optOutNetwork(address resolver) external { - if (!isNetworkOptedIn[msg.sender][resolver]) { + if (!isNetworkOptedIn(msg.sender, resolver)) { revert NetworkNotOptedIn(); } _updateLimit(_networkLimit[msg.sender][resolver], nextNetworkLimit[msg.sender][resolver]); - isNetworkOptedIn[msg.sender][resolver] = false; + _isNetworkOptedIn[msg.sender][resolver] = false; nextNetworkLimit[msg.sender][resolver].amount = 0; nextNetworkLimit[msg.sender][resolver].timestamp = currentEpochStart() + 2 * epochDuration; @@ -888,48 +628,14 @@ contract Vault is /** * @inheritdoc IVault */ - function setNetworkLimit( - address network, - address resolver, - uint256 amount - ) external onlyRole(NETWORK_LIMIT_SET_ROLE) { - if (!isNetworkOptedIn[network][resolver]) { - revert NetworkNotOptedIn(); - } - - if (amount > maxNetworkLimit[network][resolver]) { - revert ExceedsMaxNetworkLimit(); + function setMetadataURL(string calldata metadataURL_) external onlyOwner { + if (metadataURL.equal(metadataURL_)) { + revert AlreadySet(); } - _setLimit(_networkLimit[network][resolver], nextNetworkLimit[network][resolver], amount); - - emit SetNetworkLimit(network, resolver, amount); - } - - /** - * @inheritdoc IVault - */ - function setOperatorLimit( - address operator, - address network, - uint256 amount - ) external onlyRole(OPERATOR_LIMIT_SET_ROLE) { - Limit storage limit = _operatorLimit[operator][network]; - DelayedLimit storage nextLimit = nextOperatorLimit[operator][network]; - - if (!isOperatorOptedIn(operator)) { - if (amount != 0) { - revert OperatorNotOptedIn(); - } else { - limit.amount = 0; - nextLimit.amount = 0; - nextLimit.timestamp = 0; - } - } else { - _setLimit(limit, nextLimit, amount); - } + metadataURL = metadataURL_; - emit SetOperatorLimit(operator, network, amount); + emit SetMetadataURL(metadataURL_); } /** @@ -993,10 +699,50 @@ contract Vault is } /** - * @inheritdoc MigratableEntity + * @inheritdoc IVault + */ + function setNetworkLimit( + address network, + address resolver, + uint256 amount + ) external onlyRole(NETWORK_LIMIT_SET_ROLE) { + if (!isNetworkOptedIn(network, resolver)) { + revert NetworkNotOptedIn(); + } + + if (amount > maxNetworkLimit[network][resolver]) { + revert ExceedsMaxNetworkLimit(); + } + + _setLimit(_networkLimit[network][resolver], nextNetworkLimit[network][resolver], amount); + + emit SetNetworkLimit(network, resolver, amount); + } + + /** + * @inheritdoc IVault */ - function migrate(bytes memory) public override { - revert(); + function setOperatorLimit( + address operator, + address network, + uint256 amount + ) external onlyRole(OPERATOR_LIMIT_SET_ROLE) { + Limit storage limit = _operatorLimit[operator][network]; + DelayedLimit storage nextLimit = nextOperatorLimit[operator][network]; + + if (!isOperatorOptedIn(operator)) { + if (amount != 0) { + revert OperatorNotOptedIn(); + } else { + limit.amount = 0; + nextLimit.amount = 0; + nextLimit.timestamp = 0; + } + } else { + _setLimit(limit, nextLimit, amount); + } + + emit SetOperatorLimit(operator, network, amount); } /** @@ -1034,6 +780,13 @@ contract Vault is } } + function _getLimit(Limit storage limit, DelayedLimit storage nextLimit) private view returns (uint256) { + if (nextLimit.timestamp == 0 || clock() < nextLimit.timestamp) { + return limit.amount; + } + return nextLimit.amount; + } + function _setLimit(Limit storage limit, DelayedLimit storage nextLimit, uint256 amount) private { _updateLimit(limit, nextLimit); diff --git a/src/contracts/VaultStorage.sol b/src/contracts/VaultStorage.sol new file mode 100644 index 00000000..bc295acf --- /dev/null +++ b/src/contracts/VaultStorage.sol @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IVaultStorage} from "src/interfaces/IVaultStorage.sol"; +import {IMigratableEntity} from "src/interfaces/IMigratableEntity.sol"; + +import {MigratableEntity} from "./base/MigratableEntity.sol"; +import {ERC6372} from "./utils/ERC6372.sol"; +import {Checkpoints} from "./libraries/Checkpoints.sol"; + +import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; + +contract VaultStorage is + MigratableEntity, + ERC6372, + AccessControlUpgradeable, + ReentrancyGuardUpgradeable, + IVaultStorage +{ + /** + * @dev Some dead address to transfer slashed tokens to. + */ + address internal constant DEAD = address(0xdEaD); + + /** + * @inheritdoc IVaultStorage + */ + uint256 public constant ADMIN_FEE_BASE = 10_000; + + /** + * @inheritdoc IVaultStorage + */ + bytes32 public constant NETWORK_LIMIT_SET_ROLE = keccak256("NETWORK_LIMIT_SET_ROLE"); + + /** + * @inheritdoc IVaultStorage + */ + bytes32 public constant OPERATOR_LIMIT_SET_ROLE = keccak256("OPERATOR_LIMIT_SET_ROLE"); + + /** + * @inheritdoc IVaultStorage + */ + bytes32 public constant ADMIN_FEE_SET_ROLE = keccak256("ADMIN_FEE_SET_ROLE"); + + /** + * @inheritdoc IVaultStorage + */ + bytes32 public constant DEPOSIT_WHITELIST_SET_ROLE = keccak256("DEPOSIT_WHITELIST_SET_ROLE"); + /** + * @inheritdoc IVaultStorage + */ + bytes32 public constant DEPOSITOR_WHITELIST_ROLE = keccak256("DEPOSITOR_WHITELIST_ROLE"); + + /** + * @inheritdoc IVaultStorage + */ + address public immutable NETWORK_REGISTRY; + + /** + * @inheritdoc IVaultStorage + */ + address public immutable OPERATOR_REGISTRY; + + /** + * @inheritdoc IVaultStorage + */ + address public immutable NETWORK_MIDDLEWARE_PLUGIN; + + /** + * @inheritdoc IVaultStorage + */ + address public immutable NETWORK_OPT_IN_PLUGIN; + + /** + * @inheritdoc IVaultStorage + */ + address public collateral; + + /** + * @inheritdoc IVaultStorage + */ + uint48 public epochDurationInit; + + /** + * @inheritdoc IVaultStorage + */ + uint48 public epochDuration; + + /** + * @inheritdoc IVaultStorage + */ + uint48 public vetoDuration; + + /** + * @inheritdoc IVaultStorage + */ + uint48 public slashDuration; + + /** + * @inheritdoc IVaultStorage + */ + string public metadataURL; + + /** + * @inheritdoc IVaultStorage + */ + uint256 public adminFee; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address token => uint256 amount) public claimableAdminFee; + + /** + * @inheritdoc IVaultStorage + */ + bool public depositWhitelist; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address account => bool value) public isDepositorWhitelisted; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address account => uint48 timestamp) public firstDepositAt; + + /** + * @inheritdoc IVaultStorage + */ + mapping(uint256 epoch => uint256 amount) public withdrawals; + + /** + * @inheritdoc IVaultStorage + */ + mapping(uint256 epoch => uint256 amount) public withdrawalsShares; + + /** + * @inheritdoc IVaultStorage + */ + mapping(uint256 epoch => mapping(address account => uint256 amount)) public withdrawalsSharesOf; + + /** + * @inheritdoc IVaultStorage + */ + SlashRequest[] public slashRequests; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address token => RewardDistribution[] rewards_) public rewards; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address account => mapping(address token => uint256 rewardIndex)) public lastUnclaimedReward; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address operator => uint48 timestamp) public operatorOptOutAt; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address network => mapping(address resolver => uint256 amount)) public maxNetworkLimit; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address network => mapping(address resolver => DelayedLimit)) public nextNetworkLimit; + + /** + * @inheritdoc IVaultStorage + */ + mapping(address operator => mapping(address network => DelayedLimit)) public nextOperatorLimit; + + Checkpoints.Trace256 internal _activeShares; + + Checkpoints.Trace256 internal _activeSupplies; + + mapping(address account => Checkpoints.Trace256 shares) internal _activeSharesOf; + + mapping(uint48 timestamp => uint256 amount) internal _activeSharesCache; + + mapping(uint48 timestamp => uint256 amount) internal _activeSuppliesCache; + + mapping(address network => mapping(address resolver => bool value)) internal _isNetworkOptedIn; + + mapping(address operator => bool value) internal _isOperatorOptedIn; + + mapping(address network => mapping(address resolver => Limit limit)) internal _networkLimit; + + mapping(address operator => mapping(address network => Limit limit)) internal _operatorLimit; + + constructor( + address networkRegistry, + address operatorRegistry, + address networkMiddlewarePlugin, + address networkOptInPlugin + ) { + NETWORK_REGISTRY = networkRegistry; + OPERATOR_REGISTRY = operatorRegistry; + NETWORK_MIDDLEWARE_PLUGIN = networkMiddlewarePlugin; + NETWORK_OPT_IN_PLUGIN = networkOptInPlugin; + } + + /** + * @inheritdoc IMigratableEntity + */ + function initialize( + uint64 version_, + bytes memory data + ) public override(MigratableEntity, IMigratableEntity) reinitializer(version_) { + (IVaultStorage.InitParams memory params) = abi.decode(data, (IVaultStorage.InitParams)); + + if (params.epochDuration == 0) { + revert InvalidEpochDuration(); + } + + if (params.vetoDuration + params.slashDuration > params.epochDuration) { + revert InvalidSlashDuration(); + } + + if (params.adminFee > ADMIN_FEE_BASE) { + revert InvalidAdminFee(); + } + + __ReentrancyGuard_init(); + + _initialize(params.owner); + + metadataURL = params.metadataURL; + collateral = params.collateral; + + epochDurationInit = clock(); + epochDuration = params.epochDuration; + + vetoDuration = params.vetoDuration; + slashDuration = params.slashDuration; + + adminFee = params.adminFee; + depositWhitelist = params.depositWhitelist; + + _grantRole(DEFAULT_ADMIN_ROLE, params.owner); + _grantRole(NETWORK_LIMIT_SET_ROLE, params.owner); + _grantRole(OPERATOR_LIMIT_SET_ROLE, params.owner); + if (params.depositWhitelist) { + _grantRole(DEPOSITOR_WHITELIST_ROLE, params.owner); + } + } + + /** + * @inheritdoc IMigratableEntity + */ + function migrate(bytes memory) public override(MigratableEntity, IMigratableEntity) { + revert(); + } +} diff --git a/src/interfaces/IVault.sol b/src/interfaces/IVault.sol index f5788bcd..baeef7da 100644 --- a/src/interfaces/IVault.sol +++ b/src/interfaces/IVault.sol @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -interface IVault { +import {IVaultStorage} from "./IVaultStorage.sol"; + +interface IVault is IVaultStorage { error NotNetwork(); - error NotNetworkOwner(); error NotNetworkMiddleware(); error NotOperator(); - error NotOperatorOwner(); - error InvalidEpochDuration(); - error InvalidSlashDuration(); - error InvalidAdminFee(); error NotWhitelistedDepositor(); error InsufficientDeposit(); error InsufficientWithdrawal(); @@ -33,98 +30,14 @@ interface IVault { error OperatorNotOptedIn(); error NoRewardsToClaim(); error InvalidHintsLength(); - error InsufficientLimit(); error InsufficientReward(); error InvalidRewardTimestamp(); error UnacceptedAdminFee(); - error NotEqualLengths(); error NoDeposits(); error AlreadySet(); error InsufficientAdminFee(); error NoDepositWhitelist(); - /** - * @notice Initial parameters needed for a vault deployment. - * @param owner owner of the vault (can set metadata and enable/disable deposit whitelist) - * @param metadataURL URL with metadata of the vault - * The metadata should contain: name, description, external_url, image. - * @param collateral underlying vault collateral - * @param epochDuration duration of an vault epoch - * @param vetoDuration duration of the veto period for a slash request - * @param slashDuration duration of the slash period for a slash request (after veto period) - * @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively) - * @param depositWhitelist enable/disable deposit whitelist - */ - struct InitParams { - address owner; - string metadataURL; - address collateral; - uint48 epochDuration; - uint48 vetoDuration; - uint48 slashDuration; - uint256 adminFee; - bool depositWhitelist; - } - - /** - * @notice Structure for a slashing limit. - * @param amount amount of the collateral that can be slashed - */ - struct Limit { - uint256 amount; - } - - /** - * @notice Structure for a slashing limit which will be set in the future. - * @param amount amount of the collateral that can be slashed - * @param timestamp timestamp when the limit will be set - */ - struct DelayedLimit { - uint256 amount; - uint48 timestamp; - } - - /** - * @notice Structure for a slash request. - * @param network network which requested the slash - * @param resolver resolver who can veto the slash - * @param operator operator who could be slashed - * @param amount maximum amount of the collateral to be slashed - * @param vetoDeadline deadline for the resolver to veto the slash - * @param slashDeadline deadline to execute slash - * @param completed if the slash was vetoed/executed - * - */ - struct SlashRequest { - address network; - address resolver; - address operator; - uint256 amount; - uint48 vetoDeadline; - uint48 slashDeadline; - bool completed; - } - - /** - * @notice Structure for a reward distribution. - * @param network network on behalf of which the reward is distributed - * @param amount amount of tokens to be distributed (admin fee is excluded) - * @param timestamp time point stakes must taken into account at - * @param creation timestamp when the reward distribution was created - */ - struct RewardDistribution { - address network; - uint256 amount; - uint48 timestamp; - uint48 creation; - } - - /** - * @notice Emitted when a metadata URL is set. - * @param metadataURL metadata URL of the vault - */ - event SetMetadataURL(string metadataURL); - /** * @notice Emitted when a deposit is made. * @param depositor account that made the deposit @@ -242,20 +155,10 @@ interface IVault { ); /** - * @notice Emitted when a network limit is set. - * @param network network for which the limit is set - * @param resolver resolver for which the limit is set - * @param amount amount of the collateral that can be slashed - */ - event SetNetworkLimit(address indexed network, address indexed resolver, uint256 amount); - - /** - * @notice Emitted when an operator limit is set. - * @param operator operator for which the limit is set - * @param network network for which the limit is set - * @param amount amount of the collateral that can be slashed + * @notice Emitted when a metadata URL is set. + * @param metadataURL metadata URL of the vault */ - event SetOperatorLimit(address indexed operator, address indexed network, uint256 amount); + event SetMetadataURL(string metadataURL); /** * @notice Emitted when an admin fee is set. @@ -284,84 +187,20 @@ interface IVault { event SetDepositorWhitelistStatus(address indexed account, bool value); /** - * @notice Get the maximum admin fee (= 100%). - * @return maximum admin fee - */ - function ADMIN_FEE_BASE() external view returns (uint256); - - /** - * @notice Get the network limit setter's role. - */ - function NETWORK_LIMIT_SET_ROLE() external view returns (bytes32); - - /** - * @notice Get the operator limit setter's role. - */ - function OPERATOR_LIMIT_SET_ROLE() external view returns (bytes32); - - /** - * @notice Get the admin fee setter's role. - */ - function ADMIN_FEE_SET_ROLE() external view returns (bytes32); - - /** - * @notice Get the deposit whitelist enabler/disabler's role. - */ - function DEPOSIT_WHITELIST_SET_ROLE() external view returns (bytes32); - - /** - * @notice Get the depositor whitelist status setter's role. - */ - function DEPOSITOR_WHITELIST_ROLE() external view returns (bytes32); - - /** - * @notice Get the network registry's address. - * @return address of the registry - */ - function NETWORK_REGISTRY() external view returns (address); - - /** - * @notice Get the operator registry's address. - * @return address of the operator registry - */ - function OPERATOR_REGISTRY() external view returns (address); - - /** - * @notice Get the network middleware plugin's address. - * @return address of the network middleware plugin - */ - function NETWORK_MIDDLEWARE_PLUGIN() external view returns (address); - - /** - * @notice Get the network opt-in plugin's address. - * @return address of the network opt-in plugin - */ - function NETWORK_OPT_IN_PLUGIN() external view returns (address); - - /** - * @notice Get a URL with a vault's metadata. - * The metadata should contain: name, description, external_url, image. - * @return metadata URL of the vault - */ - function metadataURL() external view returns (string memory); - - /** - * @notice Get a vault collateral. - * @return collateral underlying vault - */ - function collateral() external view returns (address); - - /** - * @notice Get a time point of the epoch duration set. - * @return time point of the epoch duration set + * @notice Emitted when a network limit is set. + * @param network network for which the limit is set + * @param resolver resolver for which the limit is set + * @param amount amount of the collateral that can be slashed */ - function epochDurationInit() external view returns (uint48); + event SetNetworkLimit(address indexed network, address indexed resolver, uint256 amount); /** - * @notice Get a duration of the vault epoch. - * @return duration of the epoch + * @notice Emitted when an operator limit is set. + * @param operator operator for which the limit is set + * @param network network for which the limit is set + * @param amount amount of the collateral that can be slashed */ - function epochDuration() external view returns (uint48); + event SetOperatorLimit(address indexed operator, address indexed network, uint256 amount); /** * @notice Get a current vault epoch. @@ -375,18 +214,6 @@ interface IVault { */ function currentEpochStart() external view returns (uint48); - /** - * @notice Get a duration during which resolvers can veto slash requests. - * @return duration of the veto period - */ - function vetoDuration() external view returns (uint48); - - /** - * @notice Get a duration during which slash requests can be executed (after veto period). - * @return duration of the slash period - */ - function slashDuration() external view returns (uint48); - /** * @notice Get a total amount of the collateral deposited in the vault. * @return total amount of the collateral deposited @@ -465,28 +292,6 @@ interface IVault { */ function activeBalanceOf(address account) external view returns (uint256); - /** - * @notice Get a total amount of the withdrawals at a given epoch. - * @param epoch epoch to get the total amount of the withdrawals at - * @return total amount of the withdrawals at the epoch - */ - function withdrawals(uint256 epoch) external view returns (uint256); - - /** - * @notice Get a total amount of the withdrawals shares at a given epoch. - * @param epoch epoch to get the total amount of the withdrawals shares at - * @return total amount of the withdrawals shares at the epoch - */ - function withdrawalsShares(uint256 epoch) external view returns (uint256); - - /** - * @notice Get an amount of the withdrawals shares for a particular account at a given epoch. - * @param epoch epoch to get the amount of the withdrawals shares for the account at - * @param account account to get the amount of the withdrawals shares for - * @return amount of the withdrawals shares for the account at the epoch - */ - function withdrawalsSharesOf(uint256 epoch, address account) external view returns (uint256); - /** * @notice Get a withdrawals balance for a particular account at a given epoch. * @param epoch epoch to get the withdrawals balance for the account at @@ -495,13 +300,6 @@ interface IVault { */ function withdrawalsBalanceOf(uint256 epoch, address account) external view returns (uint256); - /** - * @notice Get a timestamp when the first deposit was made by a particular account. - * @param account account to get the timestamp when the first deposit was made for - * @return timestamp when the first deposit was made - */ - function firstDepositAt(address account) external view returns (uint48); - /** * @notice Get a maximum amount of collateral that can be slashed for particular network, resolver and operator. * @param network address of the network @@ -517,30 +315,6 @@ interface IVault { */ function slashRequestsLength() external view returns (uint256); - /** - * @notice Get a slash request. - * @param slashIndex index of the slash request - * @return network network which requested the slash - * @return resolver resolver who can veto the slash - * @return operator operator who could be slashed - * @return amount maximum amount of the collateral to be slashed - * @return vetoDeadline deadline for the resolver to veto the slash - * @return slashDeadline deadline to execute slash - * @return completed if the slash was vetoed/executed - */ - function slashRequests(uint256 slashIndex) - external - view - returns ( - address network, - address resolver, - address operator, - uint256 amount, - uint48 vetoDeadline, - uint48 slashDeadline, - bool completed - ); - /** * @notice Get a total number of rewards using a particular token. * @param token address of the token @@ -548,28 +322,6 @@ interface IVault { */ function rewardsLength(address token) external view returns (uint256); - /** - * @notice Get a reward distribution. - * @param token address of the token - * @param rewardIndex index of the reward distribution - * @return network network on behalf of which the reward is distributed - * @return amount amount of tokens to be distributed - * @return timestamp time point stakes must taken into account at - * @return creation timestamp when the reward distribution was created - */ - function rewards( - address token, - uint256 rewardIndex - ) external view returns (address network, uint256 amount, uint48 timestamp, uint48 creation); - - /** - * @notice Get a first index of the unclaimed rewards using a particular token by a given account. - * @param account address of the account - * @param token address of the token - * @return first index of the unclaimed rewards - */ - function lastUnclaimedReward(address account, address token) external view returns (uint256); - /** * @notice Get if a given network-resolver pair is opted in. * @return if the network-resolver pair is opted in @@ -582,21 +334,6 @@ interface IVault { */ function isOperatorOptedIn(address operator) external view returns (bool); - /** - * @notice Get a timestamp of last operator opt-out. - * @param operator address of the operator - * @return timestamp of the last operator opt-out - */ - function operatorOptOutAt(address operator) external view returns (uint48); - - /** - * @notice Get a maximum network limit for a particular network and resolver. - * @param network address of the network - * @param resolver address of the resolver - * @return maximum network limit - */ - function maxNetworkLimit(address network, address resolver) external view returns (uint256); - /** * @notice Get a network limit for a particular network and resolver. * @param network address of the network @@ -605,15 +342,6 @@ interface IVault { */ function networkLimit(address network, address resolver) external view returns (uint256); - /** - * @notice Get next network limit for a particular network and resolver. - * @param network address of the network - * @param resolver address of the resolver - * @return next network limit - * @return timestamp when the limit will be set - */ - function nextNetworkLimit(address network, address resolver) external view returns (uint256, uint48); - /** * @notice Get an operator limit for a particular operator and network. * @param operator address of the operator @@ -622,49 +350,6 @@ interface IVault { */ function operatorLimit(address operator, address network) external view returns (uint256); - /** - * @notice Get next operator limit for a particular operator and network. - * @param operator address of the operator - * @param network address of the network - * @return next operator limit - * @return timestamp when the limit will be set - */ - function nextOperatorLimit(address operator, address network) external view returns (uint256, uint48); - - /** - * @notice Get an admin fee. - * @return admin fee - */ - function adminFee() external view returns (uint256); - - /** - * @notice Get a claimable fee amount for a particular token. - * @param token address of the token - * @return claimable fee - */ - function claimableAdminFee(address token) external view returns (uint256); - - /** - * @notice Get if the deposit whitelist is enabled. - * @return if the deposit whitelist is enabled - */ - function depositWhitelist() external view returns (bool); - - /** - * @notice Get if a given account is whitelisted as a depositor. - * @param account address to check - * @return if the account is whitelisted as a depositor - */ - function isDepositorWhitelisted(address account) external view returns (bool); - - /** - * @notice Set a new metadata URL for this vault. - * @param metadataURL metadata URL of the vault - * The metadata should contain: name, description, external_url, image. - * @dev Only owner can call this function. - */ - function setMetadataURL(string calldata metadataURL) external; - /** * @notice Deposit collateral into the vault. * @param onBehalfOf account that the deposit is made on behalf of @@ -778,22 +463,12 @@ interface IVault { ) external; /** - * @notice Set a network limit for a particular network and resolver. - * @param network address of the network - * @param resolver address of the resolver - * @param amount maximum amount of the collateral that can be slashed - * @dev Only NETWORK_LIMIT_SET_ROLE holder can call this function. - */ - function setNetworkLimit(address network, address resolver, uint256 amount) external; - - /** - * @notice Set an operator limit for a particular operator and network. - * @param operator address of the operator - * @param network address of the network - * @param amount maximum amount of the collateral that can be slashed - * @dev Only OPERATOR_LIMIT_SET_ROLE holder can call this function. + * @notice Set a new metadata URL for this vault. + * @param metadataURL metadata URL of the vault + * The metadata should contain: name, description, external_url, image. + * @dev Only owner can call this function. */ - function setOperatorLimit(address operator, address network, uint256 amount) external; + function setMetadataURL(string calldata metadataURL) external; /** * @notice Set an admin fee. @@ -823,4 +498,22 @@ interface IVault { * @dev Only DEPOSITOR_WHITELIST_ROLE holder can call this function. */ function setDepositorWhitelistStatus(address account, bool status) external; + + /** + * @notice Set a network limit for a particular network and resolver. + * @param network address of the network + * @param resolver address of the resolver + * @param amount maximum amount of the collateral that can be slashed + * @dev Only NETWORK_LIMIT_SET_ROLE holder can call this function. + */ + function setNetworkLimit(address network, address resolver, uint256 amount) external; + + /** + * @notice Set an operator limit for a particular operator and network. + * @param operator address of the operator + * @param network address of the network + * @param amount maximum amount of the collateral that can be slashed + * @dev Only OPERATOR_LIMIT_SET_ROLE holder can call this function. + */ + function setOperatorLimit(address operator, address network, uint256 amount) external; } diff --git a/src/interfaces/IVaultStorage.sol b/src/interfaces/IVaultStorage.sol new file mode 100644 index 00000000..83403539 --- /dev/null +++ b/src/interfaces/IVaultStorage.sol @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IMigratableEntity} from "src/interfaces/IMigratableEntity.sol"; + +interface IVaultStorage is IMigratableEntity { + error InvalidEpochDuration(); + error InvalidSlashDuration(); + error InvalidAdminFee(); + + /** + * @notice Initial parameters needed for a vault deployment. + * @param owner owner of the vault (can set metadata and enable/disable deposit whitelist) + * @param metadataURL URL with metadata of the vault + * The metadata should contain: name, description, external_url, image. + * @param collateral underlying vault collateral + * @param epochDuration duration of an vault epoch + * @param vetoDuration duration of the veto period for a slash request + * @param slashDuration duration of the slash period for a slash request (after veto period) + * @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively) + * @param depositWhitelist enable/disable deposit whitelist + */ + struct InitParams { + address owner; + string metadataURL; + address collateral; + uint48 epochDuration; + uint48 vetoDuration; + uint48 slashDuration; + uint256 adminFee; + bool depositWhitelist; + } + + /** + * @notice Structure for a slashing limit. + * @param amount amount of the collateral that can be slashed + */ + struct Limit { + uint256 amount; + } + + /** + * @notice Structure for a slashing limit which will be set in the future. + * @param amount amount of the collateral that can be slashed + * @param timestamp timestamp when the limit will be set + */ + struct DelayedLimit { + uint256 amount; + uint48 timestamp; + } + + /** + * @notice Structure for a slash request. + * @param network network which requested the slash + * @param resolver resolver who can veto the slash + * @param operator operator who could be slashed + * @param amount maximum amount of the collateral to be slashed + * @param vetoDeadline deadline for the resolver to veto the slash + * @param slashDeadline deadline to execute slash + * @param completed if the slash was vetoed/executed + * + */ + struct SlashRequest { + address network; + address resolver; + address operator; + uint256 amount; + uint48 vetoDeadline; + uint48 slashDeadline; + bool completed; + } + + /** + * @notice Structure for a reward distribution. + * @param network network on behalf of which the reward is distributed + * @param amount amount of tokens to be distributed (admin fee is excluded) + * @param timestamp time point stakes must taken into account at + * @param creation timestamp when the reward distribution was created + */ + struct RewardDistribution { + address network; + uint256 amount; + uint48 timestamp; + uint48 creation; + } + + /** + * @notice Get the maximum admin fee (= 100%). + * @return maximum admin fee + */ + function ADMIN_FEE_BASE() external view returns (uint256); + + /** + * @notice Get the network limit setter's role. + */ + function NETWORK_LIMIT_SET_ROLE() external view returns (bytes32); + + /** + * @notice Get the operator limit setter's role. + */ + function OPERATOR_LIMIT_SET_ROLE() external view returns (bytes32); + + /** + * @notice Get the admin fee setter's role. + */ + function ADMIN_FEE_SET_ROLE() external view returns (bytes32); + + /** + * @notice Get the deposit whitelist enabler/disabler's role. + */ + function DEPOSIT_WHITELIST_SET_ROLE() external view returns (bytes32); + + /** + * @notice Get the depositor whitelist status setter's role. + */ + function DEPOSITOR_WHITELIST_ROLE() external view returns (bytes32); + + /** + * @notice Get the network registry's address. + * @return address of the registry + */ + function NETWORK_REGISTRY() external view returns (address); + + /** + * @notice Get the operator registry's address. + * @return address of the operator registry + */ + function OPERATOR_REGISTRY() external view returns (address); + + /** + * @notice Get the network middleware plugin's address. + * @return address of the network middleware plugin + */ + function NETWORK_MIDDLEWARE_PLUGIN() external view returns (address); + + /** + * @notice Get the network opt-in plugin's address. + * @return address of the network opt-in plugin + */ + function NETWORK_OPT_IN_PLUGIN() external view returns (address); + + /** + * @notice Get a vault collateral. + * @return collateral underlying vault + */ + function collateral() external view returns (address); + + /** + * @notice Get a time point of the epoch duration set. + * @return time point of the epoch duration set + */ + function epochDurationInit() external view returns (uint48); + + /** + * @notice Get a duration of the vault epoch. + * @return duration of the epoch + */ + function epochDuration() external view returns (uint48); + + /** + * @notice Get a duration during which resolvers can veto slash requests. + * @return duration of the veto period + */ + function vetoDuration() external view returns (uint48); + + /** + * @notice Get a duration during which slash requests can be executed (after veto period). + * @return duration of the slash period + */ + function slashDuration() external view returns (uint48); + + /** + * @notice Get a URL with a vault's metadata. + * The metadata should contain: name, description, external_url, image. + * @return metadata URL of the vault + */ + function metadataURL() external view returns (string memory); + + /** + * @notice Get an admin fee. + * @return admin fee + */ + function adminFee() external view returns (uint256); + + /** + * @notice Get a claimable fee amount for a particular token. + * @param token address of the token + * @return claimable fee + */ + function claimableAdminFee(address token) external view returns (uint256); + + /** + * @notice Get if the deposit whitelist is enabled. + * @return if the deposit whitelist is enabled + */ + function depositWhitelist() external view returns (bool); + + /** + * @notice Get if a given account is whitelisted as a depositor. + * @param account address to check + * @return if the account is whitelisted as a depositor + */ + function isDepositorWhitelisted(address account) external view returns (bool); + + /** + * @notice Get a total amount of the withdrawals at a given epoch. + * @param epoch epoch to get the total amount of the withdrawals at + * @return total amount of the withdrawals at the epoch + */ + function withdrawals(uint256 epoch) external view returns (uint256); + + /** + * @notice Get a total amount of the withdrawals shares at a given epoch. + * @param epoch epoch to get the total amount of the withdrawals shares at + * @return total amount of the withdrawals shares at the epoch + */ + function withdrawalsShares(uint256 epoch) external view returns (uint256); + + /** + * @notice Get an amount of the withdrawals shares for a particular account at a given epoch. + * @param epoch epoch to get the amount of the withdrawals shares for the account at + * @param account account to get the amount of the withdrawals shares for + * @return amount of the withdrawals shares for the account at the epoch + */ + function withdrawalsSharesOf(uint256 epoch, address account) external view returns (uint256); + + /** + * @notice Get a timestamp when the first deposit was made by a particular account. + * @param account account to get the timestamp when the first deposit was made for + * @return timestamp when the first deposit was made + */ + function firstDepositAt(address account) external view returns (uint48); + + /** + * @notice Get a slash request. + * @param slashIndex index of the slash request + * @return network network which requested the slash + * @return resolver resolver who can veto the slash + * @return operator operator who could be slashed + * @return amount maximum amount of the collateral to be slashed + * @return vetoDeadline deadline for the resolver to veto the slash + * @return slashDeadline deadline to execute slash + * @return completed if the slash was vetoed/executed + */ + function slashRequests(uint256 slashIndex) + external + view + returns ( + address network, + address resolver, + address operator, + uint256 amount, + uint48 vetoDeadline, + uint48 slashDeadline, + bool completed + ); + + /** + * @notice Get a reward distribution. + * @param token address of the token + * @param rewardIndex index of the reward distribution + * @return network network on behalf of which the reward is distributed + * @return amount amount of tokens to be distributed + * @return timestamp time point stakes must taken into account at + * @return creation timestamp when the reward distribution was created + */ + function rewards( + address token, + uint256 rewardIndex + ) external view returns (address network, uint256 amount, uint48 timestamp, uint48 creation); + + /** + * @notice Get a first index of the unclaimed rewards using a particular token by a given account. + * @param account address of the account + * @param token address of the token + * @return first index of the unclaimed rewards + */ + function lastUnclaimedReward(address account, address token) external view returns (uint256); + + /** + * @notice Get a timestamp of last operator opt-out. + * @param operator address of the operator + * @return timestamp of the last operator opt-out + */ + function operatorOptOutAt(address operator) external view returns (uint48); + + /** + * @notice Get a maximum network limit for a particular network and resolver. + * @param network address of the network + * @param resolver address of the resolver + * @return maximum network limit + */ + function maxNetworkLimit(address network, address resolver) external view returns (uint256); + + /** + * @notice Get next network limit for a particular network and resolver. + * @param network address of the network + * @param resolver address of the resolver + * @return next network limit + * @return timestamp when the limit will be set + */ + function nextNetworkLimit(address network, address resolver) external view returns (uint256, uint48); + + /** + * @notice Get next operator limit for a particular operator and network. + * @param operator address of the operator + * @param network address of the network + * @return next operator limit + * @return timestamp when the limit will be set + */ + function nextOperatorLimit(address operator, address network) external view returns (uint256, uint48); +} diff --git a/test/Vault.t.sol b/test/Vault.t.sol index c46364c6..4ca52c42 100644 --- a/test/Vault.t.sol +++ b/test/Vault.t.sol @@ -11,6 +11,7 @@ import {NetworkOptInPlugin} from "src/contracts/plugins/NetworkOptInPlugin.sol"; import {Vault} from "src/contracts/Vault.sol"; import {IVault} from "src/interfaces/IVault.sol"; +import {IVaultStorage} from "src/interfaces/IVaultStorage.sol"; import {Token} from "./mocks/Token.sol"; import {FeeOnTransferToken} from "test/mocks/FeeOnTransferToken.sol"; @@ -89,7 +90,7 @@ contract VaultTest is Test { vaultRegistry.create( vaultRegistry.lastVersion(), abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral), @@ -176,7 +177,7 @@ contract VaultTest is Test { vaultRegistry.create( vaultRegistry.lastVersion(), abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral), @@ -1581,12 +1582,12 @@ contract VaultTest is Test { string memory metadataURL = ""; uint64 lastVersion = vaultRegistry.lastVersion(); - vm.expectRevert(IVault.InvalidEpochDuration.selector); + vm.expectRevert(IVaultStorage.InvalidEpochDuration.selector); vault = IVault( vaultRegistry.create( lastVersion, abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral), @@ -1613,12 +1614,12 @@ contract VaultTest is Test { string memory metadataURL = ""; uint64 lastVersion = vaultRegistry.lastVersion(); - vm.expectRevert(IVault.InvalidSlashDuration.selector); + vm.expectRevert(IVaultStorage.InvalidSlashDuration.selector); vault = IVault( vaultRegistry.create( lastVersion, abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral), @@ -1642,12 +1643,12 @@ contract VaultTest is Test { string memory metadataURL = ""; uint64 lastVersion = vaultRegistry.lastVersion(); - vm.expectRevert(IVault.InvalidAdminFee.selector); + vm.expectRevert(IVaultStorage.InvalidAdminFee.selector); vault = IVault( vaultRegistry.create( lastVersion, abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral), @@ -2293,6 +2294,45 @@ contract VaultTest is Test { _distributeReward(bob, network, address(feeOnTransferToken), 1, timestamp, acceptedAdminFee); } + function test_DistributeRewardRevertUnacceptedAdminFee(uint256 amount, uint256 adminFee) public { + amount = bound(amount, 1, 100 * 10 ** 18); + + string memory metadataURL = ""; + uint48 epochDuration = 1; + uint48 vetoDuration = 0; + uint48 slashDuration = 1; + vault = _getVault(metadataURL, epochDuration, vetoDuration, slashDuration); + adminFee = bound(adminFee, 1, vault.ADMIN_FEE_BASE()); + + _grantAdminFeeSetRole(alice, alice); + _setAdminFee(alice, adminFee); + + address network = bob; + _registerNetwork(network, bob); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + for (uint256 i; i < 10; ++i) { + _deposit(alice, amount); + + blockTimestamp = blockTimestamp + 1; + vm.warp(blockTimestamp); + } + + IERC20 feeOnTransferToken = IERC20(new FeeOnTransferToken("FeeOnTransferToken")); + feeOnTransferToken.transfer(bob, 100_000 * 1e18); + vm.startPrank(bob); + feeOnTransferToken.approve(address(vault), type(uint256).max); + vm.stopPrank(); + + uint48 timestamp = 3; + uint256 acceptedAdminFee = adminFee - 1; + vm.expectRevert(IVault.UnacceptedAdminFee.selector); + _distributeReward( + bob, network, address(feeOnTransferToken), uint48(blockTimestamp), timestamp, acceptedAdminFee + ); + } + function test_ClaimRewards(uint256 amount, uint256 ditributeAmount) public { amount = bound(amount, 1, 100 * 10 ** 18); ditributeAmount = bound(ditributeAmount, 1, 100 * 10 ** 18); @@ -2690,7 +2730,7 @@ contract VaultTest is Test { vm.assume(adminFee > vault.ADMIN_FEE_BASE()); _grantAdminFeeSetRole(alice, alice); - vm.expectRevert(IVault.InvalidAdminFee.selector); + vm.expectRevert(IVaultStorage.InvalidAdminFee.selector); _setAdminFee(alice, adminFee); } @@ -2810,7 +2850,7 @@ contract VaultTest is Test { vaultRegistry.create( vaultRegistry.lastVersion(), abi.encode( - IVault.InitParams({ + IVaultStorage.InitParams({ owner: alice, metadataURL: metadataURL, collateral: address(collateral),