diff --git a/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol b/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol
index 2f1843ee56..6edcf44f69 100644
--- a/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol
+++ b/contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol
@@ -22,8 +22,6 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
uint256 public fuseIntervalStart = 0;
/// @notice end of fuse interval
uint256 public fuseIntervalEnd = 0;
- /// @notice Governor that can manually correct the accounting
- address public accountingGovernor;
uint256[50] private __gap;
@@ -37,27 +35,14 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
uint256 remainingValidators,
uint256 wethSentToVault
);
- event AccountingGovernorChanged(address newAddress);
event AccountingConsensusRewards(uint256 amount);
event AccountingManuallyFixed(
- uint256 oldActiveDepositedValidators,
- uint256 activeDepositedValidators,
- uint256 oldBeaconChainRewards,
- uint256 beaconChainRewards,
- uint256 ethToWeth,
- uint256 wethToBeSentToVault
+ int256 validatorsDelta,
+ int256 consensusRewardsDelta,
+ uint256 wethToVault
);
- /// @dev Throws if called by any account other than the Accounting Governor
- modifier onlyAccountingGovernor() {
- require(
- msg.sender == accountingGovernor,
- "Caller is not the Accounting Governor"
- );
- _;
- }
-
/// @param _wethAddress Address of the Erc20 WETH Token contract
/// @param _vaultAddress Address of the Vault
/// @param _beaconChainDepositContract Address of the beacon chain deposit contract
@@ -76,11 +61,6 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
)
{}
- function setAccountingGovernor(address _address) external onlyGovernor {
- emit AccountingGovernorChanged(_address);
- accountingGovernor = _address;
- }
-
/// @notice set fuse interval values
function setFuseInterval(
uint256 _fuseIntervalStart,
@@ -111,16 +91,28 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
/// 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.
+ /// @return accountingValid true if accounting was successful, false if fuse is blown
/* solhint-enable max-line-length */
function doAccounting()
external
onlyRegistrator
whenNotPaused
returns (bool accountingValid)
+ {
+ // pause the accounting on failure
+ accountingValid = _doAccounting(true);
+ }
+
+ function _doAccounting(bool pauseOnFail)
+ internal
+ returns (bool accountingValid)
{
if (address(this).balance < consensusRewards) {
- // pause and fail the accounting
- _pause();
+ // pause if not already
+ if (pauseOnFail) {
+ _pause();
+ }
+ // fail the accounting
return false;
}
@@ -170,66 +162,66 @@ abstract contract ValidatorAccountant is ValidatorRegistrator {
ethRemaining
);
}
- // Oh no... Fuse is blown. The governor (Multisig not OGV Governor) needs to set the
- // record straight by manually set the accounting values.
+ // Oh no... Fuse is blown. The Strategist needs to adjust the accounting values.
else {
- // will emit a paused event
- _pause();
+ // pause if not already
+ if (pauseOnFail) {
+ _pause();
+ }
+ // fail the accounting
accountingValid = false;
}
}
- /// @dev allow the accounting governor to fix the accounting of this strategy and unpause
- /// @param _activeDepositedValidators the override value of activeDepositedValidators
- /// @param _ethToWeth the amount of ETH to be converted to WETH
- /// @param _wethToBeSentToVault the amount of WETH to be sent to the Vault
- /// @param _consensusRewards the override value for consensusRewards
- /// @param _ethThresholdCheck maximum allowed ETH balance on the contract for the function to run
- /// @param _wethThresholdCheck maximum allowed WETH balance on the contract for the function to run
- /// the above 2 checks are done so transaction doesn't get front run and cause
- /// unexpected behaviour
+ /// @notice Allow the Strategist to fix the accounting of this strategy and unpause.
+ /// @param _validatorsDelta adjust the active validators by plus one, minus one or unchanged with zero
+ /// @param _wethToVaultAmount the amount of WETH to be sent to the Vault
+ /// @param _consensusRewardsDelta adjust the accounted for consensus rewards up or down
function manuallyFixAccounting(
- uint256 _activeDepositedValidators,
- uint256 _ethToWeth,
- uint256 _wethToBeSentToVault,
- uint256 _consensusRewards,
- uint256 _ethThresholdCheck,
- uint256 _wethThresholdCheck
- ) external onlyAccountingGovernor whenPaused {
- uint256 ethBalance = address(this).balance;
- uint256 wethBalance = IWETH9(WETH_TOKEN_ADDRESS).balanceOf(
- address(this)
+ int256 _validatorsDelta,
+ int256 _consensusRewardsDelta,
+ uint256 _wethToVaultAmount
+ ) external onlyStrategist whenPaused {
+ require(
+ _validatorsDelta >= -3 &&
+ _validatorsDelta <= 3 &&
+ // new value must be positive
+ int256(activeDepositedValidators) + _validatorsDelta >= 0,
+ "invalid validatorsDelta"
);
-
require(
- ethBalance <= _ethThresholdCheck &&
- wethBalance <= _wethThresholdCheck,
- "over accounting threshold"
+ _consensusRewardsDelta >= -332 ether &&
+ _consensusRewardsDelta <= 332 ether &&
+ // new value must be positive
+ int256(consensusRewards) + _consensusRewardsDelta >= 0,
+ "invalid consensusRewardsDelta"
);
+ require(_wethToVaultAmount <= 32 ether, "invalid wethToVaultAmount");
emit AccountingManuallyFixed(
- activeDepositedValidators,
- _activeDepositedValidators,
- consensusRewards,
- _consensusRewards,
- _ethToWeth,
- _wethToBeSentToVault
+ _validatorsDelta,
+ _consensusRewardsDelta,
+ _wethToVaultAmount
);
- activeDepositedValidators = _activeDepositedValidators;
- consensusRewards = _consensusRewards;
- if (_ethToWeth > 0) {
- require(_ethToWeth <= ethBalance, "insufficient ETH");
-
- IWETH9(WETH_TOKEN_ADDRESS).deposit{ value: _ethToWeth }();
- }
- if (_wethToBeSentToVault > 0) {
+ activeDepositedValidators = uint256(
+ int256(activeDepositedValidators) + _validatorsDelta
+ );
+ consensusRewards = uint256(
+ int256(consensusRewards) + _consensusRewardsDelta
+ );
+ if (_wethToVaultAmount > 0) {
IWETH9(WETH_TOKEN_ADDRESS).transfer(
VAULT_ADDRESS,
- _wethToBeSentToVault
+ _wethToVaultAmount
);
}
+ // rerun the accounting to see if it has now been fixed.
+ // Do not pause the accounting on failure as it is already paused
+ require(_doAccounting(false), "fuse still blown");
+
+ // unpause since doAccounting was successful
_unpause();
}
}
diff --git a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol
index 30270331f4..b6f61eecd2 100644
--- a/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol
+++ b/contracts/contracts/strategies/NativeStaking/ValidatorRegistrator.sol
@@ -34,7 +34,7 @@ abstract contract ValidatorRegistrator is Governable, Pausable {
/// @notice The number of validators that have 32 (!) ETH actively deposited. When a new deposit
/// to a validator happens this number increases, when a validator exit is detected this number
/// decreases.
- uint256 activeDepositedValidators;
+ uint256 public activeDepositedValidators;
/// @notice State of the validators keccak256(pubKey) => state
mapping(bytes32 => VALIDATOR_STATE) public validatorsStates;
diff --git a/contracts/deploy/mainnet/091_native_ssv_staking.js b/contracts/deploy/mainnet/091_native_ssv_staking.js
index 77cb1135e8..817a620993 100644
--- a/contracts/deploy/mainnet/091_native_ssv_staking.js
+++ b/contracts/deploy/mainnet/091_native_ssv_staking.js
@@ -152,12 +152,6 @@ module.exports = deploymentWithGovernanceProposal(
ethers.utils.parseEther("25.6"),
],
},
- // 5. configure the accounting governor
- {
- contract: cStrategy,
- signature: "setAccountingGovernor(address)",
- args: [deployerAddr], // TODO: change this to the defender action
- },
],
};
}
diff --git a/contracts/docs/NativeStakingSSVStrategyHierarchy.svg b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg
index 3007a5a443..0686fa8c78 100644
--- a/contracts/docs/NativeStakingSSVStrategyHierarchy.svg
+++ b/contracts/docs/NativeStakingSSVStrategyHierarchy.svg
@@ -90,17 +90,17 @@
-
+
-336
+340
<<Abstract>>
Pausable
../node_modules/@openzeppelin/contracts/security/Pausable.sol
-
+
-283->336
+283->340
@@ -124,17 +124,17 @@
-
+
-341
+345
<<Abstract>>
Context
../node_modules/@openzeppelin/contracts/utils/Context.sol
-
+
-336->341
+340->345
diff --git a/contracts/docs/NativeStakingSSVStrategySquashed.svg b/contracts/docs/NativeStakingSSVStrategySquashed.svg
index 392788f64f..e66ffdaf08 100644
--- a/contracts/docs/NativeStakingSSVStrategySquashed.svg
+++ b/contracts/docs/NativeStakingSSVStrategySquashed.svg
@@ -4,133 +4,130 @@
-
-
+
+
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>>
- VAULT_ADDRESS: address <<ValidatorRegistrator>>
- validatorRegistrator: address <<ValidatorRegistrator>>
- activeDepositedValidators: uint256 <<ValidatorRegistrator>>
- validatorsStates: mapping(bytes32=>VALIDATOR_STATE) <<ValidatorRegistrator>>
- MAX_STAKE: uint256 <<ValidatorAccountant>>
- consensusRewards: uint256 <<ValidatorAccountant>>
- fuseIntervalStart: uint256 <<ValidatorAccountant>>
- fuseIntervalEnd: uint256 <<ValidatorAccountant>>
- accountingGovernor: 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>>
- _initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<InitializableAbstractStrategy>>
- _collectRewardTokens() <<whenNotPaused>> <<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:
- <<payable>> null() <<NativeStakingSSVStrategy>>
- transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>>
- claimGovernance() <<Governable>>
- setRegistrator(_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>>
- depositSSV(operatorIds: uint64[], amount: uint256, cluster: Cluster) <<onlyStrategist>> <<ValidatorRegistrator>>
- setAccountingGovernor(_address: address) <<onlyGovernor>> <<ValidatorAccountant>>
- setFuseInterval(_fuseIntervalStart: uint256, _fuseIntervalEnd: uint256) <<onlyGovernor>> <<ValidatorAccountant>>
- doAccounting(): (accountingValid: bool) <<onlyRegistrator, whenNotPaused>> <<ValidatorAccountant>>
- manuallyFixAccounting(_activeDepositedValidators: uint256, _ethToWeth: uint256, _wethToBeSentToVault: uint256, _consensusRewards: uint256, _ethThresholdCheck: uint256, _wethThresholdCheck: uint256) <<onlyAccountingGovernor, whenPaused>> <<ValidatorAccountant>>
- collectRewardTokens() <<onlyHarvester, nonReentrant>> <<InitializableAbstractStrategy>>
- 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>>
-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>> RegistratorChanged(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(start: uint256, end: uint256) <<ValidatorAccountant>>
- <<event>> AccountingFullyWithdrawnValidator(noOfValidators: uint256, remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>>
- <<event>> AccountingValidatorSlashed(remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>>
- <<event>> AccountingGovernorChanged(newAddress: address) <<ValidatorAccountant>>
- <<event>> AccountingConsensusRewards(amount: uint256) <<ValidatorAccountant>>
- <<event>> AccountingManuallyFixed(oldActiveDepositedValidators: uint256, activeDepositedValidators: uint256, oldBeaconChainRewards: uint256, beaconChainRewards: 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>> onlyStrategist() <<ValidatorRegistrator>>
- <<modifier>> onlyAccountingGovernor() <<ValidatorAccountant>>
+
+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>>
+ VAULT_ADDRESS: address <<ValidatorRegistrator>>
+ validatorRegistrator: address <<ValidatorRegistrator>>
+ activeDepositedValidators: uint256 <<ValidatorRegistrator>>
+ validatorsStates: mapping(bytes32=>VALIDATOR_STATE) <<ValidatorRegistrator>>
+ MAX_STAKE: uint256 <<ValidatorAccountant>>
+ consensusRewards: uint256 <<ValidatorAccountant>>
+ fuseIntervalStart: uint256 <<ValidatorAccountant>>
+ fuseIntervalEnd: uint256 <<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>>
+ _doAccounting(pauseOnFail: bool): (accountingValid: bool) <<ValidatorAccountant>>
+ _initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<InitializableAbstractStrategy>>
+ _collectRewardTokens() <<whenNotPaused>> <<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:
+ <<payable>> null() <<NativeStakingSSVStrategy>>
+ transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>>
+ claimGovernance() <<Governable>>
+ setRegistrator(_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>>
+ depositSSV(operatorIds: uint64[], amount: uint256, cluster: Cluster) <<onlyStrategist>> <<ValidatorRegistrator>>
+ setFuseInterval(_fuseIntervalStart: uint256, _fuseIntervalEnd: uint256) <<onlyGovernor>> <<ValidatorAccountant>>
+ doAccounting(): (accountingValid: bool) <<onlyRegistrator, whenNotPaused>> <<ValidatorAccountant>>
+ manuallyFixAccounting(_validatorsDelta: int256, _consensusRewardsDelta: int256, _wethToVaultAmount: uint256) <<onlyStrategist, whenPaused>> <<ValidatorAccountant>>
+ collectRewardTokens() <<onlyHarvester, nonReentrant>> <<InitializableAbstractStrategy>>
+ 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>>
+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>> RegistratorChanged(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(start: uint256, end: uint256) <<ValidatorAccountant>>
+ <<event>> AccountingFullyWithdrawnValidator(noOfValidators: uint256, remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>>
+ <<event>> AccountingValidatorSlashed(remainingValidators: uint256, wethSentToVault: uint256) <<ValidatorAccountant>>
+ <<event>> AccountingConsensusRewards(amount: uint256) <<ValidatorAccountant>>
+ <<event>> AccountingManuallyFixed(validatorsDelta: int256, consensusRewardsDelta: int256, wethToVault: 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>> onlyStrategist() <<ValidatorRegistrator>>
<<modifier>> initializer() <<Initializable>>
<<modifier>> onlyVault() <<InitializableAbstractStrategy>>
<<modifier>> onlyHarvester() <<InitializableAbstractStrategy>>
diff --git a/contracts/docs/NativeStakingSSVStrategyStorage.svg b/contracts/docs/NativeStakingSSVStrategyStorage.svg
index 058cea8538..e5aad7e6a1 100644
--- a/contracts/docs/NativeStakingSSVStrategyStorage.svg
+++ b/contracts/docs/NativeStakingSSVStrategyStorage.svg
@@ -4,84 +4,78 @@
-
-
+
+
StorageDiagram
-
+
3
-
-NativeStakingSSVStrategy <<Contract>>
-
-slot
-
-0
+
+NativeStakingSSVStrategy <<Contract>>
+
+slot
-1
+0
-2
+1
-3-52
+2
-53
+3-52
-54
+53
-55
+54
-56
+55
-57-106
+56-105
-107
+106
-108-157
+107-156
-158
+157
-159
+158
-160
+159
-161
+160
-162
+161
-163
+162
-164
+163
-165
+164
-166-263
+165-262
-264-313
-
-type: <inherited contract>.variable (bytes)
-
-unallocated (11)
-
-address: ValidatorRegistrator.validatorRegistrator (20)
-
-bool: Pausable._paused (1)
+263-312
+
+type: <inherited contract>.variable (bytes)
-uint256: ValidatorRegistrator.activeDepositedValidators (32)
+unallocated (11)
+
+address: ValidatorRegistrator.validatorRegistrator (20)
+
+bool: Pausable._paused (1)
-mapping(bytes32=>VALIDATOR_STATE): ValidatorRegistrator.validatorsStates (32)
+uint256: ValidatorRegistrator.activeDepositedValidators (32)
-uint256[50]: ValidatorRegistrator.__gap (1600)
+mapping(bytes32=>VALIDATOR_STATE): ValidatorRegistrator.validatorsStates (32)
-uint256: ValidatorAccountant.consensusRewards (32)
+uint256[50]: ValidatorRegistrator.__gap (1600)
-uint256: ValidatorAccountant.fuseIntervalStart (32)
+uint256: ValidatorAccountant.consensusRewards (32)
-uint256: ValidatorAccountant.fuseIntervalEnd (32)
+uint256: ValidatorAccountant.fuseIntervalStart (32)
-unallocated (12)
-
-address: ValidatorAccountant.accountingGovernor (20)
+uint256: ValidatorAccountant.fuseIntervalEnd (32)
uint256[50]: ValidatorAccountant.__gap (1600)
@@ -124,48 +118,48 @@
1
-
-address[]: assetsMapped <<Array>>
-0xaadc37b8ba5645e62f4546802db221593a94729ccbfc5a97d01365a88f649878
-
-offset
-
-0
-
-type: variable (bytes)
-
-unallocated (12)
-
-address (20)
+
+address[]: assetsMapped <<Array>>
+0x78fdc8d422c49ced035a9edf18d00d3c6a8d81df210f3e5e448e045e77b41e88
+
+offset
+
+0
+
+type: variable (bytes)
+
+unallocated (12)
+
+address (20)
-3:18->1
-
-
+3:17->1
+
+
2
-
-address[]: rewardTokenAddresses <<Array>>
-0xb29a2b3b6f2ff1b765777a231725941da5072cc4fcc30ac4a2ce09706e8ddeff
-
-offset
-
-0
-
-type: variable (bytes)
-
-unallocated (12)
-
-address (20)
+
+address[]: rewardTokenAddresses <<Array>>
+0xe434dc35da084cf8d7e8186688ea2dacb53db7003d427af3abf351bd9d0a4e8d
+
+offset
+
+0
+
+type: variable (bytes)
+
+unallocated (12)
+
+address (20)
-3:23->2
-
-
+3:22->2
+
+
diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js
index 93c71a49bd..30d342bac3 100644
--- a/contracts/test/_fixture.js
+++ b/contracts/test/_fixture.js
@@ -1583,9 +1583,6 @@ async function nativeStakingSSVStrategyFixture() {
.connect(sGovernor)
.setRegistrator(governorAddr);
- await nativeStakingSSVStrategy
- .connect(sGovernor)
- .setAccountingGovernor(governorAddr);
}
return fixture;
diff --git a/contracts/test/strategies/nativeSSVStaking.js b/contracts/test/strategies/nativeSSVStaking.js
index c176d3fea3..1fd21a0ae6 100644
--- a/contracts/test/strategies/nativeSSVStaking.js
+++ b/contracts/test/strategies/nativeSSVStaking.js
@@ -1,7 +1,10 @@
const { expect } = require("chai");
const { BigNumber } = require("ethers");
const { parseEther } = require("ethers").utils;
-const { setBalance } = require("@nomicfoundation/hardhat-network-helpers");
+const {
+ setBalance,
+ setStorageAt,
+} = require("@nomicfoundation/hardhat-network-helpers");
const { isCI } = require("../helpers");
const { shouldBehaveLikeGovernable } = require("../behaviour/governable");
@@ -57,7 +60,7 @@ describe("Unit test: Native SSV Staking Strategy", function () {
);
const tx = {
to: nativeStakingSSVStrategy.address,
- value: parseEther("2", "ether"),
+ value: parseEther("2"),
};
await expect(signer.sendTransaction(tx)).to.be.revertedWith(
@@ -153,30 +156,6 @@ describe("Unit test: Native SSV Staking Strategy", function () {
.to.emit(nativeStakingSSVStrategy, "FuseIntervalUpdated")
.withArgs(fuseStartBn, fuseEndBn);
});
-
- it("Only accounting governor can call accounting", async () => {});
-
- it("Only governor can change the accounting governor", async () => {
- const { nativeStakingSSVStrategy, strategist } = fixture;
-
- await expect(
- nativeStakingSSVStrategy
- .connect(strategist)
- .setAccountingGovernor(strategist.address)
- ).to.be.revertedWith("Caller is not the Governor");
- });
-
- it("Change the accounting governor", async () => {
- const { nativeStakingSSVStrategy, governor, strategist } = fixture;
-
- const tx = await nativeStakingSSVStrategy
- .connect(governor)
- .setAccountingGovernor(strategist.address);
-
- await expect(tx)
- .to.emit(nativeStakingSSVStrategy, "AccountingGovernorChanged")
- .withArgs(strategist.address);
- });
});
describe("Accounting", function () {
@@ -466,24 +445,18 @@ describe("Unit test: Native SSV Staking Strategy", function () {
} consensus rewards, ${expectedValidatorsFullWithdrawals} withdraws${
fuseBlown ? ", fuse blown" : ""
}${slashDetected ? ", slash detected" : ""}.`, async () => {
- const { nativeStakingSSVStrategy, governor, strategist } = fixture;
+ const { nativeStakingSSVStrategy, governor } = fixture;
// setup state
if (ethBalance.gt(0)) {
await setBalance(nativeStakingSSVStrategy.address, ethBalance);
}
- // pause, so manuallyFixAccounting can be called
- await nativeStakingSSVStrategy.connect(strategist).pause();
- await nativeStakingSSVStrategy
- .connect(governor)
- .manuallyFixAccounting(
- 30, // activeDepositedValidators
- 0, //_ethToWeth
- 0, //_wethToBeSentToVault
- previousConsensusRewards, //_consensusRewards
- parseEther("3000"), //_ethThresholdCheck
- parseEther("3000") //_wethThresholdCheck
- );
+
+ await setActiveDepositedValidators(30, nativeStakingSSVStrategy);
+ await setConsensusRewards(
+ previousConsensusRewards,
+ nativeStakingSSVStrategy
+ );
// check accounting values
const tx = await nativeStakingSSVStrategy
@@ -541,124 +514,214 @@ describe("Unit test: Native SSV Staking Strategy", function () {
}
});
- it("Only accounting governor is allowed to manually fix accounting", async () => {
- const { nativeStakingSSVStrategy, strategist } = fixture;
+ it("Only strategist is allowed to manually fix accounting", async () => {
+ const { nativeStakingSSVStrategy, strategist, governor } = fixture;
await nativeStakingSSVStrategy.connect(strategist).pause();
// unit test fixture sets OUSD governor as accounting governor
await expect(
- nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
- 10, //_activeDepositedValidators
- parseEther("2", "ether"), //_ethToWeth
- parseEther("2", "ether"), //_wethToBeSentToVault
- parseEther("2", "ether"), //_consensusRewards
- parseEther("0", "ether"), //_ethThresholdCheck
- parseEther("0", "ether") //_wethThresholdCheck
+ nativeStakingSSVStrategy.connect(governor).manuallyFixAccounting(
+ 1, //_validatorsDelta
+ parseEther("2"), //_consensusRewardsDelta
+ parseEther("2") //_wethToVault
)
- ).to.be.revertedWith("Caller is not the Accounting Governor");
+ ).to.be.revertedWith("Caller is not the Strategist");
});
it("Accounting needs to be paused in order to call fix accounting function", async () => {
- const { nativeStakingSSVStrategy, governor } = fixture;
+ const { nativeStakingSSVStrategy, strategist } = fixture;
// unit test fixture sets OUSD governor as accounting governor
await expect(
- nativeStakingSSVStrategy.connect(governor).manuallyFixAccounting(
- 10, //_activeDepositedValidators
- parseEther("2", "ether"), //_ethToWeth
- parseEther("2", "ether"), //_wethToBeSentToVault
- parseEther("2", "ether"), //_beaconChainRewardWETH
- parseEther("1", "ether"), //_ethThresholdCheck
- parseEther("0", "ether") //_wethThresholdCheck
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ 1, //_validatorsDelta
+ parseEther("2"), //_consensusRewardsDelta
+ parseEther("2") //_wethToVault
)
).to.be.revertedWith("Pausable: not paused");
});
- it("Should not execute manual recovery if eth threshold reached", async () => {
- const { nativeStakingSSVStrategy, strategist, governor, josh, weth } =
- fixture;
-
- await setBalance(
- nativeStakingSSVStrategy.address,
- parseEther("6", "ether")
- );
- await weth
- .connect(josh)
- .transfer(nativeStakingSSVStrategy.address, parseEther("5", "ether"));
+ it("Validators delta should not be <-4 or >4 for fix accounting function", async () => {
+ const { nativeStakingSSVStrategy, strategist } = fixture;
await nativeStakingSSVStrategy.connect(strategist).pause();
+
await expect(
- nativeStakingSSVStrategy.connect(governor).manuallyFixAccounting(
- 10, //_activeDepositedValidators
- parseEther("2", "ether"), //_ethToWeth
- parseEther("2", "ether"), //_wethToBeSentToVault
- parseEther("2", "ether"), //_beaconChainRewardWETH
- parseEther("5", "ether"), //_ethThresholdCheck
- parseEther("5", "ether") //_wethThresholdCheck
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ -4, //_validatorsDelta
+ 0, //_consensusRewardsDelta
+ 0 //_wethToVault
)
- ).to.be.revertedWith("over accounting threshold");
+ ).to.be.revertedWith("invalid validatorsDelta");
+
+ await expect(
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ 4, //_validatorsDelta
+ 0, //_consensusRewardsDelta
+ 0 //_wethToVault
+ )
+ ).to.be.revertedWith("invalid validatorsDelta");
});
- it("Should not execute manual recovery if weth threshold reached", async () => {
- const { nativeStakingSSVStrategy, strategist, governor, josh, weth } =
- fixture;
+ it("Consensus rewards delta should not be <-333> and >333 for fix accounting function", async () => {
+ const { nativeStakingSSVStrategy, strategist } = fixture;
- await setBalance(
- nativeStakingSSVStrategy.address,
- parseEther("5", "ether")
- );
- await weth
- .connect(josh)
- .transfer(nativeStakingSSVStrategy.address, parseEther("6", "ether"));
+ await nativeStakingSSVStrategy.connect(strategist).pause();
+
+ await expect(
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ 0, //_validatorsDelta
+ parseEther("-333"), //_consensusRewardsDelta
+ 0 //_wethToVault
+ )
+ ).to.be.revertedWith("invalid consensusRewardsDelta");
+
+ await expect(
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ 0, //_validatorsDelta
+ parseEther("333"), //_consensusRewardsDelta
+ 0 //_wethToVault
+ )
+ ).to.be.revertedWith("invalid consensusRewardsDelta");
+ });
+
+ it("WETH to Vault amount should not be >32 for fix accounting function", async () => {
+ const { nativeStakingSSVStrategy, strategist } = fixture;
await nativeStakingSSVStrategy.connect(strategist).pause();
+
await expect(
- nativeStakingSSVStrategy.connect(governor).manuallyFixAccounting(
- 10, //_activeDepositedValidators
- parseEther("2", "ether"), //_ethToWeth
- parseEther("2", "ether"), //_wethToBeSentToVault
- parseEther("2", "ether"), //_beaconChainRewardWETH
- parseEther("5", "ether"), //_ethThresholdCheck
- parseEther("5", "ether") //_wethThresholdCheck
+ nativeStakingSSVStrategy.connect(strategist).manuallyFixAccounting(
+ 0, //_validatorsDelta
+ 0, //_consensusRewardsDelta
+ parseEther("33") //_wethToVault
)
- ).to.be.revertedWith("over accounting threshold");
+ ).to.be.revertedWith("invalid wethToVaultAmount");
});
- it("Should allow 5/8 governor to recover paused contract and correct the accounting state", async () => {
- const { nativeStakingSSVStrategy, strategist, governor, josh, weth } =
- fixture;
+ describe("Should allow strategist to recover paused contract", async () => {
+ for (const validatorsDelta of [-3, -2, -1, 0, 1, 2, 3]) {
+ it(`by changing validators by ${validatorsDelta}`, async () => {
+ const { nativeStakingSSVStrategy, strategist } = fixture;
- await setBalance(
- nativeStakingSSVStrategy.address,
- parseEther("5", "ether")
- );
- await weth
- .connect(josh)
- .transfer(nativeStakingSSVStrategy.address, parseEther("5", "ether"));
+ await setActiveDepositedValidators(10, nativeStakingSSVStrategy);
- await nativeStakingSSVStrategy.connect(strategist).pause();
- // unit test fixture sets OUSD governor as accounting governor
- const tx = await nativeStakingSSVStrategy
- .connect(governor)
- .manuallyFixAccounting(
- 3, //_activeDepositedValidators
- parseEther("2.1", "ether"), //_ethToWeth
- parseEther("2.2", "ether"), //_wethToBeSentToVault
- parseEther("2.3", "ether"), //_beaconChainRewardWETH
- parseEther("5", "ether"), //_ethThresholdCheck
- parseEther("5", "ether") //_wethThresholdCheck
- );
+ await nativeStakingSSVStrategy.connect(strategist).pause();
+ const activeDepositedValidatorsBefore =
+ await nativeStakingSSVStrategy.activeDepositedValidators();
- expect(tx)
- .to.emit(nativeStakingSSVStrategy, "AccountingManuallyFixed")
- .withArgs(
- 0, // oldActiveDepositedValidators
- 3, // activeDepositedValidators
- 0, // oldBeaconChainRewardWETH
- parseEther("2.3"), // beaconChainRewardWETH
- parseEther("2.1"), // ethToWeth
- parseEther("2.2") // wethToBeSentToVault
- );
+ const tx = await nativeStakingSSVStrategy
+ .connect(strategist)
+ .manuallyFixAccounting(validatorsDelta, 0, 0);
+
+ expect(tx)
+ .to.emit(nativeStakingSSVStrategy, "AccountingManuallyFixed")
+ .withArgs(validatorsDelta, 0, 0);
+
+ expect(
+ await nativeStakingSSVStrategy.activeDepositedValidators()
+ ).to.equal(
+ activeDepositedValidatorsBefore.add(validatorsDelta),
+ "active deposited validators not updated"
+ );
+ });
+ }
+
+ for (const delta of [-332, -320, -1, 0, 1, 320, 332]) {
+ it(`by changing consensus rewards by ${delta}`, async () => {
+ const { nativeStakingSSVStrategy, strategist } = fixture;
+
+ await setBalance(nativeStakingSSVStrategy.address, parseEther("670"));
+ await setConsensusRewards(
+ parseEther("336"),
+ nativeStakingSSVStrategy
+ );
+ await setActiveDepositedValidators(10000, nativeStakingSSVStrategy);
+
+ await nativeStakingSSVStrategy.connect(strategist).pause();
+ const consensusRewardsDelta = parseEther(delta.toString());
+
+ const tx = await nativeStakingSSVStrategy
+ .connect(strategist)
+ .manuallyFixAccounting(0, consensusRewardsDelta, 0);
+
+ expect(tx)
+ .to.emit(nativeStakingSSVStrategy, "AccountingManuallyFixed")
+ .withArgs(0, consensusRewardsDelta, 0);
+
+ expect(await nativeStakingSSVStrategy.consensusRewards()).to.equal(
+ await nativeStakingSSVStrategy.provider.getBalance(
+ nativeStakingSSVStrategy.address
+ ),
+ "consensus rewards matches eth balance"
+ );
+ });
+ }
+
+ it("by sending WETH to vault", async () => {
+ const { nativeStakingSSVStrategy, strategist, josh, weth } = fixture;
+
+ await weth
+ .connect(josh)
+ .transfer(nativeStakingSSVStrategy.address, parseEther("100"));
+
+ for (const wethInEthers of [0, 1, 26, 32]) {
+ await nativeStakingSSVStrategy.connect(strategist).pause();
+ const wethBefore = await weth.balanceOf(
+ nativeStakingSSVStrategy.address
+ );
+ const wethToVault = parseEther(wethInEthers.toString());
+
+ const tx = await nativeStakingSSVStrategy
+ .connect(strategist)
+ .manuallyFixAccounting(0, 0, wethToVault);
+
+ expect(tx)
+ .to.emit(nativeStakingSSVStrategy, "AccountingManuallyFixed")
+ .withArgs(0, 0, wethToVault);
+
+ expect(
+ await weth.balanceOf(nativeStakingSSVStrategy.address)
+ ).to.equal(
+ wethBefore.sub(wethToVault),
+ "consensus rewards not updated"
+ );
+
+ expect(await nativeStakingSSVStrategy.consensusRewards()).to.equal(
+ await nativeStakingSSVStrategy.provider.getBalance(
+ nativeStakingSSVStrategy.address
+ ),
+ "consensus rewards matches eth balance"
+ );
+ }
+ });
+
+ it("by changing all three manuallyFixAccounting delta values", async () => {
+ const { nativeStakingSSVStrategy, strategist, josh, weth } = fixture;
+
+ await setBalance(nativeStakingSSVStrategy.address, parseEther("5"));
+ await weth
+ .connect(josh)
+ .transfer(nativeStakingSSVStrategy.address, parseEther("5"));
+
+ await nativeStakingSSVStrategy.connect(strategist).pause();
+ // unit test fixture sets OUSD governor as accounting governor
+ const tx = await nativeStakingSSVStrategy
+ .connect(strategist)
+ .manuallyFixAccounting(
+ 1, //_validatorsDelta
+ parseEther("2.3"), //_consensusRewardsDeltaDelta
+ parseEther("2.2") //_wethToVault
+ );
+
+ expect(tx)
+ .to.emit(nativeStakingSSVStrategy, "AccountingManuallyFixed")
+ .withArgs(
+ 1, // validatorsDelta
+ parseEther("2.3"), // consensusRewards
+ parseEther("2.2") // wethToVault
+ );
+ });
});
});
@@ -747,8 +810,7 @@ describe("Unit test: Native SSV Staking Strategy", function () {
describe(`given ${testCase.feeAccumulatorEth} execution rewards, ${testCase.consensusRewards} consensus rewards, ${testCase.deposits} deposits and ${nrOfActiveDepositedValidators} validators`, () => {
beforeEach(async () => {
- const { nativeStakingSSVStrategy, governor, strategist, weth, josh } =
- fixture;
+ const { nativeStakingSSVStrategy, governor, weth, josh } = fixture;
const feeAccumulatorAddress =
await nativeStakingSSVStrategy.FEE_ACCUMULATOR_ADDRESS();
@@ -772,17 +834,11 @@ describe("Unit test: Native SSV Staking Strategy", function () {
}
// set the correct amount of staked validators
- await nativeStakingSSVStrategy.connect(strategist).pause();
- await nativeStakingSSVStrategy
- .connect(governor)
- .manuallyFixAccounting(
- nrOfActiveDepositedValidators, // activeDepositedValidators
- parseEther("0"), //_ethToWeth
- parseEther("0"), //_wethToBeSentToVault
- consensusRewards, //_consensusRewards
- parseEther("3000"), //_ethThresholdCheck
- parseEther("3000") //_wethThresholdCheck
- );
+ await setActiveDepositedValidators(
+ nrOfActiveDepositedValidators,
+ nativeStakingSSVStrategy
+ );
+ await setConsensusRewards(consensusRewards, nativeStakingSSVStrategy);
// run the accounting
await nativeStakingSSVStrategy.connect(governor).doAccounting();
@@ -827,3 +883,27 @@ describe("Unit test: Native SSV Staking Strategy", function () {
}
});
});
+
+const setActiveDepositedValidators = async (
+ validators,
+ nativeStakingSSVStrategy
+) => {
+ await setStorageAt(nativeStakingSSVStrategy.address, 52, validators);
+
+ expect(await nativeStakingSSVStrategy.activeDepositedValidators()).to.equal(
+ validators,
+ "validators no set properly"
+ );
+};
+
+const setConsensusRewards = async (
+ consensusRewards,
+ nativeStakingSSVStrategy
+) => {
+ await setStorageAt(nativeStakingSSVStrategy.address, 104, consensusRewards);
+
+ expect(await nativeStakingSSVStrategy.consensusRewards()).to.equal(
+ consensusRewards,
+ "consensusRewards no set properly"
+ );
+};