Skip to content

Commit e5f881a

Browse files
authored
[DRAFT] refactor/SMA: Inheritable Account Refactor (#133)
1 parent 0a29a98 commit e5f881a

21 files changed

+359
-271
lines changed

.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
# Factory owner capable only of managing stake
32
OWNER=
43
# EP 0.7 address
@@ -22,3 +21,6 @@ UNSTAKE_DELAY=
2221
# Allowlist Module
2322
ALLOWLIST_MODULE=
2423
ALLOWLIST_MODULE_SALT=
24+
25+
# Whether to test the semi-modular or full modular account
26+
SMA_TEST=false

.github/workflows/test.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ jobs:
6666
run: FOUNDRY_PROFILE=optimized-build forge build
6767

6868
- name: Run tests
69-
run: FOUNDRY_PROFILE=optimized-test-deep forge test -vvv
69+
run: FOUNDRY_PROFILE=optimized-test-deep SMA_TEST=false forge test -vvv
70+
71+
- name: Run SMA tests
72+
run: FOUNDRY_PROFILE=optimized-test-deep SMA_TEST=true forge test -vvv
7073

7174
test-default:
7275
name: Run Forge Tests (default)
@@ -88,4 +91,7 @@ jobs:
8891
run: forge build
8992

9093
- name: Run tests
91-
run: forge test -vvv
94+
run: SMA_TEST=false forge test -vvv
95+
96+
- name: Run SMA tests
97+
run: SMA_TEST=true forge test -vvv

src/account/AccountFactory.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ contract AccountFactory is Ownable {
111111
return keccak256(abi.encodePacked(owner, salt, entityId));
112112
}
113113

114-
function _getAddressFallbackSigner(bytes memory immutables, bytes32 salt) public view returns (address) {
114+
function _getAddressFallbackSigner(bytes memory immutables, bytes32 salt) internal view returns (address) {
115115
return LibClone.predictDeterministicAddressERC1967(address(ACCOUNT_IMPL), immutables, salt, address(this));
116116
}
117117

src/account/AccountStorage.sol

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ struct AccountStorage {
4141
// AccountStorageInitializable variables
4242
uint8 initialized;
4343
bool initializing;
44-
// Address for fallback single signer validation
45-
address fallbackSigner;
46-
// Whether or not the fallback signer is enabled, we can't use a zero fallbackSigner for this since it defaults
47-
// to reading the bytecode-appended signer.
48-
bool fallbackSignerDisabled;
4944
// Execution functions and their associated functions
5045
mapping(bytes4 selector => ExecutionData) executionData;
5146
mapping(ModuleEntity validationFunction => ValidationData) validationData;

src/account/SemiModularAccount.sol

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity ^0.8.25;
3+
4+
import {UpgradeableModularAccount} from "./UpgradeableModularAccount.sol";
5+
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
6+
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
7+
8+
import {ModuleEntityLib} from "../helpers/ModuleEntityLib.sol";
9+
10+
import {ModuleEntity, ValidationConfig} from "../interfaces/IModuleManager.sol";
11+
12+
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
13+
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
14+
import {LibClone} from "solady/utils/LibClone.sol";
15+
16+
contract SemiModularAccount is UpgradeableModularAccount {
17+
using MessageHashUtils for bytes32;
18+
using ModuleEntityLib for ModuleEntity;
19+
20+
struct SemiModularAccountStorage {
21+
address fallbackSigner;
22+
bool fallbackSignerDisabled;
23+
}
24+
25+
// keccak256("ERC6900.SemiModularAccount.Storage")
26+
uint256 internal constant _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT =
27+
0x5b9dc9aa943f8fa2653ceceda5e3798f0686455280432166ba472eca0bc17a32;
28+
29+
ModuleEntity internal constant _FALLBACK_VALIDATION = ModuleEntity.wrap(bytes24(type(uint192).max));
30+
31+
event FallbackSignerSet(address indexed previousFallbackSigner, address indexed newFallbackSigner);
32+
event FallbackSignerEnabledSet(bool prevEnabled, bool newEnabled);
33+
34+
error FallbackSignerMismatch();
35+
error FallbackSignerDisabled();
36+
error InitializerDisabled();
37+
38+
constructor(IEntryPoint anEntryPoint) UpgradeableModularAccount(anEntryPoint) {}
39+
40+
/// Override reverts on initialization, effectively disabling the initializer.
41+
function initializeWithValidation(ValidationConfig, bytes4[] calldata, bytes calldata, bytes[] calldata)
42+
external
43+
override
44+
initializer
45+
{
46+
revert InitializerDisabled();
47+
}
48+
49+
/// @notice Updates the fallback signer address in storage.
50+
/// @dev This function causes the fallback signer getter to ignore the bytecode signer if it is nonzero. It can
51+
/// also be used to revert back to the bytecode signer by setting to zero.
52+
/// @param fallbackSigner The new signer to set.
53+
function updateFallbackSigner(address fallbackSigner) external wrapNativeFunction {
54+
SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage();
55+
emit FallbackSignerSet(_storage.fallbackSigner, fallbackSigner);
56+
57+
_storage.fallbackSigner = fallbackSigner;
58+
}
59+
60+
/// @notice Sets whether the fallback signer validation should be enabled or disabled.
61+
function setFallbackSignerEnabled(bool enabled) external wrapNativeFunction {
62+
SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage();
63+
emit FallbackSignerEnabledSet(!_storage.fallbackSignerDisabled, enabled);
64+
65+
_storage.fallbackSignerDisabled = !enabled;
66+
}
67+
68+
function isFallbackSignerEnabled() external view returns (bool) {
69+
return !_getSemiModularAccountStorage().fallbackSignerDisabled;
70+
}
71+
72+
function getFallbackSigner() external view returns (address) {
73+
return _getFallbackSigner();
74+
}
75+
76+
function _execUserOpValidation(
77+
ModuleEntity userOpValidationFunction,
78+
PackedUserOperation memory userOp,
79+
bytes32 userOpHash
80+
) internal override returns (uint256) {
81+
if (userOpValidationFunction.eq(_FALLBACK_VALIDATION)) {
82+
address fallbackSigner = _getFallbackSigner();
83+
84+
if (
85+
SignatureChecker.isValidSignatureNow(
86+
fallbackSigner, userOpHash.toEthSignedMessageHash(), userOp.signature
87+
)
88+
) {
89+
return _SIG_VALIDATION_PASSED;
90+
}
91+
return _SIG_VALIDATION_FAILED;
92+
}
93+
94+
return super._execUserOpValidation(userOpValidationFunction, userOp, userOpHash);
95+
}
96+
97+
function _execRuntimeValidation(
98+
ModuleEntity runtimeValidationFunction,
99+
bytes calldata callData,
100+
bytes calldata authorization
101+
) internal override {
102+
if (runtimeValidationFunction.eq(_FALLBACK_VALIDATION)) {
103+
address fallbackSigner = _getFallbackSigner();
104+
105+
if (msg.sender != fallbackSigner) {
106+
revert FallbackSignerMismatch();
107+
}
108+
return;
109+
}
110+
super._execRuntimeValidation(runtimeValidationFunction, callData, authorization);
111+
}
112+
113+
function _exec1271Validation(ModuleEntity sigValidation, bytes32 hash, bytes calldata signature)
114+
internal
115+
view
116+
override
117+
returns (bytes4)
118+
{
119+
if (sigValidation.eq(_FALLBACK_VALIDATION)) {
120+
address fallbackSigner = _getFallbackSigner();
121+
122+
if (SignatureChecker.isValidSignatureNow(fallbackSigner, hash, signature)) {
123+
return _1271_MAGIC_VALUE;
124+
}
125+
return _1271_INVALID;
126+
}
127+
return super._exec1271Validation(sigValidation, hash, signature);
128+
}
129+
130+
function _globalValidationAllowed(bytes4 selector) internal view override returns (bool) {
131+
return selector == this.updateFallbackSigner.selector || super._globalValidationAllowed(selector);
132+
}
133+
134+
function _isValidationGlobal(ModuleEntity validationFunction) internal view override returns (bool) {
135+
return validationFunction.eq(_FALLBACK_VALIDATION) || super._isValidationGlobal(validationFunction);
136+
}
137+
138+
function _getFallbackSigner() internal view returns (address) {
139+
SemiModularAccountStorage storage _storage = _getSemiModularAccountStorage();
140+
141+
if (_storage.fallbackSignerDisabled) {
142+
revert FallbackSignerDisabled();
143+
}
144+
145+
address storageFallbackSigner = _storage.fallbackSigner;
146+
if (storageFallbackSigner != address(0)) {
147+
return storageFallbackSigner;
148+
}
149+
150+
bytes memory appendedData = LibClone.argsOnERC1967(address(this), 0, 20);
151+
152+
return address(uint160(bytes20(appendedData)));
153+
}
154+
155+
function _getSemiModularAccountStorage() internal pure returns (SemiModularAccountStorage storage) {
156+
SemiModularAccountStorage storage _storage;
157+
assembly ("memory-safe") {
158+
_storage.slot := _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT
159+
}
160+
return _storage;
161+
}
162+
}

0 commit comments

Comments
 (0)