Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.canton.network/llms.txt

Use this file to discover all available pages before exploring further.

Real-world Daml applications involve multiple parties with different roles, permissions, and trust relationships. This deep dive covers the Daml design patterns that make complex multi-party workflows work — from simple two-party agreements to multi-step authorization chains spanning several organizations.

The Propose-Accept Pattern

The most fundamental multi-party pattern in Daml. Since no party can unilaterally create a contract that binds another party (the signatory rule), you need a two-step process:
template AgreementProposal
  with
    proposer : Party
    counterparty : Party
    terms : Text
  where
    signatory proposer
    observer counterparty

    choice Accept : ContractId Agreement
      controller counterparty
      do create Agreement with
           partyA = proposer
           partyB = counterparty
           terms

    choice Reject : ()
      controller counterparty
      do pure ()

template Agreement
  with
    partyA : Party
    partyB : Party
    terms : Text
  where
    signatory partyA, partyB
The proposal is signed only by the proposer. The counterparty sees it (as an observer) and can accept or reject. Acceptance creates the agreement with both parties as signatories. This pattern appears everywhere in Canton applications: trade proposals, license issuance requests, access grants, and governance votes.

Delegation

Delegation lets one party grant another party the authority to act on their behalf within a defined scope. Unlike the propose-accept pattern (which creates a shared agreement), delegation creates a one-directional trust relationship.
template OperatorLicense
  with
    owner : Party
    operator : Party
    allowedOperations : [Text]
  where
    signatory owner
    observer operator

    choice Operate : ContractId OperationResult
      with
        operation : Text
      controller operator
      do assertMsg "Operation not allowed"
           (operation `elem` allowedOperations)
         create OperationResult with
           performer = operator
           onBehalfOf = owner
           operation
The owner grants specific operations to the operator. The operator can exercise the Operate choice, but only for allowed operations. The owner can revoke the delegation by archiving the OperatorLicense.

Multi-Step Workflows

Many business processes require a sequence of actions by different parties. Model these as a chain of contracts, where each step’s output becomes the next step’s input:
template TradeRequest
  with
    buyer : Party
    seller : Party
    asset : Text
    price : Decimal
  where
    signatory buyer
    observer seller

    choice ConfirmTrade : ContractId TradeSettlement
      controller seller
      do create TradeSettlement with
           buyer
           seller
           asset
           price

template TradeSettlement
  with
    buyer : Party
    seller : Party
    asset : Text
    price : Decimal
  where
    signatory buyer, seller

    choice Settle : ()
      controller seller
      do pure ()
Each step in the workflow is a separate template. This makes the workflow state visible and auditable — you can query the ledger to see which step any given trade is at.

Atomic Composition

Daml transactions are atomic: either all the creates and archives in a transaction succeed, or none of them do. Use this property to implement complex operations that must happen together:
choice SwapAssets : (ContractId Asset, ContractId Asset)
  controller partyA
  do -- Both transfers happen atomically
     newAssetForB <- exercise assetFromA Transfer with newOwner = partyB
     newAssetForA <- exercise assetFromB Transfer with newOwner = partyA
     pure (newAssetForA, newAssetForB)
If either transfer fails (wrong controller, contract already archived, assertion failure), neither happens. This is the foundation of delivery-versus-payment (DvP) and other settlement patterns.

Authorization Through Interfaces

Interfaces define abstract capabilities that templates can implement. Use them to create composable authorization patterns:
interface Transferable where
  viewtype TransferView
  getOwner : Party
  transfer : Party -> Update (ContractId Transferable)

  choice TransferTo : ContractId Transferable
    with newOwner : Party
    controller getOwner this
    do transfer this newOwner
Any template that implements Transferable gets the TransferTo choice. Your backend can work with the interface without knowing the specific template type, enabling generic transfer logic across different asset types.
Place interface definitions in standalone packages that contain only interfaces and no templates. An interface’s structure (methods and view type) cannot be modified after deployment. If changes are needed, introduce a new interface version in a new package.

Multi-Party Visibility Patterns

Canton’s privacy model means each party sees only the contracts where they are a stakeholder (signatory or observer). For workflows that need broader visibility without giving parties the ability to act, use the observer pattern:
template AuditableTransaction
  with
    executor : Party
    counterparty : Party
    auditor : Party
    details : Text
  where
    signatory executor, counterparty
    observer auditor  -- auditor can see but not act
For regulatory or compliance scenarios where a third party needs visibility into transactions without being a participant, add them as observers. They can read the contract data through the Ledger API but cannot exercise choices on it.

Design Considerations

When composing multi-party workflows:
  • Keep the signatory set minimal — each additional signatory adds coordination overhead
  • Use observers for read-only access rather than making parties signatories
  • Design templates so that each party’s choices are clear from the template declaration
  • Avoid deep transaction trees (many nested exercises) as they increase transaction size and latency
  • Consider whether a workflow step needs to be on-ledger or can happen off-ledger

Next Steps