Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [v0.8-develop, experimental] Remove function id #41

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 16 additions & 42 deletions src/account/AccountLoupe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol";
import {FunctionReference, IPluginManager} from "../interfaces/IPluginManager.sol";
import {IPlugin} from "../interfaces/IPlugin.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
import {AccountStorage, getAccountStorage, SelectorData, toFunctionReferenceArray} from "./AccountStorage.sol";
import {AccountStorage, getAccountStorage, toAddressArray, toPlugin, SelectorData} from "./AccountStorage.sol";

abstract contract AccountLoupe is IAccountLoupe {
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;
Expand Down Expand Up @@ -36,63 +37,39 @@ abstract contract AccountLoupe is IAccountLoupe {
config.plugin = _storage.selectorData[selector].plugin;
}

config.userOpValidationFunction = _storage.selectorData[selector].userOpValidation;

config.runtimeValidationFunction = _storage.selectorData[selector].runtimeValidation;
config.validationPlugin = address(_storage.selectorData[selector].validation);
}

/// @inheritdoc IAccountLoupe
function getExecutionHooks(bytes4 selector) external view returns (ExecutionHooks[] memory execHooks) {
SelectorData storage selectorData = getAccountStorage().selectorData[selector];
uint256 preExecHooksLength = selectorData.preHooks.length();
uint256 postOnlyExecHooksLength = selectorData.postOnlyHooks.length();
uint256 maxExecHooksLength = postOnlyExecHooksLength;

// There can only be as many associated post hooks to run as there are pre hooks.
for (uint256 i = 0; i < preExecHooksLength;) {
(, uint256 count) = selectorData.preHooks.at(i);
unchecked {
maxExecHooksLength += (count + 1);
++i;
}
}
uint256 maxExecHooksLength = postOnlyExecHooksLength + preExecHooksLength;

// Overallocate on length - not all of this may get filled up. We set the correct length later.
execHooks = new ExecutionHooks[](maxExecHooksLength);
uint256 actualExecHooksLength;

for (uint256 i = 0; i < preExecHooksLength;) {
(bytes32 key,) = selectorData.preHooks.at(i);
FunctionReference preExecHook = FunctionReference.wrap(bytes21(key));

uint256 associatedPostExecHooksLength = selectorData.associatedPostHooks[preExecHook].length();
if (associatedPostExecHooksLength > 0) {
for (uint256 j = 0; j < associatedPostExecHooksLength;) {
execHooks[actualExecHooksLength].preExecHook = preExecHook;
(key,) = selectorData.associatedPostHooks[preExecHook].at(j);
execHooks[actualExecHooksLength].postExecHook = FunctionReference.wrap(bytes21(key));

unchecked {
++actualExecHooksLength;
++j;
}
}
} else {
execHooks[actualExecHooksLength].preExecHook = preExecHook;

unchecked {
++actualExecHooksLength;
}
IPlugin preExecHookPlugin = toPlugin(key);

execHooks[actualExecHooksLength].preExecHookPlugin = address(preExecHookPlugin);

if (selectorData.hasAssociatedPostHook[preExecHookPlugin]) {
execHooks[actualExecHooksLength].postExecHookPlugin = address(preExecHookPlugin);
}

unchecked {
++actualExecHooksLength;
++i;
}
}

for (uint256 i = 0; i < postOnlyExecHooksLength;) {
(bytes32 key,) = selectorData.postOnlyHooks.at(i);
execHooks[actualExecHooksLength].postExecHook = FunctionReference.wrap(bytes21(key));
execHooks[actualExecHooksLength].postExecHookPlugin = address(toPlugin(key));

unchecked {
++actualExecHooksLength;
Expand All @@ -110,15 +87,12 @@ abstract contract AccountLoupe is IAccountLoupe {
function getPreValidationHooks(bytes4 selector)
external
view
returns (
FunctionReference[] memory preUserOpValidationHooks,
FunctionReference[] memory preRuntimeValidationHooks
)
returns (address[] memory preUserOpValidationHooks, address[] memory preRuntimeValidationHooks)
{
preUserOpValidationHooks =
toFunctionReferenceArray(getAccountStorage().selectorData[selector].preUserOpValidationHooks);
toAddressArray(getAccountStorage().selectorData[selector].preUserOpValidationHooks);
preRuntimeValidationHooks =
toFunctionReferenceArray(getAccountStorage().selectorData[selector].preRuntimeValidationHooks);
toAddressArray(getAccountStorage().selectorData[selector].preRuntimeValidationHooks);
}

/// @inheritdoc IAccountLoupe
Expand Down
26 changes: 13 additions & 13 deletions src/account/AccountStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IPlugin} from "../interfaces/IPlugin.sol";
import {FunctionReference} from "../interfaces/IPluginManager.sol";

// bytes = keccak256("ERC6900.UpgradeableModularAccount.Storage")
bytes32 constant _ACCOUNT_STORAGE_SLOT = 0x9f09680beaa4e5c9f38841db2460c401499164f368baef687948c315d9073e40;
Expand All @@ -15,7 +14,7 @@ struct PluginData {
// boolean to indicate if the plugin can spend native tokens from the account.
bool canSpendNativeToken;
bytes32 manifestHash;
FunctionReference[] dependencies;
address[] dependencies;
// Tracks the number of times this plugin has been used as a dependency function
uint256 dependentCount;
}
Expand All @@ -35,15 +34,15 @@ struct SelectorData {
// The plugin that implements this execution function.
// If this is a native function, the address must remain address(0).
address plugin;
FunctionReference userOpValidation;
FunctionReference runtimeValidation;
// User operation validation and runtime validation share a function reference.
IPlugin validation;
// The pre validation hooks for this function selector.
EnumerableMap.Bytes32ToUintMap preUserOpValidationHooks;
EnumerableMap.Bytes32ToUintMap preRuntimeValidationHooks;
// The execution hooks for this function selector.
EnumerableMap.Bytes32ToUintMap preHooks;
// bytes21 key = pre hook function reference
mapping(FunctionReference => EnumerableMap.Bytes32ToUintMap) associatedPostHooks;
// bytes20 key = pre hook plugin address
mapping(IPlugin => bool) hasAssociatedPostHook;
EnumerableMap.Bytes32ToUintMap postOnlyHooks;
}

Expand All @@ -53,7 +52,7 @@ struct AccountStorage {
bool initializing;
// Plugin metadata storage
EnumerableSet.AddressSet plugins;
mapping(address => PluginData) pluginData;
mapping(IPlugin => PluginData) pluginData;
// Execution functions and their associated functions
mapping(bytes4 => SelectorData) selectorData;
// bytes24 key = address(calling plugin) || bytes4(selector of execution function)
Expand All @@ -74,18 +73,19 @@ function getPermittedCallKey(address addr, bytes4 selector) pure returns (bytes2
return bytes24(bytes20(addr)) | (bytes24(selector) >> 160);
}

function toPlugin(bytes32 key) pure returns (IPlugin) {
return IPlugin(address(bytes20(key)));
}

// Helper function to get all elements of a set into memory.
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;

function toFunctionReferenceArray(EnumerableMap.Bytes32ToUintMap storage map)
view
returns (FunctionReference[] memory)
{
function toAddressArray(EnumerableMap.Bytes32ToUintMap storage map) view returns (address[] memory) {
uint256 length = map.length();
FunctionReference[] memory result = new FunctionReference[](length);
address[] memory result = new address[](length);
for (uint256 i = 0; i < length;) {
(bytes32 key,) = map.at(i);
result[i] = FunctionReference.wrap(bytes21(key));
result[i] = address(bytes20(key));

unchecked {
++i;
Expand Down
Loading
Loading