Skip to content

Commit ce67b50

Browse files
committed
feat: add flag for user op validation and pack efficiently
1 parent 2725fbc commit ce67b50

24 files changed

+238
-89
lines changed

src/account/AccountFactory.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ contract AccountFactory is Ownable {
5959
new ERC1967Proxy{salt: combinedSalt}(address(ACCOUNT_IMPL), "");
6060
// point proxy to actual implementation and init plugins
6161
ReferenceModularAccount(payable(addr)).initializeWithValidation(
62-
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true),
62+
ValidationConfigLib.pack(SINGLE_SIGNER_VALIDATION_MODULE, entityId, true, true, true),
6363
new bytes4[](0),
6464
pluginInstallData,
6565
new bytes[](0)

src/account/AccountStorage.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ struct ExecutionData {
2727
struct ValidationData {
2828
// Whether or not this validation can be used as a global validation function.
2929
bool isGlobal;
30-
// Whether or not this validation is a signature validator.
30+
// Whether or not this validation is allowed to validate ERC-1271 signatures.
3131
bool isSignatureValidation;
32+
// Whether or not this validation is allowed to validate ERC-4337 user operations.
33+
bool isUserOpValidation;
3234
// The pre validation hooks for this validation function.
3335
ModuleEntity[] preValidationHooks;
3436
// Permission hooks for this validation function.

src/account/ModularAccountView.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ abstract contract ModularAccountView is IModularAccountView {
4949
ValidationData storage validationData = getAccountStorage().validationData[validationFunction];
5050
data.isGlobal = validationData.isGlobal;
5151
data.isSignatureValidation = validationData.isSignatureValidation;
52+
data.isUserOpValidation = validationData.isUserOpValidation;
5253
data.preValidationHooks = validationData.preValidationHooks;
5354

5455
uint256 permissionHooksLen = validationData.permissionHooks.length();

src/account/ModuleManagerInternals.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ abstract contract ModuleManagerInternals is IModularAccount {
261261

262262
_validationData.isGlobal = validationConfig.isGlobal();
263263
_validationData.isSignatureValidation = validationConfig.isSignatureValidation();
264+
_validationData.isUserOpValidation = validationConfig.isUserOpValidation();
264265

265266
_onInstall(validationConfig.module(), installData, type(IValidationModule).interfaceId);
266267
emit ValidationInstalled(validationConfig.module(), validationConfig.entityId());

src/account/ReferenceModularAccount.sol

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ contract ReferenceModularAccount is
7676
error RuntimeValidationFunctionReverted(address module, uint32 entityId, bytes revertReason);
7777
error SelfCallRecursionDepthExceeded();
7878
error SignatureValidationInvalid(address module, uint32 entityId);
79+
error UserOpValidationInvalid(address module, uint32 entityId);
7980
error UnexpectedAggregator(address module, uint32 entityId, address aggregator);
8081
error UnrecognizedFunction(bytes4 selector);
8182
error ValidationFunctionMissing(bytes4 selector);
@@ -593,8 +594,14 @@ contract ReferenceModularAccount is
593594
PackedUserOperation memory userOp,
594595
bytes32 userOpHash
595596
) internal virtual returns (uint256) {
597+
AccountStorage storage _storage = getAccountStorage();
598+
596599
(address module, uint32 entityId) = userOpValidationFunction.unpack();
597600

601+
if (!_storage.validationData[userOpValidationFunction].isUserOpValidation) {
602+
revert UserOpValidationInvalid(module, entityId);
603+
}
604+
598605
return IValidationModule(module).validateUserOp(entityId, userOp, userOpHash);
599606
}
600607

src/helpers/ValidationConfigLib.sol

Lines changed: 63 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,67 +7,78 @@ import {ModuleEntity, ValidationConfig} from "../interfaces/IModularAccount.sol"
77
// Layout:
88
// 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA________________________ // Address
99
// 0x________________________________________BBBBBBBB________________ // Entity ID
10-
// 0x________________________________________________CC______________ // isGlobal
11-
// 0x__________________________________________________DD____________ // isSignatureValidation
12-
// 0x____________________________________________________000000000000 // unused
10+
// 0x________________________________________________CC______________ // validation flags
11+
// 0x__________________________________________________00000000000000 // unused
12+
13+
// Validation flags layout:
14+
// 0b00000___ // unused
15+
// 0b_____A__ // isGlobal
16+
// 0b______B_ // isSignatureValidation
17+
// 0b_______C // isUserOpValidation
1318

1419
library ValidationConfigLib {
15-
function pack(ModuleEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation)
16-
internal
17-
pure
18-
returns (ValidationConfig)
19-
{
20+
// is user op validation flag stored in last bit of the 25th byte
21+
bytes32 internal constant _VALIDATION_FLAG_IS_USER_OP = bytes32(uint256(1) << 56);
22+
// is signature validation flag stored in second to last bit of the 25th byte
23+
bytes32 internal constant _VALIDATION_FLAG_IS_SIGNATURE = bytes32(uint256(1) << 57);
24+
// is global flag stored in the third to last bit of the 25th byte
25+
bytes32 internal constant _VALIDATION_FLAG_IS_GLOBAL = bytes32(uint256(1) << 58);
26+
27+
function pack(
28+
ModuleEntity _validationFunction,
29+
bool _isGlobal,
30+
bool _isSignatureValidation,
31+
bool _isUserOpValidation
32+
) internal pure returns (ValidationConfig) {
2033
return ValidationConfig.wrap(
21-
bytes26(
22-
bytes26(ModuleEntity.unwrap(_validationFunction))
23-
// isGlobal flag stored in the 25th byte
24-
| bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0))
25-
// isSignatureValidation flag stored in the 26th byte
26-
| bytes26(bytes32(_isSignatureValidation ? uint256(1) << 48 : 0))
34+
bytes25(
35+
bytes25(ModuleEntity.unwrap(_validationFunction))
36+
| bytes25(bytes32(_isGlobal ? _VALIDATION_FLAG_IS_GLOBAL : bytes32(0)))
37+
| bytes25(bytes32(_isSignatureValidation ? _VALIDATION_FLAG_IS_SIGNATURE : bytes32(0)))
38+
| bytes25(bytes32(_isUserOpValidation ? _VALIDATION_FLAG_IS_USER_OP : bytes32(0)))
2739
)
2840
);
2941
}
3042

31-
function pack(address _module, uint32 _entityId, bool _isGlobal, bool _isSignatureValidation)
32-
internal
33-
pure
34-
returns (ValidationConfig)
35-
{
43+
function pack(
44+
address _module,
45+
uint32 _entityId,
46+
bool _isGlobal,
47+
bool _isSignatureValidation,
48+
bool _isUserOpValidation
49+
) internal pure returns (ValidationConfig) {
3650
return ValidationConfig.wrap(
37-
bytes26(
51+
bytes25(
3852
// module address stored in the first 20 bytes
39-
bytes26(bytes20(_module))
53+
bytes25(bytes20(_module))
4054
// entityId stored in the 21st - 24th byte
41-
| bytes26(bytes24(uint192(_entityId)))
42-
// isGlobal flag stored in the 25th byte
43-
| bytes26(bytes32(_isGlobal ? uint256(1) << 56 : 0))
44-
// isSignatureValidation flag stored in the 26th byte
45-
| bytes26(bytes32(_isSignatureValidation ? uint256(1) << 48 : 0))
55+
| bytes25(bytes24(uint192(_entityId)))
56+
| bytes25(bytes32(_isGlobal ? _VALIDATION_FLAG_IS_GLOBAL : bytes32(0)))
57+
| bytes25(bytes32(_isSignatureValidation ? _VALIDATION_FLAG_IS_SIGNATURE : bytes32(0)))
58+
| bytes25(bytes32(_isUserOpValidation ? _VALIDATION_FLAG_IS_USER_OP : bytes32(0)))
4659
)
4760
);
4861
}
4962

5063
function unpackUnderlying(ValidationConfig config)
5164
internal
5265
pure
53-
returns (address _module, uint32 _entityId, bool _isGlobal, bool _isSignatureValidation)
66+
returns (address _module, uint32 _entityId, uint8 flags)
5467
{
55-
bytes26 configBytes = ValidationConfig.unwrap(config);
68+
bytes25 configBytes = ValidationConfig.unwrap(config);
5669
_module = address(bytes20(configBytes));
5770
_entityId = uint32(bytes4(configBytes << 160));
58-
_isGlobal = uint8(configBytes[24]) == 1;
59-
_isSignatureValidation = uint8(configBytes[25]) == 1;
71+
flags = uint8(configBytes[24]);
6072
}
6173

6274
function unpack(ValidationConfig config)
6375
internal
6476
pure
65-
returns (ModuleEntity _validationFunction, bool _isGlobal, bool _isSignatureValidation)
77+
returns (ModuleEntity _validationFunction, uint8 flags)
6678
{
67-
bytes26 configBytes = ValidationConfig.unwrap(config);
79+
bytes25 configBytes = ValidationConfig.unwrap(config);
6880
_validationFunction = ModuleEntity.wrap(bytes24(configBytes));
69-
_isGlobal = uint8(configBytes[24]) == 1;
70-
_isSignatureValidation = uint8(configBytes[25]) == 1;
81+
flags = uint8(configBytes[24]);
7182
}
7283

7384
function module(ValidationConfig config) internal pure returns (address) {
@@ -83,10 +94,26 @@ library ValidationConfigLib {
8394
}
8495

8596
function isGlobal(ValidationConfig config) internal pure returns (bool) {
86-
return uint8(ValidationConfig.unwrap(config)[24]) == 1;
97+
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_GLOBAL != 0;
98+
}
99+
100+
function isGlobal(uint8 flags) internal pure returns (bool) {
101+
return flags & 0x04 != 0;
87102
}
88103

89104
function isSignatureValidation(ValidationConfig config) internal pure returns (bool) {
90-
return uint8(ValidationConfig.unwrap(config)[25]) == 1;
105+
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_SIGNATURE != 0;
106+
}
107+
108+
function isSignatureValidation(uint8 flags) internal pure returns (bool) {
109+
return flags & 0x02 != 0;
110+
}
111+
112+
function isUserOpValidation(ValidationConfig config) internal pure returns (bool) {
113+
return ValidationConfig.unwrap(config) & _VALIDATION_FLAG_IS_USER_OP != 0;
114+
}
115+
116+
function isUserOpValidation(uint8 flags) internal pure returns (bool) {
117+
return flags & 0x01 != 0;
91118
}
92119
}

src/interfaces/IModularAccount.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {ExecutionManifest} from "./IExecutionModule.sol";
55

66
type ModuleEntity is bytes24;
77

8-
type ValidationConfig is bytes26;
8+
type ValidationConfig is bytes25;
99

1010
type HookConfig is bytes26;
1111

src/interfaces/IModularAccountView.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ struct ValidationDataView {
2424
bool isGlobal;
2525
// Whether or not this validation is a signature validator.
2626
bool isSignatureValidation;
27+
// Whether or not this validation is a user operation validator.
28+
bool isUserOpValidation;
2729
// The pre validation hooks for this validation function.
2830
ModuleEntity[] preValidationHooks;
2931
// Permission hooks for this validation function.

test/account/AccountReturnData.t.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ contract AccountReturnDataTest is AccountTestBase {
4343
bytes4[] memory selectors = new bytes4[](1);
4444
selectors[0] = IModularAccount.execute.selector;
4545
account1.installValidation(
46-
ValidationConfigLib.pack(address(resultConsumerModule), DIRECT_CALL_VALIDATION_ENTITYID, false, false),
46+
ValidationConfigLib.pack(
47+
address(resultConsumerModule), DIRECT_CALL_VALIDATION_ENTITYID, false, false, true
48+
), // todo: does this need UO validation permission?
4749
selectors,
4850
"",
4951
new bytes[](0)

test/account/DirectCallsFromModule.t.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ contract DirectCallsFromModuleTest is AccountTestBase {
128128
vm.prank(address(entryPoint));
129129

130130
account1.installValidation(
131-
ValidationConfigLib.pack(extraOwner, DIRECT_CALL_VALIDATION_ENTITYID, false, false),
131+
ValidationConfigLib.pack(extraOwner, DIRECT_CALL_VALIDATION_ENTITYID, false, false, false),
132132
selectors,
133133
"",
134134
new bytes[](0)
@@ -154,7 +154,7 @@ contract DirectCallsFromModuleTest is AccountTestBase {
154154

155155
vm.prank(address(entryPoint));
156156

157-
ValidationConfig validationConfig = ValidationConfigLib.pack(_moduleEntity, false, false);
157+
ValidationConfig validationConfig = ValidationConfigLib.pack(_moduleEntity, false, false, false);
158158

159159
account1.installValidation(validationConfig, selectors, "", hooks);
160160
}
@@ -168,7 +168,7 @@ contract DirectCallsFromModuleTest is AccountTestBase {
168168

169169
vm.prank(address(entryPoint));
170170

171-
ValidationConfig validationConfig = ValidationConfigLib.pack(_moduleEntity, true, false);
171+
ValidationConfig validationConfig = ValidationConfigLib.pack(_moduleEntity, true, false, false);
172172

173173
account1.installValidation(validationConfig, new bytes4[](0), "", hooks);
174174
}

0 commit comments

Comments
 (0)