This tutorial introduces the settlement features of the library through a simple example. The purpose is to demonstrate how multiple holding transfers can be executed atomically.

We are going to:

  1. create a new TOKEN instrument
  2. credit a TOKEN holding to Alice’s account
  3. setup a delivery-vs-payment (DvP) transaction to give Alice’s TOKEN holding to Bob in exchange for a USD holding
  4. settle this transaction atomically

This example builds on the previous Transfer tutorial script in the sense that the same accounts and the existing holdings are used.

Overview of the Process

We first give a quick outline of the settlement process:

  1. Define steps to be settled
Two (or more) parties need to first agree on a set of steps to be settled.
  1. Generate settlement instructions

Instructions are generated for each step.

An instruction is a contract where the sender can specify its Allocation preference for the instruction (e.g., the matching holding they wish to send). The receiver can specify its Approval preference for the instruction (e.g., the account they wish to receive the holding to).

The creation of Instructions is done by first using a Route Provider and then applying a Settlement Factory.

  1. Allocate and approve instructions
For every instruction, the sender and the receiver specify their allocation and approval preferences, respectively.
  1. Settle the batch

A Batch contract is used to settle all instructions atomically according to the specified preferences (e.g. by transferring all allocated holdings to the corresponding receiving accounts).

This batch contract is created in step 2, together with the settlement instructions.

Run the Script

The code for this tutorial can be executed via the runSettlement function in the Settlement.daml module.

The first part executes the script from the previous Transfer tutorial to arrive at the initial state for this scenario. We then create an additional TOKEN instrument and credit Alice’s account with it.

The interesting part begins once Alice proposes the DvP trade to Bob. Before creating the DvP proposal, we need to instantiate two contracts:

  1. Route Provider

      routeProviderCid <- toInterfaceContractId @RouteProvider.I <$> submit bank do
        createCmd SingleCustodian with
          provider = bank; observers = S.fromList [alice, bob]; custodian = bank

    This is used to discover a settlement route, i.e., routed steps, for each settlement step. In this example, the route provider simply converts each step to a routed step using a single custodian (the bank).

  2. Settlement Factory

      settlementFactoryCid <- toInterfaceContractId @Settlement.F <$> submit bank do
        createCmd Settlement.Factory with
          provider = bank
          observers = S.fromList [alice, bob]

    This is used to generate the settlement batch and instructions from the routed steps.

Bob creates a Dvp.Proposal template to propose the exchange of the TOKEN against USD.

  dvpProposalCid <- submit bob do
    createCmd DvP.Proposal with
      id = "xccy trade"
      recQuantity = qty 10.0 tokenInstrument
      payQuantity = qty 1000.0 usdInstrument
      proposer = bob
      counterparty = alice
      routeProviderCid -- This is equivalent to writing routeProviderCid = routeProviderCid

Alice then accepts the proposal, agreeing to the terms of the trade.

  (batchCid, recSettleInstructionCid, paySettleInstructionCid) <- submit alice do
    exerciseCmd dvpProposalCid DvP.Accept

Once the proposal is accepted, three contracts are created:

  • an instruction to transfer 10 TOKEN from Alice to Bob
  • an instruction to transfer USD 1000 from Bob to Alice
  • a batch contract to settle the two instructions atomically

The workflow to create these contracts makes use of the route provider and the settlement factory.

        (containerCid, [recInstructionCid, payInstructionCid]) <-
          exercise settlementFactoryCid Settlement.Instruct with
            instructors = fromList [proposer, counterparty]
            settlers = singleton proposer
            id = Id id
            description = "Settlement for " <> id
            contextId = None
            settlementTime = None -- i.e., immediate settlement

As a next step, Alice allocates her TOKEN holding to the corresponding instruction. Bob then approves the instruction specifying the receiving account.

  (allocatedRecSettleInstructionCid, _) <- submit alice do
    exerciseCmd recSettleInstructionCid Instruction.Allocate with
      actors = S.singleton alice
      allocation = Pledge aliceHoldingCid

  approvedRecSettleInstructionCid <- submit bob do
    exerciseCmd allocatedRecSettleInstructionCid Instruction.Approve with
      actors = S.singleton bob
      approval = TakeDelivery bobAccount

The same happens in the second instruction (where Bob allocates his USD holding and Alice provides the receiving account).

Now that all instructions are fully allocated and approved, they can finally be settled.

  [bobHoldingCid, aliceHoldingCid] <- submit bob do
    exerciseCmd batchCid Batch.Settle with
      actors = singleton bob

Within the same transaction, Alice receives a USD holding from Bob in exchange for a TOKEN holding.

Frequently Asked Questions

Why do we need a route provider?

Consider a real-world example where Alice instructs a bank transfer to send USD 100 to Bob. The following happens:

  • USD 100 are debited from Alice’s account at her bank
  • USD 100 are transferred from Alice’s bank to Bob’s bank (via their accounts at the central bank)
  • USD 100 are credited to Bob’s account at his bank

A single settlement Step requires three RoutedSteps to settle.

The same dynamics can be reproduced in Daml with a Route Provider implementation, allowing for on-ledger intermediated settlement. For example, see the Intermediated Lifecycling tutorial.

Why do we need a settlement factory?

A settlement factory contract is used to generate settlement Instructions from RoutedSteps. It also generates a Batch contract, which is used to settle instructions atomically.

The reason why the factory is needed has already been introduced in the previous tutorial: it provides an interface abstraction, so that your workflow does not need to depend on concrete implementations of Batch or Instructions.

Can we use a different settler?

In our example, Alice triggers the final settlement of the transaction (by exercising the Settle choice on the Batch contract).

In principle, a different settler could be chosen. The choice of a settler is usually quite delicate, as this party acquires visibility on the entire transaction and hence needs to be trusted.

What if one party wants to cancel the settlement?

The parties who sign the Batch contract (the requestors) can exercise the Cancel choice of the Batch to cancel all associated Instructions atomically.


You know how to define complex transactions and settle them atomically. The main points to take away are:

  • A route provider is used to discover settlement routes, i.e., routed steps, for each settlement step.
  • A settlement factory is used to instruct settlement for an arbitrary list of routed steps.
  • Instructions are used to collect authorizations, assets to be moved, and means of settlement.
  • Batches group together instructions to be settled atomically.

In the next tutorial, we will introduce the lifecycling framework of the library, which is used to model the evolution of instruments. The concepts introduced in this tutorial will be used to settle payments arising from lifecycle events.