Reading a Transaction History: Deltas, Not Summaries

An interface tells you what it thinks a transaction did. The transaction tells you what changed. Those two are usually the same and the gap between them is where most wrong conclusions start, so this page reads a history the slow way: account lists, instruction order, and the balances recorded immediately before and immediately after execution.

02Field noteFollowing a walletThe Trace Rack Desk2227 words11 minUpdated 4 September 2026

Applies to
Any confirmed Solana transaction, successful or failed, on any cluster
Inputs
The transaction object: account keys, instructions, logs, pre and post balances, fee
Output
A per-transaction line stating what moved, which programs ran, and what the fee was
False-positive mode
Trusting a parsed summary that mislabels an instruction the parser did not recognise
Out of scope
Inferring purpose or ownership from instruction content

Read a Solana transaction history from the transactions themselves: for each signature, compare the account balances recorded immediately before execution with the balances recorded immediately after, note which programs ran and in what order, and record the fee separately from the flow. Summaries produced by an interface are a convenience layer built on those numbers, and they inherit every assumption their author made.

This is not a purist argument. Parsed views are fast and mostly right. The problem is that they fail silently on exactly the transactions worth investigating, which are the unusual ones, and a silent failure in a summary reads as a boring transaction rather than as a gap.

What a transaction actually contains

A confirmed transaction is a small structured object. It carries a list of accounts it may read or write, one or more instructions naming a program and the accounts that program may touch, one or more signatures, a recent blockhash that bounds its validity, and after execution a result object containing the fee, the logs, and arrays of balances from before and after.

The account list is more informative than it looks. Every account a transaction touches must be declared in advance, marked as writable or read-only and as a signer or not. That declaration is a compact statement of what the transaction was permitted to affect, and reading it first tells you the scope of what follows before you decode a single instruction.

The fields of an executed transaction that carry analytical weight, what each one is good for, and the mistake commonly made when reading it.
FieldWhat it tells youCommon misreading
Account keysEverything the transaction was allowed to touch, and which of those it could writeTreating every listed account as a participant when many are read-only references
SignaturesWhich keys authorised it; the first signer normally pays the feeAssuming the fee payer is the beneficiary of the transaction
InstructionsThe programs invoked, in order, with their account slicesReading only the top level and missing everything done through inner calls
Pre and post balancesExact SOL movement per account, in lamportsForgetting the fee is included in the payer's delta
Token balance arraysToken account balances before and after, with mint and ownerComparing raw integers across mints with different decimals
Log messagesProgram invocation order, success or failure, program-emitted detailTreating log text as a specification when it is whatever a developer chose to print
Error fieldWhether execution failed and which instruction index failedIgnoring failures, which removes the most informative transactions from view

Balance deltas are the ground truth

The pre and post balance arrays are the closest thing to an unarguable record of what a transaction did. They are aligned with the account key list by index, so the SOL movement for account number three is the third entry of the post array minus the third entry of the pre array, in lamports, with one SOL equal to 1,000,000,000 lamports.

Token balances get their own arrays, and these are richer because each entry carries the mint, the owner and the decimals alongside the amount. That means you can compute a token delta without knowing anything about the program that produced it, which is exactly what you want when the program is unfamiliar or when the transfer happened several layers down inside a routing call.

Work in the smallest unit and convert once, at the end, for display. Converting early and comparing rounded values is how two token movements that differ by a rounding step end up recorded as identical, and identical amounts are one of the signals people later use to argue that two accounts are related.

Instructions, inner instructions and logs

The instructions you can see in the signed message are only the outer layer. When a program calls another program, a mechanism the documentation calls cross-program invocation, the network records those calls as inner instructions attached to the index of the outer instruction that caused them. In a routed swap, the outer instruction is usually one call to a router, while the actual token movements live in a nest of inner transfers beneath it.

Logs sit alongside and record the invocation sequence: program invoked, depth, success, consumed compute units. They are useful for reconstructing order and for spotting a program you did not expect in the path. They are not a reliable description of semantics, because their content is whatever the program's authors decided to emit, and it changes between versions without notice.

The practical rule: use logs and instructions to understand structure, use balance deltas to establish effect. When those two disagree, the deltas win, and the disagreement itself is worth a note because it usually means something in the path did more than its name suggests.

Failed transactions are still evidence

Analysts routinely filter failures out and lose the most informative part of a history. A failed transaction landed in a block, paid its fee and produced logs up to the point of failure. It tells you what an operator was attempting, how often, and which error stopped them, and attempts are frequently more revealing than successes.

Two patterns are worth recognising immediately. A run of failures with a slippage error suggests an account repeatedly trying to trade against a market moving faster than its tolerance. A run of failures on account creation or insufficient funds suggests an operator whose funding did not keep up with their scheduling. Neither pattern names anyone, and both change how you read the successes around them.

Why the failure ratio matters to a total

If an account attempted many transactions and only some landed, then any total you compute from successes alone describes outcomes rather than behaviour. State which one you are measuring. A finding that says "this account executed forty swaps" is a different claim from "this account attempted sixty swaps, of which forty landed", and only the second one survives contact with the raw history.

Fees, rent and the arithmetic of noise

A large share of the small SOL movements in any history is not activity at all. Solana charges a base fee of 5,000 lamports per signature, plus an optional priority fee equal to a compute unit price multiplied by a compute unit limit, where the price is denominated in micro-lamports per compute unit. Both are paid by the fee payer whether the transaction succeeds or fails.

The arithmetic below is illustrative and uses protocol constants only. Take a transaction with one signature, a compute unit limit of 200,000 and a compute unit price of 20,000 micro-lamports. The priority component is 200,000 multiplied by 20,000 micro-lamports, which is 4,000,000,000 micro-lamports, or 4,000 lamports. Add the 5,000 lamport base fee and the transaction costs 9,000 lamports, which is 0.000009 SOL.

Across one hundred such transactions that is 900,000 lamports, or 0.0009 SOL, of pure cost appearing as a hundred small debits in the history. Separately, opening a token account requires a rent-exempt deposit, on the order of 0.00204 SOL for a standard token account, which returns to the owner if the account is later closed. Neither of these is flow, and counting them as flow is a routine way to inflate an account's apparent turnover.

Parsed labels are interpretations

Explorers and indexers show helpful strings: "Swap", "Transfer", "Unknown", "Jupiter", "Raydium", "Deposit". Each of those is a decoder's opinion about an instruction, produced by code someone wrote against a program interface at a particular time. When the program updates, or when a new program appears, the decoder falls back to raw data and the interface displays nothing interesting.

Treat every label as a claim with an author. That does not mean discarding them; it means recording the label and the underlying evidence separately, so that a finding rests on "these balances changed in this direction" rather than on "the explorer called it a swap". This distinction is the whole subject of labelling what you find, which extends the same caution from tool labels to the labels you write yourself.

Reading a history as a sequence

Once individual transactions are readable, the history becomes a time series and different questions open up. Group the signatures into sessions by looking for gaps: a burst of activity separated from the next burst by hours of silence is a session boundary, and sessions are a more natural unit of analysis than days.

Within a session, look for the opening move and the closing move. Accounts driven by hand often begin with a small test transaction. Accounts driven by software often begin with an account creation or an approval and end abruptly mid-pattern when a process stopped. Neither observation identifies anyone; both change what the sequence is likely to mean.

Across sessions, watch for changes in rhythm rather than in volume. A change in priority fee settings, a new program appearing in the path, or a shift from one venue to another usually indicates a configuration change by whoever operates the account. That is a real, dateable event, and it is often the most defensible finding a history produces.

Human rhythm and machine rhythm

Software leaves a rhythm. Intervals cluster tightly, sizes repeat or follow a simple rule, instruction sequences are identical transaction after transaction, and the compute budget instruction carries the same values every time. Human activity is lumpy: irregular gaps, round-number amounts, occasional obvious mistakes, and long silences that do not align with anything.

Distinguishing the two changes your reading, but be careful with the conclusion it invites. An automated Solana volume bot operated openly by a project produces the same tight intervals and repeated instruction sequences as any other scheduled process, so rhythm establishes that a process is running and nothing more. Whether that process was disclosed, and by whom, is not recorded anywhere on chain.

The false-positive mode of rhythm reading

Two unrelated operators running the same widely distributed software produce nearly identical rhythms, because the rhythm belongs to the software rather than to the operator. Every conclusion of the form "these accounts share a controller because they behave the same way" has to survive that alternative, and in a market where a handful of tools dominate, it very often does not.

Reconciling a history against a claim

Most of the time you are not reading a history for its own sake. Somebody has said something about an account and you want to know whether the record supports it. Reconciliation is the cleanest use of transaction reading, because the claim supplies the question and the deltas supply a yes, a no, or a specific reason the question cannot be settled.

The procedure is short. Restate the claim as something countable: an amount, a count of transactions, a date boundary, a venue. Pull the signatures that fall inside the relevant window. Compute the quantity directly from deltas rather than from any precomputed total. Then compare, and record the difference along with a note on what could account for it before deciding the claim was wrong.

The usual sources of an honest discrepancy are worth listing, because they explain most gaps without anyone having misled anybody. Fees and rent deposits counted as flow; failed attempts included or excluded; token accounts opened and closed inside the window; wrapped SOL round trips read as deposits; transfers routed through an intermediate account so one movement appears as two; and time zones, where a day boundary in local time cuts a session in half against a UTC reading.

If none of those close the gap, the honest output is still narrow: the amount computed from the record differs from the amount claimed, by this much, over this window, using this definition. That sentence is defensible. Any sentence about why the numbers differ is speculation unless someone off chain explains it.

The per-transaction checklist

  • Record the signature in full and the block time in UTC.
  • List the programs invoked in order, including inner instructions.
  • Compute SOL deltas per account in lamports, noting which account paid the fee.
  • Compute token deltas per token account, keeping mint and decimals attached.
  • Note the success or failure status and, if failed, the failing instruction index.
  • Separate fee and rent from flow before any total is computed.
  • Record any parsed label as a label, next to the evidence rather than instead of it.
  • Write one sentence describing the net effect, using only what the deltas support.

What a history will not tell you

A complete, correctly read history still leaves the important questions open. It does not say who signed, whether one person or a team held the key, whether the key was compromised, or what agreement sat behind a transfer. It does not distinguish a purchase from a repayment, a gift from a payment, or a mistake from a plan, because those distinctions live entirely off chain.

It is also bounded. Your endpoint holds a window of history, an interface may cap what it displays, and archival access changes what exists to be read. Before writing that an account did something for the first time, or stopped doing something, confirm that the boundary you are describing is the account's boundary and not your query's. The failure modes that follow from ignoring that are collected in where a trail goes cold.

What you are left with is genuinely valuable: a dated, verifiable sequence of effects that anyone can reproduce from the same signatures. That is the raw material every other method on this site consumes, and no amount of clustering, labelling or funding analysis is better than the transaction reading underneath it.

Questions this page gets asked

What is the difference between a transaction and an instruction on Solana?

A transaction is the signed unit that lands in a block. Inside it sits an ordered list of instructions, each naming a program to call, the accounts that call may touch, and a blob of data. One transaction can carry many instructions across several programs, and they either all succeed or the whole transaction fails and none of them take effect.

Why does a transfer show up as two different amounts?

Usually because you are comparing a token amount to a raw amount. Token balances are integers scaled by the mint decimals, so a display value of 1.5 for a six-decimal token is stored as 1500000. Native SOL has nine decimals, meaning one SOL is 1,000,000,000 lamports, and mixing the two scales is a routine source of nonsense totals.

Do failed transactions cost anything?

Yes. A transaction that lands and then fails during execution still pays its fee, because the network did the work of processing it. That is why a history full of failures still shows a steadily draining SOL balance, and why counting only successful transactions understates how much an account was actually attempting.

What are inner instructions?

They are the calls a program makes to other programs while handling your instruction, recorded separately from the instructions you signed. Most real value movement in a swap happens in inner instructions, so a reading that looks only at top-level instructions will see a call to a router and miss every transfer underneath it.

Can a transaction move tokens without an obvious transfer instruction?

It can look that way if you read only the instruction list, because a program may move balances through cross-program calls that appear as inner instructions rather than as a transfer you signed. This is precisely why balance deltas are the reliable reading: they capture the net effect regardless of which layer produced it.

Why do explorers sometimes label a program as unknown?

Because parsing depends on someone having written a decoder for that program. A new or uncommon program shows up as raw data, and the transaction will look empty even when it did something substantial. Unknown means unparsed, not inactive, and the balance deltas will still show what changed.

How do I tell a swap from a plain transfer?

Count the directions. A transfer moves one balance down and another up in the same asset. A swap shows one token account decreasing and a different mint increasing for the same owner within one transaction, usually with a pool or router program in the instruction list and several inner transfers underneath.

Filed under Following a wallet by The Trace Rack Desk. Addresses in the examples are placeholders written as letters rather than base58, so nothing here points at a real account. Behaviour described comes from protocol documentation and from queries the desk can run against public data; arithmetic is labelled as illustrative and describes no real wallet. Scope and refusals are set out in the casework note.

Read next