pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT import "../../core/DaoRegistry.sol"; import "../IExtension.sol"; /** MIT License Copyright (c) 2020 Openlaw Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** * @dev Proxy contract which executes delegated calls to another contract using the EVM * instruction `delegatecall`, the call is triggered via fallback function. * The call is executed in the target contract identified by its address via `implementation` argument. * The success and return data of the delegated call are be returned back to the caller of the proxy. * Only contracts with the ACL Flag: EXECUTOR are allowed to use the proxy delegated call function. * This contract was based on the OpenZeppelin Proxy contract: * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Proxy.sol */ contract ExecutorExtension is IExtension { using Address for address payable; bool public initialized = false; // internally tracks deployment under eip-1167 proxy pattern DaoRegistry public dao; enum AclFlag { EXECUTE } /// @notice Clonable contract must have an empty constructor constructor() {} modifier hasExtensionAccess(AclFlag flag) { require( (address(this) == msg.sender || address(dao) == msg.sender || !initialized || DaoHelper.isInCreationModeAndHasAccess(dao) || dao.hasAdapterAccessToExtension( msg.sender, address(this), uint8(flag) )), "executorExt::accessDenied" ); _; } /** * @notice Initialises the Executor extension to be associated with a DAO * @dev Can only be called once * @param _dao The dao address that will be associated with the new extension. */ function initialize(DaoRegistry _dao, address) external override { require(!initialized, "already initialized"); dao = _dao; initialized = true; } /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internall call site, it will return directly to the external caller. */ function _delegate( address implementation ) internal virtual hasExtensionAccess(AclFlag.EXECUTE) { require( DaoHelper.isNotZeroAddress(implementation), "executorExt: impl address can not be zero" ); require( DaoHelper.isNotReservedAddress(implementation), "executorExt: impl address can not be reserved" ); address daoAddr; bytes memory data = msg.data; assembly { daoAddr := mload(add(data, 36)) } require(daoAddr == address(dao), "wrong dao!"); // solhint-disable-next-line no-inline-assembly assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall( gas(), implementation, 0, calldatasize(), 0, 0 ) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev Delegates the current call to the sender address. * * This function does not return to its internall call site, it will return directly to the external caller. */ function _fallback() internal virtual { _delegate(msg.sender); } /** * @dev Fallback function that delegates calls to the sender address. Will run if no other * function in the contract matches the call data. */ // Only senders with the EXECUTE ACL Flag enabled is allowed to send eth. //slither-disable-next-line locked-ether fallback() external payable { _fallback(); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * is empty. */ // Only senders with the EXECUTE ACL Flag enabled is allowed to send eth. //slither-disable-next-line locked-ether receive() external payable { _fallback(); } }