-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Extended PM with permission owner and delegation capabilities #604
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1st Round ^_^
…ssionOwner to ownerAuth
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
Co-authored-by: Claudia Barcelo <[email protected]>
* fix more tests and cleaner * test fixes + permissionFrozen for multi target + more fixes * improve natspec
* fix: remove .only * fix: 63/64 rule tests * feat: add tests for undelegate function * fix typechain coverage * fix: tets * feat: add couple of tests --------- Co-authored-by: Giorgi Lagidze <[email protected]>
* fix pm contract wiwth tests * Update packages/contracts/src/core/permission/PermissionManager.sol Co-authored-by: Claudia Barcelo <[email protected]> * Update packages/contracts/src/core/permission/PermissionManager.sol Co-authored-by: Claudia Barcelo <[email protected]> * Update packages/contracts/test/core/permission/permission-manager.ts Co-authored-by: Claudia Barcelo <[email protected]> * add extra tests for delegation * fix checkOwner for delegation * separate tests * more gas efficient approach * efficient gas approach + fix removeOwner with tests * add more tests --------- Co-authored-by: Claudia Barcelo <[email protected]>
* executor interface * add tests for iexecutor * test * action import free level * Update packages/contracts/test/framework/dao/dao-factory.ts Co-authored-by: Claudia Barcelo <[email protected]> * comments removed --------- Co-authored-by: Claudia Barcelo <[email protected]>
* fix tests and typechain + loop * remove comments * add back vscode files * interface fixed
revert PermissionFrozen(_where, _permissionIdOrSelector); | ||
} | ||
|
||
if (!_checkFlags(permission.owners[msg.sender], _flags)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_checkFlags()
=> _flagIncludes(...)
return; | ||
} | ||
|
||
if (_owner != address(1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Please, add a comment explaining why the
address(1)
check - Use a constant, rather than a literal value
- Couldn't we just check
_newOwner == address(1)
, update the flags and early return?
BTW, why does address(1)
need be be checked here?
- Why shouldn't we update the counters for
1
?
address _where, | ||
bytes32 _permissionIdOrSelector, | ||
address _delegatee, | ||
uint256 _flags |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uint256 _flags | |
uint256 _delegateeFlags |
/// @return Whether the permission has been created or not. | ||
/// @return How many grant owners this permission has currently. | ||
/// @return How many revoke owners this permission has currently. | ||
function getPermissionData( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function getPermissionData( | |
function getPermissionStatus( |
/// @param _account The address for which to return the current flags. | ||
/// @return uint256 Returns owner flags. 0 if an `account` is not an owner. | ||
/// @return uint256 Returns delegatee flags. 0 if an `account` is not a delegatee. | ||
function getFlags( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
function getFlags( | |
function getPermissionFlags( |
return (_permission & _flags) == _flags; | ||
} | ||
|
||
/// @notice Checks the permissions for the applyTarget methods used by the plugin setup processor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems outdated or unrelated to what the function does
Permission storage _permission, | ||
address _who, | ||
PermissionLib.Operation _operation, | ||
bool isRoot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Why not calling
_isRoot()
within the function and saving this parameter? The function is not pure anyway - Why isn't the function
view
?
// Permission Identifiers necessary to create and set up the dao. | ||
bytes32 constant ROOT_PERMISSION_ID = keccak256("ROOT_PERMISSION"); | ||
bytes32 constant APPLY_INSTALLATION_PERMISSION_ID = keccak256("APPLY_INSTALLATION_PERMISSION"); | ||
bytes32 constant APPLY_TARGET_PERMISSION_ID = keccak256("APPLY_TARGET_PERMISSION"); | ||
bytes32 constant UPGRADE_DAO_PERMISSION_ID = keccak256("UPGRADE_DAO_PERMISSION"); | ||
bytes32 constant SET_METADATA_PERMISSION_ID = keccak256("SET_METADATA_PERMISSION"); | ||
bytes32 constant REGISTER_STANDARD_CALLBACK_PERMISSION_ID = keccak256( | ||
"REGISTER_STANDARD_CALLBACK_PERMISSION" | ||
); | ||
bytes32 constant SET_TRUSTED_FORWARDER_PERMISSION_ID = keccak256( | ||
"SET_TRUSTED_FORWARDER_PERMISSION" | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't these be read from createdDao.ROOT_PERMISSION_ID()
?
We are introducing a double source of truth here
// Permission Identifiers necessary to create and set up the dao. | ||
bytes32 constant ROOT_PERMISSION_ID = keccak256("ROOT_PERMISSION"); | ||
bytes32 constant APPLY_INSTALLATION_PERMISSION_ID = keccak256("APPLY_INSTALLATION_PERMISSION"); | ||
bytes32 constant APPLY_TARGET_PERMISSION_ID = keccak256("APPLY_TARGET_PERMISSION"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bytes32 constant APPLY_TARGET_PERMISSION_ID = keccak256("APPLY_TARGET_PERMISSION"); | |
bytes32 constant APPLY_PERMISSIONS_PERMISSION_ID = keccak256("APPLY_PERMISSIONS_PERMISSION"); |
/// @inheritdoc IExecutor | ||
function execute( | ||
bytes32 _callId, | ||
Action[] calldata _actions, | ||
uint256 _allowFailureMap | ||
) | ||
external | ||
override | ||
nonReentrant | ||
auth(EXECUTE_PERMISSION_ID) | ||
returns (bytes[] memory execResults, uint256 failureMap) | ||
{ | ||
) external override nonReentrant returns (bytes[] memory execResults, uint256 failureMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggesting a more readable implementation
function transfer(
address to,
uint value
) public auth(ETH_TRANSFER_PERMISSION_ID) returns (bool success) {
// Can be called directly or via execute()
// If you call via execute(), you'll need EXECUTE_PERMISSION as well
if (value == 0) return true;
(success) = to.call{value: value}();
}
function execute(
bytes32 _callId,
IDAO.Action[] calldata _actions,
uint256 _allowFailureMap
)
external
override
nonReentrant
auth(EXECUTE_PERMISSION)
returns (bytes[] memory execResults, uint256 failureMap)
{
if (_actions.length > MAX_ACTIONS) {
revert TooManyActions();
}
uint256 gasBefore;
uint256 gasAfter;
bool hasExecutePermission = isGranted(
address(this),
msg.sender,
EXECUTE_PERMISSION_ID,
msg.data
);
execResults = new bytes[](_actions.length);
for (uint256 i = 0; i < _actions.length; ) {
Action calldata action = _actions[i];
bool success;
bytes memory execResult;
// If action.data is 0 length, it's a native ETH transfer
if (action.data.length == 0) {
gasBefore = gasleft();
// msg.sender will be checked against auth(ETH_TRANSFER_PERMISSION_ID) there
success = transfer(action.to, action.value);
execResult = bytes("");
gasAfter = gasleft();
} else {
// Action call
bool canExecute;
bytes32 permissionIdOrSelector;
bytes32 actionSelector = keccak256(action.data[:4]);
(bool permissionCreated, , ) = getPermissionStatus(action.to, actionSelector);
if (permissionCreated) {
// new permission system
canExecute = isGranted(action.to, msg.sender, actionSelector, action.data);
permissionIdOrSelector = actionSelector;
} else {
// classic permission system
canExecute = hasExecutePermission;
permissionIdOrSelector = EXECUTE_PERMISSION_ID;
}
if (!canExecute) {
revert Unauthorized(action.to, msg.sender, permissionIdOrSelector);
}
// Try as a direct call
gasBefore = gasleft();
(success, execResult) = action.to.call{value: action.value}(action.data);
gasAfter = gasleft();
// Retry as a delegate call
if (!success && action.to == address(this)) {
bytes4 result;
assembly {
result := mload(add(execResult, 32))
}
if (result == Unauthorized.selector || result == UnauthorizedOwner.selector) {
gasBefore = gasleft();
(success, execResult) = action.to.delegatecall(action.data);
gasAfter = gasleft();
}
}
}
// Track the action result
if (!success) {
// Check if failure is allowed
if (!hasBit(_allowFailureMap, uint8(i))) {
revert ActionFailed(i);
}
// Make sure that the action call did not fail because 63/64 of `gasleft()` was insufficient to execute the external call `.to.call` (see [ERC-150](https://eips.ethereum.org/EIPS/eip-150)).
// In specific scenarios, i.e. proposal execution where the last action in the action array is allowed to fail, the account calling `execute` could force-fail this action by setting a gas limit
// where 63/64 is insufficient causing the `.to.call` to fail, but where the remaining 1/64 gas are sufficient to successfully finish the `execute` call.
if (gasAfter < gasBefore / 64) {
revert InsufficientGas();
}
// Store that this action failed.
failureMap = flipBit(failureMap, uint8(i));
}
execResults[i] = execResult;
unchecked {
++i;
}
}
emit Executed({
actor: msg.sender,
callId: _callId,
actions: _actions,
allowFailureMap: _allowFailureMap,
failureMap: failureMap,
execResults: execResults
});
}
* some changes * renaming * renaming
Description
Please include a summary of the change and be sure you follow the contributions rules we do provide here
Task ID: OS-?
Type of change
See the framework lifecycle in
packages/contracts/docs/framework-lifecycle
to decide what kind of change this pull request is.Checklist:
CHANGELOG.md
file in the root folder.DEPLOYMENT_CHECKLIST
file in the root folder.UPDATE_CHECKLIST
file in the root folder.