Masterclass

Canton/DAML

Enterprise Smart Contracts with Privacy-First Architecture

With CosmWasm Comparison

C
Canton
D
DAML
CW
CosmWasm
01

Introduction

Canton/DAML

Enterprise DLT

Daml is a purpose-built smart contract language designed for multi-party business workflows. Canton is the distributed ledger protocol that executes Daml contracts with unique privacy guarantees.

Philosophy

Privacy-first, legally-enforceable contracts with sub-transaction privacy.

  • Multi-party authorization built-in
  • Sub-transaction privacy
  • Functional, typed language
  • Cross-domain atomic transactions

CosmWasm

Cosmos Ecosystem

CosmWasm is a smart contract platform for the Cosmos ecosystem, using Rust to write WebAssembly-compiled contracts.

Philosophy

Composability, interoperability via IBC, and general-purpose smart contracts.

  • Rust programming language
  • IBC cross-chain messaging
  • WebAssembly execution
  • Cosmos SDK integration
02

Architecture Comparison

Aspect Canton/DAML CosmWasm
Execution Model UTXO-based (contracts as active agreements) Account-based (contracts as deployed code)
Privacy Sub-transaction privacy (only parties see relevant data) All data public on-chain
Consensus Domain-based (multiple sync domains) BFT (Tendermint/CometBFT)
Language Daml (functional, domain-specific) Rust (general-purpose)
Interoperability Cross-domain atomic transactions IBC protocol

Canton Multi-Domain Architecture

┌─────────────────┐     ┌─────────────────┐
│   Domain A      │     │   Domain B      │
│  (Bank Ledger)  │     │  (Exchange)     │
│                 │     │                 │
│  Participant 1 ─┼─────┼─ Participant 1  │
│  Participant 2  │     │  Participant 3  │
└─────────────────┘     └─────────────────┘

    Atomic transactions across domains
         via Canton protocol

CosmWasm IBC Architecture

┌─────────────────┐     ┌─────────────────┐
│   Chain A       │     │   Chain B       │
│   (Osmosis)     │ IBC │   (Juno)        │
│                 │◄───►│                 │
│  Contract A     │     │  Contract B     │
│  CW20 Tokens    │     │  NFT Market     │
└─────────────────┘     └─────────────────┘

    Cross-chain communication via
        IBC packet relay
03

Smart Contract Languages

Daml

Contracts as Agreements

-- Daml: A simple asset transfer contract
template Asset
  with
    issuer : Party
    owner : Party
    description : Text
    quantity : Decimal
  where
    signatory issuer, owner

    choice Transfer : ContractId Asset
      with
        newOwner : Party
      controller owner
      do
        create this with owner = newOwner

    choice Split : (ContractId Asset, ContractId Asset)
      with
        splitQuantity : Decimal
      controller owner
      do
        assert (splitQuantity > 0.0 && splitQuantity < quantity)
        asset1 <- create this with quantity = splitQuantity
        asset2 <- create this with quantity = quantity - splitQuantity
        return (asset1, asset2)
Key Concepts:
  • Templates - Define contract types with data and behavior
  • Signatories - Parties who must authorize contract creation
  • Controllers - Parties who can exercise choices
  • Choices - Actions that can be performed on contracts
Rust

Contracts as Programs

// CosmWasm: A simple asset transfer contract
use cosmwasm_std::{
    entry_point, DepsMut, Env, MessageInfo,
    Response, StdResult, Uint128, Addr,
};
use cw_storage_plus::Map;

pub const BALANCES: Map<&Addr, Uint128> = Map::new("balances");

#[entry_point]
pub fn execute(
    deps: DepsMut,
    _env: Env,
    info: MessageInfo,
    msg: ExecuteMsg,
) -> StdResult<Response> {
    match msg {
        ExecuteMsg::Transfer { recipient, amount } => {
            let sender_balance = BALANCES
                .load(deps.storage, &info.sender)?;
            let new_balance = sender_balance.checked_sub(amount)?;

            BALANCES.save(deps.storage, &info.sender, &new_balance)?;

            let recipient_addr = deps.api.addr_validate(&recipient)?;
            let recipient_balance = BALANCES
                .may_load(deps.storage, &recipient_addr)?
                .unwrap_or_default();
            BALANCES.save(
                deps.storage,
                &recipient_addr,
                &(recipient_balance + amount)
            )?;

            Ok(Response::new()
                .add_attribute("action", "transfer"))
        }
    }
}
Key Concepts:
  • Entry Points - instantiate, execute, query, migrate
  • Storage - Key-value storage with typed wrappers
  • Messages - Actions trigger state changes
  • Responses - Return events and sub-messages
04

Authorization Model

Daml: Multi-Party Authorization

Compile-Time Checked
-- Authorization is built into the language
template Proposal
  with
    proposer : Party
    acceptor : Party
    terms : Text
  where
    signatory proposer
    observer acceptor

    -- Only acceptor can accept
    choice Accept : ContractId Agreement
      controller acceptor
      do
        create Agreement with
          party1 = proposer
          party2 = acceptor
          terms

template Agreement
  with
    party1 : Party
    party2 : Party
    terms : Text
  where
    -- Both must have signed for this to exist
    signatory party1, party2
Authorization guarantees are compile-time checked.

CosmWasm: Manual Authorization

Runtime Checks
// Authorization must be manually implemented
pub fn execute_accept(
    deps: DepsMut,
    info: MessageInfo,
    proposal_id: u64,
) -> Result<Response, ContractError> {
    let proposal = PROPOSALS.load(deps.storage, proposal_id)?;

    // Manual check - runtime only
    if info.sender != proposal.acceptor {
        return Err(ContractError::Unauthorized {});
    }

    // Create agreement...
    Ok(Response::new())
}
Authorization must be manually implemented and only fails at runtime.
05

Privacy Model

Canton: Sub-Transaction Privacy

Transaction: Alice transfers asset to Bob, Bob pays Carol
Alice sees:
Alice → Bob
Bob sees:
Alice → Bob Bob → Carol
Carol sees:
Bob → Carol
Validators see:
Cryptographic Proof

Canton Privacy Features:

  • Parties only see contracts they're stakeholders in
  • Even within a transaction, data is compartmentalized
  • Validators verify without seeing contract contents

CosmWasm: Public by Default

Transaction: Alice transfers to Bob
Everyone sees:
Full transaction data, all state changes

CosmWasm Privacy Workarounds:

  • Secret Network (encrypted state)
  • Off-chain computation with on-chain verification
  • Zero-knowledge proofs (external)
06

Execution Model Deep Dive

Daml: UTXO + Active Contracts

-- Contracts have a lifecycle
-- 1. Created (active)
-- 2. Archived (consumed/exercised)

template IOU
  with
    issuer : Party
    owner : Party
    amount : Decimal
  where
    signatory issuer
    observer owner

    -- Exercising archives this contract
    choice Settle : ()
      controller issuer
      do
        -- Settlement logic
        return ()

    -- Non-consuming choice (contract stays active)
    nonconsuming choice GetBalance : Decimal
      controller owner
      do
        return amount

Benefits:

  • No re-entrancy attacks (contracts are immutable)
  • Double-spend prevention built-in
  • Clear audit trail

CosmWasm: Account Model

// Contracts persist and mutate state
pub fn execute_deposit(
    deps: DepsMut,
    info: MessageInfo,
) -> StdResult<Response> {
    let mut state = STATE.load(deps.storage)?;

    // State is mutated in place
    state.total_deposited += info.funds[0].amount;
    state.deposits.push(Deposit {
        depositor: info.sender.clone(),
        amount: info.funds[0].amount,
    });

    STATE.save(deps.storage, &state)?;
    Ok(Response::new())
}

Benefits:

  • Familiar programming model
  • Efficient for stateful applications
  • Easier composability
07

Comprehensive Comparison

Dimension Canton/DAML CosmWasm
Primary Strength Privacy & Multi-party workflows Composability & IBC
Contract Model Agreements (UTXO) Programs (Account)
Language Daml (DSL) Rust (GPL)
Authorization Compile-time checked Runtime checks
Privacy Native sub-tx privacy Public (by default)
Interop Multi-domain atomic IBC protocol
Learning Curve Moderate (new DSL) Steep (Rust + Cosmos)
Ecosystem Enterprise focus Public blockchain
Best For B2B workflows Public DeFi

Choose Canton/DAML When:

Multi-party workflows Built-in authorization model
Regulated industries Privacy compliance, audit trails
Financial infrastructure Atomic settlement, no double-spend
Cross-organization processes Sub-transaction privacy
Examples: Securities settlement, supply chain finance, insurance claims, syndicated loans

Choose CosmWasm When:

Public DeFi Composability with Cosmos ecosystem
Cross-chain applications Native IBC support
General-purpose dApps Flexibility of Rust
Token economies Easy token standards (CW20, CW721)
Examples: DEXs, NFT marketplaces, DAOs, cross-chain bridges
08

Advanced Patterns

Interface Pattern (Daml 2.0+)

-- Define reusable interface
interface Transferable where
  viewtype TransferableView

  getOwner : Party

  choice TransferTo : ContractId Transferable
    with newOwner : Party
    controller getOwner this
    do
      create this with owner = newOwner

-- Implement interface
template Stock
  with
    issuer : Party
    owner : Party
    ticker : Text
    shares : Int
  where
    signatory issuer, owner

    interface instance Transferable for Stock where
      view = TransferableView with owner
      getOwner = owner

Exception Handling

exception InsufficientFunds
  with
    requested : Decimal
    available : Decimal
  where
    message "Insufficient funds: requested " <> show requested
            <> ", available " <> show available

template Account
  with
    bank : Party
    owner : Party
    balance : Decimal
  where
    signatory bank, owner

    choice Withdraw : ContractId Account
      with amount : Decimal
      controller owner
      do
        when (amount > balance) do
          throw InsufficientFunds with
            requested = amount
            available = balance
        create this with balance = balance - amount

Testing with Daml Script

module Test where

import Daml.Script
import Main

testTransfer : Script ()
testTransfer = script do
  -- Setup parties
  alice <- allocateParty "Alice"
  bob <- allocateParty "Bob"

  -- Create asset
  assetId <- submit alice do
    createCmd Asset with
      issuer = alice
      owner = alice
      description = "Gold"
      quantity = 100.0

  -- Transfer
  newAssetId <- submit alice do
    exerciseCmd assetId Transfer with
      newOwner = bob

  -- Verify
  Some asset <- queryContractId bob newAssetId
  assert (asset.owner == bob)

Sub-Messages for Composition

pub fn execute_swap(
    deps: DepsMut,
    env: Env,
    info: MessageInfo,
    offer_asset: Asset,
    ask_asset: AssetInfo,
) -> Result<Response, ContractError> {
    // Create sub-message to DEX
    let swap_msg = WasmMsg::Execute {
        contract_addr: DEX_CONTRACT.load(deps.storage)?.to_string(),
        msg: to_json_binary(&DexExecuteMsg::Swap {
            offer_asset: offer_asset.clone(),
            ask_asset: ask_asset.clone(),
        })?,
        funds: vec![],
    };

    // Reply on success to handle result
    let submsg = SubMsg::reply_on_success(swap_msg, SWAP_REPLY_ID);

    Ok(Response::new()
        .add_submessage(submsg)
        .add_attribute("action", "swap"))
}

#[entry_point]
pub fn reply(
    deps: DepsMut,
    _env: Env,
    msg: Reply
) -> Result<Response, ContractError> {
    match msg.id {
        SWAP_REPLY_ID => {
            let response = parse_reply_result(msg)?;
            // Process received tokens...
            Ok(Response::new())
        }
        _ => Err(ContractError::UnknownReplyId { id: msg.id }),
    }
}

Migration Pattern

#[entry_point]
pub fn migrate(
    deps: DepsMut,
    _env: Env,
    msg: MigrateMsg
) -> Result<Response, ContractError> {
    let ver = cw2::get_contract_version(deps.storage)?;

    if ver.version.parse::<u64>()? < 2 {
        // Migrate from v1 to v2
        let old_state: StateV1 = STATE_V1.load(deps.storage)?;
        let new_state = StateV2 {
            owner: old_state.owner,
            total: old_state.total,
            // New field with default
            fee_rate: Decimal::percent(1),
        };
        STATE.save(deps.storage, &new_state)?;
    }

    cw2::set_contract_version(
        deps.storage,
        CONTRACT_NAME,
        CONTRACT_VERSION
    )?;
    Ok(Response::new())
}

IBC Packet Handling

#[entry_point]
pub fn ibc_packet_receive(
    deps: DepsMut,
    _env: Env,
    msg: IbcPacketReceiveMsg,
) -> Result<IbcReceiveResponse, ContractError> {
    let packet_data: PacketData = from_json(&msg.packet.data)?;

    // Process cross-chain message
    match packet_data {
        PacketData::Transfer { recipient, amount } => {
            // Mint tokens on this chain
            mint_tokens(deps, recipient, amount)?;
        }
    }

    Ok(IbcReceiveResponse::new()
        .add_attribute("action", "receive"))
}
09

Development Workflow

Daml Development

1
daml new my-project Create project
2
vim daml/Main.daml Write contracts
3
daml build Build DAR package
4
daml test Run Daml Script tests
5
daml start Start local sandbox
6
canton console Deploy to Canton

CosmWasm Development

1
cargo generate --git cw-template Create project
2
vim src/contract.rs Write contracts
3
cargo wasm && optimizer Build WASM
4
cargo test Run unit tests
5
wasmd tx wasm store Deploy contract code
6
wasmd tx wasm instantiate Create instance
10

Resources