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:
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.
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
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 https://github.com/RobinKrom/dbay. 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.
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!