Fix my Bike Again: Demonstration of Navigator in Daml
In my previous works on Daml, I keep using Daml Studio to learn, practice, and test this modeling language. Besides code editing, Daml Studio has an environment of script execution, which is good for code development and workflow simulation. Through simulation we can observe the lifecycle of contracts in the Daml ledger and make interactions with these active contracts through exercising choices.
Daml comes with another good tool, Navigator, which gives us a more intuitive graphical user interface (GUI) and lets us interact with the contracts on the fly. No test script is needed as everything is done on the GUI. In this article, we are using the BikeShop example again and see how the demonstration is done with Navigator.
You can refer to my previous article about the BikeShop example and how to use Daml Studio for simulation. We will use the same set of code, and you can make comparisons between these two tools.
While Daml Studio comes with everything for simulation, Navigator is a frontend of a stack, in which the Daml code is first compiled and then working with a Daml ledger. Our demonstration is using Daml Sandbox, while Navigator can work with other Daml ledgers.
The launch of this stack is done in a simple Daml command, with proper configuration in the configuration file daml.yaml. We are using the empty skeleton template as it is good enough to build what we need in this demonstration.
Create a project
bikedemo with template empty skeleton.
daml new bikedemo --template empty-skeleton
We need to work on two files.
We first modify the configuration file,
daml.yaml. The basic configuration is good enough for demonstration. We just change the parties to those relevant to us. Remove Alice and Bob, and insert Martin, BikeShop and SwissBank. These will be seen when Navigator is launched.
Also we create the file
daml/BikeShop.daml. Here is the code we directly copy from my previous article. We do not need a test script here as we do not test it in the Daml Studio.
module BikeShop wheredata Currency = USD | EUR | GBP | CHF
deriving (Eq, Show)template Cash
controller owner can
Transfer : ContractId Cash
create this with owner=newOwnertemplate BikeRepair
signatory bikeShop, bikeOwner
controller bikeOwner can
Pay : ContractId Cash
cashCid: ContractId Cash
cash <- fetch cashCid
cash.currency == CHF &&
cash.amount == price)
exercise cashCid Transfer with newOwner=bikeShoptemplate BikeRepairProposal
controller receiver can
Accept : ContractId BikeRepair
To launch the stack,
This command will first compile our code into DAR files. Then both Daml Sandbox and Navigator are being launched.
Navigator is launched automatically, or you can access it http://localhost:7500 on a browser.
In “Choose your role”, we see those parties configured in
Now we are ready for using Navigator to perform our interaction.
Step 1: Quick introduction of Navigator
Login with any party (say Martin). We see Martin’s view in Navigator.
There are two views in Navigator: Contracts and Templates. In Contracts, we will see active contracts, and archived contracts if we check “include archived”. In Templates, we will see the templates coded in our model.
You can refer to my previous article about Contracts and Templates in Daml.
Currently we see no contracts in the Contracts page, meaning that there are no active contracts seen by Martin. In the Templates page, we see the three templates coded in the model: Cash, BikeRepairProposal and BikeRepair.
We can try other parties (SwissBank and BikeShop) and see similar results. As our model is newly deployed, there are no active contracts yet in the ledger.
Step 2: SwissBank issues Cash
We first login as SwissBank. Select Template Cash. A page is shown requesting parameters defined in the template Cash. They are issuer, owner, currency and amount.
To create a contract with template Cash, we need to provide certain information. According to our demonstration, we input information
- issuer: SwissBank
- owner: Martin
- currency: CHF
- amount: 200.0
A pull-down menu is given if we define a data type as enum (like currency in Cash template).
After we Submit, an active Cash contract is now seen in SwissBank.
Note the contract ID (000c3d9…) of this Cash contract. Click this contract and we see the details.
Besides the detail, we also see the two choices available in this Cash contract: Transfer and Archive. The Archive is the default one for all contracts. Transfer is the choice coded in the model.
Here Martin is in Observers, which means that Martin can see this contract. Logout from SwissBank and login as Martin. We immediately see the same Cash contract in Martin’s contracts page (note the same contract ID).
We say this Cash contract is active in the ledger, and currently visible to both SwissBank and Martin.
Step 3: BikeShop proposes a service offer to Martin
Now Martin visits the BikeShop asking for a repair service for his bike. BikeShop creates a BikeRepairProposal contract with Martin.
Login as BikeShop. First, we see nothing (not the Cash contract) in BikeShop’s view. We know that there is an active Cash contract in the ledger, but it is only the matter between SwissBank and Martin. It is not visible to BikeShop.
Now go to templates and select BikeRepairProposal. Fill in the required parameters and click Submit.
- proposer: BikeShop
- receiver: Martin
- proposal.bikeShop: BikeShop
- proposal.bikeOwner: Martin
- proposal.description: “Fix the bike”
- proposal.price: 200.0
- proposal.paymentDue: 2021–03–05
With this, now there is an active BikeRepairProposal contract, visible by BikeShop.
Per our template design, Martin the receiver is set as Observers. Besides, Martin the receiver can accept this contract. Logout from BikeShop and login Martin.
Step 4: Martin accepts the proposal
We now see two active contracts in Martin’s view. The Cash contract issued by SwissBank, and the BikeRepairProposal contract created by BikeShop.
Click the BikeRepairProposal contract for the details. Assuming Martin agrees to the term and accepts this proposal. In Daml term Martin is going to exercise the choice Accept in BikeRepairProposal contract.
We can click the Choice and see the choices Martin can act here. This is the same as Martin doing this inside the contract.
Here select Accept. This comes a page showing the Contract information and further information required (nothing is required in this choice). After everything looks good, click Submit to exercise this choice.
After the ledger is updated, we now see the BikeRepairProposal contract gone, and a new BikeRepair contract is there. This is the service contract between Martin and BikeShop, with both authorization (and consent), showing the job detail and the price.
By checking Include archived, we will see the BikeRepairProposal contract. It was archived after Martin accepted the proposal.
And of no surprise, we see this same BikeRepair contract in BikeShop.
Step 5: Martin pays BikeShop after the service is complete
Assuming Martin is happy with the service and uses his Cash contract, issued by SwissBank, to pay BikeShop.
Login as Martin, in the BikeRepair contract, and exercise the Pay choice. Let us first see what is needed when exercising this choice.
Besides the contract detail, a Cash contract ID is required in order to exercise the Pay choice. Therefore we need to copy the Cash contract ID first.
Go back to the Cash contract, copy the ID. Here it is.
Now fill in the Pay choice in BikeRepair contract with this Cash contract ID and click Submit.
After Pay choice is completely exercised, we see no more active contracts in Martin’s view.
And those previously active contracts were all archived according to the code. The obligation he needs to fulfil in the BikeRepair contract (agree to pay 200.0 to BikeShop for the repair service) and his asset Cash contract is paid to BikeShop. Therefore no more contractual binding on Martin.
In BikeShop’s view, we only see the active Cash contract (contract ID: 00ddfc05…). It is the result after his fulfilment of service in the BikeRepair contract. The detail is same as Martin’s previous Cash contract except for that the owner is now BikeShop.
Similarly, the BikeRepair contract is archived as everything in that contract is fulfilled.
Finally, make observations from SwissBank. There is one active Cash contract and one archived Cash contract seen. The active one is the one owned by BikeShop, while the archived one is owned by Martin. As we see here, though the content (CHF 200.0) is identical, these two are different contracts (see the contract IDs).
There are two observations during our demonstration.
Data privacy is properly enforced in Daml. While contracts are active in the ledger, whether they are visible to individual parties depends on the model we design in the code. For example, SwissBank only sees one active Cash contract, first created and owned by Martin and later owned by BikeShop. SwissBank has no information why this happens and the business (repair contract) between Martin and BikeShop. This meets the business requirement in real life.
Besides, in Navigator, a choice visible to a party does not mean that this party can exercise it. It is still subject to the model we design in the code. For example, in the Cash template only the owner can exercise Transfer choice. We can do a quick test: login to SwissBank, select the Cash contract and exercise Transfer choice. The transfer fails. It also meets real life requirements as no one except for the owner can transfer the cash, not even the issuer.
We have repeated our demonstration in the previous article with the Navigator, which gives us a user friendly browser-based tool to interact with our model deployed in the ledger (here we are using Sandbox). Hope this brings you more information when you learn and practice with Daml. You can refer to Daml documentation for more about Navigator.