From b70c3814008e48b34c079b12579fd820a8122d0c Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 21 Nov 2023 11:54:20 -0800 Subject: [PATCH 1/8] Update ERC-6900: renaming, interface changes, and expected behavior change in --- ERCS/erc-6900.md | 104 +++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 70dbe1a4bb4..aa10698f88e 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -94,7 +94,7 @@ Each step is modular, supporting different implementations for each execution fu **Modular Smart Contract Accounts** **MAY** implement -- `IPluginLoupe.sol` to support visibility in plugin configuration on-chain. +- `IAccountLoupe.sol` to support visibility in plugin configuration on-chain. **Plugins** **MUST** implement @@ -123,8 +123,16 @@ interface IPluginManager { uint8 postExecHookFunctionId; } - event PluginInstalled(address indexed plugin, bytes32 manifestHash); - event PluginUninstalled(address indexed plugin, bytes32 manifestHash, bool onUninstallSucceeded); + event PluginInstalled( + address indexed plugin, + bytes32 manifestHash, + FunctionReference[] dependencies, + InjectedHook[] injectedHooks + ); + + event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); + event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); + event PluginIgnoredUninstallCallbackFailure(address indexed plugin); /// @notice Install a plugin to the modular account. /// @param plugin The plugin to install. @@ -168,12 +176,12 @@ Standard execute functions SHOULD check whether the call's target implements the **If the target is a plugin, the call SHOULD revert.** This prevents accidental misconfiguration or misuse of plugins (both installed and uninstalled). ```solidity -struct Execution { +struct Call { // The target contract for account to execute. address target; - // The value for the execution. + // The value for the call. uint256 value; - // The call data for the execution. + // The call data for the call. bytes data; } @@ -181,16 +189,16 @@ interface IStandardExecutor { /// @notice Standard execute method. /// @dev If the target is a plugin, the call SHOULD revert. /// @param target The target contract for account to execute. - /// @param value The value for the execution. - /// @param data The call data for the execution. + /// @param value The value for the call. + /// @param data The call data for the call. /// @return The return data from the call. function execute(address target, uint256 value, bytes calldata data) external payable returns (bytes memory); /// @notice Standard executeBatch method. /// @dev If the target is a plugin, the call SHOULD revert. - /// @param executions The array of executions. + /// @param calls The array of calls. /// @return An array containing the return data from the calls. - function executeBatch(Execution[] calldata executions) external payable returns (bytes[] memory); + function executeBatch(Call[] calldata calls) external payable returns (bytes[] memory); } ``` @@ -220,7 +228,7 @@ interface IPluginExecutor { } ``` -#### `IPluginLoupe.sol` +#### `IAccountLoupe.sol` Plugin inspection interface. Modular Smart Contract Accounts **MAY** implement this interface to support visibility in plugin configuration on-chain. @@ -228,32 +236,55 @@ Plugin inspection interface. Modular Smart Contract Accounts **MAY** implement t // Treats the first 20 bytes as an address, and the last byte as a function identifier. type FunctionReference is bytes21; -interface IPluginLoupe { - // Config for a Plugin Execution function +interface IAccountLoupe { + /// @notice Config for an execution function, given a selector struct ExecutionFunctionConfig { address plugin; FunctionReference userOpValidationFunction; FunctionReference runtimeValidationFunction; } + /// @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 preExecHook; FunctionReference postExecHook; } + /// @notice Gets the validation functions and plugin address for a selector + /// @dev If the selector is a native function, the plugin address will be the address of the account + /// @param selector The selector to get the configuration for + /// @return The configuration for this selector function getExecutionFunctionConfig(bytes4 selector) external view returns (ExecutionFunctionConfig memory); + /// @notice Gets the pre and post execution hooks for a selector + /// @param selector The selector to get the hooks for + /// @return The pre and post execution hooks for this selector function getExecutionHooks(bytes4 selector) external view returns (ExecutionHooks[] memory); + /// @notice Gets the pre and post permitted call hooks applied for a plugin calling this selector + /// @param callingPlugin The plugin that is calling the selector + /// @param selector The selector the plugin is calling + /// @return The pre and post permitted call hooks for this selector function getPermittedCallHooks(address callingPlugin, bytes4 selector) external view returns (ExecutionHooks[] memory); - function getPreUserOpValidationHooks(bytes4 selector) external view returns (FunctionReference[] memory); - - function getPreRuntimeValidationHooks(bytes4 selector) external view returns (FunctionReference[] memory); + /// @notice Gets the pre user op and runtime validation hooks associated with a selector + /// @param selector The selector to get the hooks for + /// @return preUserOpValidationHooks The pre user op validation hooks for this selector + /// @return preRuntimeValidationHooks The pre runtime validation hooks for this selector + function getPreValidationHooks(bytes4 selector) + external + view + returns ( + FunctionReference[] memory preUserOpValidationHooks, + FunctionReference[] memory preRuntimeValidationHooks + ); + /// @notice Gets an array of all installed plugins + /// @return The addresses of all installed plugins function getInstalledPlugins() external view returns (address[] memory); } ``` @@ -379,11 +410,6 @@ enum ManifestAssociatedFunctionType { PRE_HOOK_ALWAYS_DENY } -struct ManifestExecutionFunction { - bytes4 selector; - string[] permissions; -} - // For functions of type `ManifestAssociatedFunctionType.DEPENDENCY`, the MSCA MUST find the plugin address // of the function at `dependencies[dependencyIndex]` during the call to `installPlugin(config)`. struct ManifestFunction { @@ -403,7 +429,14 @@ struct ManifestExecutionHook { ManifestFunction postExecHook; } -struct PluginManifest { +struct ManifestExternalCallPermission { + address externalAddress; + bool permitAnySelector; + bytes4[] selectors; +} + +/// @dev A struct holding fields to describe the plugin in a purely view context. Intended for front end clients. +struct PluginMetadata { // A human-readable name of the plugin. string name; // The version of the plugin, following the semantic versioning scheme. @@ -411,26 +444,28 @@ struct PluginManifest { // The author field SHOULD be a username representing the identity of the user or organization // that created this plugin. string author; + // String desciptions of the relative sensitivity of specific functions. The selectors MUST be selectors for + // functions implemented by this plugin. + SelectorPermission[] permissionDescriptors; +} +/// @dev A struct describing how the plugin should be installed on a modular account. +struct PluginManifest { // List of ERC-165 interfaceIds to add to account to support introspection checks. bytes4[] interfaceIds; - // If this plugin depends on other plugins' validation functions and/or hooks, the interface IDs of // those plugins MUST be provided here, with its position in the array matching the `dependencyIndex` // members of `ManifestFunction` structs used in the manifest. bytes4[] dependencyInterfaceIds; - // Execution functions defined in this plugin to be installed on the MSCA. - ManifestExecutionFunction[] executionFunctions; - - // Native functions or execution functions already installed on the MSCA that this plugin will be - // able to call. + bytes4[] executionFunctions; + // Plugin execution functions already installed on the MSCA that this plugin will be able to call. bytes4[] permittedExecutionSelectors; - - // External contract calls that this plugin will be able to make. - bool permitAnyExternalContract; + // External addresses that this plugin will be able to call. + bool permitAnyExternalAddress; + // boolean to indicate whether the plugin needs access to spend native tokens of the account + bool canSpendNativeToken; ManifestExternalCallPermission[] permittedExternalCalls; - ManifestAssociatedFunction[] userOpValidationFunctions; ManifestAssociatedFunction[] runtimeValidationFunctions; ManifestAssociatedFunction[] preUserOpValidationHooks; @@ -438,6 +473,7 @@ struct PluginManifest { ManifestExecutionHook[] executionHooks; ManifestExecutionHook[] permittedCallHooks; } + ``` ### Expected behavior @@ -474,7 +510,7 @@ The function MUST store the plugin's permitted function selectors and external c The function MUST parse through the execution functions, validation functions, and hooks in the manifest and add them to the modular account after resolving each `ManifestFunction` type. - Each function selector MUST be added as a valid execution function on the modular account. If the function selector has already been added or matches the selector of a native function, the function SHOULD revert. -- If an associated function that is to be added already exists, the function SHOULD revert. +- If a validation function is to be added to a selector that already has that type of validation function, the function SHOULD revert. Next, the function MUST call the plugin's `onInstall` callback with the data provided in the `installData` parameter. This serves to initialize the plugin state for the modular account. If `onInstall` reverts, the `installPlugin` function MUST revert. @@ -495,7 +531,7 @@ The function SHOULD perform the following checks: - Revert if the hash of the manifest used at install time does not match the computed Keccak-256 hash of the plugin's current manifest. This prevents unclean removal of plugins that attempt to force a removal of a different plugin configuration than the one that was originally approved by the client for installation. To allow for removal of such plugins, the modular account MAY implement the capability for the manifest to be encoded in the config field as a parameter. - Revert if there is at least 1 other installed plugin that depends on execution functions, validation functions, or hooks added by this plugin. Plugins used as dependencies must not be uninstalled while dependent plugins exist. -The function SHOULD update account storage to reflect the uninstall via inspection functions, such as those defined by `IPluginLoupe`. Each dependency's record SHOULD also be updated to reflect that it has no longer has this plugin as a dependent. +The function SHOULD update account storage to reflect the uninstall via inspection functions, such as those defined by `IAccountLoupe`. Each dependency's record SHOULD also be updated to reflect that it has no longer has this plugin as a dependent. The function MUST remove records for the plugin's dependencies, injected permitted call hooks, permitted function selectors, and external contract calls. The hooks to remove MUST be exactly the same as what was provided during installation. It is up to the implementing modular account to decide how to keep this invariant. The config parameter field MAY be used. From 36182e650036dddb09438d689250c637825e224d Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 21 Nov 2023 11:55:47 -0800 Subject: [PATCH 2/8] Update ERC-6900: update ref impl url --- ERCS/erc-6900.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index aa10698f88e..f5f396cf0a6 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -586,7 +586,7 @@ No backward compatibility issues found. ## Reference Implementation -See `https://github.com/alchemyplatform/ERC-6900-Ref-Implementation` +See `https://github.com/erc6900/reference-implementation` ## Security Considerations From 119b6c2b92e66fc7bd8fc890a7e3f8f602f1cedc Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 28 Nov 2023 15:34:57 -0500 Subject: [PATCH 3/8] Remove callback events and execution function dependencies --- ERCS/erc-6900.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index f5f396cf0a6..60510345a6a 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -131,8 +131,6 @@ interface IPluginManager { ); event PluginUninstalled(address indexed plugin, bool indexed callbacksSucceeded); - event PluginIgnoredHookUnapplyCallbackFailure(address indexed plugin, address indexed providingPlugin); - event PluginIgnoredUninstallCallbackFailure(address indexed plugin); /// @notice Install a plugin to the modular account. /// @param plugin The plugin to install. @@ -529,11 +527,11 @@ The function MUST revert if the plugin is not installed on the modular account. The function SHOULD perform the following checks: - Revert if the hash of the manifest used at install time does not match the computed Keccak-256 hash of the plugin's current manifest. This prevents unclean removal of plugins that attempt to force a removal of a different plugin configuration than the one that was originally approved by the client for installation. To allow for removal of such plugins, the modular account MAY implement the capability for the manifest to be encoded in the config field as a parameter. -- Revert if there is at least 1 other installed plugin that depends on execution functions, validation functions, or hooks added by this plugin. Plugins used as dependencies must not be uninstalled while dependent plugins exist. +- Revert if there is at least 1 other installed plugin that depends on validation functions or hooks added by this plugin. Plugins used as dependencies must not be uninstalled while dependent plugins exist. The function SHOULD update account storage to reflect the uninstall via inspection functions, such as those defined by `IAccountLoupe`. Each dependency's record SHOULD also be updated to reflect that it has no longer has this plugin as a dependent. -The function MUST remove records for the plugin's dependencies, injected permitted call hooks, permitted function selectors, and external contract calls. The hooks to remove MUST be exactly the same as what was provided during installation. It is up to the implementing modular account to decide how to keep this invariant. The config parameter field MAY be used. +The function MUST remove records for the plugin's dependencies, injected permitted call hooks, permitted function selectors, and permitted external calls. The hooks to remove MUST be exactly the same as what was provided during installation. It is up to the implementing modular account to decide how to keep this invariant. The config parameter field MAY be used. The function MUST parse through the execution functions, validation functions, and hooks in the manifest and remove them from the modular account after resolving each `ManifestFunction` type. From 92019f2dbd6508da28a345ec1b067a840e67b1dd Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 28 Nov 2023 15:14:39 -0800 Subject: [PATCH 4/8] ERC-6900: update assets path --- ERCS/erc-6900.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 60510345a6a..96ddb8214d1 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -27,7 +27,7 @@ However, managing multiple account instances provides a worse user experience, f We propose a standard that coordinates the implementation work between plugin developers and wallet developers. This standard defines a modular smart contract account capable of supporting all standard-conformant plugins. This allows users to have greater portability of their data, and for plugin developers to not have to choose specific account implementations to support. -![diagram showing relationship between accounts and plugins with modular functions](../assets/eip-6900/MSCA_Shared_Components_Diagram.svg) +![diagram showing relationship between accounts and plugins with modular functions](../assets/erc-6900/MSCA_Shared_Components_Diagram.svg) We take inspiration from ERC-2535's diamond pattern for routing execution based on function selectors, and create a similarly composable account. However, the standard does not require the multi-facet proxy pattern. @@ -72,14 +72,14 @@ A modular account handles two kinds of calls: either from the `Entrypoint` throu A call to the smart contract account can be broken down into the steps as shown in the diagram below. The validation steps validate if the caller is allowed to perform the call. The pre execution hook step can be used to do any pre execution checks or updates. It can also be used along with the post execution hook step to perform additional actions or verification. The execution step performs a defined task or collection of tasks. -![diagram showing call flow within an modular account](../assets/eip-6900/Modular_Account_Call_Flow.svg) +![diagram showing call flow within an modular account](../assets/erc-6900/Modular_Account_Call_Flow.svg) The following diagram shows permitted plugin execution flows. During a plugin's execution step from the above diagram, the plugin may perform a "Plugin Execution Function", using either `executeFromPlugin` or `executeFromPluginExternal`. These can be used by plugins to execute using the account's context. - `executeFromPlugin` handles calls to other installed plugin's execution function on the modular account. - `executeFromPluginExternal` handles calls to external contracts. -![diagram showing a plugin execution flow](../assets/eip-6900/Plugin_Execution_Flow.svg) +![diagram showing a plugin execution flow](../assets/erc-6900/Plugin_Execution_Flow.svg) Each step is modular, supporting different implementations for each execution function, and composable, supporting multiple steps through hooks. Combined, these allow for open-ended programmable accounts. From 86c335bd3a0e87ab2a56230b194f9a2f0c685d8d Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 28 Nov 2023 16:14:15 -0800 Subject: [PATCH 5/8] ERC-6900: update wording --- ERCS/erc-6900.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 96ddb8214d1..13519da4e40 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -459,9 +459,9 @@ struct PluginManifest { bytes4[] executionFunctions; // Plugin execution functions already installed on the MSCA that this plugin will be able to call. bytes4[] permittedExecutionSelectors; - // External addresses that this plugin will be able to call. + // Boolean to indicate whether the plugin can call any external contract addresses. bool permitAnyExternalAddress; - // boolean to indicate whether the plugin needs access to spend native tokens of the account + // Boolean to indicate whether the plugin needs access to spend native tokens of the account. bool canSpendNativeToken; ManifestExternalCallPermission[] permittedExternalCalls; ManifestAssociatedFunction[] userOpValidationFunctions; From 5c33d1e5ec9649803425dbca7102e230cf0caec8 Mon Sep 17 00:00:00 2001 From: Fangting Liu Date: Tue, 28 Nov 2023 16:16:26 -0800 Subject: [PATCH 6/8] ERC-6900: add missing struct --- ERCS/erc-6900.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 13519da4e40..0977e4db430 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -433,6 +433,11 @@ struct ManifestExternalCallPermission { bytes4[] selectors; } +struct SelectorPermission { + bytes4 functionSelector; + string permissionDescription; +} + /// @dev A struct holding fields to describe the plugin in a purely view context. Intended for front end clients. struct PluginMetadata { // A human-readable name of the plugin. From bad05a4e35337664fe9f8a69b8bd55fdec8be906 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 29 Nov 2023 14:24:47 -0500 Subject: [PATCH 7/8] Fix external ERC links --- ERCS/erc-6900.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 0977e4db430..83efbb996ce 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -13,7 +13,7 @@ requires: 165, 4337 ## Abstract -This proposal standardizes smart contract accounts and account plugins, which are smart contract interfaces that allow for composable logic within smart contract accounts. This proposal is compliant with [ERC-4337](./eip-4337.md), and takes inspiration from [ERC-2535](./eip-2535.md) when defining interfaces for updating and querying modular function implementations. +This proposal standardizes smart contract accounts and account plugins, which are smart contract interfaces that allow for composable logic within smart contract accounts. This proposal is compliant with [ERC-4337](./erc-4337.md), and takes inspiration from [ERC-2535](./erc-2535.md) when defining interfaces for updating and querying modular function implementations. This modular approach splits account functionality into three categories, implements them in external contracts, and defines an expected execution flow from accounts. From 0c758739c75e801534b282a335177e63a2f26195 Mon Sep 17 00:00:00 2001 From: adam Date: Fri, 1 Dec 2023 15:36:25 -0500 Subject: [PATCH 8/8] Test CI checking --- ERCS/erc-6900.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ERCS/erc-6900.md b/ERCS/erc-6900.md index 83efbb996ce..1505452bb3c 100644 --- a/ERCS/erc-6900.md +++ b/ERCS/erc-6900.md @@ -13,7 +13,7 @@ requires: 165, 4337 ## Abstract -This proposal standardizes smart contract accounts and account plugins, which are smart contract interfaces that allow for composable logic within smart contract accounts. This proposal is compliant with [ERC-4337](./erc-4337.md), and takes inspiration from [ERC-2535](./erc-2535.md) when defining interfaces for updating and querying modular function implementations. +This proposal standardizes smart contract accounts and account plugins, which are smart contract interfaces that allow for composable logic within smart contract accounts. This proposal is compliant with [ERC-4337](./eip-4337.md), and takes inspiration from [ERC-2535](./eip-2535.md) when defining interfaces for updating and querying modular function implementations. This modular approach splits account functionality into three categories, implements them in external contracts, and defines an expected execution flow from accounts. @@ -27,7 +27,7 @@ However, managing multiple account instances provides a worse user experience, f We propose a standard that coordinates the implementation work between plugin developers and wallet developers. This standard defines a modular smart contract account capable of supporting all standard-conformant plugins. This allows users to have greater portability of their data, and for plugin developers to not have to choose specific account implementations to support. -![diagram showing relationship between accounts and plugins with modular functions](../assets/erc-6900/MSCA_Shared_Components_Diagram.svg) +![diagram showing relationship between accounts and plugins with modular functions](../assets/eip-6900/MSCA_Shared_Components_Diagram.svg) We take inspiration from ERC-2535's diamond pattern for routing execution based on function selectors, and create a similarly composable account. However, the standard does not require the multi-facet proxy pattern. @@ -72,14 +72,14 @@ A modular account handles two kinds of calls: either from the `Entrypoint` throu A call to the smart contract account can be broken down into the steps as shown in the diagram below. The validation steps validate if the caller is allowed to perform the call. The pre execution hook step can be used to do any pre execution checks or updates. It can also be used along with the post execution hook step to perform additional actions or verification. The execution step performs a defined task or collection of tasks. -![diagram showing call flow within an modular account](../assets/erc-6900/Modular_Account_Call_Flow.svg) +![diagram showing call flow within an modular account](../assets/eip-6900/Modular_Account_Call_Flow.svg) The following diagram shows permitted plugin execution flows. During a plugin's execution step from the above diagram, the plugin may perform a "Plugin Execution Function", using either `executeFromPlugin` or `executeFromPluginExternal`. These can be used by plugins to execute using the account's context. - `executeFromPlugin` handles calls to other installed plugin's execution function on the modular account. - `executeFromPluginExternal` handles calls to external contracts. -![diagram showing a plugin execution flow](../assets/erc-6900/Plugin_Execution_Flow.svg) +![diagram showing a plugin execution flow](../assets/eip-6900/Plugin_Execution_Flow.svg) Each step is modular, supporting different implementations for each execution function, and composable, supporting multiple steps through hooks. Combined, these allow for open-ended programmable accounts.