Taxcount
Airgapped US tax calculation for Bitcoiners
Install / Use
/learn @dcdpr/TaxcountREADME
README
What is Taxcount?
Taxcount counts up your gains and losses for US tax purposes. It runs on an airgapped-network and helps Bitcoiners (both hodlers with wallet activity and bitcoin traders) fill out US IRS tax worksheet Form 8949, 'Sales and other Dispositions of Capital Assets', for 1040 Schedule D, 'Capital Gains and Losses"'. Taxcount is production-ready, and we are currently focusing on the user experience.
In general, when using Taxcount, your accounting job is to collect and offer the exports from your wallets and exchanges, and then label any on-chain transactions that were spends or regular income (including mining). If, in the tax year of interest, you spent a UTXO that was not earned or purchased in that year, then Taxcount also has facilities for declaring its original basis.
Comprehensive Transaction Tracking
- Multi-source integration - process exchange trades and on-chain wallet transactions
- Basis tracking - follow the purchase price of your bitcoin through any number of wallet transfers and exchange round-trips
- Complete simulation - runs a parallel model of all tax-year transactions across your entire Bitcoin ecosystem
Key Features
- No cloud dependency - connect to local bitcoind or Esplora
- Multiple transaction types - supporting:
- Spending
- Trading gains/losses
- Margin Trading gains/losses
- Mining income
- Labor income
- Lending activities
- more on the way
- CSV output - for Form 8949 fields
- Non-US residency - Puerto Rico residency is supported
Technical Advantages
- Local blockchain caching - retrieve blocks and transactions over the network exactly once
- 100% Rust implementation - no unsafe code and no "stringly typed" data
- Decimal arithmatic - we are extremely persnickety about where divides occur in the codebase
- Precise fee handling - special attention paid to how fees affect gains
- FIFO binning on the exchanges - as required by IRS
- UTXO-level binning for on-chain transactions - maximum accuracy
- Handles margin trades - all trade types on the exchange are supported
- Zero need for basis averaging - tracks every detail for complete accuracy (and satisfies IRS Rev. Proc. 2024-28)
- Flexible wallet support - generic wallet format included, as well as Electrum and Ledger Live wallet formats, with more on the way
- International Currencies - the architecture is ready for trades in any fiat quote currency and any asset, with several already included
- Extensive testing - testing strategy includes generative simulated data, but see limitations
Exchange Support
Currently reads exports from Kraken.com.
Lightning Network
TBD
Overview slides
See Taxcount from 40,000 feet (PDF).
Special Thanks
Special thanks to Jay at BlipJoy, who brought a new Rustacean along on a much longer ride than anticipated. Your patience and expertise proved invaluable during countless pair-programming sessions over the years. Your willingness to both teach and adapt made this journey not only productive but genuinely enjoyable. While rgrant initially outlined the architectural vision, kept the Bitcoin mechanics grounded, and donated live test data, Jay's deep knowledge of Rust truly brought the vision to life.
Getting Started
Help
If you do not have the cargo command, you will need to install
Rust. Then, compile the project
and view its command line options:
cargo run -- --help
Your bitcoind RPC connection
Unless you have set up Esplora, you will need a bitcoind with
-txindex. See the Bitcoind Server section,
below.
Example invocation
Once your bitcoind can respond to testnet3 queries, try this test script:
./bin/taxcount-run-kraken-tests.sh
Bitcoin Backends
The Bitcoin backend is used to resolve Txids on the blockchain. The supported backends are:
- Esplora (slightly more efficient) -
configured with
ESPLORA_URLenvironment variable, defaults tohttp://localhost:3000 - bitcoind JSON-RPC - configured with
BITCOIND_URLandBITCOIND_CREDENTIALSenvironment variables. IfBITCOIND_URLis set, it takes priority over Esplora. Unset it to use the Esplora backend.
Bitcoind Server
Unless all your bitcoin transactions run through your bitcoind, you
are going to want -txindex on your bitcoind. That index takes some
time to build, but as of early 2025 only costs about 65GB of disk space.
The main downside from having it turned on is that you cannot also
-prune.
The bitcoind server requires a username and password, configured by
the BITCOIND_CREDENTIALS='username:password' environment variable.
Taxcount does not currently support local cookie authentication. Note that there is now a python script for generating the credentials:
https://raw.githubusercontent.com/bitcoin/bitcoin/master/share/rpcauth/rpcauth.py
If your bitcoind is remote, you will want to set up a local port using ssh port redirection.
After waiting for any local port redirection to be established, check the status. Here is an invocation on the testnet 18332 port:
curl --user "${BITCOIND_CREDENTIALS}" \
--data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getblockcount", "params": [] }' \
-H 'content-type: application/json;' \
http://127.0.0.1:18332/
If You Are Not Connected
If you have not set up any networking, then the network error will look like this:
Error: Wallet transaction resolution error
Caused by: Client error
Caused by: Esplora client error
Caused by: Error requesting TxId `feceb335210ee31662a8f251cfac24b605b51db3d53d10f436470e5f473a6fa3`: io: Connection refused
Bitcoin Backend Cache
The backend APIs can be rather slow to resolve a large number of transactions. To avoid requesting the same information over and over, taxcount maintains a request cache for the chosen backend.
After a successful taxcount invocation, the cache is written to the user's cache directory (shown in the table below), and the client is initialized with the cache at the start of each invocation.
The cache accumulates backend responses across all invocations.
Cache directories:
| Platform | Directory |
|----------|-----------------------------------------------------|
| Linux | $XDG_CONFIG_HOME/.cache/taxcount |
| macOS | $HOME/Library/Caches/design.contract.DCD.taxcount |
| Windows | %LOCALAPPDATA%\DCD\taxcount\cache |
One memo file will be created for each backend (depending on which backend is used):
esplora_memo.ron: Esplora backend.bitcoind_memo.ron: Bitcoind backend.
We have an open ticket to let you relocate these to your favorite encrypted volume, since they will leak toxic information about your transaction history. For now shuffling the files in and out of encrypted storage is your job.
Mock Client
The struct MockClient example in src/imports/wallet/rs
demonstrates how to connect other blockchain providers to Taxcount.
Data Files
At the moment, input files are loaded relative to the current working
directory. Run cargo test and cargo run from the project root.
If you introduce old skool UTXOs that have no associated exchange trade data, then you need to supply taxcount with their basis information using a bootstrapping process.
When you spend coins or receive UTXOs as income from either mining or labor, mark the appropriate transaction in the tx-tags file.
Kraken Ledgers CSV
When you export historical data from Kraken, you can pick a "ledgers" and a "trades" CSV (beware the PDF option they just made the default - you need the CSV). Taxcount primarily uses the ledger rows, since that is more declarative regarding assets entering and leaving your account. However, some margin trades are underspecified and also very small amounts can cause Kraken to "helpfully" [/s] elide rows, which further confuses parsing. In order to resolve these matters, Taxcount also refers back to the user's intent as recorded in the trades file.
The fields are:
"txid","refid","time","type","subtype","aclass","asset","amount","fee","balance"
Since we standardized on those ledger fields more have been added, but they don't affect parsing.
Kraken Trades CSV
As stated above, you need the trades file as well as the ledgers files.
The fields are:
"txid","ordertxid","pair","time","type","ordertype","price","cost","fee","vol","margin","misc","ledgers"
Since we standardized on those trades fields more have been added, but they don't affect parsing.
Basis Information
Deposits to the exchanges from pre-existing UTXOs, as well as assets directly sent to wallets from sources that Taxcount does not know about, need Basis Information.
There is a critical distinction to make between solving basis problems using the three tactical tools:
- with bootstrapping for UTXOs existing before the first year you get a checkpoint from taxcount (see tools/bootstrap-checkpoint);
- with
--input-tx-tagsas used for transactions in the current year; and - with the manual
--input-basisoverride for special situations.
The only special situations we are aware of at the moment that might
require --input-basis are airdrops and accounting reconciliation for
UTXOs that you had lost and now don't wan
