Skip to main content
This tutorial walks you through creating a v2 version of a Daml package. You’ll start with a simple template, add an optional field and a new choice, then verify that the upgrade compiles and that existing contracts work with the new code.

Prerequisites

  • A working dpm installation with the Daml SDK
  • Familiarity with Daml templates and choices (Module 3)
  • A text editor or Daml Studio

Step 1: Create the v1 Package

Start by scaffolding a new project. The dpm new command creates the directory structure and a daml.yaml file:
For this tutorial, set up the directory structure manually to keep things explicit:
Create daml/v1/daml.yaml:
Create daml/v1/daml/Main.daml:
Build and verify:
The SDK also provides a built-in upgrades example template you can generate with dpm new upgrade-demo --template upgrades-example. See the example source for details.

Step 2: Create the v2 Package

Copy the package — cp -r v1 v2 — and bump the version. The package name must stay the same — this is how Daml knows it’s an upgrade:
The upgrades field points to the v1 DAR. This is what tells dpm build to validate that v2 is a compatible upgrade of v1. Now make backward-compatible changes. Add an Optional field to the record and a new choice to the template:
The changes follow SCU compatibility rules:
  • expiryDate is an Optional field with implicit None default for v1 contracts
  • Renew is a new choice (doesn’t exist in v1, so no backward-compatibility issue)

Step 3: Verify Compatibility

Build the v2 package:
If the build succeeds, the compiler has verified that v2 is a valid upgrade of v1. The upgrades field in daml.yaml triggers this check — without it, dpm build compiles v2 in isolation and performs no cross-version validation. The compiler checks all the SCU rules: no removed fields, no type changes, new fields are Optional, etc. If you’ve introduced a breaking change, the compiler will report an error telling you what rule was violated.

Step 4: Test Approximated Cross-Version Behavior

To verify the upgrade path, add a test script to the v2 package. First, add the daml-script dependency to daml/v2/daml.yaml:
Then create a test script that simulates a v1 contract (with expiryDate = None) and exercises the new v2 Renew choice:
Run the test from the v2 package directory:
This test runs within a single package version, so it approximates rather than fully reproduces cross-version behavior. On a real ledger with both v1 and v2 DARs uploaded, the runtime handles the version resolution between actual v1 contracts and v2 code. See Testing Upgrades for strategies to test real cross-version scenarios.

Step 5: Deploy Both Versions

In a real deployment, you may have both DAR files uploaded to your validator. The order matters: upload v1 first (if not already uploaded), then v2. For new validators, they only need to upload v2 provided that it is SCU compatible with v1.
Once v2 is uploaded and vetted on all stakeholders’ validators, the new choice becomes available on existing v1 contracts.

What Happens Under the Hood

When a validator receives a v2 DAR:
  1. If automatic vetting is enabled, the validator node vets the new package alongside v1. Otherwise, vetting must be manually done.
  2. Both packages remain active — v1 contracts aren’t affected
  3. When v2 code fetches a v1 contract, the runtime fills Optional fields with None
  4. When v1 code fetches a v2 contract where Optional fields are None, the fetch succeeds (the fields are simply ignored)
  5. When v1 code fetches a v2 contract where an Optional field has a non-None value, the fetch fails to prevent data loss
This design ensures that mixed-version operation is safe: no data is silently lost, and incompatible interactions fail explicitly rather than corrupting state. See Package Selection to learn how different versions are resolved at runtime.

Next Steps