Skip to content

Commit

Permalink
add operation type for target
Browse files Browse the repository at this point in the history
  • Loading branch information
novaknole committed Aug 20, 2024
1 parent decd4ff commit 918763d
Show file tree
Hide file tree
Showing 10 changed files with 851 additions and 168 deletions.
32 changes: 32 additions & 0 deletions contracts/src/mocks/plugin/CustomExecutorMock.sol
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);
}
}
}
11 changes: 9 additions & 2 deletions contracts/src/mocks/plugin/PluginCloneableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ contract PluginCloneableMockBuild1 is PluginCloneable {
address _target,
uint256 _callId,
IDAO.Action[] memory _actions,
uint256 _allowFailureMap
uint256 _allowFailureMap,
Operation _op
) external returns (bytes[] memory execResults, uint256 failureMap) {
(execResults, failureMap) = _execute(_target, bytes32(_callId), _actions, _allowFailureMap);
(execResults, failureMap) = _execute(
_target,
bytes32(_callId),
_actions,
_allowFailureMap,
_op
);
}
}

Expand Down
11 changes: 9 additions & 2 deletions contracts/src/mocks/plugin/PluginMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@ contract PluginMockBuild1 is Plugin {
address _target,
uint256 _callId,
IDAO.Action[] memory _actions,
uint256 _allowFailureMap
uint256 _allowFailureMap,
Operation _op
) external returns (bytes[] memory execResults, uint256 failureMap) {
(execResults, failureMap) = _execute(_target, bytes32(_callId), _actions, _allowFailureMap);
(execResults, failureMap) = _execute(
_target,
bytes32(_callId),
_actions,
_allowFailureMap,
_op
);
}
}
11 changes: 9 additions & 2 deletions contracts/src/mocks/plugin/PluginUUPSUpgradeableMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ contract PluginUUPSUpgradeableMockBuild1 is PluginUUPSUpgradeable {
address _target,
uint256 _callId,
IDAO.Action[] memory _actions,
uint256 _allowFailureMap
uint256 _allowFailureMap,
Operation _op
) external returns (bytes[] memory execResults, uint256 failureMap) {
(execResults, failureMap) = _execute(_target, bytes32(_callId), _actions, _allowFailureMap);
(execResults, failureMap) = _execute(
_target,
bytes32(_callId),
_actions,
_allowFailureMap,
_op
);
}
}

Expand Down
122 changes: 102 additions & 20 deletions contracts/src/plugin/Plugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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");
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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);
}
}
}
Loading

0 comments on commit 918763d

Please sign in to comment.