diff --git a/foundry.toml b/foundry.toml index 810eba9d..a5a11718 100644 --- a/foundry.toml +++ b/foundry.toml @@ -20,7 +20,7 @@ quote_style = "double" tab_width = 4 [fuzz] -runs = 2048 +runs = 4096 max_test_rejects = 262144 [etherscan] diff --git a/src/contracts/common/MigratableEntity.sol b/src/contracts/common/MigratableEntity.sol index e282d6e8..7a99a752 100644 --- a/src/contracts/common/MigratableEntity.sol +++ b/src/contracts/common/MigratableEntity.sol @@ -16,7 +16,7 @@ abstract contract MigratableEntity is Initializable, OwnableUpgradeable, IMigrat address private immutable SELF; modifier uninitialized() { - if (_getInitializedVersion() != 0) { + if (_getInitializedVersion() > 0) { revert AlreadyInitialized(); } diff --git a/src/contracts/delegator/BaseDelegator.sol b/src/contracts/delegator/BaseDelegator.sol index ba761882..b6a89b02 100644 --- a/src/contracts/delegator/BaseDelegator.sol +++ b/src/contracts/delegator/BaseDelegator.sol @@ -105,7 +105,7 @@ contract BaseDelegator is Entity, AccessControlUpgradeable, IBaseDelegator { return 0; } - minOperatorNetworkStakeDuring_ = operatorNetworkStake(network, operator); + minOperatorNetworkStakeDuring_ = Math.min(IVault(vault).activeSupply(), operatorNetworkStake(network, operator)); uint48 epochDuration = IVault(vault).epochDuration(); uint48 nextEpochStart = IVault(vault).currentEpochStart() + epochDuration; @@ -148,6 +148,10 @@ contract BaseDelegator is Entity, AccessControlUpgradeable, IBaseDelegator { revert NotSlasher(); } + if (slashedAmount > operatorNetworkStake(network, operator)) { + revert TooMuchSlash(); + } + _onSlash(network, operator, slashedAmount); emit OnSlash(network, operator, slashedAmount); diff --git a/src/contracts/delegator/FullRestakeDelegator.sol b/src/contracts/delegator/FullRestakeDelegator.sol index a230e6a5..6c0d79e2 100644 --- a/src/contracts/delegator/FullRestakeDelegator.sol +++ b/src/contracts/delegator/FullRestakeDelegator.sol @@ -203,13 +203,17 @@ contract FullRestakeDelegator is BaseDelegator, IFullRestakeDelegator { function _onSlash(address network, address operator, uint256 slashedAmount) internal override { uint256 networkLimit_ = networkLimit(network); if (networkLimit_ != type(uint256).max) { - _networkLimit[network].push(Time.timestamp(), networkLimit_ - slashedAmount); + _insertCheckpoint(_networkLimit[network], Time.timestamp(), networkLimit_ - slashedAmount); } - _totalOperatorNetworkLimit[network].push(Time.timestamp(), totalOperatorNetworkLimit(network) - slashedAmount); + _insertCheckpoint( + _totalOperatorNetworkLimit[network], Time.timestamp(), totalOperatorNetworkLimit(network) - slashedAmount + ); - _operatorNetworkLimit[network][operator].push( - Time.timestamp(), operatorNetworkLimit(network, operator) - slashedAmount + _insertCheckpoint( + _operatorNetworkLimit[network][operator], + Time.timestamp(), + operatorNetworkLimit(network, operator) - slashedAmount ); } diff --git a/src/contracts/delegator/NetworkRestakeDelegator.sol b/src/contracts/delegator/NetworkRestakeDelegator.sol index 9871f042..365c54fd 100644 --- a/src/contracts/delegator/NetworkRestakeDelegator.sol +++ b/src/contracts/delegator/NetworkRestakeDelegator.sol @@ -206,19 +206,23 @@ contract NetworkRestakeDelegator is BaseDelegator, INetworkRestakeDelegator { function _onSlash(address network, address operator, uint256 slashedAmount) internal override { uint256 networkLimit_ = networkLimit(network); - if (networkLimit_ != type(uint256).max) { - _networkLimit[network].push(Time.timestamp(), networkLimit_ - slashedAmount); - } - uint256 operatorNetworkShares_ = operatorNetworkShares(network, operator); uint256 operatorSlashedShares = slashedAmount.mulDiv(operatorNetworkShares_, operatorNetworkStake(network, operator), Math.Rounding.Ceil); - _totalOperatorNetworkShares[network].push( - Time.timestamp(), totalOperatorNetworkShares(network) - operatorSlashedShares + if (networkLimit_ != type(uint256).max) { + _insertCheckpoint(_networkLimit[network], Time.timestamp(), networkLimit_ - slashedAmount); + } + + _insertCheckpoint( + _totalOperatorNetworkShares[network], + Time.timestamp(), + totalOperatorNetworkShares(network) - operatorSlashedShares ); - _operatorNetworkShares[network][operator].push(Time.timestamp(), operatorNetworkShares_ - operatorSlashedShares); + _insertCheckpoint( + _operatorNetworkShares[network][operator], Time.timestamp(), operatorNetworkShares_ - operatorSlashedShares + ); } function _initializeInternal( diff --git a/src/contracts/libraries/Checkpoints.sol b/src/contracts/libraries/Checkpoints.sol index 162c21b0..0b49efb1 100644 --- a/src/contracts/libraries/Checkpoints.sol +++ b/src/contracts/libraries/Checkpoints.sol @@ -151,7 +151,7 @@ library Checkpoints { */ function upperLookupRecent(Trace256 storage self, uint48 key) internal view returns (uint256) { uint208 idx = self._trace.upperLookupRecent(key); - return idx != 0 ? self._values[idx] : 0; + return idx > 0 ? self._values[idx] : 0; } /** @@ -207,7 +207,7 @@ library Checkpoints { */ function latest(Trace256 storage self) internal view returns (uint256) { uint208 idx = self._trace.latest(); - return idx != 0 ? self._values[idx] : 0; + return idx > 0 ? self._values[idx] : 0; } function latestCheckpoint(Trace256 storage self) internal view returns (bool exists, uint48 _key, uint256 _value) { diff --git a/src/contracts/slasher/BaseSlasher.sol b/src/contracts/slasher/BaseSlasher.sol index f552df1f..64a0166b 100644 --- a/src/contracts/slasher/BaseSlasher.sol +++ b/src/contracts/slasher/BaseSlasher.sol @@ -68,9 +68,8 @@ abstract contract BaseSlasher is Entity, IBaseSlasher { function _checkOptIns(address network, address operator) internal view { address vault_ = vault; - uint48 timestamp = IVault(vault_).currentEpoch() != 0 - ? IVault(vault_).previousEpochStart() - : IVault(vault_).currentEpochStart(); + uint48 timestamp = + IVault(vault_).currentEpoch() > 0 ? IVault(vault_).previousEpochStart() : IVault(vault_).currentEpochStart(); if (!IOptInService(NETWORK_VAULT_OPT_IN_SERVICE).wasOptedInAfter(network, vault_, timestamp)) { revert NetworkNotOptedInVault(); diff --git a/src/contracts/slasher/Slasher.sol b/src/contracts/slasher/Slasher.sol index fbfbb27b..ec9c5b88 100644 --- a/src/contracts/slasher/Slasher.sol +++ b/src/contracts/slasher/Slasher.sol @@ -36,11 +36,14 @@ contract Slasher is BaseSlasher, ISlasher { address operator, uint256 amount ) external onlyNetworkMiddleware(network) returns (uint256) { - if (amount == 0) { + uint256 operateNetworkStake = IBaseDelegator(IVault(vault).delegator()).operatorNetworkStake(network, operator); + if (amount == 0 || operateNetworkStake == 0) { revert InsufficientSlash(); } - amount = Math.min(amount, IBaseDelegator(IVault(vault).delegator()).operatorNetworkStake(network, operator)); + if (amount > operateNetworkStake) { + amount = operateNetworkStake; + } _checkOptIns(network, operator); diff --git a/src/contracts/slasher/VetoSlasher.sol b/src/contracts/slasher/VetoSlasher.sol index aecd3a7f..eba1cdae 100644 --- a/src/contracts/slasher/VetoSlasher.sol +++ b/src/contracts/slasher/VetoSlasher.sol @@ -50,6 +50,11 @@ contract VetoSlasher is BaseSlasher, AccessControlUpgradeable, IVetoSlasher { */ uint256 public resolverSetEpochsDelay; + /** + * @inheritdoc IVetoSlasher + */ + mapping(address resolver => mapping(uint256 slashIndex => bool value)) public hasVetoed; + mapping(address network => mapping(address resolver => Checkpoints.Trace256 shares)) private _resolverShares; constructor( @@ -158,7 +163,7 @@ contract VetoSlasher is BaseSlasher, AccessControlUpgradeable, IVetoSlasher { slashedAmount -= slashedAmount.mulDiv(request.vetoedShares, SHARES_BASE, Math.Rounding.Ceil); - if (slashedAmount != 0) { + if (slashedAmount > 0) { _callOnSlash(request.network, request.operator, slashedAmount); } @@ -189,6 +194,12 @@ contract VetoSlasher is BaseSlasher, AccessControlUpgradeable, IVetoSlasher { revert SlashRequestCompleted(); } + if (hasVetoed[msg.sender][slashIndex]) { + revert AlreadyVetoed(); + } + + hasVetoed[msg.sender][slashIndex] = true; + uint256 vetoedShares_ = Math.min(request.vetoedShares + resolverShares_, SHARES_BASE); request.vetoedShares = vetoedShares_; @@ -231,7 +242,7 @@ contract VetoSlasher is BaseSlasher, AccessControlUpgradeable, IVetoSlasher { } uint48 epochDuration = IVault(vault_).epochDuration(); - if (epochDuration != 0 && params.vetoDuration + params.executeDuration > epochDuration) { + if (epochDuration > 0 && params.vetoDuration + params.executeDuration > epochDuration) { revert InvalidSlashDuration(); } diff --git a/src/contracts/vault/Vault.sol b/src/contracts/vault/Vault.sol index 6b8a6d31..d596fee8 100644 --- a/src/contracts/vault/Vault.sol +++ b/src/contracts/vault/Vault.sol @@ -201,36 +201,33 @@ contract Vault is VaultStorage, MigratableEntity, AccessControlUpgradeable, IVau revert NotSlasher(); } - if (slashedAmount == 0) { - revert InsufficientSlash(); - } + if (slashedAmount > 0) { + uint256 totalSupply_ = totalSupply(); + if (slashedAmount > totalSupply_) { + revert TooMuchSlash(); + } - uint256 epoch = currentEpoch(); - uint256 totalSupply_ = totalSupply(); + uint256 epoch = currentEpoch(); + uint256 activeSupply_ = activeSupply(); + uint256 withdrawals_ = withdrawals[epoch]; + uint256 nextWithdrawals = withdrawals[epoch + 1]; - if (slashedAmount > totalSupply_) { - revert TooMuchSlash(); - } + uint256 nextWithdrawalsSlashed = slashedAmount.mulDiv(nextWithdrawals, totalSupply_); + uint256 withdrawalsSlashed = slashedAmount.mulDiv(withdrawals_, totalSupply_); + uint256 activeSlashed = slashedAmount - nextWithdrawalsSlashed - withdrawalsSlashed; - uint256 activeSupply_ = activeSupply(); - uint256 withdrawals_ = withdrawals[epoch]; - uint256 nextWithdrawals = withdrawals[epoch + 1]; + if (activeSupply_ < activeSlashed) { + withdrawalsSlashed += activeSlashed - activeSupply_; + activeSlashed = activeSupply_; + } - uint256 nextWithdrawalsSlashed = slashedAmount.mulDiv(nextWithdrawals, totalSupply_); - uint256 withdrawalsSlashed = slashedAmount.mulDiv(withdrawals_, totalSupply_); - uint256 activeSlashed = slashedAmount - nextWithdrawalsSlashed - withdrawalsSlashed; + _activeSupplies.push(Time.timestamp(), activeSupply_ - activeSlashed); + withdrawals[epoch] = withdrawals_ - withdrawalsSlashed; + withdrawals[epoch + 1] = nextWithdrawals - nextWithdrawalsSlashed; - if (activeSupply_ < activeSlashed) { - withdrawalsSlashed += activeSlashed - activeSupply_; - activeSlashed = activeSupply_; + ICollateral(collateral).issueDebt(burner, slashedAmount); } - _activeSupplies.push(Time.timestamp(), activeSupply_ - activeSlashed); - withdrawals[epoch] = withdrawals_ - withdrawalsSlashed; - withdrawals[epoch + 1] = nextWithdrawals - nextWithdrawalsSlashed; - - ICollateral(collateral).issueDebt(burner, slashedAmount); - emit OnSlash(msg.sender, slashedAmount); } @@ -242,7 +239,7 @@ contract Vault is VaultStorage, MigratableEntity, AccessControlUpgradeable, IVau revert NotSlasher(); } - if (_nextSlasher.timestamp != 0 && _nextSlasher.timestamp <= Time.timestamp()) { + if (_nextSlasher.timestamp > 0 && _nextSlasher.timestamp <= Time.timestamp()) { _slasher.address_ = _nextSlasher.address_; _nextSlasher.timestamp = 0; _nextSlasher.address_ = address(0); diff --git a/src/interfaces/delegator/IBaseDelegator.sol b/src/interfaces/delegator/IBaseDelegator.sol index a19ae5b4..cf5a6204 100644 --- a/src/interfaces/delegator/IBaseDelegator.sol +++ b/src/interfaces/delegator/IBaseDelegator.sol @@ -5,6 +5,7 @@ interface IBaseDelegator { error NotSlasher(); error NotNetwork(); error NotVault(); + error TooMuchSlash(); /** * @notice Base parameters needed for delegators' deployment. diff --git a/src/interfaces/slasher/IVetoSlasher.sol b/src/interfaces/slasher/IVetoSlasher.sol index 70e4d402..fc0131a9 100644 --- a/src/interfaces/slasher/IVetoSlasher.sol +++ b/src/interfaces/slasher/IVetoSlasher.sol @@ -17,6 +17,7 @@ interface IVetoSlasher { error InvalidResolversLength(); error InvalidResolverSetEpochsDelay(); error ResolverAlreadySet(); + error AlreadyVetoed(); /** * @notice Initial parameters needed for a slasher deployment. @@ -167,6 +168,14 @@ interface IVetoSlasher { */ function resolverShares(address network, address resolver) external view returns (uint256); + /** + * @notice Get if a resolver has vetoed a particular slash request. + * @param resolver address of the resolver + * @param slashIndex index of the slash request + * @return if the resolver has vetoed the slash request + */ + function hasVetoed(address resolver, uint256 slashIndex) external view returns (bool); + /** * @notice Request a slash using a network and a resolver for a particular operator by a given amount. * @param network address of the network diff --git a/src/interfaces/vault/IVault.sol b/src/interfaces/vault/IVault.sol index e55095d1..86c99821 100644 --- a/src/interfaces/vault/IVault.sol +++ b/src/interfaces/vault/IVault.sol @@ -22,7 +22,6 @@ interface IVault is IVaultStorage { error TooMuchWithdraw(); error InvalidSlasherSetEpochsDelay(); error NotDelegator(); - error InsufficientSlash(); error TooMuchSlash(); /** diff --git a/test/delegator/FullRestakeDelegator.t.sol b/test/delegator/FullRestakeDelegator.t.sol index 0c587aa4..7affb101 100644 --- a/test/delegator/FullRestakeDelegator.t.sol +++ b/test/delegator/FullRestakeDelegator.t.sol @@ -724,6 +724,127 @@ contract FullRestakeDelegatorTest is Test { ); } + function test_Slash( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 operatorNetworkLimit2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + operatorNetworkLimit2 = bound(operatorNetworkLimit2, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq(delegator.networkLimit(network), networkLimit); + assertEq( + delegator.totalOperatorNetworkLimitIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 + operatorNetworkLimit2 - 2 + ); + assertEq(delegator.totalOperatorNetworkLimit(network), operatorNetworkLimit1 + operatorNetworkLimit2); + assertEq( + delegator.operatorNetworkLimitIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, alice), operatorNetworkLimit1); + assertEq( + delegator.operatorNetworkLimitIn(network, bob, uint48(2 * vault.epochDuration())), operatorNetworkLimit2 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, bob), operatorNetworkLimit2); + + uint256 slashAmount1Real = + Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + assertEq(_slash(alice, network, alice, slashAmount1), slashAmount1Real); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq( + delegator.networkLimit(network), + networkLimit == type(uint256).max ? networkLimit : networkLimit - slashAmount1Real + ); + assertEq( + delegator.totalOperatorNetworkLimitIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 + operatorNetworkLimit2 - 2 + ); + assertEq( + delegator.totalOperatorNetworkLimit(network), + operatorNetworkLimit1 + operatorNetworkLimit2 - slashAmount1Real + ); + assertEq( + delegator.operatorNetworkLimitIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, alice), operatorNetworkLimit1 - slashAmount1Real); + assertEq( + delegator.operatorNetworkLimitIn(network, bob, uint48(2 * vault.epochDuration())), operatorNetworkLimit2 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, bob), operatorNetworkLimit2); + + uint256 slashAmount2Real = Math.min( + slashAmount2, + Math.min(depositAmount - slashAmount1Real, Math.min(networkLimit - slashAmount1Real, operatorNetworkLimit2)) + ); + assertEq(_slash(alice, network, bob, slashAmount2), slashAmount2Real); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq( + delegator.networkLimit(network), + networkLimit == type(uint256).max ? networkLimit : networkLimit - slashAmount1Real - slashAmount2Real + ); + assertEq( + delegator.totalOperatorNetworkLimitIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 + operatorNetworkLimit2 - 2 + ); + assertEq( + delegator.totalOperatorNetworkLimit(network), + operatorNetworkLimit1 + operatorNetworkLimit2 - slashAmount1Real - slashAmount2Real + ); + assertEq( + delegator.operatorNetworkLimitIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkLimit1 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, alice), operatorNetworkLimit1 - slashAmount1Real); + assertEq( + delegator.operatorNetworkLimitIn(network, bob, uint48(2 * vault.epochDuration())), operatorNetworkLimit2 - 1 + ); + assertEq(delegator.operatorNetworkLimit(network, bob), operatorNetworkLimit2 - slashAmount2Real); + } + function _getVaultAndDelegator(uint48 epochDuration) internal returns (Vault, FullRestakeDelegator) { (address vault_, address delegator_,) = vaultConfigurator.create( IVaultConfigurator.InitParams({ @@ -758,6 +879,43 @@ contract FullRestakeDelegatorTest is Test { return (Vault(vault_), FullRestakeDelegator(delegator_)); } + function _getVaultAndDelegatorAndSlasher(uint48 epochDuration) + internal + returns (Vault, FullRestakeDelegator, Slasher) + { + (address vault_, address delegator_, address slasher_) = vaultConfigurator.create( + IVaultConfigurator.InitParams({ + version: vaultFactory.lastVersion(), + owner: alice, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdEaD), + epochDuration: epochDuration, + slasherSetEpochsDelay: 3, + depositWhitelist: false, + defaultAdminRoleHolder: alice, + slasherSetRoleHolder: alice, + depositorWhitelistRoleHolder: alice + }), + delegatorIndex: 1, + delegatorParams: abi.encode( + IFullRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), + networkLimitSetRoleHolder: alice, + operatorNetworkLimitSetRoleHolder: alice + }) + ), + withSlasher: true, + slasherIndex: 0, + slasherParams: "" + }) + ); + + return (Vault(vault_), FullRestakeDelegator(delegator_), Slasher(slasher_)); + } + function _getSlasher(address vault_) internal returns (Slasher) { return Slasher(slasherFactory.create(0, true, abi.encode(address(vault_), ""))); } @@ -873,9 +1031,14 @@ contract FullRestakeDelegatorTest is Test { vm.stopPrank(); } - function _slash(address user, address network, address operator, uint256 amount) internal { + function _slash( + address user, + address network, + address operator, + uint256 amount + ) internal returns (uint256 slashAmount) { vm.startPrank(user); - slasher.slash(network, operator, amount); + slashAmount = slasher.slash(network, operator, amount); vm.stopPrank(); } diff --git a/test/delegator/NetworkRestakeDelegator.t.sol b/test/delegator/NetworkRestakeDelegator.t.sol index a8661b89..c0ab37f1 100644 --- a/test/delegator/NetworkRestakeDelegator.t.sol +++ b/test/delegator/NetworkRestakeDelegator.t.sol @@ -661,6 +661,163 @@ contract NetworkRestakeDelegatorTest is Test { ); } + function test_Slash( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkShares1, + uint256 operatorNetworkShares2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkShares1 = bound(operatorNetworkShares1, 1, type(uint256).max / 2); + operatorNetworkShares2 = bound(operatorNetworkShares2, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _deposit(alice, depositAmount); + + _setOperatorNetworkShares(alice, network, alice, operatorNetworkShares1); + _setOperatorNetworkShares(alice, network, bob, operatorNetworkShares2); + + blockTimestamp = blockTimestamp + 2 * vault.epochDuration(); + vm.warp(blockTimestamp); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkShares(alice, network, alice, operatorNetworkShares1 - 1); + _setOperatorNetworkShares(alice, network, bob, operatorNetworkShares2 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq(delegator.networkLimit(network), networkLimit); + assertEq( + delegator.totalOperatorNetworkSharesIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 + operatorNetworkShares2 - 2 + ); + assertEq(delegator.totalOperatorNetworkShares(network), operatorNetworkShares1 + operatorNetworkShares2); + assertEq( + delegator.operatorNetworkSharesIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 - 1 + ); + assertEq(delegator.operatorNetworkShares(network, alice), operatorNetworkShares1); + assertEq( + delegator.operatorNetworkSharesIn(network, bob, uint48(2 * vault.epochDuration())), + operatorNetworkShares2 - 1 + ); + assertEq(delegator.operatorNetworkShares(network, bob), operatorNetworkShares2); + + uint256 operatorNetworkStake1 = operatorNetworkShares1.mulDiv( + Math.min(networkLimit, depositAmount), operatorNetworkShares1 + operatorNetworkShares2 + ); + vm.assume(operatorNetworkStake1 > 0); + uint256 slashAmount1Real = Math.min(slashAmount1, operatorNetworkStake1); + assertEq(_slash(alice, network, alice, slashAmount1), slashAmount1Real); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq( + delegator.networkLimit(network), + networkLimit == type(uint256).max ? networkLimit : networkLimit - slashAmount1Real + ); + assertEq( + delegator.totalOperatorNetworkSharesIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 + operatorNetworkShares2 - 2 + ); + assertEq( + delegator.totalOperatorNetworkShares(network), + operatorNetworkShares1 + - slashAmount1Real.mulDiv(operatorNetworkShares1, operatorNetworkStake1, Math.Rounding.Ceil) + + operatorNetworkShares2 + ); + assertEq( + delegator.operatorNetworkSharesIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 - 1 + ); + assertEq( + delegator.operatorNetworkShares(network, alice), + operatorNetworkShares1 + - slashAmount1Real.mulDiv(operatorNetworkShares1, operatorNetworkStake1, Math.Rounding.Ceil) + ); + assertEq( + delegator.operatorNetworkSharesIn(network, bob, uint48(2 * vault.epochDuration())), + operatorNetworkShares2 - 1 + ); + assertEq(delegator.operatorNetworkShares(network, bob), operatorNetworkShares2); + + uint256 operatorNetworkStake2 = operatorNetworkShares2.mulDiv( + Math.min(networkLimit - slashAmount1Real, depositAmount - slashAmount1Real), + operatorNetworkShares1 + - slashAmount1Real.mulDiv(operatorNetworkShares1, operatorNetworkStake1, Math.Rounding.Ceil) + + operatorNetworkShares2 + ); + vm.assume(operatorNetworkStake2 > 0); + uint256 slashAmount2Real = Math.min(slashAmount2, operatorNetworkStake2); + assertEq(_slash(alice, network, bob, slashAmount2), slashAmount2Real); + + assertEq(delegator.networkLimitIn(network, uint48(2 * vault.epochDuration())), networkLimit - 1); + assertEq( + delegator.networkLimit(network), + networkLimit == type(uint256).max ? networkLimit : networkLimit - slashAmount1Real - slashAmount2Real + ); + assertEq( + delegator.totalOperatorNetworkSharesIn(network, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 + operatorNetworkShares2 - 2 + ); + assertEq( + delegator.totalOperatorNetworkShares(network), + operatorNetworkShares1 + - slashAmount1Real.mulDiv(operatorNetworkShares1, operatorNetworkStake1, Math.Rounding.Ceil) + + operatorNetworkShares2 + - slashAmount2Real.mulDiv(operatorNetworkShares2, operatorNetworkStake2, Math.Rounding.Ceil) + ); + assertEq( + delegator.operatorNetworkSharesIn(network, alice, uint48(2 * vault.epochDuration())), + operatorNetworkShares1 - 1 + ); + assertEq( + delegator.operatorNetworkShares(network, alice), + operatorNetworkShares1 + - slashAmount1Real.mulDiv(operatorNetworkShares1, operatorNetworkStake1, Math.Rounding.Ceil) + ); + assertEq( + delegator.operatorNetworkSharesIn(network, bob, uint48(2 * vault.epochDuration())), + operatorNetworkShares2 - 1 + ); + assertEq( + delegator.operatorNetworkShares(network, bob), + operatorNetworkShares2 + - slashAmount2Real.mulDiv(operatorNetworkShares2, operatorNetworkStake2, Math.Rounding.Ceil) + ); + + if (vault.totalSupply() == 0) { + assertEq(delegator.totalOperatorNetworkShares(network), 0); + } + } + function _getVaultAndDelegator(uint48 epochDuration) internal returns (Vault, NetworkRestakeDelegator) { (address vault_, address delegator_,) = vaultConfigurator.create( IVaultConfigurator.InitParams({ @@ -695,6 +852,43 @@ contract NetworkRestakeDelegatorTest is Test { return (Vault(vault_), NetworkRestakeDelegator(delegator_)); } + function _getVaultAndDelegatorAndSlasher(uint48 epochDuration) + internal + returns (Vault, NetworkRestakeDelegator, Slasher) + { + (address vault_, address delegator_, address slasher_) = vaultConfigurator.create( + IVaultConfigurator.InitParams({ + version: vaultFactory.lastVersion(), + owner: alice, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdEaD), + epochDuration: epochDuration, + slasherSetEpochsDelay: 3, + depositWhitelist: false, + defaultAdminRoleHolder: alice, + slasherSetRoleHolder: alice, + depositorWhitelistRoleHolder: alice + }), + delegatorIndex: 0, + delegatorParams: abi.encode( + INetworkRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), + networkLimitSetRoleHolder: alice, + operatorNetworkSharesSetRoleHolder: alice + }) + ), + withSlasher: true, + slasherIndex: 0, + slasherParams: "" + }) + ); + + return (Vault(vault_), NetworkRestakeDelegator(delegator_), Slasher(slasher_)); + } + function _getSlasher(address vault_) internal returns (Slasher) { return Slasher(slasherFactory.create(0, true, abi.encode(address(vault_), ""))); } @@ -810,9 +1004,14 @@ contract NetworkRestakeDelegatorTest is Test { vm.stopPrank(); } - function _slash(address user, address network, address operator, uint256 amount) internal { + function _slash( + address user, + address network, + address operator, + uint256 amount + ) internal returns (uint256 slashAmount) { vm.startPrank(user); - slasher.slash(network, operator, amount); + slashAmount = slasher.slash(network, operator, amount); vm.stopPrank(); } diff --git a/test/slasher/Slasher.t.sol b/test/slasher/Slasher.t.sol index e626603a..1a947d8f 100644 --- a/test/slasher/Slasher.t.sol +++ b/test/slasher/Slasher.t.sol @@ -29,6 +29,8 @@ import {IBaseDelegator} from "src/interfaces/delegator/IBaseDelegator.sol"; import {IVaultStorage} from "src/interfaces/vault/IVaultStorage.sol"; import {IBaseSlasher} from "src/interfaces/slasher/IBaseSlasher.sol"; +import {ISlasher} from "src/interfaces/slasher/ISlasher.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; contract SlasherTest is Test { address owner; @@ -157,6 +159,265 @@ contract SlasherTest is Test { slasherFactory.create(0, true, abi.encode(address(1), "")); } + function test_Slash( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 operatorNetworkLimit2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + operatorNetworkLimit2 = bound(operatorNetworkLimit2, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + assertEq( + Math.min(slashAmount1, delegator.operatorNetworkStake(network, alice)), + _slash(alice, network, alice, slashAmount1) + ); + + assertEq( + Math.min(slashAmount2, delegator.operatorNetworkStake(network, bob)), + _slash(alice, network, bob, slashAmount2) + ); + } + + function test_SlashRevertNotNetworkMiddleware( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + vm.expectRevert(IBaseSlasher.NotNetworkMiddleware.selector); + _slash(bob, network, alice, slashAmount1); + } + + function test_SlashRevertNetworkNotOptedInVault( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + vm.expectRevert(IBaseSlasher.NetworkNotOptedInVault.selector); + _slash(alice, network, alice, slashAmount1); + } + + function test_SlashRevertOperatorNotOptedInVault( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + vm.expectRevert(IBaseSlasher.OperatorNotOptedInVault.selector); + _slash(alice, network, alice, slashAmount1); + } + + function test_SlashRevertOperatorNotOptedInNetwork( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + vm.expectRevert(IBaseSlasher.OperatorNotOptedInNetwork.selector); + _slash(alice, network, alice, slashAmount1); + } + + function test_SlashRevertInsufficientSlash( + uint48 epochDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + bool zeroSlashAmount + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 < networkLimit); + + _optInNetworkVault(network); + + vm.expectRevert(ISlasher.InsufficientSlash.selector); + _slash(alice, network, alice, zeroSlashAmount ? 0 : slashAmount1); + } + function _getVaultAndDelegator(uint48 epochDuration) internal returns (Vault, FullRestakeDelegator) { (address vault_, address delegator_,) = vaultConfigurator.create( IVaultConfigurator.InitParams({ @@ -174,12 +435,12 @@ contract SlasherTest is Test { slasherSetRoleHolder: alice, depositorWhitelistRoleHolder: alice }), - delegatorIndex: 0, + delegatorIndex: 1, delegatorParams: abi.encode( - INetworkRestakeDelegator.InitParams({ + IFullRestakeDelegator.InitParams({ baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), networkLimitSetRoleHolder: alice, - operatorNetworkSharesSetRoleHolder: alice + operatorNetworkLimitSetRoleHolder: alice }) ), withSlasher: false, @@ -191,6 +452,43 @@ contract SlasherTest is Test { return (Vault(vault_), FullRestakeDelegator(delegator_)); } + function _getVaultAndDelegatorAndSlasher(uint48 epochDuration) + internal + returns (Vault, FullRestakeDelegator, Slasher) + { + (address vault_, address delegator_, address slasher_) = vaultConfigurator.create( + IVaultConfigurator.InitParams({ + version: vaultFactory.lastVersion(), + owner: alice, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdEaD), + epochDuration: epochDuration, + slasherSetEpochsDelay: 3, + depositWhitelist: false, + defaultAdminRoleHolder: alice, + slasherSetRoleHolder: alice, + depositorWhitelistRoleHolder: alice + }), + delegatorIndex: 1, + delegatorParams: abi.encode( + IFullRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), + networkLimitSetRoleHolder: alice, + operatorNetworkLimitSetRoleHolder: alice + }) + ), + withSlasher: true, + slasherIndex: 0, + slasherParams: "" + }) + ); + + return (Vault(vault_), FullRestakeDelegator(delegator_), Slasher(slasher_)); + } + function _getSlasher(address vault_) internal returns (Slasher) { return Slasher(slasherFactory.create(0, true, abi.encode(address(vault_), ""))); } @@ -294,9 +592,32 @@ contract SlasherTest is Test { vm.stopPrank(); } - function _slash(address user, address network, address operator, uint256 amount) internal { + function _setNetworkLimit(address user, address network, uint256 amount) internal { + vm.startPrank(user); + delegator.setNetworkLimit(network, amount); + vm.stopPrank(); + } + + function _setOperatorNetworkLimit(address user, address network, address operator, uint256 amount) internal { + vm.startPrank(user); + delegator.setOperatorNetworkLimit(network, operator, amount); + vm.stopPrank(); + } + + function _slash( + address user, + address network, + address operator, + uint256 amount + ) internal returns (uint256 slashAmount) { + vm.startPrank(user); + slashAmount = slasher.slash(network, operator, amount); + vm.stopPrank(); + } + + function _setMaxNetworkLimit(address user, uint256 amount) internal { vm.startPrank(user); - slasher.slash(network, operator, amount); + delegator.setMaxNetworkLimit(amount); vm.stopPrank(); } } diff --git a/test/slasher/VetoSlasher.t.sol b/test/slasher/VetoSlasher.t.sol index 2f5a1ea0..467b3e37 100644 --- a/test/slasher/VetoSlasher.t.sol +++ b/test/slasher/VetoSlasher.t.sol @@ -30,8 +30,11 @@ import {IBaseDelegator} from "src/interfaces/delegator/IBaseDelegator.sol"; import {IVaultStorage} from "src/interfaces/vault/IVaultStorage.sol"; import {IVetoSlasher} from "src/interfaces/slasher/IVetoSlasher.sol"; import {IBaseSlasher} from "src/interfaces/slasher/IBaseSlasher.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; contract VetoSlasherTest is Test { + using Math for uint256; + address owner; address alice; uint256 alicePrivateKey; @@ -160,6 +163,7 @@ contract VetoSlasherTest is Test { assertEq(slasher.resolverSetEpochsDelay(), 3); assertEq(slasher.resolverSharesAt(address(this), address(this), 0), 0); assertEq(slasher.resolverShares(address(this), address(this)), 0); + assertEq(slasher.hasVetoed(alice, 0), false); } function test_CreateRevertNotVault( @@ -285,6 +289,999 @@ contract VetoSlasherTest is Test { ); } + function test_RequestSlash( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 operatorNetworkLimit2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + operatorNetworkLimit2 = bound(operatorNetworkLimit2, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2 - 1); + + _optInNetworkVault(network); + + assertEq(0, _requestSlash(alice, network, alice, slashAmount1)); + + ( + address network_, + address operator_, + uint256 amount_, + uint48 vetoDeadline_, + uint48 executeDeadline_, + uint256 vetoedShares_, + bool completed_ + ) = slasher.slashRequests(0); + + assertEq(network_, network); + assertEq(operator_, alice); + assertEq(amount_, slashAmount1); + assertEq(vetoDeadline_, uint48(blockTimestamp + slasher.vetoDuration())); + assertEq(executeDeadline_, uint48(blockTimestamp + slasher.vetoDuration() + slasher.executeDuration())); + assertEq(vetoedShares_, 0); + assertEq(completed_, false); + + assertEq(1, _requestSlash(alice, network, bob, slashAmount2)); + + (network_, operator_, amount_, vetoDeadline_, executeDeadline_, vetoedShares_, completed_) = + slasher.slashRequests(1); + + assertEq(network_, network); + assertEq(operator_, bob); + assertEq(amount_, slashAmount2); + assertEq(vetoDeadline_, uint48(blockTimestamp + slasher.vetoDuration())); + assertEq(executeDeadline_, uint48(blockTimestamp + slasher.vetoDuration() + slasher.executeDuration())); + assertEq(vetoedShares_, 0); + assertEq(completed_, false); + } + + function test_RequestSlashRevertInsufficientSlash( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 operatorNetworkLimit2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + operatorNetworkLimit2 = bound(operatorNetworkLimit2, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + _setNetworkLimit(alice, network, networkLimit - 1); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1 - 1); + _setOperatorNetworkLimit(alice, network, bob, operatorNetworkLimit2 - 1); + + _optInNetworkVault(network); + + vm.expectRevert(IVetoSlasher.InsufficientSlash.selector); + _requestSlash(alice, network, alice, 0); + } + + function test_setResolverSharesBoth( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 resolverShares1, + uint256 resolverShares2, + uint256 resolverShares3 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + resolverShares3 = bound(resolverShares3, 1, slasher.SHARES_BASE()); + + vm.assume(resolverShares3 <= resolverShares2); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + + _setResolverShares(network, alice, resolverShares1); + + assertEq( + slasher.resolverSharesAt(network, alice, uint48(blockTimestamp + 2 * vault.epochDuration())), + resolverShares1 + ); + assertEq(slasher.resolverShares(network, alice), resolverShares1); + + _setResolverShares(network, bob, resolverShares2); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + assertEq( + slasher.resolverSharesAt(network, alice, uint48(blockTimestamp + vault.epochDuration())), resolverShares1 + ); + assertEq(slasher.resolverShares(network, alice), resolverShares1); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + _setResolverShares(network, bob, resolverShares3); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), resolverShares3 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), resolverShares2 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), resolverShares3 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), resolverShares3 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + _setResolverShares(network, bob, resolverShares3 - 1); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), resolverShares2 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares2 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares3 - 1 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares2); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 3 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + 2 * vault.epochDuration())), + resolverShares3 - 1 + ); + assertEq( + slasher.resolverSharesAt(network, bob, uint48(blockTimestamp + vault.epochDuration())), resolverShares3 - 1 + ); + assertEq(slasher.resolverShares(network, bob), resolverShares3 - 1); + } + + function test_setResolverSharesBothRevertNotOperator( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 resolverShares1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + + address network = alice; + _registerNetwork(network, alice); + + vm.expectRevert(IVetoSlasher.NotNetwork.selector); + _setResolverShares(bob, alice, resolverShares1); + } + + function test_setResolverSharesBothRevertInvalidShares( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 resolverShares1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, slasher.SHARES_BASE() + 1, type(uint256).max); + + address network = alice; + _registerNetwork(network, alice); + + vm.expectRevert(IVetoSlasher.InvalidShares.selector); + _setResolverShares(network, alice, resolverShares1); + } + + function test_ExecuteSlash( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + ( + address network_, + address operator_, + uint256 amount_, + uint48 vetoDeadline_, + uint48 executeDeadline_, + uint256 vetoedShares_, + bool completed_ + ) = slasher.slashRequests(0); + + assertEq(network_, network); + assertEq(operator_, alice); + assertEq(amount_, slashAmount1); + assertEq(vetoDeadline_, uint48(blockTimestamp + slasher.vetoDuration())); + assertEq(executeDeadline_, uint48(blockTimestamp + slasher.vetoDuration() + slasher.executeDuration())); + assertEq(vetoedShares_, 0); + assertEq(completed_, false); + + blockTimestamp = blockTimestamp + vetoDuration; + vm.warp(blockTimestamp); + + assertEq(_executeSlash(alice, 0), slashAmount1); + + assertEq(vault.totalSupply(), depositAmount - slashAmount1); + + (network_, operator_, amount_, vetoDeadline_, executeDeadline_, vetoedShares_, completed_) = + slasher.slashRequests(0); + + assertEq(network_, network); + assertEq(operator_, alice); + assertEq(amount_, slashAmount1); + assertEq(vetoDeadline_, uint48(blockTimestamp)); + assertEq(executeDeadline_, uint48(blockTimestamp + slasher.executeDuration())); + assertEq(vetoedShares_, 0); + assertEq(completed_, true); + } + + function test_ExecuteSlashRevertSlashRequestNotExist( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + blockTimestamp = blockTimestamp + vetoDuration; + vm.warp(blockTimestamp); + + vm.expectRevert(IVetoSlasher.SlashRequestNotExist.selector); + _executeSlash(alice, 1); + } + + function test_ExecuteSlashRevertVetoPeriodNotEnded( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + vm.expectRevert(IVetoSlasher.VetoPeriodNotEnded.selector); + _executeSlash(alice, 0); + } + + function test_ExecuteSlashRevertSlashPeriodEnded( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + blockTimestamp = blockTimestamp + vetoDuration + executeDuration; + vm.warp(blockTimestamp); + + vm.expectRevert(IVetoSlasher.SlashPeriodEnded.selector); + _executeSlash(alice, 0); + } + + function test_ExecuteSlashRevertSlashRequestCompleted( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 0, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + blockTimestamp = blockTimestamp + vetoDuration; + vm.warp(blockTimestamp); + + _executeSlash(alice, 0); + + vm.expectRevert(IVetoSlasher.SlashRequestCompleted.selector); + _executeSlash(alice, 0); + } + + function test_VetoSlash( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, resolverShares1); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + _vetoSlash(alice, 0); + + assertEq(slasher.hasVetoed(alice, 0), true); + + (,,,,, uint256 vetoedShares_, bool completed_) = slasher.slashRequests(0); + + assertEq(vetoedShares_, resolverShares1); + assertEq(completed_, vetoedShares_ == slasher.SHARES_BASE()); + + if (vetoedShares_ != slasher.SHARES_BASE()) { + _vetoSlash(bob, 0); + + assertEq(slasher.hasVetoed(bob, 0), true); + + (,,,,, vetoedShares_, completed_) = slasher.slashRequests(0); + + assertEq(vetoedShares_, Math.min(resolverShares1 + resolverShares2, slasher.SHARES_BASE())); + assertEq(completed_, vetoedShares_ == slasher.SHARES_BASE()); + } + + if (vetoedShares_ != slasher.SHARES_BASE()) { + blockTimestamp = blockTimestamp + vetoDuration; + vm.warp(blockTimestamp); + + assertEq( + _executeSlash(alice, 0), + ( + slashAmount1 + - slashAmount1.mulDiv(resolverShares1 + resolverShares2, slasher.SHARES_BASE(), Math.Rounding.Ceil) + ) + ); + + assertEq( + vault.totalSupply(), + depositAmount + - ( + slashAmount1 + - slashAmount1.mulDiv(resolverShares1 + resolverShares2, slasher.SHARES_BASE(), Math.Rounding.Ceil) + ) + ); + } + } + + function test_VetoSlashRevertSlashRequestNotExist( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, resolverShares1); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + vm.expectRevert(IVetoSlasher.SlashRequestNotExist.selector); + _vetoSlash(alice, 1); + } + + function test_VetoSlashRevertVetoPeriodEnded( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, resolverShares1); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + blockTimestamp = blockTimestamp + vetoDuration; + vm.warp(blockTimestamp); + + vm.expectRevert(IVetoSlasher.VetoPeriodEnded.selector); + _vetoSlash(alice, 0); + } + + function test_VetoSlashRevertNotResolver( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, resolverShares1); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + vm.expectRevert(IVetoSlasher.NotResolver.selector); + _vetoSlash(address(1), 0); + } + + function test_VetoSlashRevertSlashRequestCompleted( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE()); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, slasher.SHARES_BASE()); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + _vetoSlash(alice, 0); + + vm.expectRevert(IVetoSlasher.SlashRequestCompleted.selector); + _vetoSlash(bob, 0); + } + + function test_VetoSlashRevertAlreadyVetoed( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration, + uint256 depositAmount, + uint256 networkLimit, + uint256 operatorNetworkLimit1, + uint256 slashAmount1, + uint256 resolverShares1, + uint256 resolverShares2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + vetoDuration = uint48(bound(vetoDuration, 1, type(uint48).max / 2)); + executeDuration = uint48(bound(executeDuration, 1, type(uint48).max / 2)); + vm.assume(vetoDuration + executeDuration <= epochDuration); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + networkLimit = bound(networkLimit, 1, type(uint256).max); + operatorNetworkLimit1 = bound(operatorNetworkLimit1, 1, type(uint256).max / 2); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration, vetoDuration, executeDuration); + + resolverShares1 = bound(resolverShares1, 1, slasher.SHARES_BASE() - 1); + resolverShares2 = bound(resolverShares2, 1, slasher.SHARES_BASE()); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + + _optInOperatorVault(alice); + + _optInOperatorNetwork(alice, address(network)); + + _deposit(alice, depositAmount); + + _setNetworkLimit(alice, network, networkLimit); + + _setOperatorNetworkLimit(alice, network, alice, operatorNetworkLimit1); + + _optInNetworkVault(network); + + _setResolverShares(network, alice, resolverShares1); + _setResolverShares(network, bob, resolverShares2); + + slashAmount1 = Math.min(slashAmount1, Math.min(depositAmount, Math.min(networkLimit, operatorNetworkLimit1))); + + _requestSlash(alice, network, alice, slashAmount1); + + _vetoSlash(alice, 0); + + vm.expectRevert(IVetoSlasher.AlreadyVetoed.selector); + _vetoSlash(alice, 0); + } + function _getVaultAndDelegator(uint48 epochDuration) internal returns (Vault, FullRestakeDelegator) { (address vault_, address delegator_,) = vaultConfigurator.create( IVaultConfigurator.InitParams({ @@ -319,6 +1316,50 @@ contract VetoSlasherTest is Test { return (Vault(vault_), FullRestakeDelegator(delegator_)); } + function _getVaultAndDelegatorAndSlasher( + uint48 epochDuration, + uint48 vetoDuration, + uint48 executeDuration + ) internal returns (Vault, FullRestakeDelegator, VetoSlasher) { + (address vault_, address delegator_, address slasher_) = vaultConfigurator.create( + IVaultConfigurator.InitParams({ + version: vaultFactory.lastVersion(), + owner: alice, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdEaD), + epochDuration: epochDuration, + slasherSetEpochsDelay: 3, + depositWhitelist: false, + defaultAdminRoleHolder: alice, + slasherSetRoleHolder: alice, + depositorWhitelistRoleHolder: alice + }), + delegatorIndex: 1, + delegatorParams: abi.encode( + IFullRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), + networkLimitSetRoleHolder: alice, + operatorNetworkLimitSetRoleHolder: alice + }) + ), + withSlasher: true, + slasherIndex: 1, + slasherParams: abi.encode( + IVetoSlasher.InitParams({ + vetoDuration: vetoDuration, + executeDuration: executeDuration, + resolverSetEpochsDelay: 3 + }) + ) + }) + ); + + return (Vault(vault_), FullRestakeDelegator(delegator_), VetoSlasher(slasher_)); + } + function _getSlasher(address vault_, uint48 vetoDuration, uint48 executeDuration) internal returns (VetoSlasher) { return VetoSlasher( slasherFactory.create( @@ -437,15 +1478,20 @@ contract VetoSlasherTest is Test { vm.stopPrank(); } - function _requestSlash(address user, address network, address operator, uint256 amount) internal { + function _requestSlash( + address user, + address network, + address operator, + uint256 amount + ) internal returns (uint256 slashIndex) { vm.startPrank(user); - slasher.requestSlash(network, operator, amount); + slashIndex = slasher.requestSlash(network, operator, amount); vm.stopPrank(); } - function _executeSlash(address user, uint256 slashIndex) internal { + function _executeSlash(address user, uint256 slashIndex) internal returns (uint256 slashAmount) { vm.startPrank(user); - slasher.executeSlash(slashIndex); + slashAmount = slasher.executeSlash(slashIndex); vm.stopPrank(); } @@ -460,4 +1506,22 @@ contract VetoSlasherTest is Test { slasher.setResolverShares(resolver, shares); vm.stopPrank(); } + + function _setNetworkLimit(address user, address network, uint256 amount) internal { + vm.startPrank(user); + delegator.setNetworkLimit(network, amount); + vm.stopPrank(); + } + + function _setOperatorNetworkLimit(address user, address network, address operator, uint256 amount) internal { + vm.startPrank(user); + delegator.setOperatorNetworkLimit(network, operator, amount); + vm.stopPrank(); + } + + function _setMaxNetworkLimit(address user, uint256 amount) internal { + vm.startPrank(user); + delegator.setMaxNetworkLimit(amount); + vm.stopPrank(); + } } diff --git a/test/vault/Vault.t.sol b/test/vault/Vault.t.sol index c0a0e6cd..8f136544 100644 --- a/test/vault/Vault.t.sol +++ b/test/vault/Vault.t.sol @@ -28,8 +28,11 @@ import {IFullRestakeDelegator} from "src/interfaces/delegator/IFullRestakeDelega import {IBaseDelegator} from "src/interfaces/delegator/IBaseDelegator.sol"; import {IVaultStorage} from "src/interfaces/vault/IVaultStorage.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; contract VaultTest is Test { + using Math for uint256; + address owner; address alice; uint256 alicePrivateKey; @@ -52,6 +55,8 @@ contract VaultTest is Test { VaultConfigurator vaultConfigurator; Vault vault; + FullRestakeDelegator delegator; + Slasher slasher; function setUp() public { owner = address(this); @@ -399,7 +404,7 @@ contract VaultTest is Test { uint64 lastVersion = vaultFactory.lastVersion(); vault = Vault(vaultFactory.create(lastVersion, alice, false, "")); - address delegator = delegatorFactory.create( + address delegator_ = delegatorFactory.create( 0, true, abi.encode( @@ -421,7 +426,7 @@ contract VaultTest is Test { abi.encode( IVault.InitParams({ collateral: address(collateral), - delegator: delegator, + delegator: delegator_, slasher: address(1), burner: address(0xdEaD), epochDuration: epochDuration, @@ -986,17 +991,17 @@ contract VaultTest is Test { vault = _getVault(epochDuration); - address slasher = slasherFactory.create(0, true, abi.encode(address(vault), "")); + address slasher_ = slasherFactory.create(0, true, abi.encode(address(vault), "")); uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; - _setSlasher(alice, slasher); + _setSlasher(alice, slasher_); assertEq(vault.slasher(), address(0)); assertEq(vault.slasherIn(0), address(0)); assertEq(vault.slasherIn(1), address(0)); assertEq(vault.slasherIn(2), address(0)); - assertEq(vault.slasherIn(3), slasher); - assertEq(vault.slasherIn(4), slasher); + assertEq(vault.slasherIn(3), slasher_); + assertEq(vault.slasherIn(4), slasher_); blockTimestamp = blockTimestamp + 1; vm.warp(blockTimestamp); @@ -1004,45 +1009,45 @@ contract VaultTest is Test { assertEq(vault.slasher(), address(0)); assertEq(vault.slasherIn(0), address(0)); assertEq(vault.slasherIn(1), address(0)); - assertEq(vault.slasherIn(2), slasher); - assertEq(vault.slasherIn(3), slasher); - assertEq(vault.slasherIn(4), slasher); + assertEq(vault.slasherIn(2), slasher_); + assertEq(vault.slasherIn(3), slasher_); + assertEq(vault.slasherIn(4), slasher_); blockTimestamp = blockTimestamp + 2; vm.warp(blockTimestamp); - assertEq(vault.slasher(), slasher); - assertEq(vault.slasherIn(0), slasher); - assertEq(vault.slasherIn(1), slasher); - assertEq(vault.slasherIn(2), slasher); - assertEq(vault.slasherIn(3), slasher); - assertEq(vault.slasherIn(4), slasher); + assertEq(vault.slasher(), slasher_); + assertEq(vault.slasherIn(0), slasher_); + assertEq(vault.slasherIn(1), slasher_); + assertEq(vault.slasherIn(2), slasher_); + assertEq(vault.slasherIn(3), slasher_); + assertEq(vault.slasherIn(4), slasher_); _setSlasher(alice, address(0)); - assertEq(vault.slasher(), slasher); - assertEq(vault.slasherIn(0), slasher); - assertEq(vault.slasherIn(1), slasher); - assertEq(vault.slasherIn(2), slasher); + assertEq(vault.slasher(), slasher_); + assertEq(vault.slasherIn(0), slasher_); + assertEq(vault.slasherIn(1), slasher_); + assertEq(vault.slasherIn(2), slasher_); assertEq(vault.slasherIn(3), address(0)); assertEq(vault.slasherIn(4), address(0)); blockTimestamp = blockTimestamp + 2; vm.warp(blockTimestamp); - assertEq(vault.slasher(), slasher); - assertEq(vault.slasherIn(0), slasher); + assertEq(vault.slasher(), slasher_); + assertEq(vault.slasherIn(0), slasher_); assertEq(vault.slasherIn(1), address(0)); assertEq(vault.slasherIn(2), address(0)); assertEq(vault.slasherIn(3), address(0)); assertEq(vault.slasherIn(4), address(0)); - _setSlasher(alice, slasher); - assertEq(vault.slasher(), slasher); - assertEq(vault.slasherIn(0), slasher); - assertEq(vault.slasherIn(1), slasher); - assertEq(vault.slasherIn(2), slasher); - assertEq(vault.slasherIn(3), slasher); - assertEq(vault.slasherIn(4), slasher); + _setSlasher(alice, slasher_); + assertEq(vault.slasher(), slasher_); + assertEq(vault.slasherIn(0), slasher_); + assertEq(vault.slasherIn(1), slasher_); + assertEq(vault.slasherIn(2), slasher_); + assertEq(vault.slasherIn(3), slasher_); + assertEq(vault.slasherIn(4), slasher_); } function test_SetSlasherRevertNotSlasher() public { @@ -1065,6 +1070,94 @@ contract VaultTest is Test { vm.stopPrank(); } + function test_Slash( + uint48 epochDuration, + uint256 depositAmount, + uint256 withdrawAmount1, + uint256 withdrawAmount2, + uint256 slashAmount1, + uint256 slashAmount2 + ) public { + epochDuration = uint48(bound(epochDuration, 1, 10 days)); + depositAmount = bound(depositAmount, 1, 100 * 10 ** 18); + withdrawAmount1 = bound(withdrawAmount1, 1, 100 * 10 ** 18); + withdrawAmount2 = bound(withdrawAmount2, 1, 100 * 10 ** 18); + slashAmount1 = bound(slashAmount1, 1, type(uint256).max / 2); + slashAmount2 = bound(slashAmount2, 1, type(uint256).max / 2); + vm.assume(depositAmount >= withdrawAmount1 + withdrawAmount2); + vm.assume(depositAmount >= slashAmount1 + slashAmount2); + + (vault, delegator, slasher) = _getVaultAndDelegatorAndSlasher(epochDuration); + + uint256 blockTimestamp = block.timestamp * block.timestamp / block.timestamp * block.timestamp / block.timestamp; + + address network = alice; + _registerNetwork(network, alice); + _setMaxNetworkLimit(network, type(uint256).max); + + _registerOperator(alice); + _registerOperator(bob); + + _optInOperatorVault(alice); + _optInOperatorVault(bob); + + _optInOperatorNetwork(alice, address(network)); + _optInOperatorNetwork(bob, address(network)); + + _setNetworkLimit(alice, network, type(uint256).max); + _setNetworkLimit(alice, network, type(uint256).max - 1); + + _setOperatorNetworkLimit(alice, network, alice, type(uint256).max / 2); + _setOperatorNetworkLimit(alice, network, bob, type(uint256).max / 2); + + _setOperatorNetworkLimit(alice, network, alice, type(uint256).max / 2 - 1); + _setOperatorNetworkLimit(alice, network, bob, type(uint256).max / 2 - 1); + + vm.assume(slashAmount1 < depositAmount && slashAmount1 <= type(uint256).max / 2); + + _optInNetworkVault(network); + + _deposit(alice, depositAmount); + _withdraw(alice, withdrawAmount1); + + blockTimestamp = blockTimestamp + vault.epochDuration(); + vm.warp(blockTimestamp); + + _withdraw(alice, withdrawAmount2); + + assertEq(vault.totalSupply(), depositAmount); + assertEq(vault.activeSupply(), depositAmount - withdrawAmount1 - withdrawAmount2); + assertEq(vault.withdrawals(vault.currentEpoch()), withdrawAmount1); + assertEq(vault.withdrawals(vault.currentEpoch() + 1), withdrawAmount2); + + assertEq(_slash(alice, network, alice, slashAmount1), slashAmount1); + + uint256 activeSupply1 = depositAmount - withdrawAmount1 - withdrawAmount2 + - (depositAmount - withdrawAmount1 - withdrawAmount2).mulDiv(slashAmount1, depositAmount); + uint256 withdrawals1 = withdrawAmount1 - withdrawAmount1.mulDiv(slashAmount1, depositAmount); + uint256 nextWithdrawals1 = withdrawAmount2 - withdrawAmount2.mulDiv(slashAmount1, depositAmount); + assertEq(vault.totalSupply(), depositAmount - slashAmount1); + assertTrue(activeSupply1 - vault.activeSupply() <= 2); + assertTrue(withdrawals1 - vault.withdrawals(vault.currentEpoch()) <= 1); + assertEq(vault.withdrawals(vault.currentEpoch() + 1), nextWithdrawals1); + + assertEq(_slash(alice, network, bob, slashAmount2), slashAmount2); + + assertEq(vault.totalSupply(), depositAmount - slashAmount1 - slashAmount2); + assertTrue( + (activeSupply1 - activeSupply1.mulDiv(slashAmount2, depositAmount - slashAmount1)) - vault.activeSupply() + <= 4 + ); + assertTrue( + (withdrawals1 - withdrawals1.mulDiv(slashAmount2, depositAmount - slashAmount1)) + - vault.withdrawals(vault.currentEpoch()) <= 2 + ); + assertEq( + vault.withdrawals(vault.currentEpoch() + 1), + nextWithdrawals1 - nextWithdrawals1.mulDiv(slashAmount2, depositAmount - slashAmount1) + ); + } + function _getVault(uint48 epochDuration) internal returns (Vault) { (address vault_,,) = vaultConfigurator.create( IVaultConfigurator.InitParams({ @@ -1099,6 +1192,43 @@ contract VaultTest is Test { return Vault(vault_); } + function _getVaultAndDelegatorAndSlasher(uint48 epochDuration) + internal + returns (Vault, FullRestakeDelegator, Slasher) + { + (address vault_, address delegator_, address slasher_) = vaultConfigurator.create( + IVaultConfigurator.InitParams({ + version: vaultFactory.lastVersion(), + owner: alice, + vaultParams: IVault.InitParams({ + collateral: address(collateral), + delegator: address(0), + slasher: address(0), + burner: address(0xdEaD), + epochDuration: epochDuration, + slasherSetEpochsDelay: 3, + depositWhitelist: false, + defaultAdminRoleHolder: alice, + slasherSetRoleHolder: alice, + depositorWhitelistRoleHolder: alice + }), + delegatorIndex: 1, + delegatorParams: abi.encode( + IFullRestakeDelegator.InitParams({ + baseParams: IBaseDelegator.BaseParams({defaultAdminRoleHolder: alice}), + networkLimitSetRoleHolder: alice, + operatorNetworkLimitSetRoleHolder: alice + }) + ), + withSlasher: true, + slasherIndex: 0, + slasherParams: "" + }) + ); + + return (Vault(vault_), FullRestakeDelegator(delegator_), Slasher(slasher_)); + } + function _registerOperator(address user) internal { vm.startPrank(user); operatorRegistry.registerOperator(); @@ -1197,4 +1327,33 @@ contract VaultTest is Test { vault.setSlasher(slasher_); vm.stopPrank(); } + + function _setNetworkLimit(address user, address network, uint256 amount) internal { + vm.startPrank(user); + delegator.setNetworkLimit(network, amount); + vm.stopPrank(); + } + + function _setOperatorNetworkLimit(address user, address network, address operator, uint256 amount) internal { + vm.startPrank(user); + delegator.setOperatorNetworkLimit(network, operator, amount); + vm.stopPrank(); + } + + function _slash( + address user, + address network, + address operator, + uint256 amount + ) internal returns (uint256 slashAmount) { + vm.startPrank(user); + slashAmount = slasher.slash(network, operator, amount); + vm.stopPrank(); + } + + function _setMaxNetworkLimit(address user, uint256 amount) internal { + vm.startPrank(user); + delegator.setMaxNetworkLimit(amount); + vm.stopPrank(); + } }