Why
Bundlers pay gas up front and paymasters remain liable even when execution fails, so both apply local risk controls. The application must expose those service decisions instead of rendering every rejection as "transaction failed."
An ERC-4337 smart account does not send a normal transaction. A bundler simulates and submits its UserOperation, and a paymaster may decide to sponsor the gas. Both services can reject an operation that the EntryPoint contract would accept.
How it works
Send the same UserOperation through two bundlers and two sponsorship policies; classify simulation, policy, quota, deposit, inclusion, and execution failures; and define a user-funded fallback.
PoC
Send the same UserOperation to two bundlers under two paymaster policies. Record failures separately for RPC compatibility, simulation, reputation policy, quota, expired sponsorship, insufficient paymaster deposit, inclusion timeout, and execution revert. Then retry without sponsorship using user funds.
What it proves
"Gasless" is an application promise assembled from two off-chain services and one on-chain contract. Portability means more than changing an endpoint: error classification, EntryPoint version, sponsorship data, and fallback funding must also work.
References: ERC-4337 bundlers, paymaster security.
Review clarification
Validity is not availability
The EntryPoint contract is the only trustless party in this stack: give it a well-formed UserOperation and it will accept it. But between the user and the EntryPoint stand two services that each spend their own money before they are sure — the bundler fronts the gas to land the bundle, and the paymaster stays liable even if the call reverts. Because they pay before they know, they run local risk controls, and either can refuse an operation the chain would have accepted. So "gasless" is an availability-and-policy claim, not a wallet property — the same shape as rpc-view-not-consensus (an RPC answer is one node's view, not consensus) and receipt-is-not-settlement (a success response is not money moved). This is the account-abstraction member of that family: the happy path hides a service decision.
Follow the money — why each service rejects
Every failure class maps to one of the two services protecting its capital, or to the contract itself:
| Failure | Who raises it | What it is protecting |
|---|---|---|
| RPC compatibility | bundler | does it even speak your EntryPoint version |
| Simulation | bundler | validation must not touch banned opcodes / others' storage (anti-griefing) |
| Reputation / throttling | bundler | ERC-7562: accounts/paymasters that waste simulations get throttled or banned |
| Inclusion timeout | bundler | simulated fine, never landed |
| Policy | paymaster | will I sponsor this caller / action |
| Quota | paymaster | you have spent your allowance |
| Expired sponsorship | paymaster | the signed sponsorship data timed out |
| Insufficient deposit | paymaster | it is broke or under-staked in the EntryPoint |
| Execution revert | EntryPoint | validation passed, the actual call failed |
The griefing rules in the reference are the why behind the why: reputation and simulation exist because a malicious account can make a bundler burn gas on ops that pass validation then behave differently. The taxonomy is the surface of that adversarial design, not bureaucracy.
The taxonomy is the deliverable, because the classes demand opposite responses
Eight causes currently collapse into one useless "transaction failed" — yet the correct reaction is different, sometimes opposite, for each:
| Failure | Correct response |
|---|---|
| Quota / deposit exhausted | fall back to user-funded |
| Expired sponsorship | re-request a signature, retry sponsored |
| Reputation ban | switch bundler |
| Inclusion timeout | resubmit, possibly another bundler |
| Execution revert | stop — retrying is pointless and, unsponsored, costs the user real gas |
This is the same retry-classification discipline as x402-settlement-retry, and the fallback state machine it implies — survive, retry the right way, wait, and stay visible — is exactly what temporal-durable-execution is for.
Portability is the sharp business point — and EntryPoint version is the trap
"Send the same UserOperation through two bundlers" sounds like an endpoint swap; it is not. EntryPoint v0.6 vs v0.7/0.8 changed the UserOp struct and the paymasterAndData layout, so the "same" op is not byte-identical across stacks. Sponsorship-data format, error codes, and the fallback funding path all differ per provider. Multi-bundler resilience is therefore real engineering, and a gasless product that cannot degrade or switch providers has a single point of failure it is not advertising.
The fallback is a UX confession
"Retry unsponsored with user funds" quietly admits gasless is best-effort. That is the honest design — but it means the account must actually hold a gas asset for the degraded path, and the app must decide when to spend the user's own money without a fresh consent. Silently switching from sponsored to user-paid is a policy decision a user might want to veto — which is where this card touches embedded-wallet-policy and agentic-intent-veto.