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

update IPlugin, IPluginManager, IAccountLoupe, BaseModularAccount to match spec update 6 #13

Merged
merged 8 commits into from
Nov 29, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.19;
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IPluginLoupe} from "../interfaces/IPluginLoupe.sol";
import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
import {
Expand All @@ -15,14 +15,12 @@ import {
} from "../libraries/AccountStorage.sol";
import {FunctionReference} from "../libraries/FunctionReferenceLib.sol";

abstract contract BaseModularAccountLoupe is IPluginLoupe {
abstract contract AccountLoupe is IAccountLoupe {
using EnumerableSet for EnumerableSet.AddressSet;

error ManifestDiscrepancy(address plugin);

/// @notice Gets the validator and plugin configuration for a selector
/// @param selector The selector to get the configuration for
/// @return config The configuration for this selector
/// @inheritdoc IAccountLoupe
function getExecutionFunctionConfig(bytes4 selector)
external
view
Expand All @@ -47,9 +45,7 @@ abstract contract BaseModularAccountLoupe is IPluginLoupe {
config.runtimeValidationFunction = _storage.selectorData[selector].runtimeValidation;
}

/// @notice Gets the pre and post execution hooks for a selector
/// @param selector The selector to get the hooks for
/// @return execHooks The pre and post execution hooks for this selector
/// @inheritdoc IAccountLoupe
function getExecutionHooks(bytes4 selector) external view returns (ExecutionHooks[] memory execHooks) {
AccountStorage storage _storage = getAccountStorage();

Expand All @@ -69,10 +65,7 @@ abstract contract BaseModularAccountLoupe is IPluginLoupe {
}
}

/// @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 execHooks The pre and post permitted call hooks for this selector
/// @inheritdoc IAccountLoupe
function getPermittedCallHooks(address callingPlugin, bytes4 selector)
external
view
Expand All @@ -99,32 +92,22 @@ abstract contract BaseModularAccountLoupe is IPluginLoupe {
}
}

/// @notice Gets the pre user op validation hooks associated with a selector
/// @param selector The selector to get the hooks for
/// @return preValidationHooks The pre user op validation hooks for this selector
function getPreUserOpValidationHooks(bytes4 selector)
/// @inheritdoc IAccountLoupe
function getPreValidationHooks(bytes4 selector)
external
view
returns (FunctionReference[] memory preValidationHooks)
returns (
FunctionReference[] memory preUserOpValidationHooks,
FunctionReference[] memory preRuntimeValidationHooks
)
{
preValidationHooks =
preUserOpValidationHooks =
toFunctionReferenceArray(getAccountStorage().selectorData[selector].preUserOpValidationHooks);
}

/// @notice Gets the pre runtime validation hooks associated with a selector
/// @param selector The selector to get the hooks for
/// @return preValidationHooks The pre runtime validation hooks for this selector
function getPreRuntimeValidationHooks(bytes4 selector)
external
view
returns (FunctionReference[] memory preValidationHooks)
{
preValidationHooks =
preRuntimeValidationHooks =
toFunctionReferenceArray(getAccountStorage().selectorData[selector].preRuntimeValidationHooks);
}

/// @notice Gets an array of all installed plugins
/// @return pluginAddresses The addresses of all installed plugins
/// @inheritdoc IAccountLoupe
function getInstalledPlugins() external view returns (address[] memory pluginAddresses) {
pluginAddresses = getAccountStorage().plugins.values();
}
Expand Down
1 change: 1 addition & 0 deletions src/account/AccountStorageInitializable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.19;

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

import {AccountStorage, getAccountStorage} from "../libraries/AccountStorage.sol";

abstract contract AccountStorageInitializable {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

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

import {IPluginManager} from "../interfaces/IPluginManager.sol";
import {AccountExecutor} from "./AccountExecutor.sol";
import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol";
import {
AccountStorage,
getAccountStorage,
Expand All @@ -17,6 +13,8 @@ import {
PermittedExternalCallData,
StoredInjectedHook
} from "../libraries/AccountStorage.sol";
import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
import {
IPlugin,
ManifestExecutionHook,
Expand All @@ -27,14 +25,10 @@ import {
PluginManifest
} from "../interfaces/IPlugin.sol";

abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165 {
abstract contract PluginManagerInternals is IPluginManager {
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.AddressSet;

// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 internal constant _INTERFACE_ID_INVALID = 0xffffffff;
bytes4 internal constant _IERC165_INTERFACE_ID = 0x01ffc9a7;

error ArrayLengthMismatch();
error ExecuteFromPluginAlreadySet(bytes4 selector, address plugin);
error PermittedExecutionSelectorNotInstalled(bytes4 selector, address plugin);
Expand Down Expand Up @@ -382,9 +376,15 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165

// Update components according to the manifest.
// All conflicts should revert.

// Mark whether or not this plugin may spend native token amounts
if (manifest.canSpendNativeToken) {
_storage.pluginData[plugin].canSpendNativeToken = true;
}

length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length;) {
_setExecutionFunction(manifest.executionFunctions[i].selector, plugin);
_setExecutionFunction(manifest.executionFunctions[i], plugin);

unchecked {
++i;
Expand All @@ -402,7 +402,7 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165
}

// Add the permitted external calls to the account.
if (manifest.permitAnyExternalContract) {
if (manifest.permitAnyExternalAddress) {
_storage.pluginData[plugin].anyExternalExecPermitted = true;
} else {
// Only store the specific permitted external calls if "permit any" flag was not set.
Expand Down Expand Up @@ -615,7 +615,7 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165
revert PluginInstallCallbackFailed(plugin, revertReason);
}

emit PluginInstalled(plugin, manifestHash);
emit PluginInstalled(plugin, manifestHash, dependencies, injectedHooks);
}

function _uninstallPlugin(
Expand Down Expand Up @@ -773,7 +773,7 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165

// remove external call permissions

if (manifest.permitAnyExternalContract) {
if (manifest.permitAnyExternalAddress) {
// Only clear if it was set during install time
_storage.pluginData[plugin].anyExternalExecPermitted = false;
} else {
Expand Down Expand Up @@ -839,7 +839,7 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165

length = manifest.executionFunctions.length;
for (uint256 i = 0; i < length;) {
_removeExecutionFunction(manifest.executionFunctions[i].selector);
_removeExecutionFunction(manifest.executionFunctions[i]);

unchecked {
++i;
Expand Down Expand Up @@ -893,7 +893,7 @@ abstract contract BaseModularAccount is IPluginManager, AccountExecutor, IERC165
onUninstallSuccess = false;
}

emit PluginUninstalled(plugin, manifestHash, onUninstallSuccess);
emit PluginUninstalled(plugin, onUninstallSuccess);
}

function _toSetValue(FunctionReference functionReference) internal pure returns (bytes32) {
Expand Down
43 changes: 28 additions & 15 deletions src/account/UpgradeableModularAccount.sol
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import {BaseAccount} from "@eth-infinitism/account-abstraction/core/BaseAccount.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {UserOperation} from "@eth-infinitism/account-abstraction/interfaces/UserOperation.sol";
import {BaseAccount} from "@eth-infinitism/account-abstraction/core/BaseAccount.sol";
import {BaseModularAccount} from "./BaseModularAccount.sol";
import {BaseModularAccountLoupe} from "./BaseModularAccountLoupe.sol";
import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol";
import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol";
import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";

import {AccountExecutor} from "./AccountExecutor.sol";
import {AccountLoupe} from "./AccountLoupe.sol";
import {AccountStorage, getAccountStorage, getPermittedCallKey} from "../libraries/AccountStorage.sol";
import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol";
import {AccountStorageInitializable} from "./AccountStorageInitializable.sol";
import {FunctionReference, FunctionReferenceLib} from "../libraries/FunctionReferenceLib.sol";
import {IPlugin, PluginManifest} from "../interfaces/IPlugin.sol";
import {IPluginExecutor} from "../interfaces/IPluginExecutor.sol";
import {IPluginManager} from "../interfaces/IPluginManager.sol";
import {IStandardExecutor, Call} from "../interfaces/IStandardExecutor.sol";
import {PluginManagerInternals} from "./PluginManagerInternals.sol";
import {_coalescePreValidation, _coalesceValidation} from "../helpers/ValidationDataHelpers.sol";

contract UpgradeableModularAccount is
IPluginManager,
BaseAccount,
BaseModularAccount,
BaseModularAccountLoupe,
UUPSUpgradeable,
AccountExecutor,
AccountLoupe,
AccountStorageInitializable,
BaseAccount,
IERC165,
IPluginExecutor,
IStandardExecutor,
IPluginExecutor
PluginManagerInternals,
UUPSUpgradeable
{
using EnumerableSet for EnumerableSet.Bytes32Set;

Expand All @@ -37,13 +40,18 @@ contract UpgradeableModularAccount is

IEntryPoint private immutable _ENTRY_POINT;

// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 internal constant _INTERFACE_ID_INVALID = 0xffffffff;
bytes4 internal constant _IERC165_INTERFACE_ID = 0x01ffc9a7;

event ModularAccountInitialized(IEntryPoint indexed entryPoint);

error AlwaysDenyRule();
error AuthorizeUpgradeReverted(bytes revertReason);
error ExecFromPluginNotPermitted(address plugin, bytes4 selector);
error ExecFromPluginExternalNotPermitted(address plugin, address target, uint256 value, bytes data);
error InvalidConfiguration();
error NativeTokenSpendingNotPermitted(address plugin);
error PostExecHookReverted(address plugin, uint8 functionId, bytes revertReason);
error PreExecHookReverted(address plugin, uint8 functionId, bytes revertReason);
error PreRuntimeValidationHookFailed(address plugin, uint8 functionId, bytes revertReason);
Expand Down Expand Up @@ -213,6 +221,11 @@ contract UpgradeableModularAccount is
bytes4 selector = bytes4(data);
AccountStorage storage _storage = getAccountStorage();

// Make sure plugin is allowed to spend native token.
if (value > 0 && value > msg.value && !_storage.pluginData[msg.sender].canSpendNativeToken) {
revert NativeTokenSpendingNotPermitted(msg.sender);
}

// Check the caller plugin's permission to make this call

// Check the target contract permission.
Expand Down
56 changes: 56 additions & 0 deletions src/interfaces/IAccountLoupe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.19;

import {FunctionReference} from "../libraries/FunctionReferenceLib.sol";

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);

/// @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);
}
Loading