Skip to main content
Moving from Solidity to Daml requires a significant mental shift. This page explains the paradigm differences and how to adapt your thinking.

Programming Model Comparison

AspectSolidityDaml
ParadigmImperative, object-orientedFunctional, declarative
StateMutable storageImmutable contracts
ExecutionSequential operationsTransaction trees
TypesStatic with dynamic callsStrongly typed, ADTs
Side effectsUnlimitedControlled via monads

State Model: Mutable vs. Immutable

Solidity: Mutable State

In Solidity, contracts have mutable storage that you modify directly:
contract Token {
    mapping(address => uint256) public balances;

    function transfer(address to, uint256 amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        // Mutate state in place
        balances[msg.sender] -= amount;
        balances[to] += amount;

        emit Transfer(msg.sender, to, amount);
    }
}
Mental model: The contract is a persistent object with state you modify.

Daml: Immutable Contracts

In Daml, contracts are immutable data. State changes create new contracts or archive existing contracts:
template Token
  with
    owner : Party
    issuer : Party
    amount : Decimal
  where
    signatory issuer
    observer owner

    choice Transfer : ContractId Token
      with
        newOwner : Party
        transferAmount : Decimal
      controller owner
      do
        -- This contract will be archived
        -- Create new contracts for the split
        create Token with owner = newOwner, issuer, amount = transferAmount
        create this with amount = amount - transferAmount
Mental model: Contracts are facts. Exercise archives the fact and creates new facts.

UTXO vs. Account Model

Ethereum: Account Model

  • State is a global mapping of accounts to balances
  • Transfers modify account entries
  • Easy to query total balance
  • Contention on popular accounts

Canton: Extended UTXO Model

  • State is a set of contracts (like unspent outputs)
  • Transfers archive existing contracts, create new ones
  • Balance is sum of owned contracts
  • Better parallelism, explicit data flow
-- Canton: Holdings are individual contracts
-- Alice's total balance = sum of all Token contracts where owner = Alice

-- Query: Find all my tokens
myTokens <- queryContractKey @Token myParty
totalBalance <- pure $ sum [amount | Token{amount} <- myTokens]

Language Comparison

Type System

FeatureSolidityDaml
Type safetyModerateStrong
Null handlingImplicit (0/empty)Explicit (Optional)
Custom typesStructs, enumsADTs, records
GenericsLimitedFull parametric polymorphism

Solidity Types

struct Asset {
    address owner;
    uint256 value;
    bool isLocked;
}

enum State { Pending, Active, Completed }

Daml Types

-- Record type (like struct)
data Asset = Asset with
  owner : Party
  value : Decimal
  isLocked : Bool

-- Sum type (algebraic data type)
data AssetState
  = Pending
  | Active with activatedAt : Time
  | Completed with result : Text

-- Optional (explicit null handling)
data MaybeApprover = Some Party | None

Solidity Control Flow

function process(uint256[] memory items) public {
    for (uint i = 0; i < items.length; i++) {
        if (items[i] > threshold) {
            revert("Over threshold");
        }
        results[i] = items[i] * 2;
    }
}

Daml Control Flow

process : [Decimal] -> Update [Decimal]
process items = do
  forA items \item -> do
    assertMsg "Over threshold" (item <= threshold)
    pure (item * 2.0)

Authorization Model

Solidity: Runtime Authorization

contract Ownable {
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    function sensitiveAction() public onlyOwner {
        // Only owner can call
    }
}
Issues:
  • Authorization checked at runtime
  • Easy to forget checks
  • Anyone can attempt the call
  • Authorization mixed with logic

Daml: Declarative Authorization

template OwnedAsset
  with
    owner : Party
    data : Text
  where
    signatory owner  -- owner must authorize creation

    choice SensitiveAction : ()
      controller owner  -- only owner can exercise
      do
        -- Protocol enforces: only owner can reach here
        pure ()
Benefits:
  • Authorization declared, not coded
  • Impossible to forget (compiler enforces)
  • Only authorized parties can attempt
  • Clear separation of concerns

Multi-Party Coordination

Solidity: Manual Multi-Sig

contract MultiSig {
    mapping(address => bool) public approved;
    uint256 public approvalCount;
    uint256 public requiredApprovals;

    function approve() public {
        require(!approved[msg.sender], "Already approved");
        approved[msg.sender] = true;
        approvalCount++;
    }

    function execute() public {
        require(approvalCount >= requiredApprovals, "Not enough approvals");
        // Execute action
    }
}

Daml: Native Multi-Party

template Agreement
  with
    partyA : Party
    partyB : Party
    terms : Text
  where
    signatory partyA, partyB  -- Both must sign to create

-- Proposal pattern for gathering signatures
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

Common Patterns Translated

Solidity PatternDaml Equivalent
OwnableSignatory declaration
PausableContract archival + recreation
ERC-20Token Standard (CIP-0056)
Proxy/UpgradeableSmart Contract Upgrade (SCU)
Pull paymentPropose/accept pattern
FactoryTemplate + create
RegistryContract keys (when available)

What to Unlearn

Solidity HabitDaml Reality
Mutate state in placeArchive + create new contracts
Runtime msg.sender checksCompile-time controller declarations
Public functions anyone can callOnly controllers can exercise
Global contract addressContract IDs change on every update
Loops for iterationUse forA, mapA, fold patterns
Try/catch everywhereAssertions for validation; exceptions are deprecated

Next Steps