Balance at 14:00 UTC Does Not Exist
"What was the balance of this address at 14:00 UTC on June 30th?"
Every audit asks this. Every quarterly statement implies it. Every ledger-vs-chain reconciliation starts with it. And no blockchain can answer it, because a chain never closes. There is no end of day, no statement cut-off, no snapshot. Blocks arrive when they arrive, and "balance at a point in time" is a concept from accounting, not from consensus.
So the number has to be derived. The derivation is harder than it looks, the naive versions go wrong in production, and one property separates an answer you can defend from a number your script printed once.
The shortcuts fail first
Both obvious answers are dead ends.
"Check the explorer." Explorers disagree with each other, apply their own interpretation of internal transfers and token events, and mostly cannot answer historical questions at all. And an auditor cannot re-run an explorer. Whatever you saw on a website is not evidence, it is a screenshot.
"Query the node." Closer, but the default node has no memory. On EVM chains, balance-at-block-N needs an archive node; the standard RPC you are paying for keeps recent state only. On Solana it is worse: the RPC will not give you account state at an old slot. If you did not capture it when it was current, you are reconstructing it from transaction history. Bitcoin keeps everything, but what it keeps is UTXOs, and turning a UTXO set plus a descriptor into "the balance of this customer" is its own project.
The anchor problem
Before deriving anything, you have to decide what "14:00 UTC" even means on each chain, and that decision has more edges than any other part of the problem.
A timestamp does not name a block. You have to pick one: the last block at or before
the cut-off. On Bitcoin, block header timestamps are not even monotonic - a block can
carry an earlier timestamp than its parent - so "the block before 14:00" needs a
defined rule, not a naive binary search. On EVM you get to choose between the head of
the chain and the finalized tag, and only one of those survives a reorg.
On Solana you are choosing a slot and a commitment level.
Then comes the part that most home-grown scripts skip entirely: the anchor has to be consistent across chains. A portfolio holds BTC, three EVM chains and Solana. If each chain's balance is read at whatever height the script happened to reach, the "total at 14:00" is five readings taken at five different times, presented as one number. For a trading desk that might be tolerable. For a safeguarding report it is wrong by construction.
Per-chain, the ways the derivation lies
Once the anchor is fixed, each chain family produces a wrong number in its own way.
- Bitcoin: balances live in UTXOs, and addresses come from descriptors and xpubs with gap limits your scan must respect, or you silently miss addresses that hold money. One transaction can pay the same address three times; count transactions instead of outputs and you undercount. Coinbase outputs are unspendable for 100 blocks, which matters if you are valuing what is actually available.
- EVM:
tx.tolies. ERC-20 balances needTransferlogs, native ETH moved by internal calls only shows up in traces, and fee-on-transfer tokens make the event value wrong by design - the delta is the truth. Proxies change semantics under your feet on upgrade day. I wrote about the deposit-side version of this in Why Your Deposit Detection Misses Money; every one of those failure paths also corrupts a historical balance. - Solana: SPL deposits land on Associated Token Accounts, not on the owner address. A scanner with EVM instincts sees literally nothing. Add rent, account closures and token-2022 extensions, and "the balance of this wallet" becomes a question about a set of accounts, not an address.
Reproducibility is the actual requirement
One property separates infrastructure from a script: run the same derivation twice, get the same bytes. Same inputs, same anchors, same output.
Getting there means integer base units everywhere, because floating point will disagree with itself across runs. It means canonical ordering and serialization, because "the same data" in a different key order fails a hash check. It means recording the full input set - addresses, descriptors, anchors, even which endpoints served the data - and hashing it, so that "we derived this from that" is a verifiable claim instead of a sentence in an email.
Why does this matter outside of engineering aesthetics? Because the question at the top of this post is usually asked by someone whose job is to not take your word for it. EU safeguarding rules now expect crypto-asset service providers to be able to demonstrate client asset segregation at any time and without delay. An answer you cannot reproduce is an answer you cannot demonstrate. "Our script says so" and "here is the derivation, run it yourself" are different products, and only one of them survives an audit.
What good looks like
If you are building or buying this capability, the checklist is short:
- A defined timestamp-to-anchor rule per chain, below the finality threshold, with the anchor recorded in the output.
- One consistent as-of point across every chain in the portfolio.
- Derivation that respects each chain's semantics: descriptors and gap limits, logs plus traces plus deltas, token accounts - not one mental model stretched across three architectures.
- Deterministic output: integers, canonical serialization, an input hash. Re-running it is the test.
- Explicit failure when the data source cannot support the question - an RPC that no longer serves the needed history should produce an error, not an approximation.
This is the input side of every ledger-vs-chain reconciliation: before you can compare the books against the chain, you need the chain's side of the story at a defined moment, in a form someone else can verify. I have been building exactly this across Bitcoin, EVM and Solana, and it is also part of what I offer as an engagement - because in most teams this problem is discovered the week before an audit, which is the most expensive possible time to discover it.