Use this file to discover all available pages before exploring further.
Multi-party workflows are where Canton’s architecture shines compared to Ethereum. This page covers the key patterns and how to think about them differently.
Since Canton requires all signatories to authorize contract creation, you can’t create a multi-party contract unilaterally. The standard pattern is propose-accept:
-- Step 1: Alice creates a proposaltemplate TradeProposal with proposer : Party counterparty : Party asset : Text price : Decimal where signatory proposer observer counterparty -- Counterparty can see the proposal choice Accept : ContractId Trade controller counterparty -- Only counterparty can accept do create Trade with buyer = counterparty seller = proposer asset price choice Withdraw : () controller proposer -- Proposer can cancel do pure ()-- Step 2: Acceptance creates the multi-party contracttemplate Trade with buyer : Party seller : Party asset : Text price : Decimal where signatory buyer, seller -- Both must have agreed
template Asset with owner : Party delegate : Optional Party -- Optional delegate where signatory owner choice Transfer : ContractId Asset with newOwner : Party controller case delegate of Some d -> d -- Delegate can act if set None -> owner -- Otherwise owner acts do create this with owner = newOwner choice SetDelegate : ContractId Asset with newDelegate : Party controller owner do create this with delegate = Some newDelegate
-- Delegation authority as a separate contracttemplate DelegationAuthority with principal : Party -- Who grants authority agent : Party -- Who receives authority scope : [Text] -- What actions are allowed where signatory principal observer agent nonconsuming choice ActOnBehalf : () with action : Text controller agent do assertMsg "Action not in scope" (action `elem` scope) -- Perform delegated action... pure ()
data WorkflowState = Proposed | Approved | Settledtemplate Workflow with initiator : Party approver : Party settler : Party state : WorkflowState payload : Text where signatory initiator observer approver, settler choice Approve : ContractId Workflow controller approver do assertMsg "Must be in Proposed state" (state == Proposed) create this with state = Approved choice Settle : ContractId Workflow controller settler do assertMsg "Must be in Approved state" (state == Approved) create this with state = Settled
Canton can atomically update multiple contracts in a single transaction:
choice ExecuteSwap : () with assetA : ContractId Asset assetB : ContractId Asset controller buyer, seller do -- Both happen atomically or neither does exercise assetA Transfer with newOwner = buyer exercise assetB Transfer with newOwner = seller