Contracts that reference unobserved contracts

There may be a few of these questions coming from me over the next few weeks as I figure out the architecture of my app and whether or not DAML is right for my project.

Let’s say you have a contract:

template Product
  with
    producer : Party
    productType : ProductType
    label : Text
  where
    signatory producer

and another:

template ProductType
  with
    admin : Party
    description : Text
  where
    signatory admin

ProductType contracts are created by site admins and are basically categories for different types of products. Product contracts are created by producers. Is it possible for a producer to create a Product given that they are not observers on ProductType contract? I tried this test code:

productScenario = scenario do
  admin <- getParty "Admin"
  producer <- getParty "Producer"

  productTypeCid <- submit admin do
    create ProductType with
      description = "product"
      admin

  productCid <- submit producer do
    productType <- fetch productTypeCid
    create Product with
      producer
      productType
      label = "label

  return productCid

…it fails because the producer cannot fetch a ProductType contract as he/she is not an observer on it. Maybe there is a hacky way to do this in the UI code, by:

  • making ProductType observable to a public party (as discussed here Should everything be on the ledger?)
  • fetching ProductType with the public token
  • creating the Product with the producer token

Or maybe I should just have a ContractId ProductType on the Product?

Would either work? Do you foresee any problems?

Thanks!

1 Like

Hi Alex, no worries, this is the place to ask questions! Beware you may get different answers here.

I would not go down the route of making the ProductType public. I think there is a simpler solution here. You could add a choice CreateProduct: Product on ProductType, and specifically use the choice first syntax to pass the producer as an argument to the choice - this way you don’t need to maintain an explicit list of observers.

However, you should ask yourself: is every producer allowed to create every type of product? If that is not the case, it would be better to maintain a list of parties on the ProductType template that are allowed to create that type of product. In this case, use the controller first choice syntax for your CreateProduct choice.

Btw the pattern that we’re using here is the ‘master agreement’ pattern. It has an analogue in real life. For instance, in finance/brokerage, a broker will have a ‘master agreement’ with a client. It will say in broad terms that they can agree to enter into an agreement to buy/sell securities with discretion; meaning, they don’t need to physically sign a contract every single time a new transaction takes place, but rather use the master contract to delegate this right. The same happens when you add observers to your ProductType contract - that is the act of the ‘admins’ (as you call them) delegating the producers the right to create new Products in future, without having to agree every single time.

Let me know if that answers your question.

2 Likes

Thank you @Luciano.

Ah, that looks nifty! Glad I asked on the forum!

So could do something like:

template ProductType
  with
    admin : Party
    description : Text
  where
    signatory admin
    choice CreateProduct : ContractId Product
      with
        producer : Party
        label : Text
      controller producer
        do
          create Product with
            producer
            productType = this -- ????
            label

(I haven’t tried this in the IDE, but something like that)
Is productType = this correct?

EDIT: This design pattern still fails in a test scenario for the same reason. Even with the choice as ‘choice first’, the producer is not an observer of ProductType and hence cannot make the CreateProduct choice!

That page on choice first says:
" If you do this, you must make sure that you add that party as an observer , otherwise they won’t be able to see the contract (and therefore won’t be able to exercise the choice)."

So I would have to make producer observer on the ProductType contract? That seems like it doesn’t solve the problem because there could be many producers that want to make a product of ProductType.

Here is a complete scenario that fails with ‘choice first’. Any advice?

template Product
  with
    producer : Party
    productType : ProductType
    label : Text
  where
    signatory producer

template ProductType
  with
    admin : Party
    description : Text
  where
    signatory admin
    choice CreateProduct : ContractId Product
      with
        producer : Party
        label : Text
      controller producer
        do
          create Product with
            producer
            productType = this -- ????
            label

test = scenario do
  admin <- getParty "platform admin"
  producer <- getParty "starbucks"

  productTypeCid <- submit admin do
    create ProductType with
      admin
      description = "coffee"

  productCid <- submit producer do
    exercise productTypeCid CreateProduct with
      producer
      label = "starbucks coffee"

  return productCid

That’s very close. I realize now I made an error of judgement above - you won’t be able to simply to create new products without first ‘registering’ the producer.

I added a choice AddProducer to do this.

I also added a productId, which is used to distinguish different product types. This also serves to decouple the ProductType form Product. I would eventually set this as a key to allow producers to look up products easily.

Finally, for clarity, I renamed producerType to producerMasterAgreement.

daml 1.2

module Main where

import DA.Next.Set

type ProductId = Text
-- | ^ Should use a newtype instead (if dynamic) or a data (sum) type 
-- |   if there are an exhaustive/known number of products.

template Product
  with
    producer : Party
    productId : ProductId
    label : Text
  where
    signatory producer

template ProductMasterAgreement -- formerly ProductType
  with
    admin : Party
    id: ProductId 
    description : Text
    producers : Set Party
  where
    signatory admin

    observer producers

    choice AddProducer: ContractId ProductMasterAgreement
      with producer: Party
      controller admin
      do create this with producers = insert producer producers

    nonconsuming choice CreateProduct : ContractId Product
      with
        producer : Party
        label : Text
      controller producer
        do
          create Product with
            producer
            productId = id
            label

test = scenario do
  admin <- getParty "platform admin"
  producer <- getParty "starbucks"

  productMasterAgreementCid <- submit admin do
    create ProductMasterAgreement with
      admin
      id = "00001"
      description = "coffee"
      producers = empty
  productMasterAgreementCid <- submit admin do
    exercise productMasterAgreementCid (AddProducer producer)

  productCid <- submit producer do
    exercise productMasterAgreementCid CreateProduct with
      producer
      label = "starbucks coffee"

  return productCid
1 Like

Thank you for your help @Luciano. Unfortunately the need to register producers each time may mean the app isn’t scalable. The app is being made as if it would ultimately used for a fairly large-scale trading platform. I’ll have to think about this for a while!

EDIT: Maybe this would be a good point to ask (and maybe the answer is unclear, or platform dependant): At what point would an app with many ‘producers’ incur considerable overhead for the ledger (in terms of size, transaction time e.g.)? (If you had to regenerate the contract for every producer that wanted to make a product of a given type)

I’m not sure I completely understand your concern here. There may be some confusion:

the need to register producers each time may mean the app isn’t scalable.

If you had to regenerate the contract for every producer that wanted to make a product of a given type

Just so that we’re on the same page here: you don’t need to register a producer every time you create a new product - this only needs to be done once per producer, per product type. If you key your template by productId, it means you can have multiple ProductType contracts with different productIds.

Also note the nonconsuming keyword I use in my code example - this prevents the contract from being archived when you are creating new products.

The only potential problem I can think of here is if you really have a constantly changing number of producers - this could induce some contention on your ProductType contract. The way around this would be to refactor to have a single ProductType per producer, per product.

Also, keep in mind, as per your point:

maybe the answer is … platform dependant

DLTs tend to have a much higher throughput than public blockchains - we’re talking about thousands of transactions per second, rather than a single block every couple of seconds.

MIght I make a suggestion? Have a look at GitHub - RobinKrom/dbay: A distributed market place built with DAML. This is an ebay-like application by @drsk , running on DAML. That could give you some ideas about how to go about implementing an e-commerce platform.

1 Like

Cheers @Luciano. Thank you for the clarification. I’ll check out the app. I was worried that a ledger that held a new contract for every new person added, as well as all the archived ones, would mean that memory would inflate. That being said, I am still super green when it comes to blockchain! This is my first foray into the topic!

I have to say I am incredibly impressed with the help I have received on the forum. Please pass my regards!