The Finance Library — Part 1

György Balázsi
DAML Masterclass
Published in
11 min readAug 27, 2020
Photo by Ramón Salinero on Unsplash

This is the second part of my DAML Masterclass series. See the other parts in the list below:

In this series I reverse engineer a number of advanced reference applications, written by DAML experts, available on the DAML Marketplace, in order to get inspiration for my own projects.

If you start to dissect some financial refapps, you will notice that some of them utilize a library called the Finance Library, available in the DAML Marketplace. So first I will take a good look at this one.

The Finance Library features a number of advanced ideas I wouldn’t have thought of before reading about them, so I picked the ones I found most useful, and called them Big Ideas.

I have identified 5 Big Ideas in the Finance Library, and have divided them into two parts for readability. In this post I will guide you through the first three ones.

Big Idea #1: semantically rich identifiers

One thing you will instantly notice in the Finance Library is that contracts have a complex id with several fields, including the contract’s signatories.

The Id data type is like an ID card, which allows one to inspect directly basic properties of a subject, rather than just providing an ID string serving as a database index to look the properties up.

This also means that the properties contained in the id of some financial object (like an account or an asset) are immutable (within the scope of one specific version), and the id can also be used as a contract key for the contract instances. The Id data type contains the following fields: signatories, label, version.

Source: https://github.com/digital-asset/lib-finance/blob/master/model/src/DA/Finance/Types.daml

The Id data record is used as a value given to the id field of a specific asset or Account record. You will see eg. later on, that an AssetDeposit contract, which has an Account record as the value of its account field, derives its signatories from its account.id.signatoriesfield.

You can also notice that the Finance Library uses sets rather than lists as a collection type (imported from the DA.Next.Set module of the standard library), when the order of the elements is not specified and no duplication is allowed, like in the case of the signatories of a contract.

Big Idea #2: modularity

Sometimes it is useful to model one type of legal relationship by a mandatory contract plus one or more optional supplementary contracts. This modular approach gives you more flexibility for specifying the choices available to the participants of said relationship.

An example for the modular approach is the following:

The ownership of some asset, deposited to an account, is represented by an AssetDeposit contract. AssetDeposit contracts have the same signatories as their underlying accounts, specified in the contract’s account.id.signatoriesfield.

Source: https://github.com/digital-asset/lib-finance/blob/master/model/src/DA/Finance/Asset.daml

By default, the account owner only has the right to split and merge, but not to transfer their asset deposits. This corresponds to real-life situations when an asset’s transferability needs to be restricted, for example if an asset vests over time. Nor can by default any external party credit the account.

(By observing the respective choices, you will notice that you are not limited to split an asset into two pieces, nor are you limited to merge two assets into one, but you can specify an unlimited number of quantities for splitting one asset, or an unlimited number of different assets to be merged into one asset. The choices use the stQty (= set quantity) utility function to accomplish these operations, defined in the let block of the template. The choices also use two advanced library functions, namely the mapA function, which serves to atomically create a series of ledger updates, and the foldlA function, which serves to combine a series of fetch and archive update steps into one create or update step. I will go into the details of the mechanics of the mapA function towards the end of this post, where I explain how it is used to process a multistep asset settlement chain.)

The options to transfer the deposited asset, and/or allowing another party to credit the account, are added to the account by an AssetSettlementRule contract. The asset deposit and the asset settlement rule is connected through their underlying account id.

The AssetSettlementRule contract contains the following choices:

  • The AssetSettlement_Debit choice which archives and returns an Asset, which is deposited to the respective Account.
  • The AssetSettlement_Credit choice, which creates an asset deposit. This operation requires that the target account has an active AssetSettlementRule contract, with the controller of this choice in its ctrls (controllers) field.
  • The AssetSettlement_Transfer choice, which combines the two above choices atomically.

The way how the AssetDeposit and the AssetSettlementRule contract complements each other is shown by this diagram of the README of the Finance Library (the drawing displays a former name for the AssetSettlementRule contract, but this doesn’t affect the message):

Big Idea #3: multistep settlement chain

In real-life situations we sometimes need a multistep settlement chain rather than direct transfer.

The Finance Library contains a tutorial in the form of a series of scenarios of gradually increasing complexity, starting with a simple intra-bank transfer, all the way up to an interbank transfer involving Central Bank Digital Currency (DBDC).

Interbank transfers already require multiple settlement steps up and down in an account hierarchy in order to achieve the state transition from “Alice having a 1000 USD deposit held with Acme Bank” to “Bob having a 1000 USD deposit held with Genco Bank”.

(On the topic of how DAML is a suitable tool to implement CDBC see two recent posts on the DAML Driven blog: What is a Central Bank Digital Currency and why should people prefer CBDC over bank accounts and Why DAML is great to represent digital currency.)

DAML is a functional language, and for this reason it doesn’t have for loops or while loops to process multistep settlement chains.

(For those of you who are not super familiar with the difference between functional and procedural programming languages: Functions in functional languages like DAML or Haskell cannot have inner state. This excludes the use of loops, while the variable containing the index of the actual step of the iteration would be an inner state variable. Furthermore, functions in functional languages are declarative, which means they declare the result of the computations, and not the steps leading to that result.)

As a compensation for the lack of loops, DAML (similarly to other functional languages) has a wide range of functions to process lists. The main operations are slicing, filtering, mapping functions onto lists, combining lists in various ways, and declaring operations in an atomic manner through monads. The bonus is a higher degree of compactness and readability compared to procedural languages, as we’ll see shortly.

I will show you some examples for such functions in the following explanation of the DAML implementation of a settlement chain, defined in the DA.Finance.Trade.SettlementInstruction module.

The basic mechanism of the settlement chain is determined by the fact that

in order to make sure that the settlement chain can be processed without interruption as part of an atomic Delivery vs Payment process, the funds (the respective AssetDeposit contracts) need to be allocated at every step of the chain in advance.

The settlement chain implementation can be best understood by the following questions:

  1. How the individual steps and the sequence of the settlement chain are represented
  2. How the validity of the sequence is checked
  3. How the intermediary parties allocate funds to the steps where they want to act as senders
  4. How the chain as a whole gets processed in an atomic way

Representation of the individual steps and the sequence

The representation of the individual steps of the settlement chain is the SettlementDetails data type, which has the following fields:

  • a mandatory sender account,
  • a mandatory receiver account and
  • an optional asset deposit contract id.

The asset deposit contract id is optional, because it gets only filled in when a specific asset deposit contract gets allocated by an intermediary party in the asset allocation phase through the AllocateNext choice (see later).

Source: https://github.com/digital-asset/lib-finance/blob/59f6f03d2a137d9268fd7171c2297bc4aea76762/model/src/DA/Finance/Trade/SettlementInstruction.daml

The sequence of the individual steps is represented by the SettlementInstruction contract. The asset type and quantity which needs to be moved along the chain and finally settled (therefore it is uniform along the whole sequence) is specified in the asset field. The senders, receivers, and — optionally — the allocated asset deposit contracts of each step are contained in the steps field, as a list of SettlementDetails records.

Validity check for the sequence of steps

The precondition of a continuous “relay run” is that the sender of the transfer in one step is equal to the receiver in the previous step. This is checked by the following function in the ensure field of the SettlementInstruction template:

Source: https://github.com/digital-asset/lib-finance/blob/59f6f03d2a137d9268fd7171c2297bc4aea76762/model/src/DA/Finance/Trade/SettlementInstruction.daml

The function uses the combination of the tail function from the DA.List module (which cuts off the first element of the steps list) and the zip standard library function (which creates pairs from the elements of two lists) to create a list a tuples in the following way:

[step1, step2, step3, …] → [(step1, step2), (step2,step3), …]

and then checks if the senders and receivers match with each other as described above.

If you really want to understand the mechanics working here, I would suggest to try a simplified version of this function in the DAML REPL interactive environment, representing parties with numbers, and steps with tuples of parties:

daml> import DA.Listdaml> let parties = [1..5]daml> tail parties[2,3,4,5]daml> let steps = zip parties $ tail partiesdaml> steps[(1,2),(2,3),(3,4),(4,5)]daml> let chain = zip steps $ tail stepsdaml> chain[((1,2),(2,3)),((2,3),(3,4)),((3,4),(4,5))]daml>

In the list of steps we represent that party1 transfers to party2, party2 transfers to party3, etc.

In the list called chain we put together adjacent steps to be able to check the gapless “relay run” condition. This is a list of tuples, where the first and second element of a tuple are themselves tuples of parties, representing a transfer step from one party to the other party.

Now we can check, with a simplified version of the above ensure function if the parties to the steps fulfill the gapless “relay run” condition needed for a settlement chain (which is, of course, true because that’s how we defined the steps in the first place):

daml> all (\(s1,s2) -> snd s1 == fst s2) $ zip steps $ tail stepsTruedaml>

Of course, if we mess up the list of steps eg. by swapping party2 to party6as the sender of the second step, the result of the check will be negative:

daml> let steps' = [(1,2),(6,3),(3,4),(4,5)]daml> zip steps' $ tail steps'[((1,2),(6,3)),((6,3),(3,4)),((3,4),(4,5))]daml> all (\(s1,s2) -> snd s1 == fst s2) $ zip steps' $ tail steps'Falsedaml>

Asset allocation

The intermediary parties can allocate their funds in advance through the SettlementInstruction_Allocatenex choice. This choice can be exercised at every step by “the next sender” of the asset allocation process.

The “next sender” is either exists or not:

  • If the sequence of steps is not fully alocated yet, the “next sender” is that party which is the sender of the first step, where the asset deposit contract id field is None.
  • If the sequence of steps is already fully allocated, the “next sender” doesn’t exist.

The uncertainty that the “next sender” may or may not exist, is handled by the find monadic function, in the nextSender function.

Source: https://github.com/digital-asset/lib-finance/blob/59f6f03d2a137d9268fd7171c2297bc4aea76762/model/src/DA/Finance/Trade/SettlementInstruction.daml

Again, if you want to really understand what’s going on, I suggest to check out the following simplified example.

The below example is based on how the find function works. It has an optional return value, expressing the possibility that it doesn’t find anything which corresponds to the search criteria. In the example below, we are looking for an odd value in the steps and the steps' list. The result of the search in the steps list is Some 5, and in the steps' list is None, because steps' doesn’t contain any odd values.

If we combine the findfunction returning an optional value with the (*2) function which multiplies every numeric value by 2, in a way, that it only multiplies existing values (using the <$> operator), we get a monadic function, which returns the double of the first odd value if it exists, and None, if it doesn’t.

daml> import DA.Listdaml> let steps = [2,4,5,6,8,10]daml> find (\x -> x%2 == 1) stepsSome 5daml> let steps' = [2,4,6,8,10]daml> find (\x -> x%2 == 1) steps'Nonedaml> (*2) <$> find (\x -> x%2 == 1) stepsSome 10daml> (*2) <$> find (\x -> x%2 == 1) steps'Nonedaml>

Now we can understand the following part of the nextSender function by analogy:

(.senderAccount.owner) <$> find (\step -> isNone step.depositCid) steps

Now let’s examine the SettlementInstruction_AllocateNext choice:

Source: https://github.com/digital-asset/lib-finance/blob/59f6f03d2a137d9268fd7171c2297bc4aea76762/model/src/DA/Finance/Trade/SettlementInstruction.daml

The magic happens in the following line:

let (done, curr :: next) = break (\x -> isNone x.depositCid) steps

In order to understand this line, we need to take a peek at the break functin.

The break function, applied to a filtering function and a list, returns a tuple where

  • the first element of the tuple contains that slice of the list, which precedes the first matching result, and
  • the second element of the tuple is the rest of the list, starting with the first occurrence of the matching result.

As it is already our habit, we will examine a simplified example.

Let’s assume, we want to modify the first element of a list, which mathes some search. Let’s see the below example, where we have a list, in which we want to multiply by 100 the first element, which is positive. In order to do this

  • first, we isolate via pattern matching and the break function the non-positive part, the first positive element, and the rest of the list, and
  • second, we recreate the list in a way, that we leave the non-positive part and the rest unchanged, only changing the first positive element.
daml> import DA.Listdaml> let steps = [0,0,0,0,1,2,3,4]daml> let (zeros,firstPositive::rest) = break (>0) stepsdaml> zeros[0,0,0,0]daml> firstPositive1daml> rest[2,3,4]daml> zeros ++ 100*firstPositive :: rest[0,0,0,0,100,2,3,4]daml>

So now, by analogy, we can understand the following line:

create this with steps = done ++ currNew :: next

We can see several times the triple equation === in the code, which is the shortcut to an assert function checking the equality of two expressions, defined in the DA.Assert module.

Processing of the whole sequence atomically

This is where the magic of atomic transaction processing happens. “Atomic” means that either all transfers of the allocated funds are successful, or neither of them, so the system cannot get stuck in an incorrect state. A transfer can be unsuccessful if an intermediary party, after allocating an asset deposit contract, spends the asset in another transaction.

The atomic transaction handling in the SettlementInstruction_Process choice gets pulled off by the mapA function.

In order to better understand what the mapA function does, let’s say we have an optionalHalf function which returns an optional value, doing the following:

  • If an integer is even, it halves it, and returns the result as a Some n value, but
  • if an integer is odd, it returns None.

We have seen another function returning an optional value earlier.

The mapA function maps the optionalHalf function to a list so, that

  • If all list items are even, than the function halves all of them, and returns the list of halved values of the original list as a Some xs value,
  • If any of the original list items is odd, the function returns None.
daml> let optionalHalf n = if n % 2 == 0 then Some (n / 2) else Nonedaml> optionalHalf 10Some 5daml> optionalHalf 11Nonedaml> mapA optionalHalf [2,4,6,8,10]Some [1,2,3,4,5]daml> mapA optionalHalf [2,4,5,8,10]Nonedaml>

The SettlementInstruction_Process choice works analogously:

  • a successful transfer of a previously allocated asset deposit corresponds to the successful halving of an even number,
  • a ledger update consisting of all the transfers corresponds to the returning of the element-wise halved list.
Source: https://github.com/digital-asset/lib-finance/blob/59f6f03d2a137d9268fd7171c2297bc4aea76762/model/src/DA/Finance/Trade/SettlementInstruction.daml

Further instruction and templates

You can find further instructions and references to more templates and triggers to implement and automate an actual DvP workflow in the Delivery vs Payment (DvP) Trades section of the README.

--

--