In Part 1 we have seen how asset is created and transferred. In this part we focus on asset trading in Tutorial 7.
Asset trade can be seen as two asset transfers happening at the same time: one party hands out her asset in return of the asset from another party. One challenge is to ensure both transfers happen atomically. Both transfers are either completed or failed. It should never happen that one side receives the asset while the other side does not. When assets are implemented in DAML, this can be implemented with proper flow design.
Tutorial 7 gives us a mechanism to make this happen. Assuming two asset holders wish to trade their own asset. One party creates a Trade request, which is a contract in DAML ledger, and the other party exercises Trade_Settle choice to complete the Trade contract.
It sounds simple, but the details involve several existing contract templates. In this part we will point out one-by-one.
For easy explanation, and the test we are going to use below (test6), we specify some roles here.
- Alice owns an Asset issued by X-Bank, which is USD 1,000.
- Bob holds an Asset issued by Y-Registry, which is NASDAQ:AAPL 10.0.
- Alice and Bob are trading their assets. The result is that the asset issued by X-Bank (USD 1,000) goes to Bob, while the Asset issued by Y-Registry (NASDAQ:AAPL 10.0) goes to Alice.
This is our test scenario. And let’s begin our exploration here.
Flow of Asset Trading
The overall flow to complete a trade in this test,
- X-Bank and Y-Registry grant Alice and Bob AssetHolder contracts, respectively. Upon the AssetHolder, they issue Asset contracts to Alice and Bob, respectively, according to our test case design.
- Alice and Bob include the other side as observers. We see later this helps both sides to create the Trade contract and exercise Trade_Settle, respectively.
- Alice prepares the information needed to create Trade contract (see analysis below).
- Alice creates Trade contract. This serves as an offer to Bob.
- Bob accepts this offer by exercising Trade_Settle in Trade contract. To accept this offer, Bob needs to provide additional information to complete the flow (see analysis below).
- The logic built-in Trade_Settle completes the whole process.
Analysis of Contract Design
What information is needed to complete the Trade_Settle? Here are all we need.
To understand base and quote, in our example, simply consider base is related to X-Bank, while quote is related to Y-Registry.
Items a to d are known to Alice, while item e and f are known to Bob.
Among these six items there are three types
Base/Quote Asset Contract ID (item a and e): It is the Asset contracts issued by X-Bank and Y-Registry, respectively.
Base/Quote Asset (item b and d): It is the data (content) of the Asset contracts (item a and e). In this setup, both parties need to see content of the Asset contract of the other side. In order to do so, one is placed on the other side as observers.
Base/Quote Approval Contract ID (item c and f): It is the TradeApproval contract, showing that one has approved of receiving an asset specified. In our example, item c means Alice approves Bob’s asset, and item f means Bob approves Alice’s asset. This TradeApproval is created through exercising Preapprove_Transfer choice in AssetHolder contract. Note: This is not the previous AssetHolder contracts created before. For Alice, her previous AssetHolder contract is issued by X-Bank. To successfully receive Bob’s asset, she needs an AssetHolder contract issued by Y-Registry. Therefore we first let Y-Registry create AssetHolder contract to Alice, then Alice can exercise Preapprove_Transfer choice on it. Same applies on the Bob side.
Step 1: Setup of AssetHolder and Asset Contracts for both parties
This is identical to what we have depicted in Part 1. Here we just showed the test script part.
And this is the result after step 1.
Step 2: Observer setting
A choice SetObservers is inside Asset contract template.
The logic is a simple update on the contract. Here is the test script part.
Here is the result. Now we see Bob as an observer in Alice’s asset, and vice-versa. Note the difference from the Asset contracts in Step 1.
Step 3: Preparation of Information for Trade Contract
As discussed above, Alice needs four items to create the Trade contract. Among them, her Asset contract ID and data are already known to her. As an observer she can fetch the Bob’s Asset contract detail. The missing item is her Preapproval.
She first needs an AssetHolder contract from Y-Registry, which is the same flow we have discussed in Part 1. Inside AssetHolder, we see a choice Preapprove_Transfer. Here is the code:
In the choice Preapprove_Transfer, the owner needs to provide an asset details. Note that this is the asset to be received (i.e. Bob’s asset), not Alice’s own asset. The newOwner is now keeping owner (i.e. Alice). The result is a TransferApproval contract, showing that Bob’s asset is preapproved by new owner Alice.
Here is the test script.
- Line 166–172 is for an AssetHolder contract issued by Y-Registry to Alice.
- Line 174 is to obtain the Asset detail of Bob’s Asset (as now Alice is an observer.)
- Line 176 is to obtain the Asset detail of Alice’s own Asset.
- Line 178–180 is to create the approval of Bob’s asset and Alice as the new owner.
And here is the result. We see a new AssetHolder (#9:1), in which Y-Registry is issuer and Alice as owner.
And a TransferApproval is created by Alice. The asset part is Bob’s asset data, while the newOwner is Alice.
Step 4: Creation of Trade Contract
With the items ready in Step 3, we are ready to construct the Trade contract. Here is the Trade contract template (choices not shown yet).
Here is the test script.
And the result is a Trade contract with all the information provided (truncated).
Step 5: Preparation for Trade Settlement
As discussed above, Bob needs two items to exercise the Trade_Settle choice. Besides his own Asset contract ID, which is known to him, he needs a TransferApproval contract. The flow is similar to Alice’s in Step 3: first X-Bank issues AssetHolder contract to Bob, and Bob exercises Preapprove_Transfer choice by giving Alice’s asset detail.
Here is the test script.
- Line 194–200 is for an AssetHolder contract issued by X-Bank to Bob.
- Line 202 is to obtain the asset detail of Alice’s asset (as now Bob is an observer.)
- Line 204–206 is to create the approval of Alice’s asset and Bob as the new owner.
And here is the result. We see a new AssetHolder (#13:1), in which X-Bank is issuer and Bob as owner.
And another TransferApproval (#14:1) is created by Bob. The asset part is Alice’s asset data, while the newOwner is Bob.
Step 6: Trade Settlement
After Bob has all items needed to settle the trade, he exercises Trade_Settle in Alice’s Trade Contract.
First, let’s see the Trade_Settle choice in Trade contract.
The choice Trade_Settle is exercisable by quoteAsset.owner, which is Bob in our test case. For sake of completeness, there are two other choices: the baseAsset.owner (Alice) can cancel the trade, or quoteAsset.owner (Bob) can reject the trade. Both choices cause archival of the Trade contract.
The result of Trade_Settle is a pair of Asset contracts, which are the assets after the trade. As discussed before, quoteAssetCid and baseApprovalCid are needed. They are prepared in Step 5.
Checking is done to ensure that the content fetched from the active Asset Contract is identical to the given Asset content. The “with” clause for observers is to ignore the difference of observers (“observers” is functional and not a part of the Asset contracts).
The logic is to exercise two choices. In baseApproveCid (that is, created by Bob) TrasnferApproval_Transfer is exercised with Alice’s Asset contract. In quoteApprovedCid (that is, created by Alice) TransferApproval_Transfer is exercised with Bob’s Asset contract.
Hence we take a look at the logic of TransferApproval_Transfer in TransferApproval Contract.
It is again another propose-accept model, and we focus on the choice TransferApproval_Transfer. For easy elaboration we again use Bob’s baseApprovalCid as an example. Recall that in baseApprovalCid, asset is Alice’s asset, and newOwner is Bob (see Step 5).
Both asset.owner (which is Alice in this case) and newOwner (Bob) are the controllers for this choice. They are probably handled after Alice’s creation of Trade and Bob’s exercising Trade_Settle.
When exercising this choice, an Asset contract ID assetCid is provided. In our elaboration it is the baseAsset contract ID, which is Alice’s Asset contract ID.
The logic is first to fetch the given Asset contract ID and the result is Alice’s contract content. The assertion is made to ensure the fetched result is the same in the baseApprovalCid (which is Alice’s asset, see Step 5). The remaining steps are straightforward: archiving the given assetCid (Alice’s), and a new Asset contract is created with owner now set to Bob.
The result is an Asset contract, with content identical to Alice’s previous one, but owner now updated to Bob. That is what we need in choice TransferApproval_Transfer.
Back to Trade_Settle choice, two TransferApproval_Transfer complete the atomic transfer (swap) of two assets. The result is what we are expecting: Alice’s Asset is now 10.0 units of Apple shares, while Bob’s Asset is now USD 1,000.0.
Here is our test script.
And the final result. We see the asset trade is complete. Note that all intermediate contracts are archived after the settlement of trade. The AssetHolder contracts are still there as all choices upon them are all non-consuming.
In this part we have walked through how an atomic transaction is made between two parties. With careful design in the templates and various choices, the asset swap is made to ensure both transfers were made with approval from both sides, and this was done in one transaction (one commit).
This completes my study on the asset modeling in Tutorial 7 of DAML documentation.