Somewhere in your crypto tax report there may be a row where the software claims you paid nothing for a coin you remember paying real money for. The proceeds column has a number; the cost basis column has a zero; the gain column has the entire sale. That single zero is the seed of a phantom gain — and if you file without questioning it, it becomes real tax.

This guide is about the zero itself: not why basis matters, but the specific mechanisms by which a $0 ends up printed in a report. There are five of them, and they are worth knowing individually, because each one leaves a different fingerprint in your data — which means each one can be verified, and repaired, in a different way. None of them are defects of one vendor. They are structural properties of how crypto records move from chains, exchanges, and files into a tax calculation, which is why the same five mechanisms show up in every tool on the market.

This is not tax advice. It's an explanation of how the data pipeline behaves, with links to primary sources. For decisions about your own return, talk to a tax professional.

Where the zero comes from

Every crypto tax tool performs the same core matching job: for each coin you sold, find the acquisition that explains what you paid for it. When the match succeeds, your gain is sale price minus that cost. When it fails — the software sees a disposal but no acquisition behind it — most tools do the conservative thing and assign a cost basis of zero. Koinly, for instance, documents this default explicitly, and the behavior is standard across the category.

The zero is honest, in a narrow sense: the software won't invent a purchase price it can't see. But it means the question that decides your tax bill is not "what did I pay?" — it's "did the acquisition record survive the trip into the report?" Here are the five ways it doesn't.

Mechanism 1: A transfer split into two unrelated events

You move coins between two wallets you own. Nothing taxable happened — same owner, same coins. But a blockchain doesn't record ownership intent; it records an outflow from one address and an inflow to another. Unless the tool recognizes both addresses as yours and pairs the two legs as one transfer, it books two unrelated events: a disposal on the sending side and a from-nowhere deposit on the receiving side. The deposit has no acquisition behind it, so the coins it delivered carry a $0 basis into every future sale.

The structural reason this happens everywhere: pairing is inference, not fact. The tool has to guess that a 1.5 ETH withdrawal and a 1.499 ETH arrival minutes later are the same move — amounts differ by a network fee, timestamps differ by block times, and one side may have been imported by CSV while the other came from an API. Every tool draws its matching tolerances somewhere, and every tolerance boundary drops real transfers. The full anatomy of this failure is in our guide to why basis breaks when you move crypto between your own wallets.

How to verify it yourself: take any flagged or zero-basis deposit and look for its twin — a withdrawal of approximately the same amount of the same asset, from another account you own, within minutes or hours. If the twin exists, the acquisition record isn't missing; it's stranded on the other side of an unpaired transfer.

Mechanism 2: Coins that arrive at an exchange without their history

Send coins from self-custody to an exchange and sell them there, and the exchange's records begin at the deposit. The exchange genuinely does not know what you paid — it wasn't there. That ignorance now has a federal form: on Form 1099-DA, assets transferred in to a broker are generally "noncovered," meaning the broker isn't required to report basis for them at all, and the form has a dedicated box (12a) counting the units that arrived with an unwitnessed past.

The same logic runs inside your tax software when it imports the exchange's data. The exchange-side export describes a deposit with no cost attached. If the tool can't connect that deposit to your own on-chain history — mechanism 1 again, at the exchange boundary — the coins sell with a $0 basis, and the exchange's report will agree with the error rather than correct it.

How to verify it yourself: for each exchange where you sold, list the deposits that preceded the sales. Each deposit should trace back to a wallet you've imported into the tool. A deposit whose source address is nowhere in your account list is an acquisition record the software cannot possibly have.

Mechanism 3: A CSV that shifted, mangled, or doubled its rows

File imports fail quietly, and they fail in three characteristic ways.

First, column and format drift. There is no standard schema for exchange exports — every platform names, orders, and formats its columns differently, and formats change over time. A date column read as DD/MM instead of MM/DD silently moves an acquisition months away from where it happened, far enough that transfer matching (with its timestamp tolerances) stops recognizing it. A decimal comma read as a thousands separator turns 1,5 ETH into 15. A renamed column simply drops from the mapping, taking its data with it.

Second, partial rows. Some export formats omit whole transaction types — fiat purchases, rewards, internal conversions — so the file imports "successfully" while missing the exact rows that establish basis.

Third, and most underrated: duplicates. Import the same file twice, or overlap a CSV with an API sync covering the same period, and disposals double. The tool now believes you sold more than you ever held. The excess has no acquisition to match by definition — Koinly's help center describes its "missing purchase history" warning as exactly this state, selling more than you owned at the time of the disposal based on the imported records — and doubled acquisitions are no better, silently diluting your average cost in the other direction.

How to verify it yourself: two checks. Compare the tool's row count for an account against the source file's row count. Then look at the tool's computed balance for each asset: a negative balance, or a balance that exceeds what the wallet actually holds, is the signature of dropped or doubled rows respectively.

Mechanism 4: A bridge that severed the trail

Bridge an asset to another chain and the on-chain record usually shows one asset vanishing on the origin chain and a different asset appearing from nowhere on the destination chain. No transaction on either side says "these are the same funds" — the two legs share no identifier, often not even an address, and the destination asset is frequently a different token contract entirely (wrapped, canonical, or pool-minted). Coins born this way have no history a chain-reader can find, so they enter the report at $0.

How to verify it yourself: if a zero-basis asset's first appearance in your history is a mint or transfer from a contract address shortly after you used a bridge, you've found the severed trail. The acquisition record exists — on the other chain, attached to the asset you gave up.

Mechanism 5: A history that starts in the middle

Every import has a first day, and everything before it doesn't exist as far as the tool is concerned. This happens more ways than people expect: exchange APIs with hard time windows (in one documented forum case, an exchange's API returned only recent trades, leaving 34 of a user's 206 transactions without purchase history), export tools that only reach back a fixed number of months, exchanges that shut down and took their history with them — or simply connecting a tax tool in 2026 to a portfolio you started building in 2020.

A mid-history start produces a distinctive result: your opening balance. Coins you already held on day one of the import are all disposal and no acquisition. Sell any of them and the sale matches nothing, so it books at $0 — and because the true acquisitions are your oldest, the zeros land precisely on the coins with the largest real gains at stake and the strongest claim to long-term treatment.

How to verify it yourself: for each connected account, compare the date of its earliest imported transaction against the date you actually started using that account. Any gap between the two is unrecorded history, and every coin you held during the gap is carrying a provisional zero.

Why every tool does this — and what that means for you

It's tempting to read a report full of zeros as a broken product, but the zero is the defensible engineering answer to a genuinely missing record. The alternative — guessing a purchase price — would be worse. Where tools differ is in how loudly they tell you: some flag every affected row, some flag only balance-impossible cases, and warnings can be dismissed without the underlying zero going anywhere. The category-wide evidence says the gap between warning and reality is wide: when a college professor ran identical transaction data through five crypto tax platforms, the computed results ranged from a $1,516 loss to a $2,696 gain against a true answer of a $1,266 loss. Same data, five different survivals of the acquisition records.

The five mechanisms are also why the fix is rarely "type in a number." A zero from mechanism 1 or 3 is repaired by pairing or de-duplicating records the tool already has; a zero from mechanisms 2, 4, or 5 is repaired by importing the history that's missing. We've published a tool-agnostic repair checklist that works through exactly that triage, and a remediation guide for carrying the corrected numbers through to filing.

If you'd rather start from the chain than from the report, the verification steps above can be run for you: run my free scan to see which transfers between your own Ethereum addresses would be read as zero-basis disposals — read-only, no sign-up, with a tx-hash on every line so you can check the work.

FAQ

Why does my crypto tax software show a $0 cost basis?

Because it found a disposal it cannot match to any acquisition in the data it was given. The five common reasons: a transfer between your own wallets that wasn't paired, coins sent to an exchange whose history starts at the deposit, a file import that shifted or duplicated rows, a cross-chain bridge that severed the asset's trail, or a history imported from a mid-point so that older acquisitions were never seen. The software assigns zero rather than guessing a price.

Is a $0 cost basis ever actually correct?

Rarely, and the IRS instructions for Form 1099-DA make the same distinction: brokers are told to enter -0- only when the asset genuinely had a zero basis, not when the basis is unknown. Most coins were acquired at a real cost, or were taxable as income at fair market value when received — which itself establishes a non-zero basis. Whether any specific coin of yours truly has a zero basis is a records question for a tax professional, but a report full of zeros is almost always describing data gaps, not reality.

How do I find every $0 cost basis transaction in my report?

Use both ends. In the tool, filter transactions by its warning flags and sort disposals by proceeds, checking the basis column of the largest ones — some zero-basis sales never raise a flag. Then verify against the source: match flagged deposits to their withdrawal twins, compare per-asset balances against actual wallet balances, and compare each account's earliest imported transaction against when you really started using it. Each mechanism above leaves one of those fingerprints.


This is not tax advice, and none of the above is a substitute for a qualified tax professional who can look at your actual records.

If you'd rather have the five mechanisms checked against your actual chain history, Verilot Check reads your Ethereum wallets directly on-chain and flags every transfer between your own addresses that would otherwise be booked as a zero-basis disposal, with a tx-hash on every line. Free and read-only — no wallet connection, no sign-up, up to 5 wallets and 500 events per scan, on Ethereum mainnet, Base or Arbitrum One (one chain per scan). Run my free scan.