What patterns are available or encouraged within DAML for addressing sets of contracts within the execution of a choice? Most or all of the Daml facilities for retrieving or exercising choices on contracts (fetch
, fetchByKey
, exercise
… ) appear to be defined to work on single contracts only.
The cases for this that come to mind are querying a set of contracts for information or exercising a choice on each of a set of contracts.
The best I’ve been able to come up with is a top level contract that maintains a list of keys that can be used to look up each subordinate contract in the set. For example, I have a Daml model of todo lists, where each list has a set of items, each modeled as a contract.
One of the operations in the model is that a list can iterate over all of the items it contains and update the ownership of each. The code to do this is as follows, and depends on a list of item ID’s (itemIds
) that’s maintained in the list contract.
nonconsuming choice TodoList_UpdateItemOwnership : ()
with
party : Party
existingOwners : [ Party ]
controller party
do
forA itemIds (\itemId -> do
(cid, m) <- fetchByKey @TodoItem (existingOwners, itemId)
exercise cid TodoItem_SetOwnership
with
party
newOwners = owners)
return ()
My concern with this approach, though, is that it duplicates information in the model. Each todo list has a list of item ID’s and each toto item knows what list it’s a part of. Adding an item to a list therefore means both creating the item contract and updating the list contract with the new item ID.
There’s a similar issue with respect to querying a set of contracts. It’s possible to write code similar to the above that uses itemId
to fetch each item in a list, but again, that implies that the list contract needs to have that list of item IDs.
Is there a way to do this sort of thing with out explicitly maintaining lists of keys? Some sort of ‘fetch multiple’ or ‘exercise multiple’.
Would appreciate thoughts or guidance on best practices here. (Even if the current guidance is that this is currently an inappropriate design choice.)
For the sake of completeness, this is the code from the above model that adds an item to a list. This is a choice defined on the todo list contract that’s used to add an item. As you can see, the majority of its job is updating the itemIds
list.
choice TodoList_AddItem : (ContractId TodoList, ContractId TodoItem)
with
party : Party
itemId : Text
itemDescription : Text
controller party
do
lcid <- create TodoList with
listId
name
owners
itemIds = itemIds ++ [ itemId ]
icid <- create TodoItem with
itemId = itemId
description = itemDescription
listId = listId
owners = owners
return (lcid, icid)