Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/SafeDelegationManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: MIT AND Apache-2.0
pragma solidity 0.8.23;

import { DelegationManager } from "./DelegationManager.sol";
import { Parameter, ReadableTerm } from "./utils/Types.sol";

contract SafeDelegationManager is DelegationManager {
constructor(address _owner) DelegationManager(_owner) {}

mapping(string => address) public termsToEnforcer;

/**
* @dev Decodes readable terms into standard delegation format
*/
modifier _decodeReadableTerms(bytes[] calldata _permissionContexts) {
uint256 batchSize_ = _permissionContexts.length;
bytes[] memory rewrittenContexts_ = new bytes[](batchSize_);

for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) {
// Try to decode as readable format
try abi.decode(_permissionContexts[batchIndex_], (ReadableDelegation[])) returns (ReadableDelegation[] memory readableDelegations_) {
if (readableDelegations_.length == 0) {
rewrittenContexts_[batchIndex_] = _permissionContexts[batchIndex_];
continue;
}

// Convert to standard delegations
Delegation[] memory standardDelegations_ = new Delegation[](readableDelegations_.length);

for (uint256 delegationIndex_; delegationIndex_ < readableDelegations_.length; ++delegationIndex_) {
ReadableDelegation memory readableDelegation_ = readableDelegations_[delegationIndex_];

if (readableDelegation_.readableTerms.length == 0) {
revert("This safe delegation manager does not currently permit unconditional delegations");
}

// Convert readable terms to caveats
Caveat[] memory caveats_ = new Caveat[](readableDelegation_.readableTerms.length);
for (uint256 termIndex_; termIndex_ < readableDelegation_.readableTerms.length; ++termIndex_) {
ReadableTerm memory term_ = readableDelegation_.readableTerms[termIndex_];
address enforcer_ = termsToEnforcer[term_.permissionName];
if (enforcer_ == address(0)) revert("Unknown permission type");

// Convert readable terms to caveats using enforcer's conversion method
ReadableTerm[] memory singleTerm_ = new ReadableTerm[](1);
singleTerm_[0] = term_;
Caveat[] memory convertedCaveats_ = ICaveatEnforcer(enforcer_)._convertReadableTermsToCaveats(singleTerm_);

// Take first caveat since we only passed one term
caveats_[termIndex_] = convertedCaveats_[0];
}

standardDelegations_[delegationIndex_] = Delegation({
delegate: readableDelegation_.delegate,
delegator: readableDelegation_.delegator,
authority: readableDelegation_.authority,
caveats: caveats_,
salt: readableDelegation_.salt,
signature: readableDelegation_.signature
});
}

rewrittenContexts_[batchIndex_] = abi.encode(standardDelegations_);
} catch {
// If decoding as ReadableDelegation fails, assume it's already a standard Delegation
rewrittenContexts_[batchIndex_] = _permissionContexts[batchIndex_];
}
}

_;
}

/**
* @inheritdoc DelegationManager
*/
function redeemDelegations(
bytes[] calldata _permissionContexts,
ModeCode[] calldata _modes,
bytes[] calldata _executionCallDatas
) external override whenNotPaused _decodeReadableTerms {
super.redeemDelegations(_permissionContexts, _modes, _executionCallDatas);
}
}
3 changes: 3 additions & 0 deletions src/enforcers/CaveatEnforcer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ abstract contract CaveatEnforcer is ICaveatEnforcer {
/// @inheritdoc ICaveatEnforcer
function afterAllHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) public virtual { }

/// @inheritdoc ICaveatEnforcer
function _convertReadableTermsToCaveats(ReadableTerm[] memory _readableTerms) internal view virtual returns (Caveat[] memory) { }

/**
* @dev Require the function call to be in single execution mode
*/
Expand Down
9 changes: 9 additions & 0 deletions src/interfaces/ICaveatEnforcer.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// SPDX-License-Identifier: MIT AND Apache-2.0

import { ReadableTerm, Caveat } from "../utils/Types.sol";
pragma solidity 0.8.23;

import { ModeCode } from "../utils/Types.sol";
Expand Down Expand Up @@ -100,4 +102,11 @@ interface ICaveatEnforcer {
address _redeemer
)
external;

/**
* @notice Converts readable terms into caveats
* @param _readableTerms Array of readable terms to convert
* @return Array of converted caveats
*/
function _convertReadableTermsToCaveats(ReadableTerm[] memory _readableTerms) internal view virtual returns (Caveat[] memory);
}
35 changes: 35 additions & 0 deletions src/utils/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,42 @@ struct EIP712Domain {
* @title Delegation
* @notice Struct representing a delegation to give a delegate authority to act on behalf of a delegator.
* @dev `signature` is ignored during delegation hashing so it can be manipulated post signing.
* @dev Can be represented in a human-readable format using ReadableDelegation and ReadableTerm.
*/

/**
* @title ReadableDelegation
* @notice Human-readable version of a Delegation that uses string permission names and parameters.
*/
struct ReadableDelegation {
address delegate;
address delegator;
bytes32 authority;
ReadableTerm[] readableTerms;
uint256 salt;
bytes signature;
}

/**
* @title ReadableTerm
* @notice Human-readable version of a Caveat that uses string permission names and parameters.
*/
struct ReadableTerm {
string permissionName;
Parameter[] parameters;
bytes args;
}

/**
* @title Parameter
* @notice Name-value pair for readable delegation parameters.
*/
struct Parameter {
string name;
string value;
}

/**
struct Delegation {
address delegate;
address delegator;
Expand Down
Loading