Learn
Reading a transaction
One real transaction, every field: from the outer call down to the last log, and what each one proves.
This page takes one real Oct 18, 2026 C-Chain transaction and reads everything in it: the outer call, the one UserOperation inside, the account's batch, the logs in order, and the money. The goal is that next time you can do this on any EntryPoint transaction.
The transaction: 0xddda920f2fd5092c727088b9916f632bd61628bfa118c6ddd5a2356827214ffe
- Block 97,857,672, 2026-10-18 06:18:34 UTC, type-2, status 1,
to= EntryPoint v0.70x0000000071727de22e5e9d8baf0edac6f37da032evidence level: primary source. - In one sentence: a ZeroDev Kernel smart account is deployed and, in the same operation, approves and deposits 64.964131 USDC into Relay's depository (a cross-chain intent). Pimlico's verifying paymaster sponsors the gas, and a
0x4337…-prefixed bundler submits it. - Why this one: it is v0.7, paymaster-sponsored, includes an account deployment, and has a clear app call. The earlier population already lists it as
existing baseline operation: cross_chain.asset_send.
Everything below comes from the transaction's full calldata and receipt logs, decoded with eth_abi.
1. The outer transaction
| Field | Value | What it tells you |
|---|---|---|
from |
0x4337005db25dbad41da5692ba1188751ee5d98b6 evidence level: hint |
The bundler EOA. It signed this transaction and paid the native gas. |
to |
0x0000000071727de22e5e9d8baf0edac6f37da032 evidence level: primary source |
EntryPoint v0.7 (direct call, not wrapped). |
value |
0 | Bundlers don't send AVAX to the EntryPoint here. |
gas_used |
387,477 | Gas for the whole bundle. |
effective_gas_price |
0x14b1a92c0 = 5.555 gwei | Block base fee 5.0 gwei (blocks parquet) + bundler tip 0.555 gwei. |
| Receipt fee | 387,477 × 5,555,000,000 = 2,152,434,735,000,000 wei (0.002152 AVAX) | What the bundler paid the chain. |
| tx nonce | 125,722 | This bundler EOA has sent a lot of transactions. |
2. The outer calldata: handleOps(PackedUserOperation[], address)
The input is 2,244 bytes. Selector 0x765e827f is the packed handleOps shared by v0.7/v0.8/v0.9. The to address says v0.7.
765e827f selector
0000…0040 [0x00] offset of ops[] = 0x40
0000…4337005db25dbad41da5692ba1188751ee5d98b6 [0x20] beneficiary
0000…0001 [0x40] ops.length = 1
0000…0020 [0x60] offset of ops[0]
Already, the beneficiary (0x4337005d…) equals the bundler. That is the normal case: on Oct 18, 1,391 of 1,396 outers had beneficiary = submitter. All 5 exceptions were v0.7, and 2 of those were wrapped calls.
The op's head (static fields and offsets)
000000000000000000000000cb7509e6466632d73a509ec778f88d52fa9096d2 sender
00008104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000 nonce
…0120 offset initCode (→ 408 bytes)
…02e0 offset callData (→ 996 bytes)
00000000000000000000000000004dcca000000000000000000000000000141e7 accountGasLimits
…eb3e preVerificationGas
0000000000000000000000003cc91af3000000000000000000000001ddc7d1fc gasFees
…0700 offset paymasterAndData (→ 130 bytes)
…07c0 offset signature (→ 65 bytes)
3. The PackedUserOperation, field by field
| # | Field | Raw | Decoded / meaning |
|---|---|---|---|
| 1 | sender |
cb7509e6…96d2 |
The smart account. It doesn't exist yet when this op starts; initCode creates it. |
| 2 | nonce |
0x00008104e3ad…1c43 0000 0000000000000000 |
192-bit key + 64-bit sequence. Sequence = 0 (first op on this key). The key embeds 8104e3ad430ea6d354d013a6789fdfc71e671c43, the ZeroDev ECDSA validator. Kernel v3 selects its validator through the nonce key. (I have not re-verified Kernel's exact nonce byte layout from source; the embedded validator address is directly visible.) |
| 3 | initCode |
d703aae7…42d5 + c5265d5d… |
factory = 0xd703aae79538628d27099b8c4f621be4ccd142d5 evidence level: primary source, call = deployWithFactory(address,bytes,bytes32) (selector 0xc5265d5d, matches by keccak). See §3a. |
| 4 | callData |
e9ae5c53 01000000… |
ERC-7579 execute(bytes32 mode, bytes executionCalldata) with a batch of 3 calls. See §4. |
| 5 | accountGasLimits |
…04dcca ‖ …0141e7 |
verificationGasLimit = 318,666, callGasLimit = 82,407 |
| 6 | preVerificationGas |
0xeb3e |
60,222: covers calldata and bundle overhead the EntryPoint can't meter |
| 7 | gasFees |
…3cc91af3 ‖ …01ddc7d1fc |
maxPriorityFeePerGas = 1.019812595 gwei, maxFeePerGas = 8.015827452 gwei |
| 8 | paymasterAndData |
130 bytes | See §3b |
| 9 | signature |
6ff89ef3…5f30431c (65 bytes) |
The account owner's ECDSA signature. See §6. |
3a. initCode: who creates the account
d703aae79538628d27099b8c4f621be4ccd142d5 factory (20 bytes) = ZeroDev Kernel "meta factory"
c5265d5d deployWithFactory(address factory, bytes createData, bytes32 salt)
factory = 0x6723b44abeec4e71ebe3232bd5b455805badd22f ZeroDev Kernel v3.0 factory
salt = 0x00…00
createData = 12af322c initialize(bytes21,address,bytes,bytes)
01 8104e3ad430ea6d354d013a6789fdfc71e671c43 rootValidator = type 0x01 (validator) ‖ ECDSA validator
… hook = 0x0
… validatorData = 3f98da05dc176902d47e989ecdae9a443ebbbbeb ← the owner EOA
Identity check: ZeroDev's SDK constants (zerodevapp/sdk packages/core/constants.ts, plugins/ecdsa/constants.ts, fetched 2026-10-19) list 0x6723b44a…d22f evidence level: primary source as the Kernel v0.3.0 factoryAddress, 0xd703aaE7…42d5 evidence level: primary source as metaFactoryAddress, and 0x8104e3Ad…1c43 as the v0.3.0 ECDSA validator. The selectors 0xc5265d5d and 0x12af322c match those function signatures by keccak. This binds the wallet implementation (ZeroDev Kernel v3.0 + ECDSA validator). It says nothing about which app or person uses the wallet.
3b. paymasterAndData: who pays the gas
777777777777aec03fd955926dbf81597e66834c [0:20] paymaster = Pimlico singleton paymaster (v0.7)
0000000000000000000000000000b578 [20:36] paymasterVerificationGasLimit = 46,456
00000000000000000000000000000001 [36:52] paymasterPostOpGasLimit = 1
00 [52] Pimlico mode byte: mode 0 (verifying), allowAllBundlers = 0
00006ad46709 validUntil = 1,792,304,905 (06:28:25 UTC, ~10 min after inclusion)
000000000000 validAfter = 0
112e4e3a…4f4e7f28046cd51b 65-byte Pimlico signer signature
- The paymaster address matches Pimlico's documentation for v0.7 (
web/pimlico-erc20-paymaster-contract-addresses.mdx). - The layout after byte 52 follows Pimlico's
BaseSingletonPaymaster._parsePaymasterAndData/_parseVerifyingConfig(earlier source). - Verifying mode means Pimlico's off-chain service signed "I'll pay for this exact op until 06:28:25". No token is charged on chain. A postOp gas limit of 1 fits that: verifying mode needs no postOp accounting.
- I recovered the paymaster signature using Pimlico's V7
getHash(hash of the op fields withpaymasterAndData[:65], then chainId, then eth-signed-message). It recovers to0x4337dd7891df2580890e4f6eab503465efeb6afe. I did not read the paymaster's on-chainsignersmapping, so "that address is an authorized Pimlico signer" is implied by the op having validated, not checked directly.
4. callData: what the account was asked to do
e9ae5c53 execute(bytes32 mode, bytes executionCalldata) [ERC-7579]
0100000000000000…00 mode: callType 0x01 = batch, execType 0x00 = revert on failure
executionCalldata = abi.encode((address target, uint256 value, bytes data)[]) with 3 entries:
| Leg | Target | Value | Data | Decoded |
|---|---|---|---|---|
| 0 | 0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e evidence level: primary source (USDC) |
0 | 095ea7b3 …4cd00e38…bc31 …03df4623 |
approve(0x4cd00e38…, 64,964,131) = 64.964131 USDC |
| 1 | 0x4cd00e387622c35bddb9b4c962c136462338bc31 (Relay Depository) |
0 | e8017952 …cb7509e6… …b97ef9ef… …03df4623 e058b445…1f3f |
depositErc20(depositor=sender, currency=USDC, amount=64,964,131, id=0xe058b445…1f3f) |
| 2 | USDC | 0 | 095ea7b3 …4cd00e38… …00 |
approve(0x4cd00e38…, 0): resets the allowance |
The ERC-7579 callType byte meanings come from the ERC-7579 text ("0x00 single, 0x01 batch…"). The (address,uint256,bytes)[] encoding re-encodes exactly.
Identity checks:
- USDC at
0xb97e…8a6eevidence level: primary source: the earlier asset identity (official Avalanche USDC; the research data). - Relay:
https://api.relay.link/chains(fetched 2026-10-19) lists Avalanche (43114)protocol.v2.depository = 0x4cd00e387622c35bddb9b4c962c136462338bc31. Etherscan also labels this address "Relay: Depository". ThedepositErc20(address,address,uint256,bytes32)signature is only a decompiler/signature-database hint in the earlier research data. But the emitted event (§5, log 0x13) has topic0 = keccak(RelayErc20Deposit(address,address,uint256,bytes32)), which I recomputed, and its data echoes exactly the four request words.
What the bytes do NOT say: the destination chain, the recipient, or whether the funds arrived anywhere. The id (0xe058b445…) is Relay's order identifier. The route is defined off chain, in Relay's order book. Proving arrival needs destination-chain evidence.
5. The receipt logs, in order
Ten logs, log indices 0x0c–0x15. (Log indices are block-wide, so this transaction's first log isn't 0.)
| logIndex | Emitter | Event (topic0) | Phase | What it tells you |
|---|---|---|---|---|
| 0x0c | cb7509e6… (new account) |
RootValidatorUpdated(bytes21) 0x6789ec0c…; data 01 8104e3ad…1c43 |
validation: deployment | Kernel's initialize ran: the root validator is the ECDSA validator. |
| 0x0d | 8104e3ad… (ECDSA validator) |
OwnerRegistered(address kernel, address owner) 0xa5e1f8b4…; topics: cb7509e6…, 3f98da05… |
validation: deployment | The account's owner key is 0x3f98da05dc176902d47e989ecdae9a443ebbbbeb. |
| 0x0e | EntryPoint | AccountDeployed(userOpHash, sender, factory, paymaster) 0xd51a9c61…; topic1 0x33c4410e…, data: factory 0xd703aae7…, paymaster 0x7777…834c evidence level: primary source |
validation: deployment | EntryPoint confirms the account was created by this op and its gas was paid by this paymaster. |
| 0x0f | Pimlico 0x7777…834c evidence level: primary source |
UserOperationSponsored(bytes32 userOpHash, address user, uint8 mode, address token, uint256 tokenAmountPaid, uint256 exchangeRate) 0x7a270f29…; data all zeros |
validation: paymaster | Mode 0 (verifying). Token, amount and rate are zero: no on-chain token charge. |
| 0x10 | EntryPoint | BeforeExecution() 0xbb47ee3e… |
boundary | Validation for every op in the bundle is done. Logs after this belong to execution. |
| 0x11 | USDC | Approval(owner=cb7509e6…, spender=4cd00e38…, 64,964,131) |
execution, leg 0 | The approve ran. |
| 0x12 | USDC | Transfer(from=cb7509e6…, to=4cd00e38…, 64,964,131) |
execution, leg 1 | Relay pulled the USDC from the account. |
| 0x13 | Relay 4cd00e38… |
RelayErc20Deposit(depositor, currency, amount, id) 0x49fed1d0… |
execution, leg 1 | Relay recorded the deposit under order id 0xe058b445…. |
| 0x14 | USDC | Approval(cb7509e6…, 4cd00e38…, 0) |
execution, leg 2 | Allowance reset. |
| 0x15 | EntryPoint | UserOperationEvent(userOpHash, sender, paymaster, nonce, success, actualGasCost, actualGasUsed) 0x49628fd1… |
outcome | See below. |
Decoding the final UserOperationEvent:
topic1 0x33c4410e4871fe14003d845bf3ad3e2e28b89e9897633541e4c8fa05af329952 userOpHash
topic2 …cb7509e6466632d73a509ec778f88d52fa9096d2 sender
topic3 …777777777777aec03fd955926dbf81597e66834c paymaster
data 00008104e3ad…1c43 0000000000000000 nonce (same as the op)
…01 success = true
…0856561a5cedbe actualGasCost = 2,346,727,623,159,230 wei
…05f2ca actualGasUsed = 389,834
How to split one transaction's logs into ops: everything before BeforeExecution is validation (deployments, sponsorship). After it, each op's execution logs end at that op's UserOperationEvent. The next op's logs start right after. The earlier decoder uses exactly this windowing (execution_window_logs).
Logs that never appear here:
- No log for the AVAX paid to the beneficiary.
_compensateis a plain value call. To see it, use a trace (epi explain --trace) or a balance diff. You can derive the amount from logs (§7). - No log for the deposit debit. The paymaster's EntryPoint deposit went down by the prefund, then up by the refund. Neither step emits an event; read
balanceOf(paymaster)via RPC if you need it. - The account's
validateUserOpand the paymaster'svalidatePaymasterUserOpemitted nothing here. Validation is mostly silent; you see it only through its effects.
6. Recomputing the userOpHash and checking the signature
The v0.7 formula:
packed = encode(['address','uint256','bytes32','bytes32','bytes32','uint256','bytes32','bytes32'],
[sender, nonce, keccak(initCode), keccak(callData),
accountGasLimits, preVerificationGas, gasFees, keccak(paymasterAndData)])
inner = keccak(packed) # b45cb9e0f096362a895411c837073c27afd1b76a7505022b1e3740200239f8f0
userOpHash = keccak(encode(['bytes32','address','uint256'], [inner, ENTRYPOINT_V07, 43114]))
# = 0x33c4410e4871fe14003d845bf3ad3e2e28b89e9897633541e4c8fa05af329952
It matches topic1 of UserOperationEvent (0x15) and AccountDeployed (0x0e). That proves the bytes we decoded are exactly the op the EntryPoint processed. It also tells you the signature, gas fields and paymaster data are the real ones, not a lookalike.
Signature check (eth_keys):
ecrecover(userOpHash, sig)→0xc87dca88…0605: meaningless, wrong message.ecrecover(keccak("\x19Ethereum Signed Message:\n32" ‖ userOpHash), sig)→0x3f98da05dc176902d47e989ecdae9a443ebbbbeb
The owner registered in log 0x0d and in initCode recovers from the signature. So the chain shows that whoever holds key 0x3f98…bbeb authorized this exact operation: deploy, approve, deposit 64.96 USDC to Relay, with these gas limits, under this sponsorship.
7. The money
| Quantity | Value | Source |
|---|---|---|
| UserOp gas price | min(8.015827452, 1.019812595 + 5.0 basefee) = 6.019812595 gwei | op's gasFees + block basefee |
| actualGasUsed | 389,834 | UserOperationEvent |
| actualGasCost | 389,834 × 6,019,812,595 = 2,346,727,623,159,230 wei (exact) | UserOperationEvent |
| Max prefund locked during validation | (318,666 + 82,407 + 46,456 + 1 + 60,222) × 8,015,827,452 = 4,070,052,420,407,904 wei | v0.7 _getRequiredPrefund |
| Refunded to paymaster's deposit | 4,070,052,420,407,904 − 2,346,727,623,159,230 = 1,723,324,797,248,674 wei | derived |
Paid to beneficiary 0x4337005d… |
= sum of actualGasCost in the bundle = 2,346,727,623,159,230 wei | derived (one op), trace would confirm |
| Bundler's receipt fee | 2,152,434,735,000,000 wei | receipt |
| Bundler margin on this tx | +194,292,888,159,230 wei (~0.000194 AVAX) | difference |
Why the bundler came out ahead: the op agreed to pay a 1.02 gwei tip on top of basefee, but the bundler only tipped 0.555 gwei to the chain. actualGasUsed (389,834) is also slightly above the receipt's 387,477, because it includes the 60,222 preVerificationGas allowance. Who funded it: Pimlico's deposit inside the EntryPoint. Who paid Pimlico: not visible on chain. Verifying mode means Pimlico or its customer settles off chain.
8. Attribution summary for this transaction
| Dimension | Answer | Evidence grade |
|---|---|---|
| Outcome | Outer success; UserOp success = true; all three legs left matching logs | Chain-proven (receipt + events) |
| Operation (requested) | batch: approve → depositErc20 → approve(0) |
Chain-proven (calldata, hash-bound) |
| Operation (realized) | 64.964131 USDC moved account → Relay Depository; Relay logged the deposit | Chain-proven on C-Chain; destination fill unknown |
| Application | Relay (cross-chain settlement) | Supported: exact address in Relay's chains API + matching event signature |
| Asset | USDC (official Avalanche) | Supported: exact-address identity record |
| Route | Avalanche → ? via Relay order 0xe058b445… |
Requested on chain; destination not on C-Chain |
| Wallet implementation | ZeroDev Kernel v3.0, ECDSA validator | Supported: factory/validator addresses in ZeroDev SDK + matching selectors/events |
| Control / signer | key 0x3f98da05…bbeb signed the op and is the registered owner |
Chain-proven cryptographically; real-world identity unknown |
| Deployment | account created in this op via meta factory → Kernel factory | Chain-proven (AccountDeployed) |
| Bundler / beneficiary | 0x4337005d… submitted and was paid |
Chain-proven; operator identity inferred at best (0x4337 vanity prefix is not proof) |
| Sponsorship | Pimlico v0.7 singleton, verifying mode, ~0.00235 AVAX of its deposit | Chain-proven that Pimlico's deposit paid; who reimbursed Pimlico unknown |
| Fees | receipt 0.002152 AVAX (bundler → chain); actualGasCost 0.002347 AVAX (paymaster → bundler) | Chain-proven; don't add them together |
| Automation | Unknown. One op tells you nothing about scripts vs humans | n/a |
The general lesson: one EntryPoint transaction answers "what did this smart account request and what happened on this chain" very well. Who the person is, what app UI they used, where the money ended up off chain, and who ultimately paid for gas are separate questions, with separate evidence.