Skip to content

Commit

Permalink
manuallyFixAccounting now uses delta values and only callable by the …
Browse files Browse the repository at this point in the history
…strategist

manuallyFixAccounting calls doAccounting to check the fuse is still not blown
Removed accountingGovernor
  • Loading branch information
naddison36 committed Apr 26, 2024
1 parent 8419fc7 commit 9a6fcfb
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 413 deletions.
121 changes: 54 additions & 67 deletions contracts/contracts/strategies/NativeStaking/ValidatorAccountant.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -111,16 +91,24 @@ 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)
{
accountingValid = _doAccounting();
}

function _doAccounting() internal returns (bool accountingValid) {
if (address(this).balance < consensusRewards) {
// pause and fail the accounting
_pause();
// pause if not already
if (!paused()) {
_pause();
}
// fail the accounting
return false;
}

Expand Down Expand Up @@ -170,66 +158,65 @@ 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 (!paused()) {
_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 >= -1 &&
_validatorsDelta <= 1 &&
// new value must be positive
int256(activeDepositedValidators) + _validatorsDelta >= 0,
"invalid validatorsDelta"
);

require(
ethBalance <= _ethThresholdCheck &&
wethBalance <= _wethThresholdCheck,
"over accounting threshold"
_consensusRewardsDelta >= -32 ether &&
_consensusRewardsDelta <= 32 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.
require(_doAccounting(), "fuse still blown");

// unpause since doAccounting was successful
_unpause();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 0 additions & 6 deletions contracts/deploy/091_native_ssv_staking.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,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
},
],
};
}
Expand Down
16 changes: 8 additions & 8 deletions contracts/docs/NativeStakingSSVStrategyHierarchy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 9a6fcfb

Please sign in to comment.