Should I store contract Ids on other contracts?

What do you think of saving a list of contract id into another contract. Such as a wallet contract can hold many coin contracts? Is it a good idea from technical point of view?

2 Likes

@Frankie I’d say that in general it’s only advisable to use Contract Ids if the referenced contracts will not change, or will only change in unison. The reason is that you otherwise either get stale references or contention. Let’s use your example to see why.

Suppose I have a Wallet with a field contents : [ContractId Coin]. Now suppose I have a choice Transfer on Wallet which inspects the contents (ie fetches coins), and merges and splits them to transfer a given amount to someone else.

Firstly, this choice reads (ie fetches) all the Coins. If another choice (either on Wallet or Coin) has consumed just a single Coin, it will fail. That’s what I mean by references going stale. Basically whenever any Coin changes, you also have to change the Wallet.

The easiest way to do that is to perform all operations though Wallet itself. But now every time you call a choice on wallet that consumes a coin, you have to update the references, and thus also consume Wallet. That means all actions on Wallet now become synchronous because all choices are consuming to update the references.

What you are trying to model here is a one-to-many relation between wallet and coins. I’d model that just like in an RDB: Give the wallet a unique identifier (eg as a contract key), and reference the wallet from the coin. You can’t query all coins in the wallet from within DAML, but you can check that the coins used belong to that wallet. This makes the Wallet stable, and only causes contention if your automation tries to use the same coin twice in two transactions.

An example where I think ContractId references are fine are “offer” type contracts. For example if I were to offer you an atomic swap, I would attach my asset to the offer by Contract Id. I can make sure that I don’t touch the asset until you either accept or refuse, and since settling or negotiating are fairly synchronous processes anyway, there are unlikely to be contention problems.

5 Likes

@bernhard great idea! Really appreciate it!

As a general rule you should store references to contracts as keys rather than ids, for all the reasons @bernhard mentioned. Even the “offer” case he discusses is better handled by performing a fetchByKey on the asset contract being transferred, as this allows orthogonal administrative updates to be performed on the asset without invalidating the offer.

There is however, a more advanced DAML modelling technique called contract constellations which can sometimes require storing a contractId. In simple designs each “entity” in your business model corresponds 1:1 to a “contract” in your DAML mode. You can implement an enormous number of sophisticated business workflows without ever violating this 1:1 relationship between entity and contract. Things are complicated by the fact that in DAML contracts don’t just represent the bundle of delegated rights that is the intensional modelling of an “entity”, they also:

  • Represent the minimum granularity of disclosure in the DAML privacy model
  • Are limited to offering a single primary key for use with lookupByKey
  • Has a contractId that references a concrete value, not the more nebulous concept of “business entity”, which can lead to a need to split a contract to avoid excessive contention

For any or all of these reasons it can be necessary to change the 1:1 entity:contract relationship to a 1:N, so that each entity is represented by a constellation of contracts. Because they represent the same entity, these contracts have tightly couple lifecycles.

In some of these cases — in particular when dealing with anti-contention — the subsidiary contract will contain the contract-id of the core contract, so it can validate it remains a valid subsidiary, and also presumably to occasionally propagate updates back to the core. Similarly in the case where you are implementing an optional-key for a contract without a primary-key, you won’t have a choice but to use a contract-id.

Still, as mentioned, all these are relatively advanced techniques, and the vast majority of problems can be solved without them — in general if you see a contract-id being stored in another contract it should give you pause.

3 Likes

@Andrae Thanks. Do you know if there is a sample around for this modeling technique?

Unfortunately, all the examples I am aware of are proprietary.

Constellation contracts are one of a number of intermediate to advanced modelling techniques that includes intensional domain modelling, implicative choices, ephemeral post-condition contracts, stable-data/dynamic-workflow separation. To be honest most of this stuff is still communicated word-of-mouth by those engineers lucky enough to have had the chance to work on DAML projects of sufficient complexity to require these techniques. At some point we will need to prepare more formal training around these, but that work hasn’t been done yet.

To be fair, the vast majority of DAML projects do not require these techniques; almost by definition, the sort of explicatory problems used for sample code are not complex enough to require them. In the meantime, if you run into a problem that needs these techniques, this forum is a good place to ask about the problem — they are much easier to explain in the context of a concrete problem then in the abstract (ie. I discuss intensional modelling here: Indicate list of OR signatories)