RIP-7560: Native Account Abstraction

I also don’t see how the “unused gas” griefing vector described can’t also be done with an EOA transaction. Is there a better description of the attack somewhere that explains how it’s unique to user ops?

The issue of unused gas is only relevant for Type 4 Transactions because they can have their execution behaviour influenced by transactions that are coming after them in a block. This does not seem to be a problem unless the transaction suddenly starts using more gas than it used before.

Here is an example of how this could be turned into an attack on the block builder.

Here, Transaction #4 has a gas limit of 10’000’000. However, it only used 50’000 gas so there is a lot of available gas space in a block.

However, once Transaction #6 is included in a block and its validation phase flips some flag, Transaction #4 starts consuming the entire 10’000’000 gas.

2 Likes

So does the unused gas penalty go away after ERC-7562?

So does the unused gas penalty go away after ERC-7562 ?

Unfortunately, there seems to be no way to prevent the “flickering” gas usage by transactions with validation rules alone.
The “waste gas flag” storage can be read during execution phase of any transaction, and the “validation rules” are not meant to be applied during the execution phase.

1 Like

TL;DR: that’s the cost of account abstraction.

The complexity doesn’t come from the protocol itself (eip-7562 or ERC-4337) - It comes from the fact we want to use general-purpose EVM code for validation, which makes one transaction depend on external data - shared with other transactions, or with the external world.
With EOA, the validation is hard-coded into the protocol and depends only on the state of the account itself.
This means that a previously validated transaction can only be invalidated by issuing another transaction with the same sender (and nonce) to replace it.

With account abstraction (ANY implementation - 7560, 4337, 2938, and even 3078) we must have a mechanism to ensure the same isolation, otherwise, maliciously crafted transactions can cause mass invalidation: transactions that are accepted and propagated into the mempool, which later becomes invalid and thus draw resources from all nodes without a cost.

Removing this complexity exposes block-builders to DoS attacks, or requires removing the general EVM-code usage (the essence of account abstraction)

1 Like

I’m not against complexity, just complexity at the core protocol level. At least ERC-4337 is opt-in and I can ignore it if I think there is too much risk or chose an alternate smart contract based AA solution. With this proposal, if something goes wrong for builders (some new DoS vector is found and exploited) or some bug is found, it would take down the whole network, including users who never cared about ERC-4337 style AA.

To me, it feels like trying to embed an OS into the bare metal. The bug surface area is too large and it would alienate others who want to use a different OS.

Pasting the reply I sent you in 4337 Mafia on telegram in case others also wonder:

It protects the builder against a DoS vector, also applicable to 4337 bundlers (which is why the next EntryPoint version will have this as well).

Consider a builder trying to construct a block containing many AA transactions. It creates a batch of AA transactions (equivalent to a 4337 bundle) where all the validations will be executed, followed by all the executions. To work efficiently and not have to validate transactions sequentially, it wants to create the largest possible batch, validating all the transactions in parallel against the same state. Otherwise it can’t parallelize because transactions can invalidate each other, which makes it vulnerable to DoS by many mutually-exclusive transactions where one transaction’s execution invalidates another’s validation.

An attacker could send transactions that have a high callGasLimit which they actually use when simulated separately, but that changes its behavior to use almost no gas when it detects that another transaction’s validation has been executed. Suppose the batch has [tx1,tx2]. tx2.validation sets tx2.sender.flag=1, and tx1.execution does return (tx2.sender.flag==1 || waste5Mgas()). It is allowed to access tx2.sender.flag because it happens during execution rather than validation. The builder created the batch, thinking tx1 will use 5M gas and fill up the rest of the block, but due to the above behavior, 5M gas worth of of blockspace is not utilized. The builder now has to append more transactions sequentially (not parallelized), simulating each of them against the state after all the others in order to know how much gas it really uses.

By penalizing unreasonably high unused gas, we make this attack less scalable.

It’s ERC-7562. We extracted the ERC-4337 validation rules to a separate (and hopefully more readable) ERC since they’re identical in both systems.

Thanks!

I see Alex already replied this, but I’ll add that it was requested by one of the L2s. They wanted to be able to add future extensions (maybe even specific to their own network) without having to redeploy or update accounts.

I think making accounts upgradable offers security and usability benefits, well worth the SLOAD. But we’re also looking for ways to make it cheaper. For example, EIP-7557 could make it as cheap as 32 gas for a widely used account while also making the protocol fairer. Support EIP-7557 :slight_smile:

To put this in context, the RIP is meant for rollups that already added, or are considering adding the complexity of native AA. Some already have, and some are planning to. The purpose of this RIP is to standardize it for those who do, and avoiding fragmentation of the wallet ecosystem (wallets supporting only one chain, which is currently the case in chains that added their own form of native AA).

and that’s precisely what this RIP does. ERC-4337 has been gaining some traction, and has been used to add native AA in nonstandard ways by two L2 chains. RIP-7560 is an enshrined version of ERC-4337, optimized for rollups.

It shouldn’t. That’s why we separated RIP-7560 and ERC-7562. The mempool validation rules are incompatible with some forms of intents, but RIP-7560 isn’t. Intent systems could benefit from RIP-7560 while being incompatible with the ERC-7562 mempool and using a separate intent-solvers network. It’s one of the design goals.

No. But aggregation will be a separate RIP, to be published soon.

It’s meant for chains that choose to embed this into their “OS”. For example, Starknet and zkSync already embedded a similar native AA system (both are based on ERC-4337 with some mods). You’re right that there is no way for you to opt out of it on these chains, whereas on a chain that doesn’t implement native AA, you can choose whether to use ERC-4337 or not. L2 chains sometimes choose to be more opinionated about their OS for the sake of optimizing their UX and efficiency. However, the Ethereum ecosystem offers enough choice of L2s so you can opt out by using a different one. Some may choose to implement it early, some may be more conservative and wait.

1 Like

Will this lead to a precompiled contract for secp256r1 curve support, so we can finally implement wallets that integrate with the built-in private key encryption used in operating systems and secure enclaves?

1 Like

You’re talking about RIP-7212 (for which the answer is yes). But this thread is about RIP-7560.

I have two questions:


  1. In the section “Accepting EOA account as sender to achieve native gas abstraction,” it might not be explained clearly. When an EOA account acts as the sender (e.g., an EOA address is 0xabc), after the execution, will the ‘old nonce (the current Ethereum built-in nonce)’ of 0xabc increase, or will only the nonce managed in the ‘NonceManager’ increase?

    I think the EOA account will only increase the nonce managed in the ‘NonceManager.’ I’d like to confirm this with you.

  2. Regarding RPC methods (eth namespace)

    Because AA can no longer check if a transaction is valid like EOAs can (e.g., due to low balance with the paymaster causing temporary or permanent invalidation), a user might need to call the eth_sendTransaction interface multiple times before eth_getTransactionReceipt returns a result to confirm if their previous transaction is in a valid state (before it’s included in a block). However, sending the same transaction to the RPC multiple times might result in errors (e.g., the RPC service provider expecting users to increase the gas by 20% for each eth_sendTransaction call with the same nonce to consider it a valid request).

    So, the current RIP doesn’t explicitly explain how users can know the status of a transaction they sent before it’s included in the blockchain. I’m not sure if this is a real issue, but in my opinion, because the validity of AA transactions can be affected by external factors, users need to know if their uninclude transactions are valid, even if it’s not very likely.


thanks :heart_hands:

Is there an updated timeline for this or progress made towards it?

Is there any code implementations or references to this RIP-7560 ?
I wanna use this in my dApp for learning purpose.
If yes, please kindly drop the link for it.
Thanks .

  1. Nonce:

The entire EIP uses a NonceManager, and not the old internal nonce. The internal nonce is left for CREATE operation only.

A well-written account should not require the user to submit the same UserOperation multiple times. I think that will happen for badly written (or outright broken) accounts.
The user may re-submit the same userop with a higher gas fee if needed.

Regarding paymasters: In order to be accepted into the mempool (that is, for sendTransaction to return a valid response), a paymaster must hold a balance enough for all pending UserOperations.
In order to be able to notify the calling users that UserOperation gets dropped because of paymaster limitation, RPC nodes should accept UserOps for paymasters that have an extra deposit (e.g. 10% more), so that these UserOperations will not get dropped when passed to other nodes through the mempool.
(This doesn’t fully prevent such silent drop of UserOperations, but the same is true of the Ethereum mempool: you have no guarantee a submitted transaction will not get dropped)

1 Like

Thank you very much for clarifying!

@mcoso @rvd we’re working on a geth implementation. As soon as it’s ready for testing we will publish it, and then launch a devnet to experiment with.

And of course, if anyone wants to implement it in another client, that would be awesome and we’ll be able to test the two implementations together when ready.

1 Like

Is the geth implementation development is currently available on a public github repository Interested in seeing changes required for the project if that’s possible

1 Like

@yoavw when you mean geth, are you referring to the L1 execution client, or the execution node of a roll-up such as op-geth?
Since it’s a RIP I would assume it should be implemented in the context of a roll-up.

looking forward to the geth implementation

I’ve been thinking a little bit more about the mempool design for this RIP. At a high level, it seems not necessary and potentially worsens the developer and user experience.

As an alternative model we could consider integrating the bundler into the L2 block builder (e.g. the sequencer). The flow becomes similar to how rollups work today:

  1. RPC nodes can accept and validate transactions before relaying them to the sequencer.
  2. The sequencer can implement a reputation based system with RPC nodes to mitigate DoS.
  3. The sequencer builds blocks from transactions type 1-4 inclusive, providing guarantees to both users and developers that are not available if instead the bundlers run in the RPC nodes.

This implies there really is only one bundler on L2 (the sequencer) and that bundles can be included in the block alongside type 1-3 transactions. This seems to have no worse trust assumptions than layer 2’s today in that the sequencer is still a central point of failure. If this were to extend to decentralized sequencing it would require an implementation of leader election at the L2 level (which would also mitigate the mempool problem today but add a ton of complexity to the system which is largely unnecessary).

What do we think the tradeoffs of such a system would be?