Daml contracts, an operator backend, a React frontend, wallet handoff, tests, and runbooks for RFQs, prefunded orders, pools, swaps, and LP tokens.
New to Canton but comfortable with AMMs? Follow the single newcomer learning path. It starts with the Canton/Daml mental model, runs each proof mode, traces one swap, and ends with a tested first code change.
Quick Start · Documentation Site · Features · Architecture · Workflows · Docs · Contributing
git clone https://github.com/srikanth-bitdynamics/Canton-Dex-Reference-Implementation.git
cd Canton-Dex-Reference-Implementation
(cd services/operator-backend && npm ci)
(cd app/web && npm ci && cp .env.example .env.local)
# Then use the split-terminal Quick Start below. |
Canton DEX is a runnable reference implementation of exchange workflows on Canton. It shows how market state, wallet-authorized funding, registry-defined holdings, V2 allocations, and atomic settlement batches fit together in one application.
In AMM terms: a holding is a token balance, an allocation locks a trader's funds for a single trade, and a settlement batch is the one atomic step that exchanges them. The RFQs, orders, pools, swaps, and LP tokens named above are those Canton pieces assembled into an exchange.
It is designed to be:
- Readable: Daml templates and docs explain the workflow boundaries.
- Runnable: the browser preview and Daml-engine tests run without a Canton participant; real wallet settlement uses a configured participant.
- Verifiable: Daml tests and TypeScript tests cover the reference flows.
- Forkable: builders can reuse the Daml, backend, frontend, or docs.
Note
This is a reference implementation, not an audited turnkey production exchange. Production adopters should perform their own security review, operational hardening, compliance work, and version-compatibility checks.
The repository has three deliberately different run modes:
| Mode | Best for | Honest boundary |
|---|---|---|
| Browser preview | screens, seeded reads, quotes, wallet-intent UI | TypeScript in-memory ledger and Mock Wallet; no Daml or value settlement |
| Daml-engine tests | choices, authorization, atomicity, conservation | Daml Script runner; no browser, backend, or Canton participant |
| Live Canton proof | real Canton process, JSON Ledger API, package upload, distinct LP/swapper parties, and add → quote-bound swap → partial remove value movement | direct-ledger driver only; no backend HTTP, browser, external wallet, or persistent state |
See Getting started for the commands and expected results for each mode.
Token Standard V2 gives Canton applications a shared way to represent holdings, allocations, allocation requests, and settlement. A DEX touches all of those surfaces at once.
|
RFQs, matched trades, orders, swaps, and LP flows are implemented as real Daml workflows, not just diagrams. |
The repo includes Daml contracts, backend orchestration, wallet handoff, frontend screens, tests, and operator runbooks. |
Funds move through V2 holdings, allocations, allocation requests, and settlement factories instead of a custom off-ledger balance model. |
| Feature | Status | Notes |
|---|---|---|
| Pair listing | Implemented | DexPair records base, quote, fee model, and trading mode |
| RFQ and matched trades | Implemented | TradingAppV2-style allocation request and batch settlement |
| Prefunded orders | Implemented | Orders are backed by V2 allocations |
| Order matching | Implemented | Reference price-time-priority matcher in the backend |
| Constant-product pools | Implemented | Pool state plus committed allocation slices |
| Add/remove liquidity | Implemented | DvP (delivery-versus-payment) request, wallet allocation, and operator settle flow |
| LP token | Implemented | LP token is identified by a V2 InstrumentId and issued through the registry used by the reference |
| Single-hop swaps | Implemented | Trader allocation plus PoolRules_Swap settlement |
| Wallet handoff | Implemented with explicit boundaries | External-wallet adapters for the Canton dapp SDK (CIP-0103), PartyLayer, and WalletConnect; DEV-only operator relay and Mock; unsafe Direct Canton experiment disabled |
| Operator backend | Implemented | HTTP API, JSON Ledger API driver, idempotency, indexing, and recovery |
| You are | This helps because |
|---|---|
| Canton or Daml builder | You get a complete Token Standard V2 app to read and run |
| DEX or venue team | You can inspect RFQ, order, pool, swap, and LP-token workflows |
| Wallet team | You can validate submit flows against a working dApp |
| Operator | You get deployment, observability, cleanup, and recovery patterns |
| Auditor or evaluator | You can inspect authority boundaries and settlement choreography |
If you know AMMs but not Canton, follow this order. Each step assumes only the steps before it:
- Canton and Daml primer
- Overview
- Getting started: run the three proof modes
- Trace one AMM swap
- Understand the design in 15 minutes
- Architecture
- Workflow design
- Make your first AMM code change
- Builder guide
Keep the Glossary open as a companion. The same canonical path, including the outcome of every step, is maintained in the documentation index.
The quickest local experience is a browser preview, not a settled Canton DEX. The backend uses seeded TypeScript state and Mock Wallet returns fake contract IDs. Use the Daml tests below to exercise real contract semantics.
- Node.js 24 or newer and its bundled npm.
- Git.
- Eclipse Temurin JDK 17 or newer, DPM, and Daml SDK 3.5.2 for Daml builds/tests and the default live-Canton proof.
See the full prerequisites. If the language is new to you, start with Digital Asset's official Get started with Daml tutorial.
git clone https://github.com/srikanth-bitdynamics/Canton-Dex-Reference-Implementation.git
cd Canton-Dex-Reference-Implementation
(cd services/operator-backend && npm ci)
(cd app/web && npm ci && cp .env.example .env.local)cd services/operator-backend
ALLOWED_ORIGINS=http://localhost:5173 npm run devLeave this foreground process running. Success includes
dev server listening at http://127.0.0.1:8080. The explicit origin is
required because browser CORS access is denied by default.
In another terminal:
cd app/web
npm run devOpen the URL Vite prints, normally http://localhost:5173.
The app header and warning banner identify this mode as In-memory preview — no Canton participant. If that banner is absent, verify which backend URL the dApp is using before treating any displayed state as ledger-backed.
- Confirm Trade and Pools show the seeded
BTC/USDCmarket. - Select Mock Wallet (dev) and inspect the
trader-demoholdings. - Change a swap amount and inspect the quote, fee, and price impact.
- Browse Pools, Orders, RFQ, Portfolio, and Admin to learn the surfaces.
Mock Wallet logs an intent and returns a #mock-…:0 placeholder; it does not
sign or submit a Canton transaction. Writes are 401 by default. The explicit
local-only DEX_DEV_OPEN=1 bypass opens the non-admin operator-write gate, but
allocation-backed flows can still return 501 not_supported because this
ledger does not implement them. Admin routes still require their admin token.
These are expected preview boundaries.
Install DPM, JDK 17+, and SDK 3.5.2 as described in Getting started, then run:
bash scripts/run-local-daml-tests.shA successful run builds canton-dex-trading-0.1.4.dar, reports every Daml
Script test as ok, and exits 0. At this revision there are 111 test
declarations. This proves Daml behavior, including real-holding settlement
fixtures, but still does not run a Canton participant or browser integration.
The default live path needs no Canton DevKit or Docker:
bash scripts/run-dpm-sandbox-proof.shIt builds the DAR, starts a throwaway dpm sandbox, uploads that DAR (including
its embedded Token Standard dependency closure), creates distinct LP/trader and
swapper parties, and runs the live JSON Ledger API DvP driver. The driver adds
liquidity, executes a quote-bound swap, and removes half the LP position.
Success ends with:
==> PASS: portable live-Canton proof completed
The throwaway sandbox is now stopping; no persistent ledger state remains.
This proves real ledger value movement between distinct counterparties in an
authentication-disabled local sandbox. It checks exact balances and reserves,
reserve-slice reconciliation after every phase, LP holding/supply/policy
consistency, x*y nondecrease, reserve-per-LP, and total value conservation.
Operator, admin, and LP registrar still share the bootstrap party and the
sandbox user has unrestricted throwaway rights. It does not start the operator
HTTP server, browser, or an external wallet. See
Local Canton from a clean clone for its phases,
limitations, and optional persistent environments.
The complete split-terminal procedure, exact outputs, CORS explanation, and three-mode capability table are in Getting started.
Start with the DPM sandbox proof above. It is the repository's default live path and has no dependency on Canton DevKit. DevKit is an optional, separately distributed helper for a persistent Splice LocalNet; it is not a runtime dependency of the DEX application or its DARs.
For a persistent local network, bring-your-own participant, or testnet, use:
docs/guides/localnet.mdfor the default DPM sandbox proof and optional persistent DevKit LocalNet.docs/guides/run-on-testnet.mdfor the testnet setup flow.docs/guides/deployment.mdfor Docker Compose and production environment variables.docs/guides/operator-runbook.mdfor recovery, observability, cleanup, and incident response.docs/guides/validator-test-plan.mdfor a full live-validation checklist.
A live browser flow additionally needs deployed/vetted packages, separated parties and ledger rights, long-lived registry factories and holdings, a funded pool, backend credentials, explicit CORS, and a compatible wallet. Passing the DPM sandbox proof does not establish those browser/wallet boundaries.
Self-custodial value flows keep trader authority in the wallet: order funding,
swap allocation creation, and LP add/remove allocations never require the
operator to act as the trader. The included operator-mediated RFQ routes are an explicit
exception: they require the backend's ledger user to be authorized for the
configured trader party, and Rfq_Accept also requires the operator. External
deployments should provide those authorities through their own wallet,
delegation, or co-submission design.
| Path | Purpose |
|---|---|
trading/ |
Daml package for the DEX app, LP-token component, and reference registry |
trading-tests/ |
Daml Script tests and Token Standard harnesses |
services/operator-backend/ |
Operator HTTP API, ledger submission, indexing, idempotency, pricing, and recovery |
services/registry-client/ |
Registry context and factory discovery client |
app/web/ |
React frontend and wallet-provider boundary |
scripts/ |
Build, bootstrap, deployment, and smoke-test helpers |
docs/ |
Architecture, workflows, guides, runbooks, and API reference |
vendor/splice/ |
Vendored Token Standard packages used by this reference |
React frontend
|
| HTTP reads and operator APIs
v
Operator backend
|
| JSON Ledger API submissions, indexing, recovery
v
Canton ledger
|
+-- DEX application contracts
| DexPair, Order, Rfq, MatchedTrade, Pool, PoolState, PoolSlice
|
+-- LP-token component
| LPTokenPolicy and reference-registry LP config
|
+-- Token Standard / registry contracts
Holding, Allocation, AllocationRequest, SettlementFactory
The boundary is intentionally strict:
- DEX contracts own market state and workflow validation.
- Token Standard contracts own asset reservation and settlement.
- Registries own instrument semantics and choice context. The reference
registry uses its own
InstrumentConfig, but Token Standard V2 does not require a configuration template. - Wallets own trader-authored allocation submissions in self-custodial flows.
- The operator backend submits administrative and settlement commands. Its operator-mediated RFQ path is a documented authority exception, not a self-custodial path or a public relay service.
The Daml package separates LP-token policy, venue workflows, and the reference registry by module/template. It implements upstream Token Standard V2 interfaces, but it does not define custom Daml interfaces that decouple those components into independently swappable apps.
Read docs/concepts/architecture.md and
docs/concepts/workflows.md for the full model.
| Workflow | Where To Read | Key Contracts |
|---|---|---|
| Pair listing | docs/guides/add-a-trading-pair.md |
DexPair |
| RFQ and matched trade | docs/concepts/workflows.md |
Rfq, RfqQuote, MatchedTrade, TradeAllocationRequest |
| Orders | docs/concepts/workflows.md |
OrderFundingRequest, Order, OrderAllocationRequest, OrderMatchExecution |
| Pools and swaps | docs/concepts/liquidity-and-custody.md |
Pool, PoolState, PoolSlice, PoolRules |
| Add/remove liquidity | docs/concepts/liquidity-and-custody.md |
PoolLiquidityRules, LiquidityAllocationRequest, LPTokenPolicy |
| LP and custom instruments | docs/guides/add-lp-or-instrument.md |
LPTokenPolicy, InstrumentConfig, registry extension boundary |
| Choice context | docs/guides/choice-context.md |
Registry factories and Token Standard choices |
The frontend separates external-wallet integrations from local conveniences:
- External-wallet adapters: the Canton dapp SDK (CIP-0103), PartyLayer, and WalletConnect. The SDK path is marked DvP-ready, PartyLayer remains marked unproven until a selected wallet passes the live validator plan, and the current WalletConnect adapter is marked no-DvP and rejects LP add/remove.
- Development-only adapters: the
token-standardprovider is accurately labelled Operator Relay (dev only) because the backend submits with configured ledger rights; Mock Wallet returns placeholders and performs no ledger write. Neither is registered in production builds. - Disabled experiment: Direct Canton is not registered. Its former
/v1/wallet/executetarget is not a participant Ledger API endpoint, and the app does not retain a participant bearer token in browser storage.
No external wallet is connected automatically. When several production-facing adapters are enabled, one row gets a recommendation badge, but the user still chooses and authorizes the wallet. If none is configured, production does not fall back to the operator relay.
PartyLayer live-validation steps are documented in
docs/guides/run-on-testnet.md.
# Refresh Token Standard DARs from a Splice release (optional)
bash scripts/fetch-splice-dars.sh 0.6.12
# Build the DEX Daml package
bash scripts/build-trading-surface.sh
# Build the Daml package and run every Daml test project
bash scripts/run-local-daml-tests.sh
# Run backend checks
(cd services/operator-backend && npm run typecheck && npm test)
# Run frontend checks
(cd app/web && npm test -- --run && npm run build)
# Bootstrap a Canton testnet
./scripts/deploy-testnet.sh
# Run the smoke test script
bash scripts/backend-http-smoke.sh
# Run the portable live-Canton add -> swap -> partial-remove proof
bash scripts/run-dpm-sandbox-proof.shThis reference is built on the Canton Network Token Standard V2 (CIP-0112) — the privacy, performance, and traditional-accounting revision of the base token standard (CIP-0056) — and uses the CIP-0103 dApp standard for trader-authorized wallet submissions.
The build consumes the released Splice Token Standard DARs committed under
vendor/splice/dars/. Their release and provenance are
recorded in vendor/splice/VENDOR_PIN.md. The pool relies on
iterated-settlement and committed-allocation semantics — the exact surface it
depends on is documented in
Allocation Surface.
This is a reference implementation with active development. It is appropriate for learning, evaluation, demos, and forks.
Before using it as production infrastructure, adopters should perform their own security review, operational hardening, deployment-specific compliance work, and compatibility checks against the Canton and Token Standard versions they intend to run.
Read the rendered documentation site
or the repository index at docs/README.md. The site is
published from main; documentation on an unmerged branch appears only after
that branch is merged and the Pages workflow completes. Good entry points:
- Getting Started — choose browser preview, Daml-engine proof, or live integration and see the boundary of each.
- Canton and Daml primer — the minimum ledger mental model for a newcomer.
- AMM-first walkthrough — trace one
swap from
x*y=kthrough allocation and settlement tests. - Make your first AMM code change — make a small test-first Daml refactor, check the off-ledger impact, and rerun the live-Canton proof.
- Local Canton — run the default DPM sandbox live proof; understand the optional DevKit path and exact proof boundaries.
- Overview — what it is and the trust model.
- Builder Guide — extend the reference.
- HTTP API — backend endpoints.
- Using the dApp — trader, LP, and dealer flows.
Issues and pull requests are welcome. See CONTRIBUTING.md
for development guidelines and CODE_OF_CONDUCT.md for
community expectations.
Please do not open public issues for suspected vulnerabilities. See
SECURITY.md for the disclosure process.
Apache 2.0. See LICENSE and NOTICE. Vendored upstream
packages are under vendor/ with their own attribution.
Implemented by BitDynamics as part of a Canton ecosystem development grant.