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.
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.
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 tokensmyTokens <- queryContractKey @Token myPartytotalBalance <- pure $ sum [amount | Token{amount} <- myTokens]
-- 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
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; }}
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 ()
template Agreement with partyA : Party partyB : Party terms : Text where signatory partyA, partyB -- Both must sign to create-- Proposal pattern for gathering signaturestemplate 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