// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@offchainlabs/upgrade-executor/src/IUpgradeExecutor.sol"; contract UpgradeExecutorMock is Initializable, AccessControlUpgradeable, ReentrancyGuard, IUpgradeExecutor { using Address for address; bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); /// @notice Emitted when an upgrade execution occurs event UpgradeExecuted(address indexed upgrade, uint256 value, bytes data); /// @notice Emitted when target call occurs event TargetCallExecuted(address indexed target, uint256 value, bytes data); constructor() initializer {} /// @notice Initialise the upgrade executor /// @param admin The admin who can update other roles, and itself - ADMIN_ROLE /// @param executors Can call the execute function - EXECUTOR_ROLE function initialize(address admin, address[] memory executors) public initializer { require(admin != address(0), "UpgradeExecutor: zero admin"); __AccessControl_init(); _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _setRoleAdmin(EXECUTOR_ROLE, ADMIN_ROLE); _setupRole(ADMIN_ROLE, admin); for (uint256 i = 0; i < executors.length; ++i) { _setupRole(EXECUTOR_ROLE, executors[i]); } } /// @notice Execute an upgrade by delegate calling an upgrade contract /// @dev Only executor can call this. Since we're using a delegatecall here the Upgrade contract /// will have access to the state of this contract - including the roles. Only upgrade contracts /// that do not touch local state should be used. function execute( address upgrade, bytes memory upgradeCallData ) public payable onlyRole(EXECUTOR_ROLE) nonReentrant { // OZ Address library check if the address is a contract and bubble up inner revert reason address(upgrade).functionDelegateCall( upgradeCallData, "UpgradeExecutor: inner delegate call failed without reason" ); emit UpgradeExecuted(upgrade, msg.value, upgradeCallData); } /// @notice Execute an upgrade by directly calling target contract /// @dev Only executor can call this. function executeCall( address target, bytes memory targetCallData ) public payable onlyRole(EXECUTOR_ROLE) nonReentrant { // OZ Address library check if the address is a contract and bubble up inner revert reason address(target).functionCallWithValue( targetCallData, msg.value, "UpgradeExecutor: inner call failed without reason" ); emit TargetCallExecuted(target, msg.value, targetCallData); } }