Skip to content

add function to transfer tokens to operator contracts #266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion script/DeployEigenlayerSlashing.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ contract DeployEigenlayerSlashingScript is Script {
AddressProvider public addressProvider;

address rewardsCoordinator = 0x7750d328b314EfFa365A0402CcfD489B80B0adda;
address avsOperatorManager = 0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a;

function run() external {

vm.startBroadcast();

etherFiNodeImplementation = new EtherFiNode();
etherFiNodesManagerImplementation = new EtherFiNodesManager();
etherFiRestakerImplementation = new EtherFiRestaker(rewardsCoordinator);
etherFiRestakerImplementation = new EtherFiRestaker(rewardsCoordinator, avsOperatorManager);

console2.log("etherFiNode Impl:", address(etherFiNodeImplementation));
console2.log("etherFiNodesManager Impl:", address(etherFiNodesManagerImplementation));
Expand Down
3 changes: 2 additions & 1 deletion script/deploys/DeployEtherFiRestaker.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ contract Deploy is Script {

AddressProvider public addressProvider;
address eigenlayerRewardsCoordinator;
address avsOperatorManager = 0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a;

address admin;

Expand All @@ -29,7 +30,7 @@ contract Deploy is Script {

vm.startBroadcast(deployerPrivateKey);

EtherFiRestaker restaker = EtherFiRestaker(payable(new UUPSProxy(payable(new EtherFiRestaker(eigenlayerRewardsCoordinator)), "")));
EtherFiRestaker restaker = EtherFiRestaker(payable(new UUPSProxy(payable(new EtherFiRestaker(eigenlayerRewardsCoordinator, avsOperatorManager)), "")));
restaker.initialize(
addressProvider.getContractAddress("LiquidityPool"),
addressProvider.getContractAddress("Liquifier")
Expand Down
16 changes: 15 additions & 1 deletion src/EtherFiRestaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "./Liquifier.sol";
import "./LiquidityPool.sol";
import "./interfaces/IAvsOperatorManager.sol";

import "./eigenlayer-interfaces/IStrategyManager.sol";
import "./eigenlayer-interfaces/IDelegationManager.sol";
Expand Down Expand Up @@ -44,6 +45,8 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable,
EnumerableSet.Bytes32Set private withdrawalRootsSet;
mapping(bytes32 => IDelegationManager.Withdrawal) public DEPRECATED_withdrawalRootToWithdrawal;

IAvsOperatorManager public immutable avsOperatorManager;


event QueuedStEthWithdrawals(uint256[] _reqIds);
event CompletedStEthQueuedWithdrawals(uint256[] _reqIds);
Expand All @@ -57,10 +60,12 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable,
error NotRegistered();
error WrongOutput();
error IncorrectCaller();
error InvalidOperatorContract();

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address _rewardsCoordinator) {
constructor(address _rewardsCoordinator, address _avsOperatorManager) {
rewardsCoordinator = IRewardsCoordinator(_rewardsCoordinator);
avsOperatorManager = IAvsOperatorManager(_avsOperatorManager);
_disableInitializers();
}

Expand Down Expand Up @@ -173,6 +178,15 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable,
return shares;
}

// transfer a token to one of our dedicated AvsOperator contracts
function transferTokenToOperator(uint256 operatorId, address token, uint256 amount) external onlyAdmin {

address operator = avsOperatorManager.avsOperators(operatorId);
if (operator == address(0)) revert InvalidOperatorContract();

IERC20(token).transfer(operator, amount);
}

/// queue withdrawals for un-restaking the token
/// Made easy for operators
/// @param token the token to withdraw
Expand Down
54 changes: 54 additions & 0 deletions src/interfaces/IAvsOperatorManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "../eigenlayer-interfaces/IDelegationManager.sol";
import "../eigenlayer-interfaces/IAVSDirectory.sol";

/**
* @title IAvsOperatorManager
* @author ether.fi
*/
interface IAvsOperatorManager {

function avsOperators(uint256 _id) external returns (address);
function registerAsOperator(uint256 _id, address _delegationApprover, uint32 _allocationDelay, string calldata _metaDataURI) external;
function modifyOperatorDetails(uint256 _id, address _delegationApprover) external;
function updateOperatorMetadataURI(uint256 _id, string calldata _metadataURI) external;
function forwardOperatorCall(uint256 _id, address _target, bytes4 _selector, bytes calldata _args) external;
function forwardOperatorCall(uint256 _id, address _target, bytes calldata _input) external;
function adminForwardCall(uint256 _id, address _target, bytes4 _selector, bytes calldata _args) external;
function isValidOperatorCall(uint256 _id, address _target, bytes4 _selector, bytes calldata) external returns (bool);
function isValidAdminCall(address _target, bytes4 _selector, bytes calldata) external view returns (bool);
function updateAllowedOperatorCalls(uint256 _operatorId, address _target, bytes4 _selector, bool _allowed) external;
function updateAllowedAdminCalls(address _target, bytes4 _selector, bool _allowed) external;
function updateAvsNodeRunner(uint256 _id, address _avsNodeRunner) external;
function updateEcdsaSigner(uint256 _id, address _ecdsaSigner) external;
function upgradeEtherFiAvsOperator(address _newImplementation) external;
function instantiateEtherFiAvsOperator(uint256 _nums) external returns (uint256[] memory _ids);
function avsNodeRunner(uint256 _id) external view returns (address);
function ecdsaSigner(uint256 _id) external view returns (address);
function calculateOperatorAVSRegistrationDigestHash(uint256 _id, address _avsServiceManager, bytes32 _salt, uint256 _expiry) external view returns (bytes32);

//---------------------------------------------------------------------------
//----------------------------- Events -----------------------------------
//---------------------------------------------------------------------------

event ForwardedOperatorCall(uint256 indexed id, address indexed target, bytes4 indexed selector, bytes data, address sender);
event CreatedEtherFiAvsOperator(uint256 indexed id, address etherFiAvsOperator);
event ModifiedOperatorDetails(uint256 indexed id, IDelegationManager.OperatorDetails newOperatorDetails);
event UpdatedOperatorMetadataURI(uint256 indexed id, string metadataURI);
event UpdatedAvsNodeRunner(uint256 indexed id, address avsNodeRunner);
event UpdatedEcdsaSigner(uint256 indexed id, address ecdsaSigner);
event AllowedOperatorCallsUpdated(uint256 indexed id, address indexed target, bytes4 indexed selector, bool allowed);
event AllowedAdminCallsUpdated(address indexed target, bytes4 indexed selector, bool allowed);
event AdminUpdated(address indexed admin, bool isAdmin);

//--------------------------------------------------------------------------
//----------------------------- Errors -----------------------------------
//--------------------------------------------------------------------------

error IncorrectRole();
error InvalidOperatorCall();
error InvalidAdminCall();

}
2 changes: 1 addition & 1 deletion test/ContractCodeChecker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract ContractCodeCheckerTest is TestSetup {
EtherFiNode etherFiNodeImplementation = new EtherFiNode();
address etherFiNodeImplAddress = address(0xc5F2764383f93259Fba1D820b894B1DE0d47937e);

EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda));
EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda), address(0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a));
address etherFiRestakerImplAddress = address(0x0052F731a6BEA541843385ffBA408F52B74Cb624);

// Verify bytecode matches between deployed contracts and their implementations
Expand Down
2 changes: 1 addition & 1 deletion test/EigenLayerIntegration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ contract EigenLayerIntegraitonTest is TestSetup, ProofParsing {
EtherFiNode etherFiNodeImplementation = new EtherFiNode();
address etherFiNodeImplAddress = address(0xc5F2764383f93259Fba1D820b894B1DE0d47937e);

EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda));
EtherFiRestaker etherFiRestakerImplementation = new EtherFiRestaker(address(0x7750d328b314EfFa365A0402CcfD489B80B0adda), address(0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a));
address etherFiRestakerImplAddress = address(0x0052F731a6BEA541843385ffBA408F52B74Cb624);

verifyContractByteCodeMatch(etherFiNodesManagerImplAddress, address(etherFiNodesManagerImplementation));
Expand Down
19 changes: 18 additions & 1 deletion test/EtherFiRestaker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ contract EtherFiRestakerTest is TestSetup {
EtherFiRestaker restaker = EtherFiRestaker(payable(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf));
address _claimer = vm.addr(433);

address newRestakerImpl = address(new EtherFiRestaker(address(eigenLayerRewardsCoordinator)));
address newRestakerImpl = address(new EtherFiRestaker(address(eigenLayerRewardsCoordinator), address(avsOperatorManager)));
vm.startPrank(restaker.owner());

restaker.upgradeTo(newRestakerImpl);
Expand All @@ -241,4 +241,21 @@ contract EtherFiRestakerTest is TestSetup {
assertEq(eigenLayerRewardsCoordinator.claimerFor(address(restaker)), _claimer);
}

function test_transferTokenToOperator() public {
EtherFiRestaker restaker = EtherFiRestaker(payable(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf));
address wstETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
address avsOperatorManager = 0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a;

// upgrade to new impl
address newRestakerImpl = address(new EtherFiRestaker(address(eigenLayerRewardsCoordinator), address(avsOperatorManager)));
vm.startPrank(restaker.owner());
restaker.upgradeTo(newRestakerImpl);

uint256 startBalance = stEth.balanceOf(address(restaker));
restaker.transferTokenToOperator(13, wstETH, 10 ether);
uint256 endBalance = stEth.balanceOf(address(restaker));

console2.log("balances:", startBalance, endBalance);
}

}
8 changes: 6 additions & 2 deletions test/TestSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ contract TestSetup is Test, ContractCodeChecker, DepositDataGeneration {
EtherFiTimelock public etherFiTimelockInstance;
BucketRateLimiter public bucketRateLimiter;

IAvsOperatorManager public avsOperatorManager;

bool public shouldSetupRoleRegistry = true;

bytes32 root;
Expand Down Expand Up @@ -448,7 +450,7 @@ contract TestSetup is Test, ContractCodeChecker, DepositDataGeneration {
}

function deployEtherFiRestaker() internal {
etherFiRestakerImplementation = new EtherFiRestaker(address(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf));
etherFiRestakerImplementation = new EtherFiRestaker(address(0x1B7a4C3797236A1C37f8741c0Be35c2c72736fFf), address(0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a));
etherFiRestakerProxy = new UUPSProxy(address(etherFiRestakerImplementation), "");
etherFiRestakerInstance = EtherFiRestaker(payable(etherFiRestakerProxy));

Expand Down Expand Up @@ -628,7 +630,7 @@ contract TestSetup is Test, ContractCodeChecker, DepositDataGeneration {
etherFiOracleProxy = new UUPSProxy(address(etherFiOracleImplementation), "");
etherFiOracleInstance = EtherFiOracle(payable(etherFiOracleProxy));

etherFiRestakerImplementation = new EtherFiRestaker(address(0x0));
etherFiRestakerImplementation = new EtherFiRestaker(address(0x0), address(0x0));
etherFiRestakerProxy = new UUPSProxy(address(etherFiRestakerImplementation), "");
etherFiRestakerInstance = EtherFiRestaker(payable(etherFiRestakerProxy));

Expand Down Expand Up @@ -764,6 +766,8 @@ contract TestSetup is Test, ContractCodeChecker, DepositDataGeneration {
auctionInstance.initializeOnUpgrade(address(membershipManagerInstance), 1 ether, address(etherFiAdminInstance), address(nodeOperatorManagerInstance));
membershipNftInstance.initializeOnUpgrade(address(liquidityPoolInstance));

avsOperatorManager = IAvsOperatorManager(0x2093Bbb221f1d8C7c932c32ee28Be6dEe4a37A6a);


// configure eigenlayer dependency differently for mainnet vs testnet because we rely
// on the contracts already deployed by eigenlayer on those chains
Expand Down
Loading