Skip to content
ColdAI-orgPublic

About

Multi-hop routing for CLPR: send messages and payments between any two ledgers, with cheapest/fastest/reliable/greenest routes and ISO 20022, MiCA and Energy filters.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

ColdAI

CLPRouter

Multi-hop routing for CLPR: one Channel per ledger to a hub, and any ledger can reach any other.
Messages and escrowed payments forwarded hop by hop over verified CLPR Channels, with end-to-end receipts, refunds, and cheapest / fastest / most reliable / greenest routes under ISO 20022, MiCA and Energy filters.

CI CodeQL Nightly e2e Tests: 772 passing
License: MIT Solidity 0.8.30 TypeScript Live on Sepolia and Hedera testnet Built on CLPR Status


CLPR is the cross-ledger protocol hosted by LF Decentralized Trust in which two ledgers verify each other's state proofs directly: no bridge validators, no pooled liquidity. A CLPR Channel joins exactly two ledgers. CLPRouter is an application on top of CLPR, with no protocol change, that turns those pairwise Channels into a network. It forwards a routed message hop by hop, re-checks the sender's rules on-chain at every hop, returns a receipt to the origin and settles or refunds the sender's escrow there. Every hop is verified by its own Channel's CLPR verifier; CLPRouter adds no new party to the data path.

Pre-release. Testnets and local networks only. Two internal audits, every High, Medium and Low finding fixed; no external audit yet. Do not route real value. See Security model and audit status.

Contents

What works today · Architecture · A routed message · Settle on Hedera · The provider · Modes and filters · Why CLPRouter · Quick start · Security · Status · Roadmap · For the LFDT CLPR team · Contributing

What works today on public testnets

Everything below is on Ethereum Sepolia (eip155:11155111) and Hedera testnet (eip155:296), next to the unchanged reference CLPR Service at 0xa6db474e3047c3d43b10a4ff7abad547d89982b9 on both. The full record, with every address, code hash, transaction, gas figure and cost, is in deployments/ (sepolia.json, hedera-testnet.json). All 22 Sepolia and 54 Hedera transactions recorded there were re-checked against the chains on 5 October 2026.

What Proof
Canonical CREATE2 deployment, same addresses on both chains. Registry, vault, ClprRouterDeployer and the five Router libraries share one address on each network; each ledger's Router sits at its canonical CREATE2 address. Sources of the Router stack verified on Sourcify. Router deploy: Sepolia 0x9bb34fe0… · Hedera 0xb36e281b…. Routers: Sepolia 0x3Ec8a28f…A653 · Hedera 0xF398F961…3E5F
A CLPR Channel Sepolia ↔ Hedera, verified on Hedera by the CLPR repo's EthMainnetVerifier (Ethereum sync-committee light client), reused byte-identical from the submodule. Hedera completeChannel: 0x28556511… (788,546 gas). Verifier: 0.0.10796650
Engineering finding: Hedera's contract trace-size cap. Opening that Channel in one transaction (67,364 B of call data) failed with INSUFFICIENT_GAS at every gas limit from 5.6M to 15M, always at 4,062,470 gas used. The cause is the consensus node's contracts.maxSerializedTraceDataBytes (262,144 B), not gas. Failed attempts: 0x6f0285ff… (5.6M limit) · 0x7dc546cd… (15M limit). Write-up: docs/lfdt/hedera-trace-cap.md
The fix: a staged-committee adapter. StagedEthConfigVerifier stages the 512 keys in 16 chunks of 32, then opens the Channel with the 16 chunk roots and returns exactly what EthMainnetVerifier.verifyConfig returns (parity-tested); every bundle is still verified by EthMainnetVerifier. First chunk: 0xdf341d48… (171,716 gas). Adapter: 0.0.10841641. Proposal: docs/proposals/clpr-staged-eth-committee.md
A route Sepolia → Hedera, delivered. Router.send on Sepolia; the Sepolia state proof (14,829 B) verified on Hedera by EthMainnetVerifier; the Hedera Router delivered the payload to the destination app and queued its DELIVERED receipt. Send: Sepolia 0x8a08f199… · delivery submitBundle: Hedera 0x1249ca90… (2,755,892 gas) · receipt flush: Hedera 0xc843d6d4…
Settle on Hedera contracts deployed. SettleOrderBook on Hedera; SettleDeposit and SettleDelivery on Sepolia; a test Connector registered with a 5 HBAR bond. The reference Connector service returned a signed quote the order book accepts. Order book: 0x41374335… (0.0.10867905) · bond: 0xeb4ddf75… · Deposit: Sepolia 0xf8df71b3… · Delivery: Sepolia 0x9a5239a3…

What is not done on testnet, plainly: the DELIVERED receipt cannot travel back to Sepolia, because no Hiero state-proof source exists yet (the Sepolia side of the Channel uses a test-only stub verifier that accepts no bundles), so the origin route stays pending until reclaim. The Hedera Channel was opened for one Sepolia sync-committee period; following the next committee needs the same staging on the rotation path (proposal above). No real Settle order has run yet: the order book's Sepolia source becomes active on 6 October 2026 at 06:20 UTC (after its one-day notice), the settle CLPR connector is not yet registered on Sepolia, and new Sepolia messages need the Channel's next sync committee. Details: deployments/README.md.

Total testnet spend: 0.0275 Sepolia ETH and about 75 HBAR (account balance difference), of which 48.1 HBAR went on the five failed attempts that led to the trace-cap finding.

Architecture

Every ledger runs the unchanged CLPR Service and the same small set of CLPRouter contracts. Hiero is the natural hub: each ledger opens one Channel to it, and routes go spoke → hub → spoke.

flowchart LR
    subgraph A["Ledger A (e.g. Ethereum)"]
        appA["Sender app"] --> rA["ClprRouter"]
        rA -- sendMessage --> sA["ClprService<br/>(unchanged)"]
    end
    subgraph H["Hiero / Hedera (hub)"]
        sH["ClprService<br/>(unchanged)"] -- onClprMessage --> rH["ClprRouter"]
        rH -- sendMessage --> sH
        regH["ProviderRegistry"]
        vH["QuarantineVault"]
        rH -. reads .-> regH
        rH -. "diverted funds" .-> vH
    end
    subgraph B["Ledger B (e.g. an L2)"]
        sB["ClprService<br/>(unchanged)"] -- onClprMessage --> rB["ClprRouter"]
        rB --> appB["Receiving app"]
    end
    sA ==>|"CLPR Channel A–H<br/>verified state proofs"| sH
    sH ==>|"CLPR Channel H–B<br/>verified state proofs"| sB
    P[("Provider committee<br/>k of n, off-chain")] -. "signed decisions,<br/>relayed by anyone" .-> regH
    Planner["Planner SDK<br/>(sender side)"] -. "route + quote" .-> appA
Loading
Component What it is
ClprRouter One per ledger, deployed at a canonical CREATE2 address. Immutable: no admin key, no pause, no upgrade. 23,175 B runtime (EIP-170 limit 24,576 B).
ProviderRegistry Append-only, hash-chained register of k-of-n committee decisions: certifications, disables, blacklist.
QuarantineVault Per-ledger vault for funds diverted by the blacklist, with fixed release rules.
ClprRouterDeployer Deploys each ledger's Router at its canonical address; every hop refuses non-canonical Routers.
src/settle/ Settle on Hedera: SettleOrderBook, SettleDeposit, SettleDelivery.
proto/ ClprRouteEnvelope / ClprRouteReceipt protobuf schema, with cross-checked Solidity and TypeScript codecs.
sdk/ TypeScript planner (k-shortest paths, five modes, filters, quotes), envelope builder, ISO 20022 module, measured route data for 86 chains.
services/ Optional indexer, route status API, quote service, permissionless forward trigger; reference Settle Connector.

Lifecycle of a routed message

sequenceDiagram
    autonumber
    participant S as Sender app (A)
    participant RA as Router A
    participant RH as Router on Hiero
    participant RB as Router B
    participant D as Receiving app (B)
    S->>RA: send(route, mode, filters, deadline) + escrow + fee budget
    RA->>RA: check structure, deadline, fees, disables,<br/>filters at a pinned registry version, blacklist
    RA->>RH: CLPR message over Channel A–H (verified by H's verifier)
    RH->>RH: authenticate previous hop, replay check,<br/>re-check every rule, take hop fee
    RH->>RB: CLPR message over Channel H–B
    RB->>D: onRouteMessage(routeId, origin, sender, payload)
    D-->>RB: result
    RB-->>RH: receipt (DELIVERED / FAILED / EXPIRED / QUARANTINED)
    RH-->>RA: receipt
    alt DELIVERED
        RA->>S: escrow to payee, hop fees paid, rest refunded
    else FAILED or EXPIRED, or no receipt by deadline + grace
        RA->>S: refund (reclaim is two-phase and permissionless)
    else a party is blacklisted
        RA->>RA: funds to the quarantine vault, notices to both parties
    end
Loading

On the reference Solidity CLPR Service, a hop that forwards from inside delivery meets the Service's reentrancy lock; the Router records the hop as pending and anyone completes it in the next transaction with forward() or flush() (every check runs again). On a Service that allows sends during delivery, the hop goes out in the same transaction.

Settle on Hedera

Pay on chain Y, be paid on chain X by a bonded Connector, with the guarantee held on Hedera. CLPR proofs only need to flow chain → Hedera, so it works with the verifiers that exist today. If the Connector misses the deadline, anyone can trigger the payout of cover plus penalty from its bond, on Hedera. Design, measurements and gaps.

sequenceDiagram
    autonumber
    participant U as User wallet
    participant C as Connector (off-chain)
    participant DY as SettleDeposit (chain Y)
    participant DX as SettleDelivery (chain X)
    participant B as SettleOrderBook (Hedera)
    U->>C: request quote
    C-->>U: EIP-712 signed quote (order id = digest)
    U->>DY: deposit(quote, sig) + amountIn
    DY-->>B: CLPR proof: DEPOSIT → order OPEN, bond reserved
    C->>DX: deliver(orderId, recipient, amountOut)
    DX-->>B: CLPR proof: DELIVERY → order DELIVERED, bond freed
    alt no proven delivery by deadline + grace
        U->>B: claimDefault(orderId), callable by anyone
        B->>U: cover + penalty from the Connector's bond
    end
Loading

Every order-book step fits one Hedera transaction (largest measured submitBundle with 5 deposits: 4,328,991 gas, an estimated 26.7 % of the trace cap); relayers cap bundles to Hedera at 12 messages.

The provider: narrow powers, enforced on-chain

flowchart TB
    subgraph CAN["Can (k-of-n or more signatures, public, time-limited)"]
        c1["Certify networks for ISO 20022, MiCA, Energy<br/>(k, after 7 days' notice, expires within a year)"]
        c2["Disable a Channel direction, ledger, Router deployment<br/>or version (k + 1, lapses after 7 days); re-enable (k, after notice)"]
        c3["Blacklist an account after an exploit (k + 1, lapses after 30 days):<br/>its routed funds go to the quarantine vault"]
        c4["Change the committee<br/>(supermajority, after notice)"]
        c5["Approve Channel directions, naming their verifier<br/>(k, after 7 days' notice): Routers use no other Channel"]
    end
    subgraph CANNOT["Cannot (no code path exists)"]
        n1["Change, pause or upgrade Routers"]
        n2["Change fees, Channels, verifiers or Connectors"]
        n3["Move funds anywhere but the quarantine vault"]
        n4["Release vault funds to any committee account"]
        n5["Change certifications under routes in flight: they pin the registry version"]
    end
Loading

The vault pays only the original sender, the original recipient, or a recovery address named by k + 1 members after a public notice and a challenge window that every deposit gets in full. Worst case with compromised committee keys: false filter labels, a weak Channel approved after a week's public notice, stopped routes, funds parked in the vault until the committee is replaced. Full analysis: threat model.

Modes and filters

Route planning runs on the sender's side (sdk/); every hop re-checks the result on-chain, so a bad quote can make a route fail but cannot redirect funds.

Mode Optimises
Cheapest Total fees in one quote currency
Fastest Expected (p90) time to delivery
Most reliable Chance of on-time delivery at a trust floor, with a disjoint fallback route
Greenest Estimated kgCO2e per message, from certified emissions figures
Balanced (default) Weighted cost, time, reliability and carbon
Filter Every ledger on the route, origin and destination included, must hold
ISO 20022 A committee certification for ISO 20022 messaging (pacs.008/009/002, camt.056/029, pacs.004 supported; the UETR travels end to end; payment messages encrypted to the destination institution, only hashes on-chain)
MiCA A committee certification for MiCA
Energy A committee certification with its emissions figure (µgCO2e per transaction) and source

Filters combine with any mode (fastest + ISO 20022, greenest + MiCA + Energy) and with constraints: max hops, deadline, fee cap and an optional on-chain trust floor. Evidence and draft decisions: registry-data/.

Why CLPRouter

Versus a full mesh of Channels. Without a router, every pair of ledgers needs its own Channel and a verifier in each direction: 100 ledgers means 4,950 pairs. With CLPRouter, one Channel per ledger to a hub is enough: 100 ledgers, 100 Channels. Forwarding, fee budgets, end-to-end receipts, deadlines and refunds are built once instead of in every application.

Compared with other interoperability designs (factual differences, not a ranking):

Design How messages are trusted Where CLPRouter differs
Validator- or multisig-signed bridges A separate signer set signs off on source-chain events No extra signer set: each hop is accepted only if its CLPR Channel's verifier accepts the proof. The provider committee cannot create or alter messages.
Intent / solver networks Solvers fill orders and are repaid after a settlement check Settle on Hedera is intent-like, but the solver (Connector) posts an on-chain bond on Hedera and both payments are proven to Hedera with CLPR proofs; a missed deadline pays the user from the bond.
IBC Light-client connections between pairs of chains Closest in spirit. CLPR provides the pairwise verified Channels; CLPRouter adds multi-hop routing, route selection, compliance filters and escrow settlement on top, as an application.
Hosted messaging networks (CCIP-style) The messaging network's own nodes and risk controls CLPRouter is not a messaging network: it reuses CLPR for transport and verification and adds no off-chain party that messages depend on. Its services are optional conveniences.

Quick start

Prerequisites: Foundry v1.5.1 (foundryup -i v1.5.1), Node.js 22.13 or later, pnpm and jq.

git clone --recurse-submodules https://github.com/ColdAI-org/clprouter && cd clprouter

forge test --skip 'script/**'                # 384 contract tests in 25 suites: unit, fuzz, invariant, security, three-ledger
(cd sdk && pnpm install && pnpm test)        # 213 tests: planner, envelope codec, ISO 20022
forge build                                  # artefacts for the services' anvil integration test
(cd services && pnpm install && pnpm test)   # 175 tests: services, Settle Connector, anvil integration

make demo                  # three anvil chains, five routes A → B → C and back, summary table (about 9 minutes)
script/settle-e2e/run.sh   # Settle on Hedera on three anvil chains, four scenarios, with Hedera trace-size checks

make demo runs script/e2e/run.sh, which sends five routes and checks each outcome: delivered with escrow released, destination app reverts (FAILED), edge disabled mid-route (FAILED), recipient blacklisted (QUARANTINED), deadline passed (EXPIRED). Gas per transaction lands in e2e-out/gas.tsv. A route through a local Hiero network (Solo) is in script/e2e-hiero/. Reproducing the testnet deployment: deployments/README.md. make demo-docker runs the same demo with only Docker installed, and make help lists the other targets (build, coverage, gas snapshot, halmos, slither). To use the SDK in your own project, see sdk/README.md.

Test counts were measured on this branch (forge test --summary, pnpm test in sdk/ and services/). CI runs the same suites on every push plus 10,000-run fuzzing, invariants, halmos proofs, coverage, a gas snapshot, Slither and CodeQL (engineering quality); the three-chain e2e runs nightly.

Security model and audit status

  • No new trust in the data path. Each hop is verified by its Channel's CLPR verifier. A route is as strong as its weakest hop, including the Router on each hop.
  • No operator. Routers are immutable, have no admin key and cannot be paused. New versions deploy beside old ones; each envelope names the version every hop must run. Forwarding is permissionless.
  • Canonical Routers only. Every Router a route names must sit at its ledger's canonical CREATE2 address, so every envelope on the wire was built by Router code.
  • A narrow provider role, enforced by the contracts (above).
Review Scope Result
Internal review 1 Router, codec, envelope (docs/audit/router-findings.md) 2 High, 5 Medium, 3 Low, all fixed with a regression test each; 6 informational (4 fixed, 1 partly, 1 acknowledged)
Internal review 2 Registry and vault (docs/audit/registry-vault-findings.md) 2 High, 4 Medium, 2 Low, 2 Info; all fixed, each with a regression test
Re-review Whole repository In progress
External audit — None yet. Required before any mainnet use.

Threat model: docs/threat-model.md (residual risks R1–R12, Settle on Hedera in section 8). Auditor pack: docs/audit-readiness.md. Report security problems privately as described in SECURITY.md, never in a public issue.

Status

Contracts, SDK, services Built and tested; deployed on Sepolia and Hedera testnet (record)
Chain → Hedera Live on testnet over EthMainnetVerifier for one sync-committee period; continuous operation needs staged rotation
Hedera → chain Waits on a Hiero state-proof source (block-node getStateProof); runs on a test verifier in local tests only
Forwarding On the reference CLPR Service each intermediate hop completes in a second, permissionless transaction
Settle on Hedera Contracts and reference Connector deployed on testnet; no real order yet
Security Two internal audits, every High, Medium and Low finding fixed; re-review in progress; no external audit
Provider A real committee, its key ceremony and legal review of the quarantine vault are prerequisites for mainnet

Roadmap

Next Depends on
Staged sync-committee rotation, so a Hedera Channel over EthMainnetVerifier keeps running across periods Upstream CLPR verifier change (proposal) or an adapter on the rotation path
Hiero → chain legs (receipts back to the origin, Hedera as a full hub) A Hiero state-proof source for EVM storage slots
First real Settle order on testnet; payment provers for Bitcoin, XRPL, Stellar Sepolia CLPR connector; per-chain payment parsing
One-transaction forwarding A CLPR Service that allows sendMessage during application delivery
External audit, then a real provider committee with an HSM key ceremony Funding and committee members
Phase 4: per-hop escrow and asset routing —

For the LFDT CLPR team

Contributing and governance

Contributions are welcome under the DCO: see CONTRIBUTING.md, GOVERNANCE.md and the Code of Conduct. lib/clpr-smart-contracts stays unchanged: CLPRouter must keep working as a plain CLPR application.

License

MIT © 2026 ColdAI. CLPR itself, included as a submodule, is licensed separately under Apache-2.0 by LF Decentralized Trust. CLPRouter is an independent project built on CLPR, maintained by ColdAI; it is not an LF Decentralized Trust project.


ColdAI
Built by ColdAI

About

Multi-hop routing for CLPR: send messages and payments between any two ledgers, with cheapest/fastest/reliable/greenest routes and ISO 20022, MiCA and Energy filters.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages