DEV Community

imokokok
imokokok

Posted on

ERC-4337 Has Two Execution Results. Your Dashboard Should Show Both.

A block explorer shows a successful transaction. A wallet dashboard marks the operation as successful too.

For an ERC-4337 flow, those may be two different results.

A bundler submits an outer EVM transaction to an EntryPoint contract. That transaction can contain one or more UserOperations. The outer transaction status tells you whether the EVM transaction succeeded. It does not, by itself, tell you whether every operation inside the bundle succeeded.

Check the operation-level event

When handleOps completes successfully, the EntryPoint emits a UserOperationEvent for each processed operation. The event includes the operation’s success flag and gas information.

That means an outer transaction can succeed while one UserOperation fails. The operation can still incur gas.

If the outer transaction itself reverts, the expected operation event and operation-level result are absent. The operation’s bytes appearing in the reverted transaction calldata do not establish that the UserOperation executed.

A bundler-returned UserOperation hash is also not inclusion evidence. It identifies an operation for tracking. To establish inclusion, find the matching event in the transaction receipt and verify the event against the operation that was authorized.

Bind the operation before submission

PriorSeal’s ERC-4337 adapter binds the canonical UserOperation hash into a normal authorization intent. Its observer checks the matching EntryPoint event in the outer transaction receipt. The documented adapter supports EntryPoint versions 0.6 through 0.9.

Here is an integration outline:

import {
  bindERC4337UserOperation,
  buildERC4337UserOperationIntent,
} from 'priorseal-sdk'

const operationInput = {
  chainId,
  entryPoint: verifiedEntryPointAddress,
  entryPointCodeHash: verifiedEntryPointCodeHash,
  entryPointVersion: '0.7' as const,
  userOperation: walletUserOperation,
}

const binding = bindERC4337UserOperation(operationInput)

const intent = buildERC4337UserOperationIntent({
  ...operationInput,
  intentId: `withdrawal-${binding.userOpHash.slice(2, 14)}`,
  validUntil,
})

// Authorize this intent before submitting the UserOperation.
// After the bundler includes it, observe the outer transaction hash.
Enter fullscreen mode Exit fullscreen mode

The wallet supplies the UserOperation. The EntryPoint address and code hash should come from the operator’s independently reviewed deployment configuration. This snippet binds and authorizes the operation; the final observation still needs the outer transaction hash and a matching event.

Know what the binding proves

The canonical hash ties the authorization to one UserOperation. The matching event ties that operation to an observed EntryPoint transaction.

Those checks do not necessarily decode the smart account’s inner call. A Safe-specific profile can provide more detail within its documented scope; without an applicable account profile, do not claim that the receipt independently proves every inner call’s meaning or business effect.

I keep three results separate in the application:

  • Outer transaction status
  • UserOperation success and gas
  • Whether the observed operation matches its authorization

That distinction matters for both success and failure paths. An outer transaction can succeed while an operation fails. A reverted outer transaction can contain operation calldata without establishing that the operation ran.

The PriorSeal ERC-4337 guide documents the supported EntryPoint and account-specific profiles. The SDK README covers the broader authorization and receipt flow.

Does your transaction UI show the bundler transaction and the UserOperation as separate records?

Top comments (0)