A private, non-custodial OTC venue for institutional block trades — built on Canton.
Umbra is a dark request-for-quote (RFQ) venue where a buyer privately solicits competing quotes from multiple dealers and settles the winning trade as a single, atomic delivery-versus-payment (DvP) swap. Competing dealers are cryptographically blind to one another — not by application logic, but by Canton's sub-transaction privacy and signatory rules. No dealer can see whether a rival was even invited, let alone what they quoted.
Every public blockchain leaks block trades. Order flow, counterparties, and size are visible to anyone — which is precisely why institutions cannot use them for real size. Canton was built to fix exactly this. Umbra is the proof.
Challenge track: Private DeFi & Capital Markets Network: Canton DevNet (Seaport validator) Status: Live end-to-end on DevNet — privacy, atomic settlement, external-party signing, and CIP-56 standard settlement all working against real ledger infrastructure.
- Demo video: link
- Live app: https://um-bra.app/
- Contact / socials: link
Umbra is not a trading app that happens to be on Canton. It is a venue that cannot meaningfully exist anywhere else, because it depends on three Canton-native properties:
-
Sub-transaction privacy. On a transparent chain, every quote is public to all participants. On Canton, a
Quotecontract is only disclosed to its stakeholders (the requester and the quoting dealer). Dealer 1's quote is invisible to Dealer 2 at the ledger level — the data is never sent to them. This is enforced by the protocol, not hidden by a frontend. -
Atomic, multi-party settlement. The cash leg and the asset leg move in one indivisible transaction, or neither moves. There is no settlement risk, no escrow intermediary, and no moment where one party has delivered and the other has not.
-
Self-custody with no trusted operator. In signed mode, each party signs with its own external key. The venue operator coordinates the workflow but cannot forge a party's authority or move their assets. Umbra is a venue, never a custodian.
A public-orderbook DEX clone fights the chain it runs on. Umbra uses the one thing Canton uniquely offers.
| # | Guarantee | How it's enforced |
|---|---|---|
| P1 | No information leakage — dealers are blind to each other | Canton signatory/observer rules; rival quotes are never disclosed to the ledger query of a competing dealer |
| P2 | Atomic DvP — both legs settle or neither does | Custom settlement engine: cash is locked on accept, then swapped against the asset in a single transaction |
| P3 | External-party signing — no trusted operator | Parties onboarded with their own keys; transactions assembled, signed, and executed under each party's own authority |
| P4 | CIP-56 Holding interface — standards-compliant assets | Holdings implement the Splice Holding interface, rendering a standard HoldingView |
| P5 | CIP-56 allocation-based atomic DvP — settlement via the token standard | Each party creates its own Allocation (signatory = sender); the executor assembles a fully-signed settlement and fires a single atomic ExecuteTransfer across both legs |
| P6 | Sound settlement — underfunded trades are rejected, not silently minted | Each allocation leg asserts the holder actually holds enough (cash.amount >= legAmount, inst.quantity >= legQty) before any transfer; proven by headless daml test cases confirming an underfunded buyer or under-delivering dealer cannot settle |
The headline engineering result is P5: a working CIP-56 allocation-based atomic swap, in both operator mode and trust-no-operator signed mode, on live DevNet.
P5 is also sound: the atomic transfer refuses to execute unless each party genuinely holds its leg — verified by headless tests, so the guarantee holds without a live ledger.
Requester Umbra venue Dealer 1 Dealer 2
| | | |
|-- create RFQ ----------->| | |
|-- invite D1, D2 -------->|---- invitation --------->| |
| |---- invitation -------------------------->|
| |<--- private quote -------| |
| |<--- private quote --------------------------|
| (sees BOTH quotes) | (D1 cannot see D2's quote, or vice versa)
|-- award / CIP-56 ------->| | |
| |==== atomic DvP swap =====| |
| cash --> dealer, asset --> requester, in ONE indivisible transaction
The requester chooses which dealers to invite — this is relationship-based block trading, by design. The privacy that matters is dealer-to-dealer blindness: it stops inter-dealer information leakage, which is what gives the requester honest, competitive pricing.
umbra/
daml/
Umbra.daml Core market contracts + custom atomic settlement engine
UmbraDvP.daml CIP-56 allocation-based atomic DvP (standards-compliant path)
UmbraTest.daml Demo init script
backend/
server.js Express API over the Canton JSON Ledger API v2
token.js OAuth client-credentials auth + ledger fetch with 401 retry
external.js External-party onboarding + prepare/sign/execute (signed mode)
public/
landing.html Marketing landing page (/)
index.html The live trading terminal (React via CDN) (/app)
vendor/ Vendored Splice CIP-56 interface DARs (v1.0.99)
Umbra.daml — the core venue and the custom settlement engine:
CashHolding,InstrumentHolding— tokenized cash and securities, implementing the CIP-56Holdinginterface (P4).Rfq— a request for quote, signed by the requester.RfqInvitation— a dealer's invitation to quote (Invite/DeclineInvitation).Quote— a dealer's private bid (SubmitQuote), visible only to requester + that dealer (P1).SettlementInstruction— created onAcceptQuote(which locks the requester's cash by archiving it);Settlethen performs the atomic swap (P2).
UmbraDvP.daml — the CIP-56 allocation-based settlement path (P5):
CashAllocation,InstrumentAllocation— each implements the SpliceAllocationinterface, with signatory = the sending party, so each party authorizes its own leg. The operator never forges authority.DvPProposal->DvPDealerAccepted->DvPSettlement— a propose/accept choreography. Because the finalDvPSettlementis signed by requester, dealer, and executor, itsExecuteDvPchoice carries enough authority to drive the nestedAllocation_ExecuteTransferon both legs atomically. This solves the authority-propagation wall that a naive flat multi-party submission hits.
Why the choreography matters:
Allocation_ExecuteTransferrequires the authority of executor, sender, and receiver. A flatactAs: [a, b, c]submission does not propagate that authority through a nested interface exercise. The propose/accept flow gathers each party's signature onto a single jointly-signed contract whose choice then carries all the authority required for the atomic transfer.
A thin Express layer over the Canton JSON Ledger API v2. It does not hold keys to user assets and is non-custodial by design. Selected endpoints:
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /api/mode |
Toggle operator / signed (trust-no-operator) mode |
| POST | /api/rfqs |
Create an RFQ |
| POST | /api/rfqs/:cid/invite |
Invite a dealer |
| POST | /api/invitations/:cid/quote |
Submit a private quote |
| GET | /api/quotes?role=... |
Role-scoped quote view (proves P1: dealers see only their own) |
| POST | /api/quotes/:cid/accept |
Lock cash, create settlement instruction |
| POST | /api/settlements/:cid/settle |
Custom atomic DvP swap (P2) |
| POST | /api/award |
One-call custom atomic settlement |
| POST | /api/dvp/award |
One-call CIP-56 allocation-based atomic settlement (P5) |
| GET | /api/holdings?role=... |
Holdings / balances per party |
Key helpers: cleanupRfq (archives an RFQ and its quotes/invitations after settlement), pollNewCid (handles asynchronous signed-mode commits), and partyIdFor / roleOfParty (operator- vs. external-party identity mapping).
A single-file React terminal (loaded via CDN — no build step) that renders all four party views side by side so the privacy asymmetry is visible: the requester sees every quote, each dealer sees only its own (rival quotes show as redaction bars), and a public observer sees nothing. A settlement overlay animates the atomic swap as the climax of a trade.
- Node.js v20+
- Daml SDK 3.4.11
- Access credentials for a Canton DevNet validator (ledger-API scope)
Create backend/.env (never commit this):
AUTH_URL=https://auth.sandbox.fivenorth.io/application/o/token/
CLIENT_ID=validator-devnet-m2m
CLIENT_SECRET=your_secret_here
AUDIENCE=validator-devnet-m2m
SCOPE=daml_ledger_api
LEDGER_API=https://ledger-api.validator.devnet.sandbox.fivenorth.io
SYNCHRONIZER_ID=global-domain::1220...
REQUESTER=Requester::1220...
DEALER1=Dealer1::1220...
DEALER2=Dealer2::1220...
export PATH="$HOME/.daml/bin:$PATH"
daml build
cd backend
npm install
node server.js
- Landing page: http://localhost:4000/
- Trading terminal: http://localhost:4000/app
- Health check: http://localhost:4000/health
Umbra demonstrates the full private-RFQ-to-atomic-settlement lifecycle end to end on live DevNet. For the hackathon, holdings are minted as test stand-ins for what would be bank-issued tokenized cash and registry-issued tokenized securities in production — the atomic settlement logic is identical either way. The focus is the hard part: private quoting and standards-compliant atomic DvP, both proven live.
- Settle against parties' standing balances. Today each settlement is funded per-trade; the next step binds settlement to persistent balances. Atomic change-return and underfunded-trade rejection are already proven — this extends them to standing balances.
- Real issuer parties for tokenized cash and tokenized securities, replacing minted stand-ins with registry-issued assets.
- Full multi-party external-signer execution via a dedicated executor party.
- Loop Wallet / Canton Coin onboarding for end-to-end self-custody UX.
- Configurable dealer panels and persistent settlement history.
MIT