Module 0 · Lesson 2 · ~30 min read + interactive
Daml is the language every Canton contract is written in. This is a working-knowledge lesson, not a Daml masterclass — your goal is to read Daml code, understand what ledger changes it will cause, and know what's happening when your Go code submits a command.
Daml is a pure functional DSL for defining templated contracts, their data, and the choices (state transitions) that each stakeholder is allowed to exercise — with the privacy model baked into the type system.
Canton's unit of currency in every sense is the template. A template describes a kind of contract — its fields, who the stakeholders are, and what actions can be taken on instances of it.
module Iou where
template Iou
with
issuer : Party
owner : Party
amount : Decimal
currency : Text
where
signatory issuer, owner
observer []
choice Transfer : ContractId Iou
with
newOwner : Party
controller owner
do
create this with owner = newOwner
choice Settle : ()
controller issuer
do
return ()
Five concepts in fifteen lines. All of them matter.
with) and the behavior (choices via choice). An instance of a template is a contract.controller (who's allowed to invoke it) and a body (what it does, in do syntax that resembles Haskell).Transfer, only the owner can transfer. On Settle, only the issuer can settle. If a non-controller tries, the transaction is rejected before it even reaches the synchronizer.By default a choice is consuming — exercising it archives the current contract. The Transfer choice above consumes the old IOU and creates a new one with a different owner. On the ledger, you now have: old IOU (archived), new IOU (active).
Why? Because Daml wants state changes to be explicit, immutable, and auditable. There's no mutation in place. Every historical state is visible in the transaction log forever.
-- explicitly non-consuming: contract stays active
nonconsuming choice Peek : Decimal
controller owner
do
return amount
Peek doesn't archive the contract — useful for read-only queries that still need to pass through the ledger (and thus be ordered and auditable).
This is where Daml earns its place over "just use Solidity." Consider the IOU above. When Alice (issuer) creates an IOU for Bob (owner):
Now Alice transfers the IOU to Carol. Carol becomes a new signatory on the resulting contract. The Transfer transaction informs Carol's participant. Bob's participant still sees the archive event (because Bob was a stakeholder on the archived contract), but Bob never sees the new contract's contents — he sees that his IOU was transferred, but not to whom.
Privacy in Daml is type-level and mandatory. You can't accidentally broadcast a contract. The set of parties who see a contract is determined entirely by the signatory and observer declarations — nothing else. The compiler enforces this.
Below is a tiny simulated Daml ledger. The Iou template from above is pre-loaded, and you can see what different actions do to the ledger state. This isn't a real Daml compiler — it's a faithful simulation of the lifecycle so you can see what "create," "exercise," and "archive" mean as ledger operations.
Play with it for a minute. Notice: (1) every action mutates the ledger by archiving and/or creating, never by editing in place; (2) every contract knows its signatories; (3) if you wanted a real Daml runtime check, only the owner could transfer and only the issuer could settle — this toy simulator lets you exercise anything to keep the mechanics visible.
You'll see Daml files in the wild. Here's a checklist for reading any unfamiliar template:
module Foo where). Ignore for now.data Point = Point { x: Int, y: Int }.with block — that's the shape of an instance.signatory — that tells you who must authorize creation.choice — name, return type, arguments, controller, body. That's the state machine.ensure clauses, key/maintainer clauses, and interface implementations. Come back to them when you need them.Daml source (.daml) is compiled to an intermediate called DAML-LF (Ledger Fragment) — a binary, versioned, Protobuf-serialized form. This is what participant nodes actually execute. You won't write DAML-LF by hand, but you'll see it mentioned in Canton's logs, the wire protocol, and upgrade-compatibility discussions.
In the Module 7 capstone, you'll build a Go client that submits commands and streams transactions against a running Canton sandbox. The sandbox comes with a simple Daml model pre-compiled (typically an Iou- or AssetTransfer-shaped model). You don't need to write Daml — but you need to recognize what's happening on the ledger when your Go code submits a command.
| Concept | Solidity | SQL schema | Daml |
|---|---|---|---|
| State container | contract (one instance per deployment) |
TABLE |
template + many contract instances |
| Row / instance | n/a (contract is singleton) | row | contract |
| Mutation | Mutable storage fields | UPDATE / DELETE | Archive + create (immutable) |
| Who can act | Anyone with gas | Anyone with permissions | Controllers only (type-enforced) |
| Who can read | Everyone | Granted via GRANT | Signatories & observers only (type-enforced) |
| History | Global chain history | Optional (audit tables) | Always — transaction stream per party |