Before the Tax Software: What a Classification Layer Should Hand You for Every DeFi Hash
Most DeFi engagements break at the same seam: the point where raw on-chain activity becomes a row your tax software will accept. We built CryptoTaxEdge to sit on that seam and do one job there. This post is about the job: what belongs on each record at the hand-off, what deliberately does not, and how a reviewer should read the record before anything moves downstream.
A seam, not a pipeline
Picture the work in three layers. At the bottom is the chain itself: receipts, logs, token movements, contract calls. At the top is the software that holds lots, computes basis, and produces the forms. The firm's preparer and reviewer own the judgment that connects them.
The middle layer answers a narrower question than people expect: what was this transaction, and which US tax treatment does that event map to? It does not hold the client's lots, it does not know what they paid for anything, and it should not pretend to. When a tool in that middle position starts producing proceeds, cost basis, or gain and loss figures, it is doing arithmetic on inputs it never had, and the errors land quietly in a column nobody checks.
So we draw the line hard. CryptoTaxEdge returns a classification per hash. Basis tracking, gain and loss, holding period, and the filing position stay with your tax software and your preparer. We work alongside Koinly and CoinTracker, not in place of them.
The five things on the record
Enter a hash in the Explorer or call POST /v1/classify with a chain and a hash, and the record that comes back carries the following. Read them in this order.
- 1. Category: what happened
- The canonical event type, such as
swap,liquidity_add,reward, orbridge_transfer. Next to it sit the asset legs, derived from the transaction receipt and read from the wallet's side: what was sent, what was received, amounts as exact decimal strings. The category is an open vocabulary that grows over time, so it identifies the event; it is not the field to branch your logic on. - 2. Treatment: what it maps to
- One of exactly five values:
disposal,income,non_taxable,expense, orneeds_review. That set is closed. Ataxableboolean travels with it. - 3. Confidence: how much the evidence agreed
- An integer from 0 to 100. It is a routing signal, not an accuracy percentage, and scores are not comparable from one transaction to another. It gates one decision: whether the record is served with a treatment or sent to a person.
- 4. needs_review: whether we declined to answer
- When the engine cannot honestly assert a treatment, the record says so:
needs_reviewis true, the treatment isneeds_review, andtaxableis null. Those three always move together. Areview_noteexplains why. - 5. The alternate position, where one exists
- For the handful of categories where two treatments are both defensible, the record names the one we did not serve, with its basis, so the reviewer can switch without re-deriving the analysis.
Plain-English reasoning sits in description, and the protocol is attributed when we can identify it. That is the whole contract. Anything the next layer needs beyond it, it should get from the client's records, not from us.
Three hashes that stress the seam
The shapes below are where we see the most rework at the hand-off. In each, the useful question is not "is this taxable?" in the abstract, but "what does the record actually assert, and what should make me look twice?"
A trade that took the long way round
A client swaps one token for another through an aggregator. On the explorer, the transaction shows the input token hopping through two or three pools and an intermediate asset before the output arrives. Do the hops count as separate dispositions?
We do not treat that as settled, and we do not apply a blanket rule to it. Our approach is to read the transaction by net flow from the wallet's point of view: what entered the wallet and what left it. Intermediate tokens that pass between pools without the wallet ever holding them are routing, and the record typically shows one swap with the asset that left and the asset that arrived. That is a reading of the evidence, not a ruling that hops can never matter.
What should make you look twice: an intermediate token left over in the wallet, a partial fill with a refund leg, or a second protocol touched in the same hash. Those shapes do not always net cleanly, and while they often route to review, the flag is not guaranteed on every multi-protocol hash. Check the sent and received legs against what the client says they did.
The same asset, on another chain
A client moves a stablecoin from one network to another through a bridge. It is tempting to stamp every bridge as non-taxable. Our published standard does not fix a house treatment for bridge_transfer, and an earlier version that called bridges a settled non_taxable was corrected for overstating it. The treatment on a bridge record comes from the evidence for that specific transaction, so read the treatment field rather than inferring anything from the category name.
A bridge record can reasonably come back non_taxable when it is the client's own asset moving between the client's own wallets. Several things move it off that answer. If the asset is swapped into something different en route, it is represented as a swap, a disposition of the source asset. If ownership of the receiving address is not established, or one side of the movement is missing, do not take a quiet non_taxable at face value, whether or not the record carries a review flag. Resolve it against the client's known-wallet list.
A pool deposit
Here the doubt is about the law, not the data. For liquidity_add and liquidity_remove, the house default treats the add or the remove as a disposition, and the continuity alternative, which treats the deposit as a non-recognition contribution and the withdrawal as an unwind, stays on the record as a named alternate with its basis. Wraps and unwraps run the other way: the house default is non_taxable, with the disposition view exposed.
A house default is a documented starting point, not a firm's election. If your firm has taken a position on these categories, set it once as a tax policy (signed-in Explorer users can save one under Settings, and API callers can pass one on classify), and the engine applies it consistently. One limit to know: the Chrome extension does not send a policy.
What the reviewer does with it
A record is built to be sorted, not just read. The practical triage we recommend:
- Anything with
needs_reviewtrue goes to a review queue, never to the books. Do not coerce a nulltaxableto false because a column demanded a value. The deferral is the engine telling you where your hour is best spent. - Records on contested categories get one firm decision, not fifty row-level ones. Decide the stance, save the policy, rerun. The alternate on each record lets you see what changes before you commit.
- Asserted records still get sampling. A record served without the review flag is asserted at whatever confidence it carries; that is our routing decision, not a guarantee. Spot-check the legs on the shapes your clients use most.
Only after that pass should rows move into the tax software, where lots, basis, and gain or loss are computed against the client's own history.
Questions we hear at the hand-off
Can I export gain and loss from CryptoTaxEdge?
No. We classify; we do not compute basis, proceeds, or gain and loss. The ledger_action field suggests a label your tax platform can import, and the numbers are computed there.
Is a high confidence score the probability the treatment is right? No. It reflects how well the available evidence agreed and decides whether the record is served or routed. It is not an accuracy rate.
Why didn't a contested category come back as needs_review? Usually because a defensible house default exists and is served, with the alternate carried on the record. A contested category can still be routed to review when the evidence for that transaction does not support asserting any treatment; routing overrides the house default.
Informational only, not tax advice. Classifications and the positions described here are general framing under US federal income tax principles, not a filing position for any taxpayer. Verify results with a qualified tax professional before filing.