Repository navigation
Conversation
Contracts that route signature checks through ERC-1271 whenever the signer address has code - Permit2, OpenZeppelin's SignatureChecker, Seaport - hand a delegated account the plain ECDSA signature the EOA's wallet produced, since the wallet has no way to know the address is delegated. SemiModularAccount7702 read the leading bytes of that signature as a validation locator, resolved an uninstalled validation function, and reverted while parsing rather than returning the ERC-1271 failure value. Uniswap surfaced this as an unrelated gas estimation failure. Accept bare 64- and 65-byte ECDSA signatures over the unwrapped digest, verified against address(this). This matches Uniswap's Calibur, ZeroDev's Kernel7702, MetaMask's stateless delegator, and eth-infinitism's Simple7702Account. No replay-safe wrapping is needed on this path: the recovered address is compared against address(this), which binds the signature to this account just as the domain separator would. The path is gated on the EOA still being the account's active fallback signer, so the accepted signers are a subset of those fallback validation already accepts. Only SemiModularAccount7702 is affected - in the other variants the fallback signer is an independent key that may sign for several accounts, so replay-safe hashing remains required there. Scoped to the external isValidSignature entry point. User operation validation, and with it deferred action authorization, is untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Route 64- and 65-byte signatures through the standard validation pipeline whenever raw mode is inactive, including when fallback validation hooks are installed. Document the opt-out and replay implications and add regression coverage. Co-Authored-By: Codex <noreply@openai.com>
Use the latest stable release compatible with the release branch's formatter, and format the SMA7702 additions with that toolchain. Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Codex <noreply@openai.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Some applications dispatch signature verification exclusively on whether the signer has code. Permit2 is the
concrete affected integration: after an EOA delegates with EIP-7702, Permit2 routes the EOA's otherwise ordinary
signature through ERC-1271. SMA7702 previously decoded that bare signature as a modular validation locator and
could revert, surfacing to callers as an unrelated gas-estimation failure.
This is narrower than general ERC-1271 compatibility. A verifier does not need this path for formats it recovers
as ECDSA before trying ERC-1271. In the OpenZeppelin version vendored here,
SignatureChecker.isValidSignatureNowrecovers 65-byte signatures first, while its bytes-based recovery does notaccept 64-byte ERC-2098 signatures and therefore routes that compact form to ERC-1271.
Contract changes
SemiModularAccount7702.isValidSignatureaccepts canonical 65-byte ECDSA and 64-byte ERC-2098 signaturesdirectly over the caller-supplied digest.
ModularAccountBase.isValidSignaturebecomespublic virtual, allowing SMA7702 to delegate all non-raw inputsto the unchanged standard modular decoder.
SemiModularAccountBase._checkSignaturerejectsCONTRACT_OWNERwhen the resolved owner isaddress(this).The delegated key must use the
EOAtype. This closes a pre-existing self-ERC-1271 recursion that could promotea signature-only validation to the native fallback's global UserOperation or deferred-action authority.
CONTRACT_OWNERremains supported for a distinct contract fallback signer.alchemy.sma-7702,alchemy.sma-bytecode, andalchemy.sma-storage.Raw mode is active by default and remains active only while all three gates hold:
address(this); andFALLBACK_VALIDATIONhas no pre-validation hooks.Configuration and integration behavior
There is deliberately no independent raw-signature flag. The EOA key already has root authority through direct
transactions and can change or clear its EIP-7702 delegation. Raw mode can instead be deactivated by:
FALLBACK_VALIDATION;address(this); orThe first option retains fallback signing but is not free: it restores standard modular decoding for bare
signatures, runs the hook across fallback runtime/UserOperation/ERC-1271 validation, and makes the fallback
ineligible for deferred actions. A no-op opt-out hook must implement all three validation-hook entry points and
accept empty hook data. Removing all native fallback validation hooks can reactivate raw mode if the other two
gates still hold.
Execution hooks, hooks on other validation keys, and the native fallback's
isGlobal,isSignatureValidation,and
isUserOpValidationflags do not control raw mode. Those flags are inert for native fallback validationgenerally because the SMA fallback paths short-circuit the ordinary flag checks and treat it as global.
The bare-length dispatcher exists only in public ERC-1271 validation. Runtime validation, UserOperation
validation, and deferred-action validation retain their existing framing, selector checks, hooks, signer-state
checks, and path-specific digests. Direct top-level transactions from the delegated EOA still bypass validation
and validation-associated hooks; selector-associated execution hooks still run.
The README also records two existing configuration hazards rather than expanding this patch:
executeWithRuntimeValidationis root-equivalent; access toexecuteBatchalone is not;SingleSignerValidationModulewithsigner == accountand using that module'sCONTRACT_OWNERencoding creates a separate circular ERC-1271 authority composition. It does not traverse the native fallback
branch and is not closed by the self-owner guard.
No module production code is changed.
Security implications
compatibility encoding, not a new key or authority boundary.
action, and revocation binding come from the calling application, matching an undelegated EOA for digest
binding. Recovery is deliberately stricter than raw
ecrecover: 65-byte signatures require low-sandvof 27 or 28; 64-byte signatures use ERC-2098.is active, the caller supplies the same digest, and the application's nonce/deadline/revocation state still
allows it. Re-delegating an existing v1.0.0 EOA to v1.1.0 therefore requires migration review of still-live
approvals for codesize-only verifiers.
collides with module payloads of 58/59 bytes under an entity locator and 42/43 bytes under a direct-call locator.
The module is not called; the collision normally returns ERC-1271 failure.
doc/SMA7702-Raw-ERC1271-Signatures.mdpublishes the complete length formula and current-state detectionprocedure.
Scope
The raw ERC-1271 dispatcher is SMA7702-only. The self-
CONTRACT_OWNERguard lives inSemiModularAccountBase, so deployed bytecode changes for all three SMA implementations:SemiModularAccount7702SemiModularAccountBytecodeSemiModularAccountStorageOnlyModularAccountand production modules are unchanged. There is no module change and no forward port in this PR.All three SMA implementations belong in the external audit and release/deployment scope. This work is outside the
December 2024 audit scope.
Verification
All checks below use the CI-pinned Foundry
v1.3.6:forge fmt --checkpnpm lintoptimized-build,optimized-build-sma-storage, andoptimized-build-standalonegit diff --checkOptimized deployed sizes:
SemiModularAccount7702SemiModularAccountBytecodeSemiModularAccountStorageOnly(optimized-build-sma-storage)Coverage includes Permit2 and both OpenZeppelin helper paths, 64/65 canonical behavior, high-
sand invalid-vrejection, all raw-mode gate combinations and lifecycle transitions, hook types and fallback flags, entity and
direct-call locator collisions, exact 63/64/65/66/67 boundaries, direct bare UserOperation rejection,
self-
CONTRACT_OWNERrejection in UserOperation and deferred-action paths, exploit regression, and preservationof distinct contract fallback signers.
Release requirements
and dependent factory deployments before deployment. The existing deployment ledger is intentionally not
changed until that release work is complete.
v1.3.6because v1.4.0 introduced formatter and build-time lint changes not applied to thev2.0.x release line. Existing Solhint jobs remain enforced.