EIP-6492: Signature validation for pre-deploy contracts

Yes, I’m familiar with 6551 (I’m an author on it), but this EIP is specifically for non-web3 identifiers linking to web3 accounts for future claim via a controlled registry. The open question right now is how signing could be supported in such a design.

AA via an NFT doesn’t really make sense here (I’ve tried an implementation around it, and it’s far too clunky).

The pattern I’m thinking of at the moment is a scheme where authentication could be delegated to the contract factory. But thinking more about it, this may be impossible to validate, as how could one ensure that a given counterfactual address is indeed deployable by an indicated factory without simulation (which is what you’ve addressed via the multicall in your spec).

Off topic, but would appreciate further thoughts around security on this thread:

I’m not sure I understand the security concern here. The deploy data is leaked in the mempool, but the account creation verification is based on a registry’s configured signing key, so it wouldn’t really matter who ends up performing the transaction, as it would still assign the wallet to the appropriate owner.

Perhaps, with respect to 6981, it might be smarter to split up the concept of account creation and account control assignment, which would make it 6492 compatible.

Love the spec.

We’re supporting this in our ROA accounts. I think this will be very interesting for onboarding people.

I was implementing this EIP in our wallet and I found that a small addition to its logic can extend its functionality quite a bit. Right now, it skips the call to the factory if it detects that the wallet is already deployed. This makes sense most of the time because someone could have deployed the wallet in the time between the signing of the message and its validation.

However, this makes the EIP useless when the wallet doesn’t need to be deployed, but it needs to be updated in one way or another. A wallet may already have code, but it may not be in a state that’s ready to validate the signature.

This could be all sort of things:

  • Contract code
  • Onchain set of signers
  • Enabling of a flag

We can extend the EIP to address this by performing a “second try”. If the validation of the signature fails (and there is factoryCalldata), we can try executing this data even if the wallet already has code. This would enable any wallet to perform any sort of “preparations”, even after deployment.

The only downside of this approach is that this new code path is less efficient. The contract needs to perform a failed validation and only then can it proceed to the “full validation”. However, the alternative is that the EIP doesn’t provide any support for this scenario at all.

This is the proposed change:

Hello there, I don’t know if anyone mentioned it before. I found a bug in the sample code.
The isValid variable should be declared before try catch scope like:

      bool isValid;
      try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate) returns (bytes4 magicValue) {
        isValid = magicValue == ERC1271_SUCCESS;

Otherwise, the isValid variable in catch scope will report DeclarationError when compile by solc.

        } catch (bytes memory err) {
        // retry, but this time assume the prefix is a prepare call
        if (!isValid && !tryPrepare && contractCodeLen > 0) {
          return isValidSigImpl(_signer, _hash, _signature, allowSideEffects, true);
        }

hello,

this is fixed now, the reason for the issue was slightly different

Hey all, a huge fan of this EIP! I’m curious what the latest timeline estimate is for deploying a canonical address for the most-used EVM chains? Has anyone been doing outreach to wallet authenticators like Dynamic or Privy to get them onboard and help distribute the standard? Happy to help on that last point if not :).

Also as a piece of feedback on how the Verifier side works. Right now, it seems ambiguous what network should be used when doing eth_call.

Pre-EIP-6492, in my own experimentation of performing SIWE signatures with smart contract accounts on Sequence, they cite that the “wallet needs to be deployed on Ethereum to sign messages”. This implies that there is some agreement among Verifiers to use Ethereum mainnet currently. In absence of a mechanism to specify which network should be used in EIP-6492, I would naturally assume the same choice would be made. I think 6492 gives us an opportunity to remove this default assumption.

If it has not been considered yet, I propose to add some mechanism of including a specific chainId in the signature schema so that Verifiers can check a specific network. Chain-specificity gives developers more control over the nuances of multi-chain account deployments.

One example improvement this brings is easier compatibility with ERC-6551, where an NFT on one network can control accounts on multiple networks. The least-effort way to verify account control for a 6551 Account is to do the 6492 process on the same network that the underlying NFT is deployed. Having a mechanism for the chainId to be provided in the signature returned to Verifiers, that they then MUST call eth_call on could solve the problem.

Looking for any comments/feedback and thank you for your time :).

Adding further clarification on my previous message, here are quotes from the current EIP:

Signer side

  • If the contract is deployed, produce a normal ERC-1271 signature
  • If the contract is not deployed yet, wrap the signature as follows: concat(abi.encode((create2Factory, factoryCalldata, originalERC1271Signature), (address, bytes, bytes)), magicBytes)
  • If the contract is deployed but not ready to verify using ERC-1271, wrap the signature as follows: concat(abi.encode((prepareTo, prepareData, originalERC1271Signature), (address, bytes, bytes)), magicBytes); prepareTo and prepareData must contain the necessary transaction that will make the contract ready to verify using ERC-1271 (e.g. a call to migrate or update)

“If the contract is deployed” → This is not a realistic framing of the boolean check a signer will perform because a contract may be deployed on some networks, but not others. If my account is deployed on Polygon, but not Ethereum, what should I do as a Signer?

The same applies for the specification for Verifiers:

Verifier side

Full signature verification MUST be performed in the following order:

  • check if the signature ends with magic bytes, in which case do an eth_call to a multicall contract that will call the factory first with the factoryCalldata and deploy the contract if it isn’t already deployed; Then, call contract.isValidSignature as usual with the unwrapped signature
  • check if there’s contract code at the address. If so perform ERC-1271 verification as usual by invoking isValidSignature
  • if the ERC-1271 verification fails, and the deploy call to the factory was skipped due to the wallet already having code, execute the factoryCalldata transaction and try isValidSignature again
  • if there is no contract code at the address, try ecrecover verification

“do an eth_call to a multicall contract…” / “check if there’s contract code at the address” → on which network? Following the same theoretical, I get different results if I choose Polygon vs. Ethereum vs. Other.


I think the EIP can remove these ambiguities that risk implementation variance that will negatively impact UX consistency by implementing something akin to the following (attempted to highlight the diff with bolding):

Signer side

  • If the contract is deployed, wrap the signature as follows: abi.encode((chainId, signature), (uint256, bytes))
  • If the contract is not deployed yet, wrap the signature as follows: concat(abi.encode((chainId, create2Factory, factoryCalldata, originalERC1271Signature), (uint256, address, bytes, bytes)), magicBytes)
  • If the contract is deployed but not ready to verify using ERC-1271, wrap the signature as follows: concat(abi.encode((chainId, prepareTo, prepareData, originalERC1271Signature), (uint256, address, bytes, bytes)), magicBytes); chainId, prepareTo and prepareData must contain the necessary transaction that will make the contract ready to verify using ERC-1271 (e.g. a call to migrate or update)

and for the Verifier side:

Verifier side

Full signature verification MUST be performed in the following order:

  • check if the signature ends with magic bytes, in which case do an eth_call to a multicall contract on the network matching chainId that will call the factory first with the factoryCalldata and deploy the contract if it isn’t already deployed; Then, call contract.isValidSignature as usual with the unwrapped signature
  • check if there’s contract code at the address on the network matching chainId. If so perform ERC-1271 verification as usual by invoking isValidSignature
  • if the ERC-1271 verification fails, and the deploy call to the factory was skipped due to the wallet already having code, execute the factoryCalldata transaction and try isValidSignature again
  • if there is no contract code at the address, try ecrecover verification

Apologies for the large message block, but I thought the quotes would help communicate the issues I think need resolution!

Hey, thanks for the feedback and I think that could be quite useful.

However, I see a few issues with this:

  • signatures may be intentionally cross-chain - this can be resolved via a santinel value for chainId that signifies “universal”
  • signatures may be malleable, and if the chainId is in the wrapper format, the developers MUST also include it in the hash in order to avoid malleability
  • the EIP is now final so I’m not sure if such changes are ok or this should be a new EIP

Can’t wallet providers deduce where to verify the signatures based on the currently selected network of the dapp?

Hey Ivshti, thanks for the response!

Good call on a sentinel value and clarifying the need for chain malleability awareness on the account.

As for the EIP being final, this leads me to think about drafting a new EIP with this feature. Do you think that this scope increase is large enough to warrant a new proposal?

Can’t wallet providers deduce where to verify the signatures based on the currently selected network of the dapp?

Technically, I think they can, but this creates a high UX burden on the user. I’m reaching out to my contacts who do 1271 verification and right now it seems like everyone informally requires the account to be on Ethereum mainnet which is a big bummer for accounts that desire to be L2-first for cost effectiveness. Practically speaking, it’s a large UX burden for users to internalize that they need to connect the account, make sure their account selects the appropriate network, then trigger a SIWE flow. Some apps also trigger a SIWE immediately after wallet connection which makes it even harder for the user to know what to do. Some account implementations also rely on authentication deriving from state that is local to a single network (e.g. ERC-721 owner for an EIP-6551 account) which also demands some way to communicate to a Verifier that a specific chain should be used for authentication.

New EIP if you’re going to be making material/normative changes.