2026-05-29·5 min read·sota.io Team

EU MiCA CASP Client Asset Safeguarding 2026: Art.70 Segregation, Proof-of-Reserves, and Insurance Requirements

Post #1381 in the sota.io EU Cyber Compliance Series

EU MiCA CASP Client Asset Safeguarding 2026: Art.70 Segregation and Proof-of-Reserves

When a CASP holds client funds — whether crypto-assets or fiat — those assets belong to clients, not to the platform. MiCA Article 70 translates this principle into hard legal obligations: strict segregation, continuous proof-of-holdings, and mandatory insurance coverage. This is post #3/5 in the EU-MICA-CASP-DEVELOPER-2026 series.

The June 30, 2026 grace period is less than five weeks away. If your platform custodies client assets, your engineering and compliance teams need to understand exactly what Art.70 demands — and what the technical architecture to meet it looks like.


What MiCA Art.70 Actually Requires

Article 70 of Regulation (EU) 2023/1114 (MiCA) establishes the client asset protection framework for CASPs. The obligations apply to:

Art.70(1): Prohibition on Commingling

CASPs shall not use client crypto-assets for their own account. This sounds obvious, but the engineering implications are severe:

Prohibited ActionWhy It Violates Art.70(1)
Using client BTC as liquidity for market-makingOwn-account use of client assets
Pooling client ETH in a single hot wallet with operating fundsCommingling, balance attribution impossible
Using client stablecoins to cover exchange feesDirect own-account use
Rehypothecating client positions for DeFi yieldUse without explicit written consent

The only exception: CASPs may use client crypto-assets for their own account if the client has given prior, explicit, written consent — and this must be documented with a clear record of which assets, for what period, and with what risk disclosure.

Art.70(2): Segregation Architecture

Client crypto-assets must be held separately from CASP's own crypto-assets. The practical requirement is wallet-level segregation, not just accounting-level:

CASP Wallet Architecture (Art.70(2) Compliant):

┌─────────────────────────────────────────────────────┐
│                   CASP OWN FUNDS                    │
│  ┌─────────────────┐   ┌──────────────────────────┐ │
│  │ Operating Wallet│   │ Reserve / Capital Wallet  │ │
│  │ (Hot — Ops)     │   │ (Cold — Capital Adequacy) │ │
│  └─────────────────┘   └──────────────────────────┘ │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│                 CLIENT ASSETS (Segregated)          │
│  ┌───────────────┐  ┌───────────────┐               │
│  │ Client Hot    │  │ Client Cold   │               │
│  │ Wallet Pool   │  │ Storage (≥95%)│               │
│  │ (< 5% total)  │  │               │               │
│  └───────────────┘  └───────────────┘               │
│  Per-client accounting: individual balance ledger    │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│              CLIENT FIAT (Bank Accounts)            │
│  Separate bank account(s) designated "CLIENT FUNDS" │
│  Ring-fenced: CASP bankruptcy remote                │
└─────────────────────────────────────────────────────┘

The "95/5 Rule" (Industry Standard for Art.70 Compliance): While MiCA does not specify exact percentages, ESMA's technical guidance and NCA supervisory expectations have converged on keeping at least 95% of client crypto in cold storage and at most 5% in hot wallets for operational liquidity.

If your platform uses client assets (e.g., for DeFi yield products), you need:

  1. Explicit written consent per client, per use case
  2. Risk disclosure covering counterparty risk, volatility, liquidation scenarios
  3. Revocability: Client can withdraw consent within 30 days
  4. Accounting separation: Clear ledger entry distinguishing consented vs. non-consented balances
  5. Return guarantee: Clear mechanism to return equivalent assets

Art.70(4)–(6): Fiat Fund Investment

When CASPs hold client fiat (EUR, GBP, etc.), they may invest it only in:

Asset ClassPermittedConditions
Government bonds (EU/EEA sovereign)✅ YesLiquid, investment grade
Money market funds (UCITS)✅ YesDaily liquidity
Central bank deposits✅ YesPreferred
Corporate bonds⚠️ LimitedMust be investment grade, ≤1 year maturity
Crypto-assets❌ NoArt.70(4) prohibits fiat → crypto investment
Equities❌ NoNot permitted under Art.70

Any returns from these investments belong to clients unless the CASP discloses and clients consent to a different arrangement.


Technical Implementation: Wallet Segregation Architecture

Hot/Cold Wallet Separation

# Conceptual wallet segregation tracker
from dataclasses import dataclass, field
from decimal import Decimal
from enum import Enum
from typing import Dict, List

class WalletType(Enum):
    CASP_OPERATING = "casp_operating"
    CASP_CAPITAL = "casp_capital"
    CLIENT_HOT = "client_hot"
    CLIENT_COLD = "client_cold"

@dataclass
class AssetBalance:
    asset: str  # "BTC", "ETH", "USDC"
    amount: Decimal
    wallet_type: WalletType
    wallet_address: str
    is_client_owned: bool

class Art70SegregationChecker:
    """Validates MiCA Art.70 segregation compliance at each state change."""

    def check_hot_ratio(
        self, 
        client_hot: Decimal, 
        client_cold: Decimal,
        asset: str
    ) -> dict:
        total = client_hot + client_cold
        if total == 0:
            return {"compliant": True, "ratio": 0}
        hot_ratio = float(client_hot / total)
        return {
            "asset": asset,
            "hot_ratio": hot_ratio,
            "compliant": hot_ratio <= 0.05,  # 95% cold floor
            "warning": hot_ratio > 0.03,
            "client_hot": str(client_hot),
            "client_cold": str(client_cold),
        }

    def check_commingling(
        self, 
        wallet_address: str,
        balances: List[AssetBalance]
    ) -> bool:
        """Returns False if wallet contains both client and CASP assets."""
        wallet_balances = [b for b in balances if b.wallet_address == wallet_address]
        has_client = any(b.is_client_owned for b in wallet_balances)
        has_casp = any(not b.is_client_owned for b in wallet_balances)
        return not (has_client and has_casp)

    def get_segregation_report(self, balances: List[AssetBalance]) -> dict:
        """Full Art.70(2) segregation compliance report."""
        violations = []
        # Check for commingling
        addresses = set(b.wallet_address for b in balances)
        for addr in addresses:
            if not self.check_commingling(addr, balances):
                violations.append(f"Commingling detected at {addr}")
        # Check hot ratios per asset
        hot_ratios = {}
        for balance in balances:
            asset = balance.asset
            if asset not in hot_ratios:
                hot_ratios[asset] = {"hot": Decimal(0), "cold": Decimal(0)}
            if balance.wallet_type == WalletType.CLIENT_HOT:
                hot_ratios[asset]["hot"] += balance.amount
            elif balance.wallet_type == WalletType.CLIENT_COLD:
                hot_ratios[asset]["cold"] += balance.amount
        ratio_checks = [
            self.check_hot_ratio(v["hot"], v["cold"], asset)
            for asset, v in hot_ratios.items()
        ]
        non_compliant = [r for r in ratio_checks if not r["compliant"]]
        return {
            "commingling_violations": violations,
            "hot_ratio_violations": non_compliant,
            "overall_compliant": len(violations) == 0 and len(non_compliant) == 0,
        }

Per-Client Ledger Architecture

Art.70 requires that each client's exact holdings can be reconstructed at any moment. This means:

-- Client asset ledger schema (Art.70(2) compliant)
CREATE TABLE client_asset_ledger (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id UUID NOT NULL REFERENCES clients(id),
    asset_symbol VARCHAR(20) NOT NULL,   -- "BTC", "ETH", "USDC"
    amount NUMERIC(38,18) NOT NULL,       -- Precise to 18 decimal places
    wallet_type VARCHAR(30) NOT NULL,     -- "hot", "cold", "pending_withdrawal"
    on_chain_address VARCHAR(255),        -- Actual wallet address holding the asset
    on_chain_txhash VARCHAR(255),         -- Deposit/withdrawal transaction
    created_at TIMESTAMPTZ DEFAULT NOW(),
    event_type VARCHAR(50) NOT NULL,      -- "deposit", "withdrawal", "trade", "fee"
    balance_after NUMERIC(38,18) NOT NULL -- Running balance (for audit)
);

-- Ensure per-client balance is always reconstructable
CREATE INDEX idx_client_asset ON client_asset_ledger(client_id, asset_symbol, created_at);

-- Regulatory view: real-time client holdings
CREATE VIEW client_current_holdings AS
SELECT 
    client_id,
    asset_symbol,
    SUM(amount) AS total_balance,
    MAX(created_at) AS last_updated
FROM client_asset_ledger
GROUP BY client_id, asset_symbol
HAVING SUM(amount) > 0;

Multi-Signature Key Management

For cold storage, MiCA's governance and systems-security requirements (Art.68, covered in post #2) combined with Art.70 create a practical requirement for multi-signature (multisig) custody:

SetupThresholdUse Case
2-of-3 multisig2 of 3 keysSmall/medium CASP cold storage
3-of-5 multisig3 of 5 keysEnterprise custody
MPC (Multi-Party Computation)Threshold depends on implementationInstitutional grade

Key storage standards:


DLT Proof-of-Reserves

MiCA does not explicitly mandate proof-of-reserves, but NCAs — particularly BaFin (Germany), AMF (France), and CSSF (Luxembourg) — have signaled through supervisory guidance that CASPs should be able to demonstrate asset sufficiency on demand. The industry standard is a Merkle tree proof-of-reserves system.

How Merkle Proof-of-Reserves Works

                    Root Hash (H_root)
                   /                \
         H_AB (left)              H_CD (right)
        /         \               /           \
  H_A (client 1) H_B (client 2) H_C (client 3) H_D (client 4)
  balance: 1 BTC  balance: 0.5 BTC  balance: 2 BTC  balance: 0.1 BTC

Total client liability: 3.6 BTC
On-chain wallet holdings: ≥ 3.6 BTC (verified via blockchain explorer)

Each client can verify their balance is included without seeing other clients' balances.

Implementation Architecture

import hashlib
from typing import List, Tuple, Optional

class MerkleProofOfReserves:
    """
    Generates Merkle proof-of-reserves attestations per MiCA Art.70 
    supervisory expectations.
    """

    def _hash_leaf(self, client_id: str, asset: str, balance: str) -> str:
        """Hash a single client balance entry."""
        data = f"{client_id}:{asset}:{balance}"
        return hashlib.sha256(data.encode()).hexdigest()

    def _hash_pair(self, left: str, right: str) -> str:
        combined = left + right
        return hashlib.sha256(combined.encode()).hexdigest()

    def build_tree(self, leaves: List[Tuple[str, str, str]]) -> List[List[str]]:
        """
        Build Merkle tree from (client_id, asset, balance) tuples.
        Returns tree levels for proof generation.
        """
        # Create leaf layer
        layer = [self._hash_leaf(c, a, b) for c, a, b in leaves]
        # Pad to even number
        if len(layer) % 2 != 0:
            layer.append(layer[-1])
        
        tree = [layer]
        while len(layer) > 1:
            next_layer = []
            for i in range(0, len(layer), 2):
                next_layer.append(self._hash_pair(layer[i], layer[i+1]))
            if len(next_layer) % 2 != 0 and len(next_layer) > 1:
                next_layer.append(next_layer[-1])
            tree.append(next_layer)
            layer = next_layer
        return tree

    def generate_client_proof(
        self, 
        client_id: str, 
        asset: str, 
        balance: str,
        all_leaves: List[Tuple[str, str, str]]
    ) -> dict:
        """Generate proof that allows client to verify their balance is included."""
        tree = self.build_tree(all_leaves)
        leaf_hash = self._hash_leaf(client_id, asset, balance)
        
        # Find index
        idx = tree[0].index(leaf_hash) if leaf_hash in tree[0] else -1
        if idx == -1:
            return {"error": "Client not found in tree"}
        
        # Generate sibling path
        proof_path = []
        for level in tree[:-1]:
            sibling_idx = idx + 1 if idx % 2 == 0 else idx - 1
            if sibling_idx < len(level):
                proof_path.append({
                    "hash": level[sibling_idx],
                    "direction": "right" if idx % 2 == 0 else "left"
                })
            idx //= 2
        
        return {
            "client_id": client_id,
            "asset": asset,
            "balance": balance,
            "leaf_hash": leaf_hash,
            "root": tree[-1][0],
            "proof": proof_path,
        }

    def get_total_liability(
        self, 
        leaves: List[Tuple[str, str, str]]
    ) -> dict:
        """Aggregate client liabilities per asset for reserve check."""
        from decimal import Decimal
        totals: dict[str, Decimal] = {}
        for _, asset, balance in leaves:
            totals[asset] = totals.get(asset, Decimal(0)) + Decimal(balance)
        return {asset: str(total) for asset, total in totals.items()}

Publishing Proof-of-Reserves

Best practice (following Kraken, Binance.US, and EU-native exchanges):

  1. Monthly attestation cycle: Publish root hash + total liabilities
  2. On-chain anchoring: Anchor the Merkle root in an Ethereum/Bitcoin OP_RETURN transaction for immutability
  3. Third-party auditor: Independent auditor verifies wallet addresses hold ≥ declared liabilities
  4. Client self-verification portal: Clients can verify their balance is included without seeing others'
  5. NCA reporting: Include latest attestation hash in monthly regulatory report

Insurance and Professional Indemnity Requirements

MiCA Article 67 (Prudential requirements) requires CASPs to maintain own funds that may be satisfied via professional indemnity insurance (PII) or a comparable guarantee. The specific requirements:

Minimum Coverage Thresholds

CASP SizeMinimum PII Coverage
Annual revenue < €1M€500,000 per claim
Annual revenue €1M–€10M€1,000,000 per claim
Annual revenue > €10M10% of annual revenue (min €2M)
Custodians with client assets > €100M€5,000,000+ (NCA discretion)

What Must Be Covered

The insurance policy must cover losses arising from:

  1. Operational failures: System downtime, processing errors, incorrect transactions
  2. Cyber incidents: Hacks, key compromise, theft of client assets
  3. Employee misconduct: Fraud, unauthorized access, insider attacks
  4. Third-party vendor failures: Cloud provider outages affecting client assets
  5. Smart contract failures: Bugs in CASP-deployed contracts holding client funds

Insurance vs. Capital Adequacy (Art.67)

CASPs can satisfy Art.67 via either:

OptionMechanismTrade-Off
Professional indemnity insuranceThird-party insurerLower capital lock-up, annual renewal risk
Own capital reserveAdditional capital ≥ Art.67 minimumCapital tied up, no annual renewal risk
Hybrid (partial insurance + partial capital)MixedFlexibility, regulatory approval needed

Practical note: Most EU NCAs prefer a hybrid approach for custodians — minimum own capital (Art.67) plus PII for tail risks. Pure insurance-only arrangements have faced pushback from BaFin and CSSF during authorization reviews.

Acceptable Insurance Providers

Not every insurer is acceptable. MiCA-compliant PII should come from:

Document requirement for NCA submission: Policy declaration page + coverage schedule must be submitted during authorization and updated annually.


Bankruptcy Remoteness for Client Assets

One of the most important Art.70 principles: client assets must be bankruptcy remote — i.e., if the CASP becomes insolvent, client assets cannot be claimed by CASP creditors.

Compliant Structure:
┌────────────────────────────────┐
│ CASP Entity (Licensed)         │
│ - Authorization holder         │
│ - Operates exchange/custody    │
│ - Has own capital (Art.67)     │
└──────────┬─────────────────────┘
           │ Holds client assets AS TRUSTEE
           │ (not as owner)
           ▼
┌────────────────────────────────┐
│ Client Asset Custodial Account │
│ - Designated "CLIENT FUNDS"    │
│ - Ring-fenced bank account     │
│ - Not available to CASP        │
│   creditors in insolvency      │
│ - Separate from CASP's own     │
│   capital accounts             │
└────────────────────────────────┘

For crypto-assets specifically, the trust/custodial structure must be reflected in:

Key Engineering Controls for Bankruptcy Remoteness

ControlImplementation
Separate database schema for client assetsClient funds table isolated from CASP treasury
Dual authorization for fund movementsNo single employee can move client funds alone
Regular reconciliation against on-chainAutomated daily reconciliation with alerts on discrepancy >0.01%
Audit logs with immutable storageWrite-once audit trail (S3 Object Lock or WORM storage)
Emergency freeze capabilityAdmin can halt withdrawals in <60 seconds if suspicious activity detected

Compliance Checklist: MiCA Art.70 Client Asset Safeguarding

Part A — Wallet Architecture

Part B — Fiat Fund Segregation

Part C — Proof-of-Reserves

Part D — Insurance and Capital

Part E — Operational Controls

Part F — Governance


Common Art.70 Mistakes and How to Avoid Them

Mistake 1: Accounting Segregation ≠ Wallet Segregation

The Error: Some platforms maintain separate accounting records but hold all assets in a single set of wallets. This violates Art.70(2).

The Fix: Segregation must be at the wallet level, not just the database level. Each wallet must hold either client assets or CASP assets, never both.

Mistake 2: "We Don't Custody" as a Defense

The Error: Claiming your platform is "non-custodial" while briefly holding client assets during trade settlement.

The Fix: Settlement windows of 1–24 hours are still custody under MiCA. Even temporary possession during trade execution falls under Art.70 if clients cannot independently control the assets during that window.

Mistake 3: Ignoring the Fiat Side

The Error: Building excellent crypto segregation while commingling client EUR in the CASP's general operating bank account.

The Fix: Client fiat must be in a separate, designated bank account. The bank must acknowledge the custodial nature of the account in writing.

The Error: Offering yield products on client assets without obtaining documented, revocable, per-client consent.

The Fix: Build a consent management system before launching any yield/staking product. Each consent must be stored with timestamp, asset, amount, and the risk disclosure presented.


What's Next in the EU-MICA-CASP-DEVELOPER-2026 Series

This post covered Art.70 client asset safeguarding — the custody and segregation obligations. The remaining posts in this series will cover:

Earlier posts in the series:


sota.io and MiCA Infrastructure Compliance

If you're building a CASP and need EU-sovereign infrastructure that supports MiCA's data residency, audit trail, and Art.70 segregation requirements, sota.io provides managed deployments on Hetzner Germany — no US parent company, no CLOUD Act exposure. Your custody systems, KYC data, and client asset ledgers stay under EU jurisdiction at all times.

See also:

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.