diff --git a/src/contracts/Vault.sol b/src/contracts/Vault.sol index c8ef0a50..8ac6f393 100644 --- a/src/contracts/Vault.sol +++ b/src/contracts/Vault.sol @@ -7,28 +7,26 @@ import {IRegistry} from "src/interfaces/base/IRegistry.sol"; import {IMiddlewarePlugin} from "src/interfaces/plugins/IMiddlewarePlugin.sol"; import {INetworkOptInPlugin} from "src/interfaces/plugins/INetworkOptInPlugin.sol"; -import {VaultStorage} from "./VaultStorage.sol"; -import {Checkpoints} from "./libraries/Checkpoints.sol"; +import {VaultDelegation} from "./VaultDelegation.sol"; import {ERC4626Math} from "./libraries/ERC4626Math.sol"; +import {Checkpoints} from "./libraries/Checkpoints.sol"; 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 {MulticallUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; -contract Vault is VaultStorage, MulticallUpgradeable, IVault { +contract Vault is VaultDelegation, MulticallUpgradeable, IVault { using Checkpoints for Checkpoints.Trace256; using SafeERC20 for IERC20; using Math for uint256; - using Strings for string; constructor( address networkRegistry, address operatorRegistry, address networkMiddlewarePlugin, address networkOptInPlugin - ) VaultStorage(networkRegistry, operatorRegistry, networkMiddlewarePlugin, networkOptInPlugin) {} + ) VaultDelegation(networkRegistry, operatorRegistry, networkMiddlewarePlugin, networkOptInPlugin) {} /** * @inheritdoc IVault @@ -69,40 +67,6 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { return Math.min(totalSupply(), Math.min(networkLimit(network, resolver), operatorLimit(operator, network))); } - /** - * @inheritdoc IVault - */ - function isNetworkOptedIn(address network, address resolver) public view returns (bool) { - return _isNetworkOptedIn[network][resolver]; - } - - /** - * @inheritdoc IVault - */ - function isOperatorOptedIn(address operator) public view returns (bool) { - if (operatorOptOutAt[operator] == 0) { - return _isOperatorOptedIn[operator]; - } - if (clock() < operatorOptOutAt[operator]) { - return true; - } - return false; - } - - /** - * @inheritdoc IVault - */ - function networkLimit(address network, address resolver) public view returns (uint256) { - return _getLimit(_networkLimit[network][resolver], nextNetworkLimit[network][resolver]); - } - - /** - * @inheritdoc IVault - */ - function operatorLimit(address operator, address network) public view returns (uint256) { - return _getLimit(_operatorLimit[operator][network], nextOperatorLimit[operator][network]); - } - /** * @inheritdoc IVault */ @@ -212,7 +176,7 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { } if (!isOperatorOptedIn(operator)) { - revert OperatorNotOptedInVault(); + revert OperatorNotOptedIn(); } if ( @@ -266,7 +230,7 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { } if (!isOperatorOptedIn(request.operator)) { - revert OperatorNotOptedInVault(); + revert OperatorNotOptedIn(); } request.completed = true; @@ -466,116 +430,6 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { } } - /** - * @inheritdoc IVault - */ - function optInNetwork(address resolver, uint256 maxNetworkLimit_) external { - if (!IRegistry(NETWORK_REGISTRY).isEntity(msg.sender)) { - revert NotNetwork(); - } - - if (isNetworkOptedIn(msg.sender, resolver)) { - revert NetworkAlreadyOptedIn(); - } - - if (maxNetworkLimit_ == 0) { - revert InvalidMaxNetworkLimit(); - } - - _isNetworkOptedIn[msg.sender][resolver] = true; - - _networkLimit[msg.sender][resolver].amount = 0; - nextNetworkLimit[msg.sender][resolver].timestamp = 0; - - maxNetworkLimit[msg.sender][resolver] = maxNetworkLimit_; - - emit OptInNetwork(msg.sender, resolver); - } - - /** - * @inheritdoc IVault - */ - function optOutNetwork(address resolver) external { - if (!isNetworkOptedIn(msg.sender, resolver)) { - revert NetworkNotOptedIn(); - } - - _updateLimit(_networkLimit[msg.sender][resolver], nextNetworkLimit[msg.sender][resolver]); - - _isNetworkOptedIn[msg.sender][resolver] = false; - - nextNetworkLimit[msg.sender][resolver].amount = 0; - nextNetworkLimit[msg.sender][resolver].timestamp = currentEpochStart() + 2 * epochDuration; - - maxNetworkLimit[msg.sender][resolver] = 0; - - emit OptOutNetwork(msg.sender, resolver); - } - - /** - * @inheritdoc IVault - */ - function optInOperator() external { - if (!IRegistry(OPERATOR_REGISTRY).isEntity(msg.sender)) { - revert NotOperator(); - } - - if (isOperatorOptedIn(msg.sender)) { - revert OperatorAlreadyOptedIn(); - } - - if (!_isOperatorOptedIn[msg.sender]) { - _isOperatorOptedIn[msg.sender] = true; - } else { - operatorOptOutAt[msg.sender] = 0; - } - - emit OptInOperator(msg.sender); - } - - /** - * @inheritdoc IVault - */ - function optOutOperator() external { - if (!isOperatorOptedIn(msg.sender)) { - revert OperatorNotOptedIn(); - } - - operatorOptOutAt[msg.sender] = currentEpochStart() + 2 * epochDuration; - - emit OptOutOperator(msg.sender); - } - - /** - * @inheritdoc IVault - */ - function setMetadataURL(string calldata metadataURL_) external onlyOwner { - if (metadataURL.equal(metadataURL_)) { - revert AlreadySet(); - } - - metadataURL = metadataURL_; - - emit SetMetadataURL(metadataURL_); - } - - /** - * @inheritdoc IVault - */ - function setAdminFee(uint256 adminFee_) external onlyRole(ADMIN_FEE_SET_ROLE) { - if (adminFee == adminFee_) { - revert AlreadySet(); - } - - if (adminFee_ > ADMIN_FEE_BASE) { - revert InvalidAdminFee(); - } - - adminFee = adminFee_; - - emit SetAdminFee(adminFee_); - } - function claimAdminFee(address recipient, address token) external onlyOwner { uint256 claimableAdminFee_ = claimableAdminFee[token]; if (claimableAdminFee_ == 0) { @@ -589,83 +443,6 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { emit ClaimAdminFee(token, claimableAdminFee_); } - /** - * @inheritdoc IVault - */ - function setDepositWhitelist(bool status) external onlyRole(DEPOSIT_WHITELIST_SET_ROLE) { - if (depositWhitelist == status) { - revert AlreadySet(); - } - - depositWhitelist = status; - - emit SetDepositWhitelist(status); - } - - /** - * @inheritdoc IVault - */ - function setDepositorWhitelistStatus(address account, bool status) external onlyRole(DEPOSITOR_WHITELIST_ROLE) { - if (status && !depositWhitelist) { - revert NoDepositWhitelist(); - } - - if (isDepositorWhitelisted[account] == status) { - revert AlreadySet(); - } - - isDepositorWhitelisted[account] = status; - - emit SetDepositorWhitelistStatus(account, status); - } - - /** - * @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 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); - } - /** * @dev Searches a sorted by creation time `array` and returns the first index that contains * a RewardDistribution structure with `creation` greater or equal to `unclaimedFrom`. If no such index exists (i.e. all @@ -700,32 +477,4 @@ contract Vault is VaultStorage, MulticallUpgradeable, IVault { return len; } } - - 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); - - if (amount < limit.amount) { - nextLimit.amount = amount; - nextLimit.timestamp = currentEpochStart() + 2 * epochDuration; - } else { - limit.amount = amount; - nextLimit.amount = 0; - nextLimit.timestamp = 0; - } - } - - function _updateLimit(Limit storage limit, DelayedLimit storage nextLimit) private { - if (nextLimit.timestamp != 0 && nextLimit.timestamp <= clock()) { - limit.amount = nextLimit.amount; - nextLimit.timestamp = 0; - nextLimit.amount = 0; - } - } } diff --git a/src/contracts/VaultDelegation.sol b/src/contracts/VaultDelegation.sol new file mode 100644 index 00000000..b9bdbcf7 --- /dev/null +++ b/src/contracts/VaultDelegation.sol @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IVaultDelegation} from "src/interfaces/IVaultDelegation.sol"; +import {IRegistry} from "src/interfaces/base/IRegistry.sol"; + +import {VaultStorage} from "./VaultStorage.sol"; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +contract VaultDelegation is VaultStorage, IVaultDelegation { + using Strings for string; + + constructor( + address networkRegistry, + address operatorRegistry, + address networkMiddlewarePlugin, + address networkOptInPlugin + ) VaultStorage(networkRegistry, operatorRegistry, networkMiddlewarePlugin, networkOptInPlugin) {} + + /** + * @inheritdoc IVaultDelegation + */ + function isNetworkOptedIn(address network, address resolver) public view returns (bool) { + return _isNetworkOptedIn[network][resolver]; + } + + /** + * @inheritdoc IVaultDelegation + */ + function isOperatorOptedIn(address operator) public view returns (bool) { + if (operatorOptOutAt[operator] == 0) { + return _isOperatorOptedIn[operator]; + } + if (clock() < operatorOptOutAt[operator]) { + return true; + } + return false; + } + + /** + * @inheritdoc IVaultDelegation + */ + function networkLimit(address network, address resolver) public view returns (uint256) { + return _getLimit(_networkLimit[network][resolver], nextNetworkLimit[network][resolver]); + } + + /** + * @inheritdoc IVaultDelegation + */ + function operatorLimit(address operator, address network) public view returns (uint256) { + return _getLimit(_operatorLimit[operator][network], nextOperatorLimit[operator][network]); + } + + /** + * @inheritdoc IVaultDelegation + */ + function optInNetwork(address resolver, uint256 maxNetworkLimit_) external { + if (!IRegistry(NETWORK_REGISTRY).isEntity(msg.sender)) { + revert NotNetwork(); + } + + if (isNetworkOptedIn(msg.sender, resolver)) { + revert NetworkAlreadyOptedIn(); + } + + if (maxNetworkLimit_ == 0) { + revert InvalidMaxNetworkLimit(); + } + + _isNetworkOptedIn[msg.sender][resolver] = true; + + _networkLimit[msg.sender][resolver].amount = 0; + nextNetworkLimit[msg.sender][resolver].timestamp = 0; + + maxNetworkLimit[msg.sender][resolver] = maxNetworkLimit_; + + emit OptInNetwork(msg.sender, resolver); + } + + /** + * @inheritdoc IVaultDelegation + */ + function optOutNetwork(address resolver) external { + if (!isNetworkOptedIn(msg.sender, resolver)) { + revert NetworkNotOptedIn(); + } + + _updateLimit(_networkLimit[msg.sender][resolver], nextNetworkLimit[msg.sender][resolver]); + + _isNetworkOptedIn[msg.sender][resolver] = false; + + nextNetworkLimit[msg.sender][resolver].amount = 0; + nextNetworkLimit[msg.sender][resolver].timestamp = currentEpochStart() + 2 * epochDuration; + + maxNetworkLimit[msg.sender][resolver] = 0; + + emit OptOutNetwork(msg.sender, resolver); + } + + /** + * @inheritdoc IVaultDelegation + */ + function optInOperator() external { + if (!IRegistry(OPERATOR_REGISTRY).isEntity(msg.sender)) { + revert NotOperator(); + } + + if (isOperatorOptedIn(msg.sender)) { + revert OperatorAlreadyOptedIn(); + } + + if (!_isOperatorOptedIn[msg.sender]) { + _isOperatorOptedIn[msg.sender] = true; + } else { + operatorOptOutAt[msg.sender] = 0; + } + + emit OptInOperator(msg.sender); + } + + /** + * @inheritdoc IVaultDelegation + */ + function optOutOperator() external { + if (!isOperatorOptedIn(msg.sender)) { + revert OperatorNotOptedIn(); + } + + operatorOptOutAt[msg.sender] = currentEpochStart() + 2 * epochDuration; + + emit OptOutOperator(msg.sender); + } + + /** + * @inheritdoc IVaultDelegation + */ + function setMetadataURL(string calldata metadataURL_) external onlyOwner { + if (metadataURL.equal(metadataURL_)) { + revert AlreadySet(); + } + + metadataURL = metadataURL_; + + emit SetMetadataURL(metadataURL_); + } + + /** + * @inheritdoc IVaultDelegation + */ + function setAdminFee(uint256 adminFee_) external onlyRole(ADMIN_FEE_SET_ROLE) { + if (adminFee == adminFee_) { + revert AlreadySet(); + } + + if (adminFee_ > ADMIN_FEE_BASE) { + revert InvalidAdminFee(); + } + + adminFee = adminFee_; + + emit SetAdminFee(adminFee_); + } + + /** + * @inheritdoc IVaultDelegation + */ + function setDepositWhitelist(bool status) external onlyRole(DEPOSIT_WHITELIST_SET_ROLE) { + if (depositWhitelist == status) { + revert AlreadySet(); + } + + depositWhitelist = status; + + emit SetDepositWhitelist(status); + } + + /** + * @inheritdoc IVaultDelegation + */ + function setDepositorWhitelistStatus(address account, bool status) external onlyRole(DEPOSITOR_WHITELIST_ROLE) { + if (status && !depositWhitelist) { + revert NoDepositWhitelist(); + } + + if (isDepositorWhitelisted[account] == status) { + revert AlreadySet(); + } + + isDepositorWhitelisted[account] = status; + + emit SetDepositorWhitelistStatus(account, status); + } + + /** + * @inheritdoc IVaultDelegation + */ + 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 IVaultDelegation + */ + 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); + } + + 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); + + if (amount < limit.amount) { + nextLimit.amount = amount; + nextLimit.timestamp = currentEpochStart() + 2 * epochDuration; + } else { + limit.amount = amount; + nextLimit.amount = 0; + nextLimit.timestamp = 0; + } + } + + function _updateLimit(Limit storage limit, DelayedLimit storage nextLimit) internal { + if (nextLimit.timestamp != 0 && nextLimit.timestamp <= clock()) { + limit.amount = nextLimit.amount; + nextLimit.timestamp = 0; + nextLimit.amount = 0; + } + } +} diff --git a/src/interfaces/IVault.sol b/src/interfaces/IVault.sol index cdbcf876..8f7bca13 100644 --- a/src/interfaces/IVault.sol +++ b/src/interfaces/IVault.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import {IVaultStorage} from "./IVaultStorage.sol"; +import {IVaultDelegation} from "./IVaultDelegation.sol"; -interface IVault is IVaultStorage { - error NotNetwork(); +interface IVault is IVaultDelegation { error NotNetworkMiddleware(); - error NotOperator(); error NotWhitelistedDepositor(); error InsufficientDeposit(); error InsufficientWithdrawal(); @@ -15,28 +13,19 @@ interface IVault is IVaultStorage { error InsufficientClaim(); error InsufficientSlash(); error OperatorNotOptedInNetwork(); - error OperatorNotOptedInVault(); error SlashRequestNotExist(); error VetoPeriodNotEnded(); error SlashPeriodEnded(); error SlashCompleted(); error NotResolver(); error VetoPeriodEnded(); - error NetworkAlreadyOptedIn(); - error InvalidMaxNetworkLimit(); - error NetworkNotOptedIn(); - error OperatorAlreadyOptedIn(); - error ExceedsMaxNetworkLimit(); - error OperatorNotOptedIn(); error NoRewardsToClaim(); error InvalidHintsLength(); error InsufficientReward(); error InvalidRewardTimestamp(); error UnacceptedAdminFee(); error NoDeposits(); - error AlreadySet(); error InsufficientAdminFee(); - error NoDepositWhitelist(); /** * @notice Emitted when a deposit is made. @@ -100,32 +89,6 @@ interface IVault is IVaultStorage { */ event VetoSlash(uint256 indexed slashIndex); - /** - * @notice Emitted when a network opts in. - * @param network network which opted in - * @param resolver resolver who can veto the the network's slash requests - */ - event OptInNetwork(address indexed network, address indexed resolver); - - /** - * @notice Emitted when a network opts out. - * @param network network which opted out - * @param resolver resolver who could veto the the network's slash requests - */ - event OptOutNetwork(address indexed network, address indexed resolver); - - /** - * @notice Emitted when an operator opts in. - * @param operator operator who opted in - */ - event OptInOperator(address indexed operator); - - /** - * @notice Emitted when an operator opts out. - * @param operator operator who opted out - */ - event OptOutOperator(address indexed operator); - /** * @notice Emitted when a reward is distributed. * @param token address of the token to be distributed @@ -154,18 +117,6 @@ interface IVault is IVaultStorage { uint256 claimedAmount ); - /** - * @notice Emitted when a metadata URL is set. - * @param metadataURL metadata URL of the vault - */ - event SetMetadataURL(string metadataURL); - - /** - * @notice Emitted when an admin fee is set. - * @param adminFee admin fee - */ - event SetAdminFee(uint256 adminFee); - /** * @notice Emitted when an admin fee is claimed. * @param recipient account that received the fee @@ -173,35 +124,6 @@ interface IVault is IVaultStorage { */ event ClaimAdminFee(address indexed recipient, uint256 amount); - /** - * @notice Emitted when deposit whitelist status is set. - * @param depositWhitelist enable/disable deposit whitelist - */ - event SetDepositWhitelist(bool depositWhitelist); - - /** - * @notice Emitted when a depositor whitelist status is set. - * @param account account for which the whitelist status is set - * @param value whitelist status - */ - event SetDepositorWhitelistStatus(address indexed account, bool value); - - /** - * @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 - */ - event SetOperatorLimit(address indexed operator, address indexed network, uint256 amount); - /** * @notice Get a total amount of the collateral deposited in the vault. * @return total amount of the collateral deposited @@ -240,34 +162,6 @@ interface IVault is IVaultStorage { */ function maxSlash(address network, address resolver, address operator) external view returns (uint256); - /** - * @notice Get if a given network-resolver pair is opted in. - * @return if the network-resolver pair is opted in - */ - function isNetworkOptedIn(address network, address resolver) external view returns (bool); - - /** - * @notice Get if a given operator is opted in. - * @return if the operator is opted in - */ - function isOperatorOptedIn(address operator) external view returns (bool); - - /** - * @notice Get a network limit for a particular network and resolver. - * @param network address of the network - * @param resolver address of the resolver - * @return network limit - */ - function networkLimit(address network, address resolver) external view returns (uint256); - - /** - * @notice Get an operator limit for a particular operator and network. - * @param operator address of the operator - * @param network address of the network - * @return operator limit - */ - function operatorLimit(address operator, address network) external view returns (uint256); - /** * @notice Deposit collateral into the vault. * @param onBehalfOf account that the deposit is made on behalf of @@ -322,33 +216,6 @@ interface IVault is IVaultStorage { */ function vetoSlash(uint256 slashIndex) external; - /** - * @notice Opt in a network with a given resolver. - * @param resolver address of the resolver - * @param maxNetworkLimit maximum network limit - * @dev Only network can call this function. - */ - function optInNetwork(address resolver, uint256 maxNetworkLimit) external; - - /** - * @notice Opt out a network with a given resolver. - * @param resolver address of the resolver - * @dev Only network can call this function. - */ - function optOutNetwork(address resolver) external; - - /** - * @notice Opt in an operator. - * @dev Only operator can call this function. - */ - function optInOperator() external; - - /** - * @notice Opt out an operator. - * @dev Only operator can call this function. - */ - function optOutOperator() external; - /** * @notice Distribute rewards on behalf of a particular network using a given token. * @param network address of the network @@ -380,58 +247,10 @@ interface IVault is IVaultStorage { uint32[] calldata activeSharesOfHints ) external; - /** - * @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 Set an admin fee. - * @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively) - * @dev Only ADMIN_FEE_SET_ROLE holder can call this function. - */ - function setAdminFee(uint256 adminFee) external; - /** * @notice Claim admin fee. * @param recipient account that receives the fee * @dev Only owner can call this function. */ function claimAdminFee(address recipient, address token) external; - - /** - * @notice Enable/disable deposit whitelist. - * @param status enable/disable deposit whitelist - * @dev Only DEPOSIT_WHITELIST_SET_ROLE holder can call this function. - */ - function setDepositWhitelist(bool status) external; - - /** - * @notice Set a depositor whitelist status. - * @param account account for which the whitelist status is set - * @param status whitelist status - * @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/IVaultDelegation.sol b/src/interfaces/IVaultDelegation.sol new file mode 100644 index 00000000..4df571d5 --- /dev/null +++ b/src/interfaces/IVaultDelegation.sol @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import {IVaultStorage} from "./IVaultStorage.sol"; + +interface IVaultDelegation is IVaultStorage { + error NotNetwork(); + error NotOperator(); + error NetworkAlreadyOptedIn(); + error InvalidMaxNetworkLimit(); + error NetworkNotOptedIn(); + error OperatorAlreadyOptedIn(); + error ExceedsMaxNetworkLimit(); + error OperatorNotOptedIn(); + error AlreadySet(); + error NoDepositWhitelist(); + + /** + * @notice Emitted when a network opts in. + * @param network network which opted in + * @param resolver resolver who can veto the the network's slash requests + */ + event OptInNetwork(address indexed network, address indexed resolver); + + /** + * @notice Emitted when a network opts out. + * @param network network which opted out + * @param resolver resolver who could veto the the network's slash requests + */ + event OptOutNetwork(address indexed network, address indexed resolver); + + /** + * @notice Emitted when an operator opts in. + * @param operator operator who opted in + */ + event OptInOperator(address indexed operator); + + /** + * @notice Emitted when an operator opts out. + * @param operator operator who opted out + */ + event OptOutOperator(address indexed operator); + + /** + * @notice Emitted when a metadata URL is set. + * @param metadataURL metadata URL of the vault + */ + event SetMetadataURL(string metadataURL); + + /** + * @notice Emitted when an admin fee is set. + * @param adminFee admin fee + */ + event SetAdminFee(uint256 adminFee); + + /** + * @notice Emitted when deposit whitelist status is set. + * @param depositWhitelist enable/disable deposit whitelist + */ + event SetDepositWhitelist(bool depositWhitelist); + + /** + * @notice Emitted when a depositor whitelist status is set. + * @param account account for which the whitelist status is set + * @param value whitelist status + */ + event SetDepositorWhitelistStatus(address indexed account, bool value); + + /** + * @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 + */ + event SetOperatorLimit(address indexed operator, address indexed network, uint256 amount); + + /** + * @notice Get if a given network-resolver pair is opted in. + * @return if the network-resolver pair is opted in + */ + function isNetworkOptedIn(address network, address resolver) external view returns (bool); + + /** + * @notice Get if a given operator is opted in. + * @return if the operator is opted in + */ + function isOperatorOptedIn(address operator) external view returns (bool); + + /** + * @notice Get a network limit for a particular network and resolver. + * @param network address of the network + * @param resolver address of the resolver + * @return network limit + */ + function networkLimit(address network, address resolver) external view returns (uint256); + + /** + * @notice Get an operator limit for a particular operator and network. + * @param operator address of the operator + * @param network address of the network + * @return operator limit + */ + function operatorLimit(address operator, address network) external view returns (uint256); + + /** + * @notice Opt in a network with a given resolver. + * @param resolver address of the resolver + * @param maxNetworkLimit maximum network limit + * @dev Only network can call this function. + */ + function optInNetwork(address resolver, uint256 maxNetworkLimit) external; + + /** + * @notice Opt out a network with a given resolver. + * @param resolver address of the resolver + * @dev Only network can call this function. + */ + function optOutNetwork(address resolver) external; + + /** + * @notice Opt in an operator. + * @dev Only operator can call this function. + */ + function optInOperator() external; + + /** + * @notice Opt out an operator. + * @dev Only operator can call this function. + */ + function optOutOperator() external; + + /** + * @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 Set an admin fee. + * @param adminFee admin fee (up to ADMIN_FEE_BASE inclusively) + * @dev Only ADMIN_FEE_SET_ROLE holder can call this function. + */ + function setAdminFee(uint256 adminFee) external; + + /** + * @notice Enable/disable deposit whitelist. + * @param status enable/disable deposit whitelist + * @dev Only DEPOSIT_WHITELIST_SET_ROLE holder can call this function. + */ + function setDepositWhitelist(bool status) external; + + /** + * @notice Set a depositor whitelist status. + * @param account account for which the whitelist status is set + * @param status whitelist status + * @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/test/Vault.t.sol b/test/Vault.t.sol index 4ca52c42..6f275f53 100644 --- a/test/Vault.t.sol +++ b/test/Vault.t.sol @@ -12,6 +12,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 {IVaultDelegation} from "src/interfaces/IVaultDelegation.sol"; import {Token} from "./mocks/Token.sol"; import {FeeOnTransferToken} from "test/mocks/FeeOnTransferToken.sol"; @@ -192,7 +193,7 @@ contract VaultTest is Test { ); vm.startPrank(alice); - vm.expectRevert(IVault.AlreadySet.selector); + vm.expectRevert(IVaultDelegation.AlreadySet.selector); vault.setMetadataURL(metadataURL); vm.stopPrank(); } @@ -794,7 +795,7 @@ contract VaultTest is Test { blockTimestamp = blockTimestamp + 1; vm.warp(blockTimestamp); - vm.expectRevert(IVault.NetworkNotOptedIn.selector); + vm.expectRevert(IVaultDelegation.NetworkNotOptedIn.selector); _requestSlash(bob, network, resolver, operator, toSlash); } @@ -896,7 +897,7 @@ contract VaultTest is Test { blockTimestamp = vault.currentEpochStart() + 2 * vault.epochDuration(); vm.warp(blockTimestamp); - vm.expectRevert(IVault.OperatorNotOptedInVault.selector); + vm.expectRevert(IVaultDelegation.OperatorNotOptedIn.selector); _requestSlash(bob, network, resolver, operator, toSlash); } @@ -1285,7 +1286,7 @@ contract VaultTest is Test { blockTimestamp = blockTimestamp + 1; vm.warp(blockTimestamp); - vm.expectRevert(IVault.OperatorNotOptedInVault.selector); + vm.expectRevert(IVaultDelegation.OperatorNotOptedIn.selector); _executeSlash(address(1), slashIndex); } @@ -1753,7 +1754,7 @@ contract VaultTest is Test { _registerNetwork(network, bob); address resolver = address(1); - vm.expectRevert(IVault.InvalidMaxNetworkLimit.selector); + vm.expectRevert(IVaultDelegation.InvalidMaxNetworkLimit.selector); _optInNetwork(network, resolver, 0); } @@ -1770,7 +1771,7 @@ contract VaultTest is Test { address resolver = address(1); _optInNetwork(network, resolver, type(uint256).max); - vm.expectRevert(IVault.NetworkAlreadyOptedIn.selector); + vm.expectRevert(IVaultDelegation.NetworkAlreadyOptedIn.selector); _optInNetwork(network, resolver, type(uint256).max); } @@ -1782,7 +1783,7 @@ contract VaultTest is Test { vault = _getVault(metadataURL, epochDuration, vetoDuration, slashDuration); address resolver = address(1); - vm.expectRevert(IVault.NotNetwork.selector); + vm.expectRevert(IVaultDelegation.NotNetwork.selector); _optInNetwork(address(0), resolver, type(uint256).max); } @@ -1797,7 +1798,7 @@ contract VaultTest is Test { _registerNetwork(network, bob); address resolver = address(1); - vm.expectRevert(IVault.NetworkNotOptedIn.selector); + vm.expectRevert(IVaultDelegation.NetworkNotOptedIn.selector); _optOutNetwork(network, resolver); } @@ -1858,7 +1859,7 @@ contract VaultTest is Test { _optInOperator(operator); - vm.expectRevert(IVault.OperatorAlreadyOptedIn.selector); + vm.expectRevert(IVaultDelegation.OperatorAlreadyOptedIn.selector); _optInOperator(operator); } @@ -1869,7 +1870,7 @@ contract VaultTest is Test { uint48 slashDuration = 1; vault = _getVault(metadataURL, epochDuration, vetoDuration, slashDuration); - vm.expectRevert(IVault.NotOperator.selector); + vm.expectRevert(IVaultDelegation.NotOperator.selector); _optInOperator(address(0)); } @@ -1883,7 +1884,7 @@ contract VaultTest is Test { address operator = bob; _registerOperator(operator); - vm.expectRevert(IVault.OperatorNotOptedIn.selector); + vm.expectRevert(IVaultDelegation.OperatorNotOptedIn.selector); _optOutOperator(operator); } @@ -2021,7 +2022,7 @@ contract VaultTest is Test { _registerNetwork(network, bob); address resolver = address(1); - vm.expectRevert(IVault.NetworkNotOptedIn.selector); + vm.expectRevert(IVaultDelegation.NetworkNotOptedIn.selector); _setNetworkLimit(alice, network, resolver, amount1); } @@ -2045,7 +2046,7 @@ contract VaultTest is Test { address resolver = address(1); _optInNetwork(network, resolver, maxNetworkLimit); - vm.expectRevert(IVault.ExceedsMaxNetworkLimit.selector); + vm.expectRevert(IVaultDelegation.ExceedsMaxNetworkLimit.selector); _setNetworkLimit(alice, network, resolver, amount1); } @@ -2169,7 +2170,7 @@ contract VaultTest is Test { address network = address(1); if (amount1 > 0) { - vm.expectRevert(IVault.OperatorNotOptedIn.selector); + vm.expectRevert(IVaultDelegation.OperatorNotOptedIn.selector); } _setOperatorLimit(alice, operator, network, amount1); } @@ -2716,7 +2717,7 @@ contract VaultTest is Test { _grantAdminFeeSetRole(alice, alice); _setAdminFee(alice, adminFee); - vm.expectRevert(IVault.AlreadySet.selector); + vm.expectRevert(IVaultDelegation.AlreadySet.selector); _setAdminFee(alice, adminFee); } @@ -2780,7 +2781,7 @@ contract VaultTest is Test { _grantDepositWhitelistSetRole(alice, alice); _setDepositWhitelist(alice, true); - vm.expectRevert(IVault.AlreadySet.selector); + vm.expectRevert(IVaultDelegation.AlreadySet.selector); _setDepositWhitelist(alice, true); } @@ -2817,7 +2818,7 @@ contract VaultTest is Test { _grantDepositorWhitelistRole(alice, alice); - vm.expectRevert(IVault.NoDepositWhitelist.selector); + vm.expectRevert(IVaultDelegation.NoDepositWhitelist.selector); _setDepositorWhitelistStatus(alice, bob, true); } @@ -2836,7 +2837,7 @@ contract VaultTest is Test { _setDepositorWhitelistStatus(alice, bob, true); - vm.expectRevert(IVault.AlreadySet.selector); + vm.expectRevert(IVaultDelegation.AlreadySet.selector); _setDepositorWhitelistStatus(alice, bob, true); }