Skip to content

Overview

This is Step 2 of the canonical newcomer learning path. Complete the Canton and Daml primer first. This page shows what the DEX does, where authority sits, and how its main pieces connect.

Canton DEX is a runnable Token Standard V2 (CIP-0112) reference exchange on Canton. An exchange first agrees the terms, then moves both assets atomically. This reference shows four ways to agree the terms:

  • an automated market maker (AMM) calculates a price from two pool reserves;
  • an order book crosses compatible buy and sell limit orders;
  • a request for quote (RFQ) lets selected dealers quote a larger trade; and
  • an OTC matched trade records terms the two parties already agreed.

All four move value the same way. The holder locks funds in a Token Standard V2 allocation. A DEX choice validates the terms. The registry then settles every leg atomically. There is no custom off-ledger balance model. For swaps, orders, and liquidity, the trader keeps authority in the wallet. The RFQ demo uses a separate operator-mediated authority model.

The repo ships the Daml package, operator backend, React dApp with a CIP-0103 wallet boundary, tests, and runbooks. Its demo stack runs without a Canton participant; separate opt-in tests exercise the same flows against a live participant.

Idea Plain-language meaning Where it appears here
Holding Spendable units of one token owned by an account. BTC, quote assets, and LP shares are registry-managed V2 holdings.
Market object A DEX contract that records terms or accounting, but does not contain token balances. Order, MatchedTrade, PoolState, and Rfq describe what may happen.
Allocation Holdings locked by their owner for a named future settlement. It is narrower than an open-ended token allowance. A wallet authors the allocation that funds an order, swap, or liquidity action.
DvP batch Delivery versus payment: every required transfer succeeds in one transaction, or none does. SettlementFactory_SettleBatch moves the trader and pool/counterparty legs together.
Iterated settlement A settlement can create a successor allocation carrying the remaining locked funds. Pool slices and partially filled orders continue without asking the owner to re-fund every step.

The DEX therefore never updates an application-level balance to pretend a trade happened. DEX contracts decide what is permitted; Token Standard contracts hold, lock, and settle the actual value.

Suppose a trader wants to sell 0.1 BTC into a BTC/USDC pool:

  1. PoolState records the aggregate BTC and USDC reserves. Separate PoolSlice contracts reference the committed allocations that actually back those reserves.
  2. The backend reads PoolState to show an estimated USDC output. PoolRules_RequestSwap validates a named state and ordered slice snapshot, calculates the exact output from that snapshot, and returns the complete input-and-output allocation specification the wallet must authorize.
  3. The trader’s wallet locks 0.1 BTC in a V2 allocation. The operator cannot create this allocation on the trader’s behalf in the self-custodial flow.
  4. The backend submits PoolRules_Swap with that allocation and the reserve slices bound into the request.
  5. The choice re-derives the same output from the bound state, checks the minimum, bound contract IDs, exact signed legs, and allocations, then calls SettlementFactory_SettleBatch.
  6. BTC moves to the pool and USDC moves to the trader atomically. The choice recreates PoolState and binds the remaining reserve value to successor slices. If the pool changed after the request, the bound contract IDs are stale and the swap fails instead of silently repricing; the request must be recreated. If any check fails, neither side moves.

The other workflows change how terms are formed and what state is recreated; they do not invent a different custody or settlement mechanism.

  • Daml application builders learning how app state composes with Token Standard V2 holdings, allocations, and settlement.
  • Wallet and registry integrators validating authority, choice-context, and disclosed-contract boundaries against a complete dApp.
  • Venue operators and reviewers evaluating order, RFQ, pool, LP, recovery, and observability workflows before adapting them to a production design.

It is deliberately not a production exchange specification. It does not define governance, permissionless listing, routing, concentrated liquidity, or a decentralized operator; Non-goals explains each boundary.

There are two submission paths, split by who is allowed to sign what. A wallet signs trader-authored allocations for orders, swaps, and liquidity. The operator backend submits listing, matching, and settlement commands. The operator-mediated RFQ example also submits trader-authority commands, so its ledger user must have act-as rights for the configured trader; that exception is not a self-custodial wallet model or a public relay service. Both paths submit into canton-dex-trading, whose trading surfaces settle through a Token Standard V2 registry.

flowchart TB
  Trader["Trader React dApp — app/web"]
  Wallet["Wallet CIP-0103 (external)"]
  Operator["Operator backend services/operator-backend HTTP API · matcher · indexer"]

  subgraph Ledger["Canton ledger — canton-dex-trading package"]
    direction TB
    Surfaces["Four trading surfaces AMM pools · Order book · RFQ · OTC"]
    Registry["Token Standard V2 registry Holding · Allocation · SettlementFactory"]
    Surfaces -->|"SettlementFactory_SettleBatch"| Registry
  end

  Trader -->|"reads + orchestration APIs"| Operator
  Trader -->|"signs trader-authority commands"| Wallet
  Operator -->|"operator submissions + mediated RFQ"| Ledger
  Wallet -->|"trader-authority submissions"| Ledger
  • dApp (app/web/) — the trader-facing screens and the wallet-provider boundary (Mock, CIP-0103 SDK, WalletConnect, PartyLayer, and more).
  • Operator backend (services/operator-backend/) — HTTP API, JSON Ledger API driver, the reference matcher, indexer, idempotency, and recovery. Ships an in-memory dev ledger so the stack runs without Canton.
  • canton-dex-trading package (trading/) — the DEX templates, the LP-token component, and a reference V2 registry.
  • Registry — external by contract. The reference ships one, but V2 does not require this exact registry; the DEX integrates against InstrumentId and registry-provided choice context, not a specific config template.

The contract topology is easiest to understand by asking who may authorize each step. A service can prepare a command for another party, but it cannot submit as that party without their wallet or delegated authority.

Actor What it authorizes What it cannot do alone
Trader / liquidity provider create market intent; lock their holdings in allocations; authorize an RFQ acceptance settle or redirect pool inventory
Dealer create an RFQ quote; lock their side of an accepted trade select the winning quote or settle the trade
DEX operator list markets; propose matches; invoke validated settle and recovery choices lock a self-custodial user’s holdings or fill outside signed terms
Asset registry admin token holding, allocation, and settlement behavior for its instruments change DEX market rules
LP registrar authorize LP mint and burn accounting move base or quote reserves by itself

Each surface posts its own market object, but they converge on the same allocate-then-settle-a-batch pattern:

Surface Market object → settlement Price source
AMM pool Pool / PoolState / PoolSlicePoolRules_Swap constant-product curve, computed on-ledger
Order book OrderFundingRequestOrderOrderMatchExecution the trader’s limitPrice
RFQ Rfq / RfqQuoteRfq_AcceptMatchedTrade the dealer’s quoted price
OTC MatchedTradeMatchedTrade_Settle leg amounts both sides pre-agreed

DexPair.active and DexPair.tradingMode tell off-ledger discovery and routing which surfaces to expose. In this reference they are not on-ledger settlement gates: PoolRules and OrderMatchExecution do not fetch DexPair. A production fork that needs a ledger-enforced listing pause must bind and validate the pair contract in its terminal choices.

Settlement is grouped by registry admin. One DEX choice can call one batch per admin inside the same Daml transaction, so every batch succeeds or the whole transaction aborts. MatchedTrade_Settle shows the shape:

results <- forA (Map.toList batchesByAdmin) $ \(batchAdmin, batch) -> do
...
result <- exercise batch.factoryCid V2.SettlementFactory_SettleBatch with
settlement
transferLegs = batchLegs
allocations = batch.allocations
actors = [venue]
extraArgs = batch.extraArgs
pure (batchAdmin, result)

If your mental model is Uniswap on an EVM chain, the table below covers most of the surprises. The core shift: a token is not a balance in a shared contract but an individual holding contract you own, and a trade is an atomic multi-party settlement rather than a call into a router.

Uniswap / EVM Canton DEX / Token Standard V2 Key difference
IERC20.approve(router, amount) AllocationFactory_Allocate Locks specific holding contracts for one named settlement, not an open-ended balance allowance.
Router swapExactTokensForTokens PoolRules_Swap + SettlementFactory_SettleBatch The swap settles as one atomic multi-party batch: the input holding and the pool’s reserve slice move in a single transaction.
LP balance in the pair contract LPTokenPolicy + fungible LP Holdings LP tokens are first-class V2 holdings in the provider’s wallet, minted and burned by DvP, not a mapping entry.
reserve0 / reserve1 in the pair PoolState (pricing) + PoolSlices (custody) Reserves are a Decimal accounting figure; committed slices bound the state and input size, while PoolState remains the serialization point for one pool.
Public mempool + global state Per-party projection Contract visibility follows Canton stakeholders. The operator custodies pool allocations, while trader allocations remain under the trader’s authority in self-custodial flows.

For DvP settlement, the operator cannot spend a trader’s holdings without a trader-authored allocation. When a trader funds an order, adds liquidity, or authorizes a swap, the dApp composes that command and the trader’s wallet signs it over CIP-0103. The operator-mediated RFQ UI uses a different trust model: its backend co-submits as the configured trader and operator, and therefore needs both ledger authorities. Architecture draws these boundaries; Workflows shows how each flow choreographs them.

The canonical learning order lives in the documentation index. Use this table only to look up a topic while reading:

Doc What you’ll learn
Overview (this page) What the DEX is, the system shape, and the authority boundary.
Architecture The layered system model, component boundaries, and the executor-authority constraint that keeps operator-held funds governed on-ledger.
Workflows How each surface choreographs allocate-then-settle — the actors, contracts, and state transitions, workflow-first.
Pricing Where every executable price comes from (pool curve, limit price, quote) and why there is no oracle.
LP Tokens Why each pool’s LP share is a single, unversioned V2 instrument.
Liquidity & Custody How the pool custodies reserves as committed slices and crosses the LP boundary via DvP.
Daml proof map Exact source choices, focused Daml Script tests, and commands for each design claim.
Glossary The vocabulary: allocation, commitment, iterated settlement, DvP, slice, registrar.
Non-goals What the reference leaves out on purpose, and why.

This is a reference implementation, not an audited production exchange. It is built for learning, evaluation, demos, and forks. Production adopters should do their own security review, operational hardening, compliance work, and version-compatibility checks.

The Daml proof map connects each design claim to its current source choice and focused test. The testing reference explains what mock choreography, real-holding Daml Script, backend, UI, and live-Canton tests each prove. Test names stay there so this concept page remains readable when suites move.


Canton DEX builds on three CIPs. CIP-0056 is the base Canton Network Token Standard (holdings, transfers, metadata). CIP-0112 is Token Standard V2, the privacy / performance / traditional-accounting revision that adds the allocation and settlement surface — every asset here (base, quote, and the LP token) is a V2 instrument. CIP-0103 is the dApp standard the wallet boundary uses for trader-authorized submissions.

This repo commits the released V2 DAR dependencies so builds are reproducible; the exact Splice release is recorded in ../../vendor/splice/VENDOR_PIN.md, and the Allocation Surface reference records the committed-allocation and iterated-settlement semantics the pool depends on.

Next canonical step: Getting started. Keep the Glossary open as a companion reference.