Skip to content

Commit

Permalink
feat: add erc7739 compatibility
Browse files Browse the repository at this point in the history
chore: improve comments and dedupe constants

Co-authored-by: Adam Egyed <[email protected]>

Update src/helpers/ERC7739ReplaySafeWrapper.sol

Update src/helpers/ERC7739ReplaySafeWrapper.sol

Co-authored-by: Zer0dot <[email protected]>

Update src/helpers/ERC7739ReplaySafeWrapper.sol

Co-authored-by: Zer0dot <[email protected]>

Co-authored-by: Adam Egyed <[email protected]>

chore: gas bad

chore: fix test name

Co-authored-by: Adam Egyed <[email protected]>
  • Loading branch information
howydev and adamegyed committed Oct 16, 2024
1 parent b74a497 commit 7e14d10
Show file tree
Hide file tree
Showing 49 changed files with 884 additions and 162 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
176044
176022
2 changes: 1 addition & 1 deletion .forge-snapshots/ModularAccount_Runtime_Erc20Transfer.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
79423
79401
Original file line number Diff line number Diff line change
@@ -1 +1 @@
424078
424034
Original file line number Diff line number Diff line change
@@ -1 +1 @@
55545
55523
Original file line number Diff line number Diff line change
@@ -1 +1 @@
80392
80370
Original file line number Diff line number Diff line change
@@ -1 +1 @@
113969
113947
Original file line number Diff line number Diff line change
@@ -1 +1 @@
532471
532449
Original file line number Diff line number Diff line change
@@ -1 +1 @@
258153
262095
Original file line number Diff line number Diff line change
@@ -1 +1 @@
75869
75847
Original file line number Diff line number Diff line change
@@ -1 +1 @@
423008
422964
Original file line number Diff line number Diff line change
@@ -1 +1 @@
51991
51969
Original file line number Diff line number Diff line change
@@ -1 +1 @@
80830
80786
Original file line number Diff line number Diff line change
@@ -1 +1 @@
114407
114363
Original file line number Diff line number Diff line change
@@ -1 +1 @@
530046
530024
Original file line number Diff line number Diff line change
@@ -1 +1 @@
254593
258633
24 changes: 16 additions & 8 deletions gas/modular-account/ModularAccount.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,26 @@ contract ModularAccountGasTest is ModularAccountBenchmarkBase("ModularAccount")
uint256 deferredInstallNonce = 0;
uint48 deferredInstallDeadline = 0;

bytes memory deferredValidationSig = _packFinalSignature(
(bytes32 structHash, bytes32 digest, bytes32 domainSeparator) = _getDeferredInstallStructAndHash(
account1, deferredInstallNonce, deferredInstallDeadline, deferredValidationInstallCall
);

bytes memory deferredValidationSig = _packFinal1271Signature(
_signRawHash(
vm,
owner1Key,
_getECDSAReplaySafeHash(
account1,
ecdsaValidationModule,
_getDeferredInstallHash(
account1, deferredInstallNonce, deferredInstallDeadline, deferredValidationInstallCall
)
_getModuleReplaySafeHash(
address(account1),
address(ecdsaValidationModule),
domainSeparator,
structHash,
digest,
_DEFERRED_INSTALL_CONTENTS_TYPE
)
)
),
domainSeparator,
structHash,
_DEFERRED_INSTALL_CONTENTS_TYPE
);

userOp.signature = _encodeDeferredInstallUOSignature(
Expand Down
16 changes: 10 additions & 6 deletions gas/modular-account/SemiModularAccount.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -193,17 +193,21 @@ contract ModularAccountGasTest is ModularAccountBenchmarkBase("SemiModularAccoun
uint256 deferredInstallNonce = 0;
uint48 deferredInstallDeadline = 0;

bytes memory deferredValidationSig = _packFinalSignature(
(bytes32 structHash, bytes32 digest, bytes32 domainSeparator) = _getDeferredInstallStructAndHash(
account1, deferredInstallNonce, deferredInstallDeadline, deferredValidationInstallCall
);

bytes memory deferredValidationSig = _packFinal1271Signature(
_signRawHash(
vm,
owner1Key,
_getSMAReplaySafeHash(
account1,
_getDeferredInstallHash(
account1, deferredInstallNonce, deferredInstallDeadline, deferredValidationInstallCall
)
address(account1), domainSeparator, structHash, digest, _DEFERRED_INSTALL_CONTENTS_TYPE
)
)
),
domainSeparator,
structHash,
_DEFERRED_INSTALL_CONTENTS_TYPE
);

userOp.signature = _encodeDeferredInstallUOSignature(
Expand Down
40 changes: 6 additions & 34 deletions src/account/SemiModularAccountBase.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {IModularAccount, ModuleEntity} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {IModularAccount, ModuleEntity} from "@erc6900/reference-implementation/interfaces/IModularAccount.sol";
import {ModuleEntityLib} from "@erc6900/reference-implementation/libraries/ModuleEntityLib.sol";
import {IEntryPoint} from "@eth-infinitism/account-abstraction/interfaces/IEntryPoint.sol";
Expand All @@ -11,13 +10,16 @@ import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/Signa

import {DIRECT_CALL_VALIDATION_ENTITYID, FALLBACK_VALIDATION} from "../helpers/Constants.sol";
import {SignatureType} from "../helpers/SignatureType.sol";

import {ERC7739ReplaySafeWrapper} from "../libraries/ERC7739ReplaySafeWrapper.sol";
import {RTCallBuffer, UOCallBuffer} from "../libraries/ExecutionLib.sol";
import {SemiModularKnownSelectorsLib} from "../libraries/SemiModularKnownSelectorsLib.sol";
import {ModularAccountBase} from "./ModularAccountBase.sol";

abstract contract SemiModularAccountBase is ModularAccountBase {
using MessageHashUtils for bytes32;
using ModuleEntityLib for ModuleEntity;
using ERC7739ReplaySafeWrapper for address;

struct SemiModularAccountStorage {
address fallbackSigner;
Expand All @@ -28,10 +30,6 @@ abstract contract SemiModularAccountBase is ModularAccountBase {
uint256 internal constant _SEMI_MODULAR_ACCOUNT_STORAGE_SLOT =
0x5b9dc9aa943f8fa2653ceceda5e3798f0686455280432166ba472eca0bc17a32;

// keccak256("ReplaySafeHash(bytes32 hash)")
bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH =
0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff;

uint256 internal constant _SIG_VALIDATION_PASSED = 0;
uint256 internal constant _SIG_VALIDATION_FAILED = 1;

Expand Down Expand Up @@ -82,24 +80,6 @@ abstract contract SemiModularAccountBase is ModularAccountBase {
return "alchemy.semi-modular-account.0.0.1";
}

/// @notice Returns the replay-safe hash generated from the passed typed data hash for 1271 validation.
/// @param hash The typed data hash to wrap in a replay-safe hash.
/// @return The replay-safe hash, to be used for 1271 signature generation.
///
/// @dev Generates a replay-safe hash to wrap a standard typed data hash. This prevents replay attacks by
/// enforcing the domain separator, which includes this contract's address and the chainId. This is only
/// relevant for 1271 validation because UserOp validation relies on the UO hash and the Entrypoint has
/// safeguards.
///
/// NOTE: Like in signature-based validation modules, the returned hash should be used to generate signatures,
/// but the original hash should be passed to the external-facing function for 1271 validation.
function replaySafeHash(bytes32 hash) public view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash({
domainSeparator: _domainSeparator(),
structHash: _hashStructReplaySafeHash(hash)
});
}

function _execUserOpValidation(
ModuleEntity userOpValidationFunction,
bytes32 userOpHash,
Expand Down Expand Up @@ -143,7 +123,9 @@ abstract contract SemiModularAccountBase is ModularAccountBase {
if (sigValidation.eq(FALLBACK_VALIDATION)) {
address fallbackSigner = _getFallbackSigner();

if (_checkSignature(fallbackSigner, replaySafeHash(hash), signature)) {
(bytes32 digest, bytes calldata innerSignature) =
address(this).validateERC7739SigFormatForAccount(hash, signature);
if (_checkSignature(fallbackSigner, digest, innerSignature)) {
return _1271_MAGIC_VALUE;
}
return _1271_INVALID;
Expand Down Expand Up @@ -232,16 +214,6 @@ abstract contract SemiModularAccountBase is ModularAccountBase {
return _storage;
}

function _hashStructReplaySafeHash(bytes32 hash) internal pure virtual returns (bytes32) {
bytes32 res;
assembly ("memory-safe") {
mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
mstore(0x20, hash)
res := keccak256(0, 0x40)
}
return res;
}

// Overrides ModuleManagerInternals
function _isNativeFunction(bytes4 selector) internal pure override returns (bool) {
return SemiModularKnownSelectorsLib.isNativeFunction(selector);
Expand Down
Loading

0 comments on commit 7e14d10

Please sign in to comment.