Skip to content

Commit

Permalink
Merge pull request #248 from PufferFinance/feature/arbitrary-reop-call
Browse files Browse the repository at this point in the history
Custom external call from the ReOp
  • Loading branch information
bxmmm1 authored May 15, 2024
2 parents 75da24f + 92c2672 commit 063c1d1
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 8 deletions.
2 changes: 1 addition & 1 deletion lib/pufETH
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import { Script } from "forge-std/Script.sol";
import { AccessManager } from "openzeppelin/access/manager/AccessManager.sol";
import { Multicall } from "openzeppelin/utils/Multicall.sol";
import { console } from "forge-std/console.sol";
import { AVSContractsRegistry } from "../../src/AVSContractsRegistry.sol";
import { ROLE_ID_AVS_COORDINATOR_ALLOWLISTER, ROLE_ID_DAO } from "pufETHScript/Roles.sol";
import { PufferModuleManager } from "puffer/PufferModuleManager.sol";

/**
* @title GenerateAccessManagerCalldata1
* @author Puffer Finance
* @notice Generates the AccessManager call data to setup the public access
* The returned calldata is queued and executed by the Operations Multisig
* 1. timelock.queueTransaction(address(accessManager), encodedMulticall, 1)
* 2. ... 7 days later ...
* 3. timelock.executeTransaction(address(accessManager), encodedMulticall, 1)
*/
contract GenerateAccessManagerCalldata1 is Script {
function run(address moduleManager, address avsContractsRegistry, address whitelister)
public
pure
returns (bytes memory)
{
bytes[] memory calldatas = new bytes[](4);

bytes4[] memory whitelisterSelectors = new bytes4[](1);
whitelisterSelectors[0] = AVSContractsRegistry.setAvsRegistryCoordinator.selector;

calldatas[0] = abi.encodeWithSelector(
AccessManager.setTargetFunctionRole.selector,
avsContractsRegistry,
whitelisterSelectors,
ROLE_ID_AVS_COORDINATOR_ALLOWLISTER
);

// Whitelister has 1 day timelock to add new coordinators
calldatas[1] = abi.encodeWithSelector(
AccessManager.grantRole.selector,
ROLE_ID_AVS_COORDINATOR_ALLOWLISTER,
whitelister,
1 days // 1 day timelock
);

// The role guardian can cancel
calldatas[2] = abi.encodeWithSelector(
AccessManager.setRoleGuardian.selector, ROLE_ID_AVS_COORDINATOR_ALLOWLISTER, ROLE_ID_DAO
);

bytes4[] memory pufferModuleManagerSelectors = new bytes4[](1);

pufferModuleManagerSelectors[0] = PufferModuleManager.customExternalCall.selector;

calldatas[3] = abi.encodeWithSelector(
AccessManager.setTargetFunctionRole.selector, moduleManager, pufferModuleManagerSelectors, ROLE_ID_DAO
);

bytes memory encodedMulticall = abi.encodeCall(Multicall.multicall, (calldatas));

// console.log("GenerateAccessManagerCallData:");
// console.logBytes(encodedMulticall);

return encodedMulticall;
}
}
9 changes: 8 additions & 1 deletion script/DeployProtocolToMainnet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { PufferDepositor } from "pufETH/PufferDepositor.sol";
import { PufferProtocolDeployment } from "./DeploymentStructs.sol";
import { SetupAccess } from "script/SetupAccess.s.sol";
import { OperationsCoordinator } from "puffer/OperationsCoordinator.sol";
import { AVSContractsRegistry } from "puffer/AVSContractsRegistry.sol";

/**
* // Check that the simulation
Expand Down Expand Up @@ -60,6 +61,8 @@ contract DeployProtocolToMainnet is Script {
ValidatorTicket validatorTicketImplementation;
ERC1967Proxy validatorTicketProxy;

AVSContractsRegistry aVSContractsRegistry;

// Lido
address ST_ETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
address LIDO_WITHDRAWAL_QUEUE = 0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1;
Expand Down Expand Up @@ -153,10 +156,13 @@ contract DeployProtocolToMainnet is Script {
restakingOperatorBeacon =
new UpgradeableBeacon(address(restakingOperatorImplementation), address(accessManager));

aVSContractsRegistry = new AVSContractsRegistry(address(accessManager));

moduleManagerImplementation = new PufferModuleManager({
pufferModuleBeacon: address(pufferModuleBeacon),
restakingOperatorBeacon: address(restakingOperatorBeacon),
pufferProtocol: address(pufferProtocolProxy)
pufferProtocol: address(pufferProtocolProxy),
avsContractsRegistry: aVSContractsRegistry
});

pufferProtocolImplementation = new PufferProtocol({
Expand Down Expand Up @@ -196,6 +202,7 @@ contract DeployProtocolToMainnet is Script {
validatorTicket: address(validatorTicketProxy),
pufferOracle: address(oracle),
operationsCoordinator: address(operationsCoordinator),
aVSContractsRegistry: address(aVSContractsRegistry),
pufferDepositor: PUFFER_DEPOSITOR,
pufferVault: PUFFER_VAULT,
stETH: ST_ETH,
Expand Down
8 changes: 7 additions & 1 deletion script/DeployPuffer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ValidatorTicket } from "puffer/ValidatorTicket.sol";
import { OperationsCoordinator } from "puffer/OperationsCoordinator.sol";
import { PufferOracleV2 } from "puffer/PufferOracleV2.sol";
import { IPufferOracleV2 } from "puffer/interface/IPufferOracleV2.sol";
import { AVSContractsRegistry } from "puffer/AVSContractsRegistry.sol";

/**
* @title DeployPuffer
Expand Down Expand Up @@ -51,6 +52,7 @@ contract DeployPuffer is BaseScript {
UpgradeableBeacon restakingOperatorBeacon;
PufferModuleManager moduleManager;
OperationsCoordinator operationsCoordinator;
AVSContractsRegistry aVSContractsRegistry;

address eigenPodManager;
address delayedWithdrawalRouter;
Expand Down Expand Up @@ -132,10 +134,13 @@ contract DeployPuffer is BaseScript {
restakingOperatorBeacon =
new UpgradeableBeacon(address(restakingOperatorImplementation), address(accessManager));

aVSContractsRegistry = new AVSContractsRegistry(address(accessManager));

moduleManager = new PufferModuleManager({
pufferModuleBeacon: address(pufferModuleBeacon),
restakingOperatorBeacon: address(restakingOperatorBeacon),
pufferProtocol: address(proxy)
pufferProtocol: address(proxy),
avsContractsRegistry: aVSContractsRegistry
});

// Puffer Service implementation
Expand Down Expand Up @@ -183,6 +188,7 @@ contract DeployPuffer is BaseScript {
moduleManager: address(moduleManagerProxy),
pufferOracle: address(oracle),
operationsCoordinator: address(operationsCoordinator),
aVSContractsRegistry: address(aVSContractsRegistry),
timelock: address(0), // overwritten in DeployEverything
stETH: address(0), // overwritten in DeployEverything
pufferVault: address(0), // overwritten in DeployEverything
Expand Down
6 changes: 5 additions & 1 deletion script/DeployPufferModuleManagerImplementation.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BaseScript } from "script/BaseScript.s.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { GuardianModule } from "puffer/GuardianModule.sol";
import { PufferModuleManager } from "puffer/PufferModuleManager.sol";
import { AVSContractsRegistry } from "puffer/AVSContractsRegistry.sol";
import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

/**
Expand All @@ -20,11 +21,14 @@ contract DeployPufferModuleManagerImplementation is Script {
function run() public {
require(block.chainid == 17000, "This script is only for Puffer Holesky testnet");

address accessManager = 0xA6c916f85DAfeb6f726E03a1Ce8d08cf835138fF;

vm.startBroadcast();
PufferModuleManager impl = new PufferModuleManager({
pufferModuleBeacon: 0x5B81A4579f466fB17af4d8CC0ED51256b94c61D4,
restakingOperatorBeacon: 0xa7DC88c059F57ADcE41070cEfEFd31F74649a261,
pufferProtocol: 0x705E27D6A6A0c77081D32C07DbDE5A1E139D3F14
pufferProtocol: 0x705E27D6A6A0c77081D32C07DbDE5A1E139D3F14,
avsContractsRegistry: new AVSContractsRegistry(address(accessManager))
});

UUPSUpgradeable(payable(0xe4695ab93163F91665Ce5b96527408336f070a71)).upgradeToAndCall(address(impl), "");
Expand Down
1 change: 1 addition & 0 deletions script/DeploymentStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct PufferProtocolDeployment {
address validatorTicket;
address pufferOracle;
address operationsCoordinator;
address aVSContractsRegistry;
address pufferDepositor; // from pufETH repository (dependency)
address pufferVault; // from pufETH repository (dependency)
address stETH; // from pufETH repository (dependency)
Expand Down
68 changes: 68 additions & 0 deletions script/MainnetContractMigrations/UpgradeRestakingOperator.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "forge-std/Script.sol";
import { BaseScript } from "script/BaseScript.s.sol";
import { PufferProtocol } from "puffer/PufferProtocol.sol";
import { AccessManager } from "openzeppelin/access/manager/AccessManager.sol";
import { BaseScript } from "script/BaseScript.s.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { PufferModuleManager } from "puffer/PufferModuleManager.sol";
import { AVSContractsRegistry } from "puffer/AVSContractsRegistry.sol";
import { UUPSUpgradeable } from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { UpgradeableBeacon } from "openzeppelin/proxy/beacon/UpgradeableBeacon.sol";
import { RestakingOperator } from "puffer/RestakingOperator.sol";
import { IDelegationManager } from "eigenlayer/interfaces/IDelegationManager.sol";
import { ISlasher } from "eigenlayer/interfaces/ISlasher.sol";
import { GenerateAccessManagerCalldata1 } from "script/AccessManagerMigrations/GenerateAccessManagerCalldata1.s.sol";

/**
* forge script script/UpgradeRestakingOperator.s.sol:UpgradeRestakingOperator --rpc-url=$RPC_URL --private-key $PK
*/
contract UpgradeRestakingOperator is Script {
address DELEGATION_MANAGER = 0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A;
address EIGEN_SLASHER = 0xD92145c07f8Ed1D392c1B88017934E301CC1c3Cd;
address MODULE_MANAGER_PROXY = 0x9E1E4fCb49931df5743e659ad910d331735C3860;
address MODULE_BEACON = 0xdd38A5a7789C74fc7F64556fc772343658EEBb04;
address RESTAKING_OPERATOR_BEACON = 0x6756B856Dd3843C84249a6A31850cB56dB824c4B;
address PUFFER_PROTOCOL = 0xf7b6B32492c2e13799D921E84202450131bd238B;
address DAO = 0xC0896ab1A8cae8c2C1d27d011eb955Cca955580d;
address ACCESS_MANAGER = 0x8c1686069474410E6243425f4a10177a94EBEE11;

function run() public {
require(block.chainid == 1, "This script is only for Puffer Mainnet");
vm.startBroadcast();

AVSContractsRegistry avsRegistry = new AVSContractsRegistry(address(ACCESS_MANAGER));

PufferModuleManager pufferModuleManagerImpl = new PufferModuleManager({
pufferModuleBeacon: MODULE_BEACON,
restakingOperatorBeacon: RESTAKING_OPERATOR_BEACON,
pufferProtocol: PUFFER_PROTOCOL,
avsContractsRegistry: avsRegistry
});

RestakingOperator restakingOperatorImpl = new RestakingOperator({
delegationManager: IDelegationManager(DELEGATION_MANAGER),
slasher: ISlasher(EIGEN_SLASHER),
moduleManager: PufferModuleManager(MODULE_MANAGER_PROXY)
});

bytes memory accessCd =
new GenerateAccessManagerCalldata1().run(MODULE_MANAGER_PROXY, address(avsRegistry), DAO);

bytes memory cd1 = abi.encodeCall(UUPSUpgradeable.upgradeToAndCall, (address(pufferModuleManagerImpl), ""));
bytes memory cd2 = abi.encodeCall(UpgradeableBeacon.upgradeTo, address(restakingOperatorImpl));
bytes memory cd3 = abi.encodeCall(AccessManager.execute, (MODULE_MANAGER_PROXY, cd1));
bytes memory cd4 = abi.encodeCall(AccessManager.execute, (RESTAKING_OPERATOR_BEACON, cd2));

// calldata to execute using the timelock contract. setting the target as the Access Manager
console.logBytes(cd3);
console.logBytes(cd4);
console.logBytes(accessCd);

// AccessManager is the owner of upgradeable beacon for restaking operator & module manager
// AccessManager(ACCESS_MANAGER).execute(MODULE_MANAGER_PROXY, cd1);
// AccessManager(ACCESS_MANAGER).execute(RESTAKING_OPERATOR_BEACON, cd2);
}
}
9 changes: 8 additions & 1 deletion script/SetupAccess.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.0 <0.9.0;
import "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { BaseScript } from "script/BaseScript.s.sol";
import { GenerateAccessManagerCalldata1 } from "script/AccessManagerMigrations/GenerateAccessManagerCalldata1.s.sol";
import { AccessManager } from "openzeppelin/access/manager/AccessManager.sol";
import { Multicall } from "openzeppelin/utils/Multicall.sol";
import { PufferProtocol } from "puffer/PufferProtocol.sol";
Expand Down Expand Up @@ -55,6 +56,11 @@ contract SetupAccess is BaseScript {
// console.logBytes(cd);
(s,) = address(accessManager).call(cd);
require(s, "failed setupAccess GenerateAccessManagerCallData");

cd = new GenerateAccessManagerCalldata1().run(deployment.moduleManager, deployment.aVSContractsRegistry, DAO);
// console.logBytes(cd);
(s,) = address(accessManager).call(cd);
require(s, "failed setupAccess GenerateAccessManagerCalldata1");
}

function _generateAccessCalldata(
Expand Down Expand Up @@ -125,7 +131,7 @@ contract SetupAccess is BaseScript {
bytes[] memory calldatas = new bytes[](3);

// Dao selectors
bytes4[] memory selectors = new bytes4[](11);
bytes4[] memory selectors = new bytes4[](12);
selectors[0] = PufferModuleManager.createNewRestakingOperator.selector;
selectors[1] = PufferModuleManager.callModifyOperatorDetails.selector;
selectors[2] = PufferModuleManager.callOptIntoSlashing.selector;
Expand All @@ -137,6 +143,7 @@ contract SetupAccess is BaseScript {
selectors[8] = PufferModuleManager.callRegisterOperatorToAVSWithChurn.selector;
selectors[9] = PufferModuleManager.callDeregisterOperatorFromAVS.selector;
selectors[10] = PufferModuleManager.callUpdateOperatorAVSSocket.selector;
selectors[11] = PufferModuleManager.customExternalCall.selector;

calldatas[0] = abi.encodeWithSelector(
AccessManager.setTargetFunctionRole.selector, pufferDeployment.moduleManager, selectors, ROLE_ID_DAO
Expand Down
46 changes: 46 additions & 0 deletions src/AVSContractsRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import { AccessManaged } from "openzeppelin/access/manager/AccessManaged.sol";

/**
* @title AVSContractsRegistry
* @author Puffer Finance
* @custom:security-contact [email protected]
*/
contract AVSContractsRegistry is AccessManaged {
event AvsRegistryCoordinatorSet(address indexed avsRegistryCoordinator, bytes4 selector, bool isAllowed);

mapping(address avsRegistryCoordinator => mapping(bytes4 selector => bool allowed)) internal
_avsRegistryCoordinators;

constructor(address authority) AccessManaged(authority) { }

/**
* @notice Sets the boolean for the AVS registry coordinator contract
* @param avsRegistryCoordinator is the address of the registry coordinator of the AVS
* @param selector is the signature of the function
* @param isAllowed is the boolean value to set if coordinator contract and signature are allowed or not
*/
function setAvsRegistryCoordinator(address avsRegistryCoordinator, bytes4 selector, bool isAllowed)
external
restricted
{
_avsRegistryCoordinators[avsRegistryCoordinator][selector] = isAllowed;
emit AvsRegistryCoordinatorSet(avsRegistryCoordinator, selector, isAllowed);
}

/**
* @notice Returns `true` if the `avsRegistryCoordinator` contract is allowed
*/
function isAllowedRegistryCoordinator(address avsRegistryCoordinator, bytes calldata customCalldata)
external
view
returns (bool)
{
// Extract the function selector (first 4 bytes of customCalldata)
bytes4 selector = bytes4(customCalldata[:4]);

return _avsRegistryCoordinators[avsRegistryCoordinator][selector];
}
}
6 changes: 6 additions & 0 deletions src/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ error Unauthorized();
* @dev Signature "0xe6c4247b"
*/
error InvalidAddress();

/**
* @notice Thrown if the custom call failed
* @dev Signature "0x5515e1c7"
*/
error CustomCallFailed(address target, bytes returnData);
Loading

0 comments on commit 063c1d1

Please sign in to comment.