Daml’s authorization model is genuinely one of the better things about it. Solidity makes you bolt on
access control by hand. Daml pushes it into the language itself. That’s enforced, not correct. The rules
run exactly as you wrote them. Writing the right rules is still on you.
After auditing Daml contracts across financial services, capital markets, and enterprise workflow systems, here are the five authorization mistakes we keep seeing.
Over-permissive controllers
Every choice in Daml needs a controller: the party authorized to exercise it. The most common mistake is making that controller too broad.
choice Transfer : ContractId Asset
with newOwner : Party
controller owner
do create this with owner = newOwner
Reads fine. But what if owner is a role that multiple real-world users map onto? If your off-ledger
identity layer lets more than one principal act as owner, the contract has no way to tell a legitimate
transfer from a fraudulent one. Pin down what owner means operationally, and audit the identity model the contract sits on. The contract alone won’t save you.
Missing co-authorization on sensitive actions
Some actions should require agreement from multiple parties. Developers often model them with a single
controller “for now,” planning to add multi-party logic later. Later rarely comes.
A contract that can be archived or transferred by a single party, when the business requires bilateral
agreement, is a critical vulnerability. Model the authorization requirement first, then figure out the UX
around it.
-- Risky: only one party controls a bilateral action
controller partyA do
choice Settle : ()
-- Correct: both parties must authorize
controller partyA, partyB do
choice Settle : ()
Signatories that shouldn’t be signatories
Signatories in Daml consent to a contract’s existence and are bound by it. Developers sometimes add
parties as signatories because they want those parties to “see” the contract, which confuses the privacy
model with the authorization model. If a party is a signatory, they have to authorize every contract creation. That builds in operational dependencies, and worse, can expose parties to obligations they never agreed to carry. Use observer for visibility. Keep signatory for parties whose consent is actually required.
Fetch-based authorization bypasses
Daml lets a contract fetch other contracts during choice execution. A subtle vulnerability shows up when a contract fetches another contract to “check” a condition, but the fetched contract’s authorization model doesn’t actually guarantee what the developer thinks it does.
For example: fetching a “KYC approval” contract and assuming its existence means the counterparty is
verified. If anyone with the right template can create that contract, you’ve written a logic error the
type system won’t catch.
Always ask: who can create this contract I’m fetching, and does their ability to create it actually mean
what I think it means?
Exercising choices on behalf of the wrong party
Multi-party workflows usually have an operator or intermediary orchestrating things. The mistake is
letting that operator exercise choices on behalf of end parties when the business intent is that only the
end party should be able to act.
This gets ugly fast in custody and asset management. If an operator can move a client’s assets without the client authorizing it in the moment, you’ve built a backdoor into your custody model. How trusted the
operator is supposed to be is beside the point. Backdoors get used.
Key Takeaway
Daml catches a lot of authorization bugs for free. It cannot catch a business intent that was wrong before anyone wrote code. Every signatory, observer, and controller is a security choice, whether or not you were thinking about security when you picked it.
