A smart-contract wallet, also called a smart account, is an on-chain contract that the user controls, not a single private key. Every action shows up as a UserOperation routed through a shared EntryPoint contract, sponsored by an optional paymaster, and bundled by a relayer. Reading the on-chain trail means learning to parse those layers instead of treating them as one normal transaction.
Key takeaways
- A smart account replaces a key with a contract, so every action routes through a shared EntryPoint rather than executing directly.
- A UserOperation is a pseudotransaction with its own fields: sender, nonce, initCode, callData, paymaster, and signature.
- Sessions, also called session keys, are scoped sub-approvals with limits, expiry, and a target contract list that must be audited individually.
- Paymasters and bundlers sit between you and the chain, and a malicious or compromised one can reorder, front-run, or censor your operations.
- Revocation is mostly on-chain: rotating the signer or burning the session key is what truly kills delegated access.
Why a smart account is a different mental model
If you have ever copied an address, checked it on a block explorer, and tried to decode what a transaction did, you are starting from the assumption that one key controls one account. Smart-contract wallets break that assumption. The address you see on Etherscan or a similar explorer is not a key. It is a contract. The contract owns assets, enforces rules, and decides which instructions to accept. The user, more accurately the set of keys or sessions currently allowed to act, sends instructions to that contract through a separate pipeline.
This is why a single on-chain action often looks noisy on a block explorer. What appears as one transaction is, internally, several layers: the user's signed intent, a relayer that submits it, an EntryPoint contract that validates it, optional paymaster logic that pays gas, and the callData that runs the actual action inside the smart account. The shape on chain is unfamiliar at first, and that unfamiliarity is exactly what attackers rely on.
Transaction versus UserOperation: the core distinction
A normal Ethereum transaction has six readable fields: from, to, value, data, gas limit, and signature. A UserOperation, defined under the ERC-4337 standard, adds fields designed for delegated and sponsored execution. The ones that matter most for reading are: sender, which is the smart-account address; nonce, which is per-account and not tied to the global transaction count the way an EOA nonce is; initCode, which deploys the account on first use; callData, which is what the smart account will run if approved; and signature, which may be an ECDSA signature, an aggregated BLS signature from a bundler, or a validator hook defined by the account itself.
There is also a gas-payments overlay absent in plain transactions. paymasterAndData points to a paymaster contract that will reimburse the bundler. The paymaster is allowed to inspect your UserOperation before agreeing to sponsor it, and in some implementations it can alter the postOp step, which runs after your call completes. That post-op callback is a real attack surface: a malicious paymaster can read the return data of your call and use it in ways you did not authorize. Treat paymasters the way you treat any counterparty, because in a strict sense that is what they are.
Reading a UserOperation therefore means checking at minimum four things: that sender points to a contract you actually own or expect, that callData targets addresses you authorized (a session or a guardian-approved whitelist), that the gas parameters are not absurdly high, and that the paymaster is one you trust to behave.
The EntryPoint contract: shared infrastructure risk
Every ERC-4337 smart account on Ethereum mainnet shares a single EntryPoint address at the time of writing. The standard explicitly permits multiple EntryPoint versions, but in practice one canonical deployment carries most activity. This centralization is a deliberate design choice: a shared EntryPoint lets bundlers, paymasters, and account implementations interoperate without per-pair integrations. It also concentrates risk.
If the EntryPoint is upgraded, taken over by a governance vote, or found to contain a vulnerability, every smart account routed through it is exposed at once. That is not a hypothetical. In late 2023, an audit-by-auditor incident around an EntryPoint-related library was disclosed and patched across integrations. Nothing was lost at scale, but the lesson held: shared infrastructure is a single point of failure, and your account's safety is only as strong as the weakest component in that stack.
Reading on-chain data should therefore include a quick check that the EntryPoint your account routes through is the version you expect. Mainnet and most L2s publish canonical EntryPoint addresses, and tools such as Etherscan label the audited deployments. If you see a UserOperation being sent to a non-canonical EntryPoint, treat that as a strong warning sign: it could be a phishing relayer or a fork designed to capture signatures.
Session keys: scoped, time-limited sub-approvals
Sessions are the feature most worth understanding, because they are also the part most often abused. A session key is a secondary signer you grant to a specific application for a defined window of time, with a defined set of permissions. In practice this looks like a dApp asking for permission to trade on Uniswap on your behalf for the next hour, up to a spending cap, against an allowlist of token contracts.
On-chain, a session lives as state inside your smart account. The relevant fields, in the order they tend to appear in storage, are: an authorized session key address; an expiry timestamp or block number; a spend limit per period (often daily or per-epoch); a target allowlist or selector allowlist; and a policy hook that may apply additional rules. Implementations vary. Safe Modules, Zodiac Heads, Biconomy sessions, and Kernel session validators all encode these slightly differently, and the exact slot layout matters when you read storage directly.
The point to remember is that sessions are bounded but not safe by default. A session scoped to Uniswap's router is only as good as Uniswap's router. A session that grants an allowlist of selectors can still hit dangerous ones if the application knows which selectors to call. And sessions that do not enforce a period-reset spend limit can drain funds over time once the user stops paying attention.
Two practical habits help. First, read the exact storage slot or decoded view that the wallet UI shows before you sign the session grant, and treat it as the source of truth, not the dApp's marketing copy. Second, prefer sessions with both a per-period cap and a per-call cap, so a single runaway call cannot exceed a sane maximum.
Reading a single UserOperation in detail
Imagine you see a hash 0xabc... in your wallet and want to know what it actually did. Open the transaction on a block explorer, then click through to the EntryPoint transaction whose callData starts with handleOps. The internal transactions tab becomes the real story.
Step 1: validateUserOp
The EntryPoint first calls back into your account's validateUserOp function. This is where the signature is checked, nonces are validated against replay protection, and any session policies are enforced. A failed validation reverts the whole bundle, so a successful run is evidence that at least one valid signer approved the operation.
Step 2: execute
If validation passes, the EntryPoint calls your account's execute function with the callData you provided. This is the user-visible action: a swap, a transfer, a contract interaction. Reading the callData of this internal call gives you the actual target, value, and function selector the user wanted.
Step 3: postOp
After execution, the paymaster's postOp runs, which is used for things like settling gas debts, refunding unused gas, or paying the bundler. Any state change here was authorized by your paymaster choice, not by you directly. Anything weird that happens here, such as extra token transfers or fresh approvals, is a sign to investigate further.
Putting those three layers together tells you who authorized what, what ran, and what side effects occurred after the fact. Most phishing patterns become visible at this level once you know what to look for.
Paymasters and bundlers: visibility and trust
A bundler is the party that takes your UserOperation, packages it with others, and submits the actual on-chain transaction to the EntryPoint. Because the bundler is the one calling EntryPoint.handleOps, the bundler's address is what pays gas at the chain level. You, as the smart-account owner, never need ETH to pay gas if a paymaster is willing to sponsor. That is convenient, and it is also a trust relationship.
A paymaster's commitment is encoded on-chain in two ways: the stake it has deposited with the EntryPoint, which can be slashed for misbehavior under the standard's rules, and the policy of its validatePaymasterUserOp function, which decides whether to sponsor. Slashing exists, but it is a coarse backstop, not a fine-grained guarantee. A paymaster can still observe, censor, or front-run the operations it sees.
Front-running is the most under-discussed risk. A paymaster that sees your swap before it executes can copy the trade to its own address, run it on the same block, and capture the price improvement you expected. This is not theoretical; it is the same dynamic as miner-extractable value, repackaged for the account-abstraction world. Mitigations exist (private mempools, commit-reveal schemes, signed-intent layers), but they require active use. Default public bundlers and paymasters offer no such protection.
Reading on-chain data helps here too. If you consistently see your operations land in the same slot number or just after a specific address, that bundler or paymaster is observing you closely. Switching to a private mempool, or using paymasters that explicitly commit to non-front-running policies and have a verifiable track record, is a real upgrade in safety.
Revoking sessions and permissions on-chain
The most common mistake smart-account users make is treating a session as if revoking it in the dApp's UI is enough. It usually is not. The dApp's UI may simply forget about the session while the on-chain grant remains active until expiry. Until the smart-account contract enforces an inactivity timeout, or until you actively revoke, the session key can still sign operations that the EntryPoint will accept.
True revocation has three practical forms. The first is to call the smart account's revoke function, if one exists, with the session key's identifier. The second is to rotate the account's main signer, which invalidates any session whose authority was tied to the signer. The third is to let the session expire naturally, which works only if you set a short enough window in the first place.
Reading on-chain revocation means checking two things: the session storage slot, which should reflect the revoked or expired state, and the account's nonce sequence, which should no longer be advancing for the revoked key. If you see a session key still signing operations after a supposed revocation, that is the moment to pause and investigate. It could be a stale UI, or it could be a session that was never properly scoped and is now exploited.
How to follow smart-account activity the smart way
Smart-account activity moves fast, and the on-chain trail only makes sense once you understand the layers underneath. Tracking who routes your operations, which paymasters sponsor them, and what sessions are still live is a losing game if you do it by hand. Zippfeed surfaces wallet and protocol headlines with sentiment scoring (bullish, neutral, or bearish) and an importance rating, so you can spot the patterns behind the noise before your next signature.