Sometimes different DAML templates have a common structure. Typically this occurs when there is some logic (usually in choices) that can be applied to many different underlying contracts. Generic templates allow you to capture this logic in a single place, instead of having to duplicate it for every template. Let’s see a simple example to understand what this means.
Example: Generic Proposal¶
Suppose we want to model a propose and accept workflow. This means that a party can propose a contract to a specific party, who may accept it with the terms of that contract. We see this pattern occur frequently for different types of contracts. Of course, we could implement one proposal template for every underlying template. However this is tedious and error prone. Alternatively, we can write it once and for all using a generic template.
This is how the generic
Proposal template looks in DAML.
template Template t => Proposal t with asset : t receiver : Party where signatory (signatory asset \\ [receiver]) controller receiver can Accept : ContractId t do create asset
There are several things to notice in this short template.
Firstly, where we usually see a template name, there is now a more general template header
Template t => Proposal t. This can include multiple type parameters and constraints on those types. In this case
Proposal takes a single type parameter
t representing the type of the underlying asset. The
Template constraint says that
t is not just any type but a contract template with signatories, choices and more.
asset parameter to the template has the abstract type
t. We don’t know anything about
asset other than that it is an instance of
Template. However this is all we need to implement the proposal template.
Notice that the signatories of the proposal are obtained from the signatories of the underlying contract. This is done by calling the overloaded
signatory method. In this case we exclude the receiving party, as this is the one whose authorization we want to gain from accepting the proposal.
Finally let’s look at the
Accept choice which characterizes the propose and accept workflow. The receiver can
Accept which results in a contract of the underlying asset type being created. We are able to call
create on the asset since we know it satisfies the
The above template soundly represents the proposal workflow, but we have not yet used it on a concrete (non-generic) template. We call the concrete instantiation a template instance.
Let’s introduce a very simple
Coin contract that we can use in the proposal.
template Coin with issuer : Party owner : Party where signatory issuer, owner
We would like to model an issuing party (e.g. a bank) proposing a coin contract for an individual to accept. To do this, we need to explicitly state our intention to use
Coin in a
Proposal. We do this using the
template instance syntax.
template instance CoinProposal = Proposal Coin
Note that we must choose a name, here
CoinProposal, for the template instance. It serves two purposes. First, it provides a type synonym for the underlying type, here
Proposal Coin. Second, it must be used to create contracts of this template in some client languages (for example when using the Java ledger bindings).
With a template instance in place, we can create and exercise choices on contracts of this type.
coinIssuance = scenario do alice <- getParty "alice" bank <- getParty "bank" let coin = Coin with issuer = bank; owner = alice let proposal = Proposal with asset = coin; receiver = alice propId <- bank `submit` create proposal alice `submit` exercise propId Accept
We construct the underlying asset and the proposal data using the
Proposal data constructors respectively. The bank is able to create the coin proposal which Alice can then accept. This scenario results in two contract creations: first the proposal contract, which is consumed to give rise to the coin contract.