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

fix: Rename plugin to module #106

Merged
merged 2 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 (`SingleSignerValidation` and `TokenReceiverPlugin`). It is compliant with ERC-6900 with the latest updates.
The implementation includes an upgradable modular account with two modules (`SingleSignerValidation` and `TokenReceiverModule`). It is compliant with ERC-6900 with the latest updates.

## Important Callouts

Expand All @@ -11,7 +11,7 @@ The implementation includes an upgradable modular account with two plugins (`Sin

## Development

Anyone is welcome to submit feedback and/or PRs to improve code or add Plugins.
Anyone is welcome to submit feedback and/or PRs to improve code or add Modules.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor note/todo: we should consider if having modules be on this repo is the way to go, or if we should split them out into another repo (long term)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think we've already started encouraging people to put these in their own repos or in https://github.com/erc6900/sample-plugins, to make the maintenance load easier. The README has some other out-of-date things, too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, gonna remove the add modules part.


### Testing

Expand Down
22 changes: 11 additions & 11 deletions src/account/AccountLoupe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {ExecutionHook, IAccountLoupe} from "../interfaces/IAccountLoupe.sol";
import {IPluginManager, PluginEntity} from "../interfaces/IPluginManager.sol";
import {IModuleManager, ModuleEntity} from "../interfaces/IModuleManager.sol";
import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
import {getAccountStorage, toExecutionHook, toSelector} from "./AccountStorage.sol";

Expand All @@ -16,21 +16,21 @@ abstract contract AccountLoupe is IAccountLoupe {
using EnumerableMap for EnumerableMap.AddressToUintMap;

/// @inheritdoc IAccountLoupe
function getExecutionFunctionHandler(bytes4 selector) external view override returns (address plugin) {
function getExecutionFunctionHandler(bytes4 selector) external view override returns (address module) {
if (
selector == IStandardExecutor.execute.selector || selector == IStandardExecutor.executeBatch.selector
|| selector == UUPSUpgradeable.upgradeToAndCall.selector
|| selector == IPluginManager.installPlugin.selector
|| selector == IPluginManager.uninstallPlugin.selector
|| selector == IModuleManager.installModule.selector
|| selector == IModuleManager.uninstallModule.selector
) {
return address(this);
}

return getAccountStorage().selectorData[selector].plugin;
return getAccountStorage().selectorData[selector].module;
}

/// @inheritdoc IAccountLoupe
function getSelectors(PluginEntity validationFunction) external view returns (bytes4[] memory) {
function getSelectors(ModuleEntity validationFunction) external view returns (bytes4[] memory) {
uint256 length = getAccountStorage().validationData[validationFunction].selectors.length();

bytes4[] memory selectors = new bytes4[](length);
Expand Down Expand Up @@ -62,7 +62,7 @@ abstract contract AccountLoupe is IAccountLoupe {
}

/// @inheritdoc IAccountLoupe
function getPermissionHooks(PluginEntity validationFunction)
function getPermissionHooks(ModuleEntity validationFunction)
external
view
override
Expand All @@ -80,17 +80,17 @@ abstract contract AccountLoupe is IAccountLoupe {
}

/// @inheritdoc IAccountLoupe
function getPreValidationHooks(PluginEntity validationFunction)
function getPreValidationHooks(ModuleEntity validationFunction)
external
view
override
returns (PluginEntity[] memory preValidationHooks)
returns (ModuleEntity[] memory preValidationHooks)
{
preValidationHooks = getAccountStorage().validationData[validationFunction].preValidationHooks;
}

/// @inheritdoc IAccountLoupe
function getInstalledPlugins() external view override returns (address[] memory pluginAddresses) {
pluginAddresses = getAccountStorage().pluginManifestHashes.keys();
function getInstalledModules() external view override returns (address[] memory moduleAddresses) {
moduleAddresses = getAccountStorage().moduleManifestHashes.keys();
}
}
36 changes: 18 additions & 18 deletions src/account/AccountStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {ExecutionHook} from "../interfaces/IAccountLoupe.sol";
import {PluginEntity} from "../interfaces/IPluginManager.sol";
import {ModuleEntity} from "../interfaces/IModuleManager.sol";

// bytes = keccak256("ERC6900.UpgradeableModularAccount.Storage")
bytes32 constant _ACCOUNT_STORAGE_SLOT = 0x9f09680beaa4e5c9f38841db2460c401499164f368baef687948c315d9073e40;

// Represents data associated with a specifc function selector.
struct SelectorData {
// The plugin that implements this execution function.
// The module that implements this execution function.
// If this is a native function, the address must remain address(0).
address plugin;
address module;
// Whether or not the function needs runtime validation, or can be called by anyone. The function can still be
// state changing if this flag is set to true.
// Note that even if this is set to true, user op validation will still be required, otherwise anyone could
Expand All @@ -32,7 +32,7 @@ struct ValidationData {
// Whether or not this validation is a signature validator.
bool isSignatureValidation;
// The pre validation hooks for this validation function.
PluginEntity[] preValidationHooks;
ModuleEntity[] preValidationHooks;
// Permission hooks for this validation function.
EnumerableSet.Bytes32Set permissionHooks;
// The set of selectors that may be validated by this validation function.
Expand All @@ -43,11 +43,11 @@ struct AccountStorage {
// AccountStorageInitializable variables
uint8 initialized;
bool initializing;
// Plugin metadata storage
EnumerableMap.AddressToUintMap pluginManifestHashes;
// Module metadata storage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is outdated, todo: update

EnumerableMap.AddressToUintMap moduleManifestHashes;
// Execution functions and their associated functions
mapping(bytes4 => SelectorData) selectorData;
mapping(PluginEntity validationFunction => ValidationData) validationData;
mapping(ModuleEntity validationFunction => ValidationData) validationData;
// For ERC165 introspection
mapping(bytes4 => uint256) supportedIfaces;
}
Expand All @@ -60,30 +60,30 @@ function getAccountStorage() pure returns (AccountStorage storage _storage) {

using EnumerableSet for EnumerableSet.Bytes32Set;

function toSetValue(PluginEntity pluginEntity) pure returns (bytes32) {
return bytes32(PluginEntity.unwrap(pluginEntity));
function toSetValue(ModuleEntity moduleEntity) pure returns (bytes32) {
return bytes32(ModuleEntity.unwrap(moduleEntity));
}

function toPluginEntity(bytes32 setValue) pure returns (PluginEntity) {
return PluginEntity.wrap(bytes24(setValue));
function toModuleEntity(bytes32 setValue) pure returns (ModuleEntity) {
return ModuleEntity.wrap(bytes24(setValue));
}

// ExecutionHook layout:
// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF______________________ Hook Plugin Entity
// 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF______________________ Hook Module 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))
return bytes32(ModuleEntity.unwrap(executionHook.hookFunction))
| bytes32(executionHook.isPreHook ? uint256(1) << 56 : 0)
| bytes32(executionHook.isPostHook ? uint256(1) << 48 : 0);
}

function toExecutionHook(bytes32 setValue)
pure
returns (PluginEntity hookFunction, bool isPreHook, bool isPostHook)
returns (ModuleEntity hookFunction, bool isPreHook, bool isPostHook)
{
hookFunction = PluginEntity.wrap(bytes24(setValue));
hookFunction = ModuleEntity.wrap(bytes24(setValue));
isPreHook = (uint256(setValue) >> 56) & 0xFF == 1;
isPostHook = (uint256(setValue) >> 48) & 0xFF == 1;
}
Expand All @@ -97,12 +97,12 @@ function toSelector(bytes32 setValue) pure returns (bytes4) {
}

/// @dev Helper function to get all elements of a set into memory.
function toPluginEntityArray(EnumerableSet.Bytes32Set storage set) view returns (PluginEntity[] memory) {
function toModuleEntityArray(EnumerableSet.Bytes32Set storage set) view returns (ModuleEntity[] memory) {
uint256 length = set.length();
PluginEntity[] memory result = new PluginEntity[](length);
ModuleEntity[] memory result = new ModuleEntity[](length);
for (uint256 i = 0; i < length; ++i) {
bytes32 key = set.at(i);
result[i] = PluginEntity.wrap(bytes24(key));
result[i] = ModuleEntity.wrap(bytes24(key));
}
return result;
}
56 changes: 28 additions & 28 deletions src/account/PluginManager2.sol → src/account/ModuleManager2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@ pragma solidity ^0.8.25;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {PluginEntityLib} from "../helpers/PluginEntityLib.sol";
import {ModuleEntityLib} from "../helpers/ModuleEntityLib.sol";
import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol";

import {ExecutionHook} from "../interfaces/IAccountLoupe.sol";
import {IPlugin} from "../interfaces/IPlugin.sol";
import {PluginEntity, ValidationConfig} from "../interfaces/IPluginManager.sol";
import {IModule} from "../interfaces/IModule.sol";
import {ModuleEntity, ValidationConfig} from "../interfaces/IModuleManager.sol";
import {ValidationData, getAccountStorage, toSetValue} from "./AccountStorage.sol";

// Temporary additional functions for a user-controlled install flow for validation functions.
abstract contract PluginManager2 {
abstract contract ModuleManager2 {
using EnumerableSet for EnumerableSet.Bytes32Set;
using ValidationConfigLib for ValidationConfig;

// Index marking the start of the data for the validation function.
uint8 internal constant _RESERVED_VALIDATION_DATA_INDEX = 255;
uint32 internal constant _SELF_PERMIT_VALIDATION_FUNCTIONID = type(uint32).max;

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 PreValidationAlreadySet(ModuleEntity validationFunction, ModuleEntity preValidationFunction);
error ValidationAlreadySet(bytes4 selector, ModuleEntity validationFunction);
error ValidationNotSet(bytes4 selector, ModuleEntity validationFunction);
error PermissionAlreadySet(ModuleEntity validationFunction, ExecutionHook hook);
error PreValidationHookLimitExceeded();

function _installValidation(
Expand All @@ -34,20 +34,20 @@ abstract contract PluginManager2 {
bytes memory permissionHooks
) internal {
ValidationData storage _validationData =
getAccountStorage().validationData[validationConfig.pluginEntity()];
getAccountStorage().validationData[validationConfig.moduleEntity()];

if (preValidationHooks.length > 0) {
(PluginEntity[] memory preValidationFunctions, bytes[] memory initDatas) =
abi.decode(preValidationHooks, (PluginEntity[], bytes[]));
(ModuleEntity[] memory preValidationFunctions, bytes[] memory initDatas) =
abi.decode(preValidationHooks, (ModuleEntity[], bytes[]));

for (uint256 i = 0; i < preValidationFunctions.length; ++i) {
PluginEntity preValidationFunction = preValidationFunctions[i];
ModuleEntity preValidationFunction = preValidationFunctions[i];

_validationData.preValidationHooks.push(preValidationFunction);

if (initDatas[i].length > 0) {
(address preValidationPlugin,) = PluginEntityLib.unpack(preValidationFunction);
IPlugin(preValidationPlugin).onInstall(initDatas[i]);
(address preValidationModule,) = ModuleEntityLib.unpack(preValidationFunction);
IModule(preValidationModule).onInstall(initDatas[i]);
}
}

Expand All @@ -65,20 +65,20 @@ abstract contract PluginManager2 {
ExecutionHook memory permissionFunction = permissionFunctions[i];

if (!_validationData.permissionHooks.add(toSetValue(permissionFunction))) {
revert PermissionAlreadySet(validationConfig.pluginEntity(), permissionFunction);
revert PermissionAlreadySet(validationConfig.moduleEntity(), permissionFunction);
}

if (initDatas[i].length > 0) {
(address executionPlugin,) = PluginEntityLib.unpack(permissionFunction.hookFunction);
IPlugin(executionPlugin).onInstall(initDatas[i]);
(address executionModule,) = ModuleEntityLib.unpack(permissionFunction.hookFunction);
IModule(executionModule).onInstall(initDatas[i]);
}
}
}

for (uint256 i = 0; i < selectors.length; ++i) {
bytes4 selector = selectors[i];
if (!_validationData.selectors.add(toSetValue(selector))) {
revert ValidationAlreadySet(selector, validationConfig.pluginEntity());
revert ValidationAlreadySet(selector, validationConfig.moduleEntity());
}
}

Expand All @@ -88,13 +88,13 @@ abstract contract PluginManager2 {
_validationData.isGlobal = validationConfig.isGlobal();
_validationData.isSignatureValidation = validationConfig.isSignatureValidation();
if (installData.length > 0) {
IPlugin(validationConfig.plugin()).onInstall(installData);
IModule(validationConfig.module()).onInstall(installData);
}
}
}

function _uninstallValidation(
PluginEntity validationFunction,
ModuleEntity validationFunction,
bytes calldata uninstallData,
bytes calldata preValidationHookUninstallData,
bytes calldata permissionHookUninstallData
Expand All @@ -108,12 +108,12 @@ abstract contract PluginManager2 {
bytes[] memory preValidationHookUninstallDatas = abi.decode(preValidationHookUninstallData, (bytes[]));

// Clear pre validation hooks
PluginEntity[] storage preValidationHooks = _validationData.preValidationHooks;
ModuleEntity[] storage preValidationHooks = _validationData.preValidationHooks;
for (uint256 i = 0; i < preValidationHooks.length; ++i) {
PluginEntity preValidationFunction = preValidationHooks[i];
ModuleEntity preValidationFunction = preValidationHooks[i];
if (preValidationHookUninstallDatas[0].length > 0) {
(address preValidationPlugin,) = PluginEntityLib.unpack(preValidationFunction);
IPlugin(preValidationPlugin).onUninstall(preValidationHookUninstallDatas[0]);
(address preValidationModule,) = ModuleEntityLib.unpack(preValidationFunction);
IModule(preValidationModule).onUninstall(preValidationHookUninstallDatas[0]);
}
}
delete _validationData.preValidationHooks;
Expand All @@ -128,8 +128,8 @@ abstract contract PluginManager2 {
for (uint256 i = 0; i < permissionHookLen; ++i) {
bytes32 permissionHook = permissionHooks.at(0);
permissionHooks.remove(permissionHook);
address permissionHookPlugin = address(uint160(bytes20(permissionHook)));
IPlugin(permissionHookPlugin).onUninstall(permissionHookUninstallDatas[i]);
address permissionHookModule = address(uint160(bytes20(permissionHook)));
IModule(permissionHookModule).onUninstall(permissionHookUninstallDatas[i]);
}
}

Expand All @@ -141,8 +141,8 @@ abstract contract PluginManager2 {
}

if (uninstallData.length > 0) {
(address plugin,) = PluginEntityLib.unpack(validationFunction);
IPlugin(plugin).onUninstall(uninstallData);
(address module,) = ModuleEntityLib.unpack(validationFunction);
IModule(module).onUninstall(uninstallData);
}
}
}
Loading
Loading