How to create independent Smart Contracts in Daml?

Hello All,

I have some follow up questions regarding the topic previously created;

As a quick reminder, I have a project where I try to implement multiple, individually separated Smart Contracts that interchange content-relevant data. In my approach, the key feature for Smart Contracts is to be fully independent of each other and I simply wonder what is the best way to achieve this idea in DAML. In summary, Signatories and Observers, plus the absence of bearer tokens in DAML are the main obstacles for me.

As mentioned in the previous topic, I mainly make use of Bearer tokens in my project. Because DAML does not offer bearer tokens I declare and utilize them as record types, which is quite fine however, there are many cases where use-case scenario involves multiple data types interchanged among smart contracts based on some conditions which then led me to use of Sum types as return types (thanks to @Anthony). Yet, when Sum types are being used another problem occurred which is already asked/answered previously and ended up sort of at a dead end. In my way of thinking returning different/multiple data types and/or ContractIds according to some conditions is a common requirement to deal with in many projects, consequently, there must be a way to run DAML in this way which I’m currently missing.

Additionally, signatories and/or Observers require pre-specification of entities who will interact with the contracts being created and I cannot know those parties in all the cases I have. Therefore, I want to somehow get rid of at least the declaration of a list of observers in a way that parties who are asked to can directly access the data held within contracts.

I’m fully open to any kind of solutions, suggestions, and evaluations. Thank you very much for your time in advance.

1 Like

Hi Kayle,
I’m going to try and answer some questions here, but if you could provide a use-case scenario for what you are trying to achieve eg, “Trying to make a market place where contracts are visible to everyone”, that would help me a bit more.

Additionally, signatories and/or Observers require pre-specification of entities who will interact with the contracts being created and I cannot know those parties in all the cases I have. Therefore, I want to somehow get rid of at least the declaration of a list of observers in a way that parties who are asked to can directly access the data held within contracts.

If you get rid of an observer list, eg just write a template with no observers (which is possible as that as an optional field) ONLY the signatory will be able to view that contract, and only the signatory can decide to add a known party (eg, you would need to know the id of the person you want to add) and add that observer.

Is the below a scenario you are trying to achieve?

  • user A creates a contract, with ContractId 01, with some data inside, (currently only user A can see this)
  • user B should be able to query ContractId01 ?

I’m not sure if what you’re trying to do can be solved via Public Parties in Daml?

2 Likes

Hi @Kayle,

I’m not entirely sure I understand what you’re trying to do here. Contracts in Daml don’t “do” stuff, like exchanging information; contracts are, and parties act on them. So in a Daml world it would make more sense to say parties share information through contracts.

Daml also comes with a very strong privacy model, which means among other things that two contracts that are “fully independent” cannot interact with each other at all.

Using Bearer tokens within Daml is, generally speaking, an indication that your design is not aligned with the Daml approach; it would help greatly if you could describe what external outcome you are trying to achieve, rather than describe a preexisting, non-Daml design and ask how to implement exactly that in Daml.

Therefore, I want to somehow get rid of at least the declaration of a list of observers in a way that parties who are asked to can directly access the data held within contracts.

There is a technique in Daml called “flexible controllers”, which in combination with “public parties” may be used to achieve what you’re getting at here, if I understand you correctly. Here is how it works:

  • The contract is created with a “fake” party as the observer. This party is fake in that nobody his ever allowed to “act as” that party, but many people can get a token with “read as” that party. This constraint must be implemented in the token generator, outside of the Daml code.
  • The people who are supposed to have access to that contract get the fake party as “read as” on their tokens, so they can see the contract.
  • Choices on the contract receive the controller as an argument, allowing anyone who can see the contract to pass themselves in when exercising the choice.

Here is a quick sketch of how the Daml bits work. Again, this is a technique that requires coordination between the Daml logic and the auth server logic in order to generate appropriate tokens.

module Main where

import Daml.Script

template Product
  with
    public: Party
    supplier: Party
    name: Text
  where
    signatory supplier
    observer public
    nonconsuming choice CreateReservation : ContractId Reservation
      with customer: Party
      controller customer
      do
        create Reservation with ..

template Reservation
  with
    supplier: Party
    name: Text
    customer: Party
  where
    signatory supplier, customer

setup : Script ()
setup = script do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
  public <- allocatePartyWithHint "public" (PartyIdHint "public")
  p1 <- submit alice do
    createCmd Product with supplier = alice, public, name = "product"
  submitMulti [bob] [public] do
    exerciseCmd p1 CreateReservation with customer = bob
  return ()

In this snippet, any (possibly future) party can create a reservation, as long as they have access to a token with “readAs: public”.

3 Likes

Without trying to implement exactly what’s being asked I think there’s a couple things to consider here.

There’s often no need to handle the logic on choosing which contracts to create on the Daml side. You can instead decide on the client side what type of contract you need to make. Then instantiate that type of contract on the Daml side.

If there’s a complex workflow that also requires reacting to the creation of certain contracts, and then creating others, then reacting to these events could also be performed client side or perhaps Daml Triggers would come in to play.

Templates do not hard code specific parties/entities as signatories and observers. Templates are a reusable abstraction on this much like a Class and come into their concrete form later when they are instantiated into a Contract.

Take for example:

module Test where

import Daml.Script

template Appointment
  with
    -- We don't know providers and patients ahead of time
    -- We only know of the general relationship between them to form an appointment
    provider : Party
    patient : Party
  where
    signatory provider
    observer patient

example = script do
    alice <- allocateParty "Alice"
    bob <- allocateParty "Bob"
    doctorCarol <- allocateParty "Dr Carol"
    doctorDave <- allocateParty "Dr Dave"

    -- Later when we need to make an actual appointment we can make specific instances of our template
    aliceAppointment <- submit doctorCarol $ 
        createCmd Appointment 
            with provider = doctorCarol, patient = alice
    
    bobAppointment1 <- submit doctorDave $
        createCmd Appointment
            with provider = doctorDave, patient = bob

    bobAppointment2 <- submit doctorCarol $
        createCmd Appointment
            with provider = doctorCarol, patient = bob

    return ()

We could reuse our Appointment template as many times as we like with as many different parties as we want, and here Bob and Carol will not see each other’s appointments as demonstrated in the Script output

image

Note also that while I’m doing these createCmd actions from Daml Script and thus using Daml this same thing can be done with our JSON API or Ledger API with calls to their contract creation commands from a frontend or UI. Script is often just a way to test code or perform initial setup of a ledger.

1 Like

Fırst of all I do thank you to all ındıvıdually for your ınterest and tıme and apologize for the late reply. I can understand that the main idea seems blurry for you, I hope things will get more clear after this post. I have individual task descriptions written in a DSL which I try to transform into DAML while keeping the semantic and relational knowledge intact. The schema given below might provide a better understanding:

We have two snippets here namely “BookingWebsite” and “BoardingService” that receive and return some context-relevant information (bearer tokens). Let us consider “BookingWebsite” for instance, it receives “FlightInfo”, “UserInfo”, “Payment” as input, and given that conditions are satisfied, it returns a “Booking” token which then fed to “BoardingService” along with some other relational data such as “Passport” etc. In the next step, “BoardingService”, based on the conditions, returns one of the specified data types (“BoardingPass”, “PreBoardingPass”, “BusinessAddon”). From here, we may assume that the next step is unknown, meaning that, “Passenger” may interact let’s say “CustomerService” with “PreBoardingPass” or “Gate” with “BoardingPass”.

If we summarise all the arguments here;

  • Source DSL makes use of bearer tokens quite often which is not offered in DAML and represented by me as Record Types and I’m not exactly sure how wise to do it this way, as @Gary_Verhaegen wrote, it feels like, it is an indication that my design is not aligned with the DAML approach.
  • Data-flow in the current implementation is not as strict as it would be in DAML because of signatories, observers, etc. Furthermore, as explained earlier, I cannot know who is the next “entity” that will be interacted by the “passenger” in all the possible scenarios.
  • Returning multiple types depending on conditions as “Sum Types” requires a shared Sum data type between Smart Contracts and causes dependency which is simply against my goal.

I hope this explanation would give you a clearer vision of what I’m after. Once again, I’d kindly like to hear what would be your way to do so, I’m open to any suggestions and recommendations. Thank you in advance.

1 Like

You have two options here, depending on what the Passenger is doing:

  1. Any party with access to a particular contract can exercise a choice on the BoardingPass PreBoardingPass or BusinessAddon (what I’ll generally call the passenger’s contract here). This choice would allow for the Passenger or someone else (ex. Customer Service) to do something with this contract. Each of these choices can also return a new version of their contract so that the Passenger doesn’t lose the contract, they just get a new one.
  2. The Passenger can provide their contract to say the CustomerService who can use the contract as an argument to a choice on a CustomerService contract. This way a Customer Service agent can take the pass, run whatever choice(s) they need to with it, and issue a new passenger contract that’s either of the same type or new type.

Really in general your workflow is either:

  1. Consuming/archiving an old contract and issuing a new one any time an action needs to take place
  2. Creating a new contract and optionally depending on existing contracts to do so (without necessarily consuming/archiving the existing contracts)

Say you have a template like the below. Here we don’t know what the Passenger will do next with their BoardingPass contract, they could go to Customer Service to change their seat, or to the Gate to board their plane. In either case both are accounted for and the template doesn’t care about the order. So here the Passenger goes to Customer Service, gives their their boarding pass, and Customer Service exercises the choice ChangeSeat which archives the boarding pass they were just given, and issues a new one with the seat changed as desired.

This new contract is identical in functionality to the old one, the Passenger can go back to Customer Service to change their seat a second time, or go to the Gate to board their plane.

Interestingly here Daml caused me to think about the BoardingPass contract and how the Gate would handle it, after all it’s not valid nor desirable for a Passenger to board a plane and still have a usable BoardingPass that they can go back to Customer Service after they land and have them change an old seat assignment again. So here we can either have Gate archive the contract (by returning () from the function; ie. nothing/unit, just archive the contract) OR we could have them issue a BoardingPassReceipt which any Customer Service interacting with will now know is just a Receipt and not an actual BoardingPass. Perhaps this Receipt would then even have choices on it such as LogPostFlightFeedback or other logic that belongs with a receipt and not a BoardingPass.


data PassStatus = Final | IncorrectOrigin | NoSeatDeclared deriving (Eq, Show) -- More on this later in the post

template BoardingPass
  with
    passenger : Party
    customerServiceRepresentative : Party
    gate : Party
    seat : Text
    passStatus : PassStatus
  where
    signatory passenger
    observer customerServiceRepresentative, gate

    controller customerServiceRepresentative can
        ChangeSeat : ContractId BoardingPass -- This is like option 1 at the start of my post
            with
                newSeat : Text
            do
                create this with 
                    seat = newSeat 

    controller gate can
        AcceptBoardingPass : ContractId BoardingPassReceipt
            do
                create BoardingPassReceipt with ..

template BoardingPassReceipt
  with
    passenger : Party
    customerServiceRepresentative : Party
    gate : Party
    seat : Text
    passStatus : PassStatus
  where
    signatory passenger
    observer customerServiceRepresentative, gate

-- Here I can take other contracts and use them as arguments to choices
-- Without needing the other contract (ie. BoardingPassReceipt) to itself know anything about the CustomerServicePostFlightLog contract
template CustomerServicePostFlightLog
  with
    customerServiceRepresentative : Party
    log : [(BoardingPassReceipt, Text)]
  where
    signatory customerServiceRepresentative

    controller customerServiceRepresentative can
        LogPostFlightFeedback : ContractId CustomerServicePostFlightLog -- This is like option 2 at the start of my post
            with
                boardingPassReceipt : BoardingPassReceipt
                feedback : Text
            do
            create this with
                log = (boardingPassReceipt, feedback) :: log

Here ContractIds are roughly equivalent to this definition of Bearer Tokens in that they point to a Contract (which is a Record) on the Daml Ledger. You can use them to fetch the actual Contract and its Record data, and if you don’t know the ContractId for a Contract you can query for it (and the actual Record data) via the JSON API or Ledger API.

If the process needs to be exactly as described then Sum Types are needed as we don’t have generic templates in Daml right now. The alternative is having the client side logic (such as that of the BoardingPass issuer) determine what Contract they need to create or what Choice they need to exercise on a given contract is the way to avoid this in Daml. The advantage in doing it this way is that clients make reasonably sure that they’re making valid requests.

You could also special case things like BoardingPass and PreBoardingPass, if they’re basically the same record except one isn’t finalized then you can have a boolean status like isFinal on BoardingPass instead of two separate templates. Or you could even have a variety of statuses by including a record like:

data PassStatus = Final | IncorrectOrigin | NoSeatDeclared

template BoardingPass
  with
    passenger : Party
    issuer : Party
    customerServiceRepresentative : Party
    gate : Party
    seat : Text
    passStatus : PassStatus
    ...
2 Likes

I would suggest a small shift in perspective. You seem to be currently thinking of your system as a collection of records/data items, and then a collection of “global” actions that a number of agents can take to change some elements of this collection. That’s not a bad way to think about a system in general, but taking this approach and trying to transform each record into a Daml contract is not going to mesh very well with the broader Daml model.

Instead of trying to turn your data into templates, I would suggest thinking of your problem as a decision flowchart. Think of the “states” of your system as discrete points in time where the list of possible actions is known (though of course you don’t necessarily know which action is going to be taken). Each action can take the system to a new state (possibly of the same type), which will have its own set of possible actions.

Daml templates will then be a natural way to represent a given state of the system, allowing you to enumerate all of the possible actions at that point in time, along with who can initiate each of them, and what the resulting new state would be.

If you follow this approach, you will probably end up with a few data fields that appear in more than one template. This is not an issue: templates are not the primary way of representing data in Daml, records are. A template definition automatically creates an associated record definition as a convenience, but it is definitely possible (and encouraged) to define your own record types independent of templates (using the data keyword). Templates can then use those records.

2 Likes

Hello again,

Thank you very much, @anthony and @Gary_Verhaegen. Your explicit explanations and valuable suggestions are quite helpful. I gained better understanding and can move on with my project from here, as I do not have any additional questions.

1 Like