Disagree here with providing complex ways of finding a UserOperation .
What has provided the best dapp ux is submitting a UserOperation and the bundler returning the transactionHash. This allows the dapp to await success and handle an error efficiently.
You can potentially also be optimistic about a UserOperation for certain operations that are non-blocking.
Agree with returning transactionHash by eth_sendUserOperation, which can help retrieve the UserOp, and this does not conflict with adding transactionHash tag in eth_getUserOperationReceipt, or maybe directly using a new API such as eth_getUserOperationReceiptByTransactionHash. Furthermore, returning transactionHash can also solve the issue mentioned by @alex-forshtat-tbk that The end-user, dapp, or wallet likely do not know the transactionHash of a transaction containing the UserOperation.
I’ve been working on PrepaidGas, a decentralized, ZK-powered paymaster, and have identified a potential issue while implementing the decentralized paymaster solution. I’d appreciate your thoughts on:
The Challenge: DDoS Vector against Bundlers (without fund loss) When a user effectively acts as their own paymaster service (as in our decentralized model), a malicious actor could theoretically spam bundlers simultaneously with the same valid gas ticket (i.e., a UserOperation with a valid ZKP and paymaster context).
Result: Bundlers would successfully validate these identical UserOps (as the ZKP is initially valid), but only the first one to hit the chain would successfully execute (e.g., consuming the nullifier). The subsequent ones would fail during execution.
Impact: While no funds are lost from the paymaster, this wastes significant bundler resources. It’s akin to the UserOperationnonce preventing account-level replays, but there’s no equivalent “paymaster nonce” for such concurrent spam. This could damage the paymaster’s reputation and lead to bundlers deprioritizing or censoring its UserOps.
Why a Solution is Important (and respects separation): I believe introducing a mechanism like a “paymaster nonce” within the UserOperation could mitigate this. This wouldn’t violate the Bundler-Paymaster separation; it would simply be:
Bundler Infrastructure Protection: A crucial anti-spam primitive for bundlers, protecting their vital infrastructure.
Enabling Decentralized Paymasters: It would empower truly decentralized paymaster models like PrepaidGas to operate robustly without relying on centralized off-chain components solely for anti-spam.
I’m keen to hear any concerns or thoughts on this. Is this something already considered, or are there existing ideas/solutions in the pipeline for such scenarios without a centralized party?
Thanks for an interesting question. It would be great to learn more about the architecture of your paymaster, is it open-source? If so, could you please share a link to the repository.
Regarding the issue with a DDoS vector, you are correct that in the current ERC-4337/ERC-7562 design it is the Paymaster’s responsibility to provide an on-chain mechanism to identify and rate limit the end users, and even further, a Paymaster that fails to do so may be blacklisted by bundlers in case it is used by an attacker to DoS the bundler.
Clearly this is a challenge for the Paymaster that has no advance information about its users.
As far as I can understand the situation, this means that your Paymaster should not be “staked” in the definition of ERC-7562.
Not being staked will limit the number of UserOperations using this Pyamaster in the mempool to SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT = 10, so an attacker will be able to create and broadcast 10 UserOps using the same gas ticket, and only pay one of these.
If you really need your Paymaster to be staked, you will potentially need to add an additional DoS prevention mechanism.
However, it is not clear yet if the “paymaster nonce” is necessary here, as the existing UserOperationnonce parameter already consists of two components. Introducing extra fields to the UserOperation would require a major breaking change at this point.
The nonce field is considered to be related to the sender but it is visible to the Paymaster in the validatePaymasterUserOp callback.
Do you think you would be able to solve the DDoS issue by using the uint192 key component of the nonce field?
I don’t think it is an attack against the bundler:
as @alex-forshtat-tbk said, there can only be so many UserOps for that paymaster in the mempool (currently 10), so that’s the number of UserOps that can be invalidated.
In order to have more UserOps in the mempool, the paymaster has to be staked. But if an attack can still cause invalidation of a large # of UserOps, then the paymaster gets DoS’ed , not the bundler…
In order to prevent such DoS, the paymaster must use its own storage.
e.g: in most ZK consructs, there is a “nullifier”, which can be checked whether the proof was already spent.
As long as these nullifiers are independent (that is, have 1-1 relation to a proof, and no 2 proofs can invalidate the same nullifier), the paymaster is safe from such a DoS attack.
Thank you both for your valuable insights in the context of ERC-4337 and ERC-7562. Here are the links to the prepaid-gas paymaster repository and a demo video, for those interested in exploring further:
As both of you highlighted, and Dror explicitly mentioned, implementing nullifier tracking in paymaster storage is key. PrepaidGas Paymaster does exactly that with nullifierGasUsage and usedNullifiers, ensuring our funds are safe from on-chain replays and double-spending of ZKP-backed gas tickets.
Regarding Alex’s suggestion of using the userOp.nonce.key to leverage existing fields, my primary concern is that some smart account implementations use the userOp.nonce field for their own internal logic, like in the case of a modular smart account representing a module address. Forcing our Paymaster’s UserOperations to use this field for a different purpose could create compatibility issues with a wide range of existing Smart Accounts.
As highlighted, for an unstaked Paymaster, the SAME_UNSTAKED_ENTITY_MEMPOOL_COUNT (currently 10) directly limits the number of UserOps a single bundler will hold for us. While this offers excellent anti-DDoS protection for bundlers, does it risk throttling legitimate traffic for decentralized paymasters like PrepaidGas?
Should PrepaidGas Paymaster become staked, the challenge then becomes how to enable bundlers to accept higher volumes of UserOps without disproportionately burdening them with simulations of transactions that will ultimately revert due to an already-consumed nullifier.
Appreciate your feedback and looking forward to your thoughts!
The paymaster should be staked, so it can use a mapping of nullifiers.
and as long as a nullifier is “immutable” (that is, once issued can’t become invalid - unless it is explicitly withdran), it is safe from spamming bundlers (and from users to DoS the paymaster, by forcing it to spam bundlers…)