diff --git a/.gitmodules b/.gitmodules index 3621929..f4e1d7d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "lib/account-abstraction"] path = lib/account-abstraction url = https://github.com/eth-infinitism/account-abstraction +[submodule "lib/erc7579-implementation"] + path = lib/erc7579-implementation + url = https://github.com/erc7579/erc7579-implementation diff --git a/lib/erc7579-implementation b/lib/erc7579-implementation new file mode 160000 index 0000000..42aa538 --- /dev/null +++ b/lib/erc7579-implementation @@ -0,0 +1 @@ +Subproject commit 42aa538397138e0858bae09d1bd1a1921aa24b8c diff --git a/remappings.txt b/remappings.txt index 10bde85..6ed7311 100644 --- a/remappings.txt +++ b/remappings.txt @@ -5,4 +5,5 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ @solidity-stringutils/=lib/solidity-stringutils/src/ @bytes-utils/=lib/solidity-bytes-utils/contracts/ -@freshCryptoLib/=lib/FreshCryptoLib/solidity/src \ No newline at end of file +@freshCryptoLib/=lib/FreshCryptoLib/solidity/src/ +@erc7579/=lib/erc7579-implementation/src/ \ No newline at end of file diff --git a/src/DeleGatorCore.sol b/src/DeleGatorCore.sol index 71da3f6..3747b66 100644 --- a/src/DeleGatorCore.sol +++ b/src/DeleGatorCore.sol @@ -10,12 +10,15 @@ import { IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/IERC1155 import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { ERC1967Utils } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; +import { ExecutionHelper } from "@erc7579/core/ExecutionHelper.sol"; import { ERC1271Lib } from "./libraries/ERC1271Lib.sol"; import { IDeleGatorCore } from "./interfaces/IDeleGatorCore.sol"; import { IDelegationManager } from "./interfaces/IDelegationManager.sol"; -import { Action, Delegation, PackedUserOperation } from "./utils/Types.sol"; -import { ExecutionLib } from "./libraries/ExecutionLib.sol"; +import { CallType, ExecType, Execution, Delegation, PackedUserOperation, ModeCode } from "./utils/Types.sol"; +import { CALLTYPE_SINGLE, CALLTYPE_BATCH, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "./utils/Constants.sol"; /** * @title DeleGatorCore @@ -24,8 +27,18 @@ import { ExecutionLib } from "./libraries/ExecutionLib.sol"; * @dev DeleGator implementations can inherit this to enable Delegation, ERC4337 and UUPS. * @dev DeleGator implementations MUST use Namespaced Storage to ensure subsequent UUPS implementation updates are safe. */ -abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDeleGatorCore, IERC721Receiver, IERC1155Receiver { +abstract contract DeleGatorCore is + Initializable, + ExecutionHelper, + UUPSUpgradeable, + IERC165, + IDeleGatorCore, + IERC721Receiver, + IERC1155Receiver +{ using MessageHashUtils for bytes32; + using ModeLib for ModeCode; + using ExecutionLib for bytes; ////////////////////////////// State ////////////////////////////// @@ -46,6 +59,9 @@ abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDel /// @dev Emitted when the storage is cleared event ClearedStorage(); + /// @dev Event emitted when prefunding is sent. + event SentPrefund(address indexed sender, uint256 amount, bool success); + ////////////////////////////// Errors ////////////////////////////// /// @dev Error thrown when the caller is not this contract. @@ -60,6 +76,12 @@ abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDel /// @dev Error thrown when the caller is not the delegation manager. error NotDelegationManager(); + /// @dev Error thrown when an execution with an unsupported CallType was made + error UnsupportedCallType(CallType callType); + + /// @dev Error thrown when an execution with an unsupported ExecType was made + error UnsupportedExecType(ExecType execType); + ////////////////////////////// Modifiers ////////////////////////////// /** @@ -112,39 +134,111 @@ abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDel receive() external payable { } /** - * @notice Redeems a delegation on the DelegationManager and executes the specified actions on behalf of the root delegator. + * @notice Redeems a delegation on the DelegationManager and executes the specified executions on behalf of the root delegator. * @param _permissionContexts An array of bytes where each element is made up of an array - * of `Delegation` structs that are used to validate the authority given to execute the corresponding action on the + * of `Delegation` structs that are used to validate the authority given to execute the corresponding execution on the * root delegator, ordered from leaf to root. - * @param _actions An array of `Action` structs representing the actions to be executed. + * @param _modes An array of `ModeCode` structs representing the mode of execiton for each execution callData. + * @param _executionCallDatas An array of `Execution` structs representing the executions to be executed. */ - function redeemDelegation(bytes[] calldata _permissionContexts, Action[] calldata _actions) external onlyEntryPointOrSelf { - delegationManager.redeemDelegation(_permissionContexts, _actions); + function redeemDelegations( + bytes[] calldata _permissionContexts, + ModeCode[] calldata _modes, + bytes[] calldata _executionCallDatas + ) + external + onlyEntryPointOrSelf + { + delegationManager.redeemDelegations(_permissionContexts, _modes, _executionCallDatas); } - /// @inheritdoc IDeleGatorCore - function executeDelegatedAction(Action calldata _action) external onlyDelegationManager { - ExecutionLib._execute(_action); + /** + * @notice Executes an Execution from this contract + * @dev This method is intended to be called through a UserOp which ensures the invoker has sufficient permissions + * @dev This convenience method defeaults to reverting on failure and a single execution. + * @param _execution The Execution to be executed + */ + function execute(Execution calldata _execution) external payable onlyEntryPoint { + _execute(_execution.target, _execution.value, _execution.callData); } /** - * @notice Executes an Action from this contract + * @notice Executes an Execution from this contract + * @dev Related: @erc7579/MSAAdvanced.sol * @dev This method is intended to be called through a UserOp which ensures the invoker has sufficient permissions - * @dev This method reverts if the action fails. - * @param _action the action to execute + * @param _mode The ModeCode for the execution + * @param _executionCalldata The calldata for the execution */ - function execute(Action calldata _action) external onlyEntryPoint { - ExecutionLib._execute(_action); + function execute(ModeCode _mode, bytes calldata _executionCalldata) external payable onlyEntryPoint { + (CallType callType_, ExecType execType_,,) = _mode.decode(); + + // Check if calltype is batch or single + if (callType_ == CALLTYPE_BATCH) { + // destructure executionCallData according to batched exec + Execution[] calldata executions_ = _executionCalldata.decodeBatch(); + // Check if execType is revert or try + if (execType_ == EXECTYPE_DEFAULT) _execute(executions_); + else if (execType_ == EXECTYPE_TRY) _tryExecute(executions_); + else revert UnsupportedExecType(execType_); + } else if (callType_ == CALLTYPE_SINGLE) { + // Destructure executionCallData according to single exec + (address target_, uint256 value_, bytes calldata callData_) = _executionCalldata.decodeSingle(); + // Check if execType is revert or try + if (execType_ == EXECTYPE_DEFAULT) { + _execute(target_, value_, callData_); + } else if (execType_ == EXECTYPE_TRY) { + bytes[] memory returnData_ = new bytes[](1); + bool success_; + (success_, returnData_[0]) = _tryExecute(target_, value_, callData_); + if (!success_) emit TryExecuteUnsuccessful(0, returnData_[0]); + } else { + revert UnsupportedExecType(execType_); + } + } else { + revert UnsupportedCallType(callType_); + } } /** - * @notice This method executes several Actions in order. - * @dev This method is intended to be called through a UserOp which ensures the invoker has sufficient permissions. - * @dev This method reverts if any of the actions fail. - * @param _actions the ordered actions to execute + * @inheritdoc IDeleGatorCore + * @dev Related: @erc7579/MSAAdvanced.sol */ - function executeBatch(Action[] calldata _actions) external onlyEntryPointOrSelf { - ExecutionLib._executeBatch(_actions); + function executeFromExecutor( + ModeCode _mode, + bytes calldata _executionCalldata + ) + external + payable + onlyDelegationManager + returns (bytes[] memory returnData_) + { + (CallType callType_, ExecType execType_,,) = _mode.decode(); + + // Check if calltype is batch or single + if (callType_ == CALLTYPE_BATCH) { + // Destructure executionCallData according to batched exec + Execution[] calldata executions_ = _executionCalldata.decodeBatch(); + // check if execType is revert or try + if (execType_ == EXECTYPE_DEFAULT) returnData_ = _execute(executions_); + else if (execType_ == EXECTYPE_TRY) returnData_ = _tryExecute(executions_); + else revert UnsupportedExecType(execType_); + } else if (callType_ == CALLTYPE_SINGLE) { + // Destructure executionCallData according to single exec + (address target_, uint256 value_, bytes calldata callData_) = _executionCalldata.decodeSingle(); + returnData_ = new bytes[](1); + bool success_; + // check if execType is revert or try + if (execType_ == EXECTYPE_DEFAULT) { + returnData_[0] = _execute(target_, value_, callData_); + } else if (execType_ == EXECTYPE_TRY) { + (success_, returnData_[0]) = _tryExecute(target_, value_, callData_); + if (!success_) emit TryExecuteUnsuccessful(0, returnData_[0]); + } else { + revert UnsupportedExecType(execType_); + } + } else { + revert UnsupportedCallType(callType_); + } } /** @@ -166,7 +260,7 @@ abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDel returns (uint256 validationData_) { validationData_ = _validateUserOpSignature(_userOp, _userOpHash); - ExecutionLib._payPrefund(_missingAccountFunds); + _payPrefund(_missingAccountFunds); } /** @@ -403,4 +497,18 @@ abstract contract DeleGatorCore is Initializable, UUPSUpgradeable, IERC165, IDel return 1; } } + + /** + * @notice Sends the entrypoint (msg.sender) any needed funds for the transaction. + * @param _missingAccountFunds the minimum value this method should send the entrypoint. + * this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster. + */ + function _payPrefund(uint256 _missingAccountFunds) internal { + if (_missingAccountFunds != 0) { + (bool success_,) = payable(msg.sender).call{ value: _missingAccountFunds, gas: type(uint256).max }(""); + (success_); + // Ignore failure (it's EntryPoint's job to verify, not account.) + emit SentPrefund(msg.sender, _missingAccountFunds, success_); + } + } } diff --git a/src/DelegationManager.sol b/src/DelegationManager.sol index c82f770..4f8889a 100644 --- a/src/DelegationManager.sol +++ b/src/DelegationManager.sol @@ -11,7 +11,7 @@ import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; import { ICaveatEnforcer } from "./interfaces/ICaveatEnforcer.sol"; import { IDelegationManager } from "./interfaces/IDelegationManager.sol"; import { IDeleGatorCore } from "./interfaces/IDeleGatorCore.sol"; -import { Action, Delegation, Caveat } from "./utils/Types.sol"; +import { Delegation, Caveat, ModeCode } from "./utils/Types.sol"; import { EncoderLib } from "./libraries/EncoderLib.sol"; import { ERC1271Lib } from "./libraries/ERC1271Lib.sol"; @@ -29,7 +29,7 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 string public constant NAME = "DelegationManager"; /// @dev The full version of the contract - string public constant VERSION = "1.0.0"; + string public constant VERSION = "1.1.0"; /// @dev The version used in the domainSeparator for EIP712 string public constant DOMAIN_VERSION = "1"; @@ -108,30 +108,39 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 } /** - * @notice This method validates the provided permission contexts and executes the action if the caller has authority to do so. + * @notice This method validates the provided permission contexts and executes the execution if the caller has authority to do + * so. * @dev The structure of the _permissionContexts array is determined by the specific Delegation Manager implementation - * If an entry in _permissionsContexts is empty (i.e., its length is 0), it is treated as a self-authorized action. - * @dev The length of _permissionsContexts must match the length of _actions. - * @dev The afterHook calls of all the caveat enforcers are called after the execution of all the actions in the batch. + * If an entry in _permissionsContexts is empty (i.e., its length is 0), it is treated as a self-authorized execution. + * @dev The length of _permissionsContexts must match the length of _executionCallDatas. + * @dev The afterHook calls of all the caveat enforcers are called after the execution of all the executions in the batch. * @dev If any afterHook fails, the entire transaction will revert. * @param _permissionContexts An array of bytes where each element is made up of an array - * of `Delegation` structs that are used to validate the authority given to execute the corresponding action on the + * of `Delegation` structs that are used to validate the authority given to execute the corresponding execution on the * root delegator, ordered from leaf to root. - * @param _actions the array of actions to be executed + * @param _modes the array of modes to execute the related execution callData + * @param _executionCallDatas the array of encoded executions to be executed */ - function redeemDelegation(bytes[] calldata _permissionContexts, Action[] calldata _actions) external whenNotPaused { + function redeemDelegations( + bytes[] calldata _permissionContexts, + ModeCode[] calldata _modes, + bytes[] calldata _executionCallDatas + ) + external + whenNotPaused + { uint256 batchSize_ = _permissionContexts.length; - if (batchSize_ != _actions.length) revert BatchDataLengthMismatch(); + if (batchSize_ != _executionCallDatas.length || batchSize_ != _modes.length) revert BatchDataLengthMismatch(); Delegation[][] memory batchDelegations_ = new Delegation[][](batchSize_); bytes32[][] memory batchDelegationHashes_ = new bytes32[][](batchSize_); - // Validate and process delegations for each action + // Validate and process delegations for each execution for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) { Delegation[] memory delegations_ = abi.decode(_permissionContexts[batchIndex_], (Delegation[])); if (delegations_.length == 0) { - // Special case: If the permissionContext is empty, treat it as a self authorized action + // Special case: If the permissionContext is empty, treat it as a self authorized execution batchDelegations_[batchIndex_] = new Delegation[](0); batchDelegationHashes_[batchIndex_] = new bytes32[](0); } else { @@ -142,7 +151,9 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 batchDelegationHashes_[batchIndex_] = delegationHashes_; // Validate caller - if (delegations_[0].delegate != msg.sender && delegations_[0].delegate != ANY_DELEGATE) revert InvalidDelegate(); + if (delegations_[0].delegate != msg.sender && delegations_[0].delegate != ANY_DELEGATE) { + revert InvalidDelegate(); + } for (uint256 delegationsIndex_; delegationsIndex_ < delegations_.length; ++delegationsIndex_) { Delegation memory delegation_ = delegations_[delegationsIndex_]; @@ -153,22 +164,19 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 revert EmptySignature(); } - // Check if the delegator is an EOA or a contract - address delegator_ = delegation_.delegator; - - if (delegator_.code.length == 0) { + if (delegation_.delegator.code.length == 0) { // Validate delegation if it's an EOA address result_ = ECDSA.recover( MessageHashUtils.toTypedDataHash(getDomainHash(), delegationHashes_[delegationsIndex_]), delegation_.signature ); - if (result_ != delegator_) revert InvalidSignature(); + if (result_ != delegation_.delegator) revert InvalidSignature(); } else { // Validate delegation if it's a contract bytes32 typedDataHash_ = MessageHashUtils.toTypedDataHash(getDomainHash(), delegationHashes_[delegationsIndex_]); - bytes32 result_ = IERC1271(delegator_).isValidSignature(typedDataHash_, delegation_.signature); + bytes32 result_ = IERC1271(delegation_.delegator).isValidSignature(typedDataHash_, delegation_.signature); if (result_ != ERC1271Lib.EIP1271_MAGIC_VALUE) { revert InvalidSignature(); } @@ -202,19 +210,18 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 // beforeHook (leaf to root) for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) { if (batchDelegations_[batchIndex_].length > 0) { - Delegation[] memory delegations_ = batchDelegations_[batchIndex_]; - bytes32[] memory delegationHashes_ = batchDelegationHashes_[batchIndex_]; // Execute beforeHooks - for (uint256 delegationsIndex_; delegationsIndex_ < delegations_.length; ++delegationsIndex_) { - Caveat[] memory caveats_ = delegations_[delegationsIndex_].caveats; + for (uint256 delegationsIndex_; delegationsIndex_ < batchDelegations_[batchIndex_].length; ++delegationsIndex_) { + Caveat[] memory caveats_ = batchDelegations_[batchIndex_][delegationsIndex_].caveats; for (uint256 caveatsIndex_; caveatsIndex_ < caveats_.length; ++caveatsIndex_) { ICaveatEnforcer enforcer_ = ICaveatEnforcer(caveats_[caveatsIndex_].enforcer); enforcer_.beforeHook( caveats_[caveatsIndex_].terms, caveats_[caveatsIndex_].args, - _actions[batchIndex_], - delegationHashes_[delegationsIndex_], - delegations_[delegationsIndex_].delegator, + _modes[batchIndex_], + _executionCallDatas[batchIndex_], + batchDelegationHashes_[batchIndex_][delegationsIndex_], + batchDelegations_[batchIndex_][delegationsIndex_].delegator, msg.sender ); } @@ -225,29 +232,29 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) { if (batchDelegations_[batchIndex_].length == 0) { // special case: If there are no delegations, defer the call to the caller. - IDeleGatorCore(msg.sender).executeDelegatedAction(_actions[batchIndex_]); + IDeleGatorCore(msg.sender).executeFromExecutor(_modes[batchIndex_], _executionCallDatas[batchIndex_]); } else { IDeleGatorCore(batchDelegations_[batchIndex_][batchDelegations_[batchIndex_].length - 1].delegator) - .executeDelegatedAction(_actions[batchIndex_]); + .executeFromExecutor(_modes[batchIndex_], _executionCallDatas[batchIndex_]); } } // afterHook (root to leaf) for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) { if (batchDelegations_[batchIndex_].length > 0) { - Delegation[] memory delegations_ = batchDelegations_[batchIndex_]; - bytes32[] memory delegationHashes_ = batchDelegationHashes_[batchIndex_]; // Execute afterHooks - for (uint256 delegationsIndex_ = delegations_.length; delegationsIndex_ > 0; --delegationsIndex_) { - Caveat[] memory caveats_ = delegations_[delegationsIndex_ - 1].caveats; + for (uint256 delegationsIndex_ = batchDelegations_[batchIndex_].length; delegationsIndex_ > 0; --delegationsIndex_) + { + Caveat[] memory caveats_ = batchDelegations_[batchIndex_][delegationsIndex_ - 1].caveats; for (uint256 caveatsIndex_; caveatsIndex_ < caveats_.length; ++caveatsIndex_) { ICaveatEnforcer enforcer_ = ICaveatEnforcer(caveats_[caveatsIndex_].enforcer); enforcer_.afterHook( caveats_[caveatsIndex_].terms, caveats_[caveatsIndex_].args, - _actions[batchIndex_], - delegationHashes_[delegationsIndex_ - 1], - delegations_[delegationsIndex_ - 1].delegator, + _modes[batchIndex_], + _executionCallDatas[batchIndex_], + batchDelegationHashes_[batchIndex_][delegationsIndex_ - 1], + batchDelegations_[batchIndex_][delegationsIndex_ - 1].delegator, msg.sender ); } @@ -257,10 +264,11 @@ contract DelegationManager is IDelegationManager, Ownable2Step, Pausable, EIP712 for (uint256 batchIndex_; batchIndex_ < batchSize_; ++batchIndex_) { if (batchDelegations_[batchIndex_].length > 0) { - Delegation[] memory delegations_ = batchDelegations_[batchIndex_]; - for (uint256 delegationsIndex_; delegationsIndex_ < delegations_.length; ++delegationsIndex_) { + for (uint256 delegationsIndex_; delegationsIndex_ < batchDelegations_[batchIndex_].length; ++delegationsIndex_) { emit RedeemedDelegation( - delegations_[delegations_.length - 1].delegator, msg.sender, delegations_[delegationsIndex_] + batchDelegations_[batchIndex_][batchDelegations_[batchIndex_].length - 1].delegator, + msg.sender, + batchDelegations_[batchIndex_][delegationsIndex_] ); } } diff --git a/src/HybridDeleGator.sol b/src/HybridDeleGator.sol index b062c43..39bd87f 100644 --- a/src/HybridDeleGator.sol +++ b/src/HybridDeleGator.sol @@ -30,7 +30,7 @@ contract HybridDeleGator is DeleGatorCore, IERC173 { ////////////////////////////// State ////////////////////////////// /// @dev The version of the contract - string public constant VERSION = "1.0.0"; + string public constant VERSION = "1.1.0"; /// @dev The storage location used for state /// @dev keccak256(abi.encode(uint256(keccak256("DeleGator.HybridDeleGator")) - 1)) & ~bytes32(uint256(0xff)) diff --git a/src/MultiSigDeleGator.sol b/src/MultiSigDeleGator.sol index b7d8cac..398aa40 100644 --- a/src/MultiSigDeleGator.sol +++ b/src/MultiSigDeleGator.sol @@ -25,7 +25,7 @@ contract MultiSigDeleGator is DeleGatorCore { ////////////////////////////// State ////////////////////////////// /// @dev The version of the contract - string public constant VERSION = "1.0.0"; + string public constant VERSION = "1.1.0"; /// @dev The storage slot for the MultiSig DeleGator /// @dev keccak256(abi.encode(uint256(keccak256("DeleGator.MultiSigDeleGator")) - 1)) & ~bytes32(uint256(0xff)) diff --git a/src/enforcers/AllowedCalldataEnforcer.sol b/src/enforcers/AllowedCalldataEnforcer.sol index df3dafe..4a89508 100644 --- a/src/enforcers/AllowedCalldataEnforcer.sol +++ b/src/enforcers/AllowedCalldataEnforcer.sol @@ -1,17 +1,22 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title AllowedCalldataEnforcer * @dev This contract enforces that some subset of the calldata to be executed matches the allowed subset of calldata. + * @dev This caveat enforcer only works when the execution is in single mode. * @dev A common use case for this enforcer is enforcing function parameters. It's strongly recommended to use this enforcer for * validating static types and not dynamic types. Ensuring that dynamic types are correct can be done through a series of * AllowedCalldataEnforcer terms but this is tedious and error-prone. */ contract AllowedCalldataEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// Public Methods ////////////////////////////// /** @@ -20,12 +25,14 @@ contract AllowedCalldataEnforcer is CaveatEnforcer { * @param _terms This is packed bytes where: * - the first 32 bytes is the start of the subset of calldata bytes * - the remainder of the bytes is the expected value - * @param _action The action the delegate is trying try to execute. + * @param _mode The execution mode for the execution. + * @param _executionCallData The execution the delegate is trying try to execute. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32, address, address @@ -33,18 +40,19 @@ contract AllowedCalldataEnforcer is CaveatEnforcer { public pure override + onlySingleExecutionMode(_mode) { // Ensure that the first two term values are valid and at least 1 byte for value_ uint256 dataStart_; bytes memory value_; - bytes memory calldataValue_; + + (,, bytes calldata callData_) = _executionCallData.decodeSingle(); (dataStart_, value_) = getTermsInfo(_terms); uint256 valueLength_ = value_.length; - require(dataStart_ + valueLength_ <= _action.data.length, "AllowedCalldataEnforcer:invalid-calldata-length"); + require(dataStart_ + valueLength_ <= callData_.length, "AllowedCalldataEnforcer:invalid-calldata-length"); - calldataValue_ = _action.data[dataStart_:dataStart_ + valueLength_]; - require(_compare(calldataValue_, value_), "AllowedCalldataEnforcer:invalid-calldata"); + require(_compare(callData_[dataStart_:dataStart_ + valueLength_], value_), "AllowedCalldataEnforcer:invalid-calldata"); } /** diff --git a/src/enforcers/AllowedMethodsEnforcer.sol b/src/enforcers/AllowedMethodsEnforcer.sol index 8630f96..770b879 100644 --- a/src/enforcers/AllowedMethodsEnforcer.sol +++ b/src/enforcers/AllowedMethodsEnforcer.sol @@ -1,26 +1,34 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title AllowedMethodsEnforcer * @dev This contract enforces the allowed methods a delegate may call. + * @dev This caveat enforcer only works when the execution is in single mode. */ contract AllowedMethodsEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// Public Methods ////////////////////////////// /** * @notice Allows the delegator to limit what methods the delegate may call. * @dev This function enforces the allowed methods before the transaction is performed. * @param _terms A series of 4byte method identifiers, representing the methods that the delegate is allowed to call. - * @param _action The transaction the delegate might try to perform. + * @param _mode The execution mode for the execution. + * @param _executionCallData The execution the delegate is trying try to execute. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32, address, address @@ -28,10 +36,13 @@ contract AllowedMethodsEnforcer is CaveatEnforcer { public pure override + onlySingleExecutionMode(_mode) { - require(_action.data.length >= 4, "AllowedMethodsEnforcer:invalid-action-data-length"); + (,, bytes calldata callData_) = _executionCallData.decodeSingle(); + + require(callData_.length >= 4, "AllowedMethodsEnforcer:invalid-execution-data-length"); - bytes4 targetSig_ = bytes4(_action.data[0:4]); + bytes4 targetSig_ = bytes4(callData_[0:4]); bytes4[] memory allowedSignatures_ = getTermsInfo(_terms); uint256 allowedSignaturesLength_ = allowedSignatures_.length; diff --git a/src/enforcers/AllowedTargetsEnforcer.sol b/src/enforcers/AllowedTargetsEnforcer.sol index 65fe04e..7ec281a 100644 --- a/src/enforcers/AllowedTargetsEnforcer.sol +++ b/src/enforcers/AllowedTargetsEnforcer.sol @@ -1,26 +1,33 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title AllowedTargetsEnforcer * @dev This contract enforces the allowed target addresses for a delegate. + * @dev This caveat enforcer only works when the execution is in single mode. */ contract AllowedTargetsEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// Public Methods ////////////////////////////// /** * @notice Allows the delegator to limit what addresses the delegate may call. * @dev This function enforces the allowed target addresses before the transaction is performed. * @param _terms A series of 20byte addresses, representing the addresses that the delegate is allowed to call. - * @param _action The transaction the delegate might try to perform. + * @param _mode The execution mode for the execution. + * @param _executionCallData The execution the delegate is trying try to execute. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32, address, address @@ -28,12 +35,14 @@ contract AllowedTargetsEnforcer is CaveatEnforcer { public pure override + onlySingleExecutionMode(_mode) { - address targetAddress_ = _action.to; + (address target_,,) = _executionCallData.decodeSingle(); + address[] memory allowedTargets_ = getTermsInfo(_terms); uint256 allowedTargetsLength_ = allowedTargets_.length; for (uint256 i = 0; i < allowedTargetsLength_; ++i) { - if (targetAddress_ == allowedTargets_[i]) { + if (target_ == allowedTargets_[i]) { return; } } diff --git a/src/enforcers/ArgsEqualityCheckEnforcer.sol b/src/enforcers/ArgsEqualityCheckEnforcer.sol index e48cebc..60e7a56 100644 --- a/src/enforcers/ArgsEqualityCheckEnforcer.sol +++ b/src/enforcers/ArgsEqualityCheckEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * * @title ArgsEqualityCheckEnforcer @@ -31,7 +31,8 @@ contract ArgsEqualityCheckEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata _args, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address, address _redeemer diff --git a/src/enforcers/BlockNumberEnforcer.sol b/src/enforcers/BlockNumberEnforcer.sol index 3e3a58c..ebbf4b7 100644 --- a/src/enforcers/BlockNumberEnforcer.sol +++ b/src/enforcers/BlockNumberEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title BlockNumberEnforcer @@ -17,7 +17,19 @@ contract BlockNumberEnforcer is CaveatEnforcer { * @param _terms A bytes32 blocknumber range where the first half of the word is the earliest the delegation can be used and * the last half of the word is the latest the delegation can be used. The block number ranges are not inclusive. */ - function beforeHook(bytes calldata _terms, bytes calldata, Action calldata, bytes32, address, address) public view override { + function beforeHook( + bytes calldata _terms, + bytes calldata, + ModeCode, + bytes calldata, + bytes32, + address, + address + ) + public + view + override + { (uint128 blockAfterThreshold_, uint128 blockBeforeThreshold_) = getTermsInfo(_terms); if (blockAfterThreshold_ > 0) { diff --git a/src/enforcers/CaveatEnforcer.sol b/src/enforcers/CaveatEnforcer.sol index 4c816e9..84dbba0 100644 --- a/src/enforcers/CaveatEnforcer.sol +++ b/src/enforcers/CaveatEnforcer.sol @@ -1,17 +1,38 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; + import { ICaveatEnforcer } from "../interfaces/ICaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; +import { CALLTYPE_SINGLE, CALLTYPE_BATCH } from "../utils/Constants.sol"; /** * @title CaveatEnforcer - * @dev This abstract contract enforces caveats before and after the execution of an action. + * @dev This abstract contract enforces caveats before and after the execution of an execution. */ abstract contract CaveatEnforcer is ICaveatEnforcer { + using ModeLib for ModeCode; + /// @inheritdoc ICaveatEnforcer - function beforeHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) public virtual { } + function beforeHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) public virtual { } /// @inheritdoc ICaveatEnforcer - function afterHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) public virtual { } + function afterHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) public virtual { } + + /** + * @dev Require the function call to be in single execution mode + */ + modifier onlySingleExecutionMode(ModeCode _mode) { + require(ModeLib.getCallType(_mode) == CALLTYPE_SINGLE, "CaveatEnforcer:invalid-call-type"); + _; + } + + /** + * @dev Require the function call to be in batch execution mode + */ + modifier onlyBatchExecutionMode(ModeCode _mode) { + require(ModeLib.getCallType(_mode) == CALLTYPE_BATCH, "CaveatEnforcer:invalid-call-type"); + _; + } } diff --git a/src/enforcers/DeployedEnforcer.sol b/src/enforcers/DeployedEnforcer.sol index 7b8e4fa..d2c3647 100644 --- a/src/enforcers/DeployedEnforcer.sol +++ b/src/enforcers/DeployedEnforcer.sol @@ -5,7 +5,7 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title DeployedEnforcer @@ -49,7 +49,18 @@ contract DeployedEnforcer is CaveatEnforcer { * the next 32 bytes are the salt to use for create2 * the remaining bytes are the bytecode of the contract to deploy */ - function beforeHook(bytes calldata _terms, bytes calldata, Action calldata, bytes32, address, address) public override { + function beforeHook( + bytes calldata _terms, + bytes calldata, + ModeCode, + bytes calldata, + bytes32, + address, + address + ) + public + override + { (address expectedAddress_, bytes32 salt_, bytes memory bytecode_) = getTermsInfo(_terms); // check if this contract has been deployed yet diff --git a/src/enforcers/ERC20BalanceGteEnforcer.sol b/src/enforcers/ERC20BalanceGteEnforcer.sol index bfe2269..b5e1197 100644 --- a/src/enforcers/ERC20BalanceGteEnforcer.sol +++ b/src/enforcers/ERC20BalanceGteEnforcer.sol @@ -4,12 +4,13 @@ pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title ERC20BalanceGteEnforcer * @dev This contract enforces that the delegator's ERC20 balance has increased by at least the specified amount - * after the action has been executed, measured between the `beforeHook` and `afterHook` calls, regardless of what the action is. + * after the execution has been executed, measured between the `beforeHook` and `afterHook` calls, regardless of what the execution + * is. * @dev This contract has no enforcement of how the balance increases. It's meant to be used alongside additional enforcers to * create granular permissions. */ @@ -42,7 +43,8 @@ contract ERC20BalanceGteEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address _delegator, address @@ -66,7 +68,8 @@ contract ERC20BalanceGteEnforcer is CaveatEnforcer { function afterHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address _delegator, address diff --git a/src/enforcers/ERC20TransferAmountEnforcer.sol b/src/enforcers/ERC20TransferAmountEnforcer.sol index 580c9db..6775a55 100644 --- a/src/enforcers/ERC20TransferAmountEnforcer.sol +++ b/src/enforcers/ERC20TransferAmountEnforcer.sol @@ -2,15 +2,19 @@ pragma solidity 0.8.23; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title ERC20TransferAmountEnforcer * @dev This contract enforces the transfer limit for ERC20 tokens. + * @dev This caveat enforcer only works when the execution is in single mode. */ contract ERC20TransferAmountEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// State ////////////////////////////// mapping(address delegationManager => mapping(bytes32 delegationHash => uint256 amount)) public spentMap; @@ -27,36 +31,24 @@ contract ERC20TransferAmountEnforcer is CaveatEnforcer { * @dev This function enforces the transfer limit before the transaction is performed. * @param _terms The ERC20 token address, and the numeric maximum amount that the recipient may transfer on the signer's * behalf. - * @param _action The transaction the delegate might try to perform. + * @param _mode The mode of the execution. + * @param _executionCallData The transaction the delegate might try to perform. * @param _delegationHash The hash of the delegation being operated on. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32 _delegationHash, address, address _redeemer ) public override + onlySingleExecutionMode(_mode) { - require(_action.data.length == 68, "ERC20TransferAmountEnforcer:invalid-action-length"); - - (address allowedContract_, uint256 limit_) = getTermsInfo(_terms); - address targetContract_ = _action.to; - bytes4 allowedMethod_ = IERC20.transfer.selector; - - require(allowedContract_ == targetContract_, "ERC20TransferAmountEnforcer:invalid-contract"); - - bytes4 targetSig_ = bytes4(_action.data[0:4]); - require(targetSig_ == allowedMethod_, "ERC20TransferAmountEnforcer:invalid-method"); - - uint256 sending_ = uint256(bytes32(_action.data[36:68])); - - uint256 spent_ = spentMap[msg.sender][_delegationHash] += sending_; - require(spent_ <= limit_, "ERC20TransferAmountEnforcer:allowance-exceeded"); - + (uint256 limit_, uint256 spent_) = _validateAndIncrease(_terms, _executionCallData, _delegationHash); emit IncreasedSpentMap(msg.sender, _redeemer, _delegationHash, limit_, spent_); } @@ -72,4 +64,35 @@ contract ERC20TransferAmountEnforcer is CaveatEnforcer { allowedContract_ = address((bytes20(_terms[:20]))); maxTokens_ = uint256(bytes32(_terms[20:])); } + + /** + * @notice Returns the amount of tokens that the delegator has already spent. + * @param _terms The ERC20 token address, and the numeric maximum amount that the recipient may transfer + * @param _executionCallData The transaction the delegate might try to perform. + * @param _delegationHash The hash of the delegation being operated on. + * @return limit_ The maximum amount of tokens that the delegator is allowed to spend. + * @return spent_ The amount of tokens that the delegator has spent. + */ + function _validateAndIncrease( + bytes calldata _terms, + bytes calldata _executionCallData, + bytes32 _delegationHash + ) + internal + returns (uint256 limit_, uint256 spent_) + { + (address target_,, bytes calldata callData_) = _executionCallData.decodeSingle(); + + require(callData_.length == 68, "ERC20TransferAmountEnforcer:invalid-execution-length"); + + address allowedContract_; + (allowedContract_, limit_) = getTermsInfo(_terms); + + require(allowedContract_ == target_, "ERC20TransferAmountEnforcer:invalid-contract"); + + require(bytes4(callData_[0:4]) == IERC20.transfer.selector, "ERC20TransferAmountEnforcer:invalid-method"); + + spent_ = spentMap[msg.sender][_delegationHash] += uint256(bytes32(callData_[36:68])); + require(spent_ <= limit_, "ERC20TransferAmountEnforcer:allowance-exceeded"); + } } diff --git a/src/enforcers/IdEnforcer.sol b/src/enforcers/IdEnforcer.sol index ba092be..87b242a 100644 --- a/src/enforcers/IdEnforcer.sol +++ b/src/enforcers/IdEnforcer.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.23; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title IdEnforcer Contract @@ -31,7 +31,8 @@ contract IdEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32, address _delegator, address _redeemer diff --git a/src/enforcers/LimitedCallsEnforcer.sol b/src/enforcers/LimitedCallsEnforcer.sol index fe53dfa..41684b4 100644 --- a/src/enforcers/LimitedCallsEnforcer.sol +++ b/src/enforcers/LimitedCallsEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title Limited Calls Enforcer Contract @@ -30,7 +30,8 @@ contract LimitedCallsEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address, address _redeemer diff --git a/src/enforcers/NativeBalanceGteEnforcer.sol b/src/enforcers/NativeBalanceGteEnforcer.sol index e599d8b..d965fba 100644 --- a/src/enforcers/NativeBalanceGteEnforcer.sol +++ b/src/enforcers/NativeBalanceGteEnforcer.sol @@ -2,12 +2,13 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title NativeBalanceGteEnforcer * @dev This contract enforces that a recipient's native token balance has increased by at least the specified amount - * after the action has been executed, measured between the `beforeHook` and `afterHook` calls, regardless of what the action is. + * after the execution has been executed, measured between the `beforeHook` and `afterHook` calls, regardless of what the execution + * is. * @dev This contract does not enforce how the balance increases. It is meant to be used with additional enforcers to create * granular permissions. */ @@ -40,7 +41,8 @@ contract NativeBalanceGteEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address, address @@ -65,7 +67,8 @@ contract NativeBalanceGteEnforcer is CaveatEnforcer { function afterHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address, address diff --git a/src/enforcers/NativeTokenPaymentEnforcer.sol b/src/enforcers/NativeTokenPaymentEnforcer.sol index f17f6fa..c6ff002 100644 --- a/src/enforcers/NativeTokenPaymentEnforcer.sol +++ b/src/enforcers/NativeTokenPaymentEnforcer.sol @@ -1,20 +1,26 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action, Delegation } from "../utils/Types.sol"; +import { Execution, Delegation, ModeCode } from "../utils/Types.sol"; import { IDelegationManager } from "../interfaces/IDelegationManager.sol"; /** * @title NativeTokenPaymentEnforcer * @notice This contract enforces payment in native token (e.g., ETH) for the right to use a delegation. - * @dev The redeemer must include a payment delegation in the arguments when executing an action. - * The payment, made in native token, is processed during the execution of the delegated action, ensuring that the + * @dev The redeemer must include a payment delegation in the arguments when executing an execution. + * The payment, made in native token, is processed during the execution of the delegated execution, ensuring that the * enforced conditions are met. * Combining `NativeTokenTransferAmountEnforcer` and `ArgsEqualityCheckEnforcer` when creating the payment delegation is recommended * to prevent front-running attacks. + * @dev Requires the redeemer to be a DeleGator that supports the single execution mode. */ contract NativeTokenPaymentEnforcer is CaveatEnforcer { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// /// @dev The Delegation Manager contract to redeem the delegation @@ -55,7 +61,8 @@ contract NativeTokenPaymentEnforcer is CaveatEnforcer { function afterHook( bytes calldata _terms, bytes calldata _args, - Action calldata, + ModeCode, + bytes calldata, bytes32 _delegationHash, address _delegator, address _redeemer @@ -80,16 +87,19 @@ contract NativeTokenPaymentEnforcer is CaveatEnforcer { } } - uint256 balanceBefore_ = recipient_.balance; + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(recipient_, amount_, hex""); - Action[] memory actions_ = new Action[](1); - actions_[0] = Action({ to: recipient_, value: amount_, data: hex"" }); + ModeCode[] memory encodedModes_ = new ModeCode[](1); + encodedModes_[0] = ModeLib.encodeSimpleSingle(); + + uint256 balanceBefore_ = recipient_.balance; // Attempt to redeem the delegation and make the payment - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, encodedModes_, executionCallDatas_); // Ensure the recipient received the payment uint256 balanceAfter_ = recipient_.balance; diff --git a/src/enforcers/NativeTokenTransferAmountEnforcer.sol b/src/enforcers/NativeTokenTransferAmountEnforcer.sol index 0665d31..a31016e 100644 --- a/src/enforcers/NativeTokenTransferAmountEnforcer.sol +++ b/src/enforcers/NativeTokenTransferAmountEnforcer.sol @@ -1,14 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title NativeTokenTransferAmountEnforcer * @notice This contract enforces an allowance of native currency (e.g., ETH) for a specific delegation. */ contract NativeTokenTransferAmountEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// State ////////////////////////////// /// @notice Mapping to store used allowance for each delegation @@ -25,24 +29,29 @@ contract NativeTokenTransferAmountEnforcer is CaveatEnforcer { /** * @notice Enforces the conditions that should hold before a transaction is performed. * @param _terms The encoded amount of native token allowance. - * @param _action The action of the transaction. + * @param _mode The mode of the execution. + * @param _executionCallData The call data of the execution. * @param _delegationHash The hash of the delegation. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32 _delegationHash, address, address _redeemer ) public override + onlySingleExecutionMode(_mode) { // Decode the total allowance from _terms uint256 allowance_ = getTermsInfo(_terms); - uint256 spent_ = spentMap[msg.sender][_delegationHash] += _action.value; + (, uint256 value_,) = _executionCallData.decodeSingle(); + + uint256 spent_ = spentMap[msg.sender][_delegationHash] += value_; require(spent_ <= allowance_, "NativeTokenTransferAmountEnforcer:allowance-exceeded"); emit IncreasedSpentMap(msg.sender, _redeemer, _delegationHash, allowance_, spent_); diff --git a/src/enforcers/NonceEnforcer.sol b/src/enforcers/NonceEnforcer.sol index 5cfcbda..094a293 100644 --- a/src/enforcers/NonceEnforcer.sol +++ b/src/enforcers/NonceEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title Nonce Enforcer Contract @@ -27,7 +27,8 @@ contract NonceEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32, address _delegator, address diff --git a/src/enforcers/RedeemerEnforcer.sol b/src/enforcers/RedeemerEnforcer.sol index aea25d9..c6b0a0b 100644 --- a/src/enforcers/RedeemerEnforcer.sol +++ b/src/enforcers/RedeemerEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title RedeemerEnforcer @@ -21,7 +21,8 @@ contract RedeemerEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata, + ModeCode, + bytes calldata, bytes32, address, address _redeemer diff --git a/src/enforcers/TimestampEnforcer.sol b/src/enforcers/TimestampEnforcer.sol index 345efcd..a272c21 100644 --- a/src/enforcers/TimestampEnforcer.sol +++ b/src/enforcers/TimestampEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title Timestamp Enforcer Contract @@ -17,7 +17,19 @@ contract TimestampEnforcer is CaveatEnforcer { * @param _terms - A bytes32 timestamp range where the first half of the word is the earliest the delegation can be used and the * last half of the word is the latest the delegation can be used. The timestamp ranges are not inclusive. */ - function beforeHook(bytes calldata _terms, bytes calldata, Action calldata, bytes32, address, address) public view override { + function beforeHook( + bytes calldata _terms, + bytes calldata, + ModeCode, + bytes calldata, + bytes32, + address, + address + ) + public + view + override + { (uint128 timestampAfterThreshold_, uint128 timestampBeforeThreshold_) = getTermsInfo(_terms); if (timestampAfterThreshold_ > 0) { diff --git a/src/enforcers/ValueLteEnforcer.sol b/src/enforcers/ValueLteEnforcer.sol index c2b0584..929d027 100644 --- a/src/enforcers/ValueLteEnforcer.sol +++ b/src/enforcers/ValueLteEnforcer.sol @@ -1,25 +1,31 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + import { CaveatEnforcer } from "./CaveatEnforcer.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title ValueLteEnforcer - * @dev This contract extends the CaveatEnforcer contract. It provides functionality to enforce a specific value for the Action + * @dev This contract extends the CaveatEnforcer contract. It provides functionality to enforce a specific value for the Execution * being executed. + * @dev This caveat enforcer only works when the execution is in single mode. */ contract ValueLteEnforcer is CaveatEnforcer { + using ExecutionLib for bytes; + ////////////////////////////// Public Methods ////////////////////////////// /** * @notice Allows the delegator to specify a maximum value of native tokens that the delegate can spend. - * @param _terms - A uint256 value that the Action's value must be less than or equal to. + * @param _terms - A uint256 value that the Execution's value must be less than or equal to. */ function beforeHook( bytes calldata _terms, bytes calldata, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCallData, bytes32, address, address @@ -27,15 +33,17 @@ contract ValueLteEnforcer is CaveatEnforcer { public pure override + onlySingleExecutionMode(_mode) { - uint256 value_ = getTermsInfo(_terms); - require(_action.value <= value_, "ValueLteEnforcer:value-too-high"); + (, uint256 value_,) = _executionCallData.decodeSingle(); + uint256 termsValue_ = getTermsInfo(_terms); + require(value_ <= termsValue_, "ValueLteEnforcer:value-too-high"); } /** * @notice Decodes the terms used in this CaveatEnforcer. * @param _terms encoded data that is used during the execution hooks. - * @return value_ The value that the Action's value must be less than or equal to. + * @return value_ The value that the Execution's value must be less than or equal to. */ function getTermsInfo(bytes calldata _terms) public pure returns (uint256 value_) { require(_terms.length == 32, "ValueLteEnforcer:invalid-terms-length"); diff --git a/src/interfaces/ICaveatEnforcer.sol b/src/interfaces/ICaveatEnforcer.sol index 83c4e69..0b5802f 100644 --- a/src/interfaces/ICaveatEnforcer.sol +++ b/src/interfaces/ICaveatEnforcer.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title CaveatEnforcer - * @notice This is an abstract contract that exposes pre and post Action hooks during delegation redemption. - * @dev Hooks can be used to enforce conditions before and after an Action is performed. + * @notice This is an abstract contract that exposes pre and post Execution hooks during delegation redemption. + * @dev Hooks can be used to enforce conditions before and after an Execution is performed. * @dev Reverting during the hooks will revert the entire delegation redemption. * @dev Child contracts can implement the beforeHook method and/or afterHook method. - * @dev NOTE: There is no guarantee that the action is executed. If you are relying on the action then be sure to use the + * @dev NOTE: There is no guarantee that the execution is executed. If you are relying on the execution then be sure to use the * `afterHook` method. */ interface ICaveatEnforcer { @@ -18,7 +18,8 @@ interface ICaveatEnforcer { * @dev This function MUST revert if the conditions are not met. * @param _terms The terms to enforce set by the delegator. * @param _args An optional input parameter set by the redeemer at time of invocation. - * @param _action The action of the transaction. + * @param _mode The mode of execution for the executionCalldata. + * @param _executionCalldata The data representing the execution. * @param _delegationHash The hash of the delegation. * @param _delegator The address of the delegator. * @param _redeemer The address that is redeeming the delegation. @@ -26,7 +27,8 @@ interface ICaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata _args, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCalldata, bytes32 _delegationHash, address _delegator, address _redeemer @@ -38,7 +40,8 @@ interface ICaveatEnforcer { * @dev This function MUST revert if the conditions are not met. * @param _terms The terms to enforce set by the delegator. * @param _args An optional input parameter set by the redeemer at time of invocation. - * @param _action The action of the transaction. + * @param _mode The mode of execution for the executionCalldata. + * @param _executionCalldata The data representing the execution. * @param _delegationHash The hash of the delegation. * @param _delegator The address of the delegator. * @param _redeemer The address that is redeeming the delegation. @@ -46,7 +49,8 @@ interface ICaveatEnforcer { function afterHook( bytes calldata _terms, bytes calldata _args, - Action calldata _action, + ModeCode _mode, + bytes calldata _executionCalldata, bytes32 _delegationHash, address _delegator, address _redeemer diff --git a/src/interfaces/IDeleGatorCore.sol b/src/interfaces/IDeleGatorCore.sol index 3696058..9790052 100644 --- a/src/interfaces/IDeleGatorCore.sol +++ b/src/interfaces/IDeleGatorCore.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.23; import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; -import { Action } from "../utils/Types.sol"; +import { ModeCode } from "../utils/Types.sol"; /** * @title IDeleGatorCore @@ -11,9 +11,19 @@ import { Action } from "../utils/Types.sol"; */ interface IDeleGatorCore is IERC1271 { /** - * @notice executes a CALL using the data provided in the action - * @dev MUST enforce calls come from an approved DelegationManager address - * @param _action the onchain action to perform + * @dev Executes a transaction on behalf of the account. + * This function is intended to be called by Executor Modules + * @dev Ensure adequate authorization control: i.e. onlyExecutorModule + * @dev If a mode is requested that is not supported by the Account, it MUST revert + * @dev Related: @erc7579/MSAAdvanced.sol + * @param _mode The encoded execution mode of the transaction. See @erc7579/ModeLib.sol for details. + * @param _executionCalldata The encoded execution call data */ - function executeDelegatedAction(Action calldata _action) external; + function executeFromExecutor( + ModeCode _mode, + bytes calldata _executionCalldata + ) + external + payable + returns (bytes[] memory returnData); } diff --git a/src/interfaces/IDeleGatorCoreFull.sol b/src/interfaces/IDeleGatorCoreFull.sol deleted file mode 100644 index 8e03d4f..0000000 --- a/src/interfaces/IDeleGatorCoreFull.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: MIT AND Apache-2.0 -pragma solidity 0.8.23; - -import { IEntryPoint } from "@account-abstraction/interfaces/IEntryPoint.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { IDeleGatorCore } from "./IDeleGatorCore.sol"; -import { IDelegationManager } from "./IDelegationManager.sol"; -import { Action, Delegation, PackedUserOperation } from "../utils/Types.sol"; - -/** - * @title IDeleGatorCoreFull - * @notice Interface for a DeleGator that exposes the minimal functionality required. - */ -interface IDeleGatorCoreFull is IDeleGatorCore, IERC165 { - ////////////////////////////// Events ////////////////////////////// - event SetDelegationManager(IDelegationManager indexed newDelegationManager); - - event SetEntryPoint(IEntryPoint indexed entryPoint); - - event ClearedStorage(); - - ////////////////////////////// Errors ////////////////////////////// - - error NotSelf(); - error NotEntryPoint(); - error NotEntryPointOrSelf(); - error NotDelegationManager(); - - ////////////////////////////// MM Implementation Methods ////////////////////////////// - - function redeemDelegation(bytes[] calldata _permissionContexts, Action[] calldata _actions) external; - - function execute(Action calldata _action) external; - - function executeBatch(Action[] calldata _actions) external; - - function validateUserOp( - PackedUserOperation calldata _userOp, - bytes32 _userOpHash, - uint256 _missingAccountFunds - ) - external - returns (uint256 validationData_); - - function isValidSignature(bytes32 _hash, bytes memory _signature) external view returns (bytes4 magicValue_); - - function addDeposit() external payable; - - function withdrawDeposit(address payable _withdrawAddress, uint256 _withdrawAmount) external; - - function disableDelegation(Delegation calldata _delegation) external; - - function enableDelegation(Delegation calldata _delegation) external; - - function upgradeToAndCall(address _newImplementation, bytes memory _data) external payable; - - function upgradeToAndCallAndRetainStorage(address _newImplementation, bytes memory _data) external payable; - - function isDelegationDisabled(bytes32 _delegationHash) external view returns (bool); - - function entryPoint() external view returns (IEntryPoint); - - function delegationManager() external view returns (IDelegationManager); - - function getNonce() external view returns (uint256); - - function getNonce(uint192 _key) external view returns (uint256); - - function getDeposit() external view returns (uint256); - - function getImplementation() external view returns (address); - - function getInitializedVersion() external view returns (uint64); - - ////////////////////////////// TokenCallbackHandler Methods ////////////////////////////// - - function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4); - - function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4); - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) - external - pure - returns (bytes4); - - ////////////////////////////// UUPSUpgradeable Methods ////////////////////////////// - - function proxiableUUID() external view returns (bytes32); -} diff --git a/src/interfaces/IDelegationManager.sol b/src/interfaces/IDelegationManager.sol index 1668b3b..0435a8e 100644 --- a/src/interfaces/IDelegationManager.sol +++ b/src/interfaces/IDelegationManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { Action, Delegation } from "../utils/Types.sol"; +import { Delegation, Execution, ModeCode } from "../utils/Types.sol"; /** * @title IDelegationManager @@ -54,7 +54,7 @@ interface IDelegationManager { /// @dev Error thrown when the delegation provided is already enabled error AlreadyEnabled(); - /// @dev Error thrown when the batch size doesn't match the action array size + /// @dev Error thrown when the batch size doesn't match the execution array size error BatchDataLengthMismatch(); ////////////////////////////// MM Implementation Methods ////////////////////////////// @@ -71,7 +71,12 @@ interface IDelegationManager { function getDelegationHash(Delegation calldata _delegation) external pure returns (bytes32); - function redeemDelegation(bytes[] calldata _permissionContexts, Action[] calldata _actions) external; + function redeemDelegations( + bytes[] calldata _permissionContexts, + ModeCode[] calldata _modes, + bytes[] calldata _executionCallDatas + ) + external; function getDomainHash() external view returns (bytes32); } diff --git a/src/libraries/EncoderLib.sol b/src/libraries/EncoderLib.sol index 0477515..fb97745 100644 --- a/src/libraries/EncoderLib.sol +++ b/src/libraries/EncoderLib.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { Delegation, Caveat } from "../utils/Types.sol"; -import { DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../utils/Typehashes.sol"; +import { DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../utils/Constants.sol"; /** * @dev Provides implementations for common utility methods for Delegation. diff --git a/src/libraries/ExecutionLib.sol b/src/libraries/ExecutionLib.sol deleted file mode 100644 index e5c1a3a..0000000 --- a/src/libraries/ExecutionLib.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: MIT AND Apache-2.0 -pragma solidity 0.8.23; - -import { Action } from "../utils/Types.sol"; - -/** - * @title Execution Library - * Provides a common implementation for executing actions. - */ -library ExecutionLib { - ////////////////////////////// Errors ////////////////////////////// - - /// @dev Error thrown when execution fails without providing a reason - error FailedExecutionWithoutReason(); - - /// @dev Error thrown when executing empty Actions array - error InvalidActionsLength(); - - ////////////////////////////// Events ////////////////////////////// - - /// @dev Event emitted when an action is executed. - event ExecutedAction(address indexed to, uint256 value, bool success, bytes errorMessage); - - /// @dev Event emitted when prefunding is sent. - event SentPrefund(address indexed sender, uint256 amount, bool success); - - ////////////////////////////// Internal Functions ////////////////////////////// - - /** - * @notice Executes the provided Action and reverts if the execution fails. - * @dev Ensure caller permissions are checked before calling this method - * @param _action the Action to execute - */ - function _execute(Action calldata _action) internal { - (bool success_, bytes memory errorMessage_) = _action.to.call{ value: _action.value }(_action.data); - - emit ExecutedAction(_action.to, _action.value, success_, errorMessage_); - - if (!success_) { - if (errorMessage_.length == 0) revert FailedExecutionWithoutReason(); - - assembly { - revert(add(32, errorMessage_), mload(errorMessage_)) - } - } - } - - /** - * @notice Executes several Actions in order and reverts if any of the executions fail. - * @param _actions the ordered actions to execute - */ - function _executeBatch(Action[] calldata _actions) internal { - uint256 actionLength = _actions.length; - if (actionLength == 0) revert InvalidActionsLength(); - for (uint256 i = 0; i < actionLength; ++i) { - _execute(_actions[i]); - } - } - - /** - * @notice Sends the entrypoint (msg.sender) any needed funds for the transaction. - * @param _missingAccountFunds the minimum value this method should send the entrypoint. - * this value MAY be zero, in case there is enough deposit, or the userOp has a paymaster. - */ - function _payPrefund(uint256 _missingAccountFunds) internal { - if (_missingAccountFunds != 0) { - (bool success_,) = payable(msg.sender).call{ value: _missingAccountFunds, gas: type(uint256).max }(""); - (success_); - //ignore failure (its EntryPoint's job to verify, not account.) - emit SentPrefund(msg.sender, _missingAccountFunds, success_); - } - } -} diff --git a/src/utils/Typehashes.sol b/src/utils/Constants.sol similarity index 79% rename from src/utils/Typehashes.sol rename to src/utils/Constants.sol index 019d50f..9dab2ac 100644 --- a/src/utils/Typehashes.sol +++ b/src/utils/Constants.sol @@ -1,6 +1,10 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { + CALLTYPE_SINGLE, CALLTYPE_BATCH, EXECTYPE_DEFAULT, EXECTYPE_TRY, MODE_DEFAULT, MODE_OFFSET +} from "@erc7579/lib/ModeLib.sol"; + bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); diff --git a/src/utils/Types.sol b/src/utils/Types.sol index c75f000..50bff06 100644 --- a/src/utils/Types.sol +++ b/src/utils/Types.sol @@ -2,6 +2,8 @@ pragma solidity 0.8.23; import { PackedUserOperation } from "@account-abstraction/interfaces/PackedUserOperation.sol"; +import { Execution } from "@erc7579/interfaces/IERC7579Account.sol"; +import { ModeCode, CallType, ExecType, ModeSelector, ModePayload } from "@erc7579/lib/ModeLib.sol"; /** * @title EIP712Domain @@ -39,19 +41,6 @@ struct Caveat { bytes args; } -/** - * @title Action - * @notice This struct represents an action to be taken. - * @dev It is used to pass the action of a transaction to a CaveatEnforcer. - * It only includes the functional part of a transaction, allowing it to be - * agnostic whether this was sent from a protocol-level tx or UserOperation. - */ -struct Action { - address to; - uint256 value; - bytes data; -} - /** * @title P256 Public Key * @notice Struct containing the X and Y coordinates of a P256 public key. diff --git a/test/CounterfactualAssetsTest.t.sol b/test/CounterfactualAssetsTest.t.sol index a14428a..f661ea1 100644 --- a/test/CounterfactualAssetsTest.t.sol +++ b/test/CounterfactualAssetsTest.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { BaseTest } from "./utils/BaseTest.t.sol"; -import { Delegation, Caveat, Action } from "../src/utils/Types.sol"; +import { Delegation, Caveat, Execution } from "../src/utils/Types.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; import { BasicCF721 } from "./utils/BasicCF721.t.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; @@ -83,17 +83,17 @@ contract CounterfactualAssetsTest is BaseTest { // Alice signs Delegation to deploy BasicCF721 delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action_ to mint an NFT - Action memory action_ = Action({ - to: predictedAddr_, + // Create Bob's execution_ to mint an NFT + Execution memory execution_ = Execution({ + target: predictedAddr_, value: 0, - data: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.bob.deleGator)]) + callData: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.bob.deleGator)]) }); // Execute Bob's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final state bytes memory finalCode_ = predictedAddr_.code; @@ -153,17 +153,17 @@ contract CounterfactualAssetsTest is BaseTest { // Alice signs Delegation to deploy BasicCF721 delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action_ to mint an NFT - Action memory action_ = Action({ - to: predictedAddr_, + // Create Bob's execution_ to mint an NFT + Execution memory execution_ = Execution({ + target: predictedAddr_, value: 0, - data: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.bob.deleGator)]) + callData: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.bob.deleGator)]) }); // Execute Bob's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final state bytes memory finalCode_ = predictedAddr_.code; @@ -237,18 +237,18 @@ contract CounterfactualAssetsTest is BaseTest { // Bob signs Delegation to Carol bobDelegation_ = signDelegation(users.bob, bobDelegation_); - // Create Carols's action_ to mint an NFT - Action memory action_ = Action({ - to: predictedAddr_, + // Create Carols's execution_ to mint an NFT + Execution memory execution_ = Execution({ + target: predictedAddr_, value: 0, - data: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.carol.deleGator)]) + callData: abi.encodeWithSelector(BasicCF721.mint.selector, [address(users.carol.deleGator)]) }); // Execute Carol's UserOp Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = bobDelegation_; delegations_[1] = aliceDelegation_; - invokeDelegation_UserOp(users.carol, delegations_, action_); + invokeDelegation_UserOp(users.carol, delegations_, execution_); // Get final state bytes memory finalCode_ = predictedAddr_.code; diff --git a/test/DeleGatorTestSuite.t.sol b/test/DeleGatorTestSuite.t.sol index ec55f8b..ffbbd30 100644 --- a/test/DeleGatorTestSuite.t.sol +++ b/test/DeleGatorTestSuite.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { IEntryPoint, EntryPoint } from "@account-abstraction/core/EntryPoint.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; @@ -9,29 +10,32 @@ import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionHelper } from "@erc7579/core/ExecutionHelper.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; -import { Delegation, Caveat, PackedUserOperation, Delegation, Action } from "../src/utils/Types.sol"; +import { Delegation, Caveat, PackedUserOperation, Delegation, Execution, ModeCode } from "../src/utils/Types.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; import { Counter } from "./utils/Counter.t.sol"; import { StorageUtilsLib } from "./utils/StorageUtilsLib.t.sol"; import { SigningUtilsLib } from "./utils/SigningUtilsLib.t.sol"; -import { ExecutionLib } from "../src/libraries/ExecutionLib.sol"; - -import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; +import { EXECUTE_SINGULAR_SIGNATURE } from "./utils/Constants.sol"; import { IDeleGatorCore } from "../src/interfaces/IDeleGatorCore.sol"; -import { IDeleGatorCoreFull } from "../src/interfaces/IDeleGatorCoreFull.sol"; +import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; +import { DeleGatorCore } from "../src/DeleGatorCore.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; import { AllowedMethodsEnforcer } from "../src/enforcers/AllowedMethodsEnforcer.sol"; import { AllowedTargetsEnforcer } from "../src/enforcers/AllowedTargetsEnforcer.sol"; abstract contract DeleGatorTestSuite is BaseTest { + using ModeLib for ModeCode; using MessageHashUtils for bytes32; ////////////////////////////// Setup ////////////////////// Counter aliceDeleGatorCounter; Counter bobDeleGatorCounter; + ModeCode[] oneSingularMode; function setUp() public virtual override { super.setUp(); @@ -43,6 +47,9 @@ abstract contract DeleGatorTestSuite is BaseTest { aliceDeleGatorCounter = new Counter(address(users.alice.deleGator)); bobDeleGatorCounter = new Counter(address(users.bob.deleGator)); + + oneSingularMode = new ModeCode[](1); + oneSingularMode[0] = ModeLib.encodeSimpleSingle(); } ////////////////////////////// State ////////////////////////////// @@ -78,7 +85,6 @@ abstract contract DeleGatorTestSuite is BaseTest { uint256 actualGasUsed ); event Withdrawn(address indexed account, address withdrawAddress, uint256 amount); - event ExecutedAction(address indexed to, uint256 value, bool success, bytes errorMessage); event SentPrefund(address indexed sender, uint256 amount, bool success); event RedeemedDelegation(address indexed rootDelegator, address indexed redeemer, Delegation delegation); @@ -246,22 +252,25 @@ abstract contract DeleGatorTestSuite is BaseTest { signature: hex"" }); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); vm.expectRevert(abi.encodeWithSelector(IDelegationManager.EmptySignature.selector)); vm.prank(address(users.bob.deleGator)); - users.bob.deleGator.redeemDelegation(encodedDelegations_, actions_); + users.bob.deleGator.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); } // should not allow to enable already enabled delegation @@ -362,19 +371,22 @@ abstract contract DeleGatorTestSuite is BaseTest { // Alice signs delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Execute Bob's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get intermediate count uint256 intermediateValue_ = aliceDeleGatorCounter.count(); @@ -389,8 +401,9 @@ abstract contract DeleGatorTestSuite is BaseTest { ) ); - bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory userOpCallData_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); uint256[] memory signers_ = new uint256[](1); signers_[0] = users.bob.privateKey; @@ -459,34 +472,6 @@ abstract contract DeleGatorTestSuite is BaseTest { assertFalse(isDisabled_); } - // should emit an event when the action_ is executed - function test_emit_executedActionEvent() public { - uint256 initialValue_ = aliceDeleGatorCounter.count(); - - // Bob's Delegator redeems the delegation - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - - // Creating a valid action - vm.prank(address(entryPoint)); - vm.expectEmit(true, true, true, true, address(users.alice.deleGator)); - emit ExecutedAction(action_.to, action_.value, true, hex""); - users.alice.deleGator.execute(action_); - - // Validate that the count has increased by 1 - uint256 updatedValue_ = aliceDeleGatorCounter.count(); - assertEq(updatedValue_, initialValue_ + 1); - - // Creating a invalid action_ - action_.value = 1; - vm.prank(address(entryPoint)); - // Expect it to emit a reverted event - vm.expectEmit(true, true, true, true, address(users.alice.deleGator)); - vm.expectRevert(abi.encodeWithSelector(ExecutionLib.FailedExecutionWithoutReason.selector)); - emit ExecutedAction(action_.to, action_.value, false, hex""); - users.alice.deleGator.execute(action_); - } - // should emit an event when paying the prefund function test_emit_sentPrefund() public { PackedUserOperation memory packedUserOperation_; @@ -527,12 +512,15 @@ abstract contract DeleGatorTestSuite is BaseTest { delegation_ = signDelegation(users.alice, delegation_); // Bob's Delegator redeems the delegation - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has increased by 1 updatedValue_ = aliceDeleGatorCounter.count(); @@ -542,13 +530,13 @@ abstract contract DeleGatorTestSuite is BaseTest { // Bob redeems the delegation vm.prank(users.bob.addr); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Validate that the count has increased by 1 updatedValue_ = aliceDeleGatorCounter.count(); @@ -591,21 +579,24 @@ abstract contract DeleGatorTestSuite is BaseTest { bobDelegation_ = signDelegation(users.bob, bobDelegation_); // Carol redeems the delegation - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = bobDelegation_; delegations_[1] = aliceDelegation_; // Carol redeems the delegation vm.prank(users.carol.addr); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Validate that the count has increased by 1 updatedValue_ = aliceDeleGatorCounter.count(); @@ -629,19 +620,22 @@ abstract contract DeleGatorTestSuite is BaseTest { delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); vm.prank(users.bob.addr); vm.expectRevert(); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode("quack"); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode("quack"); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; - delegationManager.redeemDelegation(encodedDelegations_, actions_); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -712,15 +706,18 @@ abstract contract DeleGatorTestSuite is BaseTest { // Alice signs delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Bob's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -747,9 +744,12 @@ abstract contract DeleGatorTestSuite is BaseTest { // Alice signs delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Redeem Bob's delegation Delegation[] memory delegations_ = new Delegation[](1); @@ -757,13 +757,13 @@ abstract contract DeleGatorTestSuite is BaseTest { vm.prank(users.bob.addr); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -803,9 +803,12 @@ abstract contract DeleGatorTestSuite is BaseTest { bytes32 typedDataHash_ = MessageHashUtils.toTypedDataHash(domainHash_, delegationHash2_); delegation2_.signature = SigningUtilsLib.signHash_EOA(users.bob.privateKey, typedDataHash_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Redeem Carol's delegation Delegation[] memory delegations_ = new Delegation[](2); @@ -814,13 +817,13 @@ abstract contract DeleGatorTestSuite is BaseTest { vm.prank(users.carol.addr); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -860,9 +863,12 @@ abstract contract DeleGatorTestSuite is BaseTest { bytes32 typedDataHash_ = MessageHashUtils.toTypedDataHash(domainHash_, delegationHash2_); delegation2_.signature = SigningUtilsLib.signHash_EOA(users.dave.privateKey, typedDataHash_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Redeem Carol's delegation Delegation[] memory delegations_ = new Delegation[](2); @@ -872,13 +878,13 @@ abstract contract DeleGatorTestSuite is BaseTest { vm.prank(users.carol.addr); vm.expectRevert(abi.encodeWithSelector(IDelegationManager.InvalidSignature.selector)); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count // Validate that the count has increased by 1 @@ -914,15 +920,18 @@ abstract contract DeleGatorTestSuite is BaseTest { // Alice signs delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Bob's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -962,16 +971,19 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Bob's delegation bobDelegation_ = signDelegation(users.bob, bobDelegation_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Carol's UserOp Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = bobDelegation_; delegations_[1] = aliceDelegation_; - invokeDelegation_UserOp(users.carol, delegations_, action_); + invokeDelegation_UserOp(users.carol, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1011,23 +1023,28 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Carol's delegation carolDelegation_ = signDelegation(users.carol, carolDelegation_); - // Create Dave's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Dave's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Execute Dave's UserOp Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = carolDelegation_; delegations_[1] = aliceDelegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); PackedUserOperation memory userOp_ = createAndSignUserOp( users.dave, address(users.dave.deleGator), - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_) + abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ) ); PackedUserOperation[] memory userOps_ = new PackedUserOperation[](1); @@ -1082,16 +1099,19 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Bob's delegation bobDelegation_ = signDelegation(users.bob, bobDelegation_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Carol's UserOp Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = bobDelegation_; delegations_[1] = aliceDelegation_; - invokeDelegation_UserOp(users.carol, delegations_, action_); + invokeDelegation_UserOp(users.carol, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1131,9 +1151,12 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Bob's delegation bobDelegation_ = signDelegation(users.bob, bobDelegation_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Carol's UserOp Delegation[] memory delegations_ = new Delegation[](2); @@ -1142,13 +1165,13 @@ abstract contract DeleGatorTestSuite is BaseTest { vm.prank(users.carol.addr); - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1157,20 +1180,26 @@ abstract contract DeleGatorTestSuite is BaseTest { assertEq(finalValue_, initialValue_ + 1); } - // should allow Alice to execute multiple actions_ in a single UserOp - function test_allow_multiAction_UserOp() public { + // should allow Alice to execute multiple executionCallDatas_ in a single UserOp + function test_allow_multiExecution_UserOp() public { // Get Alice's DeleGator's Counter's initial count uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create actions_ - Action[] memory actions_ = new Action[](2); - actions_[0] = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - actions_[1] = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create executionCallDatas_ + Execution[] memory executionCallDatas_ = new Execution[](2); + executionCallDatas_[0] = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + executionCallDatas_[1] = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); - // Execute Actions - executeBatch_UserOp(users.alice, actions_); + // Execute Executions + executeBatch_UserOp(users.alice, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1179,29 +1208,8 @@ abstract contract DeleGatorTestSuite is BaseTest { assertEq(finalValue_, initialValue_ + 2); } - // should not allow to execute empty Actions - function test_notAllow_emptyAction_UserOp() public { - //Create Actions - Action[] memory actions_ = new Action[](0); - - bytes memory userOpCallData__ = abi.encodeWithSelector(IDeleGatorCoreFull.executeBatch.selector, actions_); - PackedUserOperation memory userOp_ = createUserOp(address(users.alice.deleGator), userOpCallData__); - bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); - userOp_.signature = signHash(users.alice, userOpHash_.toEthSignedMessageHash()); - - PackedUserOperation[] memory userOps_ = new PackedUserOperation[](1); - userOps_[0] = userOp_; - vm.prank(bundler); - - vm.expectEmit(true, true, false, true, address(entryPoint)); - emit UserOperationRevertReason( - userOpHash_, address(users.alice.deleGator), 0, abi.encodeWithSelector(ExecutionLib.InvalidActionsLength.selector) - ); - entryPoint.handleOps(userOps_, payable(bundler)); - } - - // should allow Bob to execute multiple actions_ that redeem delegations_ in a single UserOp (offchain) - function test_allow_multiActionDelegationClaim_Offchain_UserOp() public { + // should allow Bob to execute multiple executionCallDatas_ that redeem delegations_ in a single UserOp (offchain) + function test_allow_multiExecutionDelegationClaim_Offchain_UserOp() public { // Get Alice's DeleGator's Counter's initial count uint256 initialValue_ = aliceDeleGatorCounter.count(); @@ -1218,28 +1226,34 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Invoke delegation calldata Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - bytes memory invokeDelegationCalldata_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory invokeDelegationCalldata_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); - // Create invoke delegation Actions - Action[] memory redemptionActions_ = new Action[](2); - redemptionActions_[0] = Action({ to: address(users.bob.deleGator), value: 0, data: invokeDelegationCalldata_ }); - redemptionActions_[1] = Action({ to: address(users.bob.deleGator), value: 0, data: invokeDelegationCalldata_ }); + // Create invoke delegation Executions + Execution[] memory redemptionExecutions_ = new Execution[](2); + redemptionExecutions_[0] = + Execution({ target: address(users.bob.deleGator), value: 0, callData: invokeDelegationCalldata_ }); + redemptionExecutions_[1] = + Execution({ target: address(users.bob.deleGator), value: 0, callData: invokeDelegationCalldata_ }); // Execute delegations_ - executeBatch_UserOp(users.bob, redemptionActions_); + executeBatch_UserOp(users.bob, redemptionExecutions_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1248,8 +1262,8 @@ abstract contract DeleGatorTestSuite is BaseTest { assertEq(finalValue_, initialValue_ + 2); } - // should allow Alice to execute a combination of actions_ through a single UserOp - function test_allow_multiActionCombination_UserOp() public { + // should allow Alice to execute a combination of executionCallDatas_ through a single UserOp + function test_allow_multiExecutionCombination_UserOp() public { // Get DeleGator's Counter's initial count uint256 initialValueAlice_ = aliceDeleGatorCounter.count(); uint256 initialValueBob_ = bobDeleGatorCounter.count(); @@ -1270,24 +1284,29 @@ abstract contract DeleGatorTestSuite is BaseTest { // Invoke delegation calldata Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = + ExecutionLib.encodeSingle(address(aliceDeleGatorCounter), 0, abi.encodeWithSelector(Counter.increment.selector)); - bytes memory invokeDelegationCalldata_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory invokeDelegationCalldata_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); - // Create invoke delegation Actions - Action[] memory redemptionActions_ = new Action[](2); - redemptionActions_[0] = Action({ to: address(users.bob.deleGator), value: 0, data: invokeDelegationCalldata_ }); - redemptionActions_[1] = - Action({ to: address(bobDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create invoke delegation Executions + Execution[] memory redemptionExecutions_ = new Execution[](2); + redemptionExecutions_[0] = + Execution({ target: address(users.bob.deleGator), value: 0, callData: invokeDelegationCalldata_ }); + redemptionExecutions_[1] = Execution({ + target: address(bobDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute delegations_ - executeBatch_UserOp(users.bob, redemptionActions_); + executeBatch_UserOp(users.bob, redemptionExecutions_); // Get final count uint256 finalValueAlice_ = aliceDeleGatorCounter.count(); @@ -1300,21 +1319,27 @@ abstract contract DeleGatorTestSuite is BaseTest { ////////////////////////////// Invalid cases ////////////////////////////// - // should not allow a second Action to execute if the first Action fails - function test_notAllow_multiActionUserOp() public { + // should not allow a second Execution to execute if the first Execution fails + function test_notAllow_multiExecutionUserOp() public { // Get Alice's DeleGator's Counter's initial count uint256 initialValueAlice_ = aliceDeleGatorCounter.count(); uint256 initialValueBob_ = bobDeleGatorCounter.count(); - // Create actions_, incorrectly incrementing Bob's Counter first - Action[] memory actions_ = new Action[](2); - actions_[0] = - Action({ to: address(bobDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - actions_[1] = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create executionCallDatas_, incorrectly incrementing Bob's Counter first + Execution[] memory executionCallDatas_ = new Execution[](2); + executionCallDatas_[0] = Execution({ + target: address(bobDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + executionCallDatas_[1] = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); - // Execute actions_ - executeBatch_UserOp(users.alice, actions_); + // Execute executionCallDatas_ + executeBatch_UserOp(users.alice, executionCallDatas_); // Get final count uint256 finalValueAlice_ = aliceDeleGatorCounter.count(); @@ -1327,14 +1352,15 @@ abstract contract DeleGatorTestSuite is BaseTest { // should revert without reason and catch it function test_executionRevertsWithoutReason() public { - // Invalid action_, sending ETH to a contract that can't receive it. - Action memory action_ = Action({ to: address(aliceDeleGatorCounter), value: 1, data: hex"" }); + // Invalid execution_, sending ETH to a contract that can't receive it. + Execution memory execution_ = Execution({ target: address(aliceDeleGatorCounter), value: 1, callData: hex"" }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + ModeCode mode_ = ModeLib.encodeSimpleSingle(); vm.prank(address(delegationManager)); - - vm.expectRevert(abi.encodeWithSelector(ExecutionLib.FailedExecutionWithoutReason.selector)); - - users.alice.deleGator.executeDelegatedAction(action_); + // Expect it to emit a bubbled up reverted event + vm.expectRevert(); + users.alice.deleGator.executeFromExecutor(mode_, executionCallData_); } // should NOT allow Carol to redeem a delegation to Bob through a UserOp (offchain) @@ -1355,15 +1381,18 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Alice's delegation to Bob delegation_ = signDelegation(users.alice, delegation_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Carol's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.carol, delegations_, action_); + invokeDelegation_UserOp(users.carol, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1402,7 +1431,7 @@ abstract contract DeleGatorTestSuite is BaseTest { IEntryPoint.FailedOpWithRevert.selector, 0, "AA23 reverted", - abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPoint.selector) + abi.encodeWithSelector(DeleGatorCore.NotEntryPoint.selector) ) ); newEntryPoint_.handleOps(userOps_, bundler); @@ -1419,7 +1448,7 @@ abstract contract DeleGatorTestSuite is BaseTest { IEntryPoint.FailedOpWithRevert.selector, 0, "AA23 reverted", - abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPoint.selector) + abi.encodeWithSelector(DeleGatorCore.NotEntryPoint.selector) ) ); newEntryPoint_.handleOps(userOps_, bundler); @@ -1427,12 +1456,15 @@ abstract contract DeleGatorTestSuite is BaseTest { // should NOT allow a UserOp with an invalid signature function test_notAllow_invalidUserOpSignature() public { - // Create action_ - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create execution_ + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Create Alice's UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_); + bytes memory userOpCallData_ = abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_); PackedUserOperation memory userOp_ = createUserOp(address(users.alice.deleGator), userOpCallData_, hex""); // Bob signs UserOp @@ -1447,13 +1479,16 @@ abstract contract DeleGatorTestSuite is BaseTest { // should NOT allow a UserOp with a reused nonce function test_notAllow_nonceReuse() public { - // Create action_ - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create execution_ + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Create and Sign Alice's UserOp PackedUserOperation memory userOp_ = createAndSignUserOp( - users.alice, address(users.alice.deleGator), abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_) + users.alice, address(users.alice.deleGator), abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_) ); // Submit the UserOp through the Bundler @@ -1467,14 +1502,14 @@ abstract contract DeleGatorTestSuite is BaseTest { ////////////////////////////// EVENTS Emission ////////////////////////////// function test_event_Deposited() public { - Action memory action_ = Action({ - to: address(users.alice.deleGator), + Execution memory execution_ = Execution({ + target: address(users.alice.deleGator), value: 1 ether, - data: abi.encodeWithSelector(IDeleGatorCoreFull.addDeposit.selector) + callData: abi.encodeWithSelector(DeleGatorCore.addDeposit.selector) }); PackedUserOperation memory userOp_ = createAndSignUserOp( - users.alice, address(users.alice.deleGator), abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_) + users.alice, address(users.alice.deleGator), abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_) ); vm.expectEmit(true, false, false, true); @@ -1484,26 +1519,26 @@ abstract contract DeleGatorTestSuite is BaseTest { } function test_allow_withdrawDeposit() public { - Action memory action_ = Action({ - to: address(users.alice.deleGator), + Execution memory execution_ = Execution({ + target: address(users.alice.deleGator), value: 1 ether, - data: abi.encodeWithSelector(IDeleGatorCoreFull.addDeposit.selector) + callData: abi.encodeWithSelector(DeleGatorCore.addDeposit.selector) }); PackedUserOperation memory userOp_ = createAndSignUserOp( - users.alice, address(users.alice.deleGator), abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_) + users.alice, address(users.alice.deleGator), abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_) ); submitUserOp_Bundler(userOp_); - action_ = Action({ - to: address(users.alice.deleGator), + execution_ = Execution({ + target: address(users.alice.deleGator), value: 0 ether, - data: abi.encodeWithSelector(IDeleGatorCoreFull.withdrawDeposit.selector, address(users.alice.addr), 0.5 ether) + callData: abi.encodeWithSelector(DeleGatorCore.withdrawDeposit.selector, address(users.alice.addr), 0.5 ether) }); userOp_ = createAndSignUserOp( - users.alice, address(users.alice.deleGator), abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_) + users.alice, address(users.alice.deleGator), abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_) ); vm.expectEmit(true, false, false, true); @@ -1563,23 +1598,26 @@ abstract contract DeleGatorTestSuite is BaseTest { // Store delegation delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Redeem Bob's delegation Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); vm.prank(users.bob.addr); vm.expectRevert(); - delegationManager.redeemDelegation(encodedDelegations_, actions_); + delegationManager.redeemDelegations(permissionContexts_, oneSingularMode, executionCallDatas_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); @@ -1602,20 +1640,24 @@ abstract contract DeleGatorTestSuite is BaseTest { delegation_ = signDelegation(users.alice, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); // create user operation calldata for invokeDelegation - bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory userOpCallData_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); PackedUserOperation memory userOp_ = createAndSignUserOp(users.carol, address(users.carol.deleGator), userOpCallData_); @@ -1652,11 +1694,14 @@ abstract contract DeleGatorTestSuite is BaseTest { // Sign Alice's delegation with Carol's private key delegation_ = signDelegation(users.carol, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // signing the userOp_ from bob uint256[] memory signers_ = new uint256[](1); @@ -1664,11 +1709,12 @@ abstract contract DeleGatorTestSuite is BaseTest { Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory userOpCallData_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); PackedUserOperation memory userOp_ = createAndSignUserOp(users.bob, address(users.bob.deleGator), userOpCallData_); @@ -1705,20 +1751,24 @@ abstract contract DeleGatorTestSuite is BaseTest { // Carol signs the delegation using Alice's domain hash delegation_ = signDelegation(users.carol, delegation_); - // Create Bob's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + // Create Bob's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); // create user operation calldata for invokeDelegation - bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory userOpCallData_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); // create and sign user operation with Bob PackedUserOperation memory userOp_ = createAndSignUserOp(users.bob, address(users.bob.deleGator), userOpCallData_); @@ -1739,20 +1789,29 @@ abstract contract DeleGatorTestSuite is BaseTest { entryPoint.handleOps(userOps_, bundler); } - // Should revert if executeDelegatedAction is called from an address other than the DelegationManager + // Should revert if executeFromExecutor is called from an address other than the DelegationManager function test_notAllow_notDelegationManager() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotDelegationManager.selector)); - users.alice.deleGator.executeDelegatedAction(action_); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + ModeCode mode_ = ModeLib.encodeSimpleSingle(); + + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotDelegationManager.selector)); + users.alice.deleGator.executeFromExecutor(mode_, executionCallData_); } // Should revert if execute is called from an address other than the EntryPoint function test_notAllow_notEntryPoint() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPoint.selector)); - users.alice.deleGator.execute(action_); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPoint.selector)); + users.alice.deleGator.execute(execution_); } // Should revert if the delegation chain contains invalid authority @@ -1792,22 +1851,26 @@ abstract contract DeleGatorTestSuite is BaseTest { bobDelegation_ = signDelegation(users.bob, bobDelegation_); - // Create Carol's action - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Carol's execution + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Delegation[] memory delegations_ = new Delegation[](2); delegations_[0] = bobDelegation_; delegations_[1] = aliceDelegation_; - bytes[] memory encodedDelegations_ = new bytes[](1); - encodedDelegations_[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // create user operation calldata for invokeDelegation - bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, encodedDelegations_, actions_); + bytes memory userOpCallData_ = abi.encodeWithSelector( + DeleGatorCore.redeemDelegations.selector, permissionContexts_, oneSingularMode, executionCallDatas_ + ); PackedUserOperation memory userOp_ = createAndSignUserOp(users.carol, address(users.carol.deleGator), userOpCallData_); diff --git a/test/DelegationManagerTest.t.sol b/test/DelegationManagerTest.t.sol index 00a23f4..285d369 100644 --- a/test/DelegationManagerTest.t.sol +++ b/test/DelegationManagerTest.t.sol @@ -6,30 +6,47 @@ import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { ShortStrings, ShortString } from "@openzeppelin/contracts/utils/ShortStrings.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { Counter } from "./utils/Counter.t.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; -import { Delegation, Caveat, Action } from "../src/utils/Types.sol"; +import { Delegation, Caveat, Execution, ModeCode } from "../src/utils/Types.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; import { DelegationManager } from "../src/DelegationManager.sol"; import { Invalid1271Returns, Invalid1271Reverts } from "./utils/Invalid1271.t.sol"; import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; -import { EIP712_DOMAIN_TYPEHASH } from "../src/utils/Typehashes.sol"; +import { EIP712_DOMAIN_TYPEHASH } from "../src/utils/Constants.sol"; import { MockCaveatEnforcer } from "./utils/MockCaveatEnforcer.sol"; import { MockFailureCaveatEnforcer } from "./utils/MockFailureCaveatEnforcer.sol"; contract DelegationManagerTest is BaseTest { + using ModeLib for ModeCode; using ShortStrings for *; + ////////////////////////////// Setup ////////////////////////////// string private _nameFallback; string private _versionFallback; + ModeCode[] _oneSingularMode; + ModeCode[] _twoSingularModes; constructor() { IMPLEMENTATION = Implementation.Hybrid; SIGNATURE_TYPE = SignatureType.RawP256; } + function setUp() public virtual override { + super.setUp(); + + _oneSingularMode = new ModeCode[](1); + _oneSingularMode[0] = ModeLib.encodeSimpleSingle(); + + _twoSingularModes = new ModeCode[](2); + _twoSingularModes[0] = ModeLib.encodeSimpleSingle(); + _twoSingularModes[1] = ModeLib.encodeSimpleSingle(); + } + ////////////////////////////// Events ////////////////////////////// event SetDomain( @@ -50,7 +67,7 @@ contract DelegationManagerTest is BaseTest { // Should allow reading contract version function test_allow_contractVersionReads() public { string memory contractVersion_ = delegationManager.VERSION(); - assertEq("1.0.0", contractVersion_); + assertEq("1.1.0", contractVersion_); } // Should allow reading contract version @@ -164,13 +181,13 @@ contract DelegationManagerTest is BaseTest { vm.prank(address(users.bob.addr)); - bytes[] memory dataArray = new bytes[](1); - dataArray[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = Action({ to: address(0), data: new bytes(0), value: 0 }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = abi.encode(Execution({ target: address(0), callData: new bytes(0), value: 0 })); - delegationManager.redeemDelegation(dataArray, actions_); + delegationManager.redeemDelegations(permissionContexts_, _oneSingularMode, executionCallDatas_); } function test_notAllow_invalidSignatureReturns() public { @@ -192,13 +209,13 @@ contract DelegationManagerTest is BaseTest { vm.prank(address(users.bob.addr)); - bytes[] memory dataArray = new bytes[](1); - dataArray[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); - Action[] memory actions_ = new Action[](1); - actions_[0] = Action({ to: address(0), data: new bytes(0), value: 0 }); + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = abi.encode(Execution({ target: address(0), callData: new bytes(0), value: 0 })); - delegationManager.redeemDelegation(dataArray, actions_); + delegationManager.redeemDelegations(permissionContexts_, _oneSingularMode, executionCallDatas_); } function test_allow_redeemBatchDelegation() public { @@ -226,7 +243,7 @@ contract DelegationManagerTest is BaseTest { }); delegation2.caveats[0] = Caveat({ enforcer: address(mockEnforcer), terms: hex"", args: hex"" }); - bytes[] memory permissionContexts = new bytes[](2); + bytes[] memory permissionContexts_ = new bytes[](2); // Sign delegations delegation1 = signDelegation(users.alice, delegation1); @@ -234,97 +251,26 @@ contract DelegationManagerTest is BaseTest { Delegation[] memory delegations1 = new Delegation[](1); delegations1[0] = delegation1; - permissionContexts[0] = abi.encode(delegations1); + permissionContexts_[0] = abi.encode(delegations1); Delegation[] memory delegations2 = new Delegation[](1); delegations2[0] = delegation2; - permissionContexts[1] = abi.encode(delegations2); + permissionContexts_[1] = abi.encode(delegations2); - Action[] memory actions = new Action[](2); - actions[0] = Action({ to: address(0), data: hex"", value: 0 }); - actions[1] = Action({ to: address(0), data: hex"", value: 0 }); + bytes[] memory executionCallDatas_ = new bytes[](2); + executionCallDatas_[0] = ExecutionLib.encodeSingle(address(0), 0, hex""); + executionCallDatas_[1] = ExecutionLib.encodeSingle(address(0), 0, hex""); vm.prank(address(users.bob.addr)); - delegationManager.redeemDelegation(permissionContexts, actions); + delegationManager.redeemDelegations(permissionContexts_, _twoSingularModes, executionCallDatas_); - // Assert that beforeHook was called for each action + // Assert that beforeHook was called for each execution assertEq(mockEnforcer.beforeHookCallCount(), 2); - // Assert that afterHook was called after executing all actions + // Assert that afterHook was called after executing all executions assertEq(mockEnforcer.afterHookCallCount(), 2); } - function test_allow_redeemBatchDelegationMixedWithNoDelegation() public { - Counter aliceDeleGatorCounter = new Counter(address(users.alice.deleGator)); - Counter bobDeleGatorCounter = new Counter(address(users.bob.deleGator)); - - // Create a mock caveat enforcers contract - MockCaveatEnforcer mockEnforcer = new MockCaveatEnforcer(); - - // Create delegations with caveats - Delegation memory delegation1 = Delegation({ - delegate: address(users.bob.deleGator), - delegator: address(users.alice.deleGator), - authority: ROOT_AUTHORITY, - caveats: new Caveat[](1), - salt: 0, - signature: hex"" - }); - delegation1.caveats[0] = Caveat({ enforcer: address(mockEnforcer), terms: hex"", args: hex"" }); - - // Create delegations with caveats - Delegation memory delegation3 = Delegation({ - delegate: address(users.bob.deleGator), - delegator: address(users.alice.deleGator), - authority: ROOT_AUTHORITY, - caveats: new Caveat[](1), - salt: 0, - signature: hex"" - }); - delegation3.caveats[0] = Caveat({ enforcer: address(mockEnforcer), terms: hex"", args: hex"" }); - - bytes[] memory permissionContexts = new bytes[](3); - - // Sign delegations - delegation1 = signDelegation(users.alice, delegation1); - delegation3 = signDelegation(users.alice, delegation3); - - Delegation[] memory delegations1 = new Delegation[](1); - delegations1[0] = delegation1; - permissionContexts[0] = abi.encode(delegations1); - - // This is an empty delegation for a self execution - Delegation[] memory delegations2 = new Delegation[](0); - permissionContexts[1] = abi.encode(delegations2); - - Delegation[] memory delegations3 = new Delegation[](1); - delegations3[0] = delegation3; - permissionContexts[2] = abi.encode(delegations3); - - Action memory incrementCounter_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - Action[] memory actions = new Action[](3); - actions[0] = incrementCounter_; - actions[1] = - Action({ to: address(bobDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - actions[2] = incrementCounter_; - - assertEq(aliceDeleGatorCounter.count(), 0); - assertEq(bobDeleGatorCounter.count(), 0); - - vm.prank(address(users.bob.deleGator)); - delegationManager.redeemDelegation(permissionContexts, actions); - - // Assert that beforeHook was called for each action - assertEq(mockEnforcer.beforeHookCallCount(), 2); - - // Assert that afterHook was called after executing all actions - assertEq(mockEnforcer.afterHookCallCount(), 2); - - assertEq(aliceDeleGatorCounter.count(), 2); - assertEq(bobDeleGatorCounter.count(), 1); - } - function test_allow_redeemBatchWithEoaInSecondBatchDelegation() public { // Create a mock caveat enforcers contract MockCaveatEnforcer mockEnforcer = new MockCaveatEnforcer(); @@ -362,7 +308,7 @@ contract DelegationManagerTest is BaseTest { }); delegation3.caveats[0] = Caveat({ enforcer: address(mockEnforcer), terms: hex"", args: hex"" }); - bytes[] memory permissionContexts = new bytes[](2); + bytes[] memory permissionContexts_ = new bytes[](2); // Sign delegations delegation1 = signDelegation(users.alice, delegation1); @@ -376,24 +322,24 @@ contract DelegationManagerTest is BaseTest { Delegation[] memory delegations1 = new Delegation[](1); delegations1[0] = delegation1; - permissionContexts[0] = abi.encode(delegations1); + permissionContexts_[0] = abi.encode(delegations1); Delegation[] memory delegations2 = new Delegation[](2); delegations2[0] = delegation3; delegations2[1] = delegation2; - permissionContexts[1] = abi.encode(delegations2); + permissionContexts_[1] = abi.encode(delegations2); - Action[] memory actions = new Action[](2); - actions[0] = Action({ to: address(0), data: hex"", value: 0 }); - actions[1] = Action({ to: address(0), data: hex"", value: 0 }); + bytes[] memory executionCallDatas_ = new bytes[](2); + executionCallDatas_[0] = ExecutionLib.encodeSingle(address(0), 0, hex""); + executionCallDatas_[1] = ExecutionLib.encodeSingle(address(0), 0, hex""); vm.prank(address(users.carol.addr)); - delegationManager.redeemDelegation(permissionContexts, actions); + delegationManager.redeemDelegations(permissionContexts_, _twoSingularModes, executionCallDatas_); - // Assert that beforeHook was called for each action + // Assert that beforeHook was called for each execution assertEq(mockEnforcer.beforeHookCallCount(), 3); - // Assert that afterHook was called after executing all actions + // Assert that afterHook was called after executing all executions assertEq(mockEnforcer.afterHookCallCount(), 3); } @@ -424,7 +370,7 @@ contract DelegationManagerTest is BaseTest { }); delegation2.caveats[0] = Caveat({ enforcer: address(mockFailureEnforcer), terms: hex"", args: hex"" }); - bytes[] memory permissionContexts = new bytes[](2); + bytes[] memory permissionContexts_ = new bytes[](2); // Sign delegations delegation1 = signDelegation(users.alice, delegation1); @@ -432,19 +378,19 @@ contract DelegationManagerTest is BaseTest { Delegation[] memory delegations1 = new Delegation[](1); delegations1[0] = delegation1; - permissionContexts[0] = abi.encode(delegations1); + permissionContexts_[0] = abi.encode(delegations1); Delegation[] memory delegations2 = new Delegation[](1); delegations2[0] = delegation2; - permissionContexts[1] = abi.encode(delegations2); + permissionContexts_[1] = abi.encode(delegations2); - Action[] memory actions_ = new Action[](2); - actions_[0] = Action({ to: address(0), data: hex"", value: 0 }); - actions_[1] = Action({ to: address(0), data: hex"", value: 0 }); + bytes[] memory executionCallDatas_ = new bytes[](2); + executionCallDatas_[0] = ExecutionLib.encodeSingle(address(0), 0, hex""); + executionCallDatas_[1] = ExecutionLib.encodeSingle(address(0), 0, hex""); vm.prank(address(users.bob.addr)); vm.expectRevert(); - delegationManager.redeemDelegation(permissionContexts, actions_); + delegationManager.redeemDelegations(permissionContexts_, _twoSingularModes, executionCallDatas_); } /////////////////////////////// Ownership ////////////////////////////// @@ -535,13 +481,11 @@ contract DelegationManagerTest is BaseTest { vm.startPrank(delegationManager.owner()); delegationManager.pause(); - Action[] memory actions_ = new Action[](1); - + bytes[] memory executionCallDatas_ = new bytes[](1); bytes[] memory permissionContexts_ = new bytes[](1); - permissionContexts_[0] = hex""; vm.expectRevert(Pausable.EnforcedPause.selector); - delegationManager.redeemDelegation(permissionContexts_, actions_); + delegationManager.redeemDelegations(permissionContexts_, _oneSingularMode, executionCallDatas_); } // Should fail to pause when the pause is active diff --git a/test/HybridDeleGatorTest.t.sol b/test/HybridDeleGatorTest.t.sol index a077971..f2bf097 100644 --- a/test/HybridDeleGatorTest.t.sol +++ b/test/HybridDeleGatorTest.t.sol @@ -6,18 +6,20 @@ import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy import { FCL_ecdsa_utils } from "@freshCryptoLib/FCL_ecdsa_utils.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import { BytesLib } from "@bytes-utils/BytesLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { SigningUtilsLib } from "./utils/SigningUtilsLib.t.sol"; import { StorageUtilsLib } from "./utils/StorageUtilsLib.t.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; -import { Action, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; +import { Execution, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; import { HybridDeleGator } from "../src/HybridDeleGator.sol"; -import { IDeleGatorCoreFull } from "../src/interfaces/IDeleGatorCoreFull.sol"; import { IDeleGatorCore } from "../src/interfaces/IDeleGatorCore.sol"; +import { DeleGatorCore } from "../src/DeleGatorCore.sol"; import { IERC173 } from "../src/interfaces/IERC173.sol"; import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; +import { EXECUTE_SINGULAR_SIGNATURE } from "./utils/Constants.sol"; contract HybridDeleGator_Test is BaseTest { using MessageHashUtils for bytes32; @@ -95,12 +97,12 @@ contract HybridDeleGator_Test is BaseTest { // Should emit AddedP256Key in addKey function test_keyAdded_addKey() public { // Create and Sign UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(aliceDeleGator), + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(aliceDeleGator), value: 0, - data: abi.encodeWithSelector(HybridDeleGator.addKey.selector, keyId, users.alice.x, users.alice.y) + callData: abi.encodeWithSelector(HybridDeleGator.addKey.selector, keyId, users.alice.x, users.alice.y) }) ); PackedUserOperation memory userOp_ = createAndSignUserOp(users.alice, address(aliceDeleGator), userOpCallData_); @@ -117,9 +119,13 @@ contract HybridDeleGator_Test is BaseTest { execute_UserOp(users.alice, abi.encodeWithSelector(HybridDeleGator.addKey.selector, keyId, users.alice.x, users.alice.y)); // Create and Sign UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ to: address(aliceDeleGator), value: 0, data: abi.encodeWithSelector(HybridDeleGator.removeKey.selector, keyId) }) + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(aliceDeleGator), + value: 0, + callData: abi.encodeWithSelector(HybridDeleGator.removeKey.selector, keyId) + }) ); PackedUserOperation memory userOp_ = createAndSignUserOp(users.alice, address(aliceDeleGator), userOpCallData_); @@ -328,7 +334,7 @@ contract HybridDeleGator_Test is BaseTest { // Replace the signers vm.startPrank(address(aliceDeleGator)); aliceDeleGator.reinitialize( - uint8(IDeleGatorCoreFull(address(aliceDeleGator)).getInitializedVersion() + 1), + uint8(DeleGatorCore(payable(address(aliceDeleGator))).getInitializedVersion() + 1), users.bob.addr, keyIds_, xValues_, @@ -363,7 +369,7 @@ contract HybridDeleGator_Test is BaseTest { // Replace the signers vm.startPrank(address(aliceDeleGator)); aliceDeleGator.reinitialize( - uint8(IDeleGatorCoreFull(address(aliceDeleGator)).getInitializedVersion() + 1), + uint8(DeleGatorCore(payable(address(aliceDeleGator))).getInitializedVersion() + 1), users.bob.addr, keyIds_, xValues_, @@ -402,11 +408,11 @@ contract HybridDeleGator_Test is BaseTest { execute_UserOp( users.alice, abi.encodeWithSelector( - IDeleGatorCoreFull.upgradeToAndCall.selector, + DeleGatorCore.upgradeToAndCall.selector, address(hybridDeleGatorImpl), abi.encodeWithSelector( HybridDeleGator.reinitialize.selector, - IDeleGatorCoreFull(deleGator_).getInitializedVersion() + 1, + DeleGatorCore(deleGator_).getInitializedVersion() + 1, users.bob.addr, keyIds_, xValues_, @@ -418,7 +424,7 @@ contract HybridDeleGator_Test is BaseTest { ); // Assert DeleGator is Hybrid - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); // ERC4337 should be the same assertEq(delegatorCoreStoragePre_, vm.load(deleGator_, DELEGATOR_CORE_STORAGE_LOCATION)); @@ -491,7 +497,7 @@ contract HybridDeleGator_Test is BaseTest { function test_notAllow_transferOwnership_directOwner() public { // Submit Alice's tx vm.prank(users.alice.addr); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.transferOwnership(users.bob.addr); } @@ -499,7 +505,7 @@ contract HybridDeleGator_Test is BaseTest { function test_notAllow_transferOwnership_directNonOwner() public { // Submit Bob's tx vm.prank(users.bob.addr); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.transferOwnership(users.bob.addr); } @@ -525,12 +531,12 @@ contract HybridDeleGator_Test is BaseTest { assertEq(users.alice.addr, onlyEoaHybridDeleGator.owner()); // Create and Sign UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(onlyEoaHybridDeleGator), + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(onlyEoaHybridDeleGator), value: 0, - data: abi.encodeWithSelector( + callData: abi.encodeWithSelector( HybridDeleGator.updateSigners.selector, users.bob.addr, new string[](0), new uint256[](0), new uint256[](0) ) }) @@ -560,12 +566,12 @@ contract HybridDeleGator_Test is BaseTest { yValues_[0] = users.bob.y; // Create and Sign UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(onlyEoaHybridDeleGator), + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(onlyEoaHybridDeleGator), value: 0, - data: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, address(0), keyIds_, xValues_, yValues_) + callData: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, address(0), keyIds_, xValues_, yValues_) }) ); PackedUserOperation memory userOp_ = createUserOp(address(onlyEoaHybridDeleGator), userOpCallData_); @@ -595,12 +601,12 @@ contract HybridDeleGator_Test is BaseTest { yValues_[0] = users.bob.y; // Create and Sign UserOp - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(onlyEoaHybridDeleGator), + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(onlyEoaHybridDeleGator), value: 0, - data: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, users.bob.addr, keyIds_, xValues_, yValues_) + callData: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, users.bob.addr, keyIds_, xValues_, yValues_) }) ); PackedUserOperation memory userOp_ = createUserOp(address(onlyEoaHybridDeleGator), userOpCallData_); @@ -632,11 +638,11 @@ contract HybridDeleGator_Test is BaseTest { xValues_[0] = users.bob.x; yValues_[0] = users.bob.y; - // Create the action that would be executed - Action memory action_ = Action({ - to: address(aliceDeleGator), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGator), value: 0, - data: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, users.bob.addr, keyIds_, xValues_, yValues_) + callData: abi.encodeWithSelector(HybridDeleGator.updateSigners.selector, users.bob.addr, keyIds_, xValues_, yValues_) }); Caveat[] memory caveats_ = new Caveat[](0); @@ -656,7 +662,7 @@ contract HybridDeleGator_Test is BaseTest { Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Bob is the EOA owner now (uint256 x__, uint256 y__) = aliceDeleGator.getKey(users.bob.name); @@ -685,13 +691,13 @@ contract HybridDeleGator_Test is BaseTest { vm.prank(address(entryPoint)); aliceDeleGator.addKey(webAuthnKeyId_, xWebAuthn_, yWebAuthn_); - // Create the action that would be executed - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(aliceDeleGator), + // Create the execution that would be executed + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(aliceDeleGator), value: 0, - data: abi.encodeWithSelector(Ownable.transferOwnership.selector, address(1)) + callData: abi.encodeWithSelector(Ownable.transferOwnership.selector, address(1)) }) ); PackedUserOperation memory userOp_ = createUserOp(address(aliceDeleGator), userOpCallData_); @@ -745,13 +751,13 @@ contract HybridDeleGator_Test is BaseTest { vm.prank(address(entryPoint)); aliceDeleGator.addKey(webAuthnKeyId_, xWebAuthn_, yWebAuthn_); - // Create the action that would be executed - bytes memory userOpCallData_ = abi.encodeWithSelector( - IDeleGatorCoreFull.execute.selector, - Action({ - to: address(aliceDeleGator), + // Create the execution that would be executed + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SINGULAR_SIGNATURE, + Execution({ + target: address(aliceDeleGator), value: 0, - data: abi.encodeWithSelector(Ownable.transferOwnership.selector, address(1)) + callData: abi.encodeWithSelector(Ownable.transferOwnership.selector, address(1)) }) ); PackedUserOperation memory userOp_ = createUserOp(address(aliceDeleGator), userOpCallData_); diff --git a/test/InviteTest.t.sol b/test/InviteTest.t.sol index 0a64ec0..d833572 100644 --- a/test/InviteTest.t.sol +++ b/test/InviteTest.t.sol @@ -3,19 +3,23 @@ pragma solidity 0.8.23; import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; -import { Delegation, Action, PackedUserOperation, Caveat } from "../src/utils/Types.sol"; +import { Delegation, Execution, PackedUserOperation, Caveat, ModeCode } from "../src/utils/Types.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; import { Counter } from "./utils/Counter.t.sol"; import { MultiSigDeleGator } from "../src/MultiSigDeleGator.sol"; -import { IDeleGatorCoreFull } from "../src/interfaces/IDeleGatorCoreFull.sol"; +import { DeleGatorCore } from "../src/DeleGatorCore.sol"; import { SimpleFactory } from "../src/utils/SimpleFactory.sol"; import { AllowedTargetsEnforcer } from "../src/enforcers/AllowedTargetsEnforcer.sol"; import { AllowedMethodsEnforcer } from "../src/enforcers/AllowedMethodsEnforcer.sol"; +import { EXECUTE_SINGULAR_SIGNATURE } from "./utils/Constants.sol"; contract InviteTest is BaseTest { using MessageHashUtils for bytes32; + using ModeLib for ModeCode; constructor() { IMPLEMENTATION = Implementation.Hybrid; @@ -63,22 +67,22 @@ contract InviteTest is BaseTest { abi.encodeWithSelector(SimpleFactory.deploy.selector, abi.encodePacked(type(ERC1967Proxy).creationCode, args_), salt) ); - // Create action to send ETH to Bob - Action memory action_ = Action({ to: address(users.bob.addr), value: 1, data: hex"" }); + // Create execution to send ETH to Bob + Execution memory execution_ = Execution({ target: address(users.bob.addr), value: 1, callData: hex"" }); - // Give the new MultiSigDeleGator some funds to pay for the action + // Give the new MultiSigDeleGator some funds to pay for the execution vm.deal(predictedAddr_, 100); // Preload the EntryPoint with funds for the new MultiSigDeleGator vm.prank(users.alice.addr); entryPoint.depositTo{ value: 5 ether }(predictedAddr_); - // Fetch balance before action executes + // Fetch balance before execution executes uint256 balanceBefore_ = users.bob.addr.balance; // Create and Sign UserOp with Bob's key PackedUserOperation memory userOp_ = createAndSignUserOp( - users.bob, predictedAddr_, abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_), initcode_ + users.bob, predictedAddr_, abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_), initcode_ ); // Validate the contract hasn't been deployed yet @@ -87,7 +91,7 @@ contract InviteTest is BaseTest { // Submit the UserOp through the Bundler submitUserOp_Bundler(userOp_); - // Fetch balance after action executes + // Fetch balance after execution executes uint256 balanceAfter_ = users.bob.addr.balance; assertEq(balanceAfter_, balanceBefore_ + 1); @@ -149,7 +153,7 @@ contract InviteTest is BaseTest { delegation_ = signDelegation(users.alice, delegation_); } - // Give the new MultiSigDeleGator some funds to pay for the action + // Give the new MultiSigDeleGator some funds to pay for the execution vm.deal(predictedAddr_, 100); // Preload the EntryPoint with funds for the new MultiSigDeleGator @@ -160,25 +164,29 @@ contract InviteTest is BaseTest { uint256[] memory counts_ = new uint256[](2); counts_[0] = aliceDeleGatorCounter.count(); - // Create action to deploy a new MultiSigDeleGator and execute Bob's UserOp - Action memory action_; - { - action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); - } + // Create execution to deploy a new MultiSigDeleGator and execute Bob's UserOp + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Bob's UserOp { Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - bytes[] memory dataArray = new bytes[](1); - dataArray[0] = abi.encode(delegations_); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(delegations_); + + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); - Action[] memory actions_ = new Action[](1); - actions_[0] = action_; + ModeCode[] memory modes_ = new ModeCode[](1); + modes_[0] = ModeLib.encodeSimpleSingle(); - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, dataArray, actions_); + bytes memory userOpCallData_ = + abi.encodeWithSelector(DeleGatorCore.redeemDelegations.selector, permissionContexts_, modes_, executionCallDatas_); PackedUserOperation memory userOp_ = createUserOp(predictedAddr_, userOpCallData_, initcode_); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(users.bob, userOpHash_.toEthSignedMessageHash()); diff --git a/test/MultiSigDeleGatorTest.t.sol b/test/MultiSigDeleGatorTest.t.sol index 8351c51..a143935 100644 --- a/test/MultiSigDeleGatorTest.t.sol +++ b/test/MultiSigDeleGatorTest.t.sol @@ -10,12 +10,12 @@ import { ERC1967Proxy as DeleGatorProxy } from "@openzeppelin/contracts/proxy/ER import { SigningUtilsLib } from "./utils/SigningUtilsLib.t.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; -import { Action, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; +import { Execution, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; import { AccountSorterLib } from "./utils/AccountSorterLib.t.sol"; import { MultiSigDeleGator } from "../src/MultiSigDeleGator.sol"; import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; -import { IDeleGatorCoreFull } from "../src/interfaces/IDeleGatorCoreFull.sol"; +import { DeleGatorCore } from "../src/DeleGatorCore.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; import { Counter } from "./utils/Counter.t.sol"; import { SimpleFactory } from "../src/utils/SimpleFactory.sol"; @@ -126,7 +126,7 @@ contract MultiSigDeleGatorTest is BaseTest { abi.encodeWithSelector(SimpleFactory.deploy.selector, abi.encodePacked(type(DeleGatorProxy).creationCode, args_), salt) ); - // Give the new MultiSigDeleGator some funds to pay for the action + // Give the new MultiSigDeleGator some funds to pay for the execution vm.deal(predictedAddr_, 100); // Preload the EntryPoint with funds for the new MultiSigDeleGator @@ -167,15 +167,18 @@ contract MultiSigDeleGatorTest is BaseTest { bytes memory signature_ = SigningUtilsLib.signHash_MultiSig(sharedDeleGatorPrivateKeys, typedDataHash_); delegation_.signature = signature_; - // Create Dave's action - Action memory action_ = - Action({ to: address(sharedDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create Dave's execution + Execution memory execution_ = Execution({ + target: address(sharedDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); // Execute Dave's UserOp Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.dave, delegations_, action_); + invokeDelegation_UserOp(users.dave, delegations_, execution_); // Get final count uint256 finalValue_ = sharedDeleGatorCounter.count(); @@ -518,7 +521,7 @@ contract MultiSigDeleGatorTest is BaseTest { // Don't allow someone else to replace Alice's MultiSigDeleGator's signers // replaceSigner call must come from the MultiSigDeleGator itself vm.prank(address(users.alice.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.replaceSigner(users.alice.addr, users.bob.addr); // Mock calls from the MultiSigDeleGator itself @@ -572,7 +575,7 @@ contract MultiSigDeleGatorTest is BaseTest { // Don't allow someone else to remove signers. // Call must come from the MultiSigDeleGator itself. vm.prank(address(users.alice.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.addSigner(users.bob.addr); // Mock calls from the MultiSigDeleGator itself @@ -656,7 +659,7 @@ contract MultiSigDeleGatorTest is BaseTest { // Don't allow someone else to remove signers. // Call must come from the MultiSigDeleGator itself. vm.prank(address(users.alice.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.removeSigner(users.alice.addr); // Mock calls from the MultiSigDeleGator itself @@ -700,7 +703,7 @@ contract MultiSigDeleGatorTest is BaseTest { // Don't allow someone else to remove signers. // Call must come from the MultiSigDeleGator itself. vm.prank(address(users.alice.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.updateThreshold(1); uint256 preThreshold_ = aliceDeleGator.getThreshold(); @@ -729,17 +732,17 @@ contract MultiSigDeleGatorTest is BaseTest { // Don't allow Alice to update Alice's MultiSigDeleGator's signers vm.prank(address(users.alice.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.updateMultiSigParameters(signers_, 1, false); // Don't allow Bob to update Alice's MultiSigDeleGator's signers vm.prank(address(users.bob.addr)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.updateMultiSigParameters(signers_, 1, false); // Don't allow Bob's MultiSigDeleGator to update Alice's MultiSigDeleGator's signers vm.prank(address(bobDeleGator)); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); aliceDeleGator.updateMultiSigParameters(signers_, 1, false); } @@ -851,12 +854,12 @@ contract MultiSigDeleGatorTest is BaseTest { // Load DeleGator to manipulate address payable delegator_ = payable(address(aliceDeleGator)); - uint64 version_ = IDeleGatorCoreFull(delegator_).getInitializedVersion() + 1; + uint64 version_ = DeleGatorCore(delegator_).getInitializedVersion() + 1; address[] memory newSigners_ = new address[](1); newSigners_[0] = users.bob.addr; vm.prank(users.bob.addr); - vm.expectRevert(abi.encodeWithSelector(IDeleGatorCoreFull.NotEntryPointOrSelf.selector)); + vm.expectRevert(abi.encodeWithSelector(DeleGatorCore.NotEntryPointOrSelf.selector)); MultiSigDeleGator(delegator_).reinitialize(version_, newSigners_, 1, false); } diff --git a/test/ProxyMigrationTest.t.sol b/test/ProxyMigrationTest.t.sol index 2baaee1..9406fe7 100644 --- a/test/ProxyMigrationTest.t.sol +++ b/test/ProxyMigrationTest.t.sol @@ -10,13 +10,14 @@ import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/Mes import { SigningUtilsLib } from "./utils/SigningUtilsLib.t.sol"; import { StorageUtilsLib } from "./utils/StorageUtilsLib.t.sol"; import { Implementation, SignatureType } from "./utils/Types.t.sol"; -import { Action, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; +import { Execution, PackedUserOperation, Caveat, Delegation } from "../src/utils/Types.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; import { HybridDeleGator } from "../src/HybridDeleGator.sol"; import { MultiSigDeleGator } from "../src/MultiSigDeleGator.sol"; -import { IDeleGatorCoreFull } from "../src/interfaces/IDeleGatorCoreFull.sol"; +import { DeleGatorCore } from "../src/DeleGatorCore.sol"; import { IDelegationManager } from "../src/interfaces/IDelegationManager.sol"; import { EncoderLib } from "../src/libraries/EncoderLib.sol"; +import { EXECUTE_SINGULAR_SIGNATURE } from "./utils/Constants.sol"; contract HybridDeleGator_Test is BaseTest { using MessageHashUtils for bytes32; @@ -43,18 +44,18 @@ contract HybridDeleGator_Test is BaseTest { vm.deal(deleGator_, 100 ether); // Assert DeleGator is MultiSig - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); // Upgrade to Hybrid - Action memory action_ = Action({ - to: address(deleGator_), + Execution memory execution_ = Execution({ + target: address(deleGator_), value: 0, - data: abi.encodeWithSelector( - IDeleGatorCoreFull.upgradeToAndCall.selector, + callData: abi.encodeWithSelector( + DeleGatorCore.upgradeToAndCall.selector, address(hybridDeleGatorImpl), abi.encodeWithSelector( HybridDeleGator.reinitialize.selector, - IDeleGatorCoreFull(deleGator_).getInitializedVersion() + 1, + DeleGatorCore(deleGator_).getInitializedVersion() + 1, users.alice.addr, new string[](0), new uint256[](0), @@ -63,7 +64,7 @@ contract HybridDeleGator_Test is BaseTest { ) ) }); - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_); + bytes memory userOpCallData_ = abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_); PackedUserOperation memory userOp_ = createUserOp(address(deleGator_), userOpCallData_); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(SignatureType.MultiSig, users.alice, userOpHash_.toEthSignedMessageHash()); @@ -74,7 +75,7 @@ contract HybridDeleGator_Test is BaseTest { submitUserOp_Bundler(userOp_); // Assert DeleGator is Hybrid - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); // Assert MultiSig storage was cleared bytes32 DEFAULT_MULTISIG_DELEGATOR_STORAGE_LOCATION = StorageUtilsLib.getStorageLocation("DeleGator.MultiSigDeleGator"); @@ -95,22 +96,18 @@ contract HybridDeleGator_Test is BaseTest { // Upgrade to MultiSig owners_ = new address[](1); owners_[0] = users.alice.addr; - action_ = Action({ - to: address(deleGator_), + execution_ = Execution({ + target: address(deleGator_), value: 0, - data: abi.encodeWithSelector( - IDeleGatorCoreFull.upgradeToAndCall.selector, + callData: abi.encodeWithSelector( + DeleGatorCore.upgradeToAndCall.selector, address(multiSigDeleGatorImpl), abi.encodeWithSelector( - MultiSigDeleGator.reinitialize.selector, - IDeleGatorCoreFull(deleGator_).getInitializedVersion() + 1, - owners_, - 1, - false + MultiSigDeleGator.reinitialize.selector, DeleGatorCore(deleGator_).getInitializedVersion() + 1, owners_, 1, false ) ) }); - userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_); + userOpCallData_ = abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_); userOp_ = createUserOp(address(deleGator_), userOpCallData_); userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(SignatureType.EOA, users.alice, userOpHash_.toEthSignedMessageHash()); @@ -121,7 +118,7 @@ contract HybridDeleGator_Test is BaseTest { submitUserOp_Bundler(userOp_); // Assert DeleGator is MultiSig - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); // Assert Hybrid storage was cleared bytes32 DEFAULT_HYBRID_DELEGATOR_STORAGE_LOCATION = StorageUtilsLib.getStorageLocation("DeleGator.HybridDeleGator"); @@ -148,18 +145,18 @@ contract HybridDeleGator_Test is BaseTest { vm.deal(deleGator_, 100 ether); // Assert DeleGator is MultiSig - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(multiSigDeleGatorImpl)); // Upgrade to Hybrid - Action memory action_ = Action({ - to: address(deleGator_), + Execution memory execution_ = Execution({ + target: address(deleGator_), value: 0, - data: abi.encodeWithSelector( - IDeleGatorCoreFull.upgradeToAndCallAndRetainStorage.selector, + callData: abi.encodeWithSelector( + DeleGatorCore.upgradeToAndCallAndRetainStorage.selector, address(hybridDeleGatorImpl), abi.encodeWithSelector( HybridDeleGator.reinitialize.selector, - IDeleGatorCoreFull(deleGator_).getInitializedVersion() + 1, + DeleGatorCore(deleGator_).getInitializedVersion() + 1, users.alice.addr, new string[](0), new uint256[](0), @@ -168,7 +165,7 @@ contract HybridDeleGator_Test is BaseTest { ) ) }); - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, action_); + bytes memory userOpCallData_ = abi.encodeWithSignature(EXECUTE_SINGULAR_SIGNATURE, execution_); PackedUserOperation memory userOp_ = createUserOp(address(deleGator_), userOpCallData_); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(SignatureType.MultiSig, users.alice, userOpHash_.toEthSignedMessageHash()); @@ -176,7 +173,7 @@ contract HybridDeleGator_Test is BaseTest { submitUserOp_Bundler(userOp_); // Assert DeleGator is Hybrid - assertEq(address(IDeleGatorCoreFull(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); + assertEq(address(DeleGatorCore(deleGator_).getImplementation()), address(hybridDeleGatorImpl)); // Assert MultiSig storage was not cleared bytes32 DEFAULT_MULTISIG_DELEGATOR_STORAGE_LOCATION = StorageUtilsLib.getStorageLocation("DeleGator.MultiSigDeleGator"); diff --git a/test/enforcers/AllowedCalldataEnforcer.t.sol b/test/enforcers/AllowedCalldataEnforcer.t.sol index 93f3924..b845486 100644 --- a/test/enforcers/AllowedCalldataEnforcer.t.sol +++ b/test/enforcers/AllowedCalldataEnforcer.t.sol @@ -4,9 +4,11 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { BytesLib } from "@bytes-utils/BytesLib.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import { Counter } from "../utils/Counter.t.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { AllowedCalldataEnforcer } from "../../src/enforcers/AllowedCalldataEnforcer.sol"; import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol"; @@ -21,10 +23,13 @@ contract DummyContract { } contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// AllowedCalldataEnforcer public allowedCalldataEnforcer; BasicERC20 public basicCF20; BasicCF721 public basicCF721; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -40,12 +45,13 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { // should allow a single method to be called when a single function parameter is equal function test_singleMethodCanBeCalledWithEqualParam() public { - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(100)) + callData: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(100)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator uint256 paramStart_ = abi.encodeWithSelector(BasicERC20.mint.selector, address(0)).length; @@ -53,7 +59,7 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { bytes memory inputTerms_ = abi.encodePacked(paramStart_, paramValue_); vm.prank(address(delegationManager)); - allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should allow a method to be called when a single function parameter that is a dynamic array @@ -62,52 +68,55 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { param[0] = 1; param[1] = 2; - // Create the action that would be executed - Action memory action_ = - Action({ to: address(0), value: 0, data: abi.encodeWithSelector(DummyContract.arrayFn.selector, param) }); + // Create the execution that would be executed + Execution memory execution_ = + Execution({ target: address(0), value: 0, callData: abi.encodeWithSelector(DummyContract.arrayFn.selector, param) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // The subset of the calldata that includes the index of the calldata where the dynamic array starts bytes memory offsetTerms_ = abi.encodePacked(uint256(4), uint256(32)); // The subset of the calldata that includes the number of elements in the array bytes memory lengthTerms_ = abi.encodePacked(uint256(36), uint256(2)); // The subset of the calldata that includes data in the array - bytes memory parameterTerms_ = abi.encodePacked(uint256(68), BytesLib.slice(action_.data, uint256(68), uint256(64))); + bytes memory parameterTerms_ = abi.encodePacked(uint256(68), BytesLib.slice(execution_.callData, uint256(68), uint256(64))); vm.prank(address(delegationManager)); - allowedCalldataEnforcer.beforeHook(offsetTerms_, hex"", action_, keccak256(""), address(0), address(0)); - allowedCalldataEnforcer.beforeHook(lengthTerms_, hex"", action_, keccak256(""), address(0), address(0)); - allowedCalldataEnforcer.beforeHook(parameterTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(offsetTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(lengthTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(parameterTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should allow a single method to be called when a single function parameter that is dynamic is equal function test_singleMethodCanBeCalledWithEqualDynamicStringParam() public { string memory param = "Test string"; - // Create the action that would be executed - Action memory action_ = - Action({ to: address(0), value: 0, data: abi.encodeWithSelector(DummyContract.arrayFn.selector, param) }); + // Create the execution that would be executed + Execution memory execution_ = + Execution({ target: address(0), value: 0, callData: abi.encodeWithSelector(DummyContract.arrayFn.selector, param) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // The offset of the string in the calldata bytes memory offsetTerms_ = abi.encodePacked(uint256(4), uint256(32)); // The length of the string bytes memory lengthTerms_ = abi.encodePacked(uint256(36), uint256(11)); // The string itself - bytes memory parameterTerms_ = abi.encodePacked(uint256(68), BytesLib.slice(action_.data, uint256(68), uint256(32))); + bytes memory parameterTerms_ = abi.encodePacked(uint256(68), BytesLib.slice(execution_.callData, uint256(68), uint256(32))); vm.prank(address(delegationManager)); - allowedCalldataEnforcer.beforeHook(offsetTerms_, hex"", action_, keccak256(""), address(0), address(0)); - allowedCalldataEnforcer.beforeHook(lengthTerms_, hex"", action_, keccak256(""), address(0), address(0)); - allowedCalldataEnforcer.beforeHook(parameterTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(offsetTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(lengthTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(parameterTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should allow Artist to create NFT specific delegations with metadata caveat function test_methodCanBeCalledWithSpecificMetadata() public { string memory metadataUrl_ = "ipfs://bafybeigxsy55qgbdqw44y5yrs2jhhk2kdn7vbt6y4myvhcwdjak6cwj464/3762"; - // The Action with the mint calldata that would be executed + // The Execution with the mint calldata that would be executed bytes memory encodedData_ = abi.encodeWithSelector(BasicCF721.mintWithMetadata.selector, address(users.bob.deleGator), metadataUrl_); - Action memory action_ = Action({ to: address(basicCF721), value: 0, data: encodedData_ }); + Execution memory execution_ = Execution({ target: address(basicCF721), value: 0, callData: encodedData_ }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Calculate the start and length of the metadata bytes within the calldata bytes memory encodedMetadataString_ = abi.encode(metadataUrl_); @@ -117,19 +126,20 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); // NOTE: Using encodedData_ not encodedMetadataString_ to ensure the value for the offset is correct bytes memory allowedCalldata_ = abi.encodePacked(start_, BytesLib.slice(encodedData_, uint256(start_), uint256(length_))); - allowedCalldataEnforcer.beforeHook(allowedCalldata_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(allowedCalldata_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } ////////////////////// Invalid cases ////////////////////// // should NOT allow a method to be called when a single function parameter is not equal function test_singleMethodCanNotCalledWithNonEqualParam() public { - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) + callData: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator uint256 paramStart_ = abi.encodeWithSelector(BasicERC20.mint.selector, address(0)).length; @@ -138,17 +148,18 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("AllowedCalldataEnforcer:invalid-calldata"); - allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should NOT allow to pass an invalid calldata length (invalid terms) function test_failsWithInvalidTermsLength() public { - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) + callData: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator uint256 paramStart_ = abi.encodeWithSelector(BasicERC20.mint.selector, address(0)).length; @@ -157,17 +168,18 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("AllowedCalldataEnforcer:invalid-calldata-length"); - allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should NOT allow a method to be called when a terms size is invalid function test_singleMethodCanNotCalledWithInvalidTermsSize() public { - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) + callData: abi.encodeWithSelector(BasicERC20.mint.selector, address(users.alice.deleGator), uint256(200)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator uint256 paramStart_ = abi.encodeWithSelector(BasicERC20.mint.selector, address(0)).length; @@ -175,7 +187,7 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("AllowedCalldataEnforcer:invalid-terms-size"); - allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + allowedCalldataEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } ////////////////////// Integration ////////////////////// @@ -184,11 +196,11 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { function test_singleMethodCanBeCalledWithEqualParamIntegration() public { assertEq(basicCF20.balanceOf(address(users.bob.deleGator)), 0); - // Create the action that would be executed on Alice for transferring a ft tokens - Action memory action1_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed on Alice for transferring a ft tokens + Execution memory execution1_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(1)) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(1)) }); // create terms for the enforcer @@ -214,13 +226,13 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action1_); + invokeDelegation_UserOp(users.bob, delegations_, execution1_); // Validate that the balance have increased assertEq(basicCF20.balanceOf(address(users.bob.deleGator)), uint256(1)); // Enforcer allows to reuse the delegation - invokeDelegation_UserOp(users.bob, delegations_, action1_); + invokeDelegation_UserOp(users.bob, delegations_, execution1_); // Validate that the balance has increased again assertEq(basicCF20.balanceOf(address(users.bob.deleGator)), uint256(2)); @@ -230,11 +242,11 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { function test_singleMethodCanNotBeCalledWithNonEqualParamIntegration() public { assertEq(basicCF20.balanceOf(address(users.bob.deleGator)), 0); - // Create the action that would be executed on Alice for transferring a ft tokens - Action memory action1_ = Action({ - to: address(basicCF20), + // Create the execution that would be executed on Alice for transferring a ft tokens + Execution memory execution1_ = Execution({ + target: address(basicCF20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(2)) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(2)) }); // create terms for the enforcer @@ -260,7 +272,7 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action1_); + invokeDelegation_UserOp(users.bob, delegations_, execution1_); // Validate that the balance have not increased assertEq(basicCF20.balanceOf(address(users.bob.deleGator)), uint256(0)); diff --git a/test/enforcers/AllowedMethodsEnforcer.t.sol b/test/enforcers/AllowedMethodsEnforcer.t.sol index 1cd32f9..5e179cb 100644 --- a/test/enforcers/AllowedMethodsEnforcer.t.sol +++ b/test/enforcers/AllowedMethodsEnforcer.t.sol @@ -3,8 +3,10 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { AllowedMethodsEnforcer } from "../../src/enforcers/AllowedMethodsEnforcer.sol"; @@ -13,9 +15,12 @@ import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////// State ////////////////////// AllowedMethodsEnforcer public allowedMethodsEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -29,29 +34,38 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { // should allow a method to be called when a single method is allowed function test_singleMethodCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); allowedMethodsEnforcer.beforeHook( - abi.encodePacked(Counter.increment.selector), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(Counter.increment.selector), hex"", mode, executionCallData_, keccak256(""), address(0), address(0) ); } // should allow a method to be called when a multiple methods are allowed function test_multiMethodCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); allowedMethodsEnforcer.beforeHook( abi.encodePacked(Counter.setCount.selector, Ownable.renounceOwnership.selector, Counter.increment.selector), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -66,18 +80,21 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { allowedMethodsEnforcer.getTermsInfo(bytes("1")); } - // should FAIL if action.data length < 4 - function test_notAllow_invalidActionLength() public { - // Create the action that would be executed - Action memory action_ = Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodePacked(true) }); + // should FAIL if execution.callData length < 4 + function test_notAllow_invalidExecutionLength() public { + // Create the execution that would be executed + Execution memory execution_ = + Execution({ target: address(aliceDeleGatorCounter), value: 0, callData: abi.encodePacked(true) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); - vm.expectRevert("AllowedMethodsEnforcer:invalid-action-data-length"); + vm.expectRevert("AllowedMethodsEnforcer:invalid-execution-data-length"); allowedMethodsEnforcer.beforeHook( abi.encodePacked(Counter.setCount.selector, Ownable.renounceOwnership.selector, Ownable.owner.selector), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -86,9 +103,13 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { // should NOT allow a method to be called when the method is not allowed function test_onlyApprovedMethodsCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); @@ -96,7 +117,8 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { allowedMethodsEnforcer.beforeHook( abi.encodePacked(Counter.setCount.selector, Ownable.renounceOwnership.selector, Ownable.owner.selector), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -109,9 +131,12 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { function test_methodCanBeSingleMethodIntegration() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = @@ -132,14 +157,14 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get count uint256 valueAfter_ = aliceDeleGatorCounter.count(); // Validate that the count has increased by 1 assertEq(valueAfter_, initialValue_ + 1); // Enforcer allows to reuse the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); // Validate that the count has increased again @@ -150,9 +175,12 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { function test_onlyApprovedMethodsCanBeCalledIntegration() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ @@ -176,7 +204,7 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 valueAfter_ = aliceDeleGatorCounter.count(); // Validate that the count has not changed diff --git a/test/enforcers/AllowedTargetsEnforcer.t.sol b/test/enforcers/AllowedTargetsEnforcer.t.sol index 3f35074..585993b 100644 --- a/test/enforcers/AllowedTargetsEnforcer.t.sol +++ b/test/enforcers/AllowedTargetsEnforcer.t.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { AllowedTargetsEnforcer } from "../../src/enforcers/AllowedTargetsEnforcer.sol"; @@ -13,10 +15,13 @@ import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; import { BasicERC20, IERC20 } from "../utils/BasicERC20.t.sol"; contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// AllowedTargetsEnforcer public allowedTargetsEnforcer; BasicERC20 public testFToken1; BasicERC20 public testFToken2; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -32,29 +37,38 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { // should allow a method to be called when a single target is allowed function test_singleTargetCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); allowedTargetsEnforcer.beforeHook( - abi.encodePacked(address(aliceDeleGatorCounter)), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(address(aliceDeleGatorCounter)), hex"", mode, executionCallData_, keccak256(""), address(0), address(0) ); } // should allow a method to be called when a multiple targets are allowed function test_multiTargetCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); allowedTargetsEnforcer.beforeHook( abi.encodePacked(address(bobDeleGatorCounter), address(carolDeleGatorCounter), address(aliceDeleGatorCounter)), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -71,9 +85,13 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { // should NOT allow a method to be called when the target is not allowed function test_onlyApprovedTargetsCanBeCalled() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(daveDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(daveDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Dave's DeleGator vm.prank(address(delegationManager)); @@ -81,7 +99,8 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { allowedTargetsEnforcer.beforeHook( abi.encodePacked(address(bobDeleGatorCounter), address(carolDeleGatorCounter), address(aliceDeleGatorCounter)), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -95,15 +114,18 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { assertEq(aliceDeleGatorCounter.count(), 0); assertEq(testFToken1.balanceOf(address(users.bob.deleGator)), 0); - // Create the action that would be executed on Alice for incrementing the count - Action memory action1_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed on Alice for incrementing the count + Execution memory execution1_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); - // Create the action that would be executed on Alice for transferring a ft tokens - Action memory action2_ = Action({ - to: address(testFToken1), + // Create the execution that would be executed on Alice for transferring a ft tokens + Execution memory execution2_ = Execution({ + target: address(testFToken1), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) }); Caveat[] memory caveats_ = new Caveat[](1); @@ -128,16 +150,16 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action1_); - invokeDelegation_UserOp(users.bob, delegations_, action2_); + invokeDelegation_UserOp(users.bob, delegations_, execution1_); + invokeDelegation_UserOp(users.bob, delegations_, execution2_); // Validate that the count and balance have increased assertEq(aliceDeleGatorCounter.count(), 1); assertEq(testFToken1.balanceOf(address(users.bob.deleGator)), 1 ether); // Enforcer allows to reuse the delegation - invokeDelegation_UserOp(users.bob, delegations_, action1_); - invokeDelegation_UserOp(users.bob, delegations_, action2_); + invokeDelegation_UserOp(users.bob, delegations_, execution1_); + invokeDelegation_UserOp(users.bob, delegations_, execution2_); // Validate that the count and balance has increased again assertEq(aliceDeleGatorCounter.count(), 2); @@ -149,11 +171,11 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { assertEq(testFToken1.balanceOf(address(users.bob.deleGator)), 0); assertEq(testFToken2.balanceOf(address(users.bob.deleGator)), 0); - // Create the action that would be executed on Alice for transferring FToken2 - Action memory action_ = Action({ - to: address(testFToken2), + // Create the execution that would be executed on Alice for transferring FToken2 + Execution memory execution_ = Execution({ + target: address(testFToken2), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) }); // Approving the user to use the FToken1 @@ -176,7 +198,7 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation_; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the balances on both FTokens has not increased assertEq(testFToken1.balanceOf(address(users.bob.deleGator)), 0); diff --git a/test/enforcers/ArgsEqualityCheckEnforcer.t.sol b/test/enforcers/ArgsEqualityCheckEnforcer.t.sol index 2816ce4..5588682 100644 --- a/test/enforcers/ArgsEqualityCheckEnforcer.t.sol +++ b/test/enforcers/ArgsEqualityCheckEnforcer.t.sol @@ -1,15 +1,20 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; + import "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { ArgsEqualityCheckEnforcer } from "../../src/enforcers/ArgsEqualityCheckEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract ArgsEqualityCheckEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////// State ////////////////////// ArgsEqualityCheckEnforcer public argsEqualityCheckEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -25,7 +30,9 @@ contract ArgsEqualityCheckEnforcerTest is CaveatEnforcerBaseTest { function test_passEnforcerWhenTermsEqualsArgs() public { bytes memory terms_ = bytes("This is an example"); bytes memory args_ = bytes("This is an example"); - argsEqualityCheckEnforcer.beforeHook(terms_, args_, new Action[](1)[0], bytes32(0), address(0), address(0)); + argsEqualityCheckEnforcer.beforeHook( + terms_, args_, mode, abi.encode(new Execution[](1)[0]), bytes32(0), address(0), address(0) + ); } ////////////////////// Invalid cases ////////////////////// @@ -39,7 +46,9 @@ contract ArgsEqualityCheckEnforcerTest is CaveatEnforcerBaseTest { vm.expectRevert("ArgsEqualityCheckEnforcer:different-args-and-terms"); vm.expectEmit(true, true, true, true, address(argsEqualityCheckEnforcer)); emit ArgsEqualityCheckEnforcer.DifferentArgsAndTerms(address(delegationManager), redeemer_, bytes32(0), terms_, args_); - argsEqualityCheckEnforcer.beforeHook(terms_, args_, new Action[](1)[0], bytes32(0), address(0), redeemer_); + argsEqualityCheckEnforcer.beforeHook( + terms_, args_, mode, abi.encode(new Execution[](1)[0]), bytes32(0), address(0), redeemer_ + ); } function _getEnforcer() internal view override returns (ICaveatEnforcer) { diff --git a/test/enforcers/BlockNumberEnforcer.t.sol b/test/enforcers/BlockNumberEnforcer.t.sol index 43c3f79..c5eb7bc 100644 --- a/test/enforcers/BlockNumberEnforcer.t.sol +++ b/test/enforcers/BlockNumberEnforcer.t.sol @@ -3,8 +3,10 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { BlockNumberEnforcer } from "../../src/enforcers/BlockNumberEnforcer.sol"; @@ -16,6 +18,7 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { ////////////////////// State ////////////////////// BlockNumberEnforcer public blockNumberEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -29,40 +32,55 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { // should SUCCEED to INVOKE method AFTER blockNumber reached function test_methodCanBeCalledAfterBlockNumber() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + vm.roll(10000); uint128 blockAfterThreshold_ = 1; uint128 blockBeforeThreshold_ = 0; // Not using before threshold bytes memory inputTerms_ = abi.encodePacked(blockAfterThreshold_, blockBeforeThreshold_); vm.prank(address(delegationManager)); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } //should SUCCEED to INVOKE method BEFORE blockNumber reached function test_methodCanBeCalledBeforeBlockNumber() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 blockAfterThreshold_ = 0; // Not using after threshold uint128 blockBeforeThreshold_ = uint128(block.number + 10000); bytes memory inputTerms_ = abi.encodePacked(blockAfterThreshold_, blockBeforeThreshold_); vm.prank(address(delegationManager)); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should SUCCEED to INVOKE method inside blockNumber RANGE function test_methodCanBeCalledInsideBlockNumberRange() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 blockAfterThreshold_ = 1; uint128 blockBeforeThreshold_ = uint128(block.number + 10000); vm.roll(1000); // making block number between 1 and 10001 bytes memory inputTerms_ = abi.encodePacked(blockAfterThreshold_, blockBeforeThreshold_); vm.prank(address(delegationManager)); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } ////////////////////// Invalid cases ////////////////////// @@ -75,9 +93,13 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { // should FAIL to INVOKE method BEFORE blockNumber reached function test_methodFailsIfCalledBeforeBlockNumber() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); uint128 blockAfterThreshold_ = uint128(block.number + 10000); uint128 blockBeforeThreshold_ = 0; // Not using before threshold @@ -85,14 +107,19 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("BlockNumberEnforcer:early-delegation"); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method AFTER blockNumber reached function test_methodFailsIfCalledAfterBlockNumber() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 blockAfterThreshold_ = 0; // Not using after threshold uint128 blockBeforeThreshold_ = uint128(block.number); vm.roll(10000); @@ -100,28 +127,38 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("BlockNumberEnforcer:expired-delegation"); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method BEFORE blocknumber RANGE function test_methodFailsIfCalledBeforeBlockNumberRange() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 blockAfterThreshold_ = uint128(block.number + 10000); uint128 blockBeforeThreshold_ = uint128(block.number + 20000); bytes memory inputTerms_ = abi.encodePacked(blockAfterThreshold_, blockBeforeThreshold_); vm.prank(address(delegationManager)); vm.expectRevert("BlockNumberEnforcer:early-delegation"); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method AFTER blocknumber RANGE" function test_methodFailsIfCalledAfterBlockNumberRange() public { - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 blockAfterThreshold_ = uint128(block.number + 10000); uint128 blockBeforeThreshold_ = uint128(block.number + 20000); vm.roll(30000); @@ -129,7 +166,7 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("BlockNumberEnforcer:expired-delegation"); - blockNumberEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + blockNumberEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } ////////////////////// Integration ////////////////////// @@ -137,9 +174,12 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { // should SUCCEED to INVOKE until reaching blockNumber function test_methodCanBeCalledAfterBlockNumberIntegration() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action_ that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); vm.roll(10); // Not using before threshold (blockAfterThreshold_ = 1, blockBeforeThreshold_ = 100) bytes memory inputTerms_ = abi.encodePacked(uint128(1), uint128(100)); @@ -162,7 +202,7 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 valueAfter_ = aliceDeleGatorCounter.count(); // Validate that the count has increased by 1 @@ -170,7 +210,7 @@ contract BlockNumberEnforcerTest is CaveatEnforcerBaseTest { // Enforcer blocks the delegation vm.roll(100); - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); // Validate that the count has not increased by 1 diff --git a/test/enforcers/DeployedEnforcer.t.sol b/test/enforcers/DeployedEnforcer.t.sol index b642789..870b179 100644 --- a/test/enforcers/DeployedEnforcer.t.sol +++ b/test/enforcers/DeployedEnforcer.t.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.23; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { DeployedEnforcer } from "../../src/enforcers/DeployedEnforcer.sol"; import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol"; @@ -11,10 +13,13 @@ import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract DeployedEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// DeployedEnforcer public deployedEnforcer; bytes32 public salt; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////////////// Events ////////////////////////////// @@ -46,9 +51,13 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { bytes32 bytecodeHash_ = hashInitCode(type(Counter).creationCode); address predictedAddr_ = vm.computeCreate2Address(salt, bytecodeHash_, address(deployedEnforcer)); - // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Check that the contract hasn't been deployed yet bytes memory initialCode_ = predictedAddr_.code; @@ -63,7 +72,8 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { deployedEnforcer.beforeHook( abi.encodePacked(predictedAddr_, salt, abi.encodePacked(type(Counter).creationCode)), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -81,14 +91,24 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { bytes32 bytecodeHash_ = hashInitCode(bytecode_); address predictedAddr_ = vm.computeCreate2Address(salt, bytecodeHash_, address(deployedEnforcer)); - // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Deploy the contract vm.prank(address(delegationManager)); deployedEnforcer.beforeHook( - abi.encodePacked(predictedAddr_, salt, bytecode_), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(predictedAddr_, salt, bytecode_), + hex"", + mode, + executionCallData_, + keccak256(""), + address(0), + address(0) ); // beforeHook, mimicking the behavior of Alice's DeleGator @@ -98,7 +118,13 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { // Use enforcer when contract is already deployed deployedEnforcer.beforeHook( - abi.encodePacked(predictedAddr_, salt, bytecode_), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(predictedAddr_, salt, bytecode_), + hex"", + mode, + executionCallData_, + keccak256(""), + address(0), + address(0) ); } @@ -106,9 +132,13 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { // should revert if the predicted address doesn't match the deployed address function test_revertIfPredictedAddressDoesNotMatch() public { - // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); @@ -116,7 +146,8 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { deployedEnforcer.beforeHook( abi.encodePacked(users.alice.addr, salt, abi.encodePacked(type(Counter).creationCode)), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -125,24 +156,36 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { // should revert if the length of the terms is not sufficient function test_revertIfTermsLengthIsInvalid() public { - // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); vm.startPrank(address(delegationManager)); // 0 bytes vm.expectRevert("DeployedEnforcer:invalid-terms-length"); - deployedEnforcer.beforeHook(hex"", hex"", action_, keccak256(""), address(0), address(0)); + deployedEnforcer.beforeHook(hex"", hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); // 20 bytes vm.expectRevert("DeployedEnforcer:invalid-terms-length"); - deployedEnforcer.beforeHook(abi.encodePacked(users.alice.addr), hex"", action_, keccak256(""), address(0), address(0)); + deployedEnforcer.beforeHook( + abi.encodePacked(users.alice.addr), hex"", mode, executionCallData_, keccak256(""), address(0), address(0) + ); // 52 bytes vm.expectRevert("DeployedEnforcer:invalid-terms-length"); deployedEnforcer.beforeHook( - abi.encodePacked(users.alice.addr, bytes32(hex"")), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(users.alice.addr, bytes32(hex"")), + hex"", + mode, + executionCallData_, + keccak256(""), + address(0), + address(0) ); } @@ -154,15 +197,25 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { bytes32 bytecodeHash_ = hashInitCode(bytecode_); address predictedAddr_ = vm.computeCreate2Address(salt, bytecodeHash_, address(deployedEnforcer)); - // // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // beforeHook, mimicking the behavior of Alice's DeleGator vm.prank(address(delegationManager)); vm.expectRevert(abi.encodeWithSelector(DeployedEnforcer.DeployedEmptyContract.selector, predictedAddr_)); deployedEnforcer.beforeHook( - abi.encodePacked(predictedAddr_, salt, bytecode_), hex"", action_, keccak256(""), address(0), address(0) + abi.encodePacked(predictedAddr_, salt, bytecode_), + hex"", + mode, + executionCallData_, + keccak256(""), + address(0), + address(0) ); } @@ -174,9 +227,13 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { bytes32 bytecodeHash_ = hashInitCode(hex""); address predictedAddr_ = vm.computeCreate2Address(salt, bytecodeHash_, address(deployedEnforcer)); - // NOTE: Action isn't very relevant for this test. - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Check that the contract hasn't been deployed yet bytes memory initialCode_ = predictedAddr_.code; @@ -188,7 +245,8 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { deployedEnforcer.beforeHook( abi.encodePacked(predictedAddr_, salt, abi.encodePacked(type(Counter).creationCode)), hex"", - action_, + mode, + executionCallData_, keccak256(""), address(0), address(0) @@ -203,8 +261,10 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { bytes32 bytecodeHash_ = hashInitCode(type(Counter).creationCode); address predictedAddr_ = vm.computeCreate2Address(salt, bytecodeHash_, address(deployedEnforcer)); - // NOTE: Action isn't very relevant for this test. - Action memory action_ = Action({ to: predictedAddr_, value: 0, data: abi.encodeWithSelector(Counter.setCount.selector, 1) }); + // NOTE: Execution isn't very relevant for this test. + Execution memory execution_ = + Execution({ target: predictedAddr_, value: 0, callData: abi.encodeWithSelector(Counter.setCount.selector, 1) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Check that the contract hasn't been deployed yet bytes memory initialCode_ = predictedAddr_.code; @@ -232,7 +292,7 @@ contract DeployedEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Check that the contract has been deployed properly bytes memory finalCode_ = predictedAddr_.code; diff --git a/test/enforcers/ERC20BalanceGteEnforcer.t.sol b/test/enforcers/ERC20BalanceGteEnforcer.t.sol index 7776891..48362e8 100644 --- a/test/enforcers/ERC20BalanceGteEnforcer.t.sol +++ b/test/enforcers/ERC20BalanceGteEnforcer.t.sol @@ -3,22 +3,26 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; import { BasicERC20 } from "../utils/BasicERC20.t.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { ERC20BalanceGteEnforcer } from "../../src/enforcers/ERC20BalanceGteEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// ERC20BalanceGteEnforcer public enforcer; BasicERC20 public token; address delegator; address delegate; address dm; - Action noAction; - Action mintAction; + Execution mintExecution; + bytes mintExecutionCallData; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -31,8 +35,9 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { vm.label(address(enforcer), "ERC20 BalanceGte Enforcer"); token = new BasicERC20(delegator, "TEST", "TEST", 0); vm.label(address(token), "ERC20 Test Token"); - mintAction = Action({ to: address(token), value: 0, data: abi.encodeWithSelector(token.mint.selector, delegator, 100) }); - noAction = Action(address(0), 0, hex""); + mintExecution = + Execution({ target: address(token), value: 0, callData: abi.encodeWithSelector(token.mint.selector, delegator, 100) }); + mintExecutionCallData = abi.encode(mintExecution); } ////////////////////// Basic Functionality ////////////////////// @@ -54,19 +59,19 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 100 vm.prank(dm); - enforcer.beforeHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); vm.prank(delegator); token.mint(delegator, 100); vm.prank(dm); - enforcer.afterHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); // Increase by 1000 vm.prank(dm); - enforcer.beforeHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); vm.prank(delegator); token.mint(delegator, 1000); vm.prank(dm); - enforcer.afterHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); } // ////////////////////// Errors ////////////////////// @@ -78,12 +83,12 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 10, expect revert vm.prank(dm); - enforcer.beforeHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); vm.prank(delegator); token.mint(delegator, 10); vm.prank(dm); vm.expectRevert(bytes("ERC20BalanceGteEnforcer:balance-not-gt")); - enforcer.afterHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); } // Reverts if a enforcer is locked @@ -95,20 +100,20 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 100 vm.startPrank(dm); // Locks the enforcer - enforcer.beforeHook(terms_, hex"", mintAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, delegationHash_, delegator, delegate); bytes32 hashKey_ = enforcer.getHashKey(address(delegationManager), address(token), delegationHash_); assertTrue(enforcer.isLocked(hashKey_)); vm.expectRevert(bytes("ERC20BalanceGteEnforcer:enforcer-is-locked")); - enforcer.beforeHook(terms_, hex"", mintAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, delegationHash_, delegator, delegate); vm.startPrank(delegator); token.mint(delegator, 1000); vm.startPrank(dm); // Unlocks the enforcer - enforcer.afterHook(terms_, hex"", mintAction, delegationHash_, delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, mintExecutionCallData, delegationHash_, delegator, delegate); assertFalse(enforcer.isLocked(hashKey_)); // Can be used again, and locks it again - enforcer.beforeHook(terms_, hex"", mintAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, delegationHash_, delegator, delegate); assertTrue(enforcer.isLocked(hashKey_)); } @@ -134,7 +139,7 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Invalid token terms_ = abi.encodePacked(address(0), uint256(100)); vm.expectRevert(); - enforcer.beforeHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); } // Validates that an invalid ID reverts @@ -144,9 +149,9 @@ contract ERC20BalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase vm.prank(dm); - enforcer.beforeHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); vm.expectRevert(); - enforcer.afterHook(terms_, hex"", mintAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, mintExecutionCallData, bytes32(0), delegator, delegate); } ////////////////////// Integration ////////////////////// diff --git a/test/enforcers/ERC20TransferAmountEnforcer.t.sol b/test/enforcers/ERC20TransferAmountEnforcer.t.sol index d8f0572..dc4ca88 100644 --- a/test/enforcers/ERC20TransferAmountEnforcer.t.sol +++ b/test/enforcers/ERC20TransferAmountEnforcer.t.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { ERC20TransferAmountEnforcer } from "../../src/enforcers/ERC20TransferAmountEnforcer.sol"; @@ -13,10 +15,13 @@ import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol" import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// ERC20TransferAmountEnforcer public erc20TransferAmountEnforcer; BasicERC20 public basicERC20; BasicERC20 public invalidERC20; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////////////// Events ////////////////////////////// event IncreasedSpentMap( @@ -38,12 +43,14 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { function test_transferSucceedsIfCalledBelowAllowance() public { uint256 spendingLimit_ = 1 ether; - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), spendingLimit_) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), spendingLimit_) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -61,7 +68,9 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectEmit(true, true, true, true, address(erc20TransferAmountEnforcer)); emit IncreasedSpentMap(address(delegationManager), address(0), delegationHash_, spendingLimit_, 1 ether); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), spendingLimit_); } @@ -72,12 +81,14 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { function test_transferFailsIfCalledAboveAllowance() public { uint256 spendingLimit_ = 1 ether; - // Create the action that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(spendingLimit_ + 1)) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(spendingLimit_ + 1)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -95,7 +106,9 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("ERC20TransferAmountEnforcer:allowance-exceeded"); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); } @@ -106,12 +119,14 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { function test_methodFailsIfInvokesInvalidContract() public { uint256 spendingLimit_ = 1 ether; - // Create the action_ that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), spendingLimit_) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), spendingLimit_) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(invalidERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -129,23 +144,27 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); vm.prank(address(delegationManager)); vm.expectRevert("ERC20TransferAmountEnforcer:invalid-contract"); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); } - // should FAIL to INVOKE invalid action data length - function test_notAllow_invalidActionLength() public { + // should FAIL to INVOKE invalid execution data length + function test_notAllow_invalidExecutionLength() public { uint256 spendingLimit_ = 1 ether; - // Create the action_ that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector( + callData: abi.encodeWithSelector( IERC20.transferFrom.selector, address(users.alice.deleGator), address(users.bob.deleGator), spendingLimit_ ) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -162,8 +181,10 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { bytes32 delegationHash_ = EncoderLib._getDelegationHash(delegation_); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); vm.prank(address(delegationManager)); - vm.expectRevert("ERC20TransferAmountEnforcer:invalid-action-length"); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + vm.expectRevert("ERC20TransferAmountEnforcer:invalid-execution-length"); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); } @@ -172,12 +193,14 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { function test_methodFailsIfInvokesInvalidMethod() public { uint256 spendingLimit_ = 1 ether; - // Create the action_ that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transferFrom.selector, address(users.bob.deleGator), spendingLimit_) + callData: abi.encodeWithSelector(IERC20.transferFrom.selector, address(users.bob.deleGator), spendingLimit_) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -195,7 +218,9 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("ERC20TransferAmountEnforcer:invalid-method"); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); } @@ -204,12 +229,14 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { function test_methodFailsIfInvokesInvalidTermsLength() public { uint256 spendingLimit_ = 1 ether; - // Create the action_ that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(spendingLimit_)) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(spendingLimit_)) }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20)); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -227,7 +254,9 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("ERC20TransferAmountEnforcer:invalid-terms-length"); - erc20TransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + erc20TransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); } @@ -238,12 +267,13 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { assertEq(basicERC20.balanceOf(address(users.alice.deleGator)), 100 ether); assertEq(basicERC20.balanceOf(address(users.bob.deleGator)), 0); - // Create the action_ that would be executed - Action memory action_ = Action({ - to: address(basicERC20), + // Create the execution_ that would be executed + Execution memory execution_ = Execution({ + target: address(basicERC20), value: 0, - data: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) + callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), 1 ether) }); + bytes memory inputTerms_ = abi.encodePacked(address(basicERC20), spendingLimit_); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(erc20TransferAmountEnforcer), terms: inputTerms_ }); @@ -266,7 +296,7 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate Alice balance 99 ether assertEq(_getFtBalanceOf(address(users.alice.deleGator)), 99 ether); @@ -275,7 +305,7 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 1 ether); // The delegation can be reused while the allowance is enough - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate Alice balance 98 ether assertEq(_getFtBalanceOf(address(users.alice.deleGator)), 98 ether); // Validate Bob balance 1 ether @@ -283,7 +313,7 @@ contract ERC20TransferAmountEnforcerTest is CaveatEnforcerBaseTest { assertEq(erc20TransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), spendingLimit_); // If allowance is not enough the transfer should not work - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate Balances did not change when transfer are above the allowance assertEq(_getFtBalanceOf(address(users.alice.deleGator)), 98 ether); assertEq(_getFtBalanceOf(address(users.bob.deleGator)), 2 ether); diff --git a/test/enforcers/IdEnforcer.t.sol b/test/enforcers/IdEnforcer.t.sol index b9d4c73..06c19b4 100644 --- a/test/enforcers/IdEnforcer.t.sol +++ b/test/enforcers/IdEnforcer.t.sol @@ -2,9 +2,11 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { IdEnforcer } from "../../src/enforcers/IdEnforcer.sol"; @@ -14,9 +16,12 @@ import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; import { BasicERC20, IERC20 } from "../utils/BasicERC20.t.sol"; contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// IdEnforcer public idEnforcer; BasicERC20 public testFToken1; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////////////// Events ////////////////////////////// @@ -40,7 +45,8 @@ contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { // Validates that the enforcer reverts and returns false once reusing the nonce function test_blocksDelegationWithRepeatedNonce() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); uint256 id_ = uint256(123456789); bytes memory terms_ = abi.encode(id_); address delegator_ = address(users.alice.deleGator); @@ -53,7 +59,7 @@ contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { // First usage works well vm.expectEmit(true, true, true, true, address(idEnforcer)); emit UsedId(address(delegationManager), address(0), delegator_, id_); - idEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), delegator_, address(0)); + idEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), delegator_, address(0)); // After the first usage the enforcer marks the nonce as used. assertTrue(idEnforcer.getIsUsed(address(delegationManager), delegator_, id_)); @@ -61,19 +67,20 @@ contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { // Second usage reverts, and returns false. vm.expectRevert("IdEnforcer:id-already-used"); - idEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), delegator_, address(0)); + idEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), delegator_, address(0)); } // should FAIL to INVOKE with invalid input terms function test_methodFailsIfCalledWithInvalidInputTerms() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory terms_ = abi.encodePacked(uint32(1)); vm.expectRevert("IdEnforcer:invalid-terms-length"); - idEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + idEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); terms_ = abi.encodePacked(uint256(1), uint256(1)); vm.expectRevert("IdEnforcer:invalid-terms-length"); - idEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + idEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); } ////////////////////// Integration ////////////////////// @@ -81,9 +88,12 @@ contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { // Should revert to use a delegation which nonce has already been used function test_methodFailsIfNonceAlreadyUsed() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); bytes memory inputTerms_ = abi.encode(uint256(12345)); @@ -105,12 +115,12 @@ contract IdEnforcerEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has increased by 1 assertEq(aliceDeleGatorCounter.count(), initialValue_ + 1); // Enforcer blocks the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has not increased by 1 assertEq(aliceDeleGatorCounter.count(), initialValue_ + 1); } diff --git a/test/enforcers/LimitedCallsEnforcer.t.sol b/test/enforcers/LimitedCallsEnforcer.t.sol index a77a914..c07edd5 100644 --- a/test/enforcers/LimitedCallsEnforcer.t.sol +++ b/test/enforcers/LimitedCallsEnforcer.t.sol @@ -2,9 +2,11 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { LimitedCallsEnforcer } from "../../src/enforcers/LimitedCallsEnforcer.sol"; @@ -13,6 +15,8 @@ import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol" import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// Events ////////////////////////////// event IncreasedCount( address indexed sender, address indexed redeemer, bytes32 indexed delegationHash, uint256 limit, uint256 callCount @@ -21,6 +25,7 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { ////////////////////// State ////////////////////// LimitedCallsEnforcer public limitedCallsEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -34,9 +39,14 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { // should SUCCEED to INVOKE method BELOW limit number function test_methodCanBeCalledBelowLimitNumber() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint256 transactionsLimit_ = 1; bytes memory inputTerms_ = abi.encodePacked(transactionsLimit_); Caveat[] memory caveats_ = new Caveat[](1); @@ -55,7 +65,7 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectEmit(true, true, true, true, address(limitedCallsEnforcer)); emit IncreasedCount(address(delegationManager), address(0), delegationHash_, 1, 1); - limitedCallsEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + limitedCallsEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0)); assertEq(limitedCallsEnforcer.callCounts(address(delegationManager), delegationHash_), transactionsLimit_); } @@ -64,9 +74,14 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { // should FAIL to INVOKE method ABOVE limit number function test_methodFailsIfCalledAboveLimitNumber() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint256 transactionsLimit_ = 1; bytes memory inputTerms_ = abi.encodePacked(transactionsLimit_); Caveat[] memory caveats_ = new Caveat[](1); @@ -83,22 +98,24 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { bytes32 delegationHash_ = EncoderLib._getDelegationHash(delegation_); assertEq(limitedCallsEnforcer.callCounts(address(delegationManager), delegationHash_), 0); vm.startPrank(address(delegationManager)); - limitedCallsEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + limitedCallsEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0)); vm.expectRevert("LimitedCallsEnforcer:limit-exceeded"); - limitedCallsEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + limitedCallsEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0)); assertEq(limitedCallsEnforcer.callCounts(address(delegationManager), delegationHash_), transactionsLimit_); } // should FAIL to INVOKE with invalid input terms function test_methodFailsIfCalledWithInvalidInputTerms() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory terms_ = abi.encodePacked(uint32(1)); vm.expectRevert("LimitedCallsEnforcer:invalid-terms-length"); - limitedCallsEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + limitedCallsEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); terms_ = abi.encodePacked(uint256(1), uint256(1)); vm.expectRevert("LimitedCallsEnforcer:invalid-terms-length"); - limitedCallsEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + limitedCallsEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); } ////////////////////// Integration ////////////////////// @@ -107,9 +124,12 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { function test_methodFailsAboveLimitIntegration() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); bytes memory inputTerms_ = abi.encodePacked(uint256(1)); Caveat[] memory caveats_ = new Caveat[](1); caveats_[0] = Caveat({ args: hex"", enforcer: address(limitedCallsEnforcer), terms: inputTerms_ }); @@ -132,13 +152,13 @@ contract LimitedCallsEnforcerTest is CaveatEnforcerBaseTest { Delegation[] memory delegations_ = new Delegation[](1); delegations_[0] = delegation_; - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has increased by 1 uint256 valueAfter_ = aliceDeleGatorCounter.count(); assertEq(valueAfter_, initialValue_ + 1); - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has not increased assertEq(aliceDeleGatorCounter.count(), valueAfter_); diff --git a/test/enforcers/NativeAllowanceEnforcer.t.sol b/test/enforcers/NativeAllowanceEnforcer.t.sol index ee5a903..8cc5e47 100644 --- a/test/enforcers/NativeAllowanceEnforcer.t.sol +++ b/test/enforcers/NativeAllowanceEnforcer.t.sol @@ -1,13 +1,21 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { NativeTokenTransferAmountEnforcer } from "../../src/enforcers/NativeTokenTransferAmountEnforcer.sol"; import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + + ////////////////////// State ////////////////////// + ModeCode mode = ModeLib.encodeSimpleSingle(); + ////////////////////// Set up ////////////////////// NativeTokenTransferAmountEnforcer public nativeTokenTransferAmountEnforcer; @@ -29,8 +37,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { function test_transferSucceedsIfCalledBelowAllowance() public { uint256 allowance_ = 1 ether; - // Create the action that would be executed - Action memory action_ = Action({ to: address(users.bob.deleGator), value: 1 ether, data: hex"" }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ target: address(users.bob.deleGator), value: 1 ether, callData: hex"" }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory inputTerms_ = abi.encode(allowance_); @@ -42,7 +51,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { emit NativeTokenTransferAmountEnforcer.IncreasedSpentMap( address(delegationManager), address(0), delegationHash_, allowance_, 1 ether ); - nativeTokenTransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + nativeTokenTransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), allowance_); } @@ -51,8 +62,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { function test_transferSucceedsIfCalledBelowAllowanceMultipleCalls() public { uint256 allowance_ = 3 ether; - // Create the action that would be executed - Action memory action_ = Action({ to: address(users.bob.deleGator), value: 1 ether, data: hex"" }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ target: address(users.bob.deleGator), value: 1 ether, callData: hex"" }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory inputTerms_ = abi.encode(allowance_); @@ -65,7 +77,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { emit NativeTokenTransferAmountEnforcer.IncreasedSpentMap( address(delegationManager), address(0), delegationHash_, allowance_, 1 ether ); - nativeTokenTransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + nativeTokenTransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 1 ether); // Second use @@ -73,7 +87,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { emit NativeTokenTransferAmountEnforcer.IncreasedSpentMap( address(delegationManager), address(0), delegationHash_, allowance_, 2 ether ); - nativeTokenTransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + nativeTokenTransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 2 ether); // Third use, maximum allowance used @@ -81,7 +97,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { emit NativeTokenTransferAmountEnforcer.IncreasedSpentMap( address(delegationManager), address(0), delegationHash_, allowance_, allowance_ ); - nativeTokenTransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + nativeTokenTransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), allowance_); } @@ -91,9 +109,10 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { function test_transferFailsIfAllowanceExceeded() public { uint256 allowance_ = 1 ether; - // Create the action that would be executed + // Create the execution that would be executed // The value is higher than the allowance - Action memory action_ = Action({ to: address(users.bob.deleGator), value: allowance_ + 1, data: hex"" }); + Execution memory execution_ = Execution({ target: address(users.bob.deleGator), value: allowance_ + 1, callData: hex"" }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory inputTerms_ = abi.encode(allowance_); @@ -102,7 +121,9 @@ contract NativeAllowanceEnforcerTest is CaveatEnforcerBaseTest { assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); vm.prank(address(delegationManager)); vm.expectRevert("NativeTokenTransferAmountEnforcer:allowance-exceeded"); - nativeTokenTransferAmountEnforcer.beforeHook(inputTerms_, hex"", action_, delegationHash_, address(0), address(0)); + nativeTokenTransferAmountEnforcer.beforeHook( + inputTerms_, hex"", mode, executionCallData_, delegationHash_, address(0), address(0) + ); // The allowance does not change assertEq(nativeTokenTransferAmountEnforcer.spentMap(address(delegationManager), delegationHash_), 0); diff --git a/test/enforcers/NativeBalanceGteEnforcer.t.sol b/test/enforcers/NativeBalanceGteEnforcer.t.sol index e14b151..afaaf3a 100644 --- a/test/enforcers/NativeBalanceGteEnforcer.t.sol +++ b/test/enforcers/NativeBalanceGteEnforcer.t.sol @@ -1,20 +1,26 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; + import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { NativeBalanceGteEnforcer } from "../../src/enforcers/NativeBalanceGteEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; import { Counter } from "../utils/Counter.t.sol"; contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// NativeBalanceGteEnforcer public enforcer; address delegator; address delegate; address dm; - Action noAction; + Execution noExecution; + bytes executionCallData = abi.encode(noExecution); + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -25,7 +31,7 @@ contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { dm = address(delegationManager); enforcer = new NativeBalanceGteEnforcer(); vm.label(address(enforcer), "NativeBalanceGte Enforcer"); - noAction = Action(address(0), 0, hex""); + noExecution = Execution(address(0), 0, hex""); } ////////////////////// Basic Functionality ////////////////////// @@ -48,14 +54,14 @@ contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 100 vm.startPrank(dm); - enforcer.beforeHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); _increaseBalance(delegator, 100); - enforcer.afterHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); // Increase by 1000 - enforcer.beforeHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); _increaseBalance(delegator, 1000); - enforcer.afterHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); } // ////////////////////// Errors ////////////////////// @@ -68,10 +74,10 @@ contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 10, expect revert vm.startPrank(dm); - enforcer.beforeHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); _increaseBalance(delegator, 10); vm.expectRevert(bytes("NativeBalanceGteEnforcer:balance-not-gt")); - enforcer.afterHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); } // Reverts if a enforcer is locked @@ -84,19 +90,19 @@ contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { // Increase by 100 vm.startPrank(dm); // Locks the enforcer - enforcer.beforeHook(terms_, hex"", noAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, delegationHash_, delegator, delegate); bytes32 hashKey_ = enforcer.getHashKey(address(delegationManager), delegationHash_); assertTrue(enforcer.isLocked(hashKey_)); vm.expectRevert(bytes("NativeBalanceGteEnforcer:enforcer-is-locked")); - enforcer.beforeHook(terms_, hex"", noAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, delegationHash_, delegator, delegate); _increaseBalance(delegator, 1000); vm.startPrank(dm); // Unlocks the enforcer - enforcer.afterHook(terms_, hex"", noAction, delegationHash_, delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, executionCallData, delegationHash_, delegator, delegate); assertFalse(enforcer.isLocked(hashKey_)); // Can be used again, and locks it again - enforcer.beforeHook(terms_, hex"", noAction, delegationHash_, delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, delegationHash_, delegator, delegate); assertTrue(enforcer.isLocked(hashKey_)); } @@ -124,9 +130,9 @@ contract NativeBalanceGteEnforcerTest is CaveatEnforcerBaseTest { bytes memory terms_ = abi.encodePacked(recipient_, type(uint256).max); vm.deal(recipient_, type(uint256).max); vm.startPrank(dm); - enforcer.beforeHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); vm.expectRevert(); - enforcer.afterHook(terms_, hex"", noAction, bytes32(0), delegator, delegate); + enforcer.afterHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, delegate); } function _increaseBalance(address _recipient, uint256 _amount) internal { diff --git a/test/enforcers/NativeTokenPaymentEnforcer.t.sol b/test/enforcers/NativeTokenPaymentEnforcer.t.sol index 0fd3458..94fbb96 100644 --- a/test/enforcers/NativeTokenPaymentEnforcer.t.sol +++ b/test/enforcers/NativeTokenPaymentEnforcer.t.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { NativeTokenPaymentEnforcer } from "../../src/enforcers/NativeTokenPaymentEnforcer.sol"; import { NativeTokenTransferAmountEnforcer } from "../../src/enforcers/NativeTokenTransferAmountEnforcer.sol"; @@ -14,6 +17,8 @@ import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; import { Counter } from "../utils/Counter.t.sol"; contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////// Set up ////////////////////// NativeTokenPaymentEnforcer public nativeTokenPaymentEnforcer; NativeTokenTransferAmountEnforcer public nativeTokenTransferAmountEnforcer; @@ -70,8 +75,13 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { (bytes32 delegationHash_,) = _getExampleDelegation(terms_, hex""); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + ModeCode mode_ = ModeLib.encodeSimpleSingle(); vm.startPrank(address(delegationManager)); vm.expectEmit(true, true, true, true, address(nativeTokenPaymentEnforcer)); @@ -79,7 +89,9 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { address(delegationManager), delegationHash_, recipient_, address(users.alice.deleGator), address(0), 1 ether ); - nativeTokenPaymentEnforcer.afterHook(terms_, args_, action_, delegationHash_, address(users.alice.deleGator), address(0)); + nativeTokenPaymentEnforcer.afterHook( + terms_, args_, mode_, executionCallData_, delegationHash_, address(users.alice.deleGator), address(0) + ); } // Should SUCCEED to make the payment with a redelegation @@ -114,8 +126,13 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { (bytes32 delegationHash_,) = _getExampleDelegation(terms_, hex""); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + ModeCode mode_ = ModeLib.encodeSimpleSingle(); vm.startPrank(address(delegationManager)); vm.expectEmit(true, true, true, true, address(nativeTokenPaymentEnforcer)); @@ -123,14 +140,15 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { address(delegationManager), delegationHash_, recipient_, address(users.alice.deleGator), address(0), 1 ether ); - nativeTokenPaymentEnforcer.afterHook(terms_, args_, action_, delegationHash_, address(users.alice.deleGator), address(0)); + nativeTokenPaymentEnforcer.afterHook( + terms_, args_, mode_, executionCallData_, delegationHash_, address(users.alice.deleGator), address(0) + ); } // Should only overwrite the args of the args equality enforcer function test_onlyOverwriteAllowanceEnforcerArgs() public { // The terms indicate to send 1 ether to Alice. - address recipient_ = address(users.alice.deleGator); - bytes memory paymentTerms_ = abi.encodePacked(recipient_, uint256(1 ether)); + bytes memory paymentTerms_ = abi.encodePacked(address(users.alice.deleGator), uint256(1 ether)); (bytes32 delegationHash_, Delegation memory paidDelegation_) = _getExampleDelegation(paymentTerms_, hex""); Delegation[] memory paidDelegations_ = new Delegation[](1); @@ -146,7 +164,11 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { Caveat[] memory paymentCaveats_ = new Caveat[](4); paymentCaveats_[0] = Caveat({ args: hex"", enforcer: address(nativeTokenTransferAmountEnforcer), terms: allowanceTerms_ }); paymentCaveats_[1] = Caveat({ args: hex"", enforcer: address(limitedCallsEnforcer), terms: abi.encodePacked(uint256(10)) }); - paymentCaveats_[2] = Caveat({ args: hex"", enforcer: address(allowedTargetsEnforcer), terms: abi.encodePacked(recipient_) }); + paymentCaveats_[2] = Caveat({ + args: hex"", + enforcer: address(allowedTargetsEnforcer), + terms: abi.encodePacked(address(users.alice.deleGator)) + }); paymentCaveats_[3] = Caveat({ args: hex"", enforcer: address(argsEqualityCheckEnforcer), terms: argsTerms_ }); // Create payment delegation from Bob to NativeTokenPaymentEnforcer @@ -165,8 +187,11 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // The args contain the payment delegation to redeem bytes memory args_ = abi.encode(paymentDelegations_); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); vm.startPrank(address(delegationManager)); @@ -179,7 +204,13 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { paymentDelegations_[0].delegator, address(nativeTokenPaymentEnforcer), paymentDelegations_[0] ); nativeTokenPaymentEnforcer.afterHook( - paymentTerms_, args_, action_, delegationHash_, address(users.alice.deleGator), address(0) + paymentTerms_, + args_, + ModeLib.encodeSimpleSingle(), + ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData), + delegationHash_, + address(users.alice.deleGator), + address(0) ); } @@ -208,7 +239,6 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { salt: 0, signature: hex"" }); - paymentDelegations_[0] = signDelegation(users.bob, paymentDelegations_[0]); // Using a mock delegation manager @@ -219,12 +249,17 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { (bytes32 delegationHash_,) = _getExampleDelegation(terms_, hex""); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + ModeCode mode_ = ModeLib.encodeSimpleSingle(); vm.startPrank(mockDelegationManager_); vm.expectRevert("NativeTokenPaymentEnforcer:payment-not-received"); - nativeTokenPaymentEnforcer.afterHook(terms_, args_, action_, delegationHash_, address(0), address(0)); + nativeTokenPaymentEnforcer.afterHook(terms_, args_, mode_, executionCallData_, delegationHash_, address(0), address(0)); } // Should FAIL if the sender is different from the delegation manager. @@ -232,7 +267,9 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Using an invalid sender, it must be the delegation manager vm.startPrank(address(users.bob.deleGator)); vm.expectRevert("NativeTokenPaymentEnforcer:only-delegation-manager"); - nativeTokenPaymentEnforcer.afterHook(hex"", hex"", new Action[](1)[0], bytes32(0), address(0), address(0)); + nativeTokenPaymentEnforcer.afterHook( + hex"", hex"", ModeLib.encodeSimpleSingle(), new bytes(0), bytes32(0), address(0), address(0) + ); } function test_chargePaymentFromAllowance() public { @@ -269,8 +306,11 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // The args contain the payment delegation to redeem bytes memory args_ = abi.encode(paymentDelegations_); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); uint256 aliceBalanceBefore_ = address(users.alice.deleGator).balance; @@ -278,7 +318,7 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Pass the delegation payment in the args. paidDelegations_[0].caveats[0].args = args_; - invokeDelegation_UserOp(users.bob, paidDelegations_, action_); + invokeDelegation_UserOp(users.bob, paidDelegations_, execution_); assertEq(aliceDeleGatorCounter.count(), 1); @@ -324,8 +364,11 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // The args contain the payment delegation to redeem bytes memory argsWithBobPayment_ = abi.encode(paymentDelegations_); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); uint256 aliceBalanceBefore_ = address(users.alice.deleGator).balance; @@ -333,7 +376,7 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Pass the delegation payment in the args. maliciousPaidDelegations_[0].caveats[0].args = argsWithBobPayment_; - invokeDelegation_UserOp(users.carol, maliciousPaidDelegations_, action_); + invokeDelegation_UserOp(users.carol, maliciousPaidDelegations_, execution_); assertEq(aliceDeleGatorCounter.count(), 1); // Alice received the payment @@ -341,7 +384,7 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Pass the delegation payment in the args. originalPaidDelegations_[0].caveats[0].args = argsWithBobPayment_; - invokeDelegation_UserOp(users.bob, originalPaidDelegations_, action_); + invokeDelegation_UserOp(users.bob, originalPaidDelegations_, execution_); // The execution did not work because the allowance has already been used. assertEq(aliceDeleGatorCounter.count(), 1); } @@ -389,8 +432,11 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // The args contain the payment delegation to redeem bytes memory argsWithBobPayment_ = abi.encode(paymentDelegations_); - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); uint256 aliceBalanceBefore_ = address(users.alice.deleGator).balance; @@ -398,7 +444,7 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Pass the delegation payment in the args. maliciousPaidDelegations_[0].caveats[0].args = argsWithBobPayment_; - invokeDelegation_UserOp(users.carol, maliciousPaidDelegations_, action_); + invokeDelegation_UserOp(users.carol, maliciousPaidDelegations_, execution_); // The execution did not work because the allowance fails due to the invalid args. assertEq(aliceDeleGatorCounter.count(), 0); // Alice did not receive the payment @@ -406,7 +452,7 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { // Pass the delegation payment in the args. originalPaidDelegations_[0].caveats[0].args = argsWithBobPayment_; - invokeDelegation_UserOp(users.bob, originalPaidDelegations_, action_); + invokeDelegation_UserOp(users.bob, originalPaidDelegations_, execution_); // The execution works well with a proper args assertEq(aliceDeleGatorCounter.count(), 1); // Alice received the payment @@ -464,9 +510,15 @@ contract NativeTokenPaymentEnforcerTest is CaveatEnforcerBaseTest { } } -/// @dev This contract is used for testing a case where the redeemDelegation() function doesn't work as expected +/// @dev This contract is used for testing a case where the redeemDelegations() function doesn't work as expected contract MockDelegationManager { - function redeemDelegation(bytes[] calldata _permissionContexts, Action[] calldata _actions) external { - // Does not do anything, the action is not processed + function redeemDelegations( + bytes[] calldata _permissionContexts, + ModeCode[] calldata _modes, + bytes[] calldata _executionCallDatas + ) + external + { + // Does not do anything, the execution is not processed } } diff --git a/test/enforcers/NonceEnforcer.t.sol b/test/enforcers/NonceEnforcer.t.sol index 2658db2..3252eb1 100644 --- a/test/enforcers/NonceEnforcer.t.sol +++ b/test/enforcers/NonceEnforcer.t.sol @@ -2,19 +2,25 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { NonceEnforcer } from "../../src/enforcers/NonceEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract NonceEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// NonceEnforcer public enforcer; - Action action = Action({ to: address(0), value: 0, data: hex"" }); + Execution execution = Execution({ target: address(0), value: 0, callData: hex"" }); + bytes executionCallData = ExecutionLib.encodeSingle(execution.target, execution.value, execution.callData); address delegator = address(users.alice.deleGator); address dm = address(delegationManager); + ModeCode mode = ModeLib.encodeSimpleSingle(); ////////////////////////////// Events ////////////////////////////// @@ -66,7 +72,7 @@ contract NonceEnforcerTest is CaveatEnforcerBaseTest { vm.startPrank(dm); // Should not revert - enforcer.beforeHook(terms_, hex"", action, bytes32(0), delegator, address(0)); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, address(0)); } ////////////////////// Errors ////////////////////// @@ -94,7 +100,7 @@ contract NonceEnforcerTest is CaveatEnforcerBaseTest { bytes memory terms_ = abi.encode(nonce_ + 1); vm.startPrank(dm); vm.expectRevert(bytes("NonceEnforcer:invalid-nonce")); - enforcer.beforeHook(terms_, hex"", action, bytes32(0), delegator, address(0)); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, address(0)); // Increment ID so the current ID is high enough to check a lower ID vm.startPrank(dm); @@ -105,7 +111,7 @@ contract NonceEnforcerTest is CaveatEnforcerBaseTest { terms_ = abi.encode(nonce_ - 1); vm.startPrank(dm); vm.expectRevert(bytes("NonceEnforcer:invalid-nonce")); - enforcer.beforeHook(terms_, hex"", action, bytes32(0), delegator, address(0)); + enforcer.beforeHook(terms_, hex"", mode, executionCallData, bytes32(0), delegator, address(0)); } ////////////////////// Integration ////////////////////// diff --git a/test/enforcers/PasswordEnforcer.t.sol b/test/enforcers/PasswordEnforcer.t.sol index 91569c2..15ca38d 100644 --- a/test/enforcers/PasswordEnforcer.t.sol +++ b/test/enforcers/PasswordEnforcer.t.sol @@ -2,9 +2,11 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { PasswordEnforcer } from "../utils/PasswordCaveatEnforcer.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; @@ -15,8 +17,11 @@ import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/Mes import { SigningUtilsLib } from "../utils/SigningUtilsLib.t.sol"; contract PasswordEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// PasswordEnforcer public passwordEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -27,7 +32,8 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { } function test_userInputCorrectArgsWorks() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); uint256 password_ = uint256(123456789); bytes memory terms_ = abi.encode(password_); address delegator_ = address(users.alice.deleGator); @@ -35,11 +41,12 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { vm.startPrank(address(delegationManager)); // First usage works well - passwordEnforcer.beforeHook(terms_, abi.encode(password_), action_, bytes32(0), delegator_, address(0)); + passwordEnforcer.beforeHook(terms_, abi.encode(password_), mode, executionCallData_, bytes32(0), delegator_, address(0)); } function test_userInputIncorrectArgs() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); uint256 password_ = uint256(123456789); uint256 incorrectPassword_ = uint256(5154848789); bytes memory terms_ = abi.encode(password_); @@ -49,16 +56,21 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { vm.expectRevert("PasswordEnforcerError"); - passwordEnforcer.beforeHook(terms_, abi.encode(incorrectPassword_), action_, bytes32(0), delegator_, address(0)); + passwordEnforcer.beforeHook( + terms_, abi.encode(incorrectPassword_), mode, executionCallData_, bytes32(0), delegator_, address(0) + ); } ////////////////////// Integration ////////////////////// function test_userInputIncorrectArgsWithOffchainDelegation() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); bytes memory inputTerms_ = abi.encode(uint256(12345)); bytes memory incorrectPassword_ = abi.encode(uint256(123154245)); @@ -81,7 +93,7 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has not increased assertEq(aliceDeleGatorCounter.count(), initialValue_); } @@ -90,9 +102,12 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { function test_userInputCorrectArgsWorksWithOffchainDelegation() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); bytes memory inputTerms_ = abi.encode(uint256(12345)); bytes memory password_ = abi.encode(uint256(12345)); @@ -122,16 +137,19 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has increased by 1 assertEq(aliceDeleGatorCounter.count(), initialValue_ + 1); } function test_userInputIncorrectArgsWorksWithOffchainDelegation() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); bytes memory inputTerms_ = abi.encode(uint256(12345)); @@ -162,7 +180,7 @@ contract PasswordEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Validate that the count has NOT increased assertEq(aliceDeleGatorCounter.count(), initialValue_); } diff --git a/test/enforcers/RedeemerEnforcer.t.sol b/test/enforcers/RedeemerEnforcer.t.sol index 396c8eb..6ac8030 100644 --- a/test/enforcers/RedeemerEnforcer.t.sol +++ b/test/enforcers/RedeemerEnforcer.t.sol @@ -1,15 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -import { Action } from "../../src/utils/Types.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; + +import { Execution, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { RedeemerEnforcer } from "../../src/enforcers/RedeemerEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract RedeemerEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// RedeemerEnforcer public redeemerEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -31,23 +37,37 @@ contract RedeemerEnforcerTest is CaveatEnforcerBaseTest { // should pass if called from a single valid redeemer function test_validSingleRedeemerCanExecute() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory terms_ = abi.encodePacked(address(users.bob.deleGator)); vm.prank(address(delegationManager)); - redeemerEnforcer.beforeHook(terms_, hex"", action_, keccak256(""), address(0), address(users.bob.deleGator)); + redeemerEnforcer.beforeHook( + terms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(users.bob.deleGator) + ); } // should pass if called from multiple valid redeemers function test_validMultipleRedeemersCanExecute() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory terms_ = abi.encodePacked(address(users.alice.deleGator), address(users.bob.deleGator)); vm.startPrank(address(delegationManager)); - redeemerEnforcer.beforeHook(terms_, hex"", action_, keccak256(""), address(0), address(users.alice.deleGator)); - redeemerEnforcer.beforeHook(terms_, hex"", action_, keccak256(""), address(0), address(users.bob.deleGator)); + redeemerEnforcer.beforeHook( + terms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(users.alice.deleGator) + ); + redeemerEnforcer.beforeHook( + terms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(users.bob.deleGator) + ); } ////////////////////// Invalid cases ////////////////////// @@ -60,25 +80,37 @@ contract RedeemerEnforcerTest is CaveatEnforcerBaseTest { // should revert if called from an invalid redeemer function test_revertWithInvalidRedeemer() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory terms_ = abi.encodePacked(address(users.bob.deleGator)); vm.prank(address(delegationManager)); // Dave is not a valid redeemer vm.expectRevert("RedeemerEnforcer:unauthorized-redeemer"); - redeemerEnforcer.beforeHook(terms_, hex"", action_, keccak256(""), address(0), address(users.dave.deleGator)); + redeemerEnforcer.beforeHook( + terms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(users.dave.deleGator) + ); } // should revert with invalid terms length function test_revertWithInvalidTerms() public { - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); bytes memory invalidTerms_ = abi.encodePacked(uint8(1)); vm.prank(address(delegationManager)); vm.expectRevert("RedeemerEnforcer:invalid-terms-length"); - redeemerEnforcer.beforeHook(invalidTerms_, hex"", action_, keccak256(""), address(0), address(users.bob.deleGator)); + redeemerEnforcer.beforeHook( + invalidTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(users.bob.deleGator) + ); } function _getEnforcer() internal view override returns (ICaveatEnforcer) { diff --git a/test/enforcers/TimestampEnforcer.t.sol b/test/enforcers/TimestampEnforcer.t.sol index 2d6b2d6..1accd25 100644 --- a/test/enforcers/TimestampEnforcer.t.sol +++ b/test/enforcers/TimestampEnforcer.t.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -import { Action, Caveat, Delegation } from "../../src/utils/Types.sol"; +import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { Counter } from "../utils/Counter.t.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { TimestampEnforcer } from "../../src/enforcers/TimestampEnforcer.sol"; @@ -13,9 +15,12 @@ import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract TimestampEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////// State ////////////////////// TimestampEnforcer public timestampEnforcer; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////// Set up ////////////////////// @@ -29,49 +34,68 @@ contract TimestampEnforcerTest is CaveatEnforcerBaseTest { // should SUCCEED to INVOKE method AFTER timestamp reached function test_methodCanBeCalledAfterTimestamp() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + skip(1 hours); // Increase time 1 hour uint128 timestampAfterThreshold_ = 1; // Minimum timestamp uint128 timestampBeforeThreshold_ = 0; // Not using before threshold bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should SUCCEED to INVOKE method BEFORE timestamp reached function test_methodCanBeCalledBeforeTimestamp() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 timestampAfterThreshold_ = 0; // Not using after threshold uint128 timestampBeforeThreshold_ = uint128(block.timestamp + 1 hours); bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should SUCCEED to INVOKE method inside of timestamp RANGE function test_methodCanBeCalledInsideTimestampRange() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 timestampAfterThreshold_ = 1; // Minimum timestamp uint128 timestampBeforeThreshold_ = uint128(block.timestamp + 1 hours); skip(1 minutes); // Increase time 1 minute bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } ////////////////////// Invalid cases ////////////////////// // should FAIL to INVOKE method BEFORE timestamp reached function test_methodFailsIfCalledTimestamp() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); uint128 timestampAfterThreshold_ = uint128(block.timestamp + 1 hours); uint128 timestampBeforeThreshold_ = 0; // Not using before threshold @@ -79,69 +103,89 @@ contract TimestampEnforcerTest is CaveatEnforcerBaseTest { vm.prank(address(delegationManager)); vm.expectRevert("TimestampEnforcer:early-delegation"); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method AFTER timestamp reached function test_methodFailsIfCalledAfterTimestamp() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 timestampAfterThreshold_ = 0; // Not using after threshold uint128 timestampBeforeThreshold_ = uint128(block.timestamp); skip(1 hours); // Increase time 1 hour bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); vm.expectRevert("TimestampEnforcer:expired-delegation"); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method BEFORE timestamp RANGE function test_methodFailsIfCalledBeforeTimestampRange() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 timestampAfterThreshold_ = uint128(block.timestamp + 1 hours); uint128 timestampBeforeThreshold_ = uint128(block.timestamp + 2 hours); bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); vm.expectRevert("TimestampEnforcer:early-delegation"); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE method AFTER timestamp RANGE function test_methodFailsIfCalledAfterTimestampRange() public { - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + uint128 timestampAfterThreshold_ = uint128(block.timestamp + 1 hours); uint128 timestampBeforeThreshold_ = uint128(block.timestamp + 2 hours); skip(3 hours); // Increase time 3 hours bytes memory inputTerms_ = abi.encodePacked(timestampAfterThreshold_, timestampBeforeThreshold_); vm.prank(address(delegationManager)); vm.expectRevert("TimestampEnforcer:expired-delegation"); - timestampEnforcer.beforeHook(inputTerms_, hex"", action_, keccak256(""), address(0), address(0)); + timestampEnforcer.beforeHook(inputTerms_, hex"", mode, executionCallData_, keccak256(""), address(0), address(0)); } // should FAIL to INVOKE with invalid input terms function test_methodFailsIfCalledWithInvalidInputTerms() public { - Action memory action_; + Execution memory execution_; + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); + bytes memory terms_ = abi.encodePacked(uint32(1)); vm.expectRevert("TimestampEnforcer:invalid-terms-length"); - timestampEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + timestampEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); terms_ = abi.encodePacked(uint256(1), uint256(1)); vm.expectRevert("TimestampEnforcer:invalid-terms-length"); - timestampEnforcer.beforeHook(terms_, hex"", action_, bytes32(0), address(0), address(0)); + timestampEnforcer.beforeHook(terms_, hex"", mode, executionCallData_, bytes32(0), address(0), address(0)); } ////////////////////// Integration ////////////////////// // should SUCCEED to INVOKE until reaching timestamp Integration function test_methodCanBeCalledAfterTimestampIntegration() public { uint256 initialValue_ = aliceDeleGatorCounter.count(); - // Create the action that would be executed - Action memory action_ = - Action({ to: address(aliceDeleGatorCounter), value: 0, data: abi.encodeWithSelector(Counter.increment.selector) }); + // Create the execution that would be executed + Execution memory execution_ = Execution({ + target: address(aliceDeleGatorCounter), + value: 0, + callData: abi.encodeWithSelector(Counter.increment.selector) + }); skip(10); // Increase time 10 seconds // Not using before threshold (timestampAfterThreshold_ = 1, timestampBeforeThreshold_ = 100) bytes memory inputTerms_ = abi.encodePacked(uint128(1), uint128(100)); @@ -164,7 +208,7 @@ contract TimestampEnforcerTest is CaveatEnforcerBaseTest { delegations_[0] = delegation; // Enforcer allows the delegation - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 valueAfter_ = aliceDeleGatorCounter.count(); @@ -174,7 +218,7 @@ contract TimestampEnforcerTest is CaveatEnforcerBaseTest { // Enforcer blocks the delegation skip(100); // Increase time 100 seconds - invokeDelegation_UserOp(users.bob, delegations_, action_); + invokeDelegation_UserOp(users.bob, delegations_, execution_); // Get final count uint256 finalValue_ = aliceDeleGatorCounter.count(); // Validate that the count has not increased by 1 diff --git a/test/enforcers/ValueLteEnforcer.t.sol b/test/enforcers/ValueLteEnforcer.t.sol index 6adf975..737b61b 100644 --- a/test/enforcers/ValueLteEnforcer.t.sol +++ b/test/enforcers/ValueLteEnforcer.t.sol @@ -3,18 +3,23 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; import { BasicERC20 } from "../utils/BasicERC20.t.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; import "../../src/utils/Types.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution, ModeCode } from "../../src/utils/Types.sol"; import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; import { ValueLteEnforcer } from "../../src/enforcers/ValueLteEnforcer.sol"; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; contract ValueLteEnforcerTest is CaveatEnforcerBaseTest { + using ModeLib for ModeCode; + ////////////////////////////// State ////////////////////////////// ValueLteEnforcer public enforcer; BasicERC20 public token; address delegator; + ModeCode public mode = ModeLib.encodeSimpleSingle(); ////////////////////////////// Events ////////////////////////////// @@ -53,25 +58,27 @@ contract ValueLteEnforcerTest is CaveatEnforcerBaseTest { // Validates that valid values don't revert function test_allow_valueLte() public view { // Equal - bytes memory terms_ = abi.encodePacked(uint256(1 ether)); - Action memory action_ = Action({ - to: address(users.alice.deleGator), + bytes memory terms_ = abi.encode(uint256(1 ether)); + Execution memory execution_ = Execution({ + target: address(users.alice.deleGator), value: 1 ether, - data: abi.encodeWithSignature("test_valueLteIsAllowed()") + callData: abi.encodeWithSignature("test_valueLteIsAllowed()") }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Should not revert - enforcer.beforeHook(terms_, "", action_, bytes32(0), address(0), address(0)); + enforcer.beforeHook(terms_, "", mode, executionCallData_, bytes32(0), address(0), address(0)); // Less than - action_ = Action({ - to: address(users.alice.deleGator), + execution_ = Execution({ + target: address(users.alice.deleGator), value: 0.1 ether, - data: abi.encodeWithSignature("test_valueLteIsAllowed()") + callData: abi.encodeWithSignature("test_valueLteIsAllowed()") }); + executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Should not revert - enforcer.beforeHook(terms_, "", action_, bytes32(0), address(0), address(0)); + enforcer.beforeHook(terms_, "", mode, executionCallData_, bytes32(0), address(0), address(0)); } //////////////////////// Errors //////////////////////// @@ -80,15 +87,16 @@ contract ValueLteEnforcerTest is CaveatEnforcerBaseTest { function test_notAllow_valueGt() public { // Gt bytes memory terms_ = abi.encodePacked(uint256(1 ether)); - Action memory action_ = Action({ - to: address(users.alice.deleGator), + Execution memory execution_ = Execution({ + target: address(users.alice.deleGator), value: 2 ether, - data: abi.encodeWithSignature("test_valueLteIsAllowed()") + callData: abi.encodeWithSignature("test_valueLteIsAllowed()") }); + bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); // Should not revert vm.expectRevert(bytes("ValueLteEnforcer:value-too-high")); - enforcer.beforeHook(terms_, "", action_, bytes32(0), address(0), address(0)); + enforcer.beforeHook(terms_, "", mode, executionCallData_, bytes32(0), address(0), address(0)); } // Validates the terms are well formed diff --git a/test/metaTests/EncoderLibTest.t.sol b/test/metaTests/EncoderLibTest.t.sol index e188e95..530ca89 100644 --- a/test/metaTests/EncoderLibTest.t.sol +++ b/test/metaTests/EncoderLibTest.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.23; import { Test } from "forge-std/Test.sol"; import { Caveat, Delegation } from "../../src/utils/Types.sol"; -import { DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../../src/utils/Typehashes.sol"; +import { DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../../src/utils/Constants.sol"; import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; contract EncoderLibTest is Test { diff --git a/test/metaTests/TypehashTest.t.sol b/test/metaTests/TypehashTest.t.sol index 8d8d066..d999d14 100644 --- a/test/metaTests/TypehashTest.t.sol +++ b/test/metaTests/TypehashTest.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.23; import "forge-std/Test.sol"; -import { EIP712_DOMAIN_TYPEHASH, DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../../src/utils/Typehashes.sol"; +import { EIP712_DOMAIN_TYPEHASH, DELEGATION_TYPEHASH, CAVEAT_TYPEHASH } from "../../src/utils/Constants.sol"; contract TypehashTest is Test { ////////////////////////////// State ////////////////////////////// diff --git a/test/utils/BaseTest.t.sol b/test/utils/BaseTest.t.sol index e27fd9f..e5e6301 100644 --- a/test/utils/BaseTest.t.sol +++ b/test/utils/BaseTest.t.sol @@ -9,23 +9,26 @@ import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy import { FCL_ecdsa_utils } from "@freshCryptoLib/FCL_ecdsa_utils.sol"; import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import { IEntryPoint } from "@account-abstraction/core/EntryPoint.sol"; +import { ModeLib } from "@erc7579/lib/ModeLib.sol"; +import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; -// import { P256 } from "../../src/external/Daimo/P256.sol"; import { P256FCLVerifierLib } from "../../src/libraries/P256FCLVerifierLib.sol"; import { FCL_all_wrapper } from "./FCLWrapperLib.sol"; +import { EXECUTE_SIGNATURE } from "./Constants.sol"; import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; import { TestUser, TestUsers, Implementation, SignatureType } from "./Types.t.sol"; import { SigningUtilsLib } from "./SigningUtilsLib.t.sol"; import { StorageUtilsLib } from "./StorageUtilsLib.t.sol"; -import { Action, PackedUserOperation, Delegation } from "../../src/utils/Types.sol"; +import { Execution, PackedUserOperation, Delegation, ModeCode } from "../../src/utils/Types.sol"; import { SimpleFactory } from "../../src/utils/SimpleFactory.sol"; import { DelegationManager } from "../../src/DelegationManager.sol"; -import { IDeleGatorCoreFull } from "../../src/interfaces/IDeleGatorCoreFull.sol"; +import { DeleGatorCore } from "../../src/DeleGatorCore.sol"; import { HybridDeleGator } from "../../src/HybridDeleGator.sol"; import { MultiSigDeleGator } from "../../src/MultiSigDeleGator.sol"; abstract contract BaseTest is Test { + using ModeLib for ModeCode; using MessageHashUtils for bytes32; SignatureType public SIGNATURE_TYPE; @@ -273,23 +276,27 @@ abstract contract BaseTest is Test { userOperation_ = createAndSignUserOp(_user, _sender, _callData, hex""); } - function execute_UserOp(TestUser memory _user, Action memory _action) public { - execute_UserOp(_user, _action, false); + function execute_UserOp(TestUser memory _user, Execution memory _execution) public { + execute_UserOp(_user, _execution, hex"", false); } - function execute_UserOp(TestUser memory _user, Action memory _action, bool _shouldFail) public { - execute_UserOp(_user, _action, hex"", _shouldFail); + function execute_UserOp(TestUser memory _user, Execution memory _execution, bool _shouldFail) public { + execute_UserOp(_user, _execution, hex"", _shouldFail); } function execute_UserOp( TestUser memory _user, - Action memory _action, + Execution memory _execution, bytes memory _paymasterAndData, bool _shouldFail ) public { - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.execute.selector, _action); + bytes memory userOpCallData_ = abi.encodeWithSignature( + EXECUTE_SIGNATURE, + ModeLib.encodeSimpleSingle(), + ExecutionLib.encodeSingle(_execution.target, _execution.value, _execution.callData) + ); PackedUserOperation memory userOp_ = createUserOp(address(_user.deleGator), userOpCallData_, hex"", _paymasterAndData); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(_user, userOpHash_.toEthSignedMessageHash()); @@ -297,38 +304,42 @@ abstract contract BaseTest is Test { } function execute_UserOp(TestUser memory _user, bytes memory _callData) public { - Action memory action_ = Action({ to: address(_user.deleGator), value: 0, data: _callData }); - execute_UserOp(_user, action_); + Execution memory execution_ = Execution({ target: address(_user.deleGator), value: 0, callData: _callData }); + execute_UserOp(_user, execution_); } - function executeBatch_UserOp(TestUser memory _user, Action[] memory _actions) public { - bytes memory userOpCallData_ = abi.encodeWithSelector(IDeleGatorCoreFull.executeBatch.selector, _actions); + function executeBatch_UserOp(TestUser memory _user, Execution[] memory _executions) public { + bytes memory userOpCallData_ = + abi.encodeWithSignature(EXECUTE_SIGNATURE, ModeLib.encodeSimpleBatch(), abi.encode(_executions)); PackedUserOperation memory userOp_ = createUserOp(address(_user.deleGator), userOpCallData_); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(_user, userOpHash_.toEthSignedMessageHash()); submitUserOp_Bundler(userOp_, false); } - function invokeDelegation_UserOp(TestUser memory _user, Delegation[] memory _delegations, Action memory _action) public { - return invokeDelegation_UserOp(_user, _delegations, _action, hex""); + function invokeDelegation_UserOp(TestUser memory _user, Delegation[] memory _delegations, Execution memory _execution) public { + return invokeDelegation_UserOp(_user, _delegations, _execution, hex""); } function invokeDelegation_UserOp( TestUser memory _user, Delegation[] memory _delegations, - Action memory _action, + Execution memory _execution, bytes memory _initCode ) public { - bytes[] memory dataArray = new bytes[](1); - dataArray[0] = abi.encode(_delegations); + bytes[] memory permissionContexts_ = new bytes[](1); + permissionContexts_[0] = abi.encode(_delegations); + + bytes[] memory executionCallDatas_ = new bytes[](1); + executionCallDatas_[0] = ExecutionLib.encodeSingle(_execution.target, _execution.value, _execution.callData); - Action[] memory actions_ = new Action[](1); - actions_[0] = _action; + ModeCode[] memory modes_ = new ModeCode[](1); + modes_[0] = ModeLib.encodeSimpleSingle(); bytes memory userOpCallData_ = - abi.encodeWithSelector(IDeleGatorCoreFull.redeemDelegation.selector, dataArray, actions_); + abi.encodeWithSelector(DeleGatorCore.redeemDelegations.selector, permissionContexts_, modes_, executionCallDatas_); PackedUserOperation memory userOp_ = createUserOp(address(_user.deleGator), userOpCallData_, _initCode); bytes32 userOpHash_ = entryPoint.getUserOpHash(userOp_); userOp_.signature = signHash(_user, userOpHash_.toEthSignedMessageHash()); @@ -412,7 +423,7 @@ abstract contract BaseTest is Test { user_.addr = payable(addr_); user_.privateKey = privateKey_; (user_.x, user_.y) = FCL_ecdsa_utils.ecdsa_derivKpub(user_.privateKey); - user_.deleGator = IDeleGatorCoreFull(deployDeleGator(user_)); + user_.deleGator = DeleGatorCore(payable(deployDeleGator(user_))); vm.deal(address(user_.deleGator), 100 ether); vm.label(address(user_.deleGator), string.concat(_name, " DeleGator")); diff --git a/test/utils/Constants.sol b/test/utils/Constants.sol new file mode 100644 index 0000000..b5677f7 --- /dev/null +++ b/test/utils/Constants.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT AND Apache-2.0 + +pragma solidity 0.8.23; + +string constant EXECUTE_SIGNATURE = "execute(bytes32,bytes)"; +string constant EXECUTE_SINGULAR_SIGNATURE = "execute((address,uint256,bytes))"; diff --git a/test/utils/MockCaveatEnforcer.sol b/test/utils/MockCaveatEnforcer.sol index c0d489f..9cab7da 100644 --- a/test/utils/MockCaveatEnforcer.sol +++ b/test/utils/MockCaveatEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { ModeCode } from "../../src/utils/Types.sol"; /** * @title MockCaveatEnforcer @@ -16,7 +16,7 @@ contract MockCaveatEnforcer is ICaveatEnforcer { * @dev Mocked implementation of the beforeHook function. * Increments the beforeHook call count. */ - function beforeHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) external { + function beforeHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) external { beforeHookCallCount++; } @@ -24,7 +24,7 @@ contract MockCaveatEnforcer is ICaveatEnforcer { * @dev Mocked implementation of the afterHook function. * Increments the afterHook call count. */ - function afterHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) external { + function afterHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) external { afterHookCallCount++; } } diff --git a/test/utils/MockFailureCaveatEnforcer.sol b/test/utils/MockFailureCaveatEnforcer.sol index 5eef1e1..1c5c80b 100644 --- a/test/utils/MockFailureCaveatEnforcer.sol +++ b/test/utils/MockFailureCaveatEnforcer.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { ModeCode } from "../../src/utils/Types.sol"; /** * @title MockFailureCaveatEnforcer @@ -15,7 +15,7 @@ contract MockFailureCaveatEnforcer is ICaveatEnforcer { * @dev Mocked implementation of the beforeHook function. * Increments the beforeHook call count. */ - function beforeHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) external { + function beforeHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) external { beforeHookCallCount++; } @@ -23,7 +23,7 @@ contract MockFailureCaveatEnforcer is ICaveatEnforcer { * @dev Mocked implementation of the afterHook function. * Increments the afterHook call count. */ - function afterHook(bytes calldata, bytes calldata, Action calldata, bytes32, address, address) external pure { + function afterHook(bytes calldata, bytes calldata, ModeCode, bytes calldata, bytes32, address, address) external pure { revert(); } } diff --git a/test/utils/PasswordCaveatEnforcer.t.sol b/test/utils/PasswordCaveatEnforcer.t.sol index 636c629..1903daa 100644 --- a/test/utils/PasswordCaveatEnforcer.t.sol +++ b/test/utils/PasswordCaveatEnforcer.t.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.23; import { CaveatEnforcer } from "../../src/enforcers/CaveatEnforcer.sol"; -import { Action } from "../../src/utils/Types.sol"; +import { Execution, ModeCode } from "../../src/utils/Types.sol"; /** * @title Password Enforcer @@ -18,7 +18,8 @@ contract PasswordEnforcer is CaveatEnforcer { function beforeHook( bytes calldata _terms, bytes calldata _args, - Action calldata, + ModeCode, + bytes calldata, bytes32, address, address diff --git a/test/utils/Types.t.sol b/test/utils/Types.t.sol index 299bd13..16329d5 100644 --- a/test/utils/Types.t.sol +++ b/test/utils/Types.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT AND Apache-2.0 pragma solidity 0.8.23; -import { IDeleGatorCoreFull } from "../../src/interfaces/IDeleGatorCoreFull.sol"; +import { DeleGatorCore } from "../../src/DeleGatorCore.sol"; struct TestUser { string name; address payable addr; uint256 privateKey; - IDeleGatorCoreFull deleGator; + DeleGatorCore deleGator; uint256 x; uint256 y; }