We run a terminal in house. I have written before about where the line sits between that and the open source tracker: the public one reads and cannot move anything, the private one is where anything touching a transaction lives, and what the private side does is ours.
This post is not about what it does. It is about five things building it taught us, and one of those is a correction to something we published here and got wrong.
1. The most dangerous number is the one that looks right
We sat down with the dashboard on one screen and the actual wallet on the other and compared them line by line. That sounds obvious. We had not done it in a while, because the dashboard looked fine. It always looked fine.
It was wrong in three separate ways.
A whole chain was missing from the list, so an asset held there was invisible. Not wrong, not zero. Absent, with nothing on screen to suggest anything was absent.
Token discovery on another chain had quietly degraded to a single hardcoded entry, because the general method needed a key we did not have. Everything else held there simply did not exist as far as the app was concerned.
And the Bitcoin balance counted only confirmed output and ignored pending transactions, so a transfer made minutes earlier read as money gone.
Every one of those produced a total that was plausible. That is the entire problem. A number that is obviously broken gets fixed in an hour. A number that is quietly ten percent low gets trusted for a year, because nothing about it asks to be checked.
The fix is not cleverness. It is the boring discipline of periodically comparing what your software says against the world it claims to describe.
2. A hardcoded table is a guess wearing good posture
Some of those contract addresses and chain identifiers had been written from memory. They looked authoritative sitting there in the code.
Think about what a wrong one actually does. An address that is mistyped but happens to point at some real token returns a real balance, under a confident label that is now wrong. A wrong decimal place is a tenfold error in your total. A wrong chain identifier ends up inside a link that sends money.
None of that is visible by looking at the application. It renders beautifully.
So we stopped trusting the table and asked the source instead. The app now reads each token's own symbol and decimal count directly from the chain, converts quantities with the contract's real decimals rather than ours, and treats an address that does not answer as what it probably is: a typo. Anything whose real identity disagrees with our label still shows, but marked unverified, priced by its address rather than by the name that turned out to be wrong.
Reference data you typed in is not data. It is a hypothesis.
3. Unreachable and wrong are different states
This one took a revision to get right.
When a check fails, there are two very different reasons. Either the thing is genuinely wrong, or you could not reach the thing that would tell you. Collapse those two into one and you get false alarms every time a provider has a bad minute.
False alarms are worse than no alarms, because they train you to ignore the real one.
So a confirmed mismatch now fails closed, loudly, on the specific operation that would be dangerous. An unreachable check leaves everything visible and says it could not verify. The distinction costs a few lines and buys you an alarm you will still believe in six months.
4. Pick your provider at runtime, not at commit time
Every chain needs somewhere to ask about balances, and those endpoints go down, rate limit you, move, and occasionally just stop existing.
Hardcoding one means a single dead URL silently removes a whole chain from your totals, and you find out when you compare against a wallet, which is lesson one again.
Instead we keep a list of candidates per chain, probe them when needed, remember which one answered, recheck a working one on a slow schedule and a dead set on a fast one, and show the result on a status page. Now an endpoint that cannot be verified costs nothing. It gets skipped instead of taking a feature down with it.
5. The correction: cost basis rules are not universal
Here is the one we got wrong in public.
We published a post explaining that our open source tracker moves cost basis across a coin-to-coin trade using average cost, and we said that was the treatment most tax regimes apply. That sentence was wrong for the United States, which is where most of our readers are.
Average cost is not a permitted method for crypto in the US. It is used in some other countries, notably Canada, which is probably where the assumption came from. Since the first of January 2025, the only accepted methods for digital assets are first in first out, which is the default, and specific identification with records made at the time of the trade rather than reconstructed later.
We are fixing this in both places. The earlier post carries a correction at the top pointing here, and the open source tool is moving to first in first out. The in-house terminal already worked that way, which is the irony: the private version had the correct accounting and the public explanation of it did not.
I would rather write this paragraph than quietly edit the old one and hope nobody noticed. A correction you can see is worth more than a page that was never wrong.
What none of this is
There is nothing advanced in this post. No cryptography, no consensus, no algorithm anyone would name.
It is bookkeeping, error handling, and the ongoing refusal to trust your own earlier work. Every real problem we found came from assuming something we had written down was still true, and the fix every time was to go ask something that actually knows.
Gunnar
Founder, Tofte Ventures LLC
Sources
- Internal Revenue Service guidance on digital asset cost basis methods, as summarised by Chainwise CPA and CoinTracker.