Settlement¶
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:
- create a new
TOKEN
instrument - credit a
TOKEN
holding to Alice’s account - setup a delivery-vs-payment (DvP) transaction to give Alice’s
TOKEN
holding to Bob in exchange for aUSD
holding - 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:
|
Two (or more) parties need to first agree on a set of steps to be settled. |
|
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. |
|
For every instruction, the sender and the receiver specify their allocation and approval preferences, respectively. |
|
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:
-
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).
-
settlementFactoryCid <- toInterfaceContractId @Settlement.F <$> submit bank do createCmd 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
settlementFactoryCid
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
routedSteps
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] <- submitMulti [bob] [public] 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 bankUSD 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.
Summary¶
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.