Is there a way to define TemplateA such that when a contract of TemplateA is created, it automatically creates a contract of TemplateB? I guess it would be like a choice that automatically runs or a do block that serves as a “constructor.”
Daml has no “constructor” and a create event has no consequence other than the creation of the contract itself. If you want to construct two contracts atomically you can make it part of exercising a choice on a “constructor” contract. Depending on your situation it might be some sort of “static” contract that only contains the (possibly non-consuming) logic to instantiate these two contracts atomically.
If going that route you have to bear in mind that the b in A is not a contract of Template B but a record of type B. That can get confusing; I’m not sure I’d recommend it.
As Stefano mentioned, there is no way to enforce this.
You can have a third template with a choice that create the two templates, and thus if you go through that choice to create your contracts, both will be created at the same time atomically. But there is no way to prevent someone else from creating either of the two contracts on its own.
Another possible approach would be to have a trigger that tries to enforce “there is a B contract for every instance of A”. You’ll have to decide what to do when the rule is violated: do you archive the orphan contract, create the missing one, etc.
This works under the assumption that you don’t need the contracts to be created atomically. It can be achieved with a trigger or with any other kind of off-ledger integration using the Ledger API.
Yes, there’s definitely a tradeoff there. The rule approach (whether it’s implemented with a Trigger or a custom Ledger API client) has the disadvantage that it’s not atomic, but the advantage that if contracts get created “directly” (which you cannot prevent) they still get caught.
Depending on use case, you may need to have both: a choice that creates both contracts atomically and a rule that archives “orphan” ones, perhaps?
Here is my attempt to illustrate the suggested approach of a “third template with a choice that creates the other two templates.”
import Daml.Script
template StudentView
with
teacher : Party
student : Party
question : Text
where
signatory teacher
observer student
template TeacherView
with
teacher : Party
student : Party
question : Text
solution : Text -- only teacher can see this
where
signatory teacher
template Questions
with
teacher : Party
where
signatory teacher
nonconsuming choice Create : (ContractId TeacherView, ContractId StudentView)
with
student : Party
question : Text
solution : Text
controller teacher
do
studentView <- create StudentView with teacher, student, question
teacherView <- create TeacherView with teacher, student, question, solution
return (teacherView, studentView)
test_create = do
student <- allocateParty "Student"
teacher <- allocateParty "Teacher"
questions <- submit teacher do createCmd Questions with teacher
submit teacher do
exerciseCmd questions Create with student, question = "What is the answer?", solution = "42"
Does that capture the idea? Comments?
This does not tackle the problem of “orphan” contracts.
It captures the idea, yes. Perhaps one thing you can do, if feasible, is to “embed” the StudentView as a ContractId StudentView in TeacherView, so that you have a single reference to the question. Something along these lines:
template Question
with
teacher : Party
student : Party
question : Text
where
signatory teacher
observer student
template Answer
with
question: ContractId Question
solution : Text -- only signatory of the question (i.e. the teacher) can see this
where
signatory (signatory question)
template Questions
with
teacher : Party
where
signatory teacher
nonconsuming choice Create : ContractId Question
with
student : Party
question : Text
solution : Text
controller teacher
do
questionId <- create Question with teacher, student, question
answerId <- create Answer with questionId, solution
return questionId
Ha! When I was initially building my example I tried what you suggest, @stefanobaghino-da. However, I could not get it to work. I could not figure out how to get the teacher out of the other contract.
From your example, this was the skill I was missing: