Skip to content

Commit

Permalink
Merge pull request #231 from etherfi-protocol/vaibhav/update-oracle
Browse files Browse the repository at this point in the history
Vaibhav/update oracle
  • Loading branch information
vvalecha519 authored Feb 7, 2025
2 parents b5ea356 + b459fb7 commit 2775f41
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 77 deletions.
4 changes: 1 addition & 3 deletions script/deploys/DeployPhaseTwo.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ contract DeployPhaseTwoScript is Script {
acceptableRebaseAprInBps,
postReportWaitTimeInSlots
);

etherFiAdminInstance.updateAdmin(oracleAdminAddress, true);

// etherFiAdminInstance.updateAdmin(oracleAdminAddress, true);
IEtherFiOracle(address(etherFiOracleAddress)).setEtherFiAdmin(address(etherFiAdminInstance));
IWithdrawRequestNFT(address(withdrawRequestNFTAddress)).updateAdmin(address(etherFiAdminInstance), true);

Expand Down
182 changes: 123 additions & 59 deletions src/EtherFiAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";

import "./RoleRegistry.sol";

import "./interfaces/IEtherFiOracle.sol";
import "./interfaces/IStakingManager.sol";
import "./interfaces/IAuctionManager.sol";
Expand All @@ -19,6 +21,19 @@ interface IEtherFiPausable {

contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {

enum TaskType {
ValidatorApproval,
SendExitRequests,
ProcessNodeExit,
MarkBeingSlashed
}

struct TaskStatus {
bool completed;
bool exists;
TaskType taskType;
}

IEtherFiOracle public etherFiOracle;
IStakingManager public stakingManager;
IAuctionManager public auctionManager;
Expand All @@ -27,7 +42,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
IMembershipManager public membershipManager;
IWithdrawRequestNFT public withdrawRequestNft;

mapping(address => bool) public admins;
mapping(address => bool) public DEPRECATED_admins;

uint32 public lastHandledReportRefSlot;
uint32 public lastHandledReportRefBlock;
Expand All @@ -38,11 +53,25 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
uint16 public postReportWaitTimeInSlots;
uint32 public lastAdminExecutionBlock;

mapping(address => bool) public pausers;
mapping(address => bool) public DEPRECATED_pausers;

mapping(bytes32 => TaskStatus) public validatorManagementTaskStatus;
uint16 validatorTaskBatchSize;

RoleRegistry public roleRegistry;

bytes32 public constant ETHERFI_ADMIN_ADMIN_ROLE = keccak256("ETHERFI_ADMIN_ADMIN_ROLE");
bytes32 public constant ETHERFI_ADMIN_TASK_EXECUTOR_ROLE = keccak256("ETHERFI_ADMIN_TASK_EXECUTOR_ROLE");

event AdminUpdated(address _address, bool _isAdmin);
event AdminOperationsExecuted(address indexed _address, bytes32 indexed _reportHash);

event ValidatorManagementTaskCreated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps, TaskType _taskType);
event ValidatorManagementTaskCompleted(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps, TaskType _taskType);
event ValidatorManagementTaskInvalidated(bytes32 indexed _taskHash, bytes32 indexed _reportHash, uint256[] _validators, uint32[] _timestamps,TaskType _taskType);

error IncorrectRole();

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
Expand Down Expand Up @@ -77,7 +106,8 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
// based on the boolean flags
// if true, pause,
// else, unpuase
function pause(bool _etherFiOracle, bool _stakingManager, bool _auctionManager, bool _etherFiNodesManager, bool _liquidityPool, bool _membershipManager) external isPauser() {
function pause(bool _etherFiOracle, bool _stakingManager, bool _auctionManager, bool _etherFiNodesManager, bool _liquidityPool, bool _membershipManager) external {
require(roleRegistry.hasRole(roleRegistry.PROTOCOL_PAUSER(), msg.sender), "Caller is not an pauser");
if (_etherFiOracle && !IEtherFiPausable(address(etherFiOracle)).paused()) {
etherFiOracle.pauseContract();
}
Expand All @@ -103,7 +133,8 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
}
}

function unPause(bool _etherFiOracle, bool _stakingManager, bool _auctionManager, bool _etherFiNodesManager, bool _liquidityPool, bool _membershipManager) external onlyOwner {
function unPause(bool _etherFiOracle, bool _stakingManager, bool _auctionManager, bool _etherFiNodesManager, bool _liquidityPool, bool _membershipManager) external {
require(roleRegistry.hasRole(roleRegistry.PROTOCOL_UNPAUSER(), msg.sender), "Caller is not an unpauser");
if (_etherFiOracle && IEtherFiPausable(address(etherFiOracle)).paused()) {
etherFiOracle.unPauseContract();
}
Expand All @@ -129,6 +160,19 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
}
}

function initializeRoleRegistry(address _roleRegistry) external onlyOwner {
require(address(roleRegistry) == address(0x00), "already initialized");

// TODO: compile list of values in DEPRECATED_pausers to clear out
// TODO: compile list of values in DEPRECATED_admins to clear out
roleRegistry = RoleRegistry(_roleRegistry);
}


function setValidatorTaskBatchSize(uint16 _batchSize) external onlyOwner {
validatorTaskBatchSize = _batchSize;
}

function canExecuteTasks(IEtherFiOracle.OracleReport calldata _report) external view returns (bool) {
bytes32 reportHash = etherFiOracle.generateReportHash(_report);
uint32 current_slot = etherFiOracle.computeSlotAtTimestamp(block.timestamp);
Expand All @@ -140,7 +184,9 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
return true;
}

function executeTasks(IEtherFiOracle.OracleReport calldata _report, bytes[] calldata _pubKey, bytes[] calldata _signature) external isAdmin() {
function executeTasks(IEtherFiOracle.OracleReport calldata _report) external {
if (!roleRegistry.hasRole(ETHERFI_ADMIN_ADMIN_ROLE, msg.sender)) revert IncorrectRole();

bytes32 reportHash = etherFiOracle.generateReportHash(_report);
uint32 current_slot = etherFiOracle.computeSlotAtTimestamp(block.timestamp);
require(etherFiOracle.isConsensusReached(reportHash), "EtherFiAdmin: report didn't reach consensus");
Expand All @@ -152,9 +198,8 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {

_handleAccruedRewards(_report);
_handleProtocolFees(_report);
_handleValidators(_report, _pubKey, _signature);
_handleValidators(reportHash, _report);
_handleWithdrawals(_report);
_handleTargetFundsAllocations(_report);

lastHandledReportRefSlot = _report.refSlotTo;
lastHandledReportRefBlock = _report.refBlockTo;
Expand All @@ -163,12 +208,49 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
emit AdminOperationsExecuted(msg.sender, reportHash);
}

//_timestamp will only be used for TaskType.ProcessNodeExit and pubkeys and signatures will only be used for TaskType.ValidatorApproval
function executeValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] calldata _timestamps, bytes[] calldata _pubKeys, bytes[] calldata _signatures) external {
if (!roleRegistry.hasRole(ETHERFI_ADMIN_TASK_EXECUTOR_ROLE, msg.sender)) revert IncorrectRole();

require(etherFiOracle.isConsensusReached(_reportHash), "EtherFiAdmin: report didn't reach consensus");
bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators, _timestamps));
require(validatorManagementTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist");
require(!validatorManagementTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed");
TaskType taskType = validatorManagementTaskStatus[taskHash].taskType;

if (taskType == TaskType.ValidatorApproval) {
liquidityPool.batchApproveRegistration(_validators, _pubKeys, _signatures);
} else if (taskType == TaskType.SendExitRequests) {
liquidityPool.sendExitRequests(_validators);
} else if (taskType == TaskType.ProcessNodeExit) {
etherFiNodesManager.processNodeExit(_validators, _timestamps);
} else if (taskType == TaskType.MarkBeingSlashed) {
etherFiNodesManager.markBeingSlashed(_validators);
}
validatorManagementTaskStatus[taskHash].completed = true;
emit ValidatorManagementTaskCompleted(taskHash, _reportHash, _validators, _timestamps, taskType);
}

function invalidateValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] calldata _timestamps) external {
if (!roleRegistry.hasRole(ETHERFI_ADMIN_ADMIN_ROLE, msg.sender)) revert IncorrectRole();

bytes32 taskHash = keccak256(abi.encode(_reportHash, _validators, _timestamps));
require(validatorManagementTaskStatus[taskHash].exists, "EtherFiAdmin: task doesn't exist");
require(!validatorManagementTaskStatus[taskHash].completed, "EtherFiAdmin: task already completed");
validatorManagementTaskStatus[taskHash].exists = false;
emit ValidatorManagementTaskInvalidated(taskHash, _reportHash, _validators, _timestamps, validatorManagementTaskStatus[taskHash].taskType);
}

//protocol owns the eth that was distributed to NO and treasury in eigenpods and etherfinodes
function _handleProtocolFees(IEtherFiOracle.OracleReport calldata _report) internal {
require(_report.protocolFees >= 0, "EtherFiAdmin: protocol fees can't be negative");
if(_report.protocolFees == 0) {
return;
}

int128 totalRewards = _report.protocolFees + _report.accruedRewards;
// protocol fees are less than 20% of total rewards
require( _report.protocolFees * 5 <= totalRewards, "EtherFiAdmin: protocol fees exceed total rewards");

liquidityPool.payProtocolFees(uint128(_report.protocolFees));
}

Expand All @@ -182,7 +264,7 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
int256 elapsedTime = 12 seconds * elapsedSlots;

// This guard will be removed in future versions
// Ensure that thew TVL didnt' change too much
// Ensure that the new TVL didnt' change too much
// Check if the absolute change (increment, decrement) in TVL is beyond the threshold variable
// - 5% APR = 0.0137% per day
// - 10% APR = 0.0274% per day
Expand All @@ -197,44 +279,45 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
membershipManager.rebase(_report.accruedRewards);
}

function _handleValidators(IEtherFiOracle.OracleReport calldata _report, bytes[] calldata _pubKey, bytes[] calldata _signature) internal {
// validatorsToApprove
if (_report.validatorsToApprove.length > 0) {
liquidityPool.batchApproveRegistration(_report.validatorsToApprove, _pubKey, _signature);
}
function _enqueueValidatorManagementTask(bytes32 _reportHash, uint256[] calldata _validators, uint32[] memory _timestamps, TaskType taskType) internal {
uint256 numBatches = (_validators.length + validatorTaskBatchSize - 1) / validatorTaskBatchSize;

// liquidityPoolValidatorsToExit
if (_report.liquidityPoolValidatorsToExit.length > 0) {
liquidityPool.sendExitRequests(_report.liquidityPoolValidatorsToExit);
if(_validators.length == 0) {
return;
}

// exitedValidators
if (_report.exitedValidators.length > 0) {
etherFiNodesManager.processNodeExit(_report.exitedValidators, _report.exitedValidatorsExitTimestamps);
for (uint256 i = 0; i < numBatches; i++) {
uint256 start = i * validatorTaskBatchSize;
uint256 end = (i + 1) * validatorTaskBatchSize > _validators.length ? _validators.length : (i + 1) * validatorTaskBatchSize;
uint256 timestampSize = taskType == TaskType.ProcessNodeExit ? end - start : 0;
uint256[] memory batchValidators = new uint256[](end - start);
uint32[] memory batchTimestamps = new uint32[](timestampSize);

for (uint256 j = start; j < end; j++) {
batchValidators[j - start] = _validators[j];
if(taskType == TaskType.ProcessNodeExit) {
batchTimestamps[j - start] = _timestamps[j];
}
}
bytes32 taskHash = keccak256(abi.encode(_reportHash, batchValidators, batchTimestamps));
validatorManagementTaskStatus[taskHash] = TaskStatus({completed: false, exists: true, taskType: taskType});
emit ValidatorManagementTaskCreated(taskHash, _reportHash, batchValidators, batchTimestamps, taskType);
}
}

// slashedValidators
if (_report.slashedValidators.length > 0) {
etherFiNodesManager.markBeingSlashed(_report.slashedValidators);
}
function _handleValidators(bytes32 _reportHash, IEtherFiOracle.OracleReport calldata _report) internal {
uint32[] memory emptyTimestamps = new uint32[](0);
_enqueueValidatorManagementTask(_reportHash, _report.validatorsToApprove, emptyTimestamps, TaskType.ValidatorApproval);
_enqueueValidatorManagementTask(_reportHash, _report.liquidityPoolValidatorsToExit, emptyTimestamps, TaskType.SendExitRequests);
_enqueueValidatorManagementTask(_reportHash, _report.exitedValidators, _report.exitedValidatorsExitTimestamps, TaskType.ProcessNodeExit);
_enqueueValidatorManagementTask(_reportHash, _report.slashedValidators, emptyTimestamps, TaskType.MarkBeingSlashed);
}

function _handleWithdrawals(IEtherFiOracle.OracleReport calldata _report) internal {
for (uint256 i = 0; i < _report.withdrawalRequestsToInvalidate.length; i++) {
withdrawRequestNft.invalidateRequest(_report.withdrawalRequestsToInvalidate[i]);
}
withdrawRequestNft.finalizeRequests(_report.lastFinalizedWithdrawalRequestId);

liquidityPool.addEthAmountLockedForWithdrawal(_report.finalizedWithdrawalAmount);
}

function _handleTargetFundsAllocations(IEtherFiOracle.OracleReport calldata _report) internal {
// To handle the case when we want to avoid updating the params too often (to save gas fee)
if (_report.eEthTargetAllocationWeight == 0 && _report.etherFanTargetAllocationWeight == 0) {
return;
}
liquidityPool.setStakingTargetWeights(_report.eEthTargetAllocationWeight, _report.etherFanTargetAllocationWeight);
}
}

function slotForNextReportToProcess() public view returns (uint32) {
return (lastHandledReportRefSlot == 0) ? 0 : lastHandledReportRefSlot + 1;
Expand All @@ -244,21 +327,13 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
return (lastHandledReportRefBlock == 0) ? 0 : lastHandledReportRefBlock + 1;
}

function updateAdmin(address _address, bool _isAdmin) external onlyOwner {
admins[_address] = _isAdmin;

emit AdminUpdated(_address, _isAdmin);
}

function updatePauser(address _address, bool _isPauser) external onlyOwner {
pausers[_address] = _isPauser;
}

function updateAcceptableRebaseApr(int32 _acceptableRebaseAprInBps) external onlyOwner {
acceptableRebaseAprInBps = _acceptableRebaseAprInBps;
}

function updatePostReportWaitTimeInSlots(uint16 _postReportWaitTimeInSlots) external isAdmin {
function updatePostReportWaitTimeInSlots(uint16 _postReportWaitTimeInSlots) external {
if (!roleRegistry.hasRole(ETHERFI_ADMIN_ADMIN_ROLE, msg.sender)) revert IncorrectRole();

postReportWaitTimeInSlots = _postReportWaitTimeInSlots;
}

Expand All @@ -267,15 +342,4 @@ contract EtherFiAdmin is Initializable, OwnableUpgradeable, UUPSUpgradeable {
}

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}


modifier isAdmin() {
require(admins[msg.sender] || msg.sender == owner(), "EtherFiAdmin: not an admin");
_;
}

modifier isPauser() {
require(pausers[msg.sender] || msg.sender == owner(), "EtherFiAdmin: not a pauser");
_;
}
}
}
Loading

0 comments on commit 2775f41

Please sign in to comment.