Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

A full-stack, **Token Standard V2 (CIP-0112)** reference DEX for the Canton
Network: Daml contracts, an operator backend, a React dApp with a CIP-0103
wallet boundary, tests, and operator runbooks covering RFQs, prefunded
wallet boundary, tests, and operator runbooks, covering RFQs, prefunded
orders, constant-product pools, swaps, and LP tokens.

New here? **[Start with Getting Started](getting-started.md)** it takes you
New here? **[Start with Getting Started](getting-started.md)**: it takes you
from a clone to the full stack running locally (no Canton participant needed).
For the ideas behind the design, read the **[Overview](concepts/overview.md)**.

Expand Down Expand Up @@ -35,7 +35,7 @@ For the ideas behind the design, read the **[Overview](concepts/overview.md)**.

## All documentation

The docs follow the [Diátaxis](https://diataxis.fr/) model separating
The docs follow the [Diátaxis](https://diataxis.fr/) model, separating
learning (tutorial), tasks (how-to guides), understanding (concepts), and
lookup (reference).

Expand Down Expand Up @@ -84,11 +84,11 @@ lookup (reference).
## Also in the repo
- **[Getting Started](getting-started.md)** doubles as the local test-suite
reference (Daml, backend, and dApp commands with expected counts).
- The [Builder Guide](guides/builder-guide.md) includes a **guided tour of the
four workflow families** — pair listing, matched-trade/RFQ, prefunded orders,
- The [Builder Guide](guides/builder-guide.md) walks through the four workflow
families — pair listing, matched-trade/RFQ, prefunded orders,
and pool/swap/LP — with file and test pointers.
- [`examples/stable-pool/`](../examples/stable-pool/) is a separate Daml
project that consumes the DEX DAR a reuse proof point.
project that consumes the DEX DAR (a reuse proof point).

## Governance
[Contributing](../CONTRIBUTING.md) · [Code of Conduct](../CODE_OF_CONDUCT.md)
Expand Down
55 changes: 26 additions & 29 deletions docs/concepts/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

Canton DEX is a token-standard-native reference DEX for Canton.

It is intentionally not a generic settlement engine. What the reference leaves
out on purpose, and why, is collected in [Non-goals](non-goals.md). The goal is
to show builders how to build a real exchange directly on top of:
It is intentionally not a generic settlement engine. [Non-goals](non-goals.md)
collects what the reference leaves out on purpose, and why. The goal is to
show builders how to build a real exchange directly on top of:

- Token Standard V2 allocations and batch settlement
- registry-backed holdings whose registries implement the V2 holding,
allocation, and settlement APIs
- Canton privacy, routing, and atomic transaction semantics

## Design Inputs
## Design inputs

The architecture is based on three concrete upstream inputs.

Expand Down Expand Up @@ -63,10 +63,10 @@ extensions, now merged into `canton-network/splice` `main`:
- `FinalizedAllocation.extraTransferLegSides`
- settle results that return next-iteration allocation state

Those changes are what make it possible to use allocations not only for trade
Those changes make it possible to use allocations not only for trade
reservation but also for long-lived pool inventory.

## Core Decisions
## Core decisions

1. Token standard first
- the DEX should use V2 allocation primitives directly, not hide them behind
Expand All @@ -86,7 +86,7 @@ reservation but also for long-lived pool inventory.
`Pool` and `MatchedTrade` each carry a single `admin : Party`, and the
standard's `TransferLeg.instrumentId` is bare `Text`, so a leg cannot
name its own admin. Pairing instruments from two different registries is
therefore not expressible today — see
therefore not expressible today. See
[Registry Integration](../guides/registry-integration.md#what-the-dex-does-not-assume)

5. Instrument lifecycle stays outside DEX logic
Expand All @@ -110,7 +110,7 @@ reservation but also for long-lived pool inventory.
- therefore every permitted use of those funds should be validated by Daml
contract state and choice logic, not only by an off-ledger service

## System Model
## System model

```text
┌──────────────────────────────────────────────────────────────┐
Expand Down Expand Up @@ -142,9 +142,9 @@ reservation but also for long-lived pool inventory.
└──────────────────────────────────────────────────────────────┘
```

## Workflow-First Reading
## Workflow-first reading

The best way to read this architecture is through the workflows:
Read this architecture through its workflows:

- pair listing
- OTC / RFQ trade settlement
Expand All @@ -157,7 +157,7 @@ The best way to read this architecture is through the workflows:
Those workflows are described in [workflows.md](./workflows.md). The contracts
should be shaped around those state transitions, not the other way around.

## On-Ledger Model
## On-ledger model

### Instrument layer

Expand Down Expand Up @@ -210,9 +210,8 @@ A practical model is:
- each swap adjusts those allocations, settles them, and rolls forward the
next-iteration allocations

This is the critical architectural move: pool inventory
should be allocation-native, not a custom internal balance model with a
different settlement bridge behind it.
Pool inventory should be allocation-native, not a custom internal balance model
with a different settlement bridge behind it.

### Executor-control constraint

Expand All @@ -234,7 +233,7 @@ The intended model is:
- the off-chain operator proposes actions, but the ledger-visible contracts
validate the quantity, pair, expiry, side, and reserve references being used

> **Further reading decentralizing the operator.** This validation logic can
> **Further reading: decentralizing the operator.** This validation logic can
> itself be decentralized. See the
> [BitSafe decentralization manager proposal](https://github.com/canton-foundation/canton-dev-fund/blob/main/proposals/2026-05-BitSafe-decentralization-manager.md)
> for decentralizing the execution of validation logic, the
Expand Down Expand Up @@ -278,8 +277,8 @@ side` is protected at three levels:
`PoolRules` / `PoolLiquidityRules` choice that rewrites `reserves` asserts
inside the choice that its reserve delta equals the net slice-amount change
the same choice performs (created slice amounts minus consumed/drained
slice amounts). These are assertions on the choice's own arithmetic cheap
and contention-free so a future code change cannot silently let reserves
slice amounts). These are assertions on the choice's own arithmetic (cheap
and contention-free), so a future code change cannot silently let reserves
and slices drift apart.
- **Global equality, auditable on-ledger on demand.** The nonconsuming
`PoolRules_ReconcileState` choice takes the `PoolState` and the full list
Expand Down Expand Up @@ -307,7 +306,7 @@ Expected characteristics:
- the LP instrument definition should explain redemption policy and pool
identity

## Token Standard Usage
## Token Standard usage

### For OTC and RFQ

Expand Down Expand Up @@ -353,7 +352,7 @@ Without those semantics, a pool-backed design would drift back toward custom
escrow or off-ledger reserve tracking, which is exactly what this repo is trying
to avoid.

## Admin and Pairing Model
## Admin and pairing model

The DEX should support arbitrary trading pairs of `InstrumentId`, but
allocations still need to respect token-standard admin boundaries.
Expand All @@ -365,9 +364,7 @@ That implies:
- pool state should store active allocation references in a way that makes
admin partitioning explicit

This is an important design constraint, not an implementation detail.

## Rich Asset Lifecycle Model
## Rich asset lifecycle model

The standard holding model remains intentionally small:

Expand Down Expand Up @@ -401,10 +398,10 @@ In other words, a lifecycle-aware registry may take one instrument version in
and hand back a new version with the lifecycle side effects applied. The DEX
does not assume this is available for every registry.

The important point is that the traded asset remains a standard holding even
The traded asset remains a standard holding even
when its lifecycle is rich.

## Off-Chain Services
## Off-chain services

Off-chain services are still necessary, but their job is narrower than in older
generic-settlement architectures.
Expand All @@ -420,21 +417,21 @@ They should focus on:
They should not become the main abstraction for moving value around. The token
standard remains the settlement substrate.

## Dependency Boundary
## Dependency boundary

The reference architecture has a deliberate split:

- OTC and RFQ flows follow the `TradingAppV2` allocation-request and
per-admin `SettlementFactory_SettleBatch` pattern, against V2 allocations
only this repo declares no V1 allocation dependency
only (this repo declares no V1 allocation dependency)
- pool-backed liquidity requires the V2 allocation extensions used by this
repo: committed allocations, iterated settlement, extra leg sides, and
next-iteration allocation results

If the upstream API shape changes before landing, this repo should preserve the
same design intent even if field names or result structures move.

## Component Boundary
## Component boundary

The current implementation separates concerns by module and template:

Expand All @@ -443,7 +440,7 @@ The current implementation separates concerns by module and template:
workflows.
- `CantonDex.Registry.V2` is a reference registry used for tests and demos.

The DAR implements upstream Token Standard V2 interfaces, but it does **not**
The DAR implements upstream Token Standard V2 interfaces, but it does not
define custom Daml interfaces that decouple the LP-token component from the
DEX venue component. The shared boundary today is the Token Standard V2
holding/allocation/settlement surface plus explicit template references inside
Expand All @@ -457,7 +454,7 @@ pair admission yet. That keeps the reference small, while leaving room for
forks to add governance, multi-operator approval, or a decentralized rules
layer.

## Repository Shape
## Repository shape

```text
canton-dex/
Expand Down
8 changes: 4 additions & 4 deletions docs/concepts/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ that explains them in depth.
### Allocation
A Token Standard V2 contract that **locks** a holder's [holding](#holding) for a
specific settlement, so it can be settled atomically later. The DEX never moves
trader assets directly — it moves them by having the trader create allocations
trader assets directly. It moves them by having the trader create allocations
and then settling a batch. See [Allocation Surface](../reference/allocation-surface.md).

### AllocationFactory / `AllocationFactory_Allocate`
Expand All @@ -27,16 +27,16 @@ when its factory choices are exercised. The operator backend fetches these and
attaches them to each submission. See [Choice Context](../guides/choice-context.md).

### CIP-0056
The **Canton Network Token Standard** the base standard (holdings, transfers,
The **Canton Network Token Standard**: the base standard (holdings, transfers,
metadata) that CIP-0112 revises.

### CIP-0103
The **dApp Standard** the wallet interaction standard used for
The **dApp Standard**: the wallet interaction standard used for
[prepare/sign/execute](#prepare--sign--execute) interactive submission. The dApp
hands trader-authority commands to a wallet over CIP-0103.

### CIP-0112
The **Canton Network Token Standard V2** the privacy / performance /
The **Canton Network Token Standard V2**: the privacy / performance /
traditional-accounting revision of CIP-0056, adding the allocation + settlement
surface this DEX is built on. Often written "Token Standard V2" or "TSv2".

Expand Down
19 changes: 9 additions & 10 deletions docs/concepts/liquidity-and-custody.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LP Liquidity Custody Model
# LP liquidity custody model

This document explains how the reference pool represents LP liquidity and how
assets cross the pool boundary during add- and remove-liquidity workflows.
Expand All @@ -10,7 +10,7 @@ assets cross the pool boundary during add- and remove-liquidity workflows.
- Between operations, pool reserves are operator-authored committed
`PoolSlice` allocations. Slices are
**locality units, not LP entitlement units**: they exist so add/remove/
swap touch only the slices they source, not so a slice "belongs to" an
swap touch only the slices they source; a slice does not "belong to" an
LP.
- An LP's entitlement is **pro-rata over the aggregate reserves**
(`lpHeld / totalLpSupply × reserves`), sourced from selected slices.
Expand All @@ -35,7 +35,7 @@ if `pool.admin == pool.lpRegistrar`):
(`LP → operator` base+quote) settle into **operator-authored receiver
allocations**. `nextIterationFunding = {instrument: amount}` is applied
on the `FinalizedAllocation` **at the settle step** (not pre-funded at
allocate time — that would trip the coverage check in
allocate time, which would trip the coverage check in
`Registry.V2`), matching the pool's slice roll-forward model. The
returned next-iteration allocation cids become the new
operator-authored `PoolSlice`s.
Expand All @@ -44,7 +44,7 @@ if `pool.admin == pool.lpRegistrar`):
receives freshly-minted LP-token holdings.

The settle choice then exercises `LPTokenPolicy_RecordMint` and rewrites
`PoolState` **once** with the new reserves + `totalLpSupply`.
`PoolState` once with the new reserves + `totalLpSupply`.

## Remove (DvP at the boundary, symmetric to Swap)

Expand All @@ -60,9 +60,8 @@ two-admin settle in one transaction:
`holder → burnAccount lpRegistrar`, against the holder's burn-sender
allocation.

Then `LPTokenPolicy_RecordBurn` + a single `PoolState` rewrite. The key
correctness point is that funds reach the **holder**, not the operator's
pool account.
Then `LPTokenPolicy_RecordBurn` + a single `PoolState` rewrite. Funds reach
the holder, not the operator's pool account.

## Choreography & authority

Expand All @@ -74,7 +73,7 @@ pool account.
(`pool.lpRegistrar`, sender-side).
- The settle choices live on a **co-controlled `PoolLiquidityRules`**
contract (`{ operator, lpRegistrar }`, `signatory operator,
lpRegistrar`, choices `controller operator, lpRegistrar`) not on the
lpRegistrar`, choices `controller operator, lpRegistrar`), not on the
operator-only `PoolRules`, which has no `lpRegistrar` visibility. This
gives the settle the authority to drive both the operator-signed
`PoolState`/`PoolSlice` writes and the lpRegistrar-controlled
Expand All @@ -95,7 +94,7 @@ pool account.
`PoolState` and aborts a
stale request.

## What does NOT change
## What does not change

- Pricing / share math (`x*y=k` on aggregate reserves; pro-rata shares).
- `PoolSlice` shape (still operator-authored, no `owner`).
Expand All @@ -113,7 +112,7 @@ crediting, and the allocate factory. `RealRegistry` already supports
these semantics.

One more registry-dependent bound: pool slices are **long-lived committed
allocations**, and some registries cap allocation lifetime Amulet enforces
allocations**, and some registries cap allocation lifetime: Amulet enforces
`tokenStandardMaxTTL` (default **90 days**) from Splice 0.6.11. Against such a
registry the operator must roll slices into fresh allocations before the cap
expires; see
Expand Down
Loading
Loading