DAML on Avalanche

Documentation

How the DAML VM works, what it supports, what's different from Canton, and the complete JSON-RPC API reference.

ava-labs/avalanche-canton(private)

How the VM Works

This is a custom AvalancheGo Virtual Machine that implements the block.ChainVM interface (Snowman linear-chain consensus). It runs as an rpcchainvmplugin — a separate process that communicates with AvalancheGo over gRPC. The VM is written entirely in Go.

The VM has two execution paths for DAML contracts:

JSON Templates

Templates defined as JSON objects with typed fields, signatory/observer declarations, and declarative choice effects. Write DAML-like syntax in the browser editor, compile client-side, deploy via registerTemplate. Choice bodies use field mappings (this.field, arg.param) to create new contracts.

DAML-LF Bytecode

Upload .dar files compiled by the official damlc compiler (DAML SDK 3.4+). The VM parses the protobuf archive, extracts DAML-LF v1 and v2 packages, and registers templates with their full choice expressions. A Go-based DAML-LF interpreter evaluates choice bodies at runtime.

Blocks are produced through Avalanche's Snowman consensus. The VM implements WaitForEvent to notify the engine when transactions arrive, BuildBlock to assemble transactions into blocks, and Verify/Accept/Rejectfor the full block lifecycle. State is persisted to LevelDB via AvalancheGo's database interface.

DAML Compatibility

The VM implements the core DAML ledger model. Here's what's supported and what's not:

What works

  • Templateswith typed fields, signatories, observers
  • Choicesconsuming and non-consuming, with controllers and parameters
  • Authorization modelsignatory/controller checks, authority expansion through choice bodies
  • DAML-LF parsingv1 and v2 protobuf archives from damlc build
  • Expression evaluatorvariables, records, lists, lambdas, let bindings, case/match, 20+ builtins
  • Update monadCreate, Exercise, Fetch, Pure, Block/Bind, GetTime
  • Party-scoped queriesneed-to-know privacy at the API layer via asParty
  • Ed25519 signaturesoptional transaction signing with registered keys
  • Template versioningupgrade templates with version suffixes
  • Contract pruningGDPR-style deletion of archived contracts

What's missing

  • Sub-transaction privacyCanton decomposes transactions into per-party views. All our validators see all data.
  • Contract keysDAML key-based lookups and exercise-by-key
  • InterfacesDAML interface definitions (LF 1.15+)
  • Try/catchDAML exception handling
  • Full stdlibsome stdlib functions referenced by complex contracts may not evaluate
  • Scenario/Script testingonly ledger operations, not test harness

Why the Gaps Don't Matter

Sub-transaction privacy? Avalanche L1s are sovereign chains with their own validator sets. On a permissioned L1, only approved validators sync the chain — the data never leaves the validator set. Put different data classes on different L1s (public, institutional, private) and you get infrastructure-level isolation without Canton's complex per-view encryption. For data-level encryption within a single L1, FHE (EncryptedERC20) is available on EVM L1s.

Contract keys and interfaces? These are convenience features in the DAML language, not fundamental ledger primitives. The core contract lifecycle (create, exercise, archive) and the authorization model (signatories, controllers, authority expansion) are fully implemented. Keys and interfaces can be added incrementally without architectural changes.

What Avalanche adds that Canton doesn't have: Sub-second finality via Snowman consensus. Full DeFi composability on the C-Chain. Standard Solidity tooling (Foundry, Hardhat, MetaMask) for EVM L1s in the same network. Cross-L1 atomic settlement via ICM. 300K+ Solidity developers. No vendor lock-in — your validators, your rules.

Architecture

AvalancheGo Node (v1.14.2)
├── P-Chain (platform, validators)
├── C-Chain (EVM, DeFi)
└── DAML L1 (custom VM)
    ├── rpcchainvm plugin (Go, 22MB binary)
    │   ├── vm.go          → block.ChainVM interface
    │   ├── block.go       → Snowman block lifecycle
    │   ├── engine.go      → DAML execution engine
    │   ├── api.go         → JSON-RPC handler
    │   └── daml/lf/       → DAML-LF protobuf parser + interpreter
    ├── Consensus: Snowman (single-slot finality)
    ├── State: LevelDB via AvalancheGo database interface
    └── Networking: AppGossip for mempool propagation

API Reference

All RPC methods follow the JSON-RPC 2.0 specification.

Base Endpoint

https://rpc.damlonavalanche.com

All JSON-RPC requests are POST to this URL. REST endpoints use the same origin.

Methods


JSON-RPC Methods

RPC

daml.getParties

Returns the list of all parties currently registered on the ledger. Parties are the fundamental identity primitive in DAML — every contract is visible to its signatories and observers.

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.getParties",
    "params": {},
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": ["Alice", "Bob", "Acme"],
  "id": 1
}
RPC

daml.getTemplates

Returns all registered DAML templates. Each template defines a contract type with fields, signatories, observers, and choices. Templates are the schema layer of the DAML ledger.

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.getTemplates",
    "params": {},
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": [
    {
      "module": "Bond",
      "name": "Bond",
      "fields": {
        "issuer": "Party",
        "owner": "Party",
        "amount": "Int64",
        "currency": "Text"
      },
      "signatories": ["issuer"],
      "observers": ["owner"],
      "choices": {
        "Transfer": {
          "consuming": true,
          "controllers": ["owner"],
          "params": { "newOwner": "Party" }
        }
      },
      "source": "template Bond with ..."
    }
  ],
  "id": 1
}
RPC

daml.getActiveContracts

Returns all active (non-archived) contracts on the ledger. Supports optional filtering by template ID and party visibility. The asParty filter demonstrates Canton's privacy model — each party only sees contracts they are a signatory or observer of.

Parameters

NameTypeRequiredDescription
templateIdstringoptionalFilter by template, e.g. "Bond:Bond"
asPartystringoptionalOnly return contracts visible to this party (privacy filter)

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.getActiveContracts",
    "params": {
      "templateId": "Bond:Bond",
      "asParty": "Alice"
    },
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": [
    {
      "id": "00a1b2c3d4...",
      "templateId": "Bond:Bond",
      "payload": {
        "issuer": "Alice",
        "owner": "Bob",
        "amount": 1000000,
        "currency": "USD"
      },
      "signatories": ["Alice"],
      "observers": ["Bob"],
      "active": true,
      "createdAt": 1711929600
    }
  ],
  "id": 1
}
RPC

daml.getContract

Fetches a single contract by its ID. Optionally scoped to a party for privacy-aware lookups.

Parameters

NameTypeRequiredDescription
contractIdstringrequiredThe contract ID to look up
asPartystringoptionalOnly return if visible to this party

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.getContract",
    "params": {
      "contractId": "00a1b2c3d4..."
    },
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "00a1b2c3d4...",
    "templateId": "Bond:Bond",
    "payload": {
      "issuer": "Alice",
      "owner": "Bob",
      "amount": 1000000,
      "currency": "USD"
    },
    "signatories": ["Alice"],
    "observers": ["Bob"],
    "active": true,
    "createdAt": 1711929600
  },
  "id": 1
}
RPC

daml.getTemplate

Returns a single template definition by its qualified ID (module:name).

Parameters

NameTypeRequiredDescription
templateIdstringrequiredQualified template ID, e.g. "Bond:Bond"

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.getTemplate",
    "params": {
      "templateId": "Bond:Bond"
    },
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": {
    "module": "Bond",
    "name": "Bond",
    "fields": {
      "issuer": "Party",
      "owner": "Party",
      "amount": "Int64",
      "currency": "Text"
    },
    "signatories": ["issuer"],
    "observers": ["owner"],
    "choices": {
      "Transfer": {
        "consuming": true,
        "controllers": ["owner"],
        "params": { "newOwner": "Party" }
      },
      "Settle": {
        "consuming": true,
        "controllers": ["issuer"],
        "params": {}
      }
    },
    "source": "template Bond with ..."
  },
  "id": 1
}
RPC

daml.submit

Submits a transaction to the ledger. A transaction consists of one or more commands executed atomically by a submitter party. Supported command types: create, exercise, allocateParty, registerTemplate. An optional Ed25519 signature can be attached for authenticated submissions.

Parameters

NameTypeRequiredDescription
transaction.submitterstringrequiredThe party submitting the transaction
transaction.commandsCommand[]requiredArray of commands: {type, templateId?, arguments?, contractId?, choice?, party?, template?}
transaction.signaturestringoptionalHex-encoded Ed25519 signature over SHA-256(submitter + JSON(commands))

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.submit",
    "params": {
      "transaction": {
        "submitter": "Alice",
        "commands": [
          {
            "type": "create",
            "templateId": "Bond:Bond",
            "arguments": {
              "issuer": "Alice",
              "owner": "Bob",
              "amount": 1000000,
              "currency": "USD"
            }
          }
        ]
      }
    },
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": {
    "accepted": true,
    "message": "Transaction committed. 1 contract(s) created."
  },
  "id": 1
}
RPC

daml.uploadDar

Uploads a compiled DAML Archive (.dar) to register its templates on the ledger. The file must be base64-encoded.

Parameters

NameTypeRequiredDescription
darFilestringrequiredBase64-encoded contents of the .dar file

Request

curl -X POST https://rpc.damlonavalanche.com \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "daml.uploadDar",
    "params": {
      "darFile": "UEsDBBQAAAAI..."
    },
    "id": 1
  }'

Response

{
  "jsonrpc": "2.0",
  "result": {
    "accepted": true,
    "message": "DAR uploaded. 3 template(s) registered."
  },
  "id": 1
}

REST Endpoints

POST

/api/compile

Compiles DAML source code into Canton template definitions. Accepts raw DAML text and returns structured template objects ready for deployment.

Request Body

{
  "source": "module Bond where\n\ntemplate Bond\n  with\n    issuer : Party\n  where\n    signatory issuer"
}

Response

{
  "templates": [
    {
      "module": "Bond",
      "name": "Bond",
      "fields": { "issuer": "Party" },
      "signatories": ["issuer"],
      "observers": [],
      "choices": {},
      "source": "template Bond with ..."
    }
  ]
}
POST

/api/benchmark

Runs a throughput benchmark by submitting a batch of transactions to the ledger and measuring TPS and latency.

Request Body

{
  "txCount": 1000
}

Response

{
  "tps": 196420,
  "totalMs": 5.09,
  "txCount": 1000,
  "successCount": 1000,
  "failCount": 0
}

Type Definitions

Template

interface Template {
  module: string;        // DAML module name
  name: string;          // Template name
  version?: string;      // Optional version
  fields: Record<string, string> | null;
                         // Field name -> DAML type (Party, Text, Int64, etc.)
  signatories: string[] | string;
  observers: string[] | string;
  choices: Record<string, Choice> | string[];
  source: string;        // Original DAML source
}

Choice

interface Choice {
  consuming: boolean;    // Whether exercising archives the contract
  controllers: string[]; // Parties allowed to exercise
  params: Record<string, string>;
                         // Parameter name -> DAML type
  effects?: unknown;     // Optional effect definitions
}

Contract

interface Contract {
  id: string;            // Unique contract ID
  templateId: string;    // Qualified template ID (module:name)
  payload: Record<string, unknown>;
                         // Contract field values
  signatories: string[]; // Signatory parties
  observers: string[];   // Observer parties
  active: boolean;       // Whether the contract is active
  createdAt: number;     // Unix timestamp
  archivedAt?: number;   // Unix timestamp (if archived)
}

Command

interface Command {
  type: string;          // "create" | "exercise" | "allocateParty" | "registerTemplate"
  templateId?: string;   // For create commands
  arguments?: Record<string, unknown>;
                         // For create/exercise commands
  contractId?: string;   // For exercise commands
  choice?: string;       // For exercise commands
  template?: Template;   // For registerTemplate commands
  party?: string;        // For allocateParty commands
}

Canton-on-Avalanche // DAML smart contracts running natively on Avalanche L1

build.avax.network