Skip to content

Commit 1a12ee6

Browse files
fix: implement fixes and add few feat(pvh)
1 parent 7059218 commit 1a12ee6

File tree

7 files changed

+106
-111
lines changed

7 files changed

+106
-111
lines changed

foundry.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ via-ir = true
3838
fuzz = { runs = 10_000 }
3939
verbosity = 4
4040

41+
[profile.via-ir]
42+
via_ir = true
43+
4144
[fuzz]
4245
runs = 1000
4346
max_test_rejects = 1_000_000

src/StartaleSmartAccount.sol

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ contract StartaleSmartAccount is IStartaleSmartAccount, BaseAccount, ExecutionHe
8787
/// @dev Features Module Enable Mode.
8888
/// This Module Enable Mode flow is intended for the module acting as the validator
8989
/// for the user operation that triggers the Module Enable Flow. Otherwise, a call to
90-
/// `Nexus.installModule` should be included in `userOp.callData`.
90+
/// `IERC7579Account.installModule` should be included in `userOp.callData`.
9191
function validateUserOp(
9292
PackedUserOperation calldata op,
9393
bytes32 userOpHash,
@@ -192,6 +192,8 @@ contract StartaleSmartAccount is IStartaleSmartAccount, BaseAccount, ExecutionHe
192192
/// - 4 for Hook
193193
/// - 8 for 1271 Prevalidation Hook
194194
/// - 9 for 4337 Prevalidation Hook
195+
/// @notice
196+
/// If the module is malicious, it can prevent itself from being uninstalled by spending all gas in the onUninstall() method.
195197
/// @param module The address of the module to uninstall.
196198
/// @param deInitData De-initialization data for the module.
197199
/// @dev Ensures that the operation is authorized and valid before proceeding with the uninstallation.
@@ -204,6 +206,7 @@ contract StartaleSmartAccount is IStartaleSmartAccount, BaseAccount, ExecutionHe
204206

205207
if (moduleTypeId == MODULE_TYPE_VALIDATOR) {
206208
_uninstallValidator(module, deInitData);
209+
_checkInitializedValidators();
207210
} else if (moduleTypeId == MODULE_TYPE_EXECUTOR) {
208211
_uninstallExecutor(module, deInitData);
209212
} else if (moduleTypeId == MODULE_TYPE_FALLBACK) {
@@ -437,6 +440,26 @@ contract StartaleSmartAccount is IStartaleSmartAccount, BaseAccount, ExecutionHe
437440
}
438441
}
439442

443+
// checks if there's at least one validator initialized
444+
function _checkInitializedValidators() internal view {
445+
if (!_amIERC7702() && !IValidator(_DEFAULT_VALIDATOR).isInitialized(address(this))) {
446+
unchecked {
447+
SentinelListLib.SentinelList storage validators = _getAccountStorage().validators;
448+
address next = validators.entries[SENTINEL];
449+
while (next != ZERO_ADDRESS && next != SENTINEL) {
450+
if (IValidator(next).isInitialized(address(this))) {
451+
break;
452+
}
453+
next = validators.getNext(next);
454+
}
455+
if (next == SENTINEL) {
456+
//went through all validators and none was initialized
457+
revert CanNotRemoveLastValidator();
458+
}
459+
}
460+
}
461+
}
462+
440463
/// @dev EIP712 domain name and version.
441464
function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
442465
name = 'Startale';

src/core/ExecutionHelper.sol

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,6 @@ abstract contract ExecutionHelper is IExecutionHelper {
3939
}
4040
}
4141

42-
/// @notice Executes a call to a target address with specified value and data.
43-
/// same as _execute, but callData can be in memory
44-
function _executeMemory(
45-
address target,
46-
uint256 value,
47-
bytes memory callData
48-
) internal virtual returns (bytes memory result) {
49-
/// @solidity memory-safe-assembly
50-
assembly {
51-
result := mload(0x40)
52-
if iszero(call(gas(), target, value, add(callData, 0x20), mload(callData), codesize(), 0x00)) {
53-
// Bubble up the revert if the call reverts.
54-
returndatacopy(result, 0x00, returndatasize())
55-
revert(result, returndatasize())
56-
}
57-
mstore(result, returndatasize()) // Store the length.
58-
let o := add(result, 0x20)
59-
returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
60-
mstore(0x40, add(o, returndatasize())) // Allocate the memory.
61-
}
62-
}
63-
6442
/// @notice Executes a call to a target address with specified value and data.
6543
/// Same as _execute but without return data for gas optimization.
6644
function _executeNoReturndata(address target, uint256 value, bytes calldata callData) internal virtual {

src/core/ModuleManager.sol

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
4747
using ExcessivelySafeCall for address;
4848
using ECDSA for bytes32;
4949

50-
/// @dev The slot in the transient storage to store the hooking flag.
51-
// keccak256(abi.encode(uint256(keccak256(bytes("startale.modulemanager.hooking"))) - 1)) & ~bytes32(uint256(0xff))
52-
bytes32 internal constant HOOKING_FLAG_TRANSIENT_STORAGE_SLOT =
53-
0x1085a7be7203eb252e321995729a7b29dd605712ca7b7b85cd33107663f41400;
54-
5550
/// @dev The default validator address.
5651
/// @notice To explicitly initialize the default validator, StartaleSmartAccount.execute(_DEFAULT_VALIDATOR.onInstall(...)) should be called.
5752
address internal immutable _DEFAULT_VALIDATOR;
@@ -75,16 +70,9 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
7570
/// @dev sender, msg.data and msg.value is passed to the hook to implement custom flows.
7671
modifier withHook() {
7772
address hook = _getHook();
78-
bool hooking;
79-
assembly {
80-
hooking := tload(HOOKING_FLAG_TRANSIENT_STORAGE_SLOT)
81-
}
82-
if (hook == address(0) || hooking) {
73+
if (hook == address(0)) {
8374
_;
8475
} else {
85-
assembly {
86-
tstore(HOOKING_FLAG_TRANSIENT_STORAGE_SLOT, 1)
87-
}
8876
bytes memory hookData = IHook(hook).preCheck(msg.sender, msg.value, msg.data);
8977
_;
9078
IHook(hook).postCheck(hookData);
@@ -191,7 +179,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
191179
/// @dev This function goes through hook checks via withHook modifier.
192180
/// @dev No need to check that the module is already installed, as this check is done
193181
/// when trying to sstore the module in an appropriate SentinelList
194-
function _installModule(uint256 moduleTypeId, address module, bytes calldata initData) internal withHook {
182+
function _installModule(uint256 moduleTypeId, address module, bytes calldata initData) internal {
195183
if (!_areSentinelListsInitialized()) {
196184
_initSentinelLists();
197185
}
@@ -218,7 +206,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
218206
/// @dev Installs a new validator module after checking if it matches the required module type.
219207
/// @param validator The address of the validator module to be installed.
220208
/// @param data Initialization data to configure the validator upon installation.
221-
function _installValidator(address validator, bytes calldata data) internal virtual {
209+
function _installValidator(address validator, bytes calldata data) internal virtual withHook {
222210
if (!IValidator(validator).isModuleType(MODULE_TYPE_VALIDATOR)) revert MismatchModuleTypeId();
223211
if (validator == _DEFAULT_VALIDATOR) {
224212
revert DefaultValidatorAlreadyInstalled();
@@ -227,7 +215,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
227215
IValidator(validator).onInstall(data);
228216
}
229217

230-
/// @dev Uninstalls a validator module /!\ ensuring the account retains at least one validator.
218+
/// @dev Uninstalls a validator module
231219
/// @param validator The address of the validator to be uninstalled.
232220
/// @param data De-initialization data to configure the validator upon uninstallation.
233221
function _uninstallValidator(address validator, bytes calldata data) internal virtual {
@@ -246,7 +234,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
246234
/// @dev Installs a new executor module after checking if it matches the required module type.
247235
/// @param executor The address of the executor module to be installed.
248236
/// @param data Initialization data to configure the executor upon installation.
249-
function _installExecutor(address executor, bytes calldata data) internal virtual {
237+
function _installExecutor(address executor, bytes calldata data) internal virtual withHook {
250238
if (!IExecutor(executor).isModuleType(MODULE_TYPE_EXECUTOR)) revert MismatchModuleTypeId();
251239
_getAccountStorage().executors.push(executor);
252240
IExecutor(executor).onInstall(data);
@@ -266,7 +254,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
266254
/// @dev Installs a hook module, ensuring no other hooks are installed before proceeding.
267255
/// @param hook The address of the hook to be installed.
268256
/// @param data Initialization data to configure the hook upon installation.
269-
function _installHook(address hook, bytes calldata data) internal virtual {
257+
function _installHook(address hook, bytes calldata data) internal virtual withHook {
270258
if (!IHook(hook).isModuleType(MODULE_TYPE_HOOK)) revert MismatchModuleTypeId();
271259
address currentHook = _getHook();
272260
require(currentHook == address(0), HookAlreadyInstalled(currentHook));
@@ -297,7 +285,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
297285
/// @dev Installs a fallback handler for a given selector with initialization data.
298286
/// @param handler The address of the fallback handler to install.
299287
/// @param params The initialization parameters including the selector and call type.
300-
function _installFallbackHandler(address handler, bytes calldata params) internal virtual {
288+
function _installFallbackHandler(address handler, bytes calldata params) internal virtual withHook {
301289
if (!IFallback(handler).isModuleType(MODULE_TYPE_FALLBACK)) revert MismatchModuleTypeId();
302290
// Extract the function selector from the provided parameters.
303291
bytes4 selector = bytes4(params[0:4]);
@@ -350,7 +338,7 @@ abstract contract ModuleManager is AllStorage, EIP712, IModuleManagerEventsAndEr
350338
uint256 preValidationHookType,
351339
address preValidationHook,
352340
bytes calldata data
353-
) internal virtual {
341+
) internal virtual withHook {
354342
if (!IModule(preValidationHook).isModuleType(preValidationHookType)) revert MismatchModuleTypeId();
355343
address currentPreValidationHook = _getPreValidationHook(preValidationHookType);
356344
if (currentPreValidationHook != address(0)) revert PrevalidationHookAlreadyInstalled(currentPreValidationHook);

src/factory/EOAOnboardingFactory.sol

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {Stakeable} from '../utils/Stakeable.sol';
1313
/// Special thanks to the Biconomy team for https://github.com/bcnmy/nexus/ on which this factory implementation is highly based on.
1414
/// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady
1515
contract EOAOnboardingFactory is Stakeable {
16-
/// @notice Stores the implementation contract address used to create new Nexus instances.
16+
/// @notice Stores the implementation contract address used to create new account instances.
1717
/// @dev This address is set once upon deployment and cannot be changed afterwards.
1818
address public immutable ACCOUNT_IMPLEMENTATION;
1919

@@ -32,7 +32,7 @@ contract EOAOnboardingFactory is Stakeable {
3232
error ZeroAddressNotAllowed();
3333

3434
/// @notice Constructor to set the immutable variables.
35-
/// @param implementation The address of the Nexus implementation to be used for all deployments.
35+
/// @param implementation The address of the smart account implementation to be used for all deployments.
3636
/// @param factoryOwner The address of the factory owner.
3737
/// @param ecdsaValidator The address of the K1 Validator module to be used for all deployments.
3838
/// @param bootstrapper The address of the Bootstrapper module to be used for all deployments.
@@ -62,9 +62,10 @@ contract EOAOnboardingFactory is Stakeable {
6262
// Compute the salt for deterministic deployment
6363
bytes32 salt = keccak256(abi.encodePacked(eoaOwner, index));
6464

65-
// Create the validator configuration using the NexusBootstrap library
66-
BootstrapConfig memory validator = BootstrapLib.createSingleConfig(ECDSA_VALIDATOR, abi.encodePacked(eoaOwner));
67-
bytes memory initData = BOOTSTRAPPER.getInitWithSingleValidatorCalldata(validator);
65+
bytes memory initData = abi.encode(
66+
address(BOOTSTRAPPER),
67+
abi.encodeCall(BOOTSTRAPPER.initWithSingleValidator, (ECDSA_VALIDATOR, abi.encodePacked(eoaOwner)))
68+
);
6869

6970
// Deploy the Smart account using the ProxyLib
7071
(bool alreadyDeployed, address payable account) = ProxyLib.deployProxy(ACCOUNT_IMPLEMENTATION, salt, initData);
@@ -85,11 +86,10 @@ contract EOAOnboardingFactory is Stakeable {
8586
// Compute the salt for deterministic deployment
8687
bytes32 salt = keccak256(abi.encodePacked(eoaOwner, index));
8788

88-
// Create the validator configuration using the NexusBootstrap library
89-
BootstrapConfig memory validator = BootstrapLib.createSingleConfig(ECDSA_VALIDATOR, abi.encodePacked(eoaOwner));
90-
91-
// Get the initialization data for the Nexus account
92-
bytes memory initData = BOOTSTRAPPER.getInitWithSingleValidatorCalldata(validator);
89+
bytes memory initData = abi.encode(
90+
address(BOOTSTRAPPER),
91+
abi.encodeCall(BOOTSTRAPPER.initWithSingleValidator, (ECDSA_VALIDATOR, abi.encodePacked(eoaOwner)))
92+
);
9393

9494
// Compute the predicted address using the ProxyLib
9595
return ProxyLib.predictProxyAddress(ACCOUNT_IMPLEMENTATION, salt, initData);

src/lib/BootstrapLib.sol

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.28;
33

4-
import {BootstrapConfig} from '../utils/Bootstrap.sol';
4+
import {BootstrapConfig, BootstrapPreValidationHookConfig} from '../utils/Bootstrap.sol';
55

66
/// @title Bootstrap Configuration Library
77
/// @notice Provides utility functions to create and manage BootstrapConfig structures.
@@ -28,6 +28,22 @@ library BootstrapLib {
2828
config[0].data = data;
2929
}
3030

31+
/// @notice Creates an array with a single BootstrapPreValidationHookConfig structure.
32+
/// @param hookType The type of the pre-validation hook.
33+
/// @param module The address of the module.
34+
/// @param data The initialization data for the module.
35+
/// @return config An array containing a single BootstrapPreValidationHookConfig structure.
36+
function createArrayPreValidationHookConfig(
37+
uint256 hookType,
38+
address module,
39+
bytes memory data
40+
) internal pure returns (BootstrapPreValidationHookConfig[] memory config) {
41+
config = new BootstrapPreValidationHookConfig[](1);
42+
config[0].hookType = hookType;
43+
config[0].module = module;
44+
config[0].data = data;
45+
}
46+
3147
/// @notice Creates an array of BootstrapConfig structures.
3248
/// @param modules An array of module addresses.
3349
/// @param datas An array of initialization data for each module.

0 commit comments

Comments
 (0)