Skip to content

EIP 7702 support #8

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

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
DEPLOYER_PRIVATE_KEY=

FOUNDRY_DISABLE_NIGHTLY_WARNING=1

ETHERSCAN_API_KEY=
ARBISCAN_API_KEY=
OPTIMISM_EXPLORER_API_KEY=
Expand Down
409 changes: 409 additions & 0 deletions .gas-snapshot

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ out/
lcov.info
coverage/

# Node
node_modules/
# Soldeer
/dependencies
15 changes: 0 additions & 15 deletions .gitmodules

This file was deleted.

16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ Smart contract infrastructure for Etherspot Modular Accounts, supporting ERC7579

## Installation & Setup

Ensure that (Foundry)[https://github.com/foundry-rs/foundry] is installed.
Ensure that [Foundry](https://github.com/foundry-rs/foundry) is installed.

```
```bash
forge install
forge build
forge test
```

## Dependencies

Uses Solidity native dependency manager [Soldeer](https://soldeer.xyz/) as package manager.

To install dependencies:

```bash
forge soldeer install
```

## ERC7579 Modular Contract Deployments

### Prerequisites
Expand Down Expand Up @@ -44,7 +54,7 @@ For individual deployment scripts (example):

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
pragma solidity ^0.8.27;;

import "@etherspot/modular-accounts/src/wallet/ModularEtherspotWallet.sol";

Expand Down
110 changes: 110 additions & 0 deletions docs/RESOURCE_LOCK_VALIDATOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Resource Lock Validator

The ResourceLockValidator is a validator module for ERC-7579 smart accounts that enables secure session key management through resource locking mechanisms and merkle proofs.

## Overview

This validator implements signature verification for both direct ECDSA signatures and merkle proof-based validations. It supports standard ethereum signatures as well as merkle proof verification for batched resource lock authorizations.

## Key Features

- ECDSA signature validation
- Merkle proof verification
- Resource lock management
- ERC-1271 compatible signature verification
- Support for both direct and eth-signed message recovery

## Contract Methods

### Installation Methods

#### `onInstall(bytes calldata _data)`

Installs the validator for a smart account, setting the owner from the provided data.

- Reverts if already installed
- Emits `RLV_ValidatorEnabled` event

#### `onUninstall(bytes calldata)`

Removes the validator from a smart account.

- Reverts if not installed
- Emits `RLV_ValidatorDisabled` event

### Validation Methods

#### `validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)`

Validates user operations through either direct signatures or merkle proofs.

- Returns validation success/failure status
- Handles both standard 65-byte signatures and merkle proof packed signatures

#### `isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata signature)`

ERC-1271 compatible signature verification method.

- Returns ERC-1271 magic value for valid signatures
- Supports both direct signatures and merkle proof verification

### Utility Methods

#### `isModuleType(uint256 typeID)`

Checks if the module is a validator type.

#### `isInitialized(address smartAccount)`

Checks if the validator is initialized for a given smart account.

## Resource Lock Structure

Resource locks contain the following parameters:

- Chain ID
- Smart wallet address
- Session key address
- Valid after timestamp
- Valid until timestamp
- Token data array
- Nonce

## Token Data Structure

Each token data entry contains:

- Token address
- Amount

## Events

- `RLV_ValidatorEnabled(address smartAccount, address owner)`
- `RLV_ValidatorDisabled(address smartAccount)`

## Error Conditions

- `RLV_AlreadyInstalled`: Validator already installed for account
- `RLV_NotInstalled`: Validator not installed for account
- `RLV_ResourceLockHashNotInProof`: Resource lock hash not found in merkle proof
- `RLV_OnlyCallTypeSingle`: Only single call type operations supported

## Security Considerations

- Merkle proofs must be properly validated
- Signature recovery includes both standard and eth-signed message formats
- Resource lock parameters should be carefully validated
- Nonce management is critical for replay protection

## Integration Guide

1. Install validator on smart account
2. Generate resource lock with desired parameters
3. Create merkle tree if batching multiple locks
4. Sign either directly or through merkle root
5. Submit user operation with appropriate signature format

## Dependencies

- Solady ECDSA
- Solady MerkleProofLib
42 changes: 31 additions & 11 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,24 +1,44 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
libs = ["dependencies"]
bytecodeHash = "none"
cbor_metadata = false
solc_version = '0.8.23'
evm_version = "london"
evm_version = "cancun"
optimizer = true
runs = 1000
gas_reports = [
"ModularEtherspotWallet",
"ModularEtherspotWalletFactory",
"MultipleOwnerECDSAValidator",
"ERC20SessionKeyValidator",
"ModuleIsolationHook",
"SessionKeyValidator",
runs = 256
gas_reports = ["*"]

remappings = [
"@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.2.0/",
"ERC4337/=dependencies/eth-infinitism-account-abstraction-0.7/contracts/",
"ERC7579/=src/ERC7579/",
"excessively-safe-call/=dependencies/excessively-safe-call-1.0.0/",
"forge-std/=dependencies/forge-std-1.9.6/src/",
"solady/=dependencies/solady-0.1.8/",
"solarray/=dependencies/solarray-0.1/src/",
]
auto_detect_remappings = false

[fuzz]
runs = 256
via_ir = true

[soldeer]
remappings_generate = false
remappings_regenerate = false
remappings_version = false
remappings_location = "txt"
remappings_prefix = ""
recursive_deps = true

[dependencies]
solady = "0.1.8"
"@openzeppelin-contracts" = "5.2.0"
eth-infinitism-account-abstraction = "0.7"
forge-std = "1.9.6"
solarray = { version = "0.1", git = "[email protected]:evmcheb/solarray.git", rev = "a547630f9bf7837af9e6919d217672afe7abf7f1" }
excessively-safe-call = { version = "1.0.0", git = "https://github.com/nomad-xyz/ExcessivelySafeCall.git", rev = "81cd99ce3e69117d665d7601c330ea03b97acce0" }

[rpc_endpoints]
# mainnets
Expand Down
1 change: 0 additions & 1 deletion lib/account-abstraction
Submodule account-abstraction deleted from 7af70c
1 change: 0 additions & 1 deletion lib/forge-std
Submodule forge-std deleted from bf909b
1 change: 0 additions & 1 deletion lib/openzeppelin-contracts
Submodule openzeppelin-contracts deleted from f42376
1 change: 0 additions & 1 deletion lib/solady
Submodule solady deleted from be154c
1 change: 0 additions & 1 deletion lib/solarray
Submodule solarray deleted from a54763
6 changes: 0 additions & 6 deletions remappings.txt

This file was deleted.

99 changes: 99 additions & 0 deletions script/CredibleAccountScript.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import "forge-std/Script.sol";
import {console2} from "forge-std/console2.sol";
import {IERC7484} from "../src/interfaces/ercs/IERC7484.sol";
import {HookMultiPlexer} from "../src/modules/hooks/HookMultiPlexer.sol";
import {CredibleAccountValidator} from "../src/modules/validators/CredibleAccountValidator.sol";
import {CredibleAccountHook} from "../src/modules/hooks/CredibleAccountHook.sol";

contract CredibleAccountSetupScript is Script {
bytes32 public immutable SALT = bytes32(abi.encodePacked("ModularEtherspotWallet:Create2:salt"));
address public constant DEPLOYER = 0x09FD4F6088f2025427AB1e89257A44747081Ed59;
address public constant ERC7484_REGISTRY_ADDRESS = 0x000000000069E2a187AEFFb852bF3cCdC95151B2; // on Sepolia
address public constant EXPECTED_MULTIPLEXER_ADDRESS = 0x0000000000000000000000000000000000000000;
address public constant EXPECTED_VALIDATOR_ADDRESS = 0x0000000000000000000000000000000000000000;
address public constant EXPECTED_HOOK_ADDRESS = 0x0000000000000000000000000000000000000000;

function run() external {
IERC7484 erc7484Registry = IERC7484(ERC7484_REGISTRY_ADDRESS);
HookMultiPlexer hookMultiPlexer;
CredibleAccountValidator credibleAccountValidator;
CredibleAccountHook credibleAccountHook;
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

/*//////////////////////////////////////////////////////////////
Starting Deployment
//////////////////////////////////////////////////////////////*/

console2.log("Starting deployment sequence...");

/*//////////////////////////////////////////////////////////////
Deploy HookMultiPlexer
//////////////////////////////////////////////////////////////*/

console2.log("Deploying HookMultiPlexer...");
// if (EXPECTED_MULTIPLEXER_ADDRESS.code.length == 0) {
hookMultiPlexer = new HookMultiPlexer{salt: SALT}();
// if (address(hookMultiPlexer) != EXPECTED_MULTIPLEXER_ADDRESS) {
// revert("Unexpected HookMultiPlexer address!!!");
// } else {
console2.log("HookMultiPlexer deployed at address", address(hookMultiPlexer));
// }
// } else {
// console2.log("HookMultiPlexer already deployed at address", EXPECTED_MULTIPLEXER_ADDRESS);
// }
Comment on lines +31 to +47
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

HookMultiPlexer deployment with commented-out validation.

The deployment logic works, but the address validation is commented out. This might be intentional for development but should be addressed before production use.

If this script is intended for production deployment, consider uncommenting and implementing the address validation logic, or explicitly documenting why the validation is disabled.


🏁 Script executed:

#!/bin/bash
# Check if this script is used in production environments by looking for references
grep -r "CredibleAccountSetupScript" --include="*.sol" .

Length of output: 141


Action Required: Address Commented-out Address Validation in Production Deployment Script

The grep results confirm that the CredibleAccountScript.s.sol file is used in production (it references CredibleAccountSetupScript), so leaving the address validation commented out poses a risk. Please either:

  • Uncomment and fully implement the address validation logic (lines 31-47) to check that the deployed HookMultiPlexer address matches the expected value.
  • OR add clear documentation explaining why the validation is intentionally disabled—with assurances that any necessary production checks are performed elsewhere.

Affected Location:

  • File: script/CredibleAccountScript.s.sol, Lines: 31-47


/*//////////////////////////////////////////////////////////////
Deploy CredibleAccountValidator
//////////////////////////////////////////////////////////////*/

console2.log("Deploying CredibleAccountValidator...");
// if (EXPECTED_VALIDATOR_ADDRESS.code.length == 0) {
credibleAccountValidator = new CredibleAccountValidator{salt: SALT}(DEPLOYER, hookMultiPlexer);
// if (address(credibleAccountValidator) != EXPECTED_VALIDATOR_ADDRESS) {
// revert("Unexpected CredibleAccountValidator address!!!");
// } else {
console2.log("CredibleAccountValidator deployed at address", address(credibleAccountValidator));
// }
// } else {
// console2.log("CredibleAccountValidator already deployed at address", EXPECTED_VALIDATOR_ADDRESS);
// }

/*//////////////////////////////////////////////////////////////
Deploy CredibleAccountHook
//////////////////////////////////////////////////////////////*/

console2.log("Deploying CredibleAccountHook...");
// if (EXPECTED_HOOK_ADDRESS.code.length == 0) {
credibleAccountHook = new CredibleAccountHook{salt: SALT}(credibleAccountValidator);
// if (address(credibleAccountHook) != EXPECTED_HOOK_ADDRESS) {
// revert("Unexpected CredibleAccountHook address!!!");
// } else {
console2.log("CredibleAccountHook deployed at address", address(credibleAccountHook));
// }
// } else {
// console2.log("CredibleAccountHook already deployed at address", EXPECTED_HOOK_ADDRESS);
// }

/*//////////////////////////////////////////////////////////////
Initialization of CredibleAccountValidator
//////////////////////////////////////////////////////////////*/

console2.log("Initializing CredibleAccountValidator...");
credibleAccountValidator.initialize(credibleAccountHook);
console2.log(
"CredibleAccountValidator initialized?",
address(credibleAccountValidator.caHook()) == address(credibleAccountHook)
);

/*//////////////////////////////////////////////////////////////
Finishing Deployment
//////////////////////////////////////////////////////////////*/
console2.log("Finished deployment sequence!");

vm.stopBroadcast();
}
}
Loading