Reconciling DAC8, CARF and CRS 2.0: One Reporting Engine, Three Regimes

DAC8, CARF and CRS 2.0 are three names for one problem: the same customer, holding the same assets, described three ways for three tax authorities. Build them as three pipelines and you will reconcile the same numbers against each other forever.

Most crypto-asset service providers I speak to have scoped these three regimes as three projects, usually with three owners and, more than once, three vendors. That is the expensive way to read the timeline. DAC8 forces the build first: Council Directive (EU) 2023/2226 applies from 1 January 2026, with first reporting between 1 January and 30 September 2027 for the 2026 year. The OECD’s Crypto-Asset Reporting Framework and the amended Common Reporting Standard sit behind it on aligned timelines. If you are building the DAC8 collection layer anyway, building it three times is a choice, not a requirement.

Why three regimes describe the same customer

The three instruments were designed to interlock, not to compete. CARF is the OECD template for automatic exchange of crypto-asset transaction data between tax authorities; sixty-seven jurisdictions have committed to it, fifty-two targeting first exchanges in 2027 and the remainder in 2028. The amended CRS — what the market has taken to calling CRS 2.0 — extends the older account-reporting standard to cover things it previously missed, including electronic money products, central bank digital currencies and certain indirect holdings of crypto. DAC8 is the European Union’s legal transposition of both: it pulls CARF and the CRS amendments into the Directive on Administrative Cooperation and adds EU-specific machinery on top, including a single-registration regime for providers operating in the Union without a MiCA authorisation.

Free · 4 minutes

When two of your systems disagree, do you know which one to believe?

Fourteen questions on ownership, lineage, and quality — the difference between a number on a dashboard and a number you could defend. Banded finding on screen, full sheet by email.

Read that way, the overlap is not a coincidence to be managed. It is the design. A user who transacts on your platform is a CARF reportable person, a DAC8 reportable person and — for their fiat, e-money or CBDC balances — a CRS reportable account holder, all from the same onboarding record. The instruments diverge on what they want reported about that user, not on who the user is or where they are resident.

The shared spine: one due-diligence layer

Almost everything expensive about these regimes lives in the due-diligence layer, and almost all of it is common. Self-certification of tax residence. Collection and plausibility-checking of taxpayer identification numbers. Determination of reportable jurisdictions. The reasonableness test that says a self-certification must not be accepted if it contradicts what you already hold. Controlling-person look-through for entity accounts. This is the same work under CARF, under CRS 2.0 and under DAC8, captured once at onboarding and refreshed on a change of circumstances.

If your programme captures this three times, in three schemas, you have already lost. Worse, you have created three places for the same customer’s residence to be recorded differently, which is precisely the discrepancy a tax authority query will surface. The data you must capture for DAC8 is, with narrow exceptions, the data you must capture for the other two — a point worth reading against the concrete first-reporting data requirements for Cyprus and Malta CASPs. Model the customer once and the divergence collapses to a much smaller surface at the reporting end.

Where the regimes genuinely diverge

Consolidation only works if you are honest about where the regimes actually differ, because that is where a single flattened model quietly produces the wrong return. Three divergences matter.

Transactions versus balances. CARF and the crypto part of DAC8 report on transactions: aggregate fair market value paid and received, aggregate units, and the number of transactions, split by crypto-asset type and by category — crypto-to-fiat exchanges, crypto-to-crypto exchanges, transfers, and reportable retail payment transactions. CRS 2.0 reports on accounts: closing balance or value and gross income by type. A record designed only for account balances cannot express a transaction count, and one designed only for transactions has nowhere to put a year-end balance.

Thresholds and carve-outs. CARF treats a transfer of crypto for goods or services as a reportable retail payment transaction only above USD 50,000, below which the value is reported as an ordinary transfer to the merchant rather than against the underlying customer. The CRS amendments and CARF also carry deliberate carve-outs so that an asset caught by one regime is not double-counted under the other. Your routing logic has to encode those carve-outs, or you will over-report — which is its own compliance failure, not a safe default.

Nexus and destination. Who you report to, and under which legal hook, differs. DAC8 routes through your Member State of registration into the EU exchange mechanism; CARF routes through the reporting provider’s jurisdiction of nexus; CRS routes through the financial institution’s jurisdiction. Same customer, potentially different destination authority. This is the same coordination problem I have described for settlement in one cutover across three rulebooks: shared mechanics, jurisdiction-specific endpoints.

One engine, three adapters

The pattern that survives contact with all three regimes is a canonical record produced once by the due-diligence and aggregation layer, a routing function that decides which regimes each record feeds, and thin per-regime adapters that shape the canonical record into each output schema. Collection and validation happen once; you branch only at report generation. The sketch below is the spine, not the whole engine.

# canonical_report.py
# One collection and validation layer; branch only at output.
# British spelling throughout.

from dataclasses import dataclass, field
from decimal import Decimal
from enum import Enum


class Regime(Enum):
    DAC8 = "dac8"      # Council Directive (EU) 2023/2226
    CARF = "carf"      # OECD Crypto-Asset Reporting Framework
    CRS2 = "crs_2_0"   # OECD amended Common Reporting Standard


@dataclass
class TaxResidence:
    jurisdiction: str            # ISO 3166 alpha-2
    tin: str                     # taxpayer identification number
    tin_absent_reason: str | None = None


@dataclass
class Party:
    """Collected once, at onboarding, via self-certification."""
    legal_name: str
    residences: list[TaxResidence] = field(default_factory=list)
    is_entity: bool = False
    self_certification_verified: bool = False


@dataclass
class CanonicalRecord:
    """One aggregated position per user, per year, per asset."""
    party: Party
    asset_type: str              # e.g. 'BTC', 'e-money', 'fiat_account'
    is_crypto_asset: bool
    fair_market_value: Decimal   # aggregate, in reporting currency
    units: Decimal | None = None            # crypto only
    txn_count: int | None = None            # crypto only
    account_balance: Decimal | None = None  # CRS-style accounts only
    retail_payment_value: Decimal = Decimal(0)


RETAIL_PAYMENT_THRESHOLD = Decimal("50000")  # USD, CARF


def route(record: CanonicalRecord) -> set[Regime]:
    """Decide which regimes feed, encoding the carve-outs."""
    if record.is_crypto_asset:
        return {Regime.CARF, Regime.DAC8}   # EU transposes CARF
    return {Regime.CRS2}                     # accounts, e-money, CBDC


def to_carf(r: CanonicalRecord) -> dict:
    """Transactions, not balances: aggregate value and units by type."""
    return {
        "userTIN": r.party.residences[0].tin,
        "assetType": r.asset_type,
        "grossAmountFMV": str(r.fair_market_value),
        "unitCount": str(r.units or 0),
        "transactionCount": r.txn_count or 0,
        "retailPaymentReportable":
            r.retail_payment_value > RETAIL_PAYMENT_THRESHOLD,
    }


def to_crs(r: CanonicalRecord) -> dict:
    """CRS 2.0: year-end balance and income, incl. e-money and CBDC."""
    return {
        "accountHolderTIN": r.party.residences[0].tin,
        "accountBalance": str(r.account_balance or 0),
        "productType": r.asset_type,
    }

The DAC8 adapter is deliberately thin because, for crypto, DAC8 reuses the CARF XML schema and adds EU-specific envelope fields such as the Member State of registration. That is the whole efficiency argument in one line: the EU chose not to reinvent the crypto data model, and neither should you. A worked version of the DAC8 output itself is in crypto-asset tax reporting from 1 January 2026 in code.

What to build once, and what to leave alone

Consolidation is not a licence to merge everything. Build once: onboarding and self-certification, TIN validation, residence determination, the reasonableness test, aggregation of values and units, and the canonical record store. Keep separate: the routing rules that encode carve-outs, the per-regime schema mappings, the destination and filing calendar, and the validation of each output against its own published schema before submission. The failure mode to avoid is a single mega-model so generalised that no regime’s rules are actually enforced in it — a model that produces three returns none of which a supervisor would accept.

The test of the design is boring and specific. When a tax authority queries one figure, can you trace it from the submitted return, through the adapter, back to the single canonical record and the transactions that built it? If the answer is yes, you have one engine. If the answer is “which of our three systems produced that number”, you have three pipelines and a reconciliation problem you will be paying for at every reporting cycle from 2027 onward.

DAC8 is going to force you to build the collection layer regardless. The only real decision is whether you build it once and adapt it, or build it three times and spend the next five years proving to three authorities that the three copies agree.

Free interactive tool

Website compliance checklist

What your site has to do, based on what it actually does

Answer as much or as little as you like — the list builds as you go. Nothing is stored against your name and no email is required.

Free interactive tool

Interactive deadline calculator

Check which regulations apply to you and when

Regulation across the EU, UK, US and Asia-Pacific has moved considerably in the past eighteen months, and several headline dates have shifted more than once. Twelve questions, about three minutes.

Results are shown on screen — no email required. A dated summary is available to download, and can be sent on if that's more useful. What we do with your answers.

Most technology problems are not technology problems. They are control problems.

The systems exist. The investment has been made. The question is whether leadership can understand, direct, evidence, and sustain what those systems produce. Find out where control exists — and where it only appears to.

Full Governance by Sixteen Pillars

Govern your business. Prove your compliance.

A board assurance cockpit for EU-regulated financial firms — tamper-evident, hash-chained proof of governance across DORA, GDPR, NIS2, ISO 27001, the EU AI Act and MiCA. In development.

See what's coming