diff --git a/contracts/contracts/interfaces/IDepositContract.sol b/contracts/contracts/interfaces/IDepositContract.sol index 9d62b5d776..07f654443b 100644 --- a/contracts/contracts/interfaces/IDepositContract.sol +++ b/contracts/contracts/interfaces/IDepositContract.sol @@ -31,4 +31,4 @@ interface IDepositContract { /// @notice Query the current deposit count. /// @return The deposit count encoded as a little endian 64-bit number. function get_deposit_count() external view returns (bytes memory); -} \ No newline at end of file +} diff --git a/contracts/contracts/mocks/BeaconChainDepositContractMock.sol b/contracts/contracts/mocks/BeaconChainDepositContractMock.sol index d2e148cf46..3d8b995efe 100644 --- a/contracts/contracts/mocks/BeaconChainDepositContractMock.sol +++ b/contracts/contracts/mocks/BeaconChainDepositContractMock.sol @@ -25,13 +25,29 @@ contract BeaconChainDepositContractMock { ) external payable { // Extended ABI length checks since dynamic types are used. require(pubkey.length == 48, "DepositContract: invalid pubkey length"); - require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length"); - require(signature.length == 96, "DepositContract: invalid signature length"); + require( + withdrawal_credentials.length == 32, + "DepositContract: invalid withdrawal_credentials length" + ); + require( + signature.length == 96, + "DepositContract: invalid signature length" + ); // Check deposit amount require(msg.value >= 1 ether, "DepositContract: deposit value too low"); - require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei"); - uint deposit_amount = msg.value / 1 gwei; - require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high"); + require( + msg.value % 1 gwei == 0, + "DepositContract: deposit value not multiple of gwei" + ); + uint256 deposit_amount = msg.value / 1 gwei; + require( + deposit_amount <= type(uint64).max, + "DepositContract: deposit value too high" + ); + require( + deposit_data_root != 0, + "DepositContract: invalid deposit_data_root" + ); } -} \ No newline at end of file +} diff --git a/contracts/contracts/strategies/NativeStaking/FeeAccumulator.sol b/contracts/contracts/strategies/NativeStaking/FeeAccumulator.sol index 6a9abd7046..baa5e2bf52 100644 --- a/contracts/contracts/strategies/NativeStaking/FeeAccumulator.sol +++ b/contracts/contracts/strategies/NativeStaking/FeeAccumulator.sol @@ -7,13 +7,14 @@ import { IWETH9 } from "../../interfaces/IWETH9.sol"; /** * @title Fee Accumulator for Native Staking SSV Strategy * @notice This contract is setup to receive fees from processing transactions on the beacon chain - * which includes priority fees and any MEV rewards + * which includes priority fees and any MEV rewards. + * It does NOT include swept ETH from consensus rewards or withdrawals. * @author Origin Protocol Inc */ contract FeeAccumulator is Governable { - /// @dev ETH is sent to the collector address + /// @notice The address the WETH is sent to on `collect` which is the Native Staking Strategy address public immutable COLLECTOR; - /// @notice WETH token address + /// @notice The address of the Wrapped ETH (WETH) token contract address public immutable WETH_TOKEN_ADDRESS; error CallerNotCollector(address caller, address expectedCaller); @@ -23,14 +24,15 @@ contract FeeAccumulator is Governable { /** * @param _collector Address of the contract that collects the fees + * @param _weth Address of the Wrapped ETH (WETH) token contract */ constructor(address _collector, address _weth) { COLLECTOR = _collector; WETH_TOKEN_ADDRESS = _weth; } - /* - * @notice Asserts that the caller is the collector + /** + * @dev Asserts that the caller is the collector */ function _assertIsCollector() internal view { if (msg.sender != COLLECTOR) { @@ -38,15 +40,16 @@ contract FeeAccumulator is Governable { } } - /* - * @notice Send all the ETH to the collector + /** + * @notice Converts ETH to WETH and sends the WETH to the collector + * @return weth The amount of WETH sent to the collector */ - function collect() external returns (uint256 wethReturned) { + function collect() external returns (uint256 weth) { _assertIsCollector(); - wethReturned = address(this).balance; - if (wethReturned > 0) { - IWETH9(WETH_TOKEN_ADDRESS).deposit{ value: wethReturned }(); - IWETH9(WETH_TOKEN_ADDRESS).transfer(COLLECTOR, wethReturned); + weth = address(this).balance; + if (weth > 0) { + IWETH9(WETH_TOKEN_ADDRESS).deposit{ value: weth }(); + IWETH9(WETH_TOKEN_ADDRESS).transfer(COLLECTOR, weth); } } } diff --git a/contracts/contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol b/contracts/contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol index 7d06295e35..7bbd0345bd 100644 --- a/contracts/contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol +++ b/contracts/contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { InitializableAbstractStrategy } from "../../utils/InitializableAbstractStrategy.sol"; +import { ISSVNetwork, Cluster } from "../../interfaces/ISSVNetwork.sol"; import { IWETH9 } from "../../interfaces/IWETH9.sol"; import { FeeAccumulator } from "./FeeAccumulator.sol"; import { ValidatorAccountant } from "./ValidatorAccountant.sol"; @@ -39,7 +40,7 @@ contract NativeStakingSSVStrategy is error EmptyRecipient(); error NotWeth(); - error InsuffiscientWethBalance( + error InsufficientWethBalance( uint256 requiredBalance, uint256 availableBalance ); @@ -60,7 +61,12 @@ contract NativeStakingSSVStrategy is address _beaconChainDepositContract ) InitializableAbstractStrategy(_baseConfig) - ValidatorAccountant(_wethAddress, _baseConfig.vaultAddress, _beaconChainDepositContract, _ssvNetwork) + ValidatorAccountant( + _wethAddress, + _baseConfig.vaultAddress, + _beaconChainDepositContract, + _ssvNetwork + ) { SSV_TOKEN_ADDRESS = _ssvToken; FEE_ACCUMULATOR_ADDRESS = _feeAccumulator; @@ -98,7 +104,8 @@ contract NativeStakingSSVStrategy is beaconChainRewardWETH; } - /// @notice Collect accumulated WETH & SSV tokens and send to the Harvester. + /// @notice Convert accumulated ETH to WETH and send to the Harvester. + /// Only callable by the Harvester. function collectRewardTokens() external virtual @@ -129,7 +136,7 @@ contract NativeStakingSSVStrategy is if (balance > 0) { if (address(rewardToken) == WETH_TOKEN_ADDRESS) { if (beaconChainRewardWETH > balance) { - revert InsuffiscientWethBalance( + revert InsufficientWethBalance( beaconChainRewardWETH, balance ); @@ -232,9 +239,9 @@ contract NativeStakingSSVStrategy is function _abstractSetPToken(address _asset, address) internal override {} /// @notice Returns the total value of (W)ETH that is staked to the validators - /// and also present on the native staking and fee accumulator contracts + /// and also present on the native staking and fee accumulator contracts. /// @param _asset Address of weth asset - /// @return balance Total value of (W)ETH + /// @return balance Total value of (W)ETH function checkBalance(address _asset) external view @@ -254,14 +261,13 @@ contract NativeStakingSSVStrategy is _pause(); } - /// @dev Retuns bool indicating whether asset is supported by strategy - /// @param _asset Address of the asset + /// @notice Returns bool indicating whether asset is supported by strategy. + /// @param _asset The address of the asset token. function supportsAsset(address _asset) public view override returns (bool) { return _asset == WETH_TOKEN_ADDRESS; } - /// @notice Approve the spending of all assets - /// @dev Approves the SSV Network contract to transfer SSV tokens for deposits + /// @notice Approves the SSV Network contract to transfer SSV tokens for deposits function safeApproveAllTokens() external override { /// @dev Approves the SSV Network contract to transfer SSV tokens for deposits IERC20(SSV_TOKEN_ADDRESS).approve( @@ -271,17 +277,20 @@ contract NativeStakingSSVStrategy is } /// @notice Deposits more SSV Tokens to the SSV Network contract which is used to pay the SSV Operators. - /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds - /// uses "onlyStrategist" modifier so continuous fron-running can't DOS our maintenance service - /// that tries to top us SSV tokens. + /// @dev A SSV cluster is defined by the SSVOwnerAddress and the set of operatorIds. + /// uses "onlyStrategist" modifier so continuous front-running can't DOS our maintenance service + /// that tries to top up SSV tokens. + /// @param cluster The SSV cluster details that must be derived from emitted events from the SSVNetwork contract. function depositSSV( uint64[] memory operatorIds, uint256 amount, Cluster memory cluster - ) - onlyStrategist - external { - // address SSV_NETWORK_ADDRESS = lrtConfig.getContract(LRTConstants.SSV_NETWORK); - // ISSVNetwork(SSV_NETWORK_ADDRESS).deposit(address(this), operatorIds, amount, cluster); + ) external onlyStrategist { + ISSVNetwork(SSV_NETWORK_ADDRESS).deposit( + address(this), + operatorIds, + amount, + cluster + ); } } diff --git a/contracts/contracts/strategies/NativeStaking/README.md b/contracts/contracts/strategies/NativeStaking/README.md new file mode 100644 index 0000000000..13e280eb9b --- /dev/null +++ b/contracts/contracts/strategies/NativeStaking/README.md @@ -0,0 +1,21 @@ +# Diagrams + +## Native Staking SSV Strategy + +### Hierarchy + +![Native Staking SSV Strategy Hierarchy](../../../docs/NativeStakingSSVStrategyHierarchy.svg) + +### Squashed + +![Native Staking SSV Strategy Squashed](../../../docs/NativeStakingSSVStrategySquashed.svg) + +### Storage + +![Native Staking SSV Strategy Storage](../../../docs/NativeStakingSSVStrategyStorage.svg) + +## Fee Accumulator + +### Squashed + +![Fee Accumulator Squashed](../../../docs/FeeAccumulatorSquashed.svg) diff --git a/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol b/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol index e85b808129..8570e96f16 100644 --- a/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol +++ b/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import { Pausable } from "@openzeppelin/contracts/security/Pausable.sol"; import { ValidatorRegistrator } from "./ValidatorRegistrator.sol"; +import { IVault } from "../../interfaces/IVault.sol"; import { IWETH9 } from "../../interfaces/IWETH9.sol"; /// @title Accountant of the rewards Beacon Chain ETH @@ -10,6 +11,10 @@ import { IWETH9 } from "../../interfaces/IWETH9.sol"; /// or partial withdrawals /// @author Origin Protocol Inc abstract contract ValidatorAccountant is ValidatorRegistrator { + /// @notice The maximum amount of ETH that can be staked by a validator + /// @dev this can change in the future with EIP-7251, Increase the MAX_EFFECTIVE_BALANCE + uint256 public constant MAX_STAKE = 32 ether; + /// @notice Address of the OETH Vault proxy contract address public immutable VAULT_ADDRESS; /// @dev The WETH present on this contract will come from 2 sources: @@ -23,14 +28,12 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { /// present as a result of a deposit. uint256 public beaconChainRewardWETH = 0; - /// @dev start of fuse interval + /// @notice start of fuse interval uint256 public fuseIntervalStart = 0; - /// @dev end of fuse interval + /// @notice end of fuse interval uint256 public fuseIntervalEnd = 0; - /// @dev Governor that can manually correct the accounting + /// @notice Governor that can manually correct the accounting address public accountingGovernor; - /// @dev Strategist that can pause the accounting - address public strategist; uint256[50] private __gap; @@ -40,12 +43,12 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { uint256 start, uint256 end ); - event AccuntingFullyWithdrawnValidator( + event AccountingFullyWithdrawnValidator( uint256 noOfValidators, uint256 remainingValidators, uint256 wethSentToVault ); - event AccuntingValidatorSlashed( + event AccountingValidatorSlashed( uint256 remainingValidators, uint256 wethSentToVault ); @@ -54,10 +57,6 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { address newAddress ); event AccountingBeaconChainRewards(uint256 amount); - event StrategistAddressChanged( - address oldStrategist, - address newStrategist - ); event AccountingManuallyFixed( uint256 oldActiveDepositedValidators, @@ -85,7 +84,10 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { /// @dev Throws if called by any account other than the Strategist modifier onlyStrategist() { - require(msg.sender == strategist, "Caller is not the Strategist"); + require( + msg.sender == IVault(VAULT_ADDRESS).strategistAddr(), + "Caller is not the Strategist" + ); _; } @@ -93,8 +95,18 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { /// @param _vaultAddress Address of the Vault /// @param _beaconChainDepositContract Address of the beacon chain deposit contract /// @param _ssvNetwork Address of the SSV Network contract - constructor(address _wethAddress, address _vaultAddress, address _beaconChainDepositContract, address _ssvNetwork) - ValidatorRegistrator(_wethAddress, _beaconChainDepositContract, _ssvNetwork) { + constructor( + address _wethAddress, + address _vaultAddress, + address _beaconChainDepositContract, + address _ssvNetwork + ) + ValidatorRegistrator( + _wethAddress, + _beaconChainDepositContract, + _ssvNetwork + ) + { VAULT_ADDRESS = _vaultAddress; } @@ -103,11 +115,6 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { accountingGovernor = _address; } - function setStrategist(address _address) external onlyGovernor { - emit StrategistAddressChanged(strategist, _address); - strategist = _address; - } - /// @notice set fuse interval values function setFuseInterval( uint256 _fuseIntervalStart, @@ -133,19 +140,24 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { fuseIntervalEnd = _fuseIntervalEnd; } + /* solhint-disable max-line-length */ /// This notion page offers a good explanation of how the accounting functions /// https://www.notion.so/originprotocol/Limited-simplified-native-staking-accounting-67a217c8420d40678eb943b9da0ee77d - /// In short after dividing by 32 if the ETH remaining on the contract falls between 0 and fuseIntervalStart the accounting + /// In short, after dividing by 32 if the ETH remaining on the contract falls between 0 and fuseIntervalStart the accounting /// function will treat that ETH as a Beacon Chain Reward ETH. /// On the contrary if after dividing by 32 the ETH remaining on the contract falls between fuseIntervalEnd and 32 the /// accounting function will treat that as a validator slashing. /// @notice Perform the accounting attributing beacon chain ETH to either full or partial withdrawals. Returns true when - /// accounting is valid and fuse isn't "blown". Returns false when fuse is blown + /// accounting is valid and fuse isn't "blown". Returns false when fuse is blown. /// @dev This function could in theory be permission-less but lets allow only the Registrator (Defender Action) to call it - /// for now - function doAccounting() external onlyRegistrator returns (bool accountingValid) { + /// for now. + /* solhint-enable max-line-length */ + function doAccounting() + external + onlyRegistrator + returns (bool accountingValid) + { uint256 ethBalance = address(this).balance; - uint256 MAX_STAKE = 32 ether; accountingValid = true; // send the WETH that is from fully withdrawn validators to the Vault @@ -157,7 +169,7 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { IWETH9(WETH_TOKEN_ADDRESS).deposit{ value: wethToVault }(); IWETH9(WETH_TOKEN_ADDRESS).transfer(VAULT_ADDRESS, wethToVault); - emit AccuntingFullyWithdrawnValidator( + emit AccountingFullyWithdrawnValidator( fullyWithdrawnValidators, activeDepositedValidators, wethToVault @@ -173,6 +185,7 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { // Beacon chain rewards swept (partial validator withdrawals) if (ethRemaining <= fuseIntervalStart) { IWETH9(WETH_TOKEN_ADDRESS).deposit{ value: ethRemaining }(); + // solhint-disable-next-line reentrancy beaconChainRewardWETH += ethRemaining; emit AccountingBeaconChainRewards(ethRemaining); } @@ -182,7 +195,7 @@ abstract contract ValidatorAccountant is ValidatorRegistrator { IWETH9(WETH_TOKEN_ADDRESS).transfer(VAULT_ADDRESS, ethRemaining); activeDepositedValidators -= 1; - emit AccuntingValidatorSlashed( + emit AccountingValidatorSlashed( activeDepositedValidators, ethRemaining ); diff --git a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol index 2ee2a87fa1..1674b47730 100644 --- a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol +++ b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol @@ -15,16 +15,16 @@ struct ValidatorStakeData { /** * @title Registrator of the validators - * @notice This contract implements all the required functionality to register validators + * @notice This contract implements all the required functionality to register, exit and remove validators. * @author Origin Protocol Inc */ abstract contract ValidatorRegistrator is Governable, Pausable { - /// @notice The Wrapped ETH (WETH) contract address + /// @notice The address of the Wrapped ETH (WETH) token contract address public immutable WETH_TOKEN_ADDRESS; - /// @notice Address of the beacon chain deposit contract + /// @notice The address of the beacon chain deposit contract address public immutable BEACON_CHAIN_DEPOSIT_CONTRACT; - /// @notice SSV Network contract used to interface with - address public immutable SSV_NETWORK_ADDRESS; + /// @notice The address of the SSV Network contract used to interface with + address public immutable SSV_NETWORK_ADDRESS; /// @notice Address of the registrator - allowed to register, exit and remove validators address public validatorRegistrator; @@ -66,7 +66,11 @@ abstract contract ValidatorRegistrator is Governable, Pausable { /// @param _wethAddress Address of the Erc20 WETH Token contract /// @param _beaconChainDepositContract Address of the beacon chain deposit contract /// @param _ssvNetwork Address of the SSV Network contract - constructor(address _wethAddress, address _beaconChainDepositContract, address _ssvNetwork) { + constructor( + address _wethAddress, + address _beaconChainDepositContract, + address _ssvNetwork + ) { WETH_TOKEN_ADDRESS = _wethAddress; BEACON_CHAIN_DEPOSIT_CONTRACT = _beaconChainDepositContract; SSV_NETWORK_ADDRESS = _ssvNetwork; @@ -88,12 +92,13 @@ abstract contract ValidatorRegistrator is Governable, Pausable { /// @notice Stakes WETH to the node validators /// @param validators A list of validator data needed to stake. - /// The ValidatorStakeData struct contains the pubkey, signature and depositDataRoot. - /// @dev Only accounts with the Operator role can call this function. - function stakeEth(ValidatorStakeData[] calldata validators) + /// The `ValidatorStakeData` struct contains the pubkey, signature and depositDataRoot. + /// Only the registrator can call this function. + function stakeEth(ValidatorStakeData[] calldata validators) + external onlyRegistrator whenNotPaused - external { + { uint256 requiredWETH = validators.length * 32 ether; uint256 wethBalance = getWETHBalanceEligibleForStaking(); if (wethBalance < requiredWETH) { @@ -104,15 +109,22 @@ abstract contract ValidatorRegistrator is Governable, Pausable { IWETH9(WETH_TOKEN_ADDRESS).withdraw(wethBalance); // For each validator - for (uint256 i = 0; i < validators.length;) { + for (uint256 i = 0; i < validators.length; ) { bytes32 pubkeyHash = keccak256(validators[i].pubkey); VALIDATOR_STATE currentState = validatorsStates[pubkeyHash]; if (currentState != VALIDATOR_STATE.REGISTERED) { - revert ValidatorInUnexpectedState(validators[i].pubkey, currentState); + revert ValidatorInUnexpectedState( + validators[i].pubkey, + currentState + ); } - _stakeEth(validators[i].pubkey, validators[i].signature, validators[i].depositDataRoot); + _stakeEth( + validators[i].pubkey, + validators[i].signature, + validators[i].depositDataRoot + ); validatorsStates[pubkeyHash] = VALIDATOR_STATE.STAKED; unchecked { @@ -121,51 +133,61 @@ abstract contract ValidatorRegistrator is Governable, Pausable { } } - /// @dev Deposit WETH to the beacon chain deposit contract - /// @dev The public functions that call this internal function are responsible for access control. - function _stakeEth(bytes calldata pubkey, bytes calldata signature, bytes32 depositDataRoot) internal { + /// @dev Deposit WETH to the beacon chain deposit contract. + /// The public functions that call this internal function are responsible for access control. + function _stakeEth( + bytes calldata pubkey, + bytes calldata signature, + bytes32 depositDataRoot + ) internal { /* 0x01 to indicate that withdrawal credentials will contain an EOA address that the sweeping function * can sweep funds to. * bytes11(0) to fill up the required zeros * remaining bytes20 are for the address */ - bytes memory withdrawal_credentials = abi.encodePacked(bytes1(0x01), bytes11(0), address(this)); + bytes memory withdrawal_credentials = abi.encodePacked( + bytes1(0x01), + bytes11(0), + address(this) + ); IDepositContract(BEACON_CHAIN_DEPOSIT_CONTRACT).deposit( - pubkey, withdrawal_credentials, signature, depositDataRoot + pubkey, + withdrawal_credentials, + signature, + depositDataRoot ); activeDepositedValidators += 1; emit ETHStaked(pubkey, 32 ether, withdrawal_credentials); - } - /// @dev Registers a new validator in the SSV Cluster + /// @notice Registers a new validator in the SSV Cluster. + /// Only the registrator can call this function. function registerSsvValidator( bytes calldata publicKey, uint64[] calldata operatorIds, bytes calldata sharesData, uint256 amount, Cluster calldata cluster - ) - external - onlyRegistrator - whenNotPaused - { - ISSVNetwork(SSV_NETWORK_ADDRESS).registerValidator(publicKey, operatorIds, sharesData, amount, cluster); + ) external onlyRegistrator whenNotPaused { + ISSVNetwork(SSV_NETWORK_ADDRESS).registerValidator( + publicKey, + operatorIds, + sharesData, + amount, + cluster + ); validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.REGISTERED; emit SSVValidatorRegistered(publicKey, operatorIds); } - /// @dev Exit a validator from the Beacon chain. - /// The staked ETH will be sent to the EigenPod. + /// @notice Exit a validator from the Beacon chain. + /// The staked ETH will eventually swept to this native staking strategy. + /// Only the registrator can call this function. function exitSsvValidator( bytes calldata publicKey, uint64[] calldata operatorIds - ) - external - onlyRegistrator - whenNotPaused - { + ) external onlyRegistrator whenNotPaused { VALIDATOR_STATE currentState = validatorsStates[keccak256(publicKey)]; if (currentState != VALIDATOR_STATE.STAKED) { revert ValidatorInUnexpectedState(publicKey, currentState); @@ -177,24 +199,25 @@ abstract contract ValidatorRegistrator is Governable, Pausable { validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.EXITING; } - /// @dev Remove a validator from the SSV Cluster. + /// @notice Remove a validator from the SSV Cluster. /// Make sure `exitSsvValidator` is called before and the validate has exited the Beacon chain. /// If removed before the validator has exited the beacon chain will result in the validator being slashed. + /// Only the registrator can call this function. function removeSsvValidator( bytes calldata publicKey, uint64[] calldata operatorIds, Cluster calldata cluster - ) - external - onlyRegistrator - whenNotPaused - { + ) external onlyRegistrator whenNotPaused { VALIDATOR_STATE currentState = validatorsStates[keccak256(publicKey)]; if (currentState != VALIDATOR_STATE.EXITING) { revert ValidatorInUnexpectedState(publicKey, currentState); } - ISSVNetwork(SSV_NETWORK_ADDRESS).removeValidator(publicKey, operatorIds, cluster); + ISSVNetwork(SSV_NETWORK_ADDRESS).removeValidator( + publicKey, + operatorIds, + cluster + ); emit SSVValidatorExitCompleted(publicKey, operatorIds); validatorsStates[keccak256(publicKey)] = VALIDATOR_STATE.EXIT_COMPLETE; diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index ed14d833ec..b3cb95cea2 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -764,7 +764,7 @@ const deployNativeStakingSSVStrategy = async () => { assetAddresses.SSV, // ssvToken assetAddresses.SSVNetwork, // ssvNetwork dFeeAccumulatorProxy.address, // feeAccumulator - assetAddresses.beaconChainDepositContract // depositContractMock + assetAddresses.beaconChainDepositContract, // depositContractMock ] ); const cStrategyImpl = await ethers.getContractAt( diff --git a/contracts/docs/FeeAccumulatorSquashed.svg b/contracts/docs/FeeAccumulatorSquashed.svg new file mode 100644 index 0000000000..3c2c1558ab --- /dev/null +++ b/contracts/docs/FeeAccumulatorSquashed.svg @@ -0,0 +1,52 @@ + + + + + + +UmlClassDiagram + + + +278 + +FeeAccumulator +../contracts/strategies/NativeStaking/FeeAccumulator.sol + +Private: +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   __gap: uint256[50] <<FeeAccumulator>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   COLLECTOR: address <<FeeAccumulator>> +   WETH_TOKEN_ADDRESS: address <<FeeAccumulator>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _assertIsCollector() <<FeeAccumulator>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    collect(): (weth: uint256) <<FeeAccumulator>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_collector: address, _weth: address) <<FeeAccumulator>> + + + diff --git a/contracts/docs/NativeStakingSSVStrategyHierarchy.svg b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg new file mode 100644 index 0000000000..3007a5a443 --- /dev/null +++ b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg @@ -0,0 +1,142 @@ + + + + + + +UmlClassDiagram + + + +20 + +Governable +../contracts/governance/Governable.sol + + + +278 + +FeeAccumulator +../contracts/strategies/NativeStaking/FeeAccumulator.sol + + + +278->20 + + + + + +280 + +NativeStakingSSVStrategy +../contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol + + + +280->278 + + + + + +281 + +<<Abstract>> +ValidatorAccountant +../contracts/strategies/NativeStaking/ValidatorAccountant.sol + + + +280->281 + + + + + +210 + +<<Abstract>> +InitializableAbstractStrategy +../contracts/utils/InitializableAbstractStrategy.sol + + + +280->210 + + + + + +283 + +<<Abstract>> +ValidatorRegistrator +../contracts/strategies/NativeStaking/ValidatorRegistrator.sol + + + +281->283 + + + + + +283->20 + + + + + +336 + +<<Abstract>> +Pausable +../node_modules/@openzeppelin/contracts/security/Pausable.sol + + + +283->336 + + + + + +209 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol + + + +210->20 + + + + + +210->209 + + + + + +341 + +<<Abstract>> +Context +../node_modules/@openzeppelin/contracts/utils/Context.sol + + + +336->341 + + + + + diff --git a/contracts/docs/NativeStakingSSVStrategySquashed.svg b/contracts/docs/NativeStakingSSVStrategySquashed.svg new file mode 100644 index 0000000000..838bee6efe --- /dev/null +++ b/contracts/docs/NativeStakingSSVStrategySquashed.svg @@ -0,0 +1,154 @@ + + + + + + +UmlClassDiagram + + + +280 + +NativeStakingSSVStrategy +../contracts/strategies/NativeStaking/NativeStakingSSVStrategy.sol + +Private: +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _paused: bool <<Pausable>> +   __gap: uint256[50] <<ValidatorRegistrator>> +   __gap: uint256[50] <<ValidatorAccountant>> +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   _deprecated_platformAddress: address <<InitializableAbstractStrategy>> +   _deprecated_vaultAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardTokenAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardLiquidationThreshold: uint256 <<InitializableAbstractStrategy>> +   _reserved: int256[98] <<InitializableAbstractStrategy>> +   __gap: uint256[50] <<NativeStakingSSVStrategy>> +Internal: +   assetsMapped: address[] <<InitializableAbstractStrategy>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   WETH_TOKEN_ADDRESS: address <<ValidatorRegistrator>> +   BEACON_CHAIN_DEPOSIT_CONTRACT: address <<ValidatorRegistrator>> +   SSV_NETWORK_ADDRESS: address <<ValidatorRegistrator>> +   validatorRegistrator: address <<ValidatorRegistrator>> +   activeDepositedValidators: uint256 <<ValidatorRegistrator>> +   validatorsStates: mapping(bytes32=>VALIDATOR_STATE) <<ValidatorRegistrator>> +   VAULT_ADDRESS: address <<ValidatorAccountant>> +   beaconChainRewardWETH: uint256 <<ValidatorAccountant>> +   fuseIntervalStart: uint256 <<ValidatorAccountant>> +   fuseIntervalEnd: uint256 <<ValidatorAccountant>> +   accountingGovernor: address <<ValidatorAccountant>> +   strategist: address <<ValidatorAccountant>> +   platformAddress: address <<InitializableAbstractStrategy>> +   vaultAddress: address <<InitializableAbstractStrategy>> +   assetToPToken: mapping(address=>address) <<InitializableAbstractStrategy>> +   harvesterAddress: address <<InitializableAbstractStrategy>> +   rewardTokenAddresses: address[] <<InitializableAbstractStrategy>> +   SSV_TOKEN_ADDRESS: address <<NativeStakingSSVStrategy>> +   FEE_ACCUMULATOR_ADDRESS: address <<NativeStakingSSVStrategy>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _msgSender(): address <<Context>> +    _msgData(): bytes <<Context>> +    _pause() <<whenNotPaused>> <<Pausable>> +    _unpause() <<whenPaused>> <<Pausable>> +    _stakeEth(pubkey: bytes, signature: bytes, depositDataRoot: bytes32) <<ValidatorRegistrator>> +    _initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<InitializableAbstractStrategy>> +    _collectRewardTokens() <<NativeStakingSSVStrategy>> +    _setPTokenAddress(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    _abstractSetPToken(_asset: address, address) <<NativeStakingSSVStrategy>> +    _deposit(_asset: address, _amount: uint256) <<NativeStakingSSVStrategy>> +    _withdraw(_recipient: address, _asset: address, _amount: uint256) <<NativeStakingSSVStrategy>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setRegistratorAddress(_address: address) <<onlyGovernor>> <<ValidatorRegistrator>> +    stakeEth(validators: ValidatorStakeData[]) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> +    registerSsvValidator(publicKey: bytes, operatorIds: uint64[], sharesData: bytes, amount: uint256, cluster: Cluster) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> +    exitSsvValidator(publicKey: bytes, operatorIds: uint64[]) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> +    removeSsvValidator(publicKey: bytes, operatorIds: uint64[], cluster: Cluster) <<onlyRegistrator, whenNotPaused>> <<ValidatorRegistrator>> +    setAccountingGovernor(_address: address) <<onlyGovernor>> <<ValidatorAccountant>> +    setStrategist(_address: address) <<onlyGovernor>> <<ValidatorAccountant>> +    setFuseInterval(_fuseIntervalStart: uint256, _fuseIntervalEnd: uint256) <<onlyGovernor>> <<ValidatorAccountant>> +    doAccounting(): (accountingValid: bool) <<onlyRegistrator>> <<ValidatorAccountant>> +    manuallyFixAccounting(_activeDepositedValidators: uint256, _ethToWeth: uint256, _wethToBeSentToVault: uint256, _beaconChainRewardWETH: uint256, _ethThresholdCheck: uint256, _wethThresholdCheck: uint256) <<onlyAccountingGovernor>> <<ValidatorAccountant>> +    collectRewardTokens() <<onlyHarvester, nonReentrant>> <<NativeStakingSSVStrategy>> +    setRewardTokenAddresses(_rewardTokenAddresses: address[]) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    getRewardTokenAddresses(): address[] <<InitializableAbstractStrategy>> +    setPTokenAddress(_asset: address, _pToken: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    removePToken(_assetIndex: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    setHarvesterAddress(_harvesterAddress: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    safeApproveAllTokens() <<NativeStakingSSVStrategy>> +    deposit(_asset: address, _amount: uint256) <<onlyVault, nonReentrant>> <<NativeStakingSSVStrategy>> +    depositAll() <<onlyVault, nonReentrant>> <<NativeStakingSSVStrategy>> +    withdraw(_recipient: address, _asset: address, _amount: uint256) <<onlyVault, nonReentrant>> <<NativeStakingSSVStrategy>> +    withdrawAll() <<onlyVaultOrGovernor, nonReentrant>> <<NativeStakingSSVStrategy>> +    checkBalance(_asset: address): (balance: uint256) <<NativeStakingSSVStrategy>> +    initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<onlyGovernor, initializer>> <<NativeStakingSSVStrategy>> +    pause() <<onlyStrategist>> <<NativeStakingSSVStrategy>> +    depositSSV(operatorIds: uint64[], amount: uint256, cluster: Cluster) <<onlyStrategist>> <<NativeStakingSSVStrategy>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> Paused(account: address) <<Pausable>> +    <<event>> Unpaused(account: address) <<Pausable>> +    <<event>> RegistratorAddressChanged(oldAddress: address, newAddress: address) <<ValidatorRegistrator>> +    <<event>> ETHStaked(pubkey: bytes, amount: uint256, withdrawal_credentials: bytes) <<ValidatorRegistrator>> +    <<event>> SSVValidatorRegistered(pubkey: bytes, operatorIds: uint64[]) <<ValidatorRegistrator>> +    <<event>> SSVValidatorExitInitiated(pubkey: bytes, operatorIds: uint64[]) <<ValidatorRegistrator>> +    <<event>> SSVValidatorExitCompleted(pubkey: bytes, operatorIds: uint64[]) <<ValidatorRegistrator>> +    <<event>> FuseIntervalUpdated(oldStart: uint256, oldEnd: uint256, start: uint256, end: uint256) <<ValidatorAccountant>> +    <<event>> AccuntingFullyWithdrawnValidator(noOfValidators: uint256, remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>> +    <<event>> AccuntingValidatorSlashed(remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>> +    <<event>> AccountingGovernorAddressChanged(oldAddress: address, newAddress: address) <<ValidatorAccountant>> +    <<event>> AccountingBeaconChainRewards(amount: uint256) <<ValidatorAccountant>> +    <<event>> StrategistAddressChanged(oldStrategist: address, newStrategist: address) <<ValidatorAccountant>> +    <<event>> AccountingManuallyFixed(oldActiveDepositedValidators: uint256, activeDepositedValidators: uint256, oldBeaconChainRewardWETH: uint256, beaconChainRewardWETH: uint256, ethToWeth: uint256, wethToBeSentToVault: uint256) <<ValidatorAccountant>> +    <<event>> PTokenAdded(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> PTokenRemoved(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> Deposit(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> Withdrawal(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenCollected(recipient: address, rewardToken: address, amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenAddressesUpdated(_oldAddresses: address[], _newAddresses: address[]) <<InitializableAbstractStrategy>> +    <<event>> HarvesterAddressesUpdated(_oldHarvesterAddress: address, _newHarvesterAddress: address) <<InitializableAbstractStrategy>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> whenNotPaused() <<Pausable>> +    <<modifier>> whenPaused() <<Pausable>> +    <<modifier>> onlyRegistrator() <<ValidatorRegistrator>> +    <<modifier>> onlyAccountingGovernor() <<ValidatorAccountant>> +    <<modifier>> onlyStrategist() <<ValidatorAccountant>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyVault() <<InitializableAbstractStrategy>> +    <<modifier>> onlyHarvester() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernor() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernorOrStrategist() <<InitializableAbstractStrategy>> +    constructor() <<Pausable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    paused(): bool <<Pausable>> +    constructor(_wethAddress: address, _beaconChainDepositContract: address, _ssvNetwork: address) <<ValidatorRegistrator>> +    getWETHBalanceEligibleForStaking(): (_amount: uint256) <<NativeStakingSSVStrategy>> +    constructor(_wethAddress: address, _vaultAddress: address, _beaconChainDepositContract: address, _ssvNetwork: address) <<ValidatorAccountant>> +    constructor(_config: BaseStrategyConfig) <<InitializableAbstractStrategy>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    supportsAsset(_asset: address): bool <<NativeStakingSSVStrategy>> +    constructor(_baseConfig: BaseStrategyConfig, _wethAddress: address, _ssvToken: address, _ssvNetwork: address, _feeAccumulator: address, _beaconChainDepositContract: address) <<NativeStakingSSVStrategy>> + + + diff --git a/contracts/docs/NativeStakingSSVStrategyStorage.svg b/contracts/docs/NativeStakingSSVStrategyStorage.svg new file mode 100644 index 0000000000..41964933b0 --- /dev/null +++ b/contracts/docs/NativeStakingSSVStrategyStorage.svg @@ -0,0 +1,177 @@ + + + + + + +StorageDiagram + + + +3 + +NativeStakingSSVStrategy <<Contract>> + +slot + +0 + +1 + +2 + +3-52 + +53 + +54 + +55 + +56 + +57 + +58-107 + +108 + +109-158 + +159 + +160 + +161 + +162 + +163 + +164 + +165 + +166 + +167-264 + +265-314 + +type: <inherited contract>.variable (bytes) + +unallocated (11) + +address: ValidatorRegistrator.validatorRegistrator (20) + +bool: Pausable._paused (1) + +uint256: ValidatorRegistrator.activeDepositedValidators (32) + +mapping(bytes32=>VALIDATOR_STATE): ValidatorRegistrator.validatorsStates (32) + +uint256[50]: ValidatorRegistrator.__gap (1600) + +uint256: ValidatorAccountant.beaconChainRewardWETH (32) + +uint256: ValidatorAccountant.fuseIntervalStart (32) + +uint256: ValidatorAccountant.fuseIntervalEnd (32) + +unallocated (12) + +address: ValidatorAccountant.accountingGovernor (20) + +unallocated (12) + +address: ValidatorAccountant.strategist (20) + +uint256[50]: ValidatorAccountant.__gap (1600) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_platformAddress (20) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_vaultAddress (20) + +mapping(address=>address): InitializableAbstractStrategy.assetToPToken (32) + +address[]: InitializableAbstractStrategy.assetsMapped (32) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_rewardTokenAddress (20) + +uint256: InitializableAbstractStrategy._deprecated_rewardLiquidationThreshold (32) + +unallocated (12) + +address: InitializableAbstractStrategy.harvesterAddress (20) + +address[]: InitializableAbstractStrategy.rewardTokenAddresses (32) + +int256[98]: InitializableAbstractStrategy._reserved (3136) + +uint256[50]: __gap (1600) + + + +1 + +address[]: assetsMapped <<Array>> +0xaaf4f58de99300cfadc4585755f376d5fa747d5bc561d5bd9d710de1f91bf42d + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:19->1 + + + + + +2 + +address[]: rewardTokenAddresses <<Array>> +0x2da56674729343acc9933752c8c469a244252915242eb6d4c02d11ddd69164a1 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:24->2 + + + + + diff --git a/contracts/docs/generate.sh b/contracts/docs/generate.sh index 1299504db7..69ab22c21c 100644 --- a/contracts/docs/generate.sh +++ b/contracts/docs/generate.sh @@ -82,6 +82,11 @@ sol2uml .. -v -hv -hf -he -hs -hl -hi -b Generalized4626Strategy -o Generalized4 sol2uml .. -s -d 0 -b Generalized4626Strategy -o Generalized4626StrategySquashed.svg sol2uml storage .. -c Generalized4626Strategy -o Generalized4626StrategyStorage.svg --hideExpand ______gap,_reserved,__gap +sol2uml .. -v -hv -hf -he -hs -hl -hi -b NativeStakingSSVStrategy -o NativeStakingSSVStrategyHierarchy.svg +sol2uml .. -s -d 0 -b NativeStakingSSVStrategy -o NativeStakingSSVStrategySquashed.svg +sol2uml .. -s -d 0 -b FeeAccumulator -o FeeAccumulatorSquashed.svg +sol2uml storage .. -c NativeStakingSSVStrategy -o NativeStakingSSVStrategyStorage.svg --hideExpand __gap,______gap,_reserved + sol2uml .. -v -hv -hf -he -hs -hl -hi -b MorphoAaveStrategy -o MorphoAaveStrategyHierarchy.svg sol2uml .. -s -d 0 -b MorphoAaveStrategy -o MorphoAaveStrategySquashed.svg sol2uml storage .. -c MorphoAaveStrategy -o MorphoAaveStrategyStorage.svg --hideExpand ______gap,_reserved diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index a3c4b13836..f969368877 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -1422,7 +1422,7 @@ async function nativeStakingSSVStrategyFixture() { .setAssetDefaultStrategy(weth.address, nativeStakingSSVStrategy.address); } else { const { governorAddr } = await getNamedAccounts(); - const { oethVault, weth, nativeStakingSSVStrategy, strategist } = fixture; + const { oethVault, weth, nativeStakingSSVStrategy } = fixture; const sGovernor = await ethers.provider.getSigner(governorAddr); // Approve Strategy @@ -1454,10 +1454,6 @@ async function nativeStakingSSVStrategyFixture() { await nativeStakingSSVStrategy .connect(sGovernor) .setAccountingGovernor(governorAddr); - - await nativeStakingSSVStrategy - .connect(sGovernor) - .setStrategist(strategist.address); } return fixture; diff --git a/contracts/test/helpers.js b/contracts/test/helpers.js index 62c72dfbd3..ae8f1a05d5 100644 --- a/contracts/test/helpers.js +++ b/contracts/test/helpers.js @@ -428,7 +428,6 @@ const getAssetAddresses = async (deployments) => { SSV: addresses.mainnet.SSV, SSVNetwork: addresses.mainnet.SSVNetwork, beaconChainDepositContract: addresses.mainnet.beaconChainDepositContract, - }; } else { const addressMap = { @@ -476,7 +475,9 @@ const getAssetAddresses = async (deployments) => { BAL: (await deployments.get("MockBAL")).address, SSV: (await deployments.get("MockSSV")).address, SSVNetwork: (await deployments.get("MockSSVNetwork")).address, - beaconChainDepositContract: (await deployments.get("BeaconChainDepositContractMock")).address, + beaconChainDepositContract: ( + await deployments.get("BeaconChainDepositContractMock") + ).address, }; try { diff --git a/contracts/test/strategies/nativeSSVStaking.js b/contracts/test/strategies/nativeSSVStaking.js index 037ff17c8f..910a5d8ba0 100644 --- a/contracts/test/strategies/nativeSSVStaking.js +++ b/contracts/test/strategies/nativeSSVStaking.js @@ -186,16 +186,6 @@ describe("Unit test: Native SSV Staking Strategy", function () { ).to.be.revertedWith("Caller is not the Governor"); }); - it("Only governor can change the strategist", async () => { - const { nativeStakingSSVStrategy, strategist } = fixture; - - await expect( - nativeStakingSSVStrategy - .connect(strategist) - .setStrategist(strategist.address) - ).to.be.revertedWith("Caller is not the Governor"); - }); - it("Change the accounting governor", async () => { const { nativeStakingSSVStrategy, governor, strategist } = fixture; @@ -217,26 +207,6 @@ describe("Unit test: Native SSV Staking Strategy", function () { strategist.address ); }); - - it("Change the strategist", async () => { - const { nativeStakingSSVStrategy, governor, strategist } = fixture; - - const tx = await nativeStakingSSVStrategy - .connect(governor) - .setStrategist(governor.address); - - const events = (await tx.wait()).events || []; - const strategistAddressChanged = events.find( - (e) => e.event === "StrategistAddressChanged" - ); - - expect(strategistAddressChanged).to.not.be.undefined; - expect(strategistAddressChanged.event).to.equal( - "StrategistAddressChanged" - ); - expect(strategistAddressChanged.args[0]).to.equal(strategist.address); - expect(strategistAddressChanged.args[1]).to.equal(governor.address); - }); }); describe("Accounting", function () { @@ -345,7 +315,7 @@ describe("Unit test: Native SSV Staking Strategy", function () { } const WithdrawnEvent = events.find( - (e) => e.event === "AccuntingFullyWithdrawnValidator" + (e) => e.event === "AccountingFullyWithdrawnValidator" ); if (expectedValidatorsFullWithdrawals > 0) { expect(WithdrawnEvent).to.not.be.undefined; @@ -374,7 +344,7 @@ describe("Unit test: Native SSV Staking Strategy", function () { } const SlashEvent = events.find( - (e) => e.event === "AccuntingValidatorSlashed" + (e) => e.event === "AccountingValidatorSlashed" ); if (slashDetected) { expect(SlashEvent).to.not.be.undefined; @@ -567,7 +537,7 @@ describe("Unit test: Native SSV Staking Strategy", function () { const { nativeStakingSSVStrategy, governor, - strategist, + // strategist, oethHarvester, weth, josh, @@ -642,7 +612,7 @@ describe("Unit test: Native SSV Staking Strategy", function () { nativeStakingSSVStrategy, governor, strategist, - oethHarvester, + // oethHarvester, weth, josh, } = fixture; @@ -651,11 +621,11 @@ describe("Unit test: Native SSV Staking Strategy", function () { beaconChainRewardEth, wethFromDeposits, expectedEthSentToHarvester, - nrOfActiveDepositedValidators + nrOfActiveDepositedValidators, } = testCase; const feeAccumulatorAddress = await nativeStakingSSVStrategy.FEE_ACCUMULATOR_ADDRESS(); - const sHarvester = await impersonateAndFund(oethHarvester.address); + // const sHarvester = await impersonateAndFund(oethHarvester.address); // setup state if (beaconChainRewardEth.gt(BigNumber.from("0"))) { @@ -690,8 +660,14 @@ describe("Unit test: Native SSV Staking Strategy", function () { // run the accounting await nativeStakingSSVStrategy.connect(governor).doAccounting(); - expect(await nativeStakingSSVStrategy.checkBalance(weth.address)).to.equal( - expectedEthSentToHarvester.add(BigNumber.from(`${nrOfActiveDepositedValidators}`).mul(utils.parseEther("32"))) + expect( + await nativeStakingSSVStrategy.checkBalance(weth.address) + ).to.equal( + expectedEthSentToHarvester.add( + BigNumber.from(`${nrOfActiveDepositedValidators}`).mul( + utils.parseEther("32") + ) + ) ); }); } diff --git a/contracts/test/strategies/nativeSsvStaking.fork-test.js b/contracts/test/strategies/nativeSsvStaking.fork-test.js index f5d6b1512d..3fbd13a3c5 100644 --- a/contracts/test/strategies/nativeSsvStaking.fork-test.js +++ b/contracts/test/strategies/nativeSsvStaking.fork-test.js @@ -1,15 +1,11 @@ -const hre = require("hardhat"); const { expect } = require("chai"); -const { units, oethUnits, isCI } = require("../helpers"); const addresses = require("../../utils/addresses"); const { createFixtureLoader, nativeStakingSSVStrategyFixture, } = require("./../_fixture"); -const { impersonateAndFund } = require("../../utils/signers"); -const { setERC20TokenBalance } = require("../_fund"); const loadFixture = createFixtureLoader(nativeStakingSSVStrategyFixture); @@ -17,7 +13,7 @@ describe("ForkTest: Native SSV Staking Strategy", function () { this.timeout(0); // Retry up to 3 times on CI - this.retries(isCI ? 3 : 0); + // this.retries(isCI ? 3 : 0); let fixture; beforeEach(async () => { @@ -26,7 +22,7 @@ describe("ForkTest: Native SSV Staking Strategy", function () { describe("Initial setup", function () { it("Should verify the initial state", async () => { - const { weth, nativeStakingSSVStrategy } = fixture; + const { nativeStakingSSVStrategy } = fixture; await expect( await nativeStakingSSVStrategy.WETH_TOKEN_ADDRESS() ).to.equal(addresses.mainnet.WETH, "Incorrect WETH address set"); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index adc918518c..36c557e3b1 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -243,7 +243,8 @@ addresses.mainnet.CurveCVXPool = "0xB576491F1E6e5E62f1d8F26062Ee822B40B0E0d4"; // SSV network addresses.mainnet.SSV = "0x9D65fF81a3c488d585bBfb0Bfe3c7707c7917f54"; addresses.mainnet.SSVNetwork = "0xDD9BC35aE942eF0cFa76930954a156B3fF30a4E1"; -addresses.mainnet.beaconChainDepositContract = "0x00000000219ab540356cbb839cbe05303d7705fa"; +addresses.mainnet.beaconChainDepositContract = + "0x00000000219ab540356cbb839cbe05303d7705fa"; // Arbitrum One addresses.arbitrumOne = {};