From 19e2c539bb1eba2296ab8dbcc43985fbb1dc146b Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Thu, 11 Jul 2024 14:10:49 -0700 Subject: [PATCH 01/11] rename functionId to validationId to prepare deleting functionId and introduce validationId --- src/account/PluginManagerInternals.sol | 8 +-- src/account/UpgradeableModularAccount.sol | 56 ++++++++++--------- src/helpers/FunctionReferenceLib.sol | 8 +-- src/helpers/ValidationConfigLib.sol | 12 ++-- src/interfaces/IExecutionHook.sol | 14 +++-- src/interfaces/IPlugin.sol | 4 +- src/interfaces/IValidation.sol | 19 ++++--- src/interfaces/IValidationHook.sol | 21 ++++--- src/plugins/owner/SingleOwnerPlugin.sol | 12 ++-- .../permissionhooks/AllowlistPlugin.sol | 10 ++-- standard/ERCs/erc-6900.md | 40 ++++++------- test/account/AccountExecHooks.t.sol | 6 +- test/account/AccountLoupe.t.sol | 16 +++--- test/account/PerHookData.t.sol | 8 +-- test/account/SelfCallAuthorization.t.sol | 2 +- test/account/ValidationIntersection.t.sol | 14 ++--- test/libraries/FunctionReferenceLib.t.sol | 12 ++-- test/mocks/plugins/ComprehensivePlugin.sol | 46 +++++++-------- .../plugins/MockAccessControlHookPlugin.sol | 10 ++-- test/mocks/plugins/ReturnDataPluginMocks.sol | 2 +- test/mocks/plugins/ValidationPluginMocks.sol | 18 +++--- test/samples/AllowlistPlugin.t.sol | 6 +- 22 files changed, 177 insertions(+), 167 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 4df90e1f..1fc64744 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -78,7 +78,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.functionId); + FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.validationId); if (mv.isDefault) { _storage.validationData[validationFunction].isGlobal = true; @@ -99,7 +99,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.functionId); + FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.validationId); _storage.validationData[validationFunction].isGlobal = false; _storage.validationData[validationFunction].isSignatureValidation = false; @@ -183,7 +183,7 @@ abstract contract PluginManagerInternals is IPluginManager { for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.functionId); + FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.validationId); _addExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } @@ -223,7 +223,7 @@ abstract contract PluginManagerInternals is IPluginManager { uint256 length = manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.functionId); + FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.validationId); EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; _removeExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index d2d1b2cc..0c1bbecd 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -68,18 +68,18 @@ contract UpgradeableModularAccount is error NativeTokenSpendingNotPermitted(address plugin); error NonCanonicalEncoding(); error NotEntryPoint(); - error PostExecHookReverted(address plugin, uint8 functionId, bytes revertReason); - error PreExecHookReverted(address plugin, uint8 functionId, bytes revertReason); - error PreRuntimeValidationHookFailed(address plugin, uint8 functionId, bytes revertReason); + error PostExecHookReverted(address plugin, uint8 validationId, bytes revertReason); + error PreExecHookReverted(address plugin, uint8 validationId, bytes revertReason); + error PreRuntimeValidationHookFailed(address plugin, uint8 validationId, bytes revertReason); error RequireUserOperationContext(); error RuntimeValidationFunctionMissing(bytes4 selector); - error RuntimeValidationFunctionReverted(address plugin, uint8 functionId, bytes revertReason); + error RuntimeValidationFunctionReverted(address plugin, uint8 validationId, bytes revertReason); error SelfCallRecursionDepthExceeded(); - error SignatureValidationInvalid(address plugin, uint8 functionId); - error UnexpectedAggregator(address plugin, uint8 functionId, address aggregator); + error SignatureValidationInvalid(address plugin, uint8 validationId); + error UnexpectedAggregator(address plugin, uint8 validationId, address aggregator); error UnrecognizedFunction(bytes4 selector); error UserOpValidationFunctionMissing(bytes4 selector); - error ValidationDoesNotApply(bytes4 selector, address plugin, uint8 functionId, bool isGlobal); + error ValidationDoesNotApply(bytes4 selector, address plugin, uint8 validationId, bool isGlobal); error ValidationSignatureSegmentMissing(); error SignatureSegmentOutOfOrder(); @@ -343,13 +343,13 @@ contract UpgradeableModularAccount is FunctionReference sigValidation = FunctionReference.wrap(bytes21(signature)); - (address plugin, uint8 functionId) = sigValidation.unpack(); + (address plugin, uint8 validationId) = sigValidation.unpack(); if (!_storage.validationData[sigValidation].isSignatureValidation) { - revert SignatureValidationInvalid(plugin, functionId); + revert SignatureValidationInvalid(plugin, validationId); } if ( - IValidation(plugin).validateSignature(functionId, msg.sender, hash, signature[21:]) + IValidation(plugin).validateSignature(validationId, msg.sender, hash, signature[21:]) == _1271_MAGIC_VALUE ) { return _1271_MAGIC_VALUE; @@ -434,13 +434,13 @@ contract UpgradeableModularAccount is userOp.signature = ""; } - (address plugin, uint8 functionId) = preUserOpValidationHooks[i].unpack(); + (address plugin, uint8 validationId) = preUserOpValidationHooks[i].unpack(); uint256 currentValidationData = - IValidationHook(plugin).preUserOpValidationHook(functionId, userOp, userOpHash); + IValidationHook(plugin).preUserOpValidationHook(validationId, userOp, userOpHash); if (uint160(currentValidationData) > 1) { // If the aggregator is not 0 or 1, it is an unexpected value - revert UnexpectedAggregator(plugin, functionId, address(uint160(currentValidationData))); + revert UnexpectedAggregator(plugin, validationId, address(uint160(currentValidationData))); } validationData = _coalescePreValidation(validationData, currentValidationData); } @@ -453,8 +453,8 @@ contract UpgradeableModularAccount is userOp.signature = signatureSegment.getBody(); - (address plugin, uint8 functionId) = userOpValidationFunction.unpack(); - uint256 currentValidationData = IValidation(plugin).validateUserOp(functionId, userOp, userOpHash); + (address plugin, uint8 validationId) = userOpValidationFunction.unpack(); + uint256 currentValidationData = IValidation(plugin).validateUserOp(validationId, userOp, userOpHash); if (preUserOpValidationHooks.length != 0) { // If we have other validation data we need to coalesce with @@ -501,15 +501,15 @@ contract UpgradeableModularAccount is currentAuthData = ""; } - (address hookPlugin, uint8 hookFunctionId) = preRuntimeValidationHooks[i].unpack(); + (address hookPlugin, uint8 hookValidationId) = preRuntimeValidationHooks[i].unpack(); try IValidationHook(hookPlugin).preRuntimeValidationHook( - hookFunctionId, msg.sender, msg.value, callData, currentAuthData + hookValidationId, msg.sender, msg.value, callData, currentAuthData ) // forgefmt: disable-start // solhint-disable-next-line no-empty-blocks {} catch (bytes memory revertReason) { // forgefmt: disable-end - revert PreRuntimeValidationHookFailed(hookPlugin, hookFunctionId, revertReason); + revert PreRuntimeValidationHookFailed(hookPlugin, hookValidationId, revertReason); } } @@ -517,14 +517,16 @@ contract UpgradeableModularAccount is revert ValidationSignatureSegmentMissing(); } - (address plugin, uint8 functionId) = runtimeValidationFunction.unpack(); + (address plugin, uint8 validationId) = runtimeValidationFunction.unpack(); - try IValidation(plugin).validateRuntime(functionId, msg.sender, msg.value, callData, authSegment.getBody()) + try IValidation(plugin).validateRuntime( + validationId, msg.sender, msg.value, callData, authSegment.getBody() + ) // forgefmt: disable-start // solhint-disable-next-line no-empty-blocks {} catch (bytes memory revertReason) { // forgefmt: disable-end - revert RuntimeValidationFunctionReverted(plugin, functionId, revertReason); + revert RuntimeValidationFunctionReverted(plugin, validationId, revertReason); } } @@ -569,14 +571,14 @@ contract UpgradeableModularAccount is internal returns (bytes memory preExecHookReturnData) { - (address plugin, uint8 functionId) = preExecHook.unpack(); - try IExecutionHook(plugin).preExecutionHook(functionId, msg.sender, msg.value, data) returns ( + (address plugin, uint8 validationId) = preExecHook.unpack(); + try IExecutionHook(plugin).preExecutionHook(validationId, msg.sender, msg.value, data) returns ( bytes memory returnData ) { preExecHookReturnData = returnData; } catch (bytes memory revertReason) { // TODO: same issue with EP0.6 - we can't do bytes4 error codes in plugins - revert PreExecHookReverted(plugin, functionId, revertReason); + revert PreExecHookReverted(plugin, validationId, revertReason); } } @@ -594,11 +596,11 @@ contract UpgradeableModularAccount is continue; } - (address plugin, uint8 functionId) = postHookToRun.postExecHook.unpack(); + (address plugin, uint8 validationId) = postHookToRun.postExecHook.unpack(); // solhint-disable-next-line no-empty-blocks - try IExecutionHook(plugin).postExecutionHook(functionId, postHookToRun.preExecHookReturnData) {} + try IExecutionHook(plugin).postExecutionHook(validationId, postHookToRun.preExecHookReturnData) {} catch (bytes memory revertReason) { - revert PostExecHookReverted(plugin, functionId, revertReason); + revert PostExecHookReverted(plugin, validationId, revertReason); } } } diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol index 7bddd94b..6adf4a94 100644 --- a/src/helpers/FunctionReferenceLib.sol +++ b/src/helpers/FunctionReferenceLib.sol @@ -9,14 +9,14 @@ library FunctionReferenceLib { // Magic value for hooks that should always revert. FunctionReference internal constant _PRE_HOOK_ALWAYS_DENY = FunctionReference.wrap(bytes21(uint168(2))); - function pack(address addr, uint8 functionId) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes21(bytes20(addr)) | bytes21(uint168(functionId))); + function pack(address addr, uint8 validationId) internal pure returns (FunctionReference) { + return FunctionReference.wrap(bytes21(bytes20(addr)) | bytes21(uint168(validationId))); } - function unpack(FunctionReference fr) internal pure returns (address addr, uint8 functionId) { + function unpack(FunctionReference fr) internal pure returns (address addr, uint8 validationId) { bytes21 underlying = FunctionReference.unwrap(fr); addr = address(bytes20(underlying)); - functionId = uint8(bytes1(underlying << 160)); + validationId = uint8(bytes1(underlying << 160)); } function isEmpty(FunctionReference fr) internal pure returns (bool) { diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index 71639f80..daaa2469 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -28,7 +28,7 @@ library ValidationConfigLib { ); } - function pack(address _plugin, uint8 _functionId, bool _isGlobal, bool _isSignatureValidation) + function pack(address _plugin, uint8 _validationId, bool _isGlobal, bool _isSignatureValidation) internal pure returns (ValidationConfig) @@ -37,8 +37,8 @@ library ValidationConfigLib { bytes23( // plugin address stored in the first 20 bytes bytes23(bytes20(_plugin)) - // functionId stored in the 21st byte - | bytes23(bytes32(uint256(_functionId) << 168)) + // validationId stored in the 21st byte + | bytes23(bytes32(uint256(_validationId) << 168)) // isGlobal flag stored in the 22nd byte | bytes23(bytes32(_isGlobal ? uint256(1) << 80 : 0)) // isSignatureValidation flag stored in the 23rd byte @@ -50,11 +50,11 @@ library ValidationConfigLib { function unpackUnderlying(ValidationConfig config) internal pure - returns (address _plugin, uint8 _functionId, bool _isGlobal, bool _isSignatureValidation) + returns (address _plugin, uint8 _validationId, bool _isGlobal, bool _isSignatureValidation) { bytes23 configBytes = ValidationConfig.unwrap(config); _plugin = address(bytes20(configBytes)); - _functionId = uint8(configBytes[20]); + _validationId = uint8(configBytes[20]); _isGlobal = uint8(configBytes[21]) == 1; _isSignatureValidation = uint8(configBytes[22]) == 1; } @@ -74,7 +74,7 @@ library ValidationConfigLib { return address(bytes20(ValidationConfig.unwrap(config))); } - function functionId(ValidationConfig config) internal pure returns (uint8) { + function validationId(ValidationConfig config) internal pure returns (uint8) { return uint8(ValidationConfig.unwrap(config)[20]); } diff --git a/src/interfaces/IExecutionHook.sol b/src/interfaces/IExecutionHook.sol index 3240c489..eaedf9dd 100644 --- a/src/interfaces/IExecutionHook.sol +++ b/src/interfaces/IExecutionHook.sol @@ -4,22 +4,24 @@ pragma solidity ^0.8.25; import {IPlugin} from "./IPlugin.sol"; interface IExecutionHook is IPlugin { - /// @notice Run the pre execution hook specified by the `functionId`. + /// @notice Run the pre execution hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint8 functionId, address sender, uint256 value, bytes calldata data) + function preExecutionHook(uint8 validationId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); - /// @notice Run the post execution hook specified by the `functionId`. + /// @notice Run the post execution hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint8 functionId, bytes calldata preExecHookData) external; + function postExecutionHook(uint8 validationId, bytes calldata preExecHookData) external; } diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index eb10e96b..f430968d 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -15,7 +15,7 @@ struct ManifestExecutionFunction { // todo: do we need these at all? Or do we fully switch to `installValidation`? struct ManifestValidation { - uint8 functionId; + uint8 validationId; bool isDefault; bool isSignatureValidation; bytes4[] selectors; @@ -24,7 +24,7 @@ struct ManifestValidation { struct ManifestExecutionHook { // TODO(erc6900 spec): These fields can be packed into a single word bytes4 executionSelector; - uint8 functionId; + uint8 validationId; bool isPreHook; bool isPostHook; } diff --git a/src/interfaces/IValidation.sol b/src/interfaces/IValidation.sol index 38c8a139..50ac7bb1 100644 --- a/src/interfaces/IValidation.sol +++ b/src/interfaces/IValidation.sol @@ -6,26 +6,28 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {IPlugin} from "./IPlugin.sol"; interface IValidation is IPlugin { - /// @notice Run the user operation validationFunction specified by the `functionId`. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @notice Run the user operation validationFunction specified by the `validationId`. + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function validateUserOp(uint8 functionId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the runtime validationFunction specified by the `functionId`. + /// @notice Run the runtime validationFunction specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. /// @param authorization Additional data for the validation function to use. function validateRuntime( - uint8 functionId, + uint8 validationId, address sender, uint256 value, bytes calldata data, @@ -34,13 +36,14 @@ interface IValidation is IPlugin { /// @notice Validates a signature using ERC-1271. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param sender the address that sent the ERC-1271 request to the smart account /// @param hash the hash of the ERC-1271 request /// @param signature the signature of the ERC-1271 request /// @return the ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid. - function validateSignature(uint8 functionId, address sender, bytes32 hash, bytes calldata signature) + function validateSignature(uint8 validationId, address sender, bytes32 hash, bytes calldata signature) external view returns (bytes4); diff --git a/src/interfaces/IValidationHook.sol b/src/interfaces/IValidationHook.sol index 8300bbb8..f4f878af 100644 --- a/src/interfaces/IValidationHook.sol +++ b/src/interfaces/IValidationHook.sol @@ -6,26 +6,28 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {IPlugin} from "./IPlugin.sol"; interface IValidationHook is IPlugin { - /// @notice Run the pre user operation validation hook specified by the `functionId`. + /// @notice Run the pre user operation validation hook specified by the `validationId`. /// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the pre runtime validation hook specified by the `functionId`. + /// @notice Run the pre runtime validation hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. function preRuntimeValidationHook( - uint8 functionId, + uint8 validationId, address sender, uint256 value, bytes calldata data, @@ -34,14 +36,15 @@ interface IValidationHook is IPlugin { // TODO: support this hook type within the account & in the manifest - /// @notice Run the pre signature validation hook specified by the `functionId`. + /// @notice Run the pre signature validation hook specified by the `validationId`. /// @dev To indicate the call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there + /// be /// more than one. /// @param sender The caller address. /// @param hash The hash of the message being signed. /// @param signature The signature of the message. - // function preSignatureValidationHook(uint8 functionId, address sender, bytes32 hash, bytes calldata + // function preSignatureValidationHook(uint8 validationId, address sender, bytes32 hash, bytes calldata // signature) // external // view diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol index dbb5d56a..04dceb4e 100644 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ b/src/plugins/owner/SingleOwnerPlugin.sol @@ -82,20 +82,20 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { } /// @inheritdoc IValidation - function validateRuntime(uint8 functionId, address sender, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint8 validationId, address sender, uint256, bytes calldata, bytes calldata) external view override { // Validate that the sender is the owner of the account or self. - if (sender != owners[functionId][msg.sender]) { + if (sender != owners[validationId][msg.sender]) { revert NotAuthorized(); } return; } /// @inheritdoc IValidation - function validateUserOp(uint8 functionId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external view override @@ -104,7 +104,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { // Validate the user op signature against the owner. if ( SignatureChecker.isValidSignatureNow( - owners[functionId][msg.sender], userOpHash.toEthSignedMessageHash(), userOp.signature + owners[validationId][msg.sender], userOpHash.toEthSignedMessageHash(), userOp.signature ) ) { return _SIG_VALIDATION_PASSED; @@ -123,13 +123,13 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { /// validation used in `validateUserOp`, this does///*not** wrap the digest in /// an "Ethereum Signed Message" envelope before checking the signature in /// the EOA-owner case. - function validateSignature(uint8 functionId, address, bytes32 digest, bytes calldata signature) + function validateSignature(uint8 validationId, address, bytes32 digest, bytes calldata signature) external view override returns (bytes4) { - if (SignatureChecker.isValidSignatureNow(owners[functionId][msg.sender], digest, signature)) { + if (SignatureChecker.isValidSignatureNow(owners[validationId][msg.sender], digest, signature)) { return _1271_MAGIC_VALUE; } return _1271_INVALID; diff --git a/src/samples/permissionhooks/AllowlistPlugin.sol b/src/samples/permissionhooks/AllowlistPlugin.sol index 209d8370..80fcaee7 100644 --- a/src/samples/permissionhooks/AllowlistPlugin.sol +++ b/src/samples/permissionhooks/AllowlistPlugin.sol @@ -9,7 +9,7 @@ import {IStandardExecutor, Call} from "../../interfaces/IStandardExecutor.sol"; import {BasePlugin} from "../../plugins/BasePlugin.sol"; contract AllowlistPlugin is IValidationHook, BasePlugin { - enum FunctionId { + enum ValidationId { PRE_VALIDATION_HOOK } @@ -68,25 +68,25 @@ contract AllowlistPlugin is IValidationHook, BasePlugin { selectorAllowlist[target][selector][msg.sender] = allowed; } - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(userOp.callData); return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata data, bytes calldata) + function preRuntimeValidationHook(uint8 validationId, address, uint256, bytes calldata data, bytes calldata) external view override { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(data); return; } diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index 06b51e4d..1adfb375 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -267,56 +267,56 @@ interface IPlugin { /// @param data Optional bytes array to be decoded and used by the plugin to clear plugin data for the modular account. function onUninstall(bytes calldata data) external; - /// @notice Run the pre user operation validation hook specified by the `functionId`. + /// @notice Run the pre user operation validation hook specified by the `validationId`. /// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1. - /// @param functionId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function preUserOpValidationHook(uint8 functionId, PackedUserOperation memory userOp, bytes32 userOpHash) external returns (uint256); + function preUserOpValidationHook(uint8 validationId, PackedUserOperation memory userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the user operation validationFunction specified by the `functionId`. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @notice Run the user operation validationFunction specified by the `validationId`. + /// @param validationId An identifier that routes the call to different internal implementations, should there be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function userOpValidationFunction(uint8 functionId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function userOpValidationFunction(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the pre runtime validation hook specified by the `functionId`. + /// @notice Run the pre runtime validation hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. - function preRuntimeValidationHook(uint8 functionId, address sender, uint256 value, bytes calldata data) external; + function preRuntimeValidationHook(uint8 validationId, address sender, uint256 value, bytes calldata data) external; - /// @notice Run the runtime validationFunction specified by the `functionId`. + /// @notice Run the runtime validationFunction specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be + /// @param validationId An identifier that routes the call to different internal implementations, should there be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. - function runtimeValidationFunction(uint8 functionId, address sender, uint256 value, bytes calldata data) + function runtimeValidationFunction(uint8 validationId, address sender, uint256 value, bytes calldata data) external; - /// @notice Run the pre execution hook specified by the `functionId`. + /// @notice Run the pre execution hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint8 functionId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); + function preExecutionHook(uint8 validationId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); - /// @notice Run the post execution hook specified by the `functionId`. + /// @notice Run the post execution hook specified by the `validationId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param functionId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint8 functionId, bytes calldata preExecHookData) external; + function postExecutionHook(uint8 validationId, bytes calldata preExecHookData) external; /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. @@ -359,7 +359,7 @@ enum ManifestAssociatedFunctionType { /// of the function at `dependencies[dependencyIndex]` during the call to `installPlugin(config)`. struct ManifestFunction { ManifestAssociatedFunctionType functionType; - uint8 functionId; + uint8 validationId; uint256 dependencyIndex; } @@ -371,7 +371,7 @@ struct ManifestAssociatedFunction { struct ManifestExecutionHook { // TODO(erc6900 spec): These fields can be packed into a single word bytes4 executionSelector; - uint8 functionId; + uint8 validationId; bool isPreHook; bool isPostHook; } diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 14ad57fc..7eab0d19 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -45,7 +45,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - functionId: _PRE_HOOK_FUNCTION_ID_1, + validationId: _PRE_HOOK_FUNCTION_ID_1, isPreHook: true, isPostHook: false }) @@ -83,7 +83,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - functionId: _BOTH_HOOKS_FUNCTION_ID_3, + validationId: _BOTH_HOOKS_FUNCTION_ID_3, isPreHook: true, isPostHook: true }) @@ -131,7 +131,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - functionId: _POST_HOOK_FUNCTION_ID_2, + validationId: _POST_HOOK_FUNCTION_ID_2, isPreHook: false, isPostHook: true }) diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 69563b51..2d006b2d 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -70,7 +70,7 @@ contract AccountLoupeTest is CustomValidationTestBase { function test_pluginLoupe_getSelectors() public { FunctionReference comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.VALIDATION) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.VALIDATION) ); bytes4[] memory selectors = account1.getSelectors(comprehensivePluginValidation); @@ -84,21 +84,21 @@ contract AccountLoupeTest is CustomValidationTestBase { ExecutionHook[3] memory expectedHooks = [ ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.BOTH_EXECUTION_HOOKS) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.BOTH_EXECUTION_HOOKS) ), isPreHook: true, isPostHook: true }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.PRE_EXECUTION_HOOK) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_EXECUTION_HOOK) ), isPreHook: true, isPostHook: false }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.POST_EXECUTION_HOOK) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.POST_EXECUTION_HOOK) ), isPreHook: false, isPostHook: true @@ -124,7 +124,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[0]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) ) ) ); @@ -132,7 +132,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[1]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) ) ) ); @@ -148,10 +148,10 @@ contract AccountLoupeTest is CustomValidationTestBase { { FunctionReference[] memory preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) ); preValidationHooks[1] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) ); bytes[] memory installDatas = new bytes[](2); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index 9c90cf5c..bf7ef948 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -213,7 +213,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.FunctionId.PRE_VALIDATION_HOOK), + uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -232,7 +232,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.FunctionId.PRE_VALIDATION_HOOK), + uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -274,7 +274,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.FunctionId.PRE_VALIDATION_HOOK), + uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Target not allowed") ) ); @@ -328,7 +328,7 @@ contract PerHookDataTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(_accessControlHookPlugin), uint8(MockAccessControlHookPlugin.FunctionId.PRE_VALIDATION_HOOK) + address(_accessControlHookPlugin), uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index 5bcf64b3..ea0911b0 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -28,7 +28,7 @@ contract SelfCallAuthorizationTest is AccountTestBase { account1.installPlugin(address(comprehensivePlugin), manifestHash, ""); comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.FunctionId.VALIDATION) + address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.VALIDATION) ); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 7f245031..68cca892 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -33,17 +33,17 @@ contract ValidationIntersectionTest is AccountTestBase { noHookValidation = FunctionReferenceLib.pack({ addr: address(noHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.USER_OP_VALIDATION) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); oneHookValidation = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.USER_OP_VALIDATION) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); twoHookValidation = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.USER_OP_VALIDATION) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); vm.startPrank(address(entryPoint)); @@ -62,7 +62,7 @@ contract ValidationIntersectionTest is AccountTestBase { FunctionReference[] memory preValidationHooks = new FunctionReference[](1); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.PRE_VALIDATION_HOOK_1) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) }); bytes[] memory installDatas = new bytes[](1); account1.installValidation( @@ -81,11 +81,11 @@ contract ValidationIntersectionTest is AccountTestBase { preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.PRE_VALIDATION_HOOK_1) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) }); preValidationHooks[1] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - functionId: uint8(MockBaseUserOpValidationPlugin.FunctionId.PRE_VALIDATION_HOOK_2) + validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_2) }); installDatas = new bytes[](2); account1.installValidation( @@ -211,7 +211,7 @@ contract ValidationIntersectionTest is AccountTestBase { abi.encodeWithSelector( UpgradeableModularAccount.UnexpectedAggregator.selector, address(oneHookPlugin), - MockBaseUserOpValidationPlugin.FunctionId.PRE_VALIDATION_HOOK_1, + MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1, badAuthorizer ) ); diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 6471fbd0..3382c43e 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -9,16 +9,16 @@ import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; contract FunctionReferenceLibTest is Test { using FunctionReferenceLib for FunctionReference; - function testFuzz_functionReference_packing(address addr, uint8 functionId) public { + function testFuzz_functionReference_packing(address addr, uint8 validationId) public { // console.log("addr: ", addr); - // console.log("functionId: ", vm.toString(functionId)); - FunctionReference fr = FunctionReferenceLib.pack(addr, functionId); + // console.log("validationId: ", vm.toString(validationId)); + FunctionReference fr = FunctionReferenceLib.pack(addr, validationId); // console.log("packed: ", vm.toString(FunctionReference.unwrap(fr))); - (address addr2, uint8 functionId2) = FunctionReferenceLib.unpack(fr); + (address addr2, uint8 validationId2) = FunctionReferenceLib.unpack(fr); // console.log("addr2: ", addr2); - // console.log("functionId2: ", vm.toString(functionId2)); + // console.log("validationId2: ", vm.toString(validationId2)); assertEq(addr, addr2); - assertEq(functionId, functionId2); + assertEq(validationId, validationId2); } function testFuzz_functionReference_operators(FunctionReference a, FunctionReference b) public { diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index cd455c88..fc6c3191 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -18,7 +18,7 @@ import {IExecutionHook} from "../../../src/interfaces/IExecutionHook.sol"; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, BasePlugin { - enum FunctionId { + enum ValidationId { PRE_VALIDATION_HOOK_1, PRE_VALIDATION_HOOK_2, VALIDATION, @@ -46,85 +46,85 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { return 0; - } else if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { return 0; } revert NotImplemented(); } - function validateUserOp(uint8 functionId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint8 validationId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (functionId == uint8(FunctionId.VALIDATION)) { + if (validationId == uint8(ValidationId.VALIDATION)) { return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint8 functionId, address, uint256, bytes calldata, bytes calldata) + function preRuntimeValidationHook(uint8 validationId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { return; - } else if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { return; } revert NotImplemented(); } - function validateRuntime(uint8 functionId, address, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint8 validationId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (functionId == uint8(FunctionId.VALIDATION)) { + if (validationId == uint8(ValidationId.VALIDATION)) { return; } revert NotImplemented(); } - function validateSignature(uint8 functionId, address, bytes32, bytes calldata) + function validateSignature(uint8 validationId, address, bytes32, bytes calldata) external pure returns (bytes4) { - if (functionId == uint8(FunctionId.SIG_VALIDATION)) { + if (validationId == uint8(ValidationId.SIG_VALIDATION)) { return 0xffffffff; } revert NotImplemented(); } - function preExecutionHook(uint8 functionId, address, uint256, bytes calldata) + function preExecutionHook(uint8 validationId, address, uint256, bytes calldata) external pure override returns (bytes memory) { - if (functionId == uint8(FunctionId.PRE_EXECUTION_HOOK)) { + if (validationId == uint8(ValidationId.PRE_EXECUTION_HOOK)) { return ""; - } else if (functionId == uint8(FunctionId.BOTH_EXECUTION_HOOKS)) { + } else if (validationId == uint8(ValidationId.BOTH_EXECUTION_HOOKS)) { return ""; } revert NotImplemented(); } - function postExecutionHook(uint8 functionId, bytes calldata) external pure override { - if (functionId == uint8(FunctionId.POST_EXECUTION_HOOK)) { + function postExecutionHook(uint8 validationId, bytes calldata) external pure override { + if (validationId == uint8(ValidationId.POST_EXECUTION_HOOK)) { return; - } else if (functionId == uint8(FunctionId.BOTH_EXECUTION_HOOKS)) { + } else if (validationId == uint8(ValidationId.BOTH_EXECUTION_HOOKS)) { return; } revert NotImplemented(); @@ -145,7 +145,7 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - functionId: uint8(FunctionId.VALIDATION), + validationId: uint8(ValidationId.VALIDATION), isDefault: true, isSignatureValidation: false, selectors: validationSelectors @@ -154,19 +154,19 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.executionHooks = new ManifestExecutionHook[](3); manifest.executionHooks[0] = ManifestExecutionHook({ executionSelector: this.foo.selector, - functionId: uint8(FunctionId.BOTH_EXECUTION_HOOKS), + validationId: uint8(ValidationId.BOTH_EXECUTION_HOOKS), isPreHook: true, isPostHook: true }); manifest.executionHooks[1] = ManifestExecutionHook({ executionSelector: this.foo.selector, - functionId: uint8(FunctionId.PRE_EXECUTION_HOOK), + validationId: uint8(ValidationId.PRE_EXECUTION_HOOK), isPreHook: true, isPostHook: false }); manifest.executionHooks[2] = ManifestExecutionHook({ executionSelector: this.foo.selector, - functionId: uint8(FunctionId.POST_EXECUTION_HOOK), + validationId: uint8(ValidationId.POST_EXECUTION_HOOK), isPreHook: false, isPostHook: true }); diff --git a/test/mocks/plugins/MockAccessControlHookPlugin.sol b/test/mocks/plugins/MockAccessControlHookPlugin.sol index c17868a8..9555270d 100644 --- a/test/mocks/plugins/MockAccessControlHookPlugin.sol +++ b/test/mocks/plugins/MockAccessControlHookPlugin.sol @@ -13,7 +13,7 @@ import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; // This is just a mock - it does not enforce this over `executeBatch` and other methods of making calls, and should // not be used in production.. contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { - enum FunctionId { + enum ValidationId { PRE_VALIDATION_HOOK } @@ -28,13 +28,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { delete allowedTargets[msg.sender]; } - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { if (bytes4(userOp.callData[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(userOp.callData[4:36], (address)); @@ -49,13 +49,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { } function preRuntimeValidationHook( - uint8 functionId, + uint8 validationId, address, uint256, bytes calldata data, bytes calldata authorization ) external view override { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { if (bytes4(data[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(data[4:36], (address)); diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 211f79af..78f9dddd 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -121,7 +121,7 @@ contract ResultConsumerPlugin is BasePlugin, IValidation { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - functionId: 0, + validationId: 0, isDefault: true, isSignatureValidation: false, selectors: validationSelectors diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index a59d5ee3..bb0dcc07 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -14,7 +14,7 @@ import {IValidationHook} from "../../../src/interfaces/IValidationHook.sol"; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook, BasePlugin { - enum FunctionId { + enum ValidationId { USER_OP_VALIDATION, PRE_VALIDATION_HOOK_1, PRE_VALIDATION_HOOK_2 @@ -32,27 +32,27 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { return _preUserOpValidationHook1Data; - } else if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { return _preUserOpValidationHook2Data; } revert NotImplemented(); } - function validateUserOp(uint8 functionId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint8 validationId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (functionId == uint8(FunctionId.USER_OP_VALIDATION)) { + if (validationId == uint8(ValidationId.USER_OP_VALIDATION)) { return _userOpValidationFunctionData; } revert NotImplemented(); @@ -108,7 +108,7 @@ contract MockUserOpValidationPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - functionId: uint8(FunctionId.USER_OP_VALIDATION), + validationId: uint8(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -151,7 +151,7 @@ contract MockUserOpValidation1HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](2); manifest.validationFunctions[0] = ManifestValidation({ - functionId: uint8(FunctionId.USER_OP_VALIDATION), + validationId: uint8(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -197,7 +197,7 @@ contract MockUserOpValidation2HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - functionId: uint8(FunctionId.USER_OP_VALIDATION), + validationId: uint8(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index d81d5f79..f3e3d327 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -184,7 +184,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK), + uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector) ); } @@ -192,7 +192,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK), + uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector) ); } @@ -293,7 +293,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(allowlistPlugin), uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK) + address(allowlistPlugin), uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); From b348a2f9999a081503f1b7cb1c030d370476423e Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Thu, 11 Jul 2024 14:41:46 -0700 Subject: [PATCH 02/11] convert validationId type from uint8 to uint32 --- src/account/AccountStorage.sol | 14 ++--- src/account/UpgradeableModularAccount.sol | 46 +++++++-------- src/helpers/FunctionReferenceLib.sol | 18 +++--- src/helpers/ValidationConfigLib.sol | 58 +++++++++---------- src/interfaces/IExecutionHook.sol | 4 +- src/interfaces/IPlugin.sol | 4 +- src/interfaces/IPluginManager.sol | 4 +- src/interfaces/IValidation.sol | 6 +- src/interfaces/IValidationHook.sol | 6 +- src/plugins/owner/SingleOwnerPlugin.sol | 16 ++--- .../permissionhooks/AllowlistPlugin.sol | 8 +-- test/account/AccountExecHooks.t.sol | 6 +- test/account/AccountLoupe.t.sol | 16 ++--- test/account/PerHookData.t.sol | 8 +-- test/account/SelfCallAuthorization.t.sol | 2 +- test/account/ValidationIntersection.t.sol | 12 ++-- test/libraries/FunctionReferenceLib.t.sol | 4 +- test/mocks/plugins/ComprehensivePlugin.sol | 44 +++++++------- .../plugins/MockAccessControlHookPlugin.sol | 8 +-- test/mocks/plugins/ReturnDataPluginMocks.sol | 8 +-- test/mocks/plugins/ValidationPluginMocks.sol | 22 +++---- test/samples/AllowlistPlugin.t.sol | 6 +- test/utils/AccountTestBase.sol | 2 +- test/utils/TestConstants.sol | 2 +- 24 files changed, 162 insertions(+), 162 deletions(-) diff --git a/src/account/AccountStorage.sol b/src/account/AccountStorage.sol index 242ddff8..35fbe13d 100644 --- a/src/account/AccountStorage.sol +++ b/src/account/AccountStorage.sol @@ -64,7 +64,7 @@ function toSetValue(FunctionReference functionReference) pure returns (bytes32) } function toFunctionReference(bytes32 setValue) pure returns (FunctionReference) { - return FunctionReference.wrap(bytes21(setValue)); + return FunctionReference.wrap(bytes24(setValue)); } // ExecutionHook layout: @@ -74,17 +74,17 @@ function toFunctionReference(bytes32 setValue) pure returns (FunctionReference) function toSetValue(ExecutionHook memory executionHook) pure returns (bytes32) { return bytes32(FunctionReference.unwrap(executionHook.hookFunction)) - | bytes32(executionHook.isPreHook ? uint256(1) << 80 : 0) - | bytes32(executionHook.isPostHook ? uint256(1) << 72 : 0); + | bytes32(executionHook.isPreHook ? uint256(1) << 56 : 0) + | bytes32(executionHook.isPostHook ? uint256(1) << 48 : 0); } function toExecutionHook(bytes32 setValue) pure returns (FunctionReference hookFunction, bool isPreHook, bool isPostHook) { - hookFunction = FunctionReference.wrap(bytes21(setValue)); - isPreHook = (uint256(setValue) >> 80) & 0xFF == 1; - isPostHook = (uint256(setValue) >> 72) & 0xFF == 1; + hookFunction = FunctionReference.wrap(bytes24(setValue)); + isPreHook = (uint256(setValue) >> 56) & 0xFF == 1; + isPostHook = (uint256(setValue) >> 48) & 0xFF == 1; } function toSetValue(bytes4 selector) pure returns (bytes32) { @@ -104,7 +104,7 @@ function toFunctionReferenceArray(EnumerableSet.Bytes32Set storage set) FunctionReference[] memory result = new FunctionReference[](length); for (uint256 i = 0; i < length; ++i) { bytes32 key = set.at(i); - result[i] = FunctionReference.wrap(bytes21(key)); + result[i] = FunctionReference.wrap(bytes24(key)); } return result; } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 0c1bbecd..17c930ad 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -68,18 +68,18 @@ contract UpgradeableModularAccount is error NativeTokenSpendingNotPermitted(address plugin); error NonCanonicalEncoding(); error NotEntryPoint(); - error PostExecHookReverted(address plugin, uint8 validationId, bytes revertReason); - error PreExecHookReverted(address plugin, uint8 validationId, bytes revertReason); - error PreRuntimeValidationHookFailed(address plugin, uint8 validationId, bytes revertReason); + error PostExecHookReverted(address plugin, uint32 validationId, bytes revertReason); + error PreExecHookReverted(address plugin, uint32 validationId, bytes revertReason); + error PreRuntimeValidationHookFailed(address plugin, uint32 validationId, bytes revertReason); error RequireUserOperationContext(); error RuntimeValidationFunctionMissing(bytes4 selector); - error RuntimeValidationFunctionReverted(address plugin, uint8 validationId, bytes revertReason); + error RuntimeValidationFunctionReverted(address plugin, uint32 validationId, bytes revertReason); error SelfCallRecursionDepthExceeded(); - error SignatureValidationInvalid(address plugin, uint8 validationId); - error UnexpectedAggregator(address plugin, uint8 validationId, address aggregator); + error SignatureValidationInvalid(address plugin, uint32 validationId); + error UnexpectedAggregator(address plugin, uint32 validationId, address aggregator); error UnrecognizedFunction(bytes4 selector); error UserOpValidationFunctionMissing(bytes4 selector); - error ValidationDoesNotApply(bytes4 selector, address plugin, uint8 validationId, bool isGlobal); + error ValidationDoesNotApply(bytes4 selector, address plugin, uint32 validationId, bool isGlobal); error ValidationSignatureSegmentMissing(); error SignatureSegmentOutOfOrder(); @@ -164,7 +164,7 @@ contract UpgradeableModularAccount is revert NotEntryPoint(); } - FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes21(userOp.signature[:21])); + FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes24(userOp.signature[:24])); PostExecToRun[] memory postPermissionHooks = _doPreHooks(getAccountStorage().validationData[userOpValidationFunction].permissionHooks, msg.data); @@ -217,13 +217,13 @@ contract UpgradeableModularAccount is returns (bytes memory) { // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - FunctionReference runtimeValidationFunction = FunctionReference.wrap(bytes21(authorization[:21])); + FunctionReference runtimeValidationFunction = FunctionReference.wrap(bytes24(authorization[:24])); // Check if the runtime validation function is allowed to be called - bool isGlobalValidation = uint8(authorization[21]) == 1; + bool isGlobalValidation = uint8(authorization[24]) == 1; _checkIfValidationAppliesCallData(data, runtimeValidationFunction, isGlobalValidation); - _doRuntimeValidation(runtimeValidationFunction, data, authorization[22:]); + _doRuntimeValidation(runtimeValidationFunction, data, authorization[25:]); // If runtime validation passes, do runtime permission checks PostExecToRun[] memory postPermissionHooks = @@ -341,15 +341,15 @@ contract UpgradeableModularAccount is function isValidSignature(bytes32 hash, bytes calldata signature) public view override returns (bytes4) { AccountStorage storage _storage = getAccountStorage(); - FunctionReference sigValidation = FunctionReference.wrap(bytes21(signature)); + FunctionReference sigValidation = FunctionReference.wrap(bytes24(signature)); - (address plugin, uint8 validationId) = sigValidation.unpack(); + (address plugin, uint32 validationId) = sigValidation.unpack(); if (!_storage.validationData[sigValidation].isSignatureValidation) { revert SignatureValidationInvalid(plugin, validationId); } if ( - IValidation(plugin).validateSignature(validationId, msg.sender, hash, signature[21:]) + IValidation(plugin).validateSignature(validationId, msg.sender, hash, signature[24:]) == _1271_MAGIC_VALUE ) { return _1271_MAGIC_VALUE; @@ -377,8 +377,8 @@ contract UpgradeableModularAccount is } // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes21(userOp.signature[:21])); - bool isGlobalValidation = uint8(userOp.signature[21]) == 1; + FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes24(userOp.signature[:24])); + bool isGlobalValidation = uint8(userOp.signature[24]) == 1; _checkIfValidationAppliesCallData(userOp.callData, userOpValidationFunction, isGlobalValidation); @@ -393,7 +393,7 @@ contract UpgradeableModularAccount is revert RequireUserOperationContext(); } - validationData = _doUserOpValidation(userOpValidationFunction, userOp, userOp.signature[22:], userOpHash); + validationData = _doUserOpValidation(userOpValidationFunction, userOp, userOp.signature[25:], userOpHash); } // To support gas estimation, we don't fail early when the failure is caused by a signature failure @@ -434,7 +434,7 @@ contract UpgradeableModularAccount is userOp.signature = ""; } - (address plugin, uint8 validationId) = preUserOpValidationHooks[i].unpack(); + (address plugin, uint32 validationId) = preUserOpValidationHooks[i].unpack(); uint256 currentValidationData = IValidationHook(plugin).preUserOpValidationHook(validationId, userOp, userOpHash); @@ -453,7 +453,7 @@ contract UpgradeableModularAccount is userOp.signature = signatureSegment.getBody(); - (address plugin, uint8 validationId) = userOpValidationFunction.unpack(); + (address plugin, uint32 validationId) = userOpValidationFunction.unpack(); uint256 currentValidationData = IValidation(plugin).validateUserOp(validationId, userOp, userOpHash); if (preUserOpValidationHooks.length != 0) { @@ -501,7 +501,7 @@ contract UpgradeableModularAccount is currentAuthData = ""; } - (address hookPlugin, uint8 hookValidationId) = preRuntimeValidationHooks[i].unpack(); + (address hookPlugin, uint32 hookValidationId) = preRuntimeValidationHooks[i].unpack(); try IValidationHook(hookPlugin).preRuntimeValidationHook( hookValidationId, msg.sender, msg.value, callData, currentAuthData ) @@ -517,7 +517,7 @@ contract UpgradeableModularAccount is revert ValidationSignatureSegmentMissing(); } - (address plugin, uint8 validationId) = runtimeValidationFunction.unpack(); + (address plugin, uint32 validationId) = runtimeValidationFunction.unpack(); try IValidation(plugin).validateRuntime( validationId, msg.sender, msg.value, callData, authSegment.getBody() @@ -571,7 +571,7 @@ contract UpgradeableModularAccount is internal returns (bytes memory preExecHookReturnData) { - (address plugin, uint8 validationId) = preExecHook.unpack(); + (address plugin, uint32 validationId) = preExecHook.unpack(); try IExecutionHook(plugin).preExecutionHook(validationId, msg.sender, msg.value, data) returns ( bytes memory returnData ) { @@ -596,7 +596,7 @@ contract UpgradeableModularAccount is continue; } - (address plugin, uint8 validationId) = postHookToRun.postExecHook.unpack(); + (address plugin, uint32 validationId) = postHookToRun.postExecHook.unpack(); // solhint-disable-next-line no-empty-blocks try IExecutionHook(plugin).postExecutionHook(validationId, postHookToRun.preExecHookReturnData) {} catch (bytes memory revertReason) { diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol index 6adf4a94..e39b9360 100644 --- a/src/helpers/FunctionReferenceLib.sol +++ b/src/helpers/FunctionReferenceLib.sol @@ -5,26 +5,26 @@ import {FunctionReference} from "../interfaces/IPluginManager.sol"; library FunctionReferenceLib { // Empty or unset function reference. - FunctionReference internal constant _EMPTY_FUNCTION_REFERENCE = FunctionReference.wrap(bytes21(0)); + FunctionReference internal constant _EMPTY_FUNCTION_REFERENCE = FunctionReference.wrap(bytes24(0)); // Magic value for hooks that should always revert. - FunctionReference internal constant _PRE_HOOK_ALWAYS_DENY = FunctionReference.wrap(bytes21(uint168(2))); + FunctionReference internal constant _PRE_HOOK_ALWAYS_DENY = FunctionReference.wrap(bytes24(uint192(2))); - function pack(address addr, uint8 validationId) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes21(bytes20(addr)) | bytes21(uint168(validationId))); + function pack(address addr, uint32 validationId) internal pure returns (FunctionReference) { + return FunctionReference.wrap(bytes24(bytes20(addr)) | bytes24(uint192(validationId))); } - function unpack(FunctionReference fr) internal pure returns (address addr, uint8 validationId) { - bytes21 underlying = FunctionReference.unwrap(fr); + function unpack(FunctionReference fr) internal pure returns (address addr, uint32 validationId) { + bytes24 underlying = FunctionReference.unwrap(fr); addr = address(bytes20(underlying)); - validationId = uint8(bytes1(underlying << 160)); + validationId = uint32(bytes4(underlying << 160)); } function isEmpty(FunctionReference fr) internal pure returns (bool) { - return FunctionReference.unwrap(fr) == bytes21(0); + return FunctionReference.unwrap(fr) == bytes24(0); } function notEmpty(FunctionReference fr) internal pure returns (bool) { - return FunctionReference.unwrap(fr) != bytes21(0); + return FunctionReference.unwrap(fr) != bytes24(0); } function eq(FunctionReference a, FunctionReference b) internal pure returns (bool) { diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index daaa2469..51bbf63c 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -18,31 +18,31 @@ library ValidationConfigLib { returns (ValidationConfig) { return ValidationConfig.wrap( - bytes23( - bytes23(FunctionReference.unwrap(_validationFunction)) - // isGlobal flag stored in the 22nd byte - | bytes23(bytes32(_isGlobal ? uint256(1) << 80 : 0)) - // isSignatureValidation flag stored in the 23rd byte - | bytes23(bytes32(_isSignatureValidation ? uint256(1) << 72 : 0)) + bytes26( + bytes26(FunctionReference.unwrap(_validationFunction)) + // isGlobal flag stored in the 25th byte + | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) + // isSignatureValidation flag stored in the 26th byte + | bytes26(bytes32(_isSignatureValidation ? uint256(1) << 48 : 0)) ) ); } - function pack(address _plugin, uint8 _validationId, bool _isGlobal, bool _isSignatureValidation) + function pack(address _plugin, uint32 _validationId, bool _isGlobal, bool _isSignatureValidation) internal pure returns (ValidationConfig) { return ValidationConfig.wrap( - bytes23( + bytes26( // plugin address stored in the first 20 bytes - bytes23(bytes20(_plugin)) - // validationId stored in the 21st byte - | bytes23(bytes32(uint256(_validationId) << 168)) - // isGlobal flag stored in the 22nd byte - | bytes23(bytes32(_isGlobal ? uint256(1) << 80 : 0)) - // isSignatureValidation flag stored in the 23rd byte - | bytes23(bytes32(_isSignatureValidation ? uint256(1) << 72 : 0)) + bytes26(bytes20(_plugin)) + // validationId stored in the 21st - 24th byte + | bytes26(bytes32(uint256(_validationId) << 168)) + // isGlobal flag stored in the 25th byte + | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) + // isSignatureValidation flag stored in the 26th byte + | bytes26(bytes32(_isSignatureValidation ? uint256(1) << 48 : 0)) ) ); } @@ -50,13 +50,13 @@ library ValidationConfigLib { function unpackUnderlying(ValidationConfig config) internal pure - returns (address _plugin, uint8 _validationId, bool _isGlobal, bool _isSignatureValidation) + returns (address _plugin, uint32 _validationId, bool _isGlobal, bool _isSignatureValidation) { - bytes23 configBytes = ValidationConfig.unwrap(config); + bytes26 configBytes = ValidationConfig.unwrap(config); _plugin = address(bytes20(configBytes)); - _validationId = uint8(configBytes[20]); - _isGlobal = uint8(configBytes[21]) == 1; - _isSignatureValidation = uint8(configBytes[22]) == 1; + _validationId = uint32(bytes4(configBytes << 160)); + _isGlobal = uint8(configBytes[24]) == 1; + _isSignatureValidation = uint8(configBytes[25]) == 1; } function unpack(ValidationConfig config) @@ -64,29 +64,29 @@ library ValidationConfigLib { pure returns (FunctionReference _validationFunction, bool _isGlobal, bool _isSignatureValidation) { - bytes23 configBytes = ValidationConfig.unwrap(config); - _validationFunction = FunctionReference.wrap(bytes21(configBytes)); - _isGlobal = uint8(configBytes[21]) == 1; - _isSignatureValidation = uint8(configBytes[22]) == 1; + bytes26 configBytes = ValidationConfig.unwrap(config); + _validationFunction = FunctionReference.wrap(bytes24(configBytes)); + _isGlobal = uint8(configBytes[24]) == 1; + _isSignatureValidation = uint8(configBytes[25]) == 1; } function plugin(ValidationConfig config) internal pure returns (address) { return address(bytes20(ValidationConfig.unwrap(config))); } - function validationId(ValidationConfig config) internal pure returns (uint8) { - return uint8(ValidationConfig.unwrap(config)[20]); + function validationId(ValidationConfig config) internal pure returns (uint32) { + return uint32(bytes4(ValidationConfig.unwrap(config) << 160)); } function functionReference(ValidationConfig config) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes21(ValidationConfig.unwrap(config))); + return FunctionReference.wrap(bytes24(ValidationConfig.unwrap(config))); } function isGlobal(ValidationConfig config) internal pure returns (bool) { - return uint8(ValidationConfig.unwrap(config)[21]) == 1; + return uint8(ValidationConfig.unwrap(config)[24]) == 1; } function isSignatureValidation(ValidationConfig config) internal pure returns (bool) { - return uint8(ValidationConfig.unwrap(config)[22]) == 1; + return uint8(ValidationConfig.unwrap(config)[25]) == 1; } } diff --git a/src/interfaces/IExecutionHook.sol b/src/interfaces/IExecutionHook.sol index eaedf9dd..3afff456 100644 --- a/src/interfaces/IExecutionHook.sol +++ b/src/interfaces/IExecutionHook.sol @@ -13,7 +13,7 @@ interface IExecutionHook is IPlugin { /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint8 validationId, address sender, uint256 value, bytes calldata data) + function preExecutionHook(uint32 validationId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); @@ -23,5 +23,5 @@ interface IExecutionHook is IPlugin { /// be /// more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint8 validationId, bytes calldata preExecHookData) external; + function postExecutionHook(uint32 validationId, bytes calldata preExecHookData) external; } diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index f430968d..1b576bd6 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -15,7 +15,7 @@ struct ManifestExecutionFunction { // todo: do we need these at all? Or do we fully switch to `installValidation`? struct ManifestValidation { - uint8 validationId; + uint32 validationId; bool isDefault; bool isSignatureValidation; bytes4[] selectors; @@ -24,7 +24,7 @@ struct ManifestValidation { struct ManifestExecutionHook { // TODO(erc6900 spec): These fields can be packed into a single word bytes4 executionSelector; - uint8 validationId; + uint32 validationId; bool isPreHook; bool isPostHook; } diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index bf1296e1..78508645 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.25; -type FunctionReference is bytes21; +type FunctionReference is bytes24; -type ValidationConfig is bytes23; +type ValidationConfig is bytes26; interface IPluginManager { event PluginInstalled(address indexed plugin, bytes32 manifestHash); diff --git a/src/interfaces/IValidation.sol b/src/interfaces/IValidation.sol index 50ac7bb1..2f3e1292 100644 --- a/src/interfaces/IValidation.sol +++ b/src/interfaces/IValidation.sol @@ -13,7 +13,7 @@ interface IValidation is IPlugin { /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function validateUserOp(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); @@ -27,7 +27,7 @@ interface IValidation is IPlugin { /// @param data The calldata sent. /// @param authorization Additional data for the validation function to use. function validateRuntime( - uint8 validationId, + uint32 validationId, address sender, uint256 value, bytes calldata data, @@ -43,7 +43,7 @@ interface IValidation is IPlugin { /// @param hash the hash of the ERC-1271 request /// @param signature the signature of the ERC-1271 request /// @return the ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid. - function validateSignature(uint8 validationId, address sender, bytes32 hash, bytes calldata signature) + function validateSignature(uint32 validationId, address sender, bytes32 hash, bytes calldata signature) external view returns (bytes4); diff --git a/src/interfaces/IValidationHook.sol b/src/interfaces/IValidationHook.sol index f4f878af..9febf60b 100644 --- a/src/interfaces/IValidationHook.sol +++ b/src/interfaces/IValidationHook.sol @@ -14,7 +14,7 @@ interface IValidationHook is IPlugin { /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); @@ -27,7 +27,7 @@ interface IValidationHook is IPlugin { /// @param value The call value. /// @param data The calldata sent. function preRuntimeValidationHook( - uint8 validationId, + uint32 validationId, address sender, uint256 value, bytes calldata data, @@ -44,7 +44,7 @@ interface IValidationHook is IPlugin { /// @param sender The caller address. /// @param hash The hash of the message being signed. /// @param signature The signature of the message. - // function preSignatureValidationHook(uint8 validationId, address sender, bytes32 hash, bytes calldata + // function preSignatureValidationHook(uint32 validationId, address sender, bytes32 hash, bytes calldata // signature) // external // view diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol index 04dceb4e..c8aa6495 100644 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ b/src/plugins/owner/SingleOwnerPlugin.sol @@ -42,7 +42,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; bytes4 internal constant _1271_INVALID = 0xffffffff; - mapping(uint8 id => mapping(address account => address)) public owners; + mapping(uint32 id => mapping(address account => address)) public owners; /// @notice This event is emitted when ownership of the account changes. /// @param account The account whose ownership changed. @@ -61,7 +61,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { /// @notice Transfer ownership of an account and ID to `newOwner`. The caller address (`msg.sender`) is user to /// identify the account. /// @param newOwner The address of the new owner. - function transferOwnership(uint8 id, address newOwner) external { + function transferOwnership(uint32 id, address newOwner) external { _transferOwnership(id, newOwner); } @@ -71,18 +71,18 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { /// @inheritdoc IPlugin function onInstall(bytes calldata data) external override { - (uint8 id, address owner) = abi.decode(data, (uint8, address)); + (uint32 id, address owner) = abi.decode(data, (uint32, address)); _transferOwnership(id, owner); } /// @inheritdoc IPlugin function onUninstall(bytes calldata data) external override { - uint8 id = abi.decode(data, (uint8)); + uint32 id = abi.decode(data, (uint32)); _transferOwnership(id, address(0)); } /// @inheritdoc IValidation - function validateRuntime(uint8 validationId, address sender, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint32 validationId, address sender, uint256, bytes calldata, bytes calldata) external view override @@ -95,7 +95,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { } /// @inheritdoc IValidation - function validateUserOp(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) external view override @@ -123,7 +123,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { /// validation used in `validateUserOp`, this does///*not** wrap the digest in /// an "Ethereum Signed Message" envelope before checking the signature in /// the EOA-owner case. - function validateSignature(uint8 validationId, address, bytes32 digest, bytes calldata signature) + function validateSignature(uint32 validationId, address, bytes32 digest, bytes calldata signature) external view override @@ -179,7 +179,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ // Transfers ownership and emits and event. - function _transferOwnership(uint8 id, address newOwner) internal { + function _transferOwnership(uint32 id, address newOwner) internal { address previousOwner = owners[id][msg.sender]; owners[id][msg.sender] = newOwner; // Todo: include id in event diff --git a/src/samples/permissionhooks/AllowlistPlugin.sol b/src/samples/permissionhooks/AllowlistPlugin.sol index 80fcaee7..3207bdd6 100644 --- a/src/samples/permissionhooks/AllowlistPlugin.sol +++ b/src/samples/permissionhooks/AllowlistPlugin.sol @@ -68,25 +68,25 @@ contract AllowlistPlugin is IValidationHook, BasePlugin { selectorAllowlist[target][selector][msg.sender] = allowed; } - function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(userOp.callData); return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint8 validationId, address, uint256, bytes calldata data, bytes calldata) + function preRuntimeValidationHook(uint32 validationId, address, uint256, bytes calldata data, bytes calldata) external view override { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(data); return; } diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 7eab0d19..1457801a 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -18,9 +18,9 @@ contract AccountExecHooksTest is AccountTestBase { bytes32 public manifestHash2; bytes4 internal constant _EXEC_SELECTOR = bytes4(uint32(1)); - uint8 internal constant _PRE_HOOK_FUNCTION_ID_1 = 1; - uint8 internal constant _POST_HOOK_FUNCTION_ID_2 = 2; - uint8 internal constant _BOTH_HOOKS_FUNCTION_ID_3 = 3; + uint32 internal constant _PRE_HOOK_FUNCTION_ID_1 = 1; + uint32 internal constant _POST_HOOK_FUNCTION_ID_2 = 2; + uint32 internal constant _BOTH_HOOKS_FUNCTION_ID_3 = 3; PluginManifest internal _m1; diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 2d006b2d..121bc935 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -70,7 +70,7 @@ contract AccountLoupeTest is CustomValidationTestBase { function test_pluginLoupe_getSelectors() public { FunctionReference comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.VALIDATION) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.VALIDATION) ); bytes4[] memory selectors = account1.getSelectors(comprehensivePluginValidation); @@ -84,21 +84,21 @@ contract AccountLoupeTest is CustomValidationTestBase { ExecutionHook[3] memory expectedHooks = [ ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.BOTH_EXECUTION_HOOKS) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.BOTH_EXECUTION_HOOKS) ), isPreHook: true, isPostHook: true }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_EXECUTION_HOOK) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_EXECUTION_HOOK) ), isPreHook: true, isPostHook: false }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.POST_EXECUTION_HOOK) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.POST_EXECUTION_HOOK) ), isPreHook: false, isPostHook: true @@ -124,7 +124,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[0]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) ) ) ); @@ -132,7 +132,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[1]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) ) ) ); @@ -148,10 +148,10 @@ contract AccountLoupeTest is CustomValidationTestBase { { FunctionReference[] memory preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) ); preValidationHooks[1] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) ); bytes[] memory installDatas = new bytes[](2); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index bf7ef948..6db83015 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -213,7 +213,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -232,7 +232,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -274,7 +274,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Target not allowed") ) ); @@ -328,7 +328,7 @@ contract PerHookDataTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(_accessControlHookPlugin), uint8(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK) + address(_accessControlHookPlugin), uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index ea0911b0..21b4de33 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -28,7 +28,7 @@ contract SelfCallAuthorizationTest is AccountTestBase { account1.installPlugin(address(comprehensivePlugin), manifestHash, ""); comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint8(ComprehensivePlugin.ValidationId.VALIDATION) + address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.VALIDATION) ); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 68cca892..3643eff5 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -33,17 +33,17 @@ contract ValidationIntersectionTest is AccountTestBase { noHookValidation = FunctionReferenceLib.pack({ addr: address(noHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); oneHookValidation = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); twoHookValidation = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) }); vm.startPrank(address(entryPoint)); @@ -62,7 +62,7 @@ contract ValidationIntersectionTest is AccountTestBase { FunctionReference[] memory preValidationHooks = new FunctionReference[](1); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) }); bytes[] memory installDatas = new bytes[](1); account1.installValidation( @@ -81,11 +81,11 @@ contract ValidationIntersectionTest is AccountTestBase { preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) }); preValidationHooks[1] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint8(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_2) + validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_2) }); installDatas = new bytes[](2); account1.installValidation( diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 3382c43e..3cc45c6b 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -9,12 +9,12 @@ import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; contract FunctionReferenceLibTest is Test { using FunctionReferenceLib for FunctionReference; - function testFuzz_functionReference_packing(address addr, uint8 validationId) public { + function testFuzz_functionReference_packing(address addr, uint32 validationId) public { // console.log("addr: ", addr); // console.log("validationId: ", vm.toString(validationId)); FunctionReference fr = FunctionReferenceLib.pack(addr, validationId); // console.log("packed: ", vm.toString(FunctionReference.unwrap(fr))); - (address addr2, uint8 validationId2) = FunctionReferenceLib.unpack(fr); + (address addr2, uint32 validationId2) = FunctionReferenceLib.unpack(fr); // console.log("addr2: ", addr2); // console.log("validationId2: ", vm.toString(validationId2)); assertEq(addr, addr2); diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index fc6c3191..af4ea806 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -46,85 +46,85 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { return 0; - } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { return 0; } revert NotImplemented(); } - function validateUserOp(uint8 validationId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint32 validationId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (validationId == uint8(ValidationId.VALIDATION)) { + if (validationId == uint32(ValidationId.VALIDATION)) { return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint8 validationId, address, uint256, bytes calldata, bytes calldata) + function preRuntimeValidationHook(uint32 validationId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { return; - } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { return; } revert NotImplemented(); } - function validateRuntime(uint8 validationId, address, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint32 validationId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (validationId == uint8(ValidationId.VALIDATION)) { + if (validationId == uint32(ValidationId.VALIDATION)) { return; } revert NotImplemented(); } - function validateSignature(uint8 validationId, address, bytes32, bytes calldata) + function validateSignature(uint32 validationId, address, bytes32, bytes calldata) external pure returns (bytes4) { - if (validationId == uint8(ValidationId.SIG_VALIDATION)) { + if (validationId == uint32(ValidationId.SIG_VALIDATION)) { return 0xffffffff; } revert NotImplemented(); } - function preExecutionHook(uint8 validationId, address, uint256, bytes calldata) + function preExecutionHook(uint32 validationId, address, uint256, bytes calldata) external pure override returns (bytes memory) { - if (validationId == uint8(ValidationId.PRE_EXECUTION_HOOK)) { + if (validationId == uint32(ValidationId.PRE_EXECUTION_HOOK)) { return ""; - } else if (validationId == uint8(ValidationId.BOTH_EXECUTION_HOOKS)) { + } else if (validationId == uint32(ValidationId.BOTH_EXECUTION_HOOKS)) { return ""; } revert NotImplemented(); } - function postExecutionHook(uint8 validationId, bytes calldata) external pure override { - if (validationId == uint8(ValidationId.POST_EXECUTION_HOOK)) { + function postExecutionHook(uint32 validationId, bytes calldata) external pure override { + if (validationId == uint32(ValidationId.POST_EXECUTION_HOOK)) { return; - } else if (validationId == uint8(ValidationId.BOTH_EXECUTION_HOOKS)) { + } else if (validationId == uint32(ValidationId.BOTH_EXECUTION_HOOKS)) { return; } revert NotImplemented(); @@ -145,7 +145,7 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint8(ValidationId.VALIDATION), + validationId: uint32(ValidationId.VALIDATION), isDefault: true, isSignatureValidation: false, selectors: validationSelectors @@ -154,19 +154,19 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.executionHooks = new ManifestExecutionHook[](3); manifest.executionHooks[0] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint8(ValidationId.BOTH_EXECUTION_HOOKS), + validationId: uint32(ValidationId.BOTH_EXECUTION_HOOKS), isPreHook: true, isPostHook: true }); manifest.executionHooks[1] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint8(ValidationId.PRE_EXECUTION_HOOK), + validationId: uint32(ValidationId.PRE_EXECUTION_HOOK), isPreHook: true, isPostHook: false }); manifest.executionHooks[2] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint8(ValidationId.POST_EXECUTION_HOOK), + validationId: uint32(ValidationId.POST_EXECUTION_HOOK), isPreHook: false, isPostHook: true }); diff --git a/test/mocks/plugins/MockAccessControlHookPlugin.sol b/test/mocks/plugins/MockAccessControlHookPlugin.sol index 9555270d..edbd2822 100644 --- a/test/mocks/plugins/MockAccessControlHookPlugin.sol +++ b/test/mocks/plugins/MockAccessControlHookPlugin.sol @@ -28,13 +28,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { delete allowedTargets[msg.sender]; } - function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { if (bytes4(userOp.callData[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(userOp.callData[4:36], (address)); @@ -49,13 +49,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { } function preRuntimeValidationHook( - uint8 validationId, + uint32 validationId, address, uint256, bytes calldata data, bytes calldata authorization ) external view override { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { if (bytes4(data[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(data[4:36], (address)); diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 78f9dddd..abe971da 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -72,17 +72,17 @@ contract ResultConsumerPlugin is BasePlugin, IValidation { // Validation function implementations. We only care about the runtime validation function, to authorize // itself. - function validateUserOp(uint8, PackedUserOperation calldata, bytes32) external pure returns (uint256) { + function validateUserOp(uint32, PackedUserOperation calldata, bytes32) external pure returns (uint256) { revert NotImplemented(); } - function validateRuntime(uint8, address sender, uint256, bytes calldata, bytes calldata) external view { + function validateRuntime(uint32, address sender, uint256, bytes calldata, bytes calldata) external view { if (sender != address(this)) { revert NotAuthorized(); } } - function validateSignature(uint8, address, bytes32, bytes calldata) external pure returns (bytes4) { + function validateSignature(uint32, address, bytes32, bytes calldata) external pure returns (bytes4) { revert NotImplemented(); } @@ -99,7 +99,7 @@ contract ResultConsumerPlugin is BasePlugin, IValidation { // This result should be allowed based on the manifest permission request bytes memory returnData = IStandardExecutor(msg.sender).executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (target, 0, abi.encodeCall(RegularResultContract.foo, ()))), - abi.encodePacked(this, uint8(0), uint8(0), uint32(1), uint8(255)) // Validation function of self, + abi.encodePacked(this, uint32(0), uint8(0), uint32(1), uint8(255)) // Validation function of self, // selector-associated, with no auth data ); diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index bb0dcc07..56ebdea9 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -32,40 +32,40 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint8 validationId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { return _preUserOpValidationHook1Data; - } else if (validationId == uint8(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { return _preUserOpValidationHook2Data; } revert NotImplemented(); } - function validateUserOp(uint8 validationId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint32 validationId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (validationId == uint8(ValidationId.USER_OP_VALIDATION)) { + if (validationId == uint32(ValidationId.USER_OP_VALIDATION)) { return _userOpValidationFunctionData; } revert NotImplemented(); } - function validateSignature(uint8, address, bytes32, bytes calldata) external pure override returns (bytes4) { + function validateSignature(uint32, address, bytes32, bytes calldata) external pure override returns (bytes4) { revert NotImplemented(); } // Empty stubs function pluginMetadata() external pure override returns (PluginMetadata memory) {} - function preRuntimeValidationHook(uint8, address, uint256, bytes calldata, bytes calldata) + function preRuntimeValidationHook(uint32, address, uint256, bytes calldata, bytes calldata) external pure override @@ -73,7 +73,7 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook revert NotImplemented(); } - function validateRuntime(uint8, address, uint256, bytes calldata, bytes calldata) external pure override { + function validateRuntime(uint32, address, uint256, bytes calldata, bytes calldata) external pure override { revert NotImplemented(); } } @@ -108,7 +108,7 @@ contract MockUserOpValidationPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint8(ValidationId.USER_OP_VALIDATION), + validationId: uint32(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -151,7 +151,7 @@ contract MockUserOpValidation1HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](2); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint8(ValidationId.USER_OP_VALIDATION), + validationId: uint32(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -197,7 +197,7 @@ contract MockUserOpValidation2HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint8(ValidationId.USER_OP_VALIDATION), + validationId: uint32(ValidationId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index f3e3d327..d4d28a27 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -184,7 +184,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector) ); } @@ -192,7 +192,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector) ); } @@ -293,7 +293,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(allowlistPlugin), uint8(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK) + address(allowlistPlugin), uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index a312fbdf..75123567 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -36,7 +36,7 @@ abstract contract AccountTestBase is OptimizedTest { uint8 public constant GLOBAL_VALIDATION = 1; // Re-declare the constant to prevent derived test contracts from having to import it - uint8 public constant TEST_DEFAULT_OWNER_FUNCTION_ID = EXT_CONST_TEST_DEFAULT_OWNER_FUNCTION_ID; + uint32 public constant TEST_DEFAULT_OWNER_FUNCTION_ID = EXT_CONST_TEST_DEFAULT_OWNER_FUNCTION_ID; uint256 public constant CALL_GAS_LIMIT = 100000; uint256 public constant VERIFICATION_GAS_LIMIT = 1200000; diff --git a/test/utils/TestConstants.sol b/test/utils/TestConstants.sol index 923692a7..f9cc8c90 100644 --- a/test/utils/TestConstants.sol +++ b/test/utils/TestConstants.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.25; -uint8 constant TEST_DEFAULT_OWNER_FUNCTION_ID = 0; +uint32 constant TEST_DEFAULT_OWNER_FUNCTION_ID = 0; From a0ed7f6dcf6ce705e9ade7cd7dbfe7d30f1ca304 Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Thu, 11 Jul 2024 15:02:29 -0700 Subject: [PATCH 03/11] rename validationId to entityId --- src/account/PluginManagerInternals.sol | 8 +-- src/account/UpgradeableModularAccount.sol | 60 +++++++++---------- src/helpers/FunctionReferenceLib.sol | 8 +-- src/helpers/ValidationConfigLib.sol | 12 ++-- src/interfaces/IExecutionHook.sol | 12 ++-- src/interfaces/IPlugin.sol | 4 +- src/interfaces/IValidation.sol | 16 ++--- src/interfaces/IValidationHook.sol | 18 +++--- src/plugins/owner/SingleOwnerPlugin.sol | 12 ++-- .../permissionhooks/AllowlistPlugin.sol | 10 ++-- standard/ERCs/erc-6900.md | 40 ++++++------- test/account/AccountExecHooks.t.sol | 6 +- test/account/AccountLoupe.t.sol | 16 ++--- test/account/PerHookData.t.sol | 8 +-- test/account/SelfCallAuthorization.t.sol | 2 +- test/account/ValidationIntersection.t.sol | 14 ++--- test/libraries/FunctionReferenceLib.t.sol | 12 ++-- test/mocks/plugins/ComprehensivePlugin.sol | 50 +++++++--------- .../plugins/MockAccessControlHookPlugin.sol | 10 ++-- test/mocks/plugins/ReturnDataPluginMocks.sol | 2 +- test/mocks/plugins/ValidationPluginMocks.sol | 18 +++--- test/samples/AllowlistPlugin.t.sol | 6 +- 22 files changed, 168 insertions(+), 176 deletions(-) diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 1fc64744..90f9b05b 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -78,7 +78,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.validationId); + FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.entityId); if (mv.isDefault) { _storage.validationData[validationFunction].isGlobal = true; @@ -99,7 +99,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.validationId); + FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.entityId); _storage.validationData[validationFunction].isGlobal = false; _storage.validationData[validationFunction].isSignatureValidation = false; @@ -183,7 +183,7 @@ abstract contract PluginManagerInternals is IPluginManager { for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.validationId); + FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.entityId); _addExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } @@ -223,7 +223,7 @@ abstract contract PluginManagerInternals is IPluginManager { uint256 length = manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.validationId); + FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.entityId); EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; _removeExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 17c930ad..8e7c2ec6 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -68,18 +68,18 @@ contract UpgradeableModularAccount is error NativeTokenSpendingNotPermitted(address plugin); error NonCanonicalEncoding(); error NotEntryPoint(); - error PostExecHookReverted(address plugin, uint32 validationId, bytes revertReason); - error PreExecHookReverted(address plugin, uint32 validationId, bytes revertReason); - error PreRuntimeValidationHookFailed(address plugin, uint32 validationId, bytes revertReason); + error PostExecHookReverted(address plugin, uint32 entityId, bytes revertReason); + error PreExecHookReverted(address plugin, uint32 entityId, bytes revertReason); + error PreRuntimeValidationHookFailed(address plugin, uint32 entityId, bytes revertReason); error RequireUserOperationContext(); error RuntimeValidationFunctionMissing(bytes4 selector); - error RuntimeValidationFunctionReverted(address plugin, uint32 validationId, bytes revertReason); + error RuntimeValidationFunctionReverted(address plugin, uint32 entityId, bytes revertReason); error SelfCallRecursionDepthExceeded(); - error SignatureValidationInvalid(address plugin, uint32 validationId); - error UnexpectedAggregator(address plugin, uint32 validationId, address aggregator); + error SignatureValidationInvalid(address plugin, uint32 entityId); + error UnexpectedAggregator(address plugin, uint32 entityId, address aggregator); error UnrecognizedFunction(bytes4 selector); error UserOpValidationFunctionMissing(bytes4 selector); - error ValidationDoesNotApply(bytes4 selector, address plugin, uint32 validationId, bool isGlobal); + error ValidationDoesNotApply(bytes4 selector, address plugin, uint32 entityId, bool isGlobal); error ValidationSignatureSegmentMissing(); error SignatureSegmentOutOfOrder(); @@ -343,15 +343,13 @@ contract UpgradeableModularAccount is FunctionReference sigValidation = FunctionReference.wrap(bytes24(signature)); - (address plugin, uint32 validationId) = sigValidation.unpack(); + (address plugin, uint32 entityId) = sigValidation.unpack(); if (!_storage.validationData[sigValidation].isSignatureValidation) { - revert SignatureValidationInvalid(plugin, validationId); + revert SignatureValidationInvalid(plugin, entityId); } - if ( - IValidation(plugin).validateSignature(validationId, msg.sender, hash, signature[24:]) - == _1271_MAGIC_VALUE - ) { + if (IValidation(plugin).validateSignature(entityId, msg.sender, hash, signature[24:]) == _1271_MAGIC_VALUE) + { return _1271_MAGIC_VALUE; } return _1271_INVALID; @@ -434,13 +432,13 @@ contract UpgradeableModularAccount is userOp.signature = ""; } - (address plugin, uint32 validationId) = preUserOpValidationHooks[i].unpack(); + (address plugin, uint32 entityId) = preUserOpValidationHooks[i].unpack(); uint256 currentValidationData = - IValidationHook(plugin).preUserOpValidationHook(validationId, userOp, userOpHash); + IValidationHook(plugin).preUserOpValidationHook(entityId, userOp, userOpHash); if (uint160(currentValidationData) > 1) { // If the aggregator is not 0 or 1, it is an unexpected value - revert UnexpectedAggregator(plugin, validationId, address(uint160(currentValidationData))); + revert UnexpectedAggregator(plugin, entityId, address(uint160(currentValidationData))); } validationData = _coalescePreValidation(validationData, currentValidationData); } @@ -453,8 +451,8 @@ contract UpgradeableModularAccount is userOp.signature = signatureSegment.getBody(); - (address plugin, uint32 validationId) = userOpValidationFunction.unpack(); - uint256 currentValidationData = IValidation(plugin).validateUserOp(validationId, userOp, userOpHash); + (address plugin, uint32 entityId) = userOpValidationFunction.unpack(); + uint256 currentValidationData = IValidation(plugin).validateUserOp(entityId, userOp, userOpHash); if (preUserOpValidationHooks.length != 0) { // If we have other validation data we need to coalesce with @@ -501,15 +499,15 @@ contract UpgradeableModularAccount is currentAuthData = ""; } - (address hookPlugin, uint32 hookValidationId) = preRuntimeValidationHooks[i].unpack(); + (address hookPlugin, uint32 hookEntityId) = preRuntimeValidationHooks[i].unpack(); try IValidationHook(hookPlugin).preRuntimeValidationHook( - hookValidationId, msg.sender, msg.value, callData, currentAuthData + hookEntityId, msg.sender, msg.value, callData, currentAuthData ) // forgefmt: disable-start // solhint-disable-next-line no-empty-blocks {} catch (bytes memory revertReason) { // forgefmt: disable-end - revert PreRuntimeValidationHookFailed(hookPlugin, hookValidationId, revertReason); + revert PreRuntimeValidationHookFailed(hookPlugin, hookEntityId, revertReason); } } @@ -517,16 +515,14 @@ contract UpgradeableModularAccount is revert ValidationSignatureSegmentMissing(); } - (address plugin, uint32 validationId) = runtimeValidationFunction.unpack(); + (address plugin, uint32 entityId) = runtimeValidationFunction.unpack(); - try IValidation(plugin).validateRuntime( - validationId, msg.sender, msg.value, callData, authSegment.getBody() - ) + try IValidation(plugin).validateRuntime(entityId, msg.sender, msg.value, callData, authSegment.getBody()) // forgefmt: disable-start // solhint-disable-next-line no-empty-blocks {} catch (bytes memory revertReason) { // forgefmt: disable-end - revert RuntimeValidationFunctionReverted(plugin, validationId, revertReason); + revert RuntimeValidationFunctionReverted(plugin, entityId, revertReason); } } @@ -571,14 +567,14 @@ contract UpgradeableModularAccount is internal returns (bytes memory preExecHookReturnData) { - (address plugin, uint32 validationId) = preExecHook.unpack(); - try IExecutionHook(plugin).preExecutionHook(validationId, msg.sender, msg.value, data) returns ( + (address plugin, uint32 entityId) = preExecHook.unpack(); + try IExecutionHook(plugin).preExecutionHook(entityId, msg.sender, msg.value, data) returns ( bytes memory returnData ) { preExecHookReturnData = returnData; } catch (bytes memory revertReason) { // TODO: same issue with EP0.6 - we can't do bytes4 error codes in plugins - revert PreExecHookReverted(plugin, validationId, revertReason); + revert PreExecHookReverted(plugin, entityId, revertReason); } } @@ -596,11 +592,11 @@ contract UpgradeableModularAccount is continue; } - (address plugin, uint32 validationId) = postHookToRun.postExecHook.unpack(); + (address plugin, uint32 entityId) = postHookToRun.postExecHook.unpack(); // solhint-disable-next-line no-empty-blocks - try IExecutionHook(plugin).postExecutionHook(validationId, postHookToRun.preExecHookReturnData) {} + try IExecutionHook(plugin).postExecutionHook(entityId, postHookToRun.preExecHookReturnData) {} catch (bytes memory revertReason) { - revert PostExecHookReverted(plugin, validationId, revertReason); + revert PostExecHookReverted(plugin, entityId, revertReason); } } } diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol index e39b9360..6e9c4dc6 100644 --- a/src/helpers/FunctionReferenceLib.sol +++ b/src/helpers/FunctionReferenceLib.sol @@ -9,14 +9,14 @@ library FunctionReferenceLib { // Magic value for hooks that should always revert. FunctionReference internal constant _PRE_HOOK_ALWAYS_DENY = FunctionReference.wrap(bytes24(uint192(2))); - function pack(address addr, uint32 validationId) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes24(bytes20(addr)) | bytes24(uint192(validationId))); + function pack(address addr, uint32 entityId) internal pure returns (FunctionReference) { + return FunctionReference.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); } - function unpack(FunctionReference fr) internal pure returns (address addr, uint32 validationId) { + function unpack(FunctionReference fr) internal pure returns (address addr, uint32 entityId) { bytes24 underlying = FunctionReference.unwrap(fr); addr = address(bytes20(underlying)); - validationId = uint32(bytes4(underlying << 160)); + entityId = uint32(bytes4(underlying << 160)); } function isEmpty(FunctionReference fr) internal pure returns (bool) { diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index 51bbf63c..f2dfacea 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -28,7 +28,7 @@ library ValidationConfigLib { ); } - function pack(address _plugin, uint32 _validationId, bool _isGlobal, bool _isSignatureValidation) + function pack(address _plugin, uint32 _entityId, bool _isGlobal, bool _isSignatureValidation) internal pure returns (ValidationConfig) @@ -37,8 +37,8 @@ library ValidationConfigLib { bytes26( // plugin address stored in the first 20 bytes bytes26(bytes20(_plugin)) - // validationId stored in the 21st - 24th byte - | bytes26(bytes32(uint256(_validationId) << 168)) + // entityId stored in the 21st - 24th byte + | bytes26(bytes32(uint256(_entityId) << 168)) // isGlobal flag stored in the 25th byte | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) // isSignatureValidation flag stored in the 26th byte @@ -50,11 +50,11 @@ library ValidationConfigLib { function unpackUnderlying(ValidationConfig config) internal pure - returns (address _plugin, uint32 _validationId, bool _isGlobal, bool _isSignatureValidation) + returns (address _plugin, uint32 _entityId, bool _isGlobal, bool _isSignatureValidation) { bytes26 configBytes = ValidationConfig.unwrap(config); _plugin = address(bytes20(configBytes)); - _validationId = uint32(bytes4(configBytes << 160)); + _entityId = uint32(bytes4(configBytes << 160)); _isGlobal = uint8(configBytes[24]) == 1; _isSignatureValidation = uint8(configBytes[25]) == 1; } @@ -74,7 +74,7 @@ library ValidationConfigLib { return address(bytes20(ValidationConfig.unwrap(config))); } - function validationId(ValidationConfig config) internal pure returns (uint32) { + function entityId(ValidationConfig config) internal pure returns (uint32) { return uint32(bytes4(ValidationConfig.unwrap(config) << 160)); } diff --git a/src/interfaces/IExecutionHook.sol b/src/interfaces/IExecutionHook.sol index 3afff456..6f0b06eb 100644 --- a/src/interfaces/IExecutionHook.sol +++ b/src/interfaces/IExecutionHook.sol @@ -4,24 +4,24 @@ pragma solidity ^0.8.25; import {IPlugin} from "./IPlugin.sol"; interface IExecutionHook is IPlugin { - /// @notice Run the pre execution hook specified by the `validationId`. + /// @notice Run the pre execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint32 validationId, address sender, uint256 value, bytes calldata data) + function preExecutionHook(uint32 entityId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); - /// @notice Run the post execution hook specified by the `validationId`. + /// @notice Run the post execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint32 validationId, bytes calldata preExecHookData) external; + function postExecutionHook(uint32 entityId, bytes calldata preExecHookData) external; } diff --git a/src/interfaces/IPlugin.sol b/src/interfaces/IPlugin.sol index 1b576bd6..824a1ddf 100644 --- a/src/interfaces/IPlugin.sol +++ b/src/interfaces/IPlugin.sol @@ -15,7 +15,7 @@ struct ManifestExecutionFunction { // todo: do we need these at all? Or do we fully switch to `installValidation`? struct ManifestValidation { - uint32 validationId; + uint32 entityId; bool isDefault; bool isSignatureValidation; bytes4[] selectors; @@ -24,7 +24,7 @@ struct ManifestValidation { struct ManifestExecutionHook { // TODO(erc6900 spec): These fields can be packed into a single word bytes4 executionSelector; - uint32 validationId; + uint32 entityId; bool isPreHook; bool isPostHook; } diff --git a/src/interfaces/IValidation.sol b/src/interfaces/IValidation.sol index 2f3e1292..266e6878 100644 --- a/src/interfaces/IValidation.sol +++ b/src/interfaces/IValidation.sol @@ -6,20 +6,20 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {IPlugin} from "./IPlugin.sol"; interface IValidation is IPlugin { - /// @notice Run the user operation validationFunction specified by the `validationId`. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @notice Run the user operation validationFunction specified by the `entityId`. + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function validateUserOp(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the runtime validationFunction specified by the `validationId`. + /// @notice Run the runtime validationFunction specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param sender The caller address. @@ -27,7 +27,7 @@ interface IValidation is IPlugin { /// @param data The calldata sent. /// @param authorization Additional data for the validation function to use. function validateRuntime( - uint32 validationId, + uint32 entityId, address sender, uint256 value, bytes calldata data, @@ -36,14 +36,14 @@ interface IValidation is IPlugin { /// @notice Validates a signature using ERC-1271. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param sender the address that sent the ERC-1271 request to the smart account /// @param hash the hash of the ERC-1271 request /// @param signature the signature of the ERC-1271 request /// @return the ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid. - function validateSignature(uint32 validationId, address sender, bytes32 hash, bytes calldata signature) + function validateSignature(uint32 entityId, address sender, bytes32 hash, bytes calldata signature) external view returns (bytes4); diff --git a/src/interfaces/IValidationHook.sol b/src/interfaces/IValidationHook.sol index 9febf60b..dc5630dc 100644 --- a/src/interfaces/IValidationHook.sol +++ b/src/interfaces/IValidationHook.sol @@ -6,28 +6,28 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {IPlugin} from "./IPlugin.sol"; interface IValidationHook is IPlugin { - /// @notice Run the pre user operation validation hook specified by the `validationId`. + /// @notice Run the pre user operation validation hook specified by the `entityId`. /// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the pre runtime validation hook specified by the `validationId`. + /// @notice Run the pre runtime validation hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. function preRuntimeValidationHook( - uint32 validationId, + uint32 entityId, address sender, uint256 value, bytes calldata data, @@ -36,15 +36,15 @@ interface IValidationHook is IPlugin { // TODO: support this hook type within the account & in the manifest - /// @notice Run the pre signature validation hook specified by the `validationId`. + /// @notice Run the pre signature validation hook specified by the `entityId`. /// @dev To indicate the call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there + /// @param entityId An identifier that routes the call to different internal implementations, should there /// be /// more than one. /// @param sender The caller address. /// @param hash The hash of the message being signed. /// @param signature The signature of the message. - // function preSignatureValidationHook(uint32 validationId, address sender, bytes32 hash, bytes calldata + // function preSignatureValidationHook(uint32 entityId, address sender, bytes32 hash, bytes calldata // signature) // external // view diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol index c8aa6495..d82a1824 100644 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ b/src/plugins/owner/SingleOwnerPlugin.sol @@ -82,20 +82,20 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { } /// @inheritdoc IValidation - function validateRuntime(uint32 validationId, address sender, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint32 entityId, address sender, uint256, bytes calldata, bytes calldata) external view override { // Validate that the sender is the owner of the account or self. - if (sender != owners[validationId][msg.sender]) { + if (sender != owners[entityId][msg.sender]) { revert NotAuthorized(); } return; } /// @inheritdoc IValidation - function validateUserOp(uint32 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) external view override @@ -104,7 +104,7 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { // Validate the user op signature against the owner. if ( SignatureChecker.isValidSignatureNow( - owners[validationId][msg.sender], userOpHash.toEthSignedMessageHash(), userOp.signature + owners[entityId][msg.sender], userOpHash.toEthSignedMessageHash(), userOp.signature ) ) { return _SIG_VALIDATION_PASSED; @@ -123,13 +123,13 @@ contract SingleOwnerPlugin is IValidation, BasePlugin { /// validation used in `validateUserOp`, this does///*not** wrap the digest in /// an "Ethereum Signed Message" envelope before checking the signature in /// the EOA-owner case. - function validateSignature(uint32 validationId, address, bytes32 digest, bytes calldata signature) + function validateSignature(uint32 entityId, address, bytes32 digest, bytes calldata signature) external view override returns (bytes4) { - if (SignatureChecker.isValidSignatureNow(owners[validationId][msg.sender], digest, signature)) { + if (SignatureChecker.isValidSignatureNow(owners[entityId][msg.sender], digest, signature)) { return _1271_MAGIC_VALUE; } return _1271_INVALID; diff --git a/src/samples/permissionhooks/AllowlistPlugin.sol b/src/samples/permissionhooks/AllowlistPlugin.sol index 3207bdd6..2d86ca18 100644 --- a/src/samples/permissionhooks/AllowlistPlugin.sol +++ b/src/samples/permissionhooks/AllowlistPlugin.sol @@ -9,7 +9,7 @@ import {IStandardExecutor, Call} from "../../interfaces/IStandardExecutor.sol"; import {BasePlugin} from "../../plugins/BasePlugin.sol"; contract AllowlistPlugin is IValidationHook, BasePlugin { - enum ValidationId { + enum EntityId { PRE_VALIDATION_HOOK } @@ -68,25 +68,25 @@ contract AllowlistPlugin is IValidationHook, BasePlugin { selectorAllowlist[target][selector][msg.sender] = allowed; } - function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(userOp.callData); return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint32 validationId, address, uint256, bytes calldata data, bytes calldata) + function preRuntimeValidationHook(uint32 entityId, address, uint256, bytes calldata data, bytes calldata) external view override { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK)) { _checkAllowlistCalldata(data); return; } diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index 1adfb375..9c80488c 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -267,56 +267,56 @@ interface IPlugin { /// @param data Optional bytes array to be decoded and used by the plugin to clear plugin data for the modular account. function onUninstall(bytes calldata data) external; - /// @notice Run the pre user operation validation hook specified by the `validationId`. + /// @notice Run the pre user operation validation hook specified by the `entityId`. /// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1. - /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param entityId An identifier that routes the call to different internal implementations, should there be more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function preUserOpValidationHook(uint8 validationId, PackedUserOperation memory userOp, bytes32 userOpHash) external returns (uint256); + function preUserOpValidationHook(uint8 entityId, PackedUserOperation memory userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the user operation validationFunction specified by the `validationId`. - /// @param validationId An identifier that routes the call to different internal implementations, should there be + /// @notice Run the user operation validationFunction specified by the `entityId`. + /// @param entityId An identifier that routes the call to different internal implementations, should there be /// more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). - function userOpValidationFunction(uint8 validationId, PackedUserOperation calldata userOp, bytes32 userOpHash) + function userOpValidationFunction(uint8 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) external returns (uint256); - /// @notice Run the pre runtime validation hook specified by the `validationId`. + /// @notice Run the pre runtime validation hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param entityId An identifier that routes the call to different internal implementations, should there be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. - function preRuntimeValidationHook(uint8 validationId, address sender, uint256 value, bytes calldata data) external; + function preRuntimeValidationHook(uint8 entityId, address sender, uint256 value, bytes calldata data) external; - /// @notice Run the runtime validationFunction specified by the `validationId`. + /// @notice Run the runtime validationFunction specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there be + /// @param entityId An identifier that routes the call to different internal implementations, should there be /// more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. - function runtimeValidationFunction(uint8 validationId, address sender, uint256 value, bytes calldata data) + function runtimeValidationFunction(uint8 entityId, address sender, uint256 value, bytes calldata data) external; - /// @notice Run the pre execution hook specified by the `validationId`. + /// @notice Run the pre execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param entityId An identifier that routes the call to different internal implementations, should there be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint8 validationId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); + function preExecutionHook(uint8 entityId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); - /// @notice Run the post execution hook specified by the `validationId`. + /// @notice Run the post execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. - /// @param validationId An identifier that routes the call to different internal implementations, should there be more than one. + /// @param entityId An identifier that routes the call to different internal implementations, should there be more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint8 validationId, bytes calldata preExecHookData) external; + function postExecutionHook(uint8 entityId, bytes calldata preExecHookData) external; /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. @@ -359,7 +359,7 @@ enum ManifestAssociatedFunctionType { /// of the function at `dependencies[dependencyIndex]` during the call to `installPlugin(config)`. struct ManifestFunction { ManifestAssociatedFunctionType functionType; - uint8 validationId; + uint8 entityId; uint256 dependencyIndex; } @@ -371,7 +371,7 @@ struct ManifestAssociatedFunction { struct ManifestExecutionHook { // TODO(erc6900 spec): These fields can be packed into a single word bytes4 executionSelector; - uint8 validationId; + uint8 entityId; bool isPreHook; bool isPostHook; } diff --git a/test/account/AccountExecHooks.t.sol b/test/account/AccountExecHooks.t.sol index 1457801a..98d16dcf 100644 --- a/test/account/AccountExecHooks.t.sol +++ b/test/account/AccountExecHooks.t.sol @@ -45,7 +45,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - validationId: _PRE_HOOK_FUNCTION_ID_1, + entityId: _PRE_HOOK_FUNCTION_ID_1, isPreHook: true, isPostHook: false }) @@ -83,7 +83,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - validationId: _BOTH_HOOKS_FUNCTION_ID_3, + entityId: _BOTH_HOOKS_FUNCTION_ID_3, isPreHook: true, isPostHook: true }) @@ -131,7 +131,7 @@ contract AccountExecHooksTest is AccountTestBase { _installPlugin1WithHooks( ManifestExecutionHook({ executionSelector: _EXEC_SELECTOR, - validationId: _POST_HOOK_FUNCTION_ID_2, + entityId: _POST_HOOK_FUNCTION_ID_2, isPreHook: false, isPostHook: true }) diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 121bc935..9602393f 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -70,7 +70,7 @@ contract AccountLoupeTest is CustomValidationTestBase { function test_pluginLoupe_getSelectors() public { FunctionReference comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.VALIDATION) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) ); bytes4[] memory selectors = account1.getSelectors(comprehensivePluginValidation); @@ -84,21 +84,21 @@ contract AccountLoupeTest is CustomValidationTestBase { ExecutionHook[3] memory expectedHooks = [ ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.BOTH_EXECUTION_HOOKS) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.BOTH_EXECUTION_HOOKS) ), isPreHook: true, isPostHook: true }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_EXECUTION_HOOK) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_EXECUTION_HOOK) ), isPreHook: true, isPostHook: false }), ExecutionHook({ hookFunction: FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.POST_EXECUTION_HOOK) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.POST_EXECUTION_HOOK) ), isPreHook: false, isPostHook: true @@ -124,7 +124,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[0]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ) ) ); @@ -132,7 +132,7 @@ contract AccountLoupeTest is CustomValidationTestBase { FunctionReference.unwrap(hooks[1]), FunctionReference.unwrap( FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ) ) ); @@ -148,10 +148,10 @@ contract AccountLoupeTest is CustomValidationTestBase { { FunctionReference[] memory preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_1) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ); preValidationHooks[1] = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.PRE_VALIDATION_HOOK_2) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ); bytes[] memory installDatas = new bytes[](2); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index 6db83015..74228643 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -213,7 +213,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -232,7 +232,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Proof doesn't match target") ) ); @@ -274,7 +274,7 @@ contract PerHookDataTest is CustomValidationTestBase { abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, _accessControlHookPlugin, - uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK), abi.encodeWithSignature("Error(string)", "Target not allowed") ) ); @@ -328,7 +328,7 @@ contract PerHookDataTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(_accessControlHookPlugin), uint32(MockAccessControlHookPlugin.ValidationId.PRE_VALIDATION_HOOK) + address(_accessControlHookPlugin), uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index 21b4de33..708117e2 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -28,7 +28,7 @@ contract SelfCallAuthorizationTest is AccountTestBase { account1.installPlugin(address(comprehensivePlugin), manifestHash, ""); comprehensivePluginValidation = FunctionReferenceLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.ValidationId.VALIDATION) + address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) ); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 3643eff5..5011aad8 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -33,17 +33,17 @@ contract ValidationIntersectionTest is AccountTestBase { noHookValidation = FunctionReferenceLib.pack({ addr: address(noHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); oneHookValidation = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); twoHookValidation = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.USER_OP_VALIDATION) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); vm.startPrank(address(entryPoint)); @@ -62,7 +62,7 @@ contract ValidationIntersectionTest is AccountTestBase { FunctionReference[] memory preValidationHooks = new FunctionReference[](1); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(oneHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); bytes[] memory installDatas = new bytes[](1); account1.installValidation( @@ -81,11 +81,11 @@ contract ValidationIntersectionTest is AccountTestBase { preValidationHooks = new FunctionReference[](2); preValidationHooks[0] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); preValidationHooks[1] = FunctionReferenceLib.pack({ addr: address(twoHookPlugin), - validationId: uint32(MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_2) + entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_2) }); installDatas = new bytes[](2); account1.installValidation( @@ -211,7 +211,7 @@ contract ValidationIntersectionTest is AccountTestBase { abi.encodeWithSelector( UpgradeableModularAccount.UnexpectedAggregator.selector, address(oneHookPlugin), - MockBaseUserOpValidationPlugin.ValidationId.PRE_VALIDATION_HOOK_1, + MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1, badAuthorizer ) ); diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 3cc45c6b..4ecfb0e9 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -9,16 +9,16 @@ import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; contract FunctionReferenceLibTest is Test { using FunctionReferenceLib for FunctionReference; - function testFuzz_functionReference_packing(address addr, uint32 validationId) public { + function testFuzz_functionReference_packing(address addr, uint32 entityId) public { // console.log("addr: ", addr); - // console.log("validationId: ", vm.toString(validationId)); - FunctionReference fr = FunctionReferenceLib.pack(addr, validationId); + // console.log("entityId: ", vm.toString(entityId)); + FunctionReference fr = FunctionReferenceLib.pack(addr, entityId); // console.log("packed: ", vm.toString(FunctionReference.unwrap(fr))); - (address addr2, uint32 validationId2) = FunctionReferenceLib.unpack(fr); + (address addr2, uint32 entityId2) = FunctionReferenceLib.unpack(fr); // console.log("addr2: ", addr2); - // console.log("validationId2: ", vm.toString(validationId2)); + // console.log("entityId2: ", vm.toString(entityId2)); assertEq(addr, addr2); - assertEq(validationId, validationId2); + assertEq(entityId, entityId2); } function testFuzz_functionReference_operators(FunctionReference a, FunctionReference b) public { diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index af4ea806..9bff9237 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -18,7 +18,7 @@ import {IExecutionHook} from "../../../src/interfaces/IExecutionHook.sol"; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, BasePlugin { - enum ValidationId { + enum EntityId { PRE_VALIDATION_HOOK_1, PRE_VALIDATION_HOOK_2, VALIDATION, @@ -46,85 +46,81 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_1)) { return 0; - } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_2)) { return 0; } revert NotImplemented(); } - function validateUserOp(uint32 validationId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint32 entityId, PackedUserOperation calldata, bytes32) external pure override returns (uint256) { - if (validationId == uint32(ValidationId.VALIDATION)) { + if (entityId == uint32(EntityId.VALIDATION)) { return 0; } revert NotImplemented(); } - function preRuntimeValidationHook(uint32 validationId, address, uint256, bytes calldata, bytes calldata) + function preRuntimeValidationHook(uint32 entityId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_1)) { return; - } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_2)) { return; } revert NotImplemented(); } - function validateRuntime(uint32 validationId, address, uint256, bytes calldata, bytes calldata) + function validateRuntime(uint32 entityId, address, uint256, bytes calldata, bytes calldata) external pure override { - if (validationId == uint32(ValidationId.VALIDATION)) { + if (entityId == uint32(EntityId.VALIDATION)) { return; } revert NotImplemented(); } - function validateSignature(uint32 validationId, address, bytes32, bytes calldata) - external - pure - returns (bytes4) - { - if (validationId == uint32(ValidationId.SIG_VALIDATION)) { + function validateSignature(uint32 entityId, address, bytes32, bytes calldata) external pure returns (bytes4) { + if (entityId == uint32(EntityId.SIG_VALIDATION)) { return 0xffffffff; } revert NotImplemented(); } - function preExecutionHook(uint32 validationId, address, uint256, bytes calldata) + function preExecutionHook(uint32 entityId, address, uint256, bytes calldata) external pure override returns (bytes memory) { - if (validationId == uint32(ValidationId.PRE_EXECUTION_HOOK)) { + if (entityId == uint32(EntityId.PRE_EXECUTION_HOOK)) { return ""; - } else if (validationId == uint32(ValidationId.BOTH_EXECUTION_HOOKS)) { + } else if (entityId == uint32(EntityId.BOTH_EXECUTION_HOOKS)) { return ""; } revert NotImplemented(); } - function postExecutionHook(uint32 validationId, bytes calldata) external pure override { - if (validationId == uint32(ValidationId.POST_EXECUTION_HOOK)) { + function postExecutionHook(uint32 entityId, bytes calldata) external pure override { + if (entityId == uint32(EntityId.POST_EXECUTION_HOOK)) { return; - } else if (validationId == uint32(ValidationId.BOTH_EXECUTION_HOOKS)) { + } else if (entityId == uint32(EntityId.BOTH_EXECUTION_HOOKS)) { return; } revert NotImplemented(); @@ -145,7 +141,7 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint32(ValidationId.VALIDATION), + entityId: uint32(EntityId.VALIDATION), isDefault: true, isSignatureValidation: false, selectors: validationSelectors @@ -154,19 +150,19 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba manifest.executionHooks = new ManifestExecutionHook[](3); manifest.executionHooks[0] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint32(ValidationId.BOTH_EXECUTION_HOOKS), + entityId: uint32(EntityId.BOTH_EXECUTION_HOOKS), isPreHook: true, isPostHook: true }); manifest.executionHooks[1] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint32(ValidationId.PRE_EXECUTION_HOOK), + entityId: uint32(EntityId.PRE_EXECUTION_HOOK), isPreHook: true, isPostHook: false }); manifest.executionHooks[2] = ManifestExecutionHook({ executionSelector: this.foo.selector, - validationId: uint32(ValidationId.POST_EXECUTION_HOOK), + entityId: uint32(EntityId.POST_EXECUTION_HOOK), isPreHook: false, isPostHook: true }); diff --git a/test/mocks/plugins/MockAccessControlHookPlugin.sol b/test/mocks/plugins/MockAccessControlHookPlugin.sol index edbd2822..6bd593f2 100644 --- a/test/mocks/plugins/MockAccessControlHookPlugin.sol +++ b/test/mocks/plugins/MockAccessControlHookPlugin.sol @@ -13,7 +13,7 @@ import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; // This is just a mock - it does not enforce this over `executeBatch` and other methods of making calls, and should // not be used in production.. contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { - enum ValidationId { + enum EntityId { PRE_VALIDATION_HOOK } @@ -28,13 +28,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { delete allowedTargets[msg.sender]; } - function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32) external view override returns (uint256) { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK)) { if (bytes4(userOp.callData[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(userOp.callData[4:36], (address)); @@ -49,13 +49,13 @@ contract MockAccessControlHookPlugin is IValidationHook, BasePlugin { } function preRuntimeValidationHook( - uint32 validationId, + uint32 entityId, address, uint256, bytes calldata data, bytes calldata authorization ) external view override { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK)) { if (bytes4(data[:4]) == IStandardExecutor.execute.selector) { address target = abi.decode(data[4:36], (address)); diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index abe971da..6bab7b0b 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -121,7 +121,7 @@ contract ResultConsumerPlugin is BasePlugin, IValidation { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: 0, + entityId: 0, isDefault: true, isSignatureValidation: false, selectors: validationSelectors diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index 56ebdea9..af57eaaa 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -14,7 +14,7 @@ import {IValidationHook} from "../../../src/interfaces/IValidationHook.sol"; import {BasePlugin} from "../../../src/plugins/BasePlugin.sol"; abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook, BasePlugin { - enum ValidationId { + enum EntityId { USER_OP_VALIDATION, PRE_VALIDATION_HOOK_1, PRE_VALIDATION_HOOK_2 @@ -32,27 +32,27 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook function onUninstall(bytes calldata) external override {} - function preUserOpValidationHook(uint32 validationId, PackedUserOperation calldata, bytes32) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_1)) { + if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_1)) { return _preUserOpValidationHook1Data; - } else if (validationId == uint32(ValidationId.PRE_VALIDATION_HOOK_2)) { + } else if (entityId == uint32(EntityId.PRE_VALIDATION_HOOK_2)) { return _preUserOpValidationHook2Data; } revert NotImplemented(); } - function validateUserOp(uint32 validationId, PackedUserOperation calldata, bytes32) + function validateUserOp(uint32 entityId, PackedUserOperation calldata, bytes32) external view override returns (uint256) { - if (validationId == uint32(ValidationId.USER_OP_VALIDATION)) { + if (entityId == uint32(EntityId.USER_OP_VALIDATION)) { return _userOpValidationFunctionData; } revert NotImplemented(); @@ -108,7 +108,7 @@ contract MockUserOpValidationPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint32(ValidationId.USER_OP_VALIDATION), + entityId: uint32(EntityId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -151,7 +151,7 @@ contract MockUserOpValidation1HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](2); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint32(ValidationId.USER_OP_VALIDATION), + entityId: uint32(EntityId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors @@ -197,7 +197,7 @@ contract MockUserOpValidation2HookPlugin is MockBaseUserOpValidationPlugin { manifest.validationFunctions = new ManifestValidation[](1); manifest.validationFunctions[0] = ManifestValidation({ - validationId: uint32(ValidationId.USER_OP_VALIDATION), + entityId: uint32(EntityId.USER_OP_VALIDATION), isDefault: false, isSignatureValidation: false, selectors: validationSelectors diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index d4d28a27..4bf5f60e 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -184,7 +184,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector) ); } @@ -192,7 +192,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { return abi.encodeWithSelector( UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, address(allowlistPlugin), - uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK), + uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK), abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector) ); } @@ -293,7 +293,7 @@ contract AllowlistPluginTest is CustomValidationTestBase { returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { FunctionReference accessControlHook = FunctionReferenceLib.pack( - address(allowlistPlugin), uint32(AllowlistPlugin.ValidationId.PRE_VALIDATION_HOOK) + address(allowlistPlugin), uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK) ); FunctionReference[] memory preValidationHooks = new FunctionReference[](1); From 2d124772e1338a4fb4068f726f31cc7f3be3afce Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Thu, 11 Jul 2024 15:16:58 -0700 Subject: [PATCH 04/11] rename Function reference to packed plugin entity --- src/account/AccountLoupe.sol | 10 +++--- src/account/AccountStorage.sol | 28 ++++++++--------- src/account/PluginManager2.sol | 38 +++++++++++------------ src/account/PluginManagerInternals.sol | 22 ++++++------- src/account/UpgradeableModularAccount.sol | 36 ++++++++++----------- src/helpers/FunctionReferenceLib.sol | 37 ---------------------- src/helpers/PackedPluginEntityLib.sol | 37 ++++++++++++++++++++++ src/helpers/ValidationConfigLib.sol | 14 ++++----- src/interfaces/IAccountLoupe.sol | 12 +++---- src/interfaces/IPluginManager.sol | 4 +-- standard/ERCs/erc-6900.md | 16 +++++----- test/account/AccountLoupe.t.sol | 36 ++++++++++----------- test/account/AccountReturnData.t.sol | 6 ++-- test/account/GlobalValidationTest.t.sol | 4 +-- test/account/MultiValidation.t.sol | 18 +++++------ test/account/PerHookData.t.sol | 8 ++--- test/account/SelfCallAuthorization.t.sol | 6 ++-- test/account/ValidationIntersection.t.sol | 24 +++++++------- test/libraries/FunctionReferenceLib.t.sol | 18 +++++------ test/samples/AllowlistPlugin.t.sol | 8 ++--- test/utils/AccountTestBase.sol | 28 ++++++++--------- test/utils/CustomValidationTestBase.sol | 6 ++-- 22 files changed, 208 insertions(+), 208 deletions(-) delete mode 100644 src/helpers/FunctionReferenceLib.sol create mode 100644 src/helpers/PackedPluginEntityLib.sol diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index d652f45c..8817f4b7 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -6,7 +6,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {IAccountLoupe, ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; import {getAccountStorage, toExecutionHook, toSelector} from "./AccountStorage.sol"; @@ -29,7 +29,7 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getSelectors(FunctionReference validationFunction) external view returns (bytes4[] memory) { + function getSelectors(PackedPluginEntity validationFunction) external view returns (bytes4[] memory) { uint256 length = getAccountStorage().validationData[validationFunction].selectors.length(); bytes4[] memory selectors = new bytes4[](length); @@ -61,7 +61,7 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getPermissionHooks(FunctionReference validationFunction) + function getPermissionHooks(PackedPluginEntity validationFunction) external view override @@ -79,11 +79,11 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getPreValidationHooks(FunctionReference validationFunction) + function getPreValidationHooks(PackedPluginEntity validationFunction) external view override - returns (FunctionReference[] memory preValidationHooks) + returns (PackedPluginEntity[] memory preValidationHooks) { preValidationHooks = getAccountStorage().validationData[validationFunction].preValidationHooks; } diff --git a/src/account/AccountStorage.sol b/src/account/AccountStorage.sol index 35fbe13d..16aadfdb 100644 --- a/src/account/AccountStorage.sol +++ b/src/account/AccountStorage.sol @@ -5,7 +5,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {FunctionReference} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; // bytes = keccak256("ERC6900.UpgradeableModularAccount.Storage") bytes32 constant _ACCOUNT_STORAGE_SLOT = 0x9f09680beaa4e5c9f38841db2460c401499164f368baef687948c315d9073e40; @@ -31,7 +31,7 @@ struct ValidationData { // Whether or not this validation is a signature validator. bool isSignatureValidation; // The pre validation hooks for this function selector. - FunctionReference[] preValidationHooks; + PackedPluginEntity[] preValidationHooks; // Permission hooks for this validation function. EnumerableSet.Bytes32Set permissionHooks; // The set of selectors that may be validated by this validation function. @@ -46,7 +46,7 @@ struct AccountStorage { EnumerableMap.AddressToUintMap pluginManifestHashes; // Execution functions and their associated functions mapping(bytes4 => SelectorData) selectorData; - mapping(FunctionReference validationFunction => ValidationData) validationData; + mapping(PackedPluginEntity validationFunction => ValidationData) validationData; // For ERC165 introspection mapping(bytes4 => uint256) supportedIfaces; } @@ -59,12 +59,12 @@ function getAccountStorage() pure returns (AccountStorage storage _storage) { using EnumerableSet for EnumerableSet.Bytes32Set; -function toSetValue(FunctionReference functionReference) pure returns (bytes32) { - return bytes32(FunctionReference.unwrap(functionReference)); +function toSetValue(PackedPluginEntity functionReference) pure returns (bytes32) { + return bytes32(PackedPluginEntity.unwrap(functionReference)); } -function toFunctionReference(bytes32 setValue) pure returns (FunctionReference) { - return FunctionReference.wrap(bytes24(setValue)); +function toPackedPluginEntity(bytes32 setValue) pure returns (PackedPluginEntity) { + return PackedPluginEntity.wrap(bytes24(setValue)); } // ExecutionHook layout: @@ -73,16 +73,16 @@ function toFunctionReference(bytes32 setValue) pure returns (FunctionReference) // 0x____________________________________________BB__________________ is post hook function toSetValue(ExecutionHook memory executionHook) pure returns (bytes32) { - return bytes32(FunctionReference.unwrap(executionHook.hookFunction)) + return bytes32(PackedPluginEntity.unwrap(executionHook.hookFunction)) | bytes32(executionHook.isPreHook ? uint256(1) << 56 : 0) | bytes32(executionHook.isPostHook ? uint256(1) << 48 : 0); } function toExecutionHook(bytes32 setValue) pure - returns (FunctionReference hookFunction, bool isPreHook, bool isPostHook) + returns (PackedPluginEntity hookFunction, bool isPreHook, bool isPostHook) { - hookFunction = FunctionReference.wrap(bytes24(setValue)); + hookFunction = PackedPluginEntity.wrap(bytes24(setValue)); isPreHook = (uint256(setValue) >> 56) & 0xFF == 1; isPostHook = (uint256(setValue) >> 48) & 0xFF == 1; } @@ -96,15 +96,15 @@ function toSelector(bytes32 setValue) pure returns (bytes4) { } /// @dev Helper function to get all elements of a set into memory. -function toFunctionReferenceArray(EnumerableSet.Bytes32Set storage set) +function toPackedPluginEntityArray(EnumerableSet.Bytes32Set storage set) view - returns (FunctionReference[] memory) + returns (PackedPluginEntity[] memory) { uint256 length = set.length(); - FunctionReference[] memory result = new FunctionReference[](length); + PackedPluginEntity[] memory result = new PackedPluginEntity[](length); for (uint256 i = 0; i < length; ++i) { bytes32 key = set.at(i); - result[i] = FunctionReference.wrap(bytes24(key)); + result[i] = PackedPluginEntity.wrap(bytes24(key)); } return result; } diff --git a/src/account/PluginManager2.sol b/src/account/PluginManager2.sol index 28eb0ecf..c1dae3ff 100644 --- a/src/account/PluginManager2.sol +++ b/src/account/PluginManager2.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.25; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IPlugin} from "../interfaces/IPlugin.sol"; -import {FunctionReference, ValidationConfig} from "../interfaces/IPluginManager.sol"; -import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol"; -import {ValidationData, getAccountStorage, toSetValue, toFunctionReference} from "./AccountStorage.sol"; +import {ValidationData, getAccountStorage, toSetValue, toPackedPluginEntity} from "./AccountStorage.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; // Temporary additional functions for a user-controlled install flow for validation functions. @@ -18,10 +18,10 @@ abstract contract PluginManager2 { // Index marking the start of the data for the validation function. uint8 internal constant _RESERVED_VALIDATION_DATA_INDEX = 255; - error PreValidationAlreadySet(FunctionReference validationFunction, FunctionReference preValidationFunction); - error ValidationAlreadySet(bytes4 selector, FunctionReference validationFunction); - error ValidationNotSet(bytes4 selector, FunctionReference validationFunction); - error PermissionAlreadySet(FunctionReference validationFunction, ExecutionHook hook); + error PreValidationAlreadySet(PackedPluginEntity validationFunction, PackedPluginEntity preValidationFunction); + error ValidationAlreadySet(bytes4 selector, PackedPluginEntity validationFunction); + error ValidationNotSet(bytes4 selector, PackedPluginEntity validationFunction); + error PermissionAlreadySet(PackedPluginEntity validationFunction, ExecutionHook hook); error PreValidationHookLimitExceeded(); function _installValidation( @@ -35,16 +35,16 @@ abstract contract PluginManager2 { getAccountStorage().validationData[validationConfig.functionReference()]; if (preValidationHooks.length > 0) { - (FunctionReference[] memory preValidationFunctions, bytes[] memory initDatas) = - abi.decode(preValidationHooks, (FunctionReference[], bytes[])); + (PackedPluginEntity[] memory preValidationFunctions, bytes[] memory initDatas) = + abi.decode(preValidationHooks, (PackedPluginEntity[], bytes[])); for (uint256 i = 0; i < preValidationFunctions.length; ++i) { - FunctionReference preValidationFunction = preValidationFunctions[i]; + PackedPluginEntity preValidationFunction = preValidationFunctions[i]; _validationData.preValidationHooks.push(preValidationFunction); if (initDatas[i].length > 0) { - (address preValidationPlugin,) = FunctionReferenceLib.unpack(preValidationFunction); + (address preValidationPlugin,) = PackedPluginEntityLib.unpack(preValidationFunction); IPlugin(preValidationPlugin).onInstall(initDatas[i]); } } @@ -67,7 +67,7 @@ abstract contract PluginManager2 { } if (initDatas[i].length > 0) { - (address executionPlugin,) = FunctionReferenceLib.unpack(permissionFunction.hookFunction); + (address executionPlugin,) = PackedPluginEntityLib.unpack(permissionFunction.hookFunction); IPlugin(executionPlugin).onInstall(initDatas[i]); } } @@ -90,7 +90,7 @@ abstract contract PluginManager2 { } function _uninstallValidation( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData @@ -104,11 +104,11 @@ abstract contract PluginManager2 { bytes[] memory preValidationHookUninstallDatas = abi.decode(preValidationHookUninstallData, (bytes[])); // Clear pre validation hooks - FunctionReference[] storage preValidationHooks = _validationData.preValidationHooks; + PackedPluginEntity[] storage preValidationHooks = _validationData.preValidationHooks; for (uint256 i = 0; i < preValidationHooks.length; ++i) { - FunctionReference preValidationFunction = preValidationHooks[i]; + PackedPluginEntity preValidationFunction = preValidationHooks[i]; if (preValidationHookUninstallDatas[0].length > 0) { - (address preValidationPlugin,) = FunctionReferenceLib.unpack(preValidationFunction); + (address preValidationPlugin,) = PackedPluginEntityLib.unpack(preValidationFunction); IPlugin(preValidationPlugin).onUninstall(preValidationHookUninstallDatas[0]); } } @@ -122,9 +122,9 @@ abstract contract PluginManager2 { EnumerableSet.Bytes32Set storage permissionHooks = _validationData.permissionHooks; uint256 i = 0; while (permissionHooks.length() > 0) { - FunctionReference permissionHook = toFunctionReference(permissionHooks.at(0)); + PackedPluginEntity permissionHook = toPackedPluginEntity(permissionHooks.at(0)); permissionHooks.remove(toSetValue(permissionHook)); - (address permissionHookPlugin,) = FunctionReferenceLib.unpack(permissionHook); + (address permissionHookPlugin,) = PackedPluginEntityLib.unpack(permissionHook); IPlugin(permissionHookPlugin).onUninstall(permissionHookUninstallDatas[i++]); } } @@ -137,7 +137,7 @@ abstract contract PluginManager2 { } if (uninstallData.length > 0) { - (address plugin,) = FunctionReferenceLib.unpack(validationFunction); + (address plugin,) = PackedPluginEntityLib.unpack(validationFunction); IPlugin(plugin).onUninstall(uninstallData); } } diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 90f9b05b..765991ea 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -5,17 +5,17 @@ import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165C import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; import {IPlugin, ManifestExecutionHook, ManifestValidation, PluginManifest} from "../interfaces/IPlugin.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; import {AccountStorage, getAccountStorage, SelectorData, toSetValue} from "./AccountStorage.sol"; abstract contract PluginManagerInternals is IPluginManager { using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableMap for EnumerableMap.AddressToUintMap; - using FunctionReferenceLib for FunctionReference; + using PackedPluginEntityLib for PackedPluginEntity; error ArrayLengthMismatch(); error Erc4337FunctionNotAllowed(bytes4 selector); @@ -23,13 +23,13 @@ abstract contract PluginManagerInternals is IPluginManager { error InvalidPluginManifest(); error IPluginFunctionNotAllowed(bytes4 selector); error NativeFunctionNotAllowed(bytes4 selector); - error NullFunctionReference(); + error NullPackedPluginEntity(); error NullPlugin(); error PluginAlreadyInstalled(address plugin); error PluginInstallCallbackFailed(address plugin, bytes revertReason); error PluginInterfaceNotSupported(address plugin); error PluginNotInstalled(address plugin); - error ValidationFunctionAlreadySet(bytes4 selector, FunctionReference validationFunction); + error ValidationFunctionAlreadySet(bytes4 selector, PackedPluginEntity validationFunction); // Storage update operations @@ -78,7 +78,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.entityId); + PackedPluginEntity validationFunction = PackedPluginEntityLib.pack(plugin, mv.entityId); if (mv.isDefault) { _storage.validationData[validationFunction].isGlobal = true; @@ -99,7 +99,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - FunctionReference validationFunction = FunctionReferenceLib.pack(plugin, mv.entityId); + PackedPluginEntity validationFunction = PackedPluginEntityLib.pack(plugin, mv.entityId); _storage.validationData[validationFunction].isGlobal = false; _storage.validationData[validationFunction].isSignatureValidation = false; @@ -113,7 +113,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addExecHooks( EnumerableSet.Bytes32Set storage hooks, - FunctionReference hookFunction, + PackedPluginEntity hookFunction, bool isPreExecHook, bool isPostExecHook ) internal { @@ -126,7 +126,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeExecHooks( EnumerableSet.Bytes32Set storage hooks, - FunctionReference hookFunction, + PackedPluginEntity hookFunction, bool isPreExecHook, bool isPostExecHook ) internal { @@ -183,7 +183,7 @@ abstract contract PluginManagerInternals is IPluginManager { for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.entityId); + PackedPluginEntity hookFunction = PackedPluginEntityLib.pack(plugin, mh.entityId); _addExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } @@ -223,7 +223,7 @@ abstract contract PluginManagerInternals is IPluginManager { uint256 length = manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; - FunctionReference hookFunction = FunctionReferenceLib.pack(plugin, mh.entityId); + PackedPluginEntity hookFunction = PackedPluginEntityLib.pack(plugin, mh.entityId); EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; _removeExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 8e7c2ec6..5f745c8d 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -10,7 +10,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {FunctionReferenceLib} from "../helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol"; import {SparseCalldataSegmentLib} from "../helpers/SparseCalldataSegmentLib.sol"; import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol"; @@ -18,7 +18,7 @@ import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol"; import {IValidation} from "../interfaces/IValidation.sol"; import {IValidationHook} from "../interfaces/IValidationHook.sol"; import {IExecutionHook} from "../interfaces/IExecutionHook.sol"; -import {FunctionReference, IPluginManager, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity, IPluginManager, ValidationConfig} from "../interfaces/IPluginManager.sol"; import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol"; import {AccountExecutor} from "./AccountExecutor.sol"; import {AccountLoupe} from "./AccountLoupe.sol"; @@ -41,13 +41,13 @@ contract UpgradeableModularAccount is UUPSUpgradeable { using EnumerableSet for EnumerableSet.Bytes32Set; - using FunctionReferenceLib for FunctionReference; + using PackedPluginEntityLib for PackedPluginEntity; using ValidationConfigLib for ValidationConfig; using SparseCalldataSegmentLib for bytes; struct PostExecToRun { bytes preExecHookReturnData; - FunctionReference postExecHook; + PackedPluginEntity postExecHook; } IEntryPoint private immutable _ENTRY_POINT; @@ -164,7 +164,7 @@ contract UpgradeableModularAccount is revert NotEntryPoint(); } - FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes24(userOp.signature[:24])); + PackedPluginEntity userOpValidationFunction = PackedPluginEntity.wrap(bytes24(userOp.signature[:24])); PostExecToRun[] memory postPermissionHooks = _doPreHooks(getAccountStorage().validationData[userOpValidationFunction].permissionHooks, msg.data); @@ -217,7 +217,7 @@ contract UpgradeableModularAccount is returns (bytes memory) { // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - FunctionReference runtimeValidationFunction = FunctionReference.wrap(bytes24(authorization[:24])); + PackedPluginEntity runtimeValidationFunction = PackedPluginEntity.wrap(bytes24(authorization[:24])); // Check if the runtime validation function is allowed to be called bool isGlobalValidation = uint8(authorization[24]) == 1; @@ -301,7 +301,7 @@ contract UpgradeableModularAccount is /// @inheritdoc IPluginManager /// @notice May be validated by a global validation. function uninstallValidation( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData @@ -341,7 +341,7 @@ contract UpgradeableModularAccount is function isValidSignature(bytes32 hash, bytes calldata signature) public view override returns (bytes4) { AccountStorage storage _storage = getAccountStorage(); - FunctionReference sigValidation = FunctionReference.wrap(bytes24(signature)); + PackedPluginEntity sigValidation = PackedPluginEntity.wrap(bytes24(signature)); (address plugin, uint32 entityId) = sigValidation.unpack(); if (!_storage.validationData[sigValidation].isSignatureValidation) { @@ -375,7 +375,7 @@ contract UpgradeableModularAccount is } // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - FunctionReference userOpValidationFunction = FunctionReference.wrap(bytes24(userOp.signature[:24])); + PackedPluginEntity userOpValidationFunction = PackedPluginEntity.wrap(bytes24(userOp.signature[:24])); bool isGlobalValidation = uint8(userOp.signature[24]) == 1; _checkIfValidationAppliesCallData(userOp.callData, userOpValidationFunction, isGlobalValidation); @@ -396,7 +396,7 @@ contract UpgradeableModularAccount is // To support gas estimation, we don't fail early when the failure is caused by a signature failure function _doUserOpValidation( - FunctionReference userOpValidationFunction, + PackedPluginEntity userOpValidationFunction, PackedUserOperation memory userOp, bytes calldata signature, bytes32 userOpHash @@ -408,7 +408,7 @@ contract UpgradeableModularAccount is uint256 validationData; // Do preUserOpValidation hooks - FunctionReference[] memory preUserOpValidationHooks = + PackedPluginEntity[] memory preUserOpValidationHooks = getAccountStorage().validationData[userOpValidationFunction].preValidationHooks; for (uint256 i = 0; i < preUserOpValidationHooks.length; ++i) { @@ -466,7 +466,7 @@ contract UpgradeableModularAccount is } function _doRuntimeValidation( - FunctionReference runtimeValidationFunction, + PackedPluginEntity runtimeValidationFunction, bytes calldata callData, bytes calldata authorizationData ) internal { @@ -475,7 +475,7 @@ contract UpgradeableModularAccount is (authSegment, authorizationData) = authorizationData.getNextSegment(); // run all preRuntimeValidation hooks - FunctionReference[] memory preRuntimeValidationHooks = + PackedPluginEntity[] memory preRuntimeValidationHooks = getAccountStorage().validationData[runtimeValidationFunction].preValidationHooks; for (uint256 i = 0; i < preRuntimeValidationHooks.length; ++i) { @@ -538,7 +538,7 @@ contract UpgradeableModularAccount is // be sure that the set of hooks to run will not be affected by state changes mid-execution. for (uint256 i = 0; i < hooksLength; ++i) { bytes32 key = executionHooks.at(i); - (FunctionReference hookFunction,, bool isPostHook) = toExecutionHook(key); + (PackedPluginEntity hookFunction,, bool isPostHook) = toExecutionHook(key); if (isPostHook) { postHooksToRun[i].postExecHook = hookFunction; } @@ -548,7 +548,7 @@ contract UpgradeableModularAccount is // exists. for (uint256 i = 0; i < hooksLength; ++i) { bytes32 key = executionHooks.at(i); - (FunctionReference hookFunction, bool isPreHook, bool isPostHook) = toExecutionHook(key); + (PackedPluginEntity hookFunction, bool isPreHook, bool isPostHook) = toExecutionHook(key); if (isPreHook) { bytes memory preExecHookReturnData; @@ -563,7 +563,7 @@ contract UpgradeableModularAccount is } } - function _runPreExecHook(FunctionReference preExecHook, bytes memory data) + function _runPreExecHook(PackedPluginEntity preExecHook, bytes memory data) internal returns (bytes memory preExecHookReturnData) { @@ -606,7 +606,7 @@ contract UpgradeableModularAccount is function _checkIfValidationAppliesCallData( bytes calldata callData, - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bool isGlobal ) internal view { bytes4 outerSelector = bytes4(callData[:4]); @@ -659,7 +659,7 @@ contract UpgradeableModularAccount is function _checkIfValidationAppliesSelector( bytes4 selector, - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bool isGlobal ) internal view { AccountStorage storage _storage = getAccountStorage(); diff --git a/src/helpers/FunctionReferenceLib.sol b/src/helpers/FunctionReferenceLib.sol deleted file mode 100644 index 6e9c4dc6..00000000 --- a/src/helpers/FunctionReferenceLib.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.25; - -import {FunctionReference} from "../interfaces/IPluginManager.sol"; - -library FunctionReferenceLib { - // Empty or unset function reference. - FunctionReference internal constant _EMPTY_FUNCTION_REFERENCE = FunctionReference.wrap(bytes24(0)); - // Magic value for hooks that should always revert. - FunctionReference internal constant _PRE_HOOK_ALWAYS_DENY = FunctionReference.wrap(bytes24(uint192(2))); - - function pack(address addr, uint32 entityId) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); - } - - function unpack(FunctionReference fr) internal pure returns (address addr, uint32 entityId) { - bytes24 underlying = FunctionReference.unwrap(fr); - addr = address(bytes20(underlying)); - entityId = uint32(bytes4(underlying << 160)); - } - - function isEmpty(FunctionReference fr) internal pure returns (bool) { - return FunctionReference.unwrap(fr) == bytes24(0); - } - - function notEmpty(FunctionReference fr) internal pure returns (bool) { - return FunctionReference.unwrap(fr) != bytes24(0); - } - - function eq(FunctionReference a, FunctionReference b) internal pure returns (bool) { - return FunctionReference.unwrap(a) == FunctionReference.unwrap(b); - } - - function notEq(FunctionReference a, FunctionReference b) internal pure returns (bool) { - return FunctionReference.unwrap(a) != FunctionReference.unwrap(b); - } -} diff --git a/src/helpers/PackedPluginEntityLib.sol b/src/helpers/PackedPluginEntityLib.sol new file mode 100644 index 00000000..bff90e80 --- /dev/null +++ b/src/helpers/PackedPluginEntityLib.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25; + +import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; + +library PackedPluginEntityLib { + // Empty or unset PackedPluginEntity. + PackedPluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PackedPluginEntity.wrap(bytes24(0)); + // Magic value for hooks that should always revert. + PackedPluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PackedPluginEntity.wrap(bytes24(uint192(2))); + + function pack(address addr, uint32 entityId) internal pure returns (PackedPluginEntity) { + return PackedPluginEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); + } + + function unpack(PackedPluginEntity fr) internal pure returns (address addr, uint32 entityId) { + bytes24 underlying = PackedPluginEntity.unwrap(fr); + addr = address(bytes20(underlying)); + entityId = uint32(bytes4(underlying << 160)); + } + + function isEmpty(PackedPluginEntity fr) internal pure returns (bool) { + return PackedPluginEntity.unwrap(fr) == bytes24(0); + } + + function notEmpty(PackedPluginEntity fr) internal pure returns (bool) { + return PackedPluginEntity.unwrap(fr) != bytes24(0); + } + + function eq(PackedPluginEntity a, PackedPluginEntity b) internal pure returns (bool) { + return PackedPluginEntity.unwrap(a) == PackedPluginEntity.unwrap(b); + } + + function notEq(PackedPluginEntity a, PackedPluginEntity b) internal pure returns (bool) { + return PackedPluginEntity.unwrap(a) != PackedPluginEntity.unwrap(b); + } +} diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index f2dfacea..c295b96e 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.25; -import {FunctionReference, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; // Validation config is a packed representation of a validation function and flags for its configuration. // Layout: @@ -12,14 +12,14 @@ import {FunctionReference, ValidationConfig} from "../interfaces/IPluginManager. // 0x______________________________________________000000000000000000 // unused library ValidationConfigLib { - function pack(FunctionReference _validationFunction, bool _isGlobal, bool _isSignatureValidation) + function pack(PackedPluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) internal pure returns (ValidationConfig) { return ValidationConfig.wrap( bytes26( - bytes26(FunctionReference.unwrap(_validationFunction)) + bytes26(PackedPluginEntity.unwrap(_validationFunction)) // isGlobal flag stored in the 25th byte | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) // isSignatureValidation flag stored in the 26th byte @@ -62,10 +62,10 @@ library ValidationConfigLib { function unpack(ValidationConfig config) internal pure - returns (FunctionReference _validationFunction, bool _isGlobal, bool _isSignatureValidation) + returns (PackedPluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) { bytes26 configBytes = ValidationConfig.unwrap(config); - _validationFunction = FunctionReference.wrap(bytes24(configBytes)); + _validationFunction = PackedPluginEntity.wrap(bytes24(configBytes)); _isGlobal = uint8(configBytes[24]) == 1; _isSignatureValidation = uint8(configBytes[25]) == 1; } @@ -78,8 +78,8 @@ library ValidationConfigLib { return uint32(bytes4(ValidationConfig.unwrap(config) << 160)); } - function functionReference(ValidationConfig config) internal pure returns (FunctionReference) { - return FunctionReference.wrap(bytes24(ValidationConfig.unwrap(config))); + function functionReference(ValidationConfig config) internal pure returns (PackedPluginEntity) { + return PackedPluginEntity.wrap(bytes24(ValidationConfig.unwrap(config))); } function isGlobal(ValidationConfig config) internal pure returns (bool) { diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index d74c5940..70e408ec 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.25; -import {FunctionReference} from "../interfaces/IPluginManager.sol"; +import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; /// @notice Pre and post hooks for a given selector. /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty. struct ExecutionHook { - FunctionReference hookFunction; + PackedPluginEntity hookFunction; bool isPreHook; bool isPostHook; } @@ -21,7 +21,7 @@ interface IAccountLoupe { /// @notice Get the selectors for a validation function. /// @param validationFunction The validation function to get the selectors for. /// @return The allowed selectors for this validation function. - function getSelectors(FunctionReference validationFunction) external view returns (bytes4[] memory); + function getSelectors(PackedPluginEntity validationFunction) external view returns (bytes4[] memory); /// @notice Get the pre and post execution hooks for a selector. /// @param selector The selector to get the hooks for. @@ -31,7 +31,7 @@ interface IAccountLoupe { /// @notice Get the pre and post execution hooks for a validation function. /// @param validationFunction The validation function to get the hooks for. /// @return The pre and post execution hooks for this validation function. - function getPermissionHooks(FunctionReference validationFunction) + function getPermissionHooks(PackedPluginEntity validationFunction) external view returns (ExecutionHook[] memory); @@ -39,10 +39,10 @@ interface IAccountLoupe { /// @notice Get the pre user op and runtime validation hooks associated with a selector. /// @param validationFunction The validation function to get the hooks for. /// @return preValidationHooks The pre validation hooks for this selector. - function getPreValidationHooks(FunctionReference validationFunction) + function getPreValidationHooks(PackedPluginEntity validationFunction) external view - returns (FunctionReference[] memory preValidationHooks); + returns (PackedPluginEntity[] memory preValidationHooks); /// @notice Get an array of all installed plugins. /// @return The addresses of all installed plugins. diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 78508645..217ff3af 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.25; -type FunctionReference is bytes24; +type PackedPluginEntity is bytes24; type ValidationConfig is bytes26; @@ -45,7 +45,7 @@ interface IPluginManager { /// data /// @param permissionHookUninstallData Optional data to be decoded and used by the plugin to clear account data function uninstallValidation( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index 9c80488c..4d42ef46 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -106,10 +106,10 @@ Plugin manager interface. Modular Smart Contract Accounts **MUST** implement thi ```solidity // Treats the first 20 bytes as an address, and the last byte as a function identifier. -type FunctionReference is bytes21; +type PackedPluginEntity is bytes21; interface IPluginManager { - event PluginInstalled(address indexed plugin, bytes32 manifestHash, FunctionReference[] dependencies); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, PackedPluginEntity[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); @@ -118,13 +118,13 @@ interface IPluginManager { /// @param manifestHash The hash of the plugin manifest. /// @param pluginInstallData Optional data to be decoded and used by the plugin to setup initial plugin data /// for the modular account. - /// @param dependencies The dependencies of the plugin, as described in the manifest. Each FunctionReference + /// @param dependencies The dependencies of the plugin, as described in the manifest. Each PackedPluginEntity /// MUST be composed of an installed plugin's address and a function ID of its validation function. function installPlugin( address plugin, bytes32 manifestHash, bytes calldata pluginInstallData, - FunctionReference[] calldata dependencies + PackedPluginEntity[] calldata dependencies ) external; /// @notice Uninstall a plugin from the modular account. @@ -215,13 +215,13 @@ interface IAccountLoupe { /// @notice Config for an execution function, given a selector. struct ExecutionFunctionConfig { address plugin; - FunctionReference validationFunction; + PackedPluginEntity validationFunction; } /// @notice Pre and post hooks for a given selector. /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty. struct ExecutionHooks { - FunctionReference hookFunction; + PackedPluginEntity hookFunction; bool isPreHook; bool isPostHook; } @@ -243,7 +243,7 @@ interface IAccountLoupe { function getPreValidationHooks(bytes4 selector) external view - returns (FunctionReference[] memory preValidationHooks); + returns (PackedPluginEntity[] memory preValidationHooks); /// @notice Get an array of all installed plugins. /// @return The addresses of all installed plugins. @@ -513,7 +513,7 @@ Additionally, when the modular account natively implements functions in `IPlugin The steps to perform are: - If the call is not from the `EntryPoint`, then find an associated runtime validation function. If one does not exist, execution MUST revert. The modular account MUST execute all pre runtime validation hooks, then the runtime validation function, with the `call` opcode. All of these functions MUST receive the caller, value, and execution function's calldata as parameters. If any of these functions revert, execution MUST revert. If any pre execution hooks are set to `PRE_HOOK_ALWAYS_DENY`, execution MUST revert. If the validation function is set to `RUNTIME_VALIDATION_ALWAYS_ALLOW`, the runtime validation function MUST be bypassed. -- If there are pre execution hooks defined for the execution function, execute those hooks with the caller, value, and execution function's calldata as parameters. If any of these hooks returns data, it MUST be preserved until the call to the post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate pre execution hooks (i.e., hooks with identical `FunctionReference`s), run the hook only once. If any of these functions revert, execution MUST revert. +- If there are pre execution hooks defined for the execution function, execute those hooks with the caller, value, and execution function's calldata as parameters. If any of these hooks returns data, it MUST be preserved until the call to the post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate pre execution hooks (i.e., hooks with identical `PackedPluginEntity`s), run the hook only once. If any of these functions revert, execution MUST revert. - Run the execution function. - If any post execution hooks are defined, run the functions. If a pre execution hook returned data to the account, that data MUST be passed as a parameter to the associated post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate post execution hooks, run them once for each unique associated pre execution hook. For post execution hooks without an associated pre execution hook, run the hook only once. If any of these functions revert, execution MUST revert. diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 9602393f..122b27ba 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {ExecutionHook} from "../../src/interfaces/IAccountLoupe.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; @@ -69,7 +69,7 @@ contract AccountLoupeTest is CustomValidationTestBase { } function test_pluginLoupe_getSelectors() public { - FunctionReference comprehensivePluginValidation = FunctionReferenceLib.pack( + PackedPluginEntity comprehensivePluginValidation = PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) ); @@ -83,21 +83,21 @@ contract AccountLoupeTest is CustomValidationTestBase { ExecutionHook[] memory hooks = account1.getExecutionHooks(comprehensivePlugin.foo.selector); ExecutionHook[3] memory expectedHooks = [ ExecutionHook({ - hookFunction: FunctionReferenceLib.pack( + hookFunction: PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.BOTH_EXECUTION_HOOKS) ), isPreHook: true, isPostHook: true }), ExecutionHook({ - hookFunction: FunctionReferenceLib.pack( + hookFunction: PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_EXECUTION_HOOK) ), isPreHook: true, isPostHook: false }), ExecutionHook({ - hookFunction: FunctionReferenceLib.pack( + hookFunction: PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.POST_EXECUTION_HOOK) ), isPreHook: false, @@ -108,8 +108,8 @@ contract AccountLoupeTest is CustomValidationTestBase { assertEq(hooks.length, 3); for (uint256 i = 0; i < hooks.length; i++) { assertEq( - FunctionReference.unwrap(hooks[i].hookFunction), - FunctionReference.unwrap(expectedHooks[i].hookFunction) + PackedPluginEntity.unwrap(hooks[i].hookFunction), + PackedPluginEntity.unwrap(expectedHooks[i].hookFunction) ); assertEq(hooks[i].isPreHook, expectedHooks[i].isPreHook); assertEq(hooks[i].isPostHook, expectedHooks[i].isPostHook); @@ -117,21 +117,21 @@ contract AccountLoupeTest is CustomValidationTestBase { } function test_pluginLoupe_getValidationHooks() public { - FunctionReference[] memory hooks = account1.getPreValidationHooks(_ownerValidation); + PackedPluginEntity[] memory hooks = account1.getPreValidationHooks(_ownerValidation); assertEq(hooks.length, 2); assertEq( - FunctionReference.unwrap(hooks[0]), - FunctionReference.unwrap( - FunctionReferenceLib.pack( + PackedPluginEntity.unwrap(hooks[0]), + PackedPluginEntity.unwrap( + PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ) ) ); assertEq( - FunctionReference.unwrap(hooks[1]), - FunctionReference.unwrap( - FunctionReferenceLib.pack( + PackedPluginEntity.unwrap(hooks[1]), + PackedPluginEntity.unwrap( + PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ) ) @@ -144,13 +144,13 @@ contract AccountLoupeTest is CustomValidationTestBase { internal virtual override - returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - FunctionReference[] memory preValidationHooks = new FunctionReference[](2); - preValidationHooks[0] = FunctionReferenceLib.pack( + PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](2); + preValidationHooks[0] = PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ); - preValidationHooks[1] = FunctionReferenceLib.pack( + preValidationHooks[1] = PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ); diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index 1738c722..a8e6ffc3 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; import { @@ -58,7 +58,7 @@ contract AccountReturnDataTest is AccountTestBase { (address(regularResultContract), 0, abi.encodeCall(RegularResultContract.foo, ())) ), _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -86,7 +86,7 @@ contract AccountReturnDataTest is AccountTestBase { bytes memory retData = account1.executeWithAuthorization( abi.encodeCall(account1.executeBatch, (calls)), _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) diff --git a/test/account/GlobalValidationTest.t.sol b/test/account/GlobalValidationTest.t.sol index 9f40f806..77160d3a 100644 --- a/test/account/GlobalValidationTest.t.sol +++ b/test/account/GlobalValidationTest.t.sol @@ -5,7 +5,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; @@ -26,7 +26,7 @@ contract GlobalValidationTest is AccountTestBase { account2 = UpgradeableModularAccount(payable(factory.getAddress(owner2, 0))); vm.deal(address(account2), 100 ether); - _ownerValidation = FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _ownerValidation = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); ethRecipient = makeAddr("ethRecipient"); vm.deal(ethRecipient, 1 wei); diff --git a/test/account/MultiValidation.t.sol b/test/account/MultiValidation.t.sol index 5254c50d..f91ced7e 100644 --- a/test/account/MultiValidation.t.sol +++ b/test/account/MultiValidation.t.sol @@ -8,9 +8,9 @@ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/Messa import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {PackedPluginEntity} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; @@ -42,9 +42,9 @@ contract MultiValidationTest is AccountTestBase { "" ); - FunctionReference[] memory validations = new FunctionReference[](2); - validations[0] = FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); - validations[1] = FunctionReferenceLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID); + PackedPluginEntity[] memory validations = new PackedPluginEntity[](2); + validations[0] = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + validations[1] = PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID); bytes4[] memory selectors0 = account1.getSelectors(validations[0]); bytes4[] memory selectors1 = account1.getSelectors(validations[1]); @@ -71,7 +71,7 @@ contract MultiValidationTest is AccountTestBase { account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - FunctionReferenceLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -81,7 +81,7 @@ contract MultiValidationTest is AccountTestBase { account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - FunctionReferenceLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -109,7 +109,7 @@ contract MultiValidationTest is AccountTestBase { bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - FunctionReferenceLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -124,7 +124,7 @@ contract MultiValidationTest is AccountTestBase { userOp.nonce = 1; (v, r, s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - FunctionReferenceLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index 74228643..3f8c3b39 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -6,7 +6,7 @@ import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntry import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {MockAccessControlHookPlugin} from "../mocks/plugins/MockAccessControlHookPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; @@ -325,13 +325,13 @@ contract PerHookDataTest is CustomValidationTestBase { internal virtual override - returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - FunctionReference accessControlHook = FunctionReferenceLib.pack( + PackedPluginEntity accessControlHook = PackedPluginEntityLib.pack( address(_accessControlHookPlugin), uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK) ); - FunctionReference[] memory preValidationHooks = new FunctionReference[](1); + PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); preValidationHooks[0] = accessControlHook; bytes[] memory preValidationHookData = new bytes[](1); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index 708117e2..2eeb1b5b 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -7,7 +7,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; @@ -16,7 +16,7 @@ import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; contract SelfCallAuthorizationTest is AccountTestBase { ComprehensivePlugin public comprehensivePlugin; - FunctionReference public comprehensivePluginValidation; + PackedPluginEntity public comprehensivePluginValidation; function setUp() public { // install the comprehensive plugin to get new exec functions with different validations configured. @@ -27,7 +27,7 @@ contract SelfCallAuthorizationTest is AccountTestBase { vm.prank(address(entryPoint)); account1.installPlugin(address(comprehensivePlugin), manifestHash, ""); - comprehensivePluginValidation = FunctionReferenceLib.pack( + comprehensivePluginValidation = PackedPluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) ); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 5011aad8..222e4df2 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import { @@ -22,26 +22,26 @@ contract ValidationIntersectionTest is AccountTestBase { MockUserOpValidation1HookPlugin public oneHookPlugin; MockUserOpValidation2HookPlugin public twoHookPlugin; - FunctionReference public noHookValidation; - FunctionReference public oneHookValidation; - FunctionReference public twoHookValidation; + PackedPluginEntity public noHookValidation; + PackedPluginEntity public oneHookValidation; + PackedPluginEntity public twoHookValidation; function setUp() public { noHookPlugin = new MockUserOpValidationPlugin(); oneHookPlugin = new MockUserOpValidation1HookPlugin(); twoHookPlugin = new MockUserOpValidation2HookPlugin(); - noHookValidation = FunctionReferenceLib.pack({ + noHookValidation = PackedPluginEntityLib.pack({ addr: address(noHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); - oneHookValidation = FunctionReferenceLib.pack({ + oneHookValidation = PackedPluginEntityLib.pack({ addr: address(oneHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); - twoHookValidation = FunctionReferenceLib.pack({ + twoHookValidation = PackedPluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); @@ -59,8 +59,8 @@ contract ValidationIntersectionTest is AccountTestBase { }); // TODO: change with new install flow // temporary fix to add the pre-validation hook - FunctionReference[] memory preValidationHooks = new FunctionReference[](1); - preValidationHooks[0] = FunctionReferenceLib.pack({ + PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); + preValidationHooks[0] = PackedPluginEntityLib.pack({ addr: address(oneHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); @@ -78,12 +78,12 @@ contract ValidationIntersectionTest is AccountTestBase { pluginInstallData: "" }); // temporary fix to add the pre-validation hook - preValidationHooks = new FunctionReference[](2); - preValidationHooks[0] = FunctionReferenceLib.pack({ + preValidationHooks = new PackedPluginEntity[](2); + preValidationHooks[0] = PackedPluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); - preValidationHooks[1] = FunctionReferenceLib.pack({ + preValidationHooks[1] = PackedPluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_2) }); diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 4ecfb0e9..5b9a66c3 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -3,29 +3,29 @@ pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; -import {FunctionReference} from "../../src/interfaces/IPluginManager.sol"; +import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PackedPluginEntity} from "../../src/interfaces/IPluginManager.sol"; -contract FunctionReferenceLibTest is Test { - using FunctionReferenceLib for FunctionReference; +contract PackedPluginEntityLibTest is Test { + using PackedPluginEntityLib for PackedPluginEntity; function testFuzz_functionReference_packing(address addr, uint32 entityId) public { // console.log("addr: ", addr); // console.log("entityId: ", vm.toString(entityId)); - FunctionReference fr = FunctionReferenceLib.pack(addr, entityId); - // console.log("packed: ", vm.toString(FunctionReference.unwrap(fr))); - (address addr2, uint32 entityId2) = FunctionReferenceLib.unpack(fr); + PackedPluginEntity fr = PackedPluginEntityLib.pack(addr, entityId); + // console.log("packed: ", vm.toString(PackedPluginEntity.unwrap(fr))); + (address addr2, uint32 entityId2) = PackedPluginEntityLib.unpack(fr); // console.log("addr2: ", addr2); // console.log("entityId2: ", vm.toString(entityId2)); assertEq(addr, addr2); assertEq(entityId, entityId2); } - function testFuzz_functionReference_operators(FunctionReference a, FunctionReference b) public { + function testFuzz_functionReference_operators(PackedPluginEntity a, PackedPluginEntity b) public { assertTrue(a.eq(a)); assertTrue(b.eq(b)); - if (FunctionReference.unwrap(a) == FunctionReference.unwrap(b)) { + if (PackedPluginEntity.unwrap(a) == PackedPluginEntity.unwrap(b)) { assertTrue(a.eq(b)); assertTrue(b.eq(a)); assertFalse(a.notEq(b)); diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index 4bf5f60e..ea10666d 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.25; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {AllowlistPlugin} from "../../src/samples/permissionhooks/AllowlistPlugin.sol"; @@ -290,13 +290,13 @@ contract AllowlistPluginTest is CustomValidationTestBase { internal virtual override - returns (FunctionReference, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - FunctionReference accessControlHook = FunctionReferenceLib.pack( + PackedPluginEntity accessControlHook = PackedPluginEntityLib.pack( address(allowlistPlugin), uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK) ); - FunctionReference[] memory preValidationHooks = new FunctionReference[](1); + PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); preValidationHooks[0] = accessControlHook; bytes[] memory preValidationHookData = new bytes[](1); diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index 75123567..347705eb 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -5,7 +5,7 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import {FunctionReference, FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; @@ -18,7 +18,7 @@ import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; /// @dev This contract handles common boilerplate setup for tests using UpgradeableModularAccount with /// SingleOwnerPlugin. abstract contract AccountTestBase is OptimizedTest { - using FunctionReferenceLib for FunctionReference; + using PackedPluginEntityLib for PackedPluginEntity; using MessageHashUtils for bytes32; EntryPoint public entryPoint; @@ -30,7 +30,7 @@ abstract contract AccountTestBase is OptimizedTest { uint256 public owner1Key; UpgradeableModularAccount public account1; - FunctionReference internal _ownerValidation; + PackedPluginEntity internal _ownerValidation; uint8 public constant SELECTOR_ASSOCIATED_VALIDATION = 0; uint8 public constant GLOBAL_VALIDATION = 1; @@ -57,7 +57,7 @@ abstract contract AccountTestBase is OptimizedTest { account1 = factory.createAccount(owner1, 0); vm.deal(address(account1), 100 ether); - _ownerValidation = FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _ownerValidation = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); } function _runExecUserOp(address target, bytes memory callData) internal { @@ -100,7 +100,7 @@ abstract contract AccountTestBase is OptimizedTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -153,7 +153,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -168,7 +168,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -190,7 +190,7 @@ abstract contract AccountTestBase is OptimizedTest { ) ), _encodeSignature( - FunctionReferenceLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -204,7 +204,7 @@ abstract contract AccountTestBase is OptimizedTest { // helper function to encode a signature, according to the per-hook and per-validation data format. function _encodeSignature( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, uint8 globalOrNot, PreValidationHookData[] memory preValidationHookData, bytes memory validationData @@ -228,11 +228,11 @@ abstract contract AccountTestBase is OptimizedTest { } // overload for the case where there are no pre-validation hooks - function _encodeSignature(FunctionReference validationFunction, uint8 globalOrNot, bytes memory validationData) - internal - pure - returns (bytes memory) - { + function _encodeSignature( + PackedPluginEntity validationFunction, + uint8 globalOrNot, + bytes memory validationData + ) internal pure returns (bytes memory) { PreValidationHookData[] memory emptyPreValidationHookData = new PreValidationHookData[](0); return _encodeSignature(validationFunction, globalOrNot, emptyPreValidationHookData, validationData); } diff --git a/test/utils/CustomValidationTestBase.sol b/test/utils/CustomValidationTestBase.sol index 2244c865..ff7feced 100644 --- a/test/utils/CustomValidationTestBase.sol +++ b/test/utils/CustomValidationTestBase.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.25; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {FunctionReference} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PackedPluginEntity} from "../../src/helpers/PackedPluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; @@ -16,7 +16,7 @@ import {AccountTestBase} from "./AccountTestBase.sol"; abstract contract CustomValidationTestBase is AccountTestBase { function _customValidationSetup() internal { ( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bool isGlobal, bool isSignatureValidation, bytes4[] memory selectors, @@ -44,7 +44,7 @@ abstract contract CustomValidationTestBase is AccountTestBase { internal virtual returns ( - FunctionReference validationFunction, + PackedPluginEntity validationFunction, bool shared, bool isSignatureValidation, bytes4[] memory selectors, From e957d0c112086c3676ef1f6307c21f2235046e5c Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Fri, 12 Jul 2024 09:21:49 -0700 Subject: [PATCH 05/11] rename PackedPluginEntity to PluginEntity --- src/account/AccountLoupe.sol | 10 +++--- src/account/AccountStorage.sol | 29 +++++++-------- src/account/PluginManager2.sol | 38 ++++++++++---------- src/account/PluginManagerInternals.sol | 22 ++++++------ src/account/UpgradeableModularAccount.sol | 43 +++++++++++------------ src/helpers/PackedPluginEntityLib.sol | 34 +++++++++--------- src/helpers/PluginEntityLib.sol | 37 +++++++++++++++++++ src/helpers/ValidationConfigLib.sol | 14 ++++---- src/interfaces/IAccountLoupe.sol | 15 ++++---- src/interfaces/IPluginManager.sol | 4 +-- standard/ERCs/erc-6900.md | 16 ++++----- test/account/AccountLoupe.t.sol | 38 ++++++++++---------- test/account/AccountReturnData.t.sol | 6 ++-- test/account/GlobalValidationTest.t.sol | 4 +-- test/account/MultiValidation.t.sol | 22 +++++------- test/account/PerHookData.t.sol | 8 ++--- test/account/SelfCallAuthorization.t.sol | 9 +++-- test/account/ValidationIntersection.t.sol | 24 ++++++------- test/libraries/FunctionReferenceLib.t.sol | 18 +++++----- test/samples/AllowlistPlugin.t.sol | 11 +++--- test/utils/AccountTestBase.sol | 28 +++++++-------- test/utils/CustomValidationTestBase.sol | 6 ++-- 22 files changed, 229 insertions(+), 207 deletions(-) create mode 100644 src/helpers/PluginEntityLib.sol diff --git a/src/account/AccountLoupe.sol b/src/account/AccountLoupe.sol index 8817f4b7..cca17149 100644 --- a/src/account/AccountLoupe.sol +++ b/src/account/AccountLoupe.sol @@ -6,7 +6,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {IAccountLoupe, ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {PackedPluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; +import {PluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol"; import {getAccountStorage, toExecutionHook, toSelector} from "./AccountStorage.sol"; @@ -29,7 +29,7 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getSelectors(PackedPluginEntity validationFunction) external view returns (bytes4[] memory) { + function getSelectors(PluginEntity validationFunction) external view returns (bytes4[] memory) { uint256 length = getAccountStorage().validationData[validationFunction].selectors.length(); bytes4[] memory selectors = new bytes4[](length); @@ -61,7 +61,7 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getPermissionHooks(PackedPluginEntity validationFunction) + function getPermissionHooks(PluginEntity validationFunction) external view override @@ -79,11 +79,11 @@ abstract contract AccountLoupe is IAccountLoupe { } /// @inheritdoc IAccountLoupe - function getPreValidationHooks(PackedPluginEntity validationFunction) + function getPreValidationHooks(PluginEntity validationFunction) external view override - returns (PackedPluginEntity[] memory preValidationHooks) + returns (PluginEntity[] memory preValidationHooks) { preValidationHooks = getAccountStorage().validationData[validationFunction].preValidationHooks; } diff --git a/src/account/AccountStorage.sol b/src/account/AccountStorage.sol index 16aadfdb..01cfd7ba 100644 --- a/src/account/AccountStorage.sol +++ b/src/account/AccountStorage.sol @@ -5,7 +5,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; +import {PluginEntity} from "../interfaces/IPluginManager.sol"; // bytes = keccak256("ERC6900.UpgradeableModularAccount.Storage") bytes32 constant _ACCOUNT_STORAGE_SLOT = 0x9f09680beaa4e5c9f38841db2460c401499164f368baef687948c315d9073e40; @@ -31,7 +31,7 @@ struct ValidationData { // Whether or not this validation is a signature validator. bool isSignatureValidation; // The pre validation hooks for this function selector. - PackedPluginEntity[] preValidationHooks; + PluginEntity[] preValidationHooks; // Permission hooks for this validation function. EnumerableSet.Bytes32Set permissionHooks; // The set of selectors that may be validated by this validation function. @@ -46,7 +46,7 @@ struct AccountStorage { EnumerableMap.AddressToUintMap pluginManifestHashes; // Execution functions and their associated functions mapping(bytes4 => SelectorData) selectorData; - mapping(PackedPluginEntity validationFunction => ValidationData) validationData; + mapping(PluginEntity validationFunction => ValidationData) validationData; // For ERC165 introspection mapping(bytes4 => uint256) supportedIfaces; } @@ -59,12 +59,12 @@ function getAccountStorage() pure returns (AccountStorage storage _storage) { using EnumerableSet for EnumerableSet.Bytes32Set; -function toSetValue(PackedPluginEntity functionReference) pure returns (bytes32) { - return bytes32(PackedPluginEntity.unwrap(functionReference)); +function toSetValue(PluginEntity functionReference) pure returns (bytes32) { + return bytes32(PluginEntity.unwrap(functionReference)); } -function toPackedPluginEntity(bytes32 setValue) pure returns (PackedPluginEntity) { - return PackedPluginEntity.wrap(bytes24(setValue)); +function toPluginEntity(bytes32 setValue) pure returns (PluginEntity) { + return PluginEntity.wrap(bytes24(setValue)); } // ExecutionHook layout: @@ -73,16 +73,16 @@ function toPackedPluginEntity(bytes32 setValue) pure returns (PackedPluginEntity // 0x____________________________________________BB__________________ is post hook function toSetValue(ExecutionHook memory executionHook) pure returns (bytes32) { - return bytes32(PackedPluginEntity.unwrap(executionHook.hookFunction)) + return bytes32(PluginEntity.unwrap(executionHook.hookFunction)) | bytes32(executionHook.isPreHook ? uint256(1) << 56 : 0) | bytes32(executionHook.isPostHook ? uint256(1) << 48 : 0); } function toExecutionHook(bytes32 setValue) pure - returns (PackedPluginEntity hookFunction, bool isPreHook, bool isPostHook) + returns (PluginEntity hookFunction, bool isPreHook, bool isPostHook) { - hookFunction = PackedPluginEntity.wrap(bytes24(setValue)); + hookFunction = PluginEntity.wrap(bytes24(setValue)); isPreHook = (uint256(setValue) >> 56) & 0xFF == 1; isPostHook = (uint256(setValue) >> 48) & 0xFF == 1; } @@ -96,15 +96,12 @@ function toSelector(bytes32 setValue) pure returns (bytes4) { } /// @dev Helper function to get all elements of a set into memory. -function toPackedPluginEntityArray(EnumerableSet.Bytes32Set storage set) - view - returns (PackedPluginEntity[] memory) -{ +function toPluginEntityArray(EnumerableSet.Bytes32Set storage set) view returns (PluginEntity[] memory) { uint256 length = set.length(); - PackedPluginEntity[] memory result = new PackedPluginEntity[](length); + PluginEntity[] memory result = new PluginEntity[](length); for (uint256 i = 0; i < length; ++i) { bytes32 key = set.at(i); - result[i] = PackedPluginEntity.wrap(bytes24(key)); + result[i] = PluginEntity.wrap(bytes24(key)); } return result; } diff --git a/src/account/PluginManager2.sol b/src/account/PluginManager2.sol index c1dae3ff..1f121880 100644 --- a/src/account/PluginManager2.sol +++ b/src/account/PluginManager2.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.25; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IPlugin} from "../interfaces/IPlugin.sol"; -import {PackedPluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; -import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PluginEntityLib} from "../helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol"; -import {ValidationData, getAccountStorage, toSetValue, toPackedPluginEntity} from "./AccountStorage.sol"; +import {ValidationData, getAccountStorage, toSetValue, toPluginEntity} from "./AccountStorage.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; // Temporary additional functions for a user-controlled install flow for validation functions. @@ -18,10 +18,10 @@ abstract contract PluginManager2 { // Index marking the start of the data for the validation function. uint8 internal constant _RESERVED_VALIDATION_DATA_INDEX = 255; - error PreValidationAlreadySet(PackedPluginEntity validationFunction, PackedPluginEntity preValidationFunction); - error ValidationAlreadySet(bytes4 selector, PackedPluginEntity validationFunction); - error ValidationNotSet(bytes4 selector, PackedPluginEntity validationFunction); - error PermissionAlreadySet(PackedPluginEntity validationFunction, ExecutionHook hook); + error PreValidationAlreadySet(PluginEntity validationFunction, PluginEntity preValidationFunction); + error ValidationAlreadySet(bytes4 selector, PluginEntity validationFunction); + error ValidationNotSet(bytes4 selector, PluginEntity validationFunction); + error PermissionAlreadySet(PluginEntity validationFunction, ExecutionHook hook); error PreValidationHookLimitExceeded(); function _installValidation( @@ -35,16 +35,16 @@ abstract contract PluginManager2 { getAccountStorage().validationData[validationConfig.functionReference()]; if (preValidationHooks.length > 0) { - (PackedPluginEntity[] memory preValidationFunctions, bytes[] memory initDatas) = - abi.decode(preValidationHooks, (PackedPluginEntity[], bytes[])); + (PluginEntity[] memory preValidationFunctions, bytes[] memory initDatas) = + abi.decode(preValidationHooks, (PluginEntity[], bytes[])); for (uint256 i = 0; i < preValidationFunctions.length; ++i) { - PackedPluginEntity preValidationFunction = preValidationFunctions[i]; + PluginEntity preValidationFunction = preValidationFunctions[i]; _validationData.preValidationHooks.push(preValidationFunction); if (initDatas[i].length > 0) { - (address preValidationPlugin,) = PackedPluginEntityLib.unpack(preValidationFunction); + (address preValidationPlugin,) = PluginEntityLib.unpack(preValidationFunction); IPlugin(preValidationPlugin).onInstall(initDatas[i]); } } @@ -67,7 +67,7 @@ abstract contract PluginManager2 { } if (initDatas[i].length > 0) { - (address executionPlugin,) = PackedPluginEntityLib.unpack(permissionFunction.hookFunction); + (address executionPlugin,) = PluginEntityLib.unpack(permissionFunction.hookFunction); IPlugin(executionPlugin).onInstall(initDatas[i]); } } @@ -90,7 +90,7 @@ abstract contract PluginManager2 { } function _uninstallValidation( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData @@ -104,11 +104,11 @@ abstract contract PluginManager2 { bytes[] memory preValidationHookUninstallDatas = abi.decode(preValidationHookUninstallData, (bytes[])); // Clear pre validation hooks - PackedPluginEntity[] storage preValidationHooks = _validationData.preValidationHooks; + PluginEntity[] storage preValidationHooks = _validationData.preValidationHooks; for (uint256 i = 0; i < preValidationHooks.length; ++i) { - PackedPluginEntity preValidationFunction = preValidationHooks[i]; + PluginEntity preValidationFunction = preValidationHooks[i]; if (preValidationHookUninstallDatas[0].length > 0) { - (address preValidationPlugin,) = PackedPluginEntityLib.unpack(preValidationFunction); + (address preValidationPlugin,) = PluginEntityLib.unpack(preValidationFunction); IPlugin(preValidationPlugin).onUninstall(preValidationHookUninstallDatas[0]); } } @@ -122,9 +122,9 @@ abstract contract PluginManager2 { EnumerableSet.Bytes32Set storage permissionHooks = _validationData.permissionHooks; uint256 i = 0; while (permissionHooks.length() > 0) { - PackedPluginEntity permissionHook = toPackedPluginEntity(permissionHooks.at(0)); + PluginEntity permissionHook = toPluginEntity(permissionHooks.at(0)); permissionHooks.remove(toSetValue(permissionHook)); - (address permissionHookPlugin,) = PackedPluginEntityLib.unpack(permissionHook); + (address permissionHookPlugin,) = PluginEntityLib.unpack(permissionHook); IPlugin(permissionHookPlugin).onUninstall(permissionHookUninstallDatas[i++]); } } @@ -137,7 +137,7 @@ abstract contract PluginManager2 { } if (uninstallData.length > 0) { - (address plugin,) = PackedPluginEntityLib.unpack(validationFunction); + (address plugin,) = PluginEntityLib.unpack(validationFunction); IPlugin(plugin).onUninstall(uninstallData); } } diff --git a/src/account/PluginManagerInternals.sol b/src/account/PluginManagerInternals.sol index 765991ea..ccc4c68b 100644 --- a/src/account/PluginManagerInternals.sol +++ b/src/account/PluginManagerInternals.sol @@ -5,17 +5,17 @@ import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165C import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; +import {PluginEntityLib} from "../helpers/PluginEntityLib.sol"; import {IPlugin, ManifestExecutionHook, ManifestValidation, PluginManifest} from "../interfaces/IPlugin.sol"; import {ExecutionHook} from "../interfaces/IAccountLoupe.sol"; -import {PackedPluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; +import {PluginEntity, IPluginManager} from "../interfaces/IPluginManager.sol"; import {KnownSelectors} from "../helpers/KnownSelectors.sol"; import {AccountStorage, getAccountStorage, SelectorData, toSetValue} from "./AccountStorage.sol"; abstract contract PluginManagerInternals is IPluginManager { using EnumerableSet for EnumerableSet.Bytes32Set; using EnumerableMap for EnumerableMap.AddressToUintMap; - using PackedPluginEntityLib for PackedPluginEntity; + using PluginEntityLib for PluginEntity; error ArrayLengthMismatch(); error Erc4337FunctionNotAllowed(bytes4 selector); @@ -23,13 +23,13 @@ abstract contract PluginManagerInternals is IPluginManager { error InvalidPluginManifest(); error IPluginFunctionNotAllowed(bytes4 selector); error NativeFunctionNotAllowed(bytes4 selector); - error NullPackedPluginEntity(); + error NullPluginEntity(); error NullPlugin(); error PluginAlreadyInstalled(address plugin); error PluginInstallCallbackFailed(address plugin, bytes revertReason); error PluginInterfaceNotSupported(address plugin); error PluginNotInstalled(address plugin); - error ValidationFunctionAlreadySet(bytes4 selector, PackedPluginEntity validationFunction); + error ValidationFunctionAlreadySet(bytes4 selector, PluginEntity validationFunction); // Storage update operations @@ -78,7 +78,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - PackedPluginEntity validationFunction = PackedPluginEntityLib.pack(plugin, mv.entityId); + PluginEntity validationFunction = PluginEntityLib.pack(plugin, mv.entityId); if (mv.isDefault) { _storage.validationData[validationFunction].isGlobal = true; @@ -99,7 +99,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeValidationFunction(address plugin, ManifestValidation memory mv) internal { AccountStorage storage _storage = getAccountStorage(); - PackedPluginEntity validationFunction = PackedPluginEntityLib.pack(plugin, mv.entityId); + PluginEntity validationFunction = PluginEntityLib.pack(plugin, mv.entityId); _storage.validationData[validationFunction].isGlobal = false; _storage.validationData[validationFunction].isSignatureValidation = false; @@ -113,7 +113,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _addExecHooks( EnumerableSet.Bytes32Set storage hooks, - PackedPluginEntity hookFunction, + PluginEntity hookFunction, bool isPreExecHook, bool isPostExecHook ) internal { @@ -126,7 +126,7 @@ abstract contract PluginManagerInternals is IPluginManager { function _removeExecHooks( EnumerableSet.Bytes32Set storage hooks, - PackedPluginEntity hookFunction, + PluginEntity hookFunction, bool isPreExecHook, bool isPostExecHook ) internal { @@ -183,7 +183,7 @@ abstract contract PluginManagerInternals is IPluginManager { for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; - PackedPluginEntity hookFunction = PackedPluginEntityLib.pack(plugin, mh.entityId); + PluginEntity hookFunction = PluginEntityLib.pack(plugin, mh.entityId); _addExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } @@ -223,7 +223,7 @@ abstract contract PluginManagerInternals is IPluginManager { uint256 length = manifest.executionHooks.length; for (uint256 i = 0; i < length; ++i) { ManifestExecutionHook memory mh = manifest.executionHooks[i]; - PackedPluginEntity hookFunction = PackedPluginEntityLib.pack(plugin, mh.entityId); + PluginEntity hookFunction = PluginEntityLib.pack(plugin, mh.entityId); EnumerableSet.Bytes32Set storage execHooks = _storage.selectorData[mh.executionSelector].executionHooks; _removeExecHooks(execHooks, hookFunction, mh.isPreHook, mh.isPostHook); } diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index 5f745c8d..da3ababb 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -10,7 +10,7 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {PackedPluginEntityLib} from "../helpers/PackedPluginEntityLib.sol"; +import {PluginEntityLib} from "../helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol"; import {SparseCalldataSegmentLib} from "../helpers/SparseCalldataSegmentLib.sol"; import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol"; @@ -18,7 +18,7 @@ import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol"; import {IValidation} from "../interfaces/IValidation.sol"; import {IValidationHook} from "../interfaces/IValidationHook.sol"; import {IExecutionHook} from "../interfaces/IExecutionHook.sol"; -import {PackedPluginEntity, IPluginManager, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PluginEntity, IPluginManager, ValidationConfig} from "../interfaces/IPluginManager.sol"; import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol"; import {AccountExecutor} from "./AccountExecutor.sol"; import {AccountLoupe} from "./AccountLoupe.sol"; @@ -41,13 +41,13 @@ contract UpgradeableModularAccount is UUPSUpgradeable { using EnumerableSet for EnumerableSet.Bytes32Set; - using PackedPluginEntityLib for PackedPluginEntity; + using PluginEntityLib for PluginEntity; using ValidationConfigLib for ValidationConfig; using SparseCalldataSegmentLib for bytes; struct PostExecToRun { bytes preExecHookReturnData; - PackedPluginEntity postExecHook; + PluginEntity postExecHook; } IEntryPoint private immutable _ENTRY_POINT; @@ -164,7 +164,7 @@ contract UpgradeableModularAccount is revert NotEntryPoint(); } - PackedPluginEntity userOpValidationFunction = PackedPluginEntity.wrap(bytes24(userOp.signature[:24])); + PluginEntity userOpValidationFunction = PluginEntity.wrap(bytes24(userOp.signature[:24])); PostExecToRun[] memory postPermissionHooks = _doPreHooks(getAccountStorage().validationData[userOpValidationFunction].permissionHooks, msg.data); @@ -217,7 +217,7 @@ contract UpgradeableModularAccount is returns (bytes memory) { // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - PackedPluginEntity runtimeValidationFunction = PackedPluginEntity.wrap(bytes24(authorization[:24])); + PluginEntity runtimeValidationFunction = PluginEntity.wrap(bytes24(authorization[:24])); // Check if the runtime validation function is allowed to be called bool isGlobalValidation = uint8(authorization[24]) == 1; @@ -301,7 +301,7 @@ contract UpgradeableModularAccount is /// @inheritdoc IPluginManager /// @notice May be validated by a global validation. function uninstallValidation( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData @@ -341,7 +341,7 @@ contract UpgradeableModularAccount is function isValidSignature(bytes32 hash, bytes calldata signature) public view override returns (bytes4) { AccountStorage storage _storage = getAccountStorage(); - PackedPluginEntity sigValidation = PackedPluginEntity.wrap(bytes24(signature)); + PluginEntity sigValidation = PluginEntity.wrap(bytes24(signature)); (address plugin, uint32 entityId) = sigValidation.unpack(); if (!_storage.validationData[sigValidation].isSignatureValidation) { @@ -375,7 +375,7 @@ contract UpgradeableModularAccount is } // Revert if the provided `authorization` less than 21 bytes long, rather than right-padding. - PackedPluginEntity userOpValidationFunction = PackedPluginEntity.wrap(bytes24(userOp.signature[:24])); + PluginEntity userOpValidationFunction = PluginEntity.wrap(bytes24(userOp.signature[:24])); bool isGlobalValidation = uint8(userOp.signature[24]) == 1; _checkIfValidationAppliesCallData(userOp.callData, userOpValidationFunction, isGlobalValidation); @@ -396,7 +396,7 @@ contract UpgradeableModularAccount is // To support gas estimation, we don't fail early when the failure is caused by a signature failure function _doUserOpValidation( - PackedPluginEntity userOpValidationFunction, + PluginEntity userOpValidationFunction, PackedUserOperation memory userOp, bytes calldata signature, bytes32 userOpHash @@ -408,7 +408,7 @@ contract UpgradeableModularAccount is uint256 validationData; // Do preUserOpValidation hooks - PackedPluginEntity[] memory preUserOpValidationHooks = + PluginEntity[] memory preUserOpValidationHooks = getAccountStorage().validationData[userOpValidationFunction].preValidationHooks; for (uint256 i = 0; i < preUserOpValidationHooks.length; ++i) { @@ -466,7 +466,7 @@ contract UpgradeableModularAccount is } function _doRuntimeValidation( - PackedPluginEntity runtimeValidationFunction, + PluginEntity runtimeValidationFunction, bytes calldata callData, bytes calldata authorizationData ) internal { @@ -475,7 +475,7 @@ contract UpgradeableModularAccount is (authSegment, authorizationData) = authorizationData.getNextSegment(); // run all preRuntimeValidation hooks - PackedPluginEntity[] memory preRuntimeValidationHooks = + PluginEntity[] memory preRuntimeValidationHooks = getAccountStorage().validationData[runtimeValidationFunction].preValidationHooks; for (uint256 i = 0; i < preRuntimeValidationHooks.length; ++i) { @@ -538,7 +538,7 @@ contract UpgradeableModularAccount is // be sure that the set of hooks to run will not be affected by state changes mid-execution. for (uint256 i = 0; i < hooksLength; ++i) { bytes32 key = executionHooks.at(i); - (PackedPluginEntity hookFunction,, bool isPostHook) = toExecutionHook(key); + (PluginEntity hookFunction,, bool isPostHook) = toExecutionHook(key); if (isPostHook) { postHooksToRun[i].postExecHook = hookFunction; } @@ -548,7 +548,7 @@ contract UpgradeableModularAccount is // exists. for (uint256 i = 0; i < hooksLength; ++i) { bytes32 key = executionHooks.at(i); - (PackedPluginEntity hookFunction, bool isPreHook, bool isPostHook) = toExecutionHook(key); + (PluginEntity hookFunction, bool isPreHook, bool isPostHook) = toExecutionHook(key); if (isPreHook) { bytes memory preExecHookReturnData; @@ -563,7 +563,7 @@ contract UpgradeableModularAccount is } } - function _runPreExecHook(PackedPluginEntity preExecHook, bytes memory data) + function _runPreExecHook(PluginEntity preExecHook, bytes memory data) internal returns (bytes memory preExecHookReturnData) { @@ -606,7 +606,7 @@ contract UpgradeableModularAccount is function _checkIfValidationAppliesCallData( bytes calldata callData, - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bool isGlobal ) internal view { bytes4 outerSelector = bytes4(callData[:4]); @@ -657,11 +657,10 @@ contract UpgradeableModularAccount is } } - function _checkIfValidationAppliesSelector( - bytes4 selector, - PackedPluginEntity validationFunction, - bool isGlobal - ) internal view { + function _checkIfValidationAppliesSelector(bytes4 selector, PluginEntity validationFunction, bool isGlobal) + internal + view + { AccountStorage storage _storage = getAccountStorage(); // Check that the provided validation function is applicable to the selector diff --git a/src/helpers/PackedPluginEntityLib.sol b/src/helpers/PackedPluginEntityLib.sol index bff90e80..5395a396 100644 --- a/src/helpers/PackedPluginEntityLib.sol +++ b/src/helpers/PackedPluginEntityLib.sol @@ -1,37 +1,37 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.25; -import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; +import {PluginEntity} from "../interfaces/IPluginManager.sol"; -library PackedPluginEntityLib { - // Empty or unset PackedPluginEntity. - PackedPluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PackedPluginEntity.wrap(bytes24(0)); +library PluginEntityLib { + // Empty or unset PluginEntity. + PluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PluginEntity.wrap(bytes24(0)); // Magic value for hooks that should always revert. - PackedPluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PackedPluginEntity.wrap(bytes24(uint192(2))); + PluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PluginEntity.wrap(bytes24(uint192(2))); - function pack(address addr, uint32 entityId) internal pure returns (PackedPluginEntity) { - return PackedPluginEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); + function pack(address addr, uint32 entityId) internal pure returns (PluginEntity) { + return PluginEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); } - function unpack(PackedPluginEntity fr) internal pure returns (address addr, uint32 entityId) { - bytes24 underlying = PackedPluginEntity.unwrap(fr); + function unpack(PluginEntity fr) internal pure returns (address addr, uint32 entityId) { + bytes24 underlying = PluginEntity.unwrap(fr); addr = address(bytes20(underlying)); entityId = uint32(bytes4(underlying << 160)); } - function isEmpty(PackedPluginEntity fr) internal pure returns (bool) { - return PackedPluginEntity.unwrap(fr) == bytes24(0); + function isEmpty(PluginEntity fr) internal pure returns (bool) { + return PluginEntity.unwrap(fr) == bytes24(0); } - function notEmpty(PackedPluginEntity fr) internal pure returns (bool) { - return PackedPluginEntity.unwrap(fr) != bytes24(0); + function notEmpty(PluginEntity fr) internal pure returns (bool) { + return PluginEntity.unwrap(fr) != bytes24(0); } - function eq(PackedPluginEntity a, PackedPluginEntity b) internal pure returns (bool) { - return PackedPluginEntity.unwrap(a) == PackedPluginEntity.unwrap(b); + function eq(PluginEntity a, PluginEntity b) internal pure returns (bool) { + return PluginEntity.unwrap(a) == PluginEntity.unwrap(b); } - function notEq(PackedPluginEntity a, PackedPluginEntity b) internal pure returns (bool) { - return PackedPluginEntity.unwrap(a) != PackedPluginEntity.unwrap(b); + function notEq(PluginEntity a, PluginEntity b) internal pure returns (bool) { + return PluginEntity.unwrap(a) != PluginEntity.unwrap(b); } } diff --git a/src/helpers/PluginEntityLib.sol b/src/helpers/PluginEntityLib.sol new file mode 100644 index 00000000..5395a396 --- /dev/null +++ b/src/helpers/PluginEntityLib.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25; + +import {PluginEntity} from "../interfaces/IPluginManager.sol"; + +library PluginEntityLib { + // Empty or unset PluginEntity. + PluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PluginEntity.wrap(bytes24(0)); + // Magic value for hooks that should always revert. + PluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PluginEntity.wrap(bytes24(uint192(2))); + + function pack(address addr, uint32 entityId) internal pure returns (PluginEntity) { + return PluginEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); + } + + function unpack(PluginEntity fr) internal pure returns (address addr, uint32 entityId) { + bytes24 underlying = PluginEntity.unwrap(fr); + addr = address(bytes20(underlying)); + entityId = uint32(bytes4(underlying << 160)); + } + + function isEmpty(PluginEntity fr) internal pure returns (bool) { + return PluginEntity.unwrap(fr) == bytes24(0); + } + + function notEmpty(PluginEntity fr) internal pure returns (bool) { + return PluginEntity.unwrap(fr) != bytes24(0); + } + + function eq(PluginEntity a, PluginEntity b) internal pure returns (bool) { + return PluginEntity.unwrap(a) == PluginEntity.unwrap(b); + } + + function notEq(PluginEntity a, PluginEntity b) internal pure returns (bool) { + return PluginEntity.unwrap(a) != PluginEntity.unwrap(b); + } +} diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index c295b96e..e3558f3c 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.25; -import {PackedPluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; +import {PluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; // Validation config is a packed representation of a validation function and flags for its configuration. // Layout: @@ -12,14 +12,14 @@ import {PackedPluginEntity, ValidationConfig} from "../interfaces/IPluginManager // 0x______________________________________________000000000000000000 // unused library ValidationConfigLib { - function pack(PackedPluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) + function pack(PluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) internal pure returns (ValidationConfig) { return ValidationConfig.wrap( bytes26( - bytes26(PackedPluginEntity.unwrap(_validationFunction)) + bytes26(PluginEntity.unwrap(_validationFunction)) // isGlobal flag stored in the 25th byte | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) // isSignatureValidation flag stored in the 26th byte @@ -62,10 +62,10 @@ library ValidationConfigLib { function unpack(ValidationConfig config) internal pure - returns (PackedPluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) + returns (PluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) { bytes26 configBytes = ValidationConfig.unwrap(config); - _validationFunction = PackedPluginEntity.wrap(bytes24(configBytes)); + _validationFunction = PluginEntity.wrap(bytes24(configBytes)); _isGlobal = uint8(configBytes[24]) == 1; _isSignatureValidation = uint8(configBytes[25]) == 1; } @@ -78,8 +78,8 @@ library ValidationConfigLib { return uint32(bytes4(ValidationConfig.unwrap(config) << 160)); } - function functionReference(ValidationConfig config) internal pure returns (PackedPluginEntity) { - return PackedPluginEntity.wrap(bytes24(ValidationConfig.unwrap(config))); + function functionReference(ValidationConfig config) internal pure returns (PluginEntity) { + return PluginEntity.wrap(bytes24(ValidationConfig.unwrap(config))); } function isGlobal(ValidationConfig config) internal pure returns (bool) { diff --git a/src/interfaces/IAccountLoupe.sol b/src/interfaces/IAccountLoupe.sol index 70e408ec..3e7d9f11 100644 --- a/src/interfaces/IAccountLoupe.sol +++ b/src/interfaces/IAccountLoupe.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.25; -import {PackedPluginEntity} from "../interfaces/IPluginManager.sol"; +import {PluginEntity} from "../interfaces/IPluginManager.sol"; /// @notice Pre and post hooks for a given selector. /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty. struct ExecutionHook { - PackedPluginEntity hookFunction; + PluginEntity hookFunction; bool isPreHook; bool isPostHook; } @@ -21,7 +21,7 @@ interface IAccountLoupe { /// @notice Get the selectors for a validation function. /// @param validationFunction The validation function to get the selectors for. /// @return The allowed selectors for this validation function. - function getSelectors(PackedPluginEntity validationFunction) external view returns (bytes4[] memory); + function getSelectors(PluginEntity validationFunction) external view returns (bytes4[] memory); /// @notice Get the pre and post execution hooks for a selector. /// @param selector The selector to get the hooks for. @@ -31,18 +31,15 @@ interface IAccountLoupe { /// @notice Get the pre and post execution hooks for a validation function. /// @param validationFunction The validation function to get the hooks for. /// @return The pre and post execution hooks for this validation function. - function getPermissionHooks(PackedPluginEntity validationFunction) - external - view - returns (ExecutionHook[] memory); + function getPermissionHooks(PluginEntity validationFunction) external view returns (ExecutionHook[] memory); /// @notice Get the pre user op and runtime validation hooks associated with a selector. /// @param validationFunction The validation function to get the hooks for. /// @return preValidationHooks The pre validation hooks for this selector. - function getPreValidationHooks(PackedPluginEntity validationFunction) + function getPreValidationHooks(PluginEntity validationFunction) external view - returns (PackedPluginEntity[] memory preValidationHooks); + returns (PluginEntity[] memory preValidationHooks); /// @notice Get an array of all installed plugins. /// @return The addresses of all installed plugins. diff --git a/src/interfaces/IPluginManager.sol b/src/interfaces/IPluginManager.sol index 217ff3af..d2df72b5 100644 --- a/src/interfaces/IPluginManager.sol +++ b/src/interfaces/IPluginManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.25; -type PackedPluginEntity is bytes24; +type PluginEntity is bytes24; type ValidationConfig is bytes26; @@ -45,7 +45,7 @@ interface IPluginManager { /// data /// @param permissionHookUninstallData Optional data to be decoded and used by the plugin to clear account data function uninstallValidation( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bytes calldata uninstallData, bytes calldata preValidationHookUninstallData, bytes calldata permissionHookUninstallData diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index 4d42ef46..bfb1d676 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -106,10 +106,10 @@ Plugin manager interface. Modular Smart Contract Accounts **MUST** implement thi ```solidity // Treats the first 20 bytes as an address, and the last byte as a function identifier. -type PackedPluginEntity is bytes21; +type PluginEntity is bytes21; interface IPluginManager { - event PluginInstalled(address indexed plugin, bytes32 manifestHash, PackedPluginEntity[] dependencies); + event PluginInstalled(address indexed plugin, bytes32 manifestHash, PluginEntity[] dependencies); event PluginUninstalled(address indexed plugin, bool indexed onUninstallSucceeded); @@ -118,13 +118,13 @@ interface IPluginManager { /// @param manifestHash The hash of the plugin manifest. /// @param pluginInstallData Optional data to be decoded and used by the plugin to setup initial plugin data /// for the modular account. - /// @param dependencies The dependencies of the plugin, as described in the manifest. Each PackedPluginEntity + /// @param dependencies The dependencies of the plugin, as described in the manifest. Each PluginEntity /// MUST be composed of an installed plugin's address and a function ID of its validation function. function installPlugin( address plugin, bytes32 manifestHash, bytes calldata pluginInstallData, - PackedPluginEntity[] calldata dependencies + PluginEntity[] calldata dependencies ) external; /// @notice Uninstall a plugin from the modular account. @@ -215,13 +215,13 @@ interface IAccountLoupe { /// @notice Config for an execution function, given a selector. struct ExecutionFunctionConfig { address plugin; - PackedPluginEntity validationFunction; + PluginEntity validationFunction; } /// @notice Pre and post hooks for a given selector. /// @dev It's possible for one of either `preExecHook` or `postExecHook` to be empty. struct ExecutionHooks { - PackedPluginEntity hookFunction; + PluginEntity hookFunction; bool isPreHook; bool isPostHook; } @@ -243,7 +243,7 @@ interface IAccountLoupe { function getPreValidationHooks(bytes4 selector) external view - returns (PackedPluginEntity[] memory preValidationHooks); + returns (PluginEntity[] memory preValidationHooks); /// @notice Get an array of all installed plugins. /// @return The addresses of all installed plugins. @@ -513,7 +513,7 @@ Additionally, when the modular account natively implements functions in `IPlugin The steps to perform are: - If the call is not from the `EntryPoint`, then find an associated runtime validation function. If one does not exist, execution MUST revert. The modular account MUST execute all pre runtime validation hooks, then the runtime validation function, with the `call` opcode. All of these functions MUST receive the caller, value, and execution function's calldata as parameters. If any of these functions revert, execution MUST revert. If any pre execution hooks are set to `PRE_HOOK_ALWAYS_DENY`, execution MUST revert. If the validation function is set to `RUNTIME_VALIDATION_ALWAYS_ALLOW`, the runtime validation function MUST be bypassed. -- If there are pre execution hooks defined for the execution function, execute those hooks with the caller, value, and execution function's calldata as parameters. If any of these hooks returns data, it MUST be preserved until the call to the post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate pre execution hooks (i.e., hooks with identical `PackedPluginEntity`s), run the hook only once. If any of these functions revert, execution MUST revert. +- If there are pre execution hooks defined for the execution function, execute those hooks with the caller, value, and execution function's calldata as parameters. If any of these hooks returns data, it MUST be preserved until the call to the post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate pre execution hooks (i.e., hooks with identical `PluginEntity`s), run the hook only once. If any of these functions revert, execution MUST revert. - Run the execution function. - If any post execution hooks are defined, run the functions. If a pre execution hook returned data to the account, that data MUST be passed as a parameter to the associated post execution hook. The operation MUST be done with the `call` opcode. If there are duplicate post execution hooks, run them once for each unique associated pre execution hook. For post execution hooks without an associated pre execution hook, run the hook only once. If any of these functions revert, execution MUST revert. diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index 122b27ba..b65bf3c9 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {ExecutionHook} from "../../src/interfaces/IAccountLoupe.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; @@ -69,9 +69,8 @@ contract AccountLoupeTest is CustomValidationTestBase { } function test_pluginLoupe_getSelectors() public { - PackedPluginEntity comprehensivePluginValidation = PackedPluginEntityLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) - ); + PluginEntity comprehensivePluginValidation = + PluginEntityLib.pack(address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION)); bytes4[] memory selectors = account1.getSelectors(comprehensivePluginValidation); @@ -83,21 +82,21 @@ contract AccountLoupeTest is CustomValidationTestBase { ExecutionHook[] memory hooks = account1.getExecutionHooks(comprehensivePlugin.foo.selector); ExecutionHook[3] memory expectedHooks = [ ExecutionHook({ - hookFunction: PackedPluginEntityLib.pack( + hookFunction: PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.BOTH_EXECUTION_HOOKS) ), isPreHook: true, isPostHook: true }), ExecutionHook({ - hookFunction: PackedPluginEntityLib.pack( + hookFunction: PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_EXECUTION_HOOK) ), isPreHook: true, isPostHook: false }), ExecutionHook({ - hookFunction: PackedPluginEntityLib.pack( + hookFunction: PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.POST_EXECUTION_HOOK) ), isPreHook: false, @@ -108,8 +107,7 @@ contract AccountLoupeTest is CustomValidationTestBase { assertEq(hooks.length, 3); for (uint256 i = 0; i < hooks.length; i++) { assertEq( - PackedPluginEntity.unwrap(hooks[i].hookFunction), - PackedPluginEntity.unwrap(expectedHooks[i].hookFunction) + PluginEntity.unwrap(hooks[i].hookFunction), PluginEntity.unwrap(expectedHooks[i].hookFunction) ); assertEq(hooks[i].isPreHook, expectedHooks[i].isPreHook); assertEq(hooks[i].isPostHook, expectedHooks[i].isPostHook); @@ -117,21 +115,21 @@ contract AccountLoupeTest is CustomValidationTestBase { } function test_pluginLoupe_getValidationHooks() public { - PackedPluginEntity[] memory hooks = account1.getPreValidationHooks(_ownerValidation); + PluginEntity[] memory hooks = account1.getPreValidationHooks(_ownerValidation); assertEq(hooks.length, 2); assertEq( - PackedPluginEntity.unwrap(hooks[0]), - PackedPluginEntity.unwrap( - PackedPluginEntityLib.pack( + PluginEntity.unwrap(hooks[0]), + PluginEntity.unwrap( + PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ) ) ); assertEq( - PackedPluginEntity.unwrap(hooks[1]), - PackedPluginEntity.unwrap( - PackedPluginEntityLib.pack( + PluginEntity.unwrap(hooks[1]), + PluginEntity.unwrap( + PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ) ) @@ -144,13 +142,13 @@ contract AccountLoupeTest is CustomValidationTestBase { internal virtual override - returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](2); - preValidationHooks[0] = PackedPluginEntityLib.pack( + PluginEntity[] memory preValidationHooks = new PluginEntity[](2); + preValidationHooks[0] = PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_1) ); - preValidationHooks[1] = PackedPluginEntityLib.pack( + preValidationHooks[1] = PluginEntityLib.pack( address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.PRE_VALIDATION_HOOK_2) ); diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index a8e6ffc3..eaf8b8a7 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; import { @@ -58,7 +58,7 @@ contract AccountReturnDataTest is AccountTestBase { (address(regularResultContract), 0, abi.encodeCall(RegularResultContract.foo, ())) ), _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -86,7 +86,7 @@ contract AccountReturnDataTest is AccountTestBase { bytes memory retData = account1.executeWithAuthorization( abi.encodeCall(account1.executeBatch, (calls)), _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) diff --git a/test/account/GlobalValidationTest.t.sol b/test/account/GlobalValidationTest.t.sol index 77160d3a..433116bc 100644 --- a/test/account/GlobalValidationTest.t.sol +++ b/test/account/GlobalValidationTest.t.sol @@ -5,7 +5,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; @@ -26,7 +26,7 @@ contract GlobalValidationTest is AccountTestBase { account2 = UpgradeableModularAccount(payable(factory.getAddress(owner2, 0))); vm.deal(address(account2), 100 ether); - _ownerValidation = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _ownerValidation = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); ethRecipient = makeAddr("ethRecipient"); vm.deal(ethRecipient, 1 wei); diff --git a/test/account/MultiValidation.t.sol b/test/account/MultiValidation.t.sol index f91ced7e..2dbae711 100644 --- a/test/account/MultiValidation.t.sol +++ b/test/account/MultiValidation.t.sol @@ -8,9 +8,9 @@ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/Messa import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {PackedPluginEntity} from "../../src/interfaces/IPluginManager.sol"; +import {PluginEntity} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; -import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; @@ -42,9 +42,9 @@ contract MultiValidationTest is AccountTestBase { "" ); - PackedPluginEntity[] memory validations = new PackedPluginEntity[](2); - validations[0] = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); - validations[1] = PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID); + PluginEntity[] memory validations = new PluginEntity[](2); + validations[0] = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + validations[1] = PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID); bytes4[] memory selectors0 = account1.getSelectors(validations[0]); bytes4[] memory selectors1 = account1.getSelectors(validations[1]); @@ -71,9 +71,7 @@ contract MultiValidationTest is AccountTestBase { account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), - GLOBAL_VALIDATION, - "" + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) ); @@ -81,9 +79,7 @@ contract MultiValidationTest is AccountTestBase { account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), - GLOBAL_VALIDATION, - "" + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) ); } @@ -109,7 +105,7 @@ contract MultiValidationTest is AccountTestBase { bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -124,7 +120,7 @@ contract MultiValidationTest is AccountTestBase { userOp.nonce = 1; (v, r, s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PackedPluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index 3f8c3b39..546a156c 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -6,7 +6,7 @@ import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntry import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {MockAccessControlHookPlugin} from "../mocks/plugins/MockAccessControlHookPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; @@ -325,13 +325,13 @@ contract PerHookDataTest is CustomValidationTestBase { internal virtual override - returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - PackedPluginEntity accessControlHook = PackedPluginEntityLib.pack( + PluginEntity accessControlHook = PluginEntityLib.pack( address(_accessControlHookPlugin), uint32(MockAccessControlHookPlugin.EntityId.PRE_VALIDATION_HOOK) ); - PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); + PluginEntity[] memory preValidationHooks = new PluginEntity[](1); preValidationHooks[0] = accessControlHook; bytes[] memory preValidationHookData = new bytes[](1); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index 2eeb1b5b..516f9f34 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -7,7 +7,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; @@ -16,7 +16,7 @@ import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; contract SelfCallAuthorizationTest is AccountTestBase { ComprehensivePlugin public comprehensivePlugin; - PackedPluginEntity public comprehensivePluginValidation; + PluginEntity public comprehensivePluginValidation; function setUp() public { // install the comprehensive plugin to get new exec functions with different validations configured. @@ -27,9 +27,8 @@ contract SelfCallAuthorizationTest is AccountTestBase { vm.prank(address(entryPoint)); account1.installPlugin(address(comprehensivePlugin), manifestHash, ""); - comprehensivePluginValidation = PackedPluginEntityLib.pack( - address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION) - ); + comprehensivePluginValidation = + PluginEntityLib.pack(address(comprehensivePlugin), uint32(ComprehensivePlugin.EntityId.VALIDATION)); } function test_selfCallFails_userOp() public { diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 222e4df2..3ab0df9b 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.19; import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import { @@ -22,26 +22,26 @@ contract ValidationIntersectionTest is AccountTestBase { MockUserOpValidation1HookPlugin public oneHookPlugin; MockUserOpValidation2HookPlugin public twoHookPlugin; - PackedPluginEntity public noHookValidation; - PackedPluginEntity public oneHookValidation; - PackedPluginEntity public twoHookValidation; + PluginEntity public noHookValidation; + PluginEntity public oneHookValidation; + PluginEntity public twoHookValidation; function setUp() public { noHookPlugin = new MockUserOpValidationPlugin(); oneHookPlugin = new MockUserOpValidation1HookPlugin(); twoHookPlugin = new MockUserOpValidation2HookPlugin(); - noHookValidation = PackedPluginEntityLib.pack({ + noHookValidation = PluginEntityLib.pack({ addr: address(noHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); - oneHookValidation = PackedPluginEntityLib.pack({ + oneHookValidation = PluginEntityLib.pack({ addr: address(oneHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); - twoHookValidation = PackedPluginEntityLib.pack({ + twoHookValidation = PluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.USER_OP_VALIDATION) }); @@ -59,8 +59,8 @@ contract ValidationIntersectionTest is AccountTestBase { }); // TODO: change with new install flow // temporary fix to add the pre-validation hook - PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); - preValidationHooks[0] = PackedPluginEntityLib.pack({ + PluginEntity[] memory preValidationHooks = new PluginEntity[](1); + preValidationHooks[0] = PluginEntityLib.pack({ addr: address(oneHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); @@ -78,12 +78,12 @@ contract ValidationIntersectionTest is AccountTestBase { pluginInstallData: "" }); // temporary fix to add the pre-validation hook - preValidationHooks = new PackedPluginEntity[](2); - preValidationHooks[0] = PackedPluginEntityLib.pack({ + preValidationHooks = new PluginEntity[](2); + preValidationHooks[0] = PluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_1) }); - preValidationHooks[1] = PackedPluginEntityLib.pack({ + preValidationHooks[1] = PluginEntityLib.pack({ addr: address(twoHookPlugin), entityId: uint32(MockBaseUserOpValidationPlugin.EntityId.PRE_VALIDATION_HOOK_2) }); diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol index 5b9a66c3..94a44a8b 100644 --- a/test/libraries/FunctionReferenceLib.t.sol +++ b/test/libraries/FunctionReferenceLib.t.sol @@ -3,29 +3,29 @@ pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; -import {PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; -import {PackedPluginEntity} from "../../src/interfaces/IPluginManager.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; +import {PluginEntity} from "../../src/interfaces/IPluginManager.sol"; -contract PackedPluginEntityLibTest is Test { - using PackedPluginEntityLib for PackedPluginEntity; +contract PluginEntityLibTest is Test { + using PluginEntityLib for PluginEntity; function testFuzz_functionReference_packing(address addr, uint32 entityId) public { // console.log("addr: ", addr); // console.log("entityId: ", vm.toString(entityId)); - PackedPluginEntity fr = PackedPluginEntityLib.pack(addr, entityId); - // console.log("packed: ", vm.toString(PackedPluginEntity.unwrap(fr))); - (address addr2, uint32 entityId2) = PackedPluginEntityLib.unpack(fr); + PluginEntity fr = PluginEntityLib.pack(addr, entityId); + // console.log("packed: ", vm.toString(PluginEntity.unwrap(fr))); + (address addr2, uint32 entityId2) = PluginEntityLib.unpack(fr); // console.log("addr2: ", addr2); // console.log("entityId2: ", vm.toString(entityId2)); assertEq(addr, addr2); assertEq(entityId, entityId2); } - function testFuzz_functionReference_operators(PackedPluginEntity a, PackedPluginEntity b) public { + function testFuzz_functionReference_operators(PluginEntity a, PluginEntity b) public { assertTrue(a.eq(a)); assertTrue(b.eq(b)); - if (PackedPluginEntity.unwrap(a) == PackedPluginEntity.unwrap(b)) { + if (PluginEntity.unwrap(a) == PluginEntity.unwrap(b)) { assertTrue(a.eq(b)); assertTrue(b.eq(a)); assertFalse(a.notEq(b)); diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index ea10666d..4a8fc4ff 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.25; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {AllowlistPlugin} from "../../src/samples/permissionhooks/AllowlistPlugin.sol"; @@ -290,13 +290,12 @@ contract AllowlistPluginTest is CustomValidationTestBase { internal virtual override - returns (PackedPluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) + returns (PluginEntity, bool, bool, bytes4[] memory, bytes memory, bytes memory, bytes memory) { - PackedPluginEntity accessControlHook = PackedPluginEntityLib.pack( - address(allowlistPlugin), uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK) - ); + PluginEntity accessControlHook = + PluginEntityLib.pack(address(allowlistPlugin), uint32(AllowlistPlugin.EntityId.PRE_VALIDATION_HOOK)); - PackedPluginEntity[] memory preValidationHooks = new PackedPluginEntity[](1); + PluginEntity[] memory preValidationHooks = new PluginEntity[](1); preValidationHooks[0] = accessControlHook; bytes[] memory preValidationHookData = new bytes[](1); diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index 347705eb..3a752f07 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -5,7 +5,7 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; -import {PackedPluginEntity, PackedPluginEntityLib} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; @@ -18,7 +18,7 @@ import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; /// @dev This contract handles common boilerplate setup for tests using UpgradeableModularAccount with /// SingleOwnerPlugin. abstract contract AccountTestBase is OptimizedTest { - using PackedPluginEntityLib for PackedPluginEntity; + using PluginEntityLib for PluginEntity; using MessageHashUtils for bytes32; EntryPoint public entryPoint; @@ -30,7 +30,7 @@ abstract contract AccountTestBase is OptimizedTest { uint256 public owner1Key; UpgradeableModularAccount public account1; - PackedPluginEntity internal _ownerValidation; + PluginEntity internal _ownerValidation; uint8 public constant SELECTOR_ASSOCIATED_VALIDATION = 0; uint8 public constant GLOBAL_VALIDATION = 1; @@ -57,7 +57,7 @@ abstract contract AccountTestBase is OptimizedTest { account1 = factory.createAccount(owner1, 0); vm.deal(address(account1), 100 ether); - _ownerValidation = PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _ownerValidation = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); } function _runExecUserOp(address target, bytes memory callData) internal { @@ -100,7 +100,7 @@ abstract contract AccountTestBase is OptimizedTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -153,7 +153,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -168,7 +168,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -190,7 +190,7 @@ abstract contract AccountTestBase is OptimizedTest { ) ), _encodeSignature( - PackedPluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" ) @@ -204,7 +204,7 @@ abstract contract AccountTestBase is OptimizedTest { // helper function to encode a signature, according to the per-hook and per-validation data format. function _encodeSignature( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, uint8 globalOrNot, PreValidationHookData[] memory preValidationHookData, bytes memory validationData @@ -228,11 +228,11 @@ abstract contract AccountTestBase is OptimizedTest { } // overload for the case where there are no pre-validation hooks - function _encodeSignature( - PackedPluginEntity validationFunction, - uint8 globalOrNot, - bytes memory validationData - ) internal pure returns (bytes memory) { + function _encodeSignature(PluginEntity validationFunction, uint8 globalOrNot, bytes memory validationData) + internal + pure + returns (bytes memory) + { PreValidationHookData[] memory emptyPreValidationHookData = new PreValidationHookData[](0); return _encodeSignature(validationFunction, globalOrNot, emptyPreValidationHookData, validationData); } diff --git a/test/utils/CustomValidationTestBase.sol b/test/utils/CustomValidationTestBase.sol index ff7feced..bd119fa1 100644 --- a/test/utils/CustomValidationTestBase.sol +++ b/test/utils/CustomValidationTestBase.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.25; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {PackedPluginEntity} from "../../src/helpers/PackedPluginEntityLib.sol"; +import {PluginEntity} from "../../src/helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; @@ -16,7 +16,7 @@ import {AccountTestBase} from "./AccountTestBase.sol"; abstract contract CustomValidationTestBase is AccountTestBase { function _customValidationSetup() internal { ( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bool isGlobal, bool isSignatureValidation, bytes4[] memory selectors, @@ -44,7 +44,7 @@ abstract contract CustomValidationTestBase is AccountTestBase { internal virtual returns ( - PackedPluginEntity validationFunction, + PluginEntity validationFunction, bool shared, bool isSignatureValidation, bytes4[] memory selectors, From 6007eef1d8ed858add4d3eaa3fce7cf7fc64db62 Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Fri, 12 Jul 2024 12:14:39 -0700 Subject: [PATCH 06/11] fix ValidationConfigLib packing --- src/helpers/ValidationConfigLib.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index e3558f3c..fa202011 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -38,7 +38,7 @@ library ValidationConfigLib { // plugin address stored in the first 20 bytes bytes26(bytes20(_plugin)) // entityId stored in the 21st - 24th byte - | bytes26(bytes32(uint256(_entityId) << 168)) + | bytes26(bytes24(uint192(_entityId))) // isGlobal flag stored in the 25th byte | bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0)) // isSignatureValidation flag stored in the 26th byte From 648edd7ee0f1cb9349f216e0cab6fd045033f922 Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 16 Jul 2024 09:14:15 -0700 Subject: [PATCH 07/11] clean up code --- src/helpers/PackedPluginEntityLib.sol | 37 --------------------------- src/helpers/PluginEntityLib.sol | 2 -- src/interfaces/IExecutionHook.sol | 6 ++--- src/interfaces/IValidation.sol | 9 +++---- src/interfaces/IValidationHook.sol | 9 +++---- 5 files changed, 8 insertions(+), 55 deletions(-) delete mode 100644 src/helpers/PackedPluginEntityLib.sol diff --git a/src/helpers/PackedPluginEntityLib.sol b/src/helpers/PackedPluginEntityLib.sol deleted file mode 100644 index 5395a396..00000000 --- a/src/helpers/PackedPluginEntityLib.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.25; - -import {PluginEntity} from "../interfaces/IPluginManager.sol"; - -library PluginEntityLib { - // Empty or unset PluginEntity. - PluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PluginEntity.wrap(bytes24(0)); - // Magic value for hooks that should always revert. - PluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PluginEntity.wrap(bytes24(uint192(2))); - - function pack(address addr, uint32 entityId) internal pure returns (PluginEntity) { - return PluginEntity.wrap(bytes24(bytes20(addr)) | bytes24(uint192(entityId))); - } - - function unpack(PluginEntity fr) internal pure returns (address addr, uint32 entityId) { - bytes24 underlying = PluginEntity.unwrap(fr); - addr = address(bytes20(underlying)); - entityId = uint32(bytes4(underlying << 160)); - } - - function isEmpty(PluginEntity fr) internal pure returns (bool) { - return PluginEntity.unwrap(fr) == bytes24(0); - } - - function notEmpty(PluginEntity fr) internal pure returns (bool) { - return PluginEntity.unwrap(fr) != bytes24(0); - } - - function eq(PluginEntity a, PluginEntity b) internal pure returns (bool) { - return PluginEntity.unwrap(a) == PluginEntity.unwrap(b); - } - - function notEq(PluginEntity a, PluginEntity b) internal pure returns (bool) { - return PluginEntity.unwrap(a) != PluginEntity.unwrap(b); - } -} diff --git a/src/helpers/PluginEntityLib.sol b/src/helpers/PluginEntityLib.sol index 5395a396..423b7f70 100644 --- a/src/helpers/PluginEntityLib.sol +++ b/src/helpers/PluginEntityLib.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.25; import {PluginEntity} from "../interfaces/IPluginManager.sol"; library PluginEntityLib { - // Empty or unset PluginEntity. - PluginEntity internal constant _EMPTY_PACKED_PLUGIN_ENTITY = PluginEntity.wrap(bytes24(0)); // Magic value for hooks that should always revert. PluginEntity internal constant _PRE_HOOK_ALWAYS_DENY = PluginEntity.wrap(bytes24(uint192(2))); diff --git a/src/interfaces/IExecutionHook.sol b/src/interfaces/IExecutionHook.sol index 6f0b06eb..9cb16482 100644 --- a/src/interfaces/IExecutionHook.sol +++ b/src/interfaces/IExecutionHook.sol @@ -7,8 +7,7 @@ interface IExecutionHook is IPlugin { /// @notice Run the pre execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. @@ -20,8 +19,7 @@ interface IExecutionHook is IPlugin { /// @notice Run the post execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param preExecHookData The context returned by its associated pre execution hook. function postExecutionHook(uint32 entityId, bytes calldata preExecHookData) external; } diff --git a/src/interfaces/IValidation.sol b/src/interfaces/IValidation.sol index 266e6878..03aed016 100644 --- a/src/interfaces/IValidation.sol +++ b/src/interfaces/IValidation.sol @@ -8,8 +8,7 @@ import {IPlugin} from "./IPlugin.sol"; interface IValidation is IPlugin { /// @notice Run the user operation validationFunction specified by the `entityId`. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). @@ -20,8 +19,7 @@ interface IValidation is IPlugin { /// @notice Run the runtime validationFunction specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. @@ -37,8 +35,7 @@ interface IValidation is IPlugin { /// @notice Validates a signature using ERC-1271. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param sender the address that sent the ERC-1271 request to the smart account /// @param hash the hash of the ERC-1271 request /// @param signature the signature of the ERC-1271 request diff --git a/src/interfaces/IValidationHook.sol b/src/interfaces/IValidationHook.sol index dc5630dc..dd7e2500 100644 --- a/src/interfaces/IValidationHook.sol +++ b/src/interfaces/IValidationHook.sol @@ -9,8 +9,7 @@ interface IValidationHook is IPlugin { /// @notice Run the pre user operation validation hook specified by the `entityId`. /// @dev Pre user operation validation hooks MUST NOT return an authorizer value other than 0 or 1. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param userOp The user operation. /// @param userOpHash The user operation hash. /// @return Packed validation data for validAfter (6 bytes), validUntil (6 bytes), and authorizer (20 bytes). @@ -21,8 +20,7 @@ interface IValidationHook is IPlugin { /// @notice Run the pre runtime validation hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param sender The caller address. /// @param value The call value. /// @param data The calldata sent. @@ -39,8 +37,7 @@ interface IValidationHook is IPlugin { /// @notice Run the pre signature validation hook specified by the `entityId`. /// @dev To indicate the call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there - /// be - /// more than one. + /// be more than one. /// @param sender The caller address. /// @param hash The hash of the message being signed. /// @param signature The signature of the message. From 9d067d1186de1f4e338fb481db1ef99c9ab7213d Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 16 Jul 2024 10:10:46 -0700 Subject: [PATCH 08/11] [2/n] rename validation res helper file and var names (#94) --- src/account/UpgradeableModularAccount.sol | 20 +++++++++---------- ...taHelpers.sol => ValidationResHelpers.sol} | 20 +++++++++---------- test/account/ValidationIntersection.t.sol | 20 +++++++++---------- test/utils/AccountTestBase.sol | 6 +++--- 4 files changed, 33 insertions(+), 33 deletions(-) rename src/helpers/{ValidationDataHelpers.sol => ValidationResHelpers.sol} (72%) diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index da3ababb..ec075e8b 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -13,7 +13,7 @@ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet import {PluginEntityLib} from "../helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol"; import {SparseCalldataSegmentLib} from "../helpers/SparseCalldataSegmentLib.sol"; -import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol"; +import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationResHelpers.sol"; import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol"; import {IValidation} from "../interfaces/IValidation.sol"; import {IValidationHook} from "../interfaces/IValidationHook.sol"; @@ -405,7 +405,7 @@ contract UpgradeableModularAccount is bytes calldata signatureSegment; (signatureSegment, signature) = signature.getNextSegment(); - uint256 validationData; + uint256 validationRes; // Do preUserOpValidation hooks PluginEntity[] memory preUserOpValidationHooks = @@ -433,14 +433,14 @@ contract UpgradeableModularAccount is } (address plugin, uint32 entityId) = preUserOpValidationHooks[i].unpack(); - uint256 currentValidationData = + uint256 currentValidationRes = IValidationHook(plugin).preUserOpValidationHook(entityId, userOp, userOpHash); - if (uint160(currentValidationData) > 1) { + if (uint160(currentValidationRes) > 1) { // If the aggregator is not 0 or 1, it is an unexpected value - revert UnexpectedAggregator(plugin, entityId, address(uint160(currentValidationData))); + revert UnexpectedAggregator(plugin, entityId, address(uint160(currentValidationRes))); } - validationData = _coalescePreValidation(validationData, currentValidationData); + validationRes = _coalescePreValidation(validationRes, currentValidationRes); } // Run the user op validationFunction @@ -452,17 +452,17 @@ contract UpgradeableModularAccount is userOp.signature = signatureSegment.getBody(); (address plugin, uint32 entityId) = userOpValidationFunction.unpack(); - uint256 currentValidationData = IValidation(plugin).validateUserOp(entityId, userOp, userOpHash); + uint256 currentValidationRes = IValidation(plugin).validateUserOp(entityId, userOp, userOpHash); if (preUserOpValidationHooks.length != 0) { // If we have other validation data we need to coalesce with - validationData = _coalesceValidation(validationData, currentValidationData); + validationRes = _coalesceValidation(validationRes, currentValidationRes); } else { - validationData = currentValidationData; + validationRes = currentValidationRes; } } - return validationData; + return validationRes; } function _doRuntimeValidation( diff --git a/src/helpers/ValidationDataHelpers.sol b/src/helpers/ValidationResHelpers.sol similarity index 72% rename from src/helpers/ValidationDataHelpers.sol rename to src/helpers/ValidationResHelpers.sol index 3f61b19c..854d442c 100644 --- a/src/helpers/ValidationDataHelpers.sol +++ b/src/helpers/ValidationResHelpers.sol @@ -2,31 +2,31 @@ pragma solidity ^0.8.25; // solhint-disable-next-line private-vars-leading-underscore -function _coalescePreValidation(uint256 validationData1, uint256 validationData2) +function _coalescePreValidation(uint256 validationRes1, uint256 validationRes2) pure returns (uint256 resValidationData) { - uint48 validUntil1 = uint48(validationData1 >> 160); + uint48 validUntil1 = uint48(validationRes1 >> 160); if (validUntil1 == 0) { validUntil1 = type(uint48).max; } - uint48 validUntil2 = uint48(validationData2 >> 160); + uint48 validUntil2 = uint48(validationRes2 >> 160); if (validUntil2 == 0) { validUntil2 = type(uint48).max; } resValidationData = ((validUntil1 > validUntil2) ? uint256(validUntil2) << 160 : uint256(validUntil1) << 160); - uint48 validAfter1 = uint48(validationData1 >> 208); - uint48 validAfter2 = uint48(validationData2 >> 208); + uint48 validAfter1 = uint48(validationRes1 >> 208); + uint48 validAfter2 = uint48(validationRes2 >> 208); resValidationData |= ((validAfter1 < validAfter2) ? uint256(validAfter2) << 208 : uint256(validAfter1) << 208); // Once we know that the authorizer field is 0 or 1, we can safely bubble up SIG_FAIL with bitwise OR - resValidationData |= uint160(validationData1) | uint160(validationData2); + resValidationData |= uint160(validationRes1) | uint160(validationRes2); } // solhint-disable-next-line private-vars-leading-underscore -function _coalesceValidation(uint256 preValidationData, uint256 validationData) +function _coalesceValidation(uint256 preValidationData, uint256 validationRes) pure returns (uint256 resValidationData) { @@ -34,17 +34,17 @@ function _coalesceValidation(uint256 preValidationData, uint256 validationData) if (validUntil1 == 0) { validUntil1 = type(uint48).max; } - uint48 validUntil2 = uint48(validationData >> 160); + uint48 validUntil2 = uint48(validationRes >> 160); if (validUntil2 == 0) { validUntil2 = type(uint48).max; } resValidationData = ((validUntil1 > validUntil2) ? uint256(validUntil2) << 160 : uint256(validUntil1) << 160); uint48 validAfter1 = uint48(preValidationData >> 208); - uint48 validAfter2 = uint48(validationData >> 208); + uint48 validAfter2 = uint48(validationRes >> 208); resValidationData |= ((validAfter1 < validAfter2) ? uint256(validAfter2) << 208 : uint256(validAfter1) << 208); // If prevalidation failed, bubble up failure - resValidationData |= uint160(preValidationData) == 1 ? 1 : uint160(validationData); + resValidationData |= uint160(preValidationData) == 1 ? 1 : uint160(validationRes); } diff --git a/test/account/ValidationIntersection.t.sol b/test/account/ValidationIntersection.t.sol index 3ab0df9b..3bdaa1b6 100644 --- a/test/account/ValidationIntersection.t.sol +++ b/test/account/ValidationIntersection.t.sol @@ -156,7 +156,7 @@ contract ValidationIntersectionTest is AccountTestBase { uint48 end2 = uint48(25); oneHookPlugin.setValidationData( - _packValidationData(address(0), start1, end1), _packValidationData(address(0), start2, end2) + _packValidationRes(address(0), start1, end1), _packValidationRes(address(0), start2, end2) ); PackedUserOperation memory userOp; @@ -167,7 +167,7 @@ contract ValidationIntersectionTest is AccountTestBase { vm.prank(address(entryPoint)); uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); - assertEq(returnedValidationData, _packValidationData(address(0), start2, end1)); + assertEq(returnedValidationData, _packValidationRes(address(0), start2, end1)); } function test_validationIntersect_timeBounds_intersect_2() public { @@ -178,7 +178,7 @@ contract ValidationIntersectionTest is AccountTestBase { uint48 end2 = uint48(25); oneHookPlugin.setValidationData( - _packValidationData(address(0), start2, end2), _packValidationData(address(0), start1, end1) + _packValidationRes(address(0), start2, end2), _packValidationRes(address(0), start1, end1) ); PackedUserOperation memory userOp; @@ -189,7 +189,7 @@ contract ValidationIntersectionTest is AccountTestBase { vm.prank(address(entryPoint)); uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); - assertEq(returnedValidationData, _packValidationData(address(0), start2, end1)); + assertEq(returnedValidationData, _packValidationRes(address(0), start2, end1)); } function test_validationIntersect_revert_unexpectedAuthorizer() public { @@ -247,7 +247,7 @@ contract ValidationIntersectionTest is AccountTestBase { address goodAuthorizer = makeAddr("goodAuthorizer"); oneHookPlugin.setValidationData( - _packValidationData(goodAuthorizer, start1, end1), _packValidationData(address(0), start2, end2) + _packValidationRes(goodAuthorizer, start1, end1), _packValidationRes(address(0), start2, end2) ); PackedUserOperation memory userOp; @@ -258,7 +258,7 @@ contract ValidationIntersectionTest is AccountTestBase { vm.prank(address(entryPoint)); uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); - assertEq(returnedValidationData, _packValidationData(goodAuthorizer, start2, end1)); + assertEq(returnedValidationData, _packValidationRes(goodAuthorizer, start2, end1)); } function test_validationIntersect_multiplePreValidationHooksIntersect() public { @@ -270,8 +270,8 @@ contract ValidationIntersectionTest is AccountTestBase { twoHookPlugin.setValidationData( 0, // returns OK - _packValidationData(address(0), start1, end1), - _packValidationData(address(0), start2, end2) + _packValidationRes(address(0), start1, end1), + _packValidationRes(address(0), start2, end2) ); PackedUserOperation memory userOp; @@ -282,7 +282,7 @@ contract ValidationIntersectionTest is AccountTestBase { vm.prank(address(entryPoint)); uint256 returnedValidationData = account1.validateUserOp(userOp, uoHash, 1 wei); - assertEq(returnedValidationData, _packValidationData(address(0), start2, end1)); + assertEq(returnedValidationData, _packValidationRes(address(0), start2, end1)); } function test_validationIntersect_multiplePreValidationHooksSigFail() public { @@ -318,7 +318,7 @@ contract ValidationIntersectionTest is AccountTestBase { validAfter = uint48(validationData >> (48 + 160)); } - function _packValidationData(address authorizer, uint48 validAfter, uint48 validUntil) + function _packValidationRes(address authorizer, uint48 validAfter, uint48 validUntil) internal pure returns (uint256) diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index 3a752f07..a8f84a21 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -214,7 +214,7 @@ abstract contract AccountTestBase is OptimizedTest { for (uint256 i = 0; i < preValidationHookData.length; ++i) { sig = abi.encodePacked( sig, - _packValidationDataWithIndex( + _packValidationResWithIndex( preValidationHookData[i].index, preValidationHookData[i].validationData ) ); @@ -222,7 +222,7 @@ abstract contract AccountTestBase is OptimizedTest { // Index of the actual validation data is the length of the preValidationHooksRetrieved - aka // one-past-the-end - sig = abi.encodePacked(sig, _packValidationDataWithIndex(255, validationData)); + sig = abi.encodePacked(sig, _packValidationResWithIndex(255, validationData)); return sig; } @@ -238,7 +238,7 @@ abstract contract AccountTestBase is OptimizedTest { } // helper function to pack validation data with an index, according to the sparse calldata segment spec. - function _packValidationDataWithIndex(uint8 index, bytes memory validationData) + function _packValidationResWithIndex(uint8 index, bytes memory validationData) internal pure returns (bytes memory) From f598f4e931b9c0361709080201c1d9a50953e6ff Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 16 Jul 2024 16:21:09 -0700 Subject: [PATCH 09/11] update comments and readme --- src/account/AccountStorage.sol | 6 +++--- src/helpers/ValidationConfigLib.sol | 8 ++++---- standard/ERCs/erc-6900.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/account/AccountStorage.sol b/src/account/AccountStorage.sol index 01cfd7ba..df081bd0 100644 --- a/src/account/AccountStorage.sol +++ b/src/account/AccountStorage.sol @@ -68,9 +68,9 @@ function toPluginEntity(bytes32 setValue) pure returns (PluginEntity) { } // ExecutionHook layout: -// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF______________________ Hook Function Reference -// 0x__________________________________________AA____________________ is pre hook -// 0x____________________________________________BB__________________ is post hook +// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF______________________ Hook Plugin Entity +// 0x________________________________________________AA____________________ is pre hook +// 0x__________________________________________________BB__________________ is post hook function toSetValue(ExecutionHook memory executionHook) pure returns (bytes32) { return bytes32(PluginEntity.unwrap(executionHook.hookFunction)) diff --git a/src/helpers/ValidationConfigLib.sol b/src/helpers/ValidationConfigLib.sol index fa202011..95e8ea90 100644 --- a/src/helpers/ValidationConfigLib.sol +++ b/src/helpers/ValidationConfigLib.sol @@ -6,10 +6,10 @@ import {PluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol"; // Validation config is a packed representation of a validation function and flags for its configuration. // Layout: // 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address -// 0x________________________________________BB______________________ // Function ID -// 0x__________________________________________CC____________________ // isGlobal -// 0x____________________________________________DD__________________ // isSignatureValidation -// 0x______________________________________________000000000000000000 // unused +// 0x________________________________________BBBBBBBB________________ // Entity ID +// 0x________________________________________________CC______________ // isGlobal +// 0x__________________________________________________DD____________ // isSignatureValidation +// 0x____________________________________________________000000000000 // unused library ValidationConfigLib { function pack(PluginEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation) diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index bfb1d676..a4a9ab76 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -443,7 +443,7 @@ The following behavior MUST be followed: #### Calls to `installPlugin` -The function `installPlugin` accepts 4 parameters: the address of the plugin to install, the Keccak-256 hash of the plugin's manifest, ABI-encoded data to pass to the plugin's `onInstall` callback, and an array of function references that represent the plugin's install dependencies. +The function `installPlugin` accepts 3 parameters: the address of the plugin to install, the Keccak-256 hash of the plugin's manifest, ABI-encoded data to pass to the plugin's `onInstall` callback. The function MUST retrieve the plugin's manifest by calling `pluginManifest()` using `staticcall`. From 0ddd484a7f84711ed8be7790544bf4768432f204 Mon Sep 17 00:00:00 2001 From: fangting-alchemy <119372438+fangting-alchemy@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:26:51 -0700 Subject: [PATCH 10/11] [3/n] Add composable single singer validation and tests (#96) --- README.md | 2 +- src/account/UpgradeableModularAccount.sol | 10 +- src/interfaces/IValidation.sol | 14 +- src/plugins/owner/SingleOwnerPlugin.sol | 188 ------------------ .../validation/ISingleSignerValidation.sol | 28 +++ .../validation/SingleSignerValidation.sol | 145 ++++++++++++++ test/account/AccountLoupe.t.sol | 4 +- test/account/AccountReturnData.t.sol | 6 +- test/account/GlobalValidationTest.t.sol | 7 +- test/account/MultiValidation.t.sol | 26 +-- test/account/PerHookData.t.sol | 43 ++-- test/account/SelfCallAuthorization.t.sol | 2 +- test/account/UpgradeableModularAccount.t.sol | 32 +-- ...ure.sol => SingleSignerFactoryFixture.sol} | 28 +-- test/mocks/plugins/ComprehensivePlugin.sol | 8 +- test/mocks/plugins/ReturnDataPluginMocks.sol | 7 +- test/mocks/plugins/ValidationPluginMocks.sol | 13 +- test/plugin/SingleOwnerPlugin.t.sol | 186 ----------------- test/plugin/TokenReceiverPlugin.t.sol | 5 +- test/samples/AllowlistPlugin.t.sol | 4 +- test/utils/AccountTestBase.sol | 37 ++-- test/utils/OptimizedTest.sol | 16 +- test/utils/TestConstants.sol | 2 +- test/validation/SingleSignerValidation.t.sol | 142 +++++++++++++ 24 files changed, 467 insertions(+), 488 deletions(-) delete mode 100644 src/plugins/owner/SingleOwnerPlugin.sol create mode 100644 src/plugins/validation/ISingleSignerValidation.sol create mode 100644 src/plugins/validation/SingleSignerValidation.sol rename test/mocks/{MSCAFactoryFixture.sol => SingleSignerFactoryFixture.sol} (79%) delete mode 100644 test/plugin/SingleOwnerPlugin.t.sol create mode 100644 test/validation/SingleSignerValidation.t.sol diff --git a/README.md b/README.md index fb3e9dbc..1c90c710 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Reference implementation for [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900). It is an early draft implementation. -The implementation includes an upgradable modular account with two plugins (`SingleOwnerPlugin` and `TokenReceiverPlugin`). It is compliant with ERC-6900 with the latest updates. +The implementation includes an upgradable modular account with two plugins (`SingleSignerValidation` and `TokenReceiverPlugin`). It is compliant with ERC-6900 with the latest updates. ## Important Callouts diff --git a/src/account/UpgradeableModularAccount.sol b/src/account/UpgradeableModularAccount.sol index ec075e8b..acbd6de5 100644 --- a/src/account/UpgradeableModularAccount.sol +++ b/src/account/UpgradeableModularAccount.sol @@ -348,8 +348,10 @@ contract UpgradeableModularAccount is revert SignatureValidationInvalid(plugin, entityId); } - if (IValidation(plugin).validateSignature(entityId, msg.sender, hash, signature[24:]) == _1271_MAGIC_VALUE) - { + if ( + IValidation(plugin).validateSignature(address(this), entityId, msg.sender, hash, signature[24:]) + == _1271_MAGIC_VALUE + ) { return _1271_MAGIC_VALUE; } return _1271_INVALID; @@ -517,7 +519,9 @@ contract UpgradeableModularAccount is (address plugin, uint32 entityId) = runtimeValidationFunction.unpack(); - try IValidation(plugin).validateRuntime(entityId, msg.sender, msg.value, callData, authSegment.getBody()) + try IValidation(plugin).validateRuntime( + address(this), entityId, msg.sender, msg.value, callData, authSegment.getBody() + ) // forgefmt: disable-start // solhint-disable-next-line no-empty-blocks {} catch (bytes memory revertReason) { diff --git a/src/interfaces/IValidation.sol b/src/interfaces/IValidation.sol index 03aed016..471cd2c1 100644 --- a/src/interfaces/IValidation.sol +++ b/src/interfaces/IValidation.sol @@ -18,6 +18,7 @@ interface IValidation is IPlugin { /// @notice Run the runtime validationFunction specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. + /// @param account the account to validate for. /// @param entityId An identifier that routes the call to different internal implementations, should there /// be more than one. /// @param sender The caller address. @@ -25,6 +26,7 @@ interface IValidation is IPlugin { /// @param data The calldata sent. /// @param authorization Additional data for the validation function to use. function validateRuntime( + address account, uint32 entityId, address sender, uint256 value, @@ -34,14 +36,18 @@ interface IValidation is IPlugin { /// @notice Validates a signature using ERC-1271. /// @dev To indicate the entire call should revert, the function MUST revert. + /// @param account the account to validate for. /// @param entityId An identifier that routes the call to different internal implementations, should there /// be more than one. /// @param sender the address that sent the ERC-1271 request to the smart account /// @param hash the hash of the ERC-1271 request /// @param signature the signature of the ERC-1271 request /// @return the ERC-1271 `MAGIC_VALUE` if the signature is valid, or 0xFFFFFFFF if invalid. - function validateSignature(uint32 entityId, address sender, bytes32 hash, bytes calldata signature) - external - view - returns (bytes4); + function validateSignature( + address account, + uint32 entityId, + address sender, + bytes32 hash, + bytes calldata signature + ) external view returns (bytes4); } diff --git a/src/plugins/owner/SingleOwnerPlugin.sol b/src/plugins/owner/SingleOwnerPlugin.sol deleted file mode 100644 index d82a1824..00000000 --- a/src/plugins/owner/SingleOwnerPlugin.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.25; - -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; -import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; - -import {PluginManifest, PluginMetadata, SelectorPermission} from "../../interfaces/IPlugin.sol"; -import {IPlugin} from "../../interfaces/IPlugin.sol"; -import {IValidation} from "../../interfaces/IValidation.sol"; -import {BasePlugin, IERC165} from "../BasePlugin.sol"; - -/// @title Single Owner Plugin -/// @author ERC-6900 Authors -/// @notice This plugin allows an EOA or smart contract to own a modular account. -/// It also supports [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signature -/// validation for both validating the signature on user operations and in -/// exposing its own `isValidSignature` method. This only works when the owner of -/// modular account also support ERC-1271. -/// -/// ERC-4337's bundler validation rules limit the types of contracts that can be -/// used as owners to validate user operation signatures. For example, the -/// contract's `isValidSignature` function may not use any forbidden opcodes -/// such as `TIMESTAMP` or `NUMBER`, and the contract may not be an ERC-1967 -/// proxy as it accesses a constant implementation slot not associated with -/// the account, violating storage access rules. This also means that the -/// owner of a modular account may not be another modular account if you want to -/// send user operations through a bundler. -contract SingleOwnerPlugin is IValidation, BasePlugin { - using ECDSA for bytes32; - using MessageHashUtils for bytes32; - - string internal constant _NAME = "Single Owner Plugin"; - string internal constant _VERSION = "1.0.0"; - string internal constant _AUTHOR = "ERC-6900 Authors"; - - uint256 internal constant _SIG_VALIDATION_PASSED = 0; - uint256 internal constant _SIG_VALIDATION_FAILED = 1; - - // bytes4(keccak256("isValidSignature(bytes32,bytes)")) - bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; - bytes4 internal constant _1271_INVALID = 0xffffffff; - - mapping(uint32 id => mapping(address account => address)) public owners; - - /// @notice This event is emitted when ownership of the account changes. - /// @param account The account whose ownership changed. - /// @param previousOwner The address of the previous owner. - /// @param newOwner The address of the new owner. - event OwnershipTransferred(address indexed account, address indexed previousOwner, address indexed newOwner); - - error AlreadyInitialized(); - error NotAuthorized(); - error NotInitialized(); - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Execution functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @notice Transfer ownership of an account and ID to `newOwner`. The caller address (`msg.sender`) is user to - /// identify the account. - /// @param newOwner The address of the new owner. - function transferOwnership(uint32 id, address newOwner) external { - _transferOwnership(id, newOwner); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin interface functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IPlugin - function onInstall(bytes calldata data) external override { - (uint32 id, address owner) = abi.decode(data, (uint32, address)); - _transferOwnership(id, owner); - } - - /// @inheritdoc IPlugin - function onUninstall(bytes calldata data) external override { - uint32 id = abi.decode(data, (uint32)); - _transferOwnership(id, address(0)); - } - - /// @inheritdoc IValidation - function validateRuntime(uint32 entityId, address sender, uint256, bytes calldata, bytes calldata) - external - view - override - { - // Validate that the sender is the owner of the account or self. - if (sender != owners[entityId][msg.sender]) { - revert NotAuthorized(); - } - return; - } - - /// @inheritdoc IValidation - function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) - external - view - override - returns (uint256) - { - // Validate the user op signature against the owner. - if ( - SignatureChecker.isValidSignatureNow( - owners[entityId][msg.sender], userOpHash.toEthSignedMessageHash(), userOp.signature - ) - ) { - return _SIG_VALIDATION_PASSED; - } - return _SIG_VALIDATION_FAILED; - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Execution view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IValidation - /// @dev The signature is valid if it is signed by the owner's private key - /// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the - /// owner (if the owner is a contract). Note that unlike the signature - /// validation used in `validateUserOp`, this does///*not** wrap the digest in - /// an "Ethereum Signed Message" envelope before checking the signature in - /// the EOA-owner case. - function validateSignature(uint32 entityId, address, bytes32 digest, bytes calldata signature) - external - view - override - returns (bytes4) - { - if (SignatureChecker.isValidSignatureNow(owners[entityId][msg.sender], digest, signature)) { - return _1271_MAGIC_VALUE; - } - return _1271_INVALID; - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Plugin view functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - /// @inheritdoc IPlugin - function pluginManifest() external pure override returns (PluginManifest memory) { - PluginManifest memory manifest; - return manifest; - } - - /// @inheritdoc IPlugin - function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { - PluginMetadata memory metadata; - metadata.name = _NAME; - metadata.version = _VERSION; - metadata.author = _AUTHOR; - - // Permission strings - string memory modifyOwnershipPermission = "Modify Ownership"; - - // Permission descriptions - metadata.permissionDescriptors = new SelectorPermission[](1); - metadata.permissionDescriptors[0] = SelectorPermission({ - functionSelector: this.transferOwnership.selector, - permissionDescription: modifyOwnershipPermission - }); - - return metadata; - } - - // ┏━━━━━━━━━━━━━━━┓ - // ┃ EIP-165 ┃ - // ┗━━━━━━━━━━━━━━━┛ - - /// @inheritdoc BasePlugin - function supportsInterface(bytes4 interfaceId) public view override(BasePlugin, IERC165) returns (bool) { - return interfaceId == type(IValidation).interfaceId || super.supportsInterface(interfaceId); - } - - // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ - // ┃ Internal / Private functions ┃ - // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ - - // Transfers ownership and emits and event. - function _transferOwnership(uint32 id, address newOwner) internal { - address previousOwner = owners[id][msg.sender]; - owners[id][msg.sender] = newOwner; - // Todo: include id in event - emit OwnershipTransferred(msg.sender, previousOwner, newOwner); - } -} diff --git a/src/plugins/validation/ISingleSignerValidation.sol b/src/plugins/validation/ISingleSignerValidation.sol new file mode 100644 index 00000000..2653b752 --- /dev/null +++ b/src/plugins/validation/ISingleSignerValidation.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25; + +import {IValidation} from "../../interfaces/IValidation.sol"; + +interface ISingleSignerValidation is IValidation { + /// @notice This event is emitted when Signer of the account's validation changes. + /// @param account The account whose validation Signer changed. + /// @param entityId The entityId for the account and the signer. + /// @param previousSigner The address of the previous signer. + /// @param newSigner The address of the new signer. + event SignerTransferred( + address indexed account, uint32 indexed entityId, address previousSigner, address newSigner + ); + + error NotAuthorized(); + + /// @notice Transfer Signer of the account's validation to `newSigner`. + /// @param entityId The entityId for the account and the signer. + /// @param newSigner The address of the new signer. + function transferSigner(uint32 entityId, address newSigner) external; + + /// @notice Get the signer of the `account`'s validation. + /// @param entityId The entityId for the account and the signer. + /// @param account The account to get the signer of. + /// @return The address of the signer. + function signerOf(uint32 entityId, address account) external view returns (address); +} diff --git a/src/plugins/validation/SingleSignerValidation.sol b/src/plugins/validation/SingleSignerValidation.sol new file mode 100644 index 00000000..5ae4517d --- /dev/null +++ b/src/plugins/validation/SingleSignerValidation.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25; + +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; +import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; +import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; + +import {IPlugin, PluginManifest, PluginMetadata} from "../../interfaces/IPlugin.sol"; +import {IValidation} from "../../interfaces/IValidation.sol"; +import {BasePlugin} from "../BasePlugin.sol"; +import {ISingleSignerValidation} from "./ISingleSignerValidation.sol"; + +/// @title ECSDA Validation +/// @author ERC-6900 Authors +/// @notice This validation enables any ECDSA (secp256k1 curve) signature validation. It handles installation by +/// each entity (entityId). +/// Note: Uninstallation will NOT disable all installed validation entities. None of the functions are installed on +/// the account. Account states are to be retrieved from this global singleton directly. +/// +/// - This validation supports ERC-1271. The signature is valid if it is signed by the owner's private key +/// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the +/// owner (if the owner is a contract). +/// +/// - This validation supports composition that other validation can relay on entities in this validation +/// to validate partially or fully. +contract SingleSignerValidation is ISingleSignerValidation, BasePlugin { + using ECDSA for bytes32; + using MessageHashUtils for bytes32; + + string public constant NAME = "SingleSigner Validation"; + string public constant VERSION = "1.0.0"; + string public constant AUTHOR = "ERC-6900 Authors"; + + uint256 internal constant _SIG_VALIDATION_PASSED = 0; + uint256 internal constant _SIG_VALIDATION_FAILED = 1; + + // bytes4(keccak256("isValidSignature(bytes32,bytes)")) + bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; + bytes4 internal constant _1271_INVALID = 0xffffffff; + + mapping(uint32 entityId => mapping(address account => address)) public signer; + + /// @inheritdoc ISingleSignerValidation + function signerOf(uint32 entityId, address account) external view returns (address) { + return signer[entityId][account]; + } + + /// @inheritdoc ISingleSignerValidation + function transferSigner(uint32 entityId, address newSigner) external { + _transferSigner(entityId, newSigner); + } + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Plugin interface functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + /// @inheritdoc IPlugin + function pluginManifest() external pure override returns (PluginManifest memory) { + PluginManifest memory manifest; + return manifest; + } + + /// @inheritdoc IPlugin + function pluginMetadata() external pure virtual override returns (PluginMetadata memory) { + PluginMetadata memory metadata; + metadata.name = NAME; + metadata.version = VERSION; + metadata.author = AUTHOR; + return metadata; + } + + /// @inheritdoc IPlugin + function onInstall(bytes calldata data) external override { + (uint32 entityId, address newSigner) = abi.decode(data, (uint32, address)); + _transferSigner(entityId, newSigner); + } + + /// @inheritdoc IPlugin + function onUninstall(bytes calldata data) external override { + // ToDo: what does it mean in the world of composable validation world to uninstall one type of validation + // We can either get rid of all SingleSigner signers. What about the nested ones? + _transferSigner(abi.decode(data, (uint32)), address(0)); + } + + /// @inheritdoc IValidation + function validateUserOp(uint32 entityId, PackedUserOperation calldata userOp, bytes32 userOpHash) + external + view + override + returns (uint256) + { + // Validate the user op signature against the owner. + (address sigSigner,,) = (userOpHash.toEthSignedMessageHash()).tryRecover(userOp.signature); + if (sigSigner == address(0) || sigSigner != signer[entityId][userOp.sender]) { + return _SIG_VALIDATION_FAILED; + } + return _SIG_VALIDATION_PASSED; + } + + /// @inheritdoc IValidation + function validateRuntime( + address account, + uint32 entityId, + address sender, + uint256, + bytes calldata, + bytes calldata + ) external view override { + // Validate that the sender is the owner of the account or self. + if (sender != signer[entityId][account]) { + revert NotAuthorized(); + } + return; + } + + /// @inheritdoc IValidation + /// @dev The signature is valid if it is signed by the owner's private key + /// (if the owner is an EOA) or if it is a valid ERC-1271 signature from the + /// owner (if the owner is a contract). Note that unlike the signature + /// validation used in `validateUserOp`, this does///*not** wrap the digest in + /// an "Ethereum Signed Message" envelope before checking the signature in + /// the EOA-owner case. + function validateSignature(address account, uint32 entityId, address, bytes32 digest, bytes calldata signature) + external + view + override + returns (bytes4) + { + if (SignatureChecker.isValidSignatureNow(signer[entityId][account], digest, signature)) { + return _1271_MAGIC_VALUE; + } + return _1271_INVALID; + } + + // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ + // ┃ Internal / Private functions ┃ + // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + + function _transferSigner(uint32 entityId, address newSigner) internal { + address previousSigner = signer[entityId][msg.sender]; + signer[entityId][msg.sender] = newSigner; + emit SignerTransferred(msg.sender, entityId, previousSigner, newSigner); + } +} diff --git a/test/account/AccountLoupe.t.sol b/test/account/AccountLoupe.t.sol index b65bf3c9..f0670848 100644 --- a/test/account/AccountLoupe.t.sol +++ b/test/account/AccountLoupe.t.sol @@ -115,7 +115,7 @@ contract AccountLoupeTest is CustomValidationTestBase { } function test_pluginLoupe_getValidationHooks() public { - PluginEntity[] memory hooks = account1.getPreValidationHooks(_ownerValidation); + PluginEntity[] memory hooks = account1.getPreValidationHooks(_signerValidation); assertEq(hooks.length, 2); assertEq( @@ -154,7 +154,7 @@ contract AccountLoupeTest is CustomValidationTestBase { bytes[] memory installDatas = new bytes[](2); return ( - _ownerValidation, + _signerValidation, true, true, new bytes4[](0), diff --git a/test/account/AccountReturnData.t.sol b/test/account/AccountReturnData.t.sol index eaf8b8a7..bb081325 100644 --- a/test/account/AccountReturnData.t.sol +++ b/test/account/AccountReturnData.t.sol @@ -10,7 +10,7 @@ import { ResultConsumerPlugin } from "../mocks/plugins/ReturnDataPluginMocks.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID} from "../utils/TestConstants.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID} from "../utils/TestConstants.sol"; // Tests all the different ways that return data can be read from plugins through an account contract AccountReturnDataTest is AccountTestBase { @@ -58,7 +58,7 @@ contract AccountReturnDataTest is AccountTestBase { (address(regularResultContract), 0, abi.encodeCall(RegularResultContract.foo, ())) ), _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) @@ -86,7 +86,7 @@ contract AccountReturnDataTest is AccountTestBase { bytes memory retData = account1.executeWithAuthorization( abi.encodeCall(account1.executeBatch, (calls)), _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) diff --git a/test/account/GlobalValidationTest.t.sol b/test/account/GlobalValidationTest.t.sol index 433116bc..7ef7fdc5 100644 --- a/test/account/GlobalValidationTest.t.sol +++ b/test/account/GlobalValidationTest.t.sol @@ -26,7 +26,8 @@ contract GlobalValidationTest is AccountTestBase { account2 = UpgradeableModularAccount(payable(factory.getAddress(owner2, 0))); vm.deal(address(account2), 100 ether); - _ownerValidation = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _signerValidation = + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID); ethRecipient = makeAddr("ethRecipient"); vm.deal(ethRecipient, 1 wei); @@ -48,7 +49,7 @@ contract GlobalValidationTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -65,7 +66,7 @@ contract GlobalValidationTest is AccountTestBase { vm.prank(owner2); account2.executeWithAuthorization( abi.encodeCall(UpgradeableModularAccount.execute, (ethRecipient, 1 wei, "")), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, "") ); assertEq(ethRecipient.balance, 2 wei); diff --git a/test/account/MultiValidation.t.sol b/test/account/MultiValidation.t.sol index 2dbae711..641fcefb 100644 --- a/test/account/MultiValidation.t.sol +++ b/test/account/MultiValidation.t.sol @@ -12,22 +12,22 @@ import {PluginEntity} from "../../src/interfaces/IPluginManager.sol"; import {IStandardExecutor} from "../../src/interfaces/IStandardExecutor.sol"; import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {SingleSignerValidation} from "../../src/plugins/validation/SingleSignerValidation.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID} from "../utils/TestConstants.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID} from "../utils/TestConstants.sol"; contract MultiValidationTest is AccountTestBase { using ECDSA for bytes32; using MessageHashUtils for bytes32; - SingleOwnerPlugin public validator2; + SingleSignerValidation public validator2; address public owner2; uint256 public owner2Key; function setUp() public { - validator2 = new SingleOwnerPlugin(); + validator2 = new SingleSignerValidation(); (owner2, owner2Key) = makeAddrAndKey("owner2"); } @@ -35,16 +35,16 @@ contract MultiValidationTest is AccountTestBase { function test_overlappingValidationInstall() public { vm.prank(address(entryPoint)); account1.installValidation( - ValidationConfigLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID, true, true), + ValidationConfigLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID, true, true), new bytes4[](0), - abi.encode(TEST_DEFAULT_OWNER_FUNCTION_ID, owner2), + abi.encode(TEST_DEFAULT_VALIDATION_ENTITY_ID, owner2), "", "" ); PluginEntity[] memory validations = new PluginEntity[](2); - validations[0] = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); - validations[1] = PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID); + validations[0] = PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID); + validations[1] = PluginEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID); bytes4[] memory selectors0 = account1.getSelectors(validations[0]); bytes4[] memory selectors1 = account1.getSelectors(validations[1]); @@ -64,14 +64,14 @@ contract MultiValidationTest is AccountTestBase { abi.encodeWithSelector( UpgradeableModularAccount.RuntimeValidationFunctionReverted.selector, address(validator2), - 0, + 1, abi.encodeWithSignature("NotAuthorized()") ) ); account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) ); @@ -79,7 +79,7 @@ contract MultiValidationTest is AccountTestBase { account1.executeWithAuthorization( abi.encodeCall(IStandardExecutor.execute, (address(0), 0, "")), _encodeSignature( - PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), GLOBAL_VALIDATION, "" + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) ); } @@ -105,7 +105,7 @@ contract MultiValidationTest is AccountTestBase { bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -120,7 +120,7 @@ contract MultiValidationTest is AccountTestBase { userOp.nonce = 1; (v, r, s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PluginEntityLib.pack(address(validator2), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(validator2), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); diff --git a/test/account/PerHookData.t.sol b/test/account/PerHookData.t.sol index 546a156c..bcb53171 100644 --- a/test/account/PerHookData.t.sol +++ b/test/account/PerHookData.t.sol @@ -37,8 +37,9 @@ contract PerHookDataTest is CustomValidationTestBase { PreValidationHookData[] memory preValidationHookData = new PreValidationHookData[](1); preValidationHookData[0] = PreValidationHookData({index: 0, validationData: abi.encodePacked(_counter)}); - userOp.signature = - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature( + _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v) + ); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -59,8 +60,9 @@ contract PerHookDataTest is CustomValidationTestBase { validationData: abi.encodePacked(address(0x1234123412341234123412341234123412341234)) }); - userOp.signature = - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature( + _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v) + ); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -80,7 +82,7 @@ contract PerHookDataTest is CustomValidationTestBase { (PackedUserOperation memory userOp, bytes32 userOpHash) = _getCounterUserOP(); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -104,8 +106,9 @@ contract PerHookDataTest is CustomValidationTestBase { preValidationHookData[0] = PreValidationHookData({index: 0, validationData: abi.encodePacked(_counter)}); preValidationHookData[1] = PreValidationHookData({index: 1, validationData: abi.encodePacked(_counter)}); - userOp.signature = - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature( + _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v) + ); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -142,8 +145,9 @@ contract PerHookDataTest is CustomValidationTestBase { PreValidationHookData[] memory preValidationHookData = new PreValidationHookData[](1); preValidationHookData[0] = PreValidationHookData({index: 0, validationData: abi.encodePacked(beneficiary)}); - userOp.signature = - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature( + _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v) + ); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -166,8 +170,9 @@ contract PerHookDataTest is CustomValidationTestBase { PreValidationHookData[] memory preValidationHookData = new PreValidationHookData[](1); preValidationHookData[0] = PreValidationHookData({index: 0, validationData: ""}); - userOp.signature = - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature( + _signerValidation, GLOBAL_VALIDATION, preValidationHookData, abi.encodePacked(r, s, v) + ); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -195,7 +200,7 @@ contract PerHookDataTest is CustomValidationTestBase { UpgradeableModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ())) ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "") ); assertEq(_counter.number(), 1); @@ -222,7 +227,7 @@ contract PerHookDataTest is CustomValidationTestBase { UpgradeableModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ())) ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "") ); } @@ -241,7 +246,7 @@ contract PerHookDataTest is CustomValidationTestBase { UpgradeableModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ())) ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, "") ); } @@ -259,7 +264,7 @@ contract PerHookDataTest is CustomValidationTestBase { UpgradeableModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ())) ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "") ); } @@ -280,7 +285,7 @@ contract PerHookDataTest is CustomValidationTestBase { ); account1.executeWithAuthorization( abi.encodeCall(UpgradeableModularAccount.execute, (beneficiary, 1 wei, "")), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "") ); } @@ -295,7 +300,7 @@ contract PerHookDataTest is CustomValidationTestBase { UpgradeableModularAccount.execute, (address(_counter), 0 wei, abi.encodeCall(Counter.increment, ())) ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, preValidationHookData, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, preValidationHookData, "") ); } @@ -341,11 +346,11 @@ contract PerHookDataTest is CustomValidationTestBase { bytes memory packedPreValidationHooks = abi.encode(preValidationHooks, preValidationHookData); return ( - _ownerValidation, + _signerValidation, true, true, new bytes4[](0), - abi.encode(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1), + abi.encode(TEST_DEFAULT_VALIDATION_ENTITY_ID, owner1), packedPreValidationHooks, "" ); diff --git a/test/account/SelfCallAuthorization.t.sol b/test/account/SelfCallAuthorization.t.sol index 516f9f34..c490eea4 100644 --- a/test/account/SelfCallAuthorization.t.sol +++ b/test/account/SelfCallAuthorization.t.sol @@ -303,7 +303,7 @@ contract SelfCallAuthorizationTest is AccountTestBase { UpgradeableModularAccount.installValidation, (ValidationConfigLib.pack(comprehensivePluginValidation, false, false), selectors, "", "", "") ), - _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, "") + _encodeSignature(_signerValidation, GLOBAL_VALIDATION, "") ); } diff --git a/test/account/UpgradeableModularAccount.t.sol b/test/account/UpgradeableModularAccount.t.sol index dda78c66..54af25c6 100644 --- a/test/account/UpgradeableModularAccount.t.sol +++ b/test/account/UpgradeableModularAccount.t.sol @@ -14,14 +14,14 @@ import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {IAccountLoupe} from "../../src/interfaces/IAccountLoupe.sol"; import {IPluginManager} from "../../src/interfaces/IPluginManager.sol"; import {Call} from "../../src/interfaces/IStandardExecutor.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {SingleSignerValidation} from "../../src/plugins/validation/SingleSignerValidation.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; import {Counter} from "../mocks/Counter.sol"; import {ComprehensivePlugin} from "../mocks/plugins/ComprehensivePlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID} from "../utils/TestConstants.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID} from "../utils/TestConstants.sol"; contract UpgradeableModularAccountTest is AccountTestBase { using ECDSA for bytes32; @@ -77,7 +77,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -95,9 +95,9 @@ contract UpgradeableModularAccountTest is AccountTestBase { callData: abi.encodeCall( UpgradeableModularAccount.execute, ( - address(singleOwnerPlugin), + address(singleSignerValidation), 0, - abi.encodeCall(SingleOwnerPlugin.transferOwnership, (TEST_DEFAULT_OWNER_FUNCTION_ID, owner2)) + abi.encodeCall(SingleSignerValidation.transferSigner, (TEST_DEFAULT_VALIDATION_ENTITY_ID, owner2)) ) ), accountGasLimits: _encodeGas(VERIFICATION_GAS_LIMIT, CALL_GAS_LIMIT), @@ -110,7 +110,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -136,7 +136,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner2Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -162,7 +162,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -190,7 +190,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -221,7 +221,7 @@ contract UpgradeableModularAccountTest is AccountTestBase { // Generate signature bytes32 userOpHash = entryPoint.getUserOpHash(userOp); (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); - userOp.signature = _encodeSignature(_ownerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); + userOp.signature = _encodeSignature(_signerValidation, GLOBAL_VALIDATION, abi.encodePacked(r, s, v)); PackedUserOperation[] memory userOps = new PackedUserOperation[](1); userOps[0] = userOp; @@ -409,16 +409,16 @@ contract UpgradeableModularAccountTest is AccountTestBase { } function test_transferOwnership() public { - assertEq(singleOwnerPlugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, address(account1)), owner1); + assertEq(singleSignerValidation.signerOf(TEST_DEFAULT_VALIDATION_ENTITY_ID, address(account1)), owner1); vm.prank(address(entryPoint)); account1.execute( - address(singleOwnerPlugin), + address(singleSignerValidation), 0, - abi.encodeCall(SingleOwnerPlugin.transferOwnership, (TEST_DEFAULT_OWNER_FUNCTION_ID, owner2)) + abi.encodeCall(SingleSignerValidation.transferSigner, (TEST_DEFAULT_VALIDATION_ENTITY_ID, owner2)) ); - assertEq(singleOwnerPlugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, address(account1)), owner2); + assertEq(singleSignerValidation.signerOf(TEST_DEFAULT_VALIDATION_ENTITY_ID, address(account1)), owner2); } function test_isValidSignature() public { @@ -426,10 +426,10 @@ contract UpgradeableModularAccountTest is AccountTestBase { (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, message); - // singleOwnerPlugin.ownerOf(address(account1)); + // singleSignerValidation.ownerOf(address(account1)); bytes memory signature = - abi.encodePacked(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID, r, s, v); + abi.encodePacked(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID, r, s, v); bytes4 validationResult = IERC1271(address(account1)).isValidSignature(message, signature); diff --git a/test/mocks/MSCAFactoryFixture.sol b/test/mocks/SingleSignerFactoryFixture.sol similarity index 79% rename from test/mocks/MSCAFactoryFixture.sol rename to test/mocks/SingleSignerFactoryFixture.sol index 8ca3a51f..b3da73ec 100644 --- a/test/mocks/MSCAFactoryFixture.sol +++ b/test/mocks/SingleSignerFactoryFixture.sol @@ -5,34 +5,32 @@ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; -import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {SingleSignerValidation} from "../../src/plugins/validation/SingleSignerValidation.sol"; import {OptimizedTest} from "../utils/OptimizedTest.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID} from "../utils/TestConstants.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID} from "../utils/TestConstants.sol"; -/** - * @title MSCAFactoryFixture - * @dev a factory that initializes UpgradeableModularAccounts with a single plugin, SingleOwnerPlugin - * intended for unit tests and local development, not for production. - */ -contract MSCAFactoryFixture is OptimizedTest { +contract SingleSignerFactoryFixture is OptimizedTest { UpgradeableModularAccount public accountImplementation; - SingleOwnerPlugin public singleOwnerPlugin; + SingleSignerValidation public singleSignerValidation; bytes32 private immutable _PROXY_BYTECODE_HASH; uint32 public constant UNSTAKE_DELAY = 1 weeks; IEntryPoint public entryPoint; - constructor(IEntryPoint _entryPoint, SingleOwnerPlugin _singleOwnerPlugin) { + address public self; + + constructor(IEntryPoint _entryPoint, SingleSignerValidation _singleSignerValidation) { entryPoint = _entryPoint; accountImplementation = _deployUpgradeableModularAccount(_entryPoint); _PROXY_BYTECODE_HASH = keccak256( abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(address(accountImplementation), "")) ); - singleOwnerPlugin = _singleOwnerPlugin; + singleSignerValidation = _singleSignerValidation; + self = address(this); } /** @@ -47,13 +45,15 @@ contract MSCAFactoryFixture is OptimizedTest { // short circuit if exists if (addr.code.length == 0) { - bytes memory pluginInstallData = abi.encode(TEST_DEFAULT_OWNER_FUNCTION_ID, owner); + bytes memory pluginInstallData = abi.encode(TEST_DEFAULT_VALIDATION_ENTITY_ID, owner); // not necessary to check return addr since next call will fail if so new ERC1967Proxy{salt: getSalt(owner, salt)}(address(accountImplementation), ""); // point proxy to actual implementation and init plugins UpgradeableModularAccount(payable(addr)).initializeWithValidation( - ValidationConfigLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID, true, true), + ValidationConfigLib.pack( + address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID, true, true + ), new bytes4[](0), pluginInstallData, "", diff --git a/test/mocks/plugins/ComprehensivePlugin.sol b/test/mocks/plugins/ComprehensivePlugin.sol index 9bff9237..306e96a4 100644 --- a/test/mocks/plugins/ComprehensivePlugin.sol +++ b/test/mocks/plugins/ComprehensivePlugin.sol @@ -85,7 +85,7 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba revert NotImplemented(); } - function validateRuntime(uint32 entityId, address, uint256, bytes calldata, bytes calldata) + function validateRuntime(address, uint32 entityId, address, uint256, bytes calldata, bytes calldata) external pure override @@ -96,7 +96,11 @@ contract ComprehensivePlugin is IValidation, IValidationHook, IExecutionHook, Ba revert NotImplemented(); } - function validateSignature(uint32 entityId, address, bytes32, bytes calldata) external pure returns (bytes4) { + function validateSignature(address, uint32 entityId, address, bytes32, bytes calldata) + external + pure + returns (bytes4) + { if (entityId == uint32(EntityId.SIG_VALIDATION)) { return 0xffffffff; } diff --git a/test/mocks/plugins/ReturnDataPluginMocks.sol b/test/mocks/plugins/ReturnDataPluginMocks.sol index 6bab7b0b..3adbb6e4 100644 --- a/test/mocks/plugins/ReturnDataPluginMocks.sol +++ b/test/mocks/plugins/ReturnDataPluginMocks.sol @@ -76,13 +76,16 @@ contract ResultConsumerPlugin is BasePlugin, IValidation { revert NotImplemented(); } - function validateRuntime(uint32, address sender, uint256, bytes calldata, bytes calldata) external view { + function validateRuntime(address, uint32, address sender, uint256, bytes calldata, bytes calldata) + external + view + { if (sender != address(this)) { revert NotAuthorized(); } } - function validateSignature(uint32, address, bytes32, bytes calldata) external pure returns (bytes4) { + function validateSignature(address, uint32, address, bytes32, bytes calldata) external pure returns (bytes4) { revert NotImplemented(); } diff --git a/test/mocks/plugins/ValidationPluginMocks.sol b/test/mocks/plugins/ValidationPluginMocks.sol index af57eaaa..8d9b6ce2 100644 --- a/test/mocks/plugins/ValidationPluginMocks.sol +++ b/test/mocks/plugins/ValidationPluginMocks.sol @@ -58,7 +58,12 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook revert NotImplemented(); } - function validateSignature(uint32, address, bytes32, bytes calldata) external pure override returns (bytes4) { + function validateSignature(address, uint32, address, bytes32, bytes calldata) + external + pure + override + returns (bytes4) + { revert NotImplemented(); } @@ -73,7 +78,11 @@ abstract contract MockBaseUserOpValidationPlugin is IValidation, IValidationHook revert NotImplemented(); } - function validateRuntime(uint32, address, uint256, bytes calldata, bytes calldata) external pure override { + function validateRuntime(address, uint32, address, uint256, bytes calldata, bytes calldata) + external + pure + override + { revert NotImplemented(); } } diff --git a/test/plugin/SingleOwnerPlugin.t.sol b/test/plugin/SingleOwnerPlugin.t.sol deleted file mode 100644 index ddfe4d41..00000000 --- a/test/plugin/SingleOwnerPlugin.t.sol +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.sol"; -import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; - -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; - -import {ContractOwner} from "../mocks/ContractOwner.sol"; -import {OptimizedTest} from "../utils/OptimizedTest.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID} from "../utils/TestConstants.sol"; - -contract SingleOwnerPluginTest is OptimizedTest { - using ECDSA for bytes32; - using MessageHashUtils for bytes32; - - SingleOwnerPlugin public plugin; - EntryPoint public entryPoint; - - bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; - address public a; - address public b; - - address public owner1; - address public owner2; - ContractOwner public contractOwner; - - // Event declarations (needed for vm.expectEmit) - event OwnershipTransferred(address indexed account, address indexed previousOwner, address indexed newOwner); - - function setUp() public { - plugin = _deploySingleOwnerPlugin(); - entryPoint = new EntryPoint(); - - a = makeAddr("a"); - b = makeAddr("b"); - owner1 = makeAddr("owner1"); - owner2 = makeAddr("owner2"); - contractOwner = new ContractOwner(); - } - - // Tests: - // - uninitialized owner is zero address - // - transferOwnership result is returned via owner afterwards - // - transferOwnership emits OwnershipTransferred event - // - owner() returns correct value after transferOwnership - // - owner() does not return a different account's owner - // - requireFromOwner succeeds when called by owner - // - requireFromOwner reverts when called by non-owner - - function test_uninitializedOwner() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - } - - function test_ownerInitialization() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - } - - function test_ownerInitializationEvent() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(a, address(0), owner1); - - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - } - - function test_ownerMigration() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner2); - assertEq(owner2, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - } - - function test_ownerMigrationEvents() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(a, address(0), owner1); - - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - - vm.expectEmit(true, true, true, true); - emit OwnershipTransferred(a, owner1, owner2); - - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner2); - assertEq(owner2, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - } - - function test_ownerForSender() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - vm.startPrank(b); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, b)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner2); - assertEq(owner2, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, b)); - } - - function test_requireOwner() public { - vm.startPrank(a); - assertEq(address(0), plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1); - assertEq(owner1, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - plugin.validateRuntime(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1, 0, "", ""); - - vm.startPrank(b); - vm.expectRevert(SingleOwnerPlugin.NotAuthorized.selector); - plugin.validateRuntime(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1, 0, "", ""); - } - - function testFuzz_validateUserOpSig(string memory salt, PackedUserOperation memory userOp) public { - // range bound the possible set of priv keys - (address signer, uint256 privateKey) = makeAddrAndKey(salt); - - vm.startPrank(a); - bytes32 userOpHash = entryPoint.getUserOpHash(userOp); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, userOpHash.toEthSignedMessageHash()); - - // sig cannot cover the whole userop struct since userop struct has sig field - userOp.signature = abi.encodePacked(r, s, v); - - // sig check should fail - uint256 success = plugin.validateUserOp(TEST_DEFAULT_OWNER_FUNCTION_ID, userOp, userOpHash); - assertEq(success, 1); - - // transfer ownership to signer - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, signer); - assertEq(signer, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - - // sig check should pass - success = plugin.validateUserOp(TEST_DEFAULT_OWNER_FUNCTION_ID, userOp, userOpHash); - assertEq(success, 0); - } - - function testFuzz_isValidSignatureForEOAOwner(string memory salt, bytes32 digest) public { - // range bound the possible set of priv keys - (address signer, uint256 privateKey) = makeAddrAndKey(salt); - - vm.startPrank(a); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); - - // sig check should fail - assertEq( - plugin.validateSignature( - TEST_DEFAULT_OWNER_FUNCTION_ID, address(this), digest, abi.encodePacked(r, s, v) - ), - bytes4(0xFFFFFFFF) - ); - - // transfer ownership to signer - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, signer); - assertEq(signer, plugin.owners(TEST_DEFAULT_OWNER_FUNCTION_ID, a)); - - // sig check should pass - assertEq( - plugin.validateSignature( - TEST_DEFAULT_OWNER_FUNCTION_ID, address(this), digest, abi.encodePacked(r, s, v) - ), - _1271_MAGIC_VALUE - ); - } - - function testFuzz_isValidSignatureForContractOwner(bytes32 digest) public { - vm.startPrank(a); - plugin.transferOwnership(TEST_DEFAULT_OWNER_FUNCTION_ID, address(contractOwner)); - bytes memory signature = contractOwner.sign(digest); - assertEq( - plugin.validateSignature(TEST_DEFAULT_OWNER_FUNCTION_ID, address(this), digest, signature), - _1271_MAGIC_VALUE - ); - } -} diff --git a/test/plugin/TokenReceiverPlugin.t.sol b/test/plugin/TokenReceiverPlugin.t.sol index 2f52a988..1e198f0b 100644 --- a/test/plugin/TokenReceiverPlugin.t.sol +++ b/test/plugin/TokenReceiverPlugin.t.sol @@ -8,7 +8,7 @@ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Re import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; -import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; +import {SingleSignerFactoryFixture} from "../mocks/SingleSignerFactoryFixture.sol"; import {MockERC721} from "../mocks/MockERC721.sol"; import {MockERC1155} from "../mocks/MockERC1155.sol"; import {OptimizedTest} from "../utils/OptimizedTest.sol"; @@ -33,7 +33,8 @@ contract TokenReceiverPluginTest is OptimizedTest, IERC1155Receiver { function setUp() public { entryPoint = new EntryPoint(); - MSCAFactoryFixture factory = new MSCAFactoryFixture(entryPoint, _deploySingleOwnerPlugin()); + SingleSignerFactoryFixture factory = + new SingleSignerFactoryFixture(entryPoint, _deploySingleSignerValidation()); acct = factory.createAccount(address(this), 0); plugin = _deployTokenReceiverPlugin(); diff --git a/test/samples/AllowlistPlugin.t.sol b/test/samples/AllowlistPlugin.t.sol index 4a8fc4ff..441fbdcb 100644 --- a/test/samples/AllowlistPlugin.t.sol +++ b/test/samples/AllowlistPlugin.t.sol @@ -305,11 +305,11 @@ contract AllowlistPluginTest is CustomValidationTestBase { bytes memory packedPreValidationHooks = abi.encode(preValidationHooks, preValidationHookData); return ( - _ownerValidation, + _signerValidation, true, true, new bytes4[](0), - abi.encode(TEST_DEFAULT_OWNER_FUNCTION_ID, owner1), + abi.encode(TEST_DEFAULT_VALIDATION_ENTITY_ID, owner1), packedPreValidationHooks, "" ); diff --git a/test/utils/AccountTestBase.sol b/test/utils/AccountTestBase.sol index a8f84a21..526365d0 100644 --- a/test/utils/AccountTestBase.sol +++ b/test/utils/AccountTestBase.sol @@ -5,38 +5,40 @@ import {EntryPoint} from "@eth-infinitism/account-abstraction/core/EntryPoint.so import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; +import {SingleSignerValidation} from "../../src/plugins/validation/SingleSignerValidation.sol"; import {PluginEntity, PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; import {OptimizedTest} from "./OptimizedTest.sol"; -import {TEST_DEFAULT_OWNER_FUNCTION_ID as EXT_CONST_TEST_DEFAULT_OWNER_FUNCTION_ID} from "./TestConstants.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID as EXT_CONST_TEST_DEFAULT_VALIDATION_ENTITY_ID} from + "./TestConstants.sol"; -import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; +import {SingleSignerFactoryFixture} from "../mocks/SingleSignerFactoryFixture.sol"; /// @dev This contract handles common boilerplate setup for tests using UpgradeableModularAccount with -/// SingleOwnerPlugin. +/// SingleSignerValidation. abstract contract AccountTestBase is OptimizedTest { using PluginEntityLib for PluginEntity; using MessageHashUtils for bytes32; EntryPoint public entryPoint; address payable public beneficiary; - SingleOwnerPlugin public singleOwnerPlugin; - MSCAFactoryFixture public factory; + + SingleSignerValidation public singleSignerValidation; + SingleSignerFactoryFixture public factory; address public owner1; uint256 public owner1Key; UpgradeableModularAccount public account1; - PluginEntity internal _ownerValidation; + PluginEntity internal _signerValidation; uint8 public constant SELECTOR_ASSOCIATED_VALIDATION = 0; uint8 public constant GLOBAL_VALIDATION = 1; // Re-declare the constant to prevent derived test contracts from having to import it - uint32 public constant TEST_DEFAULT_OWNER_FUNCTION_ID = EXT_CONST_TEST_DEFAULT_OWNER_FUNCTION_ID; + uint32 public constant TEST_DEFAULT_VALIDATION_ENTITY_ID = EXT_CONST_TEST_DEFAULT_VALIDATION_ENTITY_ID; uint256 public constant CALL_GAS_LIMIT = 100000; uint256 public constant VERIFICATION_GAS_LIMIT = 1200000; @@ -51,13 +53,14 @@ abstract contract AccountTestBase is OptimizedTest { (owner1, owner1Key) = makeAddrAndKey("owner1"); beneficiary = payable(makeAddr("beneficiary")); - singleOwnerPlugin = _deploySingleOwnerPlugin(); - factory = new MSCAFactoryFixture(entryPoint, singleOwnerPlugin); + singleSignerValidation = _deploySingleSignerValidation(); + factory = new SingleSignerFactoryFixture(entryPoint, singleSignerValidation); account1 = factory.createAccount(owner1, 0); vm.deal(address(account1), 100 ether); - _ownerValidation = PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID); + _signerValidation = + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID); } function _runExecUserOp(address target, bytes memory callData) internal { @@ -100,7 +103,7 @@ abstract contract AccountTestBase is OptimizedTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); userOp.signature = _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, abi.encodePacked(r, s, v) ); @@ -153,7 +156,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) @@ -168,7 +171,7 @@ abstract contract AccountTestBase is OptimizedTest { account1.executeWithAuthorization( callData, _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) @@ -182,15 +185,15 @@ abstract contract AccountTestBase is OptimizedTest { abi.encodeCall( account1.execute, ( - address(singleOwnerPlugin), + address(singleSignerValidation), 0, abi.encodeCall( - SingleOwnerPlugin.transferOwnership, (TEST_DEFAULT_OWNER_FUNCTION_ID, address(this)) + SingleSignerValidation.transferSigner, (TEST_DEFAULT_VALIDATION_ENTITY_ID, address(this)) ) ) ), _encodeSignature( - PluginEntityLib.pack(address(singleOwnerPlugin), TEST_DEFAULT_OWNER_FUNCTION_ID), + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), GLOBAL_VALIDATION, "" ) diff --git a/test/utils/OptimizedTest.sol b/test/utils/OptimizedTest.sol index f9431acc..d884193f 100644 --- a/test/utils/OptimizedTest.sol +++ b/test/utils/OptimizedTest.sol @@ -6,7 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {SingleOwnerPlugin} from "../../src/plugins/owner/SingleOwnerPlugin.sol"; +import {SingleSignerValidation} from "../../src/plugins/validation/SingleSignerValidation.sol"; import {TokenReceiverPlugin} from "../../src/plugins/TokenReceiverPlugin.sol"; /// @dev This contract provides functions to deploy optimized (via IR) precompiled contracts. By compiling just @@ -44,15 +44,17 @@ abstract contract OptimizedTest is Test { : new UpgradeableModularAccount(entryPoint); } - function _deploySingleOwnerPlugin() internal returns (SingleOwnerPlugin) { - return _isOptimizedTest() - ? SingleOwnerPlugin(deployCode("out-optimized/SingleOwnerPlugin.sol/SingleOwnerPlugin.json")) - : new SingleOwnerPlugin(); - } - function _deployTokenReceiverPlugin() internal returns (TokenReceiverPlugin) { return _isOptimizedTest() ? TokenReceiverPlugin(deployCode("out-optimized/TokenReceiverPlugin.sol/TokenReceiverPlugin.json")) : new TokenReceiverPlugin(); } + + function _deploySingleSignerValidation() internal returns (SingleSignerValidation) { + return _isOptimizedTest() + ? SingleSignerValidation( + deployCode("out-optimized/SingleSignerValidation.sol/SingleSignerValidation.json") + ) + : new SingleSignerValidation(); + } } diff --git a/test/utils/TestConstants.sol b/test/utils/TestConstants.sol index f9cc8c90..c15b2dd3 100644 --- a/test/utils/TestConstants.sol +++ b/test/utils/TestConstants.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.25; -uint32 constant TEST_DEFAULT_OWNER_FUNCTION_ID = 0; +uint32 constant TEST_DEFAULT_VALIDATION_ENTITY_ID = 1; diff --git a/test/validation/SingleSignerValidation.t.sol b/test/validation/SingleSignerValidation.t.sol new file mode 100644 index 00000000..a983ea93 --- /dev/null +++ b/test/validation/SingleSignerValidation.t.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; +import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; + +import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; +import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; + +import {AccountTestBase} from "../utils/AccountTestBase.sol"; +import {TEST_DEFAULT_VALIDATION_ENTITY_ID} from "../utils/TestConstants.sol"; +import {ContractOwner} from "../mocks/ContractOwner.sol"; + +contract SingleSignerValidationTest is AccountTestBase { + using MessageHashUtils for bytes32; + + bytes4 internal constant _1271_MAGIC_VALUE = 0x1626ba7e; + + address public ethRecipient; + address public owner2; + uint256 public owner2Key; + UpgradeableModularAccount public account; + + ContractOwner public contractOwner; + + function setUp() public { + ethRecipient = makeAddr("ethRecipient"); + (owner2, owner2Key) = makeAddrAndKey("owner2"); + account = factory.createAccount(owner1, 0); + vm.deal(address(account), 100 ether); + + contractOwner = new ContractOwner(); + } + + function test_userOpValidation() public { + PackedUserOperation memory userOp = PackedUserOperation({ + sender: address(account), + nonce: 0, + initCode: "", + callData: abi.encodeCall(UpgradeableModularAccount.execute, (ethRecipient, 1 wei, "")), + accountGasLimits: _encodeGas(VERIFICATION_GAS_LIMIT, CALL_GAS_LIMIT), + preVerificationGas: 0, + gasFees: _encodeGas(1, 1), + paymasterAndData: "", + signature: "" + }); + + // Generate signature + bytes32 userOpHash = entryPoint.getUserOpHash(userOp); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, userOpHash.toEthSignedMessageHash()); + userOp.signature = _encodeSignature( + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), + GLOBAL_VALIDATION, + abi.encodePacked(r, s, v) + ); + + PackedUserOperation[] memory userOps = new PackedUserOperation[](1); + userOps[0] = userOp; + + entryPoint.handleOps(userOps, beneficiary); + + assertEq(ethRecipient.balance, 1 wei); + } + + function test_runtimeValidate() public { + vm.prank(owner1); + account.executeWithAuthorization( + abi.encodeCall(UpgradeableModularAccount.execute, (ethRecipient, 1 wei, "")), + _encodeSignature( + PluginEntityLib.pack(address(singleSignerValidation), TEST_DEFAULT_VALIDATION_ENTITY_ID), + GLOBAL_VALIDATION, + "" + ) + ); + assertEq(ethRecipient.balance, 1 wei); + } + + function test_runtime_with2SameValidationInstalled() public { + uint32 newEntityId = TEST_DEFAULT_VALIDATION_ENTITY_ID + 1; + vm.prank(address(entryPoint)); + account.installValidation( + ValidationConfigLib.pack(address(singleSignerValidation), newEntityId, true, false), + new bytes4[](0), + abi.encode(newEntityId, owner2), + "", + "" + ); + + vm.prank(owner2); + account.executeWithAuthorization( + abi.encodeCall(UpgradeableModularAccount.execute, (ethRecipient, 1 wei, "")), + _encodeSignature( + PluginEntityLib.pack(address(singleSignerValidation), newEntityId), GLOBAL_VALIDATION, "" + ) + ); + assertEq(ethRecipient.balance, 1 wei); + } + + function testFuzz_isValidSignatureForEOAOwner(string memory salt, bytes32 digest) public { + // range bound the possible set of priv keys + (address signer, uint256 privateKey) = makeAddrAndKey(salt); + + address accountAddr = address(account); + + vm.startPrank(accountAddr); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + + // sig check should fail + assertEq( + singleSignerValidation.validateSignature( + accountAddr, TEST_DEFAULT_VALIDATION_ENTITY_ID, address(this), digest, abi.encodePacked(r, s, v) + ), + bytes4(0xFFFFFFFF) + ); + + // transfer ownership to signer + singleSignerValidation.transferSigner(TEST_DEFAULT_VALIDATION_ENTITY_ID, signer); + assertEq(signer, singleSignerValidation.signerOf(TEST_DEFAULT_VALIDATION_ENTITY_ID, accountAddr)); + + // sig check should pass + assertEq( + singleSignerValidation.validateSignature( + accountAddr, TEST_DEFAULT_VALIDATION_ENTITY_ID, address(this), digest, abi.encodePacked(r, s, v) + ), + _1271_MAGIC_VALUE + ); + } + + function testFuzz_isValidSignatureForContractOwner(bytes32 digest) public { + address accountAddr = address(account); + vm.startPrank(accountAddr); + singleSignerValidation.transferSigner(TEST_DEFAULT_VALIDATION_ENTITY_ID, address(contractOwner)); + bytes memory signature = contractOwner.sign(digest); + assertEq( + singleSignerValidation.validateSignature( + accountAddr, TEST_DEFAULT_VALIDATION_ENTITY_ID, address(this), digest, signature + ), + _1271_MAGIC_VALUE + ); + } +} From 87a45aade9cafe7cc35f91f9afbae7b5ca3e6145 Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 16 Jul 2024 17:44:49 -0700 Subject: [PATCH 11/11] resolve merge conflict for entityId --- src/plugins/ERC20TokenLimitPlugin.sol | 32 +++++++++--------- src/plugins/NativeTokenLimitPlugin.sol | 36 ++++++++++---------- standard/ERCs/erc-6900.md | 4 +-- test/libraries/FunctionReferenceLib.t.sol | 40 ----------------------- test/plugin/ERC20TokenLimitPlugin.t.sol | 19 +++++------ test/plugin/NativeTokenLimitPlugin.t.sol | 19 +++++------ 6 files changed, 52 insertions(+), 98 deletions(-) delete mode 100644 test/libraries/FunctionReferenceLib.t.sol diff --git a/src/plugins/ERC20TokenLimitPlugin.sol b/src/plugins/ERC20TokenLimitPlugin.sol index 1df5bcfd..4e9c17ac 100644 --- a/src/plugins/ERC20TokenLimitPlugin.sol +++ b/src/plugins/ERC20TokenLimitPlugin.sol @@ -38,20 +38,20 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { string internal constant _VERSION = "1.0.0"; string internal constant _AUTHOR = "ERC-6900 Authors"; - mapping(uint8 functionId => mapping(address token => mapping(address account => uint256 limit))) public limits; + mapping(uint32 entityId => mapping(address token => mapping(address account => uint256 limit))) public limits; AssociatedLinkedListSet internal _tokenList; error ExceededTokenLimit(); error ExceededNumberOfEntities(); error SelectorNotAllowed(); - function updateLimits(uint8 functionId, address token, uint256 newLimit) external { + function updateLimits(uint32 entityId, address token, uint256 newLimit) external { _tokenList.tryAdd(msg.sender, SetValue.wrap(bytes30(bytes20(token)))); - limits[functionId][token][msg.sender] = newLimit; + limits[entityId][token][msg.sender] = newLimit; } /// @inheritdoc IExecutionHook - function preExecutionHook(uint8 functionId, address, uint256, bytes calldata data) + function preExecutionHook(uint32 entityId, address, uint256, bytes calldata data) external override returns (bytes memory) @@ -61,13 +61,13 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { if (selector == IStandardExecutor.execute.selector) { (address token,, bytes memory innerCalldata) = abi.decode(callData, (address, uint256, bytes)); if (_tokenList.contains(msg.sender, SetValue.wrap(bytes30(bytes20(token))))) { - _decrementLimit(functionId, token, innerCalldata); + _decrementLimit(entityId, token, innerCalldata); } } else if (selector == IStandardExecutor.executeBatch.selector) { Call[] memory calls = abi.decode(callData, (Call[])); for (uint256 i = 0; i < calls.length; i++) { if (_tokenList.contains(msg.sender, SetValue.wrap(bytes30(bytes20(calls[i].target))))) { - _decrementLimit(functionId, calls[i].target, calls[i].data); + _decrementLimit(entityId, calls[i].target, calls[i].data); } } } @@ -77,25 +77,25 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { /// @inheritdoc IPlugin function onInstall(bytes calldata data) external override { - (uint8 startFunctionId, ERC20SpendLimit[] memory spendLimits) = - abi.decode(data, (uint8, ERC20SpendLimit[])); + (uint32 startEntityId, ERC20SpendLimit[] memory spendLimits) = + abi.decode(data, (uint32, ERC20SpendLimit[])); - if (startFunctionId + spendLimits.length > type(uint8).max) { + if (startEntityId + spendLimits.length > type(uint32).max) { revert ExceededNumberOfEntities(); } for (uint8 i = 0; i < spendLimits.length; i++) { _tokenList.tryAdd(msg.sender, SetValue.wrap(bytes30(bytes20(spendLimits[i].token)))); for (uint256 j = 0; j < spendLimits[i].limits.length; j++) { - limits[i + startFunctionId][spendLimits[i].token][msg.sender] = spendLimits[i].limits[j]; + limits[i + startEntityId][spendLimits[i].token][msg.sender] = spendLimits[i].limits[j]; } } } /// @inheritdoc IPlugin function onUninstall(bytes calldata data) external override { - (address token, uint8 functionId) = abi.decode(data, (address, uint8)); - delete limits[functionId][token][msg.sender]; + (address token, uint32 entityId) = abi.decode(data, (address, uint32)); + delete limits[entityId][token][msg.sender]; } function getTokensForAccount(address account) external view returns (address[] memory tokens) { @@ -108,7 +108,7 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { } /// @inheritdoc IExecutionHook - function postExecutionHook(uint8, bytes calldata) external pure override { + function postExecutionHook(uint32, bytes calldata) external pure override { revert NotImplemented(); } @@ -133,7 +133,7 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { return super.supportsInterface(interfaceId); } - function _decrementLimit(uint8 functionId, address token, bytes memory innerCalldata) internal { + function _decrementLimit(uint32 entityId, address token, bytes memory innerCalldata) internal { bytes4 selector; uint256 spend; assembly { @@ -141,12 +141,12 @@ contract ERC20TokenLimitPlugin is BasePlugin, IExecutionHook { spend := mload(add(innerCalldata, 68)) // 36:68 is recipient, 68:100 is spend } if (selector == IERC20.transfer.selector || selector == IERC20.approve.selector) { - uint256 limit = limits[functionId][token][msg.sender]; + uint256 limit = limits[entityId][token][msg.sender]; if (spend > limit) { revert ExceededTokenLimit(); } // solhint-disable-next-line reentrancy - limits[functionId][token][msg.sender] = limit - spend; + limits[entityId][token][msg.sender] = limit - spend; } else { revert SelectorNotAllowed(); } diff --git a/src/plugins/NativeTokenLimitPlugin.sol b/src/plugins/NativeTokenLimitPlugin.sol index 2b512c22..de0d9f3d 100644 --- a/src/plugins/NativeTokenLimitPlugin.sol +++ b/src/plugins/NativeTokenLimitPlugin.sol @@ -34,8 +34,8 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook { error ExceededNativeTokenLimit(); error ExceededNumberOfEntities(); - function updateLimits(uint8 functionId, uint256 newLimit) external { - limits[functionId][msg.sender] = newLimit; + function updateLimits(uint32 entityId, uint256 newLimit) external { + limits[entityId][msg.sender] = newLimit; } function updateSpecialPaymaster(address paymaster, bool allowed) external { @@ -43,7 +43,7 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook { } /// @inheritdoc IValidationHook - function preUserOpValidationHook(uint8 functionId, PackedUserOperation calldata userOp, bytes32) + function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32) external returns (uint256) { @@ -63,54 +63,54 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook { uint256 totalGas = userOp.preVerificationGas + vgl + cgl + pvgl + ppogl; uint256 usage = totalGas * UserOperationLib.unpackMaxFeePerGas(userOp); - uint256 limit = limits[functionId][msg.sender]; + uint256 limit = limits[entityId][msg.sender]; if (usage > limit) { revert ExceededNativeTokenLimit(); } - limits[functionId][msg.sender] = limit - usage; + limits[entityId][msg.sender] = limit - usage; } return 0; } /// @inheritdoc IExecutionHook - function preExecutionHook(uint8 functionId, address, uint256, bytes calldata data) + function preExecutionHook(uint32 entityId, address, uint256, bytes calldata data) external override returns (bytes memory) { - return _checkAndDecrementLimit(functionId, data); + return _checkAndDecrementLimit(entityId, data); } /// @inheritdoc IPlugin function onInstall(bytes calldata data) external override { - (uint8 startFunctionId, uint256[] memory spendLimits) = abi.decode(data, (uint8, uint256[])); + (uint32 startEntityId, uint256[] memory spendLimits) = abi.decode(data, (uint32, uint256[])); - if (startFunctionId + spendLimits.length > type(uint8).max) { + if (startEntityId + spendLimits.length > type(uint32).max) { revert ExceededNumberOfEntities(); } for (uint256 i = 0; i < spendLimits.length; i++) { - limits[i + startFunctionId][msg.sender] = spendLimits[i]; + limits[i + startEntityId][msg.sender] = spendLimits[i]; } } /// @inheritdoc IPlugin function onUninstall(bytes calldata data) external override { - // This is the highest functionId that's being used by the account - uint8 functionId = abi.decode(data, (uint8)); - for (uint256 i = 0; i < functionId; i++) { + // This is the highest entityId that's being used by the account + uint32 entityId = abi.decode(data, (uint32)); + for (uint256 i = 0; i < entityId; i++) { delete limits[i][msg.sender]; } } /// @inheritdoc IExecutionHook - function postExecutionHook(uint8, bytes calldata) external pure override { + function postExecutionHook(uint32, bytes calldata) external pure override { revert NotImplemented(); } // No implementation, no revert // Runtime spends no account gas, and we check native token spend limits in exec hooks - function preRuntimeValidationHook(uint8, address, uint256, bytes calldata, bytes calldata) + function preRuntimeValidationHook(uint32, address, uint256, bytes calldata, bytes calldata) external pure override @@ -142,7 +142,7 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook { return interfaceId == type(IExecutionHook).interfaceId || super.supportsInterface(interfaceId); } - function _checkAndDecrementLimit(uint8 functionId, bytes calldata data) internal returns (bytes memory) { + function _checkAndDecrementLimit(uint32 entityId, bytes calldata data) internal returns (bytes memory) { (bytes4 selector, bytes memory callData) = _getSelectorAndCalldata(data); uint256 value; @@ -156,11 +156,11 @@ contract NativeTokenLimitPlugin is BasePlugin, IExecutionHook, IValidationHook { } } - uint256 limit = limits[functionId][msg.sender]; + uint256 limit = limits[entityId][msg.sender]; if (value > limit) { revert ExceededNativeTokenLimit(); } - limits[functionId][msg.sender] = limit - value; + limits[entityId][msg.sender] = limit - value; return ""; } diff --git a/standard/ERCs/erc-6900.md b/standard/ERCs/erc-6900.md index a4a9ab76..fed6a625 100644 --- a/standard/ERCs/erc-6900.md +++ b/standard/ERCs/erc-6900.md @@ -310,13 +310,13 @@ interface IPlugin { /// @param value The call value. /// @param data The calldata sent. /// @return Context to pass to a post execution hook, if present. An empty bytes array MAY be returned. - function preExecutionHook(uint8 entityId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); + function preExecutionHook(uint32 entityId, address sender, uint256 value, bytes calldata data) external returns (bytes memory); /// @notice Run the post execution hook specified by the `entityId`. /// @dev To indicate the entire call should revert, the function MUST revert. /// @param entityId An identifier that routes the call to different internal implementations, should there be more than one. /// @param preExecHookData The context returned by its associated pre execution hook. - function postExecutionHook(uint8 entityId, bytes calldata preExecHookData) external; + function postExecutionHook(uint32 entityId, bytes calldata preExecHookData) external; /// @notice Describe the contents and intended configuration of the plugin. /// @dev This manifest MUST stay constant over time. diff --git a/test/libraries/FunctionReferenceLib.t.sol b/test/libraries/FunctionReferenceLib.t.sol deleted file mode 100644 index 94a44a8b..00000000 --- a/test/libraries/FunctionReferenceLib.t.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import {Test} from "forge-std/Test.sol"; - -import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; -import {PluginEntity} from "../../src/interfaces/IPluginManager.sol"; - -contract PluginEntityLibTest is Test { - using PluginEntityLib for PluginEntity; - - function testFuzz_functionReference_packing(address addr, uint32 entityId) public { - // console.log("addr: ", addr); - // console.log("entityId: ", vm.toString(entityId)); - PluginEntity fr = PluginEntityLib.pack(addr, entityId); - // console.log("packed: ", vm.toString(PluginEntity.unwrap(fr))); - (address addr2, uint32 entityId2) = PluginEntityLib.unpack(fr); - // console.log("addr2: ", addr2); - // console.log("entityId2: ", vm.toString(entityId2)); - assertEq(addr, addr2); - assertEq(entityId, entityId2); - } - - function testFuzz_functionReference_operators(PluginEntity a, PluginEntity b) public { - assertTrue(a.eq(a)); - assertTrue(b.eq(b)); - - if (PluginEntity.unwrap(a) == PluginEntity.unwrap(b)) { - assertTrue(a.eq(b)); - assertTrue(b.eq(a)); - assertFalse(a.notEq(b)); - assertFalse(b.notEq(a)); - } else { - assertTrue(a.notEq(b)); - assertTrue(b.notEq(a)); - assertFalse(a.eq(b)); - assertFalse(b.eq(a)); - } - } -} diff --git a/test/plugin/ERC20TokenLimitPlugin.t.sol b/test/plugin/ERC20TokenLimitPlugin.t.sol index 96a18c20..9693f98f 100644 --- a/test/plugin/ERC20TokenLimitPlugin.t.sol +++ b/test/plugin/ERC20TokenLimitPlugin.t.sol @@ -6,16 +6,15 @@ import {MockERC20} from "../mocks/MockERC20.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReference} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PluginEntity} from "../../src/helpers/PluginEntityLib.sol"; import {ERC20TokenLimitPlugin} from "../../src/plugins/ERC20TokenLimitPlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; import {ExecutionHook} from "../../src/interfaces/IAccountLoupe.sol"; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; -import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; contract ERC20TokenLimitPluginTest is AccountTestBase { @@ -24,7 +23,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { address payable public bundler = payable(address(2)); PluginManifest internal _m; MockPlugin public validationPlugin = new MockPlugin(_m); - FunctionReference public validationFunction; + PluginEntity public validationFunction; UpgradeableModularAccount public acct; ERC20TokenLimitPlugin public plugin = new ERC20TokenLimitPlugin(); @@ -32,8 +31,6 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { function setUp() public { // Set up a validator with hooks from the erc20 spend limit plugin attached - MSCAFactoryFixture factory = new MSCAFactoryFixture(entryPoint, _deploySingleOwnerPlugin()); - acct = factory.createAccount(address(this), 0); erc20 = new MockERC20(); @@ -41,7 +38,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { ExecutionHook[] memory permissionHooks = new ExecutionHook[](1); permissionHooks[0] = ExecutionHook({ - hookFunction: FunctionReferenceLib.pack(address(plugin), 0), + hookFunction: PluginEntityLib.pack(address(plugin), 0), isPreHook: true, isPostHook: false }); @@ -65,7 +62,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { abi.encode(permissionHooks, permissionInitDatas) ); - validationFunction = FunctionReferenceLib.pack(address(validationPlugin), 0); + validationFunction = PluginEntityLib.pack(address(validationPlugin), 0); } function _getPackedUO(bytes memory callData) internal view returns (PackedUserOperation memory uo) { @@ -78,7 +75,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { preVerificationGas: 200000, gasFees: bytes32(uint256(uint128(0))), paymasterAndData: "", - signature: _encodeSignature(FunctionReferenceLib.pack(address(validationPlugin), 0), 1, "") + signature: _encodeSignature(PluginEntityLib.pack(address(validationPlugin), 0), 1, "") }); } @@ -157,7 +154,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { assertEq(plugin.limits(0, address(erc20), address(acct)), 10 ether); acct.executeWithAuthorization( _getExecuteWithSpend(5 ether), - _encodeSignature(FunctionReferenceLib.pack(address(validationPlugin), 0), 1, "") + _encodeSignature(PluginEntityLib.pack(address(validationPlugin), 0), 1, "") ); assertEq(plugin.limits(0, address(erc20), address(acct)), 5 ether); } @@ -177,7 +174,7 @@ contract ERC20TokenLimitPluginTest is AccountTestBase { assertEq(plugin.limits(0, address(erc20), address(acct)), 10 ether); acct.executeWithAuthorization( abi.encodeCall(IStandardExecutor.executeBatch, (calls)), - _encodeSignature(FunctionReferenceLib.pack(address(validationPlugin), 0), 1, "") + _encodeSignature(PluginEntityLib.pack(address(validationPlugin), 0), 1, "") ); assertEq(plugin.limits(0, address(erc20), address(acct)), 10 ether - 6 ether - 100001); } diff --git a/test/plugin/NativeTokenLimitPlugin.t.sol b/test/plugin/NativeTokenLimitPlugin.t.sol index 5fbf2a3d..33635216 100644 --- a/test/plugin/NativeTokenLimitPlugin.t.sol +++ b/test/plugin/NativeTokenLimitPlugin.t.sol @@ -4,16 +4,15 @@ pragma solidity ^0.8.19; import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol"; import {UpgradeableModularAccount} from "../../src/account/UpgradeableModularAccount.sol"; -import {FunctionReference} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PluginEntity} from "../../src/helpers/PluginEntityLib.sol"; import {NativeTokenLimitPlugin} from "../../src/plugins/NativeTokenLimitPlugin.sol"; import {MockPlugin} from "../mocks/MockPlugin.sol"; import {ExecutionHook} from "../../src/interfaces/IAccountLoupe.sol"; -import {FunctionReferenceLib} from "../../src/helpers/FunctionReferenceLib.sol"; +import {PluginEntityLib} from "../../src/helpers/PluginEntityLib.sol"; import {IStandardExecutor, Call} from "../../src/interfaces/IStandardExecutor.sol"; import {PluginManifest} from "../../src/interfaces/IPlugin.sol"; import {ValidationConfigLib} from "../../src/helpers/ValidationConfigLib.sol"; -import {MSCAFactoryFixture} from "../mocks/MSCAFactoryFixture.sol"; import {AccountTestBase} from "../utils/AccountTestBase.sol"; contract NativeTokenLimitPluginTest is AccountTestBase { @@ -21,7 +20,7 @@ contract NativeTokenLimitPluginTest is AccountTestBase { address payable public bundler = payable(address(2)); PluginManifest internal _m; MockPlugin public validationPlugin = new MockPlugin(_m); - FunctionReference public validationFunction; + PluginEntity public validationFunction; UpgradeableModularAccount public acct; NativeTokenLimitPlugin public plugin = new NativeTokenLimitPlugin(); @@ -30,18 +29,16 @@ contract NativeTokenLimitPluginTest is AccountTestBase { function setUp() public { // Set up a validator with hooks from the gas spend limit plugin attached - MSCAFactoryFixture factory = new MSCAFactoryFixture(entryPoint, _deploySingleOwnerPlugin()); - acct = factory.createAccount(address(this), 0); vm.deal(address(acct), 10 ether); - FunctionReference[] memory preValidationHooks = new FunctionReference[](1); - preValidationHooks[0] = FunctionReferenceLib.pack(address(plugin), 0); + PluginEntity[] memory preValidationHooks = new PluginEntity[](1); + preValidationHooks[0] = PluginEntityLib.pack(address(plugin), 0); ExecutionHook[] memory permissionHooks = new ExecutionHook[](1); permissionHooks[0] = ExecutionHook({ - hookFunction: FunctionReferenceLib.pack(address(plugin), 0), + hookFunction: PluginEntityLib.pack(address(plugin), 0), isPreHook: true, isPostHook: false }); @@ -64,7 +61,7 @@ contract NativeTokenLimitPluginTest is AccountTestBase { abi.encode(permissionHooks, permissionInitDatas) ); - validationFunction = FunctionReferenceLib.pack(address(validationPlugin), 0); + validationFunction = PluginEntityLib.pack(address(validationPlugin), 0); } function _getExecuteWithValue(uint256 value) internal view returns (bytes memory) { @@ -85,7 +82,7 @@ contract NativeTokenLimitPluginTest is AccountTestBase { preVerificationGas: gas3, gasFees: bytes32(uint256(uint128(gasPrice))), paymasterAndData: "", - signature: _encodeSignature(FunctionReferenceLib.pack(address(validationPlugin), 0), 1, "") + signature: _encodeSignature(PluginEntityLib.pack(address(validationPlugin), 0), 1, "") }); }