-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
851 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
pragma solidity ^0.8.8; | ||
|
||
import {IDAO} from "../../dao/IDAO.sol"; | ||
|
||
/// @notice A mock DAO that anyone can set permissions in. | ||
/// @dev DO NOT USE IN PRODUCTION! | ||
contract CustomExecutorMock { | ||
error Failed(); | ||
|
||
event Executed( | ||
address indexed actor, | ||
bytes32 callId, | ||
IDAO.Action[] actions, | ||
uint256 allowFailureMap, | ||
uint256 failureMap, | ||
bytes[] execResults | ||
); | ||
|
||
function execute( | ||
bytes32 callId, | ||
IDAO.Action[] memory _actions, | ||
uint256 allowFailureMap | ||
) external returns (bytes[] memory execResults, uint256 failureMap) { | ||
if (callId == bytes32(0)) { | ||
revert Failed(); | ||
} else { | ||
emit Executed(msg.sender, callId, _actions, allowFailureMap, failureMap, execResults); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
pragma solidity ^0.8.8; | ||
|
||
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; | ||
import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; | ||
|
||
import {IProtocolVersion} from "../utils/versioning/IProtocolVersion.sol"; | ||
import {ProtocolVersion} from "../utils/versioning/ProtocolVersion.sol"; | ||
|
@@ -15,10 +16,29 @@ import {IPlugin} from "./IPlugin.sol"; | |
/// @notice An abstract, non-upgradeable contract to inherit from when creating a plugin being deployed via the `new` keyword. | ||
/// @custom:security-contact [email protected] | ||
abstract contract Plugin is IPlugin, ERC165, DaoAuthorizable, ProtocolVersion { | ||
address private target; | ||
using ERC165Checker for address; | ||
|
||
/// @dev Emitted each time the Target is set. | ||
event TargetSet(address indexed previousTarget, address indexed newTarget); | ||
enum Operation { | ||
Call, | ||
DelegateCall | ||
} | ||
|
||
struct TargetConfig { | ||
address target; | ||
Operation operation; | ||
} | ||
|
||
TargetConfig private currentTargetConfig; | ||
|
||
/// @notice Thrown when target is of type 'IDAO', but operation is `delegateCall`. | ||
/// @param targetConfig The target config to update it to. | ||
error InvalidTargetConfig(TargetConfig targetConfig); | ||
|
||
/// @dev Emitted each time the TargetConfig is set. | ||
event TargetSet(TargetConfig previousTargetConfig, TargetConfig newTargetConfig); | ||
|
||
/// @notice Thrown when `delegatecall` fails. | ||
error ExecuteFailed(); | ||
|
||
/// @notice The ID of the permission required to call the `setTarget` function. | ||
bytes32 public constant SET_TARGET_PERMISSION_ID = keccak256("SET_TARGET_PERMISSION"); | ||
|
@@ -32,34 +52,47 @@ abstract contract Plugin is IPlugin, ERC165, DaoAuthorizable, ProtocolVersion { | |
return PluginType.Constructable; | ||
} | ||
|
||
/// @dev Sets the target to a new target (`newTarget`). | ||
/// @param _target The target contract. | ||
function setTarget(address _target) public auth(SET_TARGET_PERMISSION_ID) { | ||
_setTarget(_target); | ||
/// @notice Returns the currently set target contract. | ||
function getTargetConfig() public view returns (TargetConfig memory) { | ||
return currentTargetConfig; | ||
} | ||
|
||
/// @notice Returns the currently set target contract. | ||
function getTarget() public view returns (address) { | ||
return target; | ||
/// @dev Sets the target to a new target (`newTarget`). | ||
/// @param _targetConfig The target Config containing the address and operation type. | ||
function setTargetConfig( | ||
TargetConfig calldata _targetConfig | ||
) public auth(SET_TARGET_PERMISSION_ID) { | ||
_setTargetConfig(_targetConfig); | ||
} | ||
|
||
/// @notice Checks if this or the parent contract supports an interface by its ID. | ||
/// @notice Checks if an interface is supported by this or its parent contract. | ||
/// @param _interfaceId The ID of the interface. | ||
/// @return Returns `true` if the interface is supported. | ||
function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) { | ||
return | ||
_interfaceId == type(IPlugin).interfaceId || | ||
_interfaceId == type(IProtocolVersion).interfaceId || | ||
_interfaceId == this.setTarget.selector ^ this.getTarget.selector || | ||
_interfaceId == this.setTargetConfig.selector ^ this.getTargetConfig.selector || | ||
super.supportsInterface(_interfaceId); | ||
} | ||
|
||
/// @notice Sets the target to a new target (`newTarget`). | ||
/// @param _target The target contract. | ||
function _setTarget(address _target) internal virtual { | ||
address previousTarget = target; | ||
target = _target; | ||
emit TargetSet(previousTarget, _target); | ||
/// @param _targetConfig The target Config containing the address and operation type. | ||
function _setTargetConfig(TargetConfig calldata _targetConfig) internal virtual { | ||
TargetConfig memory previousTargetConfig = currentTargetConfig; | ||
|
||
currentTargetConfig = _targetConfig; | ||
|
||
// safety check to avoid setting dao as `target` with `delegatecall` operation | ||
// as this would not work and cause the plugin to be bricked. | ||
if ( | ||
_targetConfig.target.supportsInterface(type(IDAO).interfaceId) && | ||
_targetConfig.operation == Operation.DelegateCall | ||
) { | ||
revert InvalidTargetConfig(_targetConfig); | ||
} | ||
|
||
emit TargetSet(previousTargetConfig, _targetConfig); | ||
} | ||
|
||
/// @notice Forwards the actions to the currently set `target` for the execution. | ||
|
@@ -73,7 +106,35 @@ abstract contract Plugin is IPlugin, ERC165, DaoAuthorizable, ProtocolVersion { | |
IDAO.Action[] memory _actions, | ||
uint256 _allowFailureMap | ||
) internal virtual returns (bytes[] memory execResults, uint256 failureMap) { | ||
(execResults, failureMap) = IDAO(target).execute(_callId, _actions, _allowFailureMap); | ||
Operation op = currentTargetConfig.operation; | ||
|
||
if (op == Operation.DelegateCall) { | ||
bool success; | ||
bytes memory data; | ||
|
||
(success, data) = currentTargetConfig.target.delegatecall( | ||
abi.encodeCall(IDAO.execute, (_callId, _actions, _allowFailureMap)) | ||
); | ||
|
||
if (!success) { | ||
if (data.length > 0) { | ||
assembly { | ||
let returndata_size := mload(data) | ||
revert(add(32, data), returndata_size) | ||
} | ||
} else { | ||
revert ExecuteFailed(); | ||
} | ||
} | ||
|
||
(execResults, failureMap) = abi.decode(data, (bytes[], uint256)); | ||
} else { | ||
(execResults, failureMap) = IDAO(currentTargetConfig.target).execute( | ||
_callId, | ||
_actions, | ||
_allowFailureMap | ||
); | ||
} | ||
} | ||
|
||
/// @notice Forwards the actions to the `target` for the execution. | ||
|
@@ -87,8 +148,29 @@ abstract contract Plugin is IPlugin, ERC165, DaoAuthorizable, ProtocolVersion { | |
address _target, | ||
bytes32 _callId, | ||
IDAO.Action[] memory _actions, | ||
uint256 _allowFailureMap | ||
uint256 _allowFailureMap, | ||
Operation _op | ||
) internal virtual returns (bytes[] memory execResults, uint256 failureMap) { | ||
(execResults, failureMap) = IDAO(_target).execute(_callId, _actions, _allowFailureMap); | ||
if (_op == Operation.DelegateCall) { | ||
bool success; | ||
bytes memory data; | ||
|
||
(success, data) = _target.delegatecall( | ||
abi.encodeCall(IDAO.execute, (_callId, _actions, _allowFailureMap)) | ||
); | ||
|
||
if (!success) { | ||
if (data.length > 0) { | ||
assembly { | ||
let returndata_size := mload(data) | ||
revert(add(32, data), returndata_size) | ||
} | ||
} else { | ||
revert ExecuteFailed(); | ||
} | ||
} | ||
} else { | ||
(execResults, failureMap) = IDAO(_target).execute(_callId, _actions, _allowFailureMap); | ||
} | ||
} | ||
} |
Oops, something went wrong.