Skip to content

Commit

Permalink
refactor: module suffix to execution and validation interfaces (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaypaik authored Aug 14, 2024
1 parent ed9258e commit 47b912a
Show file tree
Hide file tree
Showing 41 changed files with 235 additions and 187 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ ENTRYPOINT=
# Create2 expected addresses of the contracts.
# When running for the first time, the error message will contain the expected addresses.
ACCOUNT_IMPL=
SINGLE_SIGNER_VALIDATION=
SINGLE_SIGNER_VALIDATION_MODULE=
FACTORY=

# Optional, defaults to bytes32(0)
ACCOUNT_IMPL_SALT=
FACTORY_SALT=
SINGLE_SIGNER_VALIDATION_SALT=
SINGLE_SIGNER_VALIDATION_MODULE_SALT=

# Optional, defaults to 0.1 ether and 1 day, respectively
STAKE_AMOUNT=
Expand Down
5 changes: 3 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 three modules (`SingleSignerValidation`, `TokenReceiverModule`, and `AllowlistModule`). It is compliant with ERC-6900 with the latest updates.
The implementation includes an upgradable modular account with three modules (`SingleSignerValidationModule`, `TokenReceiverModule`, and `AllowlistModule`). It is compliant with ERC-6900 with the latest updates.

## Important Callouts

Expand Down Expand Up @@ -31,9 +31,10 @@ FOUNDRY_PROFILE=optimized-test forge test -vvv

## Integration testing

The reference implementation provides a sample factory and deploy script for the factory, account implementation, and the demo validation module `SingleSignerValidation`. This is not auditted, nor intended for production use. Limitations set by the GPL-V3 license apply.
The reference implementation provides a sample factory and deploy script for the factory, account implementation, and the demo validation module `SingleSignerValidationModule`. This is not auditted, nor intended for production use. Limitations set by the GPL-V3 license apply.

To run this script, provide appropriate values in a `.env` file based on the `.env.example` template, then run:

```bash
forge script script/Deploy.s.sol <wallet options> -r <rpc_url> --broadcast
```
Expand Down
5 changes: 2 additions & 3 deletions deployments/arb-sepolia.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@ Chain ID: 421614
| v0.8.0-alpha.1 | `0xC64Cb5192a1440Fea12CE03D000EAeB247B2369B` | [explorer](https://sepolia.arbiscan.io/address/0xC64Cb5192a1440Fea12CE03D000EAeB247B2369B) | `0` |
| v0.8.0-alpha.0 | `0x0809BF385117a43A322A4E31d459c0EcaA3B1A08` | [explorer](https://sepolia.arbiscan.io/address/0x0809BF385117a43A322A4E31d459c0EcaA3B1A08) | `0` |

## SingleSignerValidation
## SingleSignerValidationModule

| Version | Address | Explorer | Salt |
| -------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ | ---- |
| v0.8.0-alpha.1 | `0xEa3a0b544d517f6Ed3Dc2186C74D869c702C376e` | [explorer](https://sepolia.arbiscan.io/address/0xEa3a0b544d517f6Ed3Dc2186C74D869c702C376e) | `0` |
| v0.8.0-alpha.0 | `0x9DA8c098A483E257dd96022831DF308cB24fCBE6` | [explorer](https://sepolia.arbiscan.io/address/0x9DA8c098A483E257dd96022831DF308cB24fCBE6) | `0` |


## AllowlistModule

| Version | Address | Explorer | Salt |
| -------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------ | ---- |
| v0.8.0-alpha.1 | `0x5B13F222A841A42C59324FFF0A229FfeA1CAcC3c` | [explorer](https://sepolia.arbiscan.io/address/0x5B13F222A841A42C59324FFF0A229FfeA1CAcC3c) | `0` |
| v0.8.0-alpha.1 | `0x5B13F222A841A42C59324FFF0A229FfeA1CAcC3c` | [explorer](https://sepolia.arbiscan.io/address/0x5B13F222A841A42C59324FFF0A229FfeA1CAcC3c) | `0` |
23 changes: 12 additions & 11 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";

import {AccountFactory} from "../src/account/AccountFactory.sol";
import {UpgradeableModularAccount} from "../src/account/UpgradeableModularAccount.sol";
import {SingleSignerValidation} from "../src/modules/validation/SingleSignerValidation.sol";
import {SingleSignerValidationModule} from "../src/modules/validation/SingleSignerValidationModule.sol";

contract DeployScript is Script {
IEntryPoint public entryPoint = IEntryPoint(payable(vm.envAddress("ENTRYPOINT")));
Expand All @@ -17,11 +17,12 @@ contract DeployScript is Script {

address public accountImpl = vm.envOr("ACCOUNT_IMPL", address(0));
address public factory = vm.envOr("FACTORY", address(0));
address public singleSignerValidation = vm.envOr("SINGLE_SIGNER_VALIDATION", address(0));
address public singleSignerValidationModule = vm.envOr("SINGLE_SIGNER_VALIDATION_MODULE", address(0));

bytes32 public accountImplSalt = bytes32(vm.envOr("ACCOUNT_IMPL_SALT", uint256(0)));
bytes32 public factorySalt = bytes32(vm.envOr("FACTORY_SALT", uint256(0)));
bytes32 public singleSignerValidationSalt = bytes32(vm.envOr("SINGLE_SIGNER_VALIDATION_SALT", uint256(0)));
bytes32 public singleSignerValidationModuleSalt =
bytes32(vm.envOr("SINGLE_SIGNER_VALIDATION_MODULE_SALT", uint256(0)));

uint256 public requiredStakeAmount = vm.envOr("STAKE_AMOUNT", uint256(0.1 ether));
uint256 public requiredUnstakeDelay = vm.envOr("UNSTAKE_DELAY", uint256(1 days));
Expand All @@ -34,7 +35,7 @@ contract DeployScript is Script {

vm.startBroadcast();
_deployAccountImpl(accountImplSalt, accountImpl);
_deploySingleSignerValidation(singleSignerValidationSalt, singleSignerValidation);
_deploySingleSignerValidationModule(singleSignerValidationModuleSalt, singleSignerValidationModule);
_deployAccountFactory(factorySalt, factory);
_addStakeForFactory(uint32(requiredUnstakeDelay), requiredStakeAmount);
vm.stopBroadcast();
Expand Down Expand Up @@ -72,11 +73,11 @@ contract DeployScript is Script {
}
}

function _deploySingleSignerValidation(bytes32 salt, address expected) internal {
console.log(string.concat("Deploying SingleSignerValidation with salt: ", vm.toString(salt)));
function _deploySingleSignerValidationModule(bytes32 salt, address expected) internal {
console.log(string.concat("Deploying SingleSignerValidationModule with salt: ", vm.toString(salt)));

address addr = Create2.computeAddress(
salt, keccak256(abi.encodePacked(type(SingleSignerValidation).creationCode)), CREATE2_FACTORY
salt, keccak256(abi.encodePacked(type(SingleSignerValidationModule).creationCode)), CREATE2_FACTORY
);
if (addr != expected) {
console.log("Expected address mismatch");
Expand All @@ -87,7 +88,7 @@ contract DeployScript is Script {

if (addr.code.length == 0) {
console.log("No code found at expected address, deploying...");
SingleSignerValidation deployed = new SingleSignerValidation{salt: salt}();
SingleSignerValidationModule deployed = new SingleSignerValidationModule{salt: salt}();

if (address(deployed) != expected) {
console.log("Deployed address mismatch");
Expand All @@ -96,7 +97,7 @@ contract DeployScript is Script {
revert();
}

console.log("Deployed SingleSignerValidation at: ", address(deployed));
console.log("Deployed SingleSignerValidationModule at: ", address(deployed));
} else {
console.log("Code found at expected address, skipping deployment");
}
Expand All @@ -110,7 +111,7 @@ contract DeployScript is Script {
keccak256(
abi.encodePacked(
type(AccountFactory).creationCode,
abi.encode(entryPoint, accountImpl, singleSignerValidation, owner)
abi.encode(entryPoint, accountImpl, singleSignerValidationModule, owner)
)
),
CREATE2_FACTORY
Expand All @@ -125,7 +126,7 @@ contract DeployScript is Script {
if (addr.code.length == 0) {
console.log("No code found at expected address, deploying...");
AccountFactory deployed = new AccountFactory{salt: salt}(
entryPoint, UpgradeableModularAccount(payable(accountImpl)), singleSignerValidation, owner
entryPoint, UpgradeableModularAccount(payable(accountImpl)), singleSignerValidationModule, owner
);

if (address(deployed) != expected) {
Expand Down
8 changes: 4 additions & 4 deletions src/account/AccountFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ contract AccountFactory is Ownable {
UpgradeableModularAccount public immutable ACCOUNT_IMPL;
bytes32 private immutable _PROXY_BYTECODE_HASH;
IEntryPoint public immutable ENTRY_POINT;
address public immutable SINGLE_SIGNER_VALIDATION;
address public immutable SINGLE_SIGNER_VALIDATION_MODULE;

constructor(
IEntryPoint _entryPoint,
UpgradeableModularAccount _accountImpl,
address _singleSignerValidation,
address _singleSignerValidationModule,
address owner
) Ownable(owner) {
ENTRY_POINT = _entryPoint;
_PROXY_BYTECODE_HASH =
keccak256(abi.encodePacked(type(ERC1967Proxy).creationCode, abi.encode(address(_accountImpl), "")));
ACCOUNT_IMPL = _accountImpl;
SINGLE_SIGNER_VALIDATION = _singleSignerValidation;
SINGLE_SIGNER_VALIDATION_MODULE = _singleSignerValidationModule;
}

/**
Expand All @@ -50,7 +50,7 @@ contract AccountFactory is Ownable {
new ERC1967Proxy{salt: combinedSalt}(address(ACCOUNT_IMPL), "");
// point proxy to actual implementation and init plugins
UpgradeableModularAccount(payable(addr)).initializeWithValidation(
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION, entityId, true, true),
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true),
new bytes4[](0),
pluginInstallData,
new bytes[](0)
Expand Down
2 changes: 1 addition & 1 deletion src/account/ModuleManagerInternals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {HookConfigLib} from "../helpers/HookConfigLib.sol";
import {KnownSelectors} from "../helpers/KnownSelectors.sol";
import {ModuleEntityLib} from "../helpers/ModuleEntityLib.sol";
import {ValidationConfigLib} from "../helpers/ValidationConfigLib.sol";
import {ExecutionManifest, ManifestExecutionHook} from "../interfaces/IExecution.sol";
import {ExecutionManifest, ManifestExecutionHook} from "../interfaces/IExecutionModule.sol";
import {IModule} from "../interfaces/IModule.sol";
import {HookConfig, IModuleManager, ModuleEntity, ValidationConfig} from "../interfaces/IModuleManager.sol";
import {
Expand Down
23 changes: 12 additions & 11 deletions src/account/UpgradeableModularAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import {_coalescePreValidation, _coalesceValidation} from "../helpers/Validation

import {DIRECT_CALL_VALIDATION_ENTITYID, RESERVED_VALIDATION_DATA_INDEX} from "../helpers/Constants.sol";

import {ExecutionManifest} from "../interfaces/IExecution.sol";
import {IExecutionHook} from "../interfaces/IExecutionHook.sol";
import {IExecutionHookModule} from "../interfaces/IExecutionHookModule.sol";
import {ExecutionManifest} from "../interfaces/IExecutionModule.sol";
import {IModuleManager, ModuleEntity, ValidationConfig} from "../interfaces/IModuleManager.sol";
import {Call, IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
import {IValidation} from "../interfaces/IValidation.sol";
import {IValidationHook} from "../interfaces/IValidationHook.sol";

import {IValidationHookModule} from "../interfaces/IValidationHookModule.sol";
import {IValidationModule} from "../interfaces/IValidationModule.sol";
import {AccountExecutor} from "./AccountExecutor.sol";
import {AccountLoupe} from "./AccountLoupe.sol";
import {AccountStorage, getAccountStorage, toHookConfig, toSetValue} from "./AccountStorage.sol";
Expand Down Expand Up @@ -310,7 +311,7 @@ contract UpgradeableModularAccount is
}

if (
IValidation(module).validateSignature(address(this), entityId, msg.sender, hash, signature[24:])
IValidationModule(module).validateSignature(address(this), entityId, msg.sender, hash, signature[24:])
== _1271_MAGIC_VALUE
) {
return _1271_MAGIC_VALUE;
Expand Down Expand Up @@ -397,7 +398,7 @@ contract UpgradeableModularAccount is

(address module, uint32 entityId) = preUserOpValidationHooks[i].unpack();
uint256 currentValidationRes =
IValidationHook(module).preUserOpValidationHook(entityId, userOp, userOpHash);
IValidationHookModule(module).preUserOpValidationHook(entityId, userOp, userOpHash);

if (uint160(currentValidationRes) > 1) {
// If the aggregator is not 0 or 1, it is an unexpected value
Expand All @@ -415,7 +416,7 @@ contract UpgradeableModularAccount is
userOp.signature = signatureSegment.getBody();

(address module, uint32 entityId) = userOpValidationFunction.unpack();
uint256 currentValidationRes = IValidation(module).validateUserOp(entityId, userOp, userOpHash);
uint256 currentValidationRes = IValidationModule(module).validateUserOp(entityId, userOp, userOpHash);

if (preUserOpValidationHooks.length != 0) {
// If we have other validation data we need to coalesce with
Expand Down Expand Up @@ -470,7 +471,7 @@ contract UpgradeableModularAccount is

(address module, uint32 entityId) = runtimeValidationFunction.unpack();

try IValidation(module).validateRuntime(
try IValidationModule(module).validateRuntime(
address(this), entityId, msg.sender, msg.value, callData, authSegment.getBody()
)
// forgefmt: disable-start
Expand Down Expand Up @@ -521,7 +522,7 @@ contract UpgradeableModularAccount is
returns (bytes memory preExecHookReturnData)
{
(address module, uint32 entityId) = preExecHook.unpack();
try IExecutionHook(module).preExecutionHook(entityId, msg.sender, msg.value, data) returns (
try IExecutionHookModule(module).preExecutionHook(entityId, msg.sender, msg.value, data) returns (
bytes memory returnData
) {
preExecHookReturnData = returnData;
Expand All @@ -547,7 +548,7 @@ contract UpgradeableModularAccount is

(address module, uint32 entityId) = postHookToRun.postExecHook.unpack();
// solhint-disable-next-line no-empty-blocks
try IExecutionHook(module).postExecutionHook(entityId, postHookToRun.preExecHookReturnData) {}
try IExecutionHookModule(module).postExecutionHook(entityId, postHookToRun.preExecHookReturnData) {}
catch (bytes memory revertReason) {
revert PostExecHookReverted(module, entityId, revertReason);
}
Expand All @@ -560,7 +561,7 @@ contract UpgradeableModularAccount is
bytes memory currentAuthData
) internal {
(address hookModule, uint32 hookEntityId) = validationHook.unpack();
try IValidationHook(hookModule).preRuntimeValidationHook(
try IValidationHookModule(hookModule).preRuntimeValidationHook(
hookEntityId, msg.sender, msg.value, callData, currentAuthData
)
// forgefmt: disable-start
Expand Down
23 changes: 13 additions & 10 deletions src/helpers/KnownSelectors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IAccountLoupe} from "../interfaces/IAccountLoupe.sol";

import {IExecution} from "../interfaces/IExecution.sol";
import {IExecutionHook} from "../interfaces/IExecutionHook.sol";
import {IExecutionHookModule} from "../interfaces/IExecutionHookModule.sol";
import {IExecutionModule} from "../interfaces/IExecutionModule.sol";
import {IModule} from "../interfaces/IModule.sol";
import {IModuleManager} from "../interfaces/IModuleManager.sol";
import {IStandardExecutor} from "../interfaces/IStandardExecutor.sol";
import {IValidation} from "../interfaces/IValidation.sol";
import {IValidationHook} from "../interfaces/IValidationHook.sol";

import {IValidationHookModule} from "../interfaces/IValidationHookModule.sol";
import {IValidationModule} from "../interfaces/IValidationModule.sol";

/// @dev Library to help to check if a selector is a know function selector of the modular account or ERC-4337
/// contract.
Expand Down Expand Up @@ -49,11 +50,13 @@ library KnownSelectors {

function isIModuleFunction(bytes4 selector) internal pure returns (bool) {
return selector == IModule.onInstall.selector || selector == IModule.onUninstall.selector
|| selector == IExecution.executionManifest.selector || selector == IModule.moduleMetadata.selector
|| selector == IExecutionHook.preExecutionHook.selector
|| selector == IExecutionHook.postExecutionHook.selector || selector == IValidation.validateUserOp.selector
|| selector == IValidation.validateRuntime.selector || selector == IValidation.validateSignature.selector
|| selector == IValidationHook.preUserOpValidationHook.selector
|| selector == IValidationHook.preRuntimeValidationHook.selector;
|| selector == IExecutionModule.executionManifest.selector || selector == IModule.moduleMetadata.selector
|| selector == IExecutionHookModule.preExecutionHook.selector
|| selector == IExecutionHookModule.postExecutionHook.selector
|| selector == IValidationModule.validateUserOp.selector
|| selector == IValidationModule.validateRuntime.selector
|| selector == IValidationModule.validateSignature.selector
|| selector == IValidationHookModule.preUserOpValidationHook.selector
|| selector == IValidationHookModule.preRuntimeValidationHook.selector;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.25;

import {IModule} from "./IModule.sol";

interface IExecutionHook is IModule {
interface IExecutionHookModule is IModule {
/// @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct ExecutionManifest {
bytes4[] interfaceIds;
}

interface IExecution is IModule {
interface IExecutionModule is IModule {
/// @notice Describe the contents and intended configuration of the module.
/// @dev This manifest MUST stay constant over time.
/// @return A manifest describing the contents and intended configuration of the module.
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IModuleManager.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.25;

import {ExecutionManifest} from "./IExecution.sol";
import {ExecutionManifest} from "./IExecutionModule.sol";

type ModuleEntity is bytes24;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface

import {IModule} from "./IModule.sol";

interface IValidationHook is IModule {
interface IValidationHookModule is IModule {
/// @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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interface

import {IModule} from "./IModule.sol";

interface IValidation is IModule {
interface IValidationModule is IModule {
/// @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.
Expand Down
Loading

0 comments on commit 47b912a

Please sign in to comment.