ERC-4337: Account Abstraction via Entry Point Contract specification

Echoing a comment I made over at EIP idea: eth_signUserOperation RPC - #3 by SamWilsn : I’d really like to see a JSON Schema for all the JSON-RPC requests and responses.

What you have now is much too ambiguous for people to implement consistently.

For example, nothing in the eth_sendUserOperation section says that maxFeePerGas is optional. You have to get down to the note in eth_estimateUserOperationGas where it’s only implied:

same as eth_sendUserOperation gas limits (and prices) parameters are optional

The schema of the rpc calls are here

Can we get those in the EIP itself? Would be a huge improvement for visibility.

For IAccount in the ERC, might it be more accommodating to set the visibility of validateUserOp to public rather than external (shorter, no gas difference, more flexible)?

Can’t: its a linter issue:
Functions in interfaces must be declared external.

ah, I see, but was looking at things like EIP-20 which declare the methods public. I think my suggestion on IAccount may have confused my suggestion (which is that implementation might use public and just to clarify). This was something that came up on a recent Solady 4337 PR.

I believe nonce retrieving and validation logic should not be enshrined in the entry point and that logic should be left as in older reasons, here’s some reasons why I think this should be the case:

  1. Different wallets may want to validate nonces differently, enshrining the logic limits this.
  2. The current enshrined nonce scheme prevents wallets from taking advantage of storage packing and other optimization techniques that may decrease the cost of using and updating the nonce by >4.9k.
  3. If nonces are stored with the entry point and wallets rely on this what happens when the entry point is upgraded? Do all past user ops become re-validated again? Will the new entrypoint have to query the old one adding even more gas?

The original version had the nonce entirely in the account. But this has a drawback: a nonce can’t be enforced to be unique for the account. This also meant that userOp hash couldn’t be considered unique.
To support native account abstraction (e.g. EIP-7560), we must enforce the uniqueness at the protocol level, and not leave it to the account - or add another uniqueness mechanism at the protocol level, besides the account-based nonce.
We decided to introduce a protocol-level nonce (at the EntryPoint), but still make it flexible so the account can control how sequential or parallel it is.

In case of entry point upgrade, the nonce count resets back from zero for each key.
The hashes are still unique, since the EntryPoint address (and chain-id) are hashed into the userOpHash

(Note that unlike the sequential nonce of ethereum, which is basically the total transaction count of an account, with 4337 we have a notion of “key”, and the sequence is only within that key, and the account may introduce new keys.
When an entrypoint is upgraded, the counter for each key has to reset)

1 Like

This only seems to explain reasons why one might want to enshrine nonce invalidation into the entrypoint but not why these points are necessarily more critical than the ones I mentioned above?

Hi everyone.

In ERC4337, verificationGasLimit on the official EIP page is explained such as:

The amount of gas to allocate for the verification step

On the following page, it’s said to be:

The VGL refers to the maximum gas available for a User Operation to
execute its validation phase. This includes:

Deploying a smart account on the first transaction. Running the
validateUserOp and validatePaymasterUserOp function on the smart
account and paymaster. Running postOp function on the paymaster after
execution.

I thought that verificationGasLimit, whatever is set to, is the total that will be used for all the validation logic + deploying the contract + running paymaster’s pre and post op. Clearly, in the code , that’s not the case. here is why:

  1. deploy contract is run with “verificationGasLimit” - i.e
senderCreator.createSender{gas : opInfo.mUserOp.verificationGasLimit}(initCode);
  1. Then:
try IAccount(sender).validateUserOp{gas : mUserOp.verificationGasLimit}(op, opInfo.userOpHash, missingAccountFunds)
  1. Then:
IPaymaster(paymaster).postOp{gas : mUserOp.verificationGasLimit}(

As one can see, the code doesn’t match the explanation. It should be written as follows: If deploy contract code is run, then only pass “verificationGasLimit - gasUsedByDeploy” to validateUserOp. and so on.
I might be completely missing something though.