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
- credit a
TOKENholding to Alice’s account
- setup a delivery-vs-payment (DvP) transaction to give Alice’s
TOKENholding to Bob in exchange for a
- 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).
||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
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
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 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
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 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 TOKENfrom Alice to Bob
- an instruction to transfer
USD 1000from 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] <- 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
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 100are debited from Alice’s account at her bank
USD 100are transferred from Alice’s bank to Bob’s bank (via their accounts at the central bank)
USD 100are credited to Bob’s account at his bank
Why do we need a settlement factory?¶
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
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.
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.