Skip to content

Commit a1510cd

Browse files
authored
feat: Add SMA Replay-Safe Hash (#155)
1 parent f475848 commit a1510cd

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

src/account/SemiModularAccount.sol

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ contract SemiModularAccount is UpgradeableModularAccount {
2626
uint256 internal constant _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT =
2727
0x5b9dc9aa943f8fa2653ceceda5e3798f0686455280432166ba472eca0bc17a32;
2828

29+
// keccak256("EIP712Domain(uint256 chainId,address verifyingContract)")
30+
bytes32 private constant _DOMAIN_SEPARATOR_TYPEHASH =
31+
0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
32+
33+
// keccak256("ReplaySafeHash(bytes32 hash)")
34+
bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH =
35+
0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff;
36+
2937
ModuleEntity internal constant _FALLBACK_VALIDATION = ModuleEntity.wrap(bytes24(type(uint192).max));
3038

3139
uint256 internal constant _SIG_VALIDATION_PASSED = 0;
@@ -88,6 +96,11 @@ contract SemiModularAccount is UpgradeableModularAccount {
8896
return "erc6900/reference-semi-modular-account/0.8.0";
8997
}
9098

99+
function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
100+
return
101+
MessageHashUtils.toTypedDataHash({domainSeparator: _domainSeparator(), structHash: _hashStruct(hash)});
102+
}
103+
91104
function _execUserOpValidation(
92105
ModuleEntity userOpValidationFunction,
93106
PackedUserOperation memory userOp,
@@ -120,9 +133,9 @@ contract SemiModularAccount is UpgradeableModularAccount {
120133
if (msg.sender != fallbackSigner) {
121134
revert FallbackSignerMismatch();
122135
}
123-
return;
136+
} else {
137+
super._execRuntimeValidation(runtimeValidationFunction, callData, authorization);
124138
}
125-
super._execRuntimeValidation(runtimeValidationFunction, callData, authorization);
126139
}
127140

128141
function _exec1271Validation(ModuleEntity sigValidation, bytes32 hash, bytes calldata signature)
@@ -134,7 +147,7 @@ contract SemiModularAccount is UpgradeableModularAccount {
134147
if (sigValidation.eq(_FALLBACK_VALIDATION)) {
135148
address fallbackSigner = _getFallbackSigner();
136149

137-
if (SignatureChecker.isValidSignatureNow(fallbackSigner, hash, signature)) {
150+
if (SignatureChecker.isValidSignatureNow(fallbackSigner, replaySafeHash(hash), signature)) {
138151
return _1271_MAGIC_VALUE;
139152
}
140153
return _1271_INVALID;
@@ -176,11 +189,25 @@ contract SemiModularAccount is UpgradeableModularAccount {
176189
return address(uint160(bytes20(appendedData)));
177190
}
178191

192+
function _domainSeparator() internal view returns (bytes32) {
193+
return keccak256(abi.encode(_DOMAIN_SEPARATOR_TYPEHASH, block.chainid, address(this)));
194+
}
195+
179196
function _getSemiModularAccountStorage() internal pure returns (SemiModularAccountStorage storage) {
180197
SemiModularAccountStorage storage _storage;
181198
assembly ("memory-safe") {
182199
_storage.slot := _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT
183200
}
184201
return _storage;
185202
}
203+
204+
function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
205+
bytes32 res;
206+
assembly ("memory-safe") {
207+
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
208+
mstore(0x20, hash)
209+
res := keccak256(0, 0x40)
210+
}
211+
return res;
212+
}
186213
}

src/modules/ReplaySafeWrapper.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ abstract contract ReplaySafeWrapper is ModuleEIP712 {
2626
});
2727
}
2828

29-
function _hashStruct(bytes32 hash) internal view virtual returns (bytes32) {
30-
return keccak256(abi.encode(_REPLAY_SAFE_HASH_TYPEHASH, hash));
29+
function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
30+
bytes32 res;
31+
assembly ("memory-safe") {
32+
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
33+
mstore(0x20, hash)
34+
res := keccak256(0x00, 0x40)
35+
}
36+
return res;
3137
}
3238
}

test/account/UpgradeableModularAccount.t.sol

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -402,17 +402,11 @@ contract UpgradeableModularAccountTest is AccountTestBase {
402402
function test_isValidSignature() public {
403403
bytes32 message = keccak256("hello world");
404404

405-
uint8 v;
406-
bytes32 r;
407-
bytes32 s;
405+
bytes32 replaySafeHash = vm.envOr("SMA_TEST", false)
406+
? SemiModularAccount(payable(account1)).replaySafeHash(message)
407+
: singleSignerValidationModule.replaySafeHash(address(account1), message);
408408

409-
if (vm.envOr("SMA_TEST", false)) {
410-
// todo: implement replay-safe hashing for SMA
411-
(v, r, s) = vm.sign(owner1Key, message);
412-
} else {
413-
bytes32 replaySafeHash = singleSignerValidationModule.replaySafeHash(address(account1), message);
414-
(v, r, s) = vm.sign(owner1Key, replaySafeHash);
415-
}
409+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(owner1Key, replaySafeHash);
416410

417411
bytes memory signature = _encode1271Signature(_signerValidation, abi.encodePacked(r, s, v));
418412

0 commit comments

Comments
 (0)