This repository is a fork of iraykhel/defitaxes, which its owner iraykhel is no longer able to maintain due to work constraints.
DeFi Taxes is a web-based application designed to help you calculate your crypto taxes using blockchain transaction data.
Many EVM blockchains are supported (e.g., Ethereum, BNB Smart Chain, Base, Arbitrum, Polygon, etc.) as well as Solana. See the supported chains list for the full list.
If you would like to contribute to this project, follow the instructions below to set up a development server.
You will need Python installed. See the official Python website for installation instructions.
Redis is used for storing user sessions and managing the process queue.
To install Redis, follow the Redis installation instructions.
Details of your Redis server should be added to the REDIS_URL configuration parameter. See the Config section for more details.
All keys stored in Redis are prefixed to make them easier to identify, especially if you are sharing a Redis server with other applications. The prefix is defined by the REDIS_PREFIX configuration parameter.
-
Clone the repository:
git clone https://github.com/BittyTax/defitaxes.git
-
Create a virtual environment for Python. This is optional but recommended.
cd defitaxes python -m venv .venv source .venv/bin/activate
-
Install the required packages:
pip install -r requirements.txt
-
Create a
.envfile. See the Environment Variables section for details. -
To run the development server:
flask run
By default, Flask will assume all application data is held in a folder called instance. If you prefer to use a different location, set this using the environment variable DEFITAXES_INSTANCE_PATH.
To run the application without a web server, you can use the Flask command:
flask driver process <address> <chain>For example:
flask driver process 0x032b7d93aeed91127baa55ad570d88fd2f15d589 ETHCopy the sample.env file to .env and populate it with your API keys.
API keys can mostly be obtained for free by subscribing to the websites listed below. See footnotes for exceptions.
| Name | Description |
|---|---|
DEFITAXES_ETHERSCAN_API_KEY |
Etherscan1 - Ethereum (ETH) Blockchain Explorer |
DEFITAXES_BLOCKDAEMON_API_KEY |
Blockdaemon - Blockdaemon Institutional Gateway to Web3 (Solana RPC) |
DEFITAXES_COINGECKO_API_KEY |
CoinGecko2 - Cryptocurrency prices |
DEFITAXES_TWELVEDATA_API_KEY |
Twelve Data - Fiat prices |
DEFITAXES_DEBANK_API_KEY |
DeBank3 - Current token balances, some protocol names |
DEFITAXES_RESERVOIR_API_KEY |
Reservoir - Currently held NFTs |
DEFITAXES_COVALENTHQ_API_KEY |
CovalentHQ4 - Fees on Arbitrum, errors on Fantom, some counterparty info on Ethereum |
1 - "Free" plan does NOT support "BNB Smart Chain", Base, Optimism or Avalanche. These require the "Lite" plan $49/month.
2 - You can use either the CoinGecko "Demo" plan which is free, or the paid "Pro" plan.
3 - DeBank is prepaid only, minimum 200 USDC.
4 - GoldRush offer a 14-day fee trial, or paid plans start at $50/month.
The application configuration is loaded into Flask using a Config object.
| Name | Default | Description |
|---|---|---|
DEBUG_LEVEL |
0 |
Enable additional debug logging stored in the instance/logs folder |
REDIS_URL |
"redis://localhost:6379" |
URL of Redis server |
REDIS_PREFIX |
"defitaxes" |
Prefix added to all Redis keys |
SOLANA_MAX_TX |
10000 |
Maximum number of Solana transactions to be processed. Used to restrict API usage. Remove this parameter to remove the limit |
COINGECKO_PRO |
False |
Choose between the "Pro" paid plan, or the "Demo" free plan |
The application requires two pre-configured SQLite databases. These reside in the instance folder unless otherwise configured.
They have been removed from the repository due to size, but can be created manually.
This database contains address labels for each blockchain. It is mainly created by scrapping content from the Label Cloud of each EVM Block Explorer.
For Solana, the program labels are parsed using this Solscan file.
The code which creates this database is currently a separate repository. See blockchain-address-database.
This database contains:
- Fiat prices from Twelve Data
- Crypto prices and data from CoinGecko
- EVM signature mappings from 4Byte
I will add Flask commands to initialise and create these tables.
A job-style REST API that automates the BittyTax export workflow: add wallet addresses, process all supported blockchains, generate a combined BittyTax Records XLSX file, and download it.
Because on-chain processing can take several minutes, the API uses a three-step pattern:
- Submit — send your wallets and export options, receive a
job_idimmediately. - Status — poll until the job is
completeorfailed. - Result — download the XLSX file (allowed multiple times within 24 hours).
Two constants at the top of app/views/export_job.py control the limits:
| Constant | Default | Description |
|---|---|---|
MAX_WALLETS |
100 |
Maximum number of wallet addresses per job |
JOB_RESULT_TTL_SECONDS |
86400 |
How long the result XLSX is retained (24 hours) |
All three endpoints require a Bearer token matching the DEFITAXES_EXPORT_API_KEY environment variable:
Authorization: Bearer <your-key>
Set the key in your .env file:
DEFITAXES_EXPORT_API_KEY=your-secret-key-here
If the key is absent or wrong the API returns 401. If DEFITAXES_EXPORT_API_KEY is not set in the environment, all requests are rejected.
Validates input synchronously and, if valid, starts processing in the background.
Request body (JSON):
{
"wallets": ["0xPrimaryAddress", "0xSecondaryAddress"],
"currency": "USD",
"is_macos": false,
"export_options": {
"transfer_in_known": 0,
"transfer_in_unknown": 0,
"transfer_out_known": 0,
"transfer_out_unknown": 0
}
}| Field | Required | Description |
|---|---|---|
wallets |
Yes | Array of wallet addresses. First entry is the primary wallet. Must contain 1–MAX_WALLETS unique, valid addresses (EVM 0x… or Solana base58). |
currency |
No | Fiat currency for the report. Default "USD". Supported: USD, GBP, EUR, AUD, CAD, JPY, CHF, NZD. |
is_macos |
No | Set to true when the XLSX will be opened on macOS. Default false. |
export_options |
No | BittyTax transfer mapping settings (same as the webpage export dialog). All fields default to 0. |
Transfer mapping values:
| Value | Meaning for inbound | Meaning for outbound |
|---|---|---|
0 |
Deposit |
Withdrawal |
1 |
Buy |
Sell |
Response 200:
{ "job_id": "550e8400-e29b-41d4-a716-446655440000" }Response 400 (validation failure):
{ "error": "Duplicate wallet addresses are not allowed" }Response 401 (missing or wrong API key):
{ "error": "Invalid or missing API key" }Response 409 (primary address already processing):
{ "error": "A job is already running for this address. Poll its status or wait before submitting a new one." }Poll the job state.
| Query param | Description |
|---|---|
job_id |
ID returned by /api/export/submit |
address |
Primary wallet address (must match the address used at submit) |
Response 200:
{ "status": "processing" }{ "status": "complete" }{ "status": "failed", "error": "…detail…" }| Status | Meaning |
|---|---|
processing |
Job is running — keep polling |
complete |
XLSX is ready to download |
failed |
Processing failed — see error field |
Response 401: invalid or missing API key.
Response 403: address does not match the job's primary wallet.
Response 404: job not found or expired.
Download the generated BittyTax Records XLSX.
| Query param | Description |
|---|---|
job_id |
ID returned by /api/export/submit |
address |
Primary wallet address (must match the address used at submit) |
Response 200: XLSX file attachment (BittyTax_Records_{job_id}.xlsx).
The file can be downloaded multiple times within the JOB_RESULT_TTL_SECONDS window (24 hours by default).
Response 400: job not yet complete, or job failed.
Response 401: invalid or missing API key.
Response 403: address does not match the job's primary wallet.
Response 404: job not found or expired.
Response 410: job completed but the result file has since expired.
# 1. Submit
curl -s -X POST http://localhost:5000/api/export/submit \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-key-here" \
-d '{
"wallets": ["0xabc1234...", "0xdef5678..."],
"currency": "GBP",
"export_options": { "transfer_in_known": 1, "transfer_out_known": 1 }
}'
# → {"job_id":"550e8400-e29b-41d4-a716-446655440000"}
# 2. Poll until complete
curl -s \
-H "Authorization: Bearer your-secret-key-here" \
"http://localhost:5000/api/export/status?job_id=550e8400-e29b-41d4-a716-446655440000&address=0xabc1234..."
# → {"status":"processing"} (repeat until…)
# → {"status":"complete"}
# 3. Download
curl -OJ \
-H "Authorization: Bearer your-secret-key-here" \
"http://localhost:5000/api/export/result?job_id=550e8400-e29b-41d4-a716-446655440000&address=0xabc1234..."
# → saves BittyTax_Records_550e8400-e29b-41d4-a716-446655440000.xlsx- All submitted wallets are processed together in a single pass across all supported blockchains — the same behaviour as using the web interface.
- The combined XLSX contains transactions for all submitted wallets; all years are included (no tax-year filtering).
- Jobs older than
JOB_RESULT_TTL_SECONDSare automatically expired by Redis (24 hours by default).