OpenZeppelin / OpenZeppelin/openzeppelin-contracts

Revisit `SignatureChecker` ECDSA/ERC-1271 ordering once EIP-8151 + EIP-8298 ship

Open
#6,773 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: cryptography contracts needs milestone on hold
Dominant language
Solidity
Stars
27.2k
Forks
12.4k
Avg merge
2d 19h
Merged PRs (30d)
33

Description

Context

#4951 changed the order of operations in SignatureChecker.isValidSignatureNow(address,bytes32,bytes), from

(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
    (error == ECDSA.RecoverError.NoError && recovered == signer) ||
    isValidERC1271SignatureNow(signer, hash, signature);

to an explicit dispatch on whether the signer has code:

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/SignatureChecker.sol#L32-L39

The reason is that with the "ECDSA check, fallback to ERC-1271 if invalid" form, an account whose address is derived from an ECDSA key can never fully revoke that key at the application level: whatever its isValidSignature returns, a raw ECDSA signature from the original key keeps validating. Dispatching on code.length lets such an account migrate away from the key entirely, at the cost of one EXTCODESIZE and of making ERC-1271 the only accepted scheme once the account has code.

What would change

Two proposals in the Hegotá extension package, expected to ship together, close that gap at the protocol level:

  • EIP-8151 — "Account Code Restricted ecRecover" (Draft). Applies the EIP-3607 account-code restriction to the ecRecover precompile: after recovery it returns the recovered address only if that account's raw code is empty or is exactly an EIP-7702 delegation indicator, and 32 zero bytes otherwise.
  • EIP-8298 — "SETCODEFROM Code Reuse Instruction" (Draft). Gives an EOA the migration path: after SETCODEFROM, the account holds regular deployed code rather than an EIP-7702 delegation indicator, which disables ECDSA transaction authority under EIP-3607 and blocks EIP-7702 redelegation.

Together, an account that has migrated away from its ECDSA key ends up with regular code, and EIP-8151 makes ecRecover return zero for it. The ECDSA branch of the old || form can then no longer shadow ERC-1271, the code.length dispatch becomes redundant, and we can go back to:

(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return (err == ECDSA.RecoverError.NoError && recovered == signer) ||
    isValidERC1271SignatureNow(signer, hash, signature);

Applies to both isValidSignatureNow and isValidSignatureNowCalldata; the ERC-7913 overloads inherit the behavior through them.

Upside: lower burden on basic EIP-7702 delegations

Beyond dropping the EXTCODESIZE, this is a real usability gain for delegated EOAs that are still administered by their ECDSA key.

EIP-8151 keeps ecRecover working when the code is exactly an EIP-7702 delegation indicator, so such an account would validate its own ECDSA signatures again by default, with no need to implement ERC-1271 at all. Today, the moment an EOA delegates it gains code, and our dispatch routes every check to isValidSignature — so a minimal delegation that does not implement ERC-1271 stops validating signatures that the protocol still considers authoritative for that account.

That is the correct semantics: an account that has not migrated via EIP-8298 still has a live key that can authorize transactions, and there is no reason application-level signature checks should disagree. It does mean small/basic delegations no longer have to carry an ERC-1271 implementation just to keep working with SignatureChecker.

It is still an observable behavior change from what we ship today, so it should be a deliberate decision rather than a side effect of the refactor.

Caveats to resolve before doing it

  • Gas is not an obvious win. Under EIP-8151, ecRecover itself pays an account access cost (100 warm / 2600 cold) on top of the 3000 base, so dropping our explicit EXTCODESIZE saves less than it looks. Worth benchmarking rather than assuming.
  • Deployment targets. Both EIPs are Draft and core. Even once they activate on mainnet, this library is deployed on chains that will lag or never adopt them — on such a chain, reverting would silently reintroduce the unrevokable-key behavior. This likely gates the change behind broad adoption, not just mainnet activation.
  • Backward compatibility. This is an observable behavior change in a released contract, so it is a maintainer call on timing/versioning and needs a changeset.

Action

Track EIP-8151 and EIP-8298 through Hegotá. Revisit once they are live and widely deployed; no action until then.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with contracts/utils/cryptography/SignatureChecker.sol and the isValidSignatureNow and isValidSignatureNowCalldata entry points, then read the EIP-8151 and EIP-8298 status. Confirm broad deployment support and benchmark the gas impact before proposing changes. Done requires a maintainer decision on timing, updated behavior tests, and a changeset if the refactor proceeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
solidity
Domain
blockchain, cryptography, security
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.