Contract keys in DAML 3

G-d willing

Hello,
Can someone please provide some information about the use of contract keys in DAML 3 as it seems to be not supported.
And whatever the reason is, what is the alternative solution for it

1 Like

Contract keys are indeed not supported in Daml 3 for the time being. The intent is to bring contract keys back in a future Daml release, however without the uniquness guarantee, which is fundamentally incompatible with multi-domain architecture of the Canton Network.
The contract keys served two main functions in Daml 2.

  1. Provide uniqueness guarantee on-ledger.
    As I mentioned, this function is fundamentally incompatible with multi-domain architecture of the Canton Network. The solution is to amend the business logic to either not rely on the uniquness of a keyed contract or to use off-ledger techniques to prevent the creation of a duplicate contract. E.g. use Daml authorization model to ensure that only a dedicated party can create a certain contract, and then use ledger automation to ensure that, before this party submits a command to create a contract, the ledger automation queries the ledger to check whether a duplicate contract already exists.
  2. Provide a stable identifier to an entity represented by the contract.
    In this case the alternative is to use ContractId as such identifier, and make it stable by separating on-ledger data with high churn into a different template, so that the contract that represents a stable entity wouldn’t need to be frequently mutated (archived and re-created with updated payload).
1 Like

Thanks @a_putkov for your answer.
First, do you have any estimate when the contract keys will be available again?
Also, can you advise me on the best practice for fetching a specific contract of type template A? There are 20,000 contracts on the ledger, all of which are visible to the same party (e.g., the administrator)?
In this case, in addition to my question, how can I, from within a choice, fetch a contract without knowing its contract Id, or key? The only functions that are available are fetch, fetchByKey, lookupByKey, exercise and exerciseByKey.
If there were functions like query and queryFilter which is only available for Script that could help a bit (But I am not saying that this a proper solution),

Thanks,

1 Like

I’m afraid I don’t have an ETA for the availability of contract keys in Daml 3.

For retrieving contracts from the ledger into an off-ledger service, the main recommendation is to use PQS, which provides rich indexing and querying capabilities. A contract key is composed from the fields in a template. A relatively straightforward replacement for querying the ledger by contract key is to query the PQS using the values of the fields that comprise the contract key.

For fetching a contract on-ledger, i.e. in the body of a Daml choice, the only options are fetch by ContractId or by key. And in lieu of contract keys, in Daml 3 the only option at the moment is fetch by ContractId. A ContractId could be retrieved from the PQS by the ledger automation service that submits the command to exercise the choice, and passed as a parameter to the choice.

1 Like

Well, since DAML 3 will be introduced in the Canton Network, and since the use of super validators is to allow one single transaction across several nodes, the use of PQS is not relevant in here.
And even for more simpler scenarios, say in my choice I want to validate the existance of a generic contract, how can I do that?
How can my application even fetch a configuration contract, which has only 1 instance of it on the ledger?
Off-ledger solutions are not relevant or sufficient in so many on-ledger cases.
There must be a solution for this, otherwise, on-ledger applications will be extremely limited and degenerated.

1 Like

I’m afraid I’m not quite following your arguments. They seem misinformed, the way I read them. Am I missing something?

since DAML 3 will be introduced in the Canton Network

Daml 3 is already used on the Canton Network. The Global Synchronizer Mainnet has been live for almost a year. There’s a number of production applications that synchronize transactions on the Global Synchronizer, which use Daml 3. More apps are constantly being developed and launched.

the use of PQS is not relevant in here

The use of PQS is just as relevant for Daml apps that synchronize transactions on the Global Synchronizer, as it is for apps synchronizing transactions on a private synchronizer.

I want to validate the existance of a generic contract, how can I do that?

The recommended way is to query the PQS, find the ContractId and then fetch the contract.

How can my application even fetch a configuration contract, which has only 1 instance of it on the ledger?

I assume the question is about fetching the contract in the body of a Daml choice, right? In Daml 3 the only way is to use the fetch function. This implies you need to pass the ContractId for the contract you want to fetch as an argument to the choice. This is not that different from how you fetch the contract by contract key. In Daml 2, when you fetch a contract by key, you pass the value of the key to the choice, right? So, when you submit a command to the ledger that exercises the choice, you use the value of the contract key in the command. In Daml 3 you need to use the ContractId instead. To find the ContractId that you need to use in the command exercising the choice, you can use the PQS.

Off-ledger solutions are not relevant or sufficient in so many on-ledger cases.

Let’s explore this. Would you mind providing an example of a use case that you had in mind?

1 Like

Hi @a_putkov
Thanks for a detailed answer.
I mistakenly wrote "since DAML 3 will be introduced in the Canton Network", I know it is already launched.
Anyway, I am sure you understand that when apps can fetch a contract by a key, which in other words, does not need to know the exact contract ID, apps can work independently without the need for any off-ledger application (for example, PQS).
For example, let’s have a rent agreement, and I would like to have a choice that allows the landlord to charge the tenant. If I have the account id of the tenant stored in the agreement, the choice won’t need any payload in order to work properly. But, now, that fetching by key is not allowed, the application needs to be expanded to use the PQS in order to get the contract id of the tenant’s account in order to complete the transaction.
Furthermore, it is possible that by the time I get the contract id from the PQS till my off ledger application exercises the payment choice, that contract id was already consumed by other operation which will fail the payment process.
In addition, I will need to add an API layer for getting information from PQS which adds complexity to the application, which could be saved by having the possibility to fetch by key.
I hope I explained you well my point, and thank you very much for taking the time and answering me.

1 Like

I most certainly appreciate that in Daml 2 contract keys are very convenient for certain use cases, e.g. to link two contracts in the property rental application you described. However, I would argue that the value of this convenience goes away almost entirely, if the uniqueness of a keyed contract cannot be guaranteed. Using your example, if the tenant’s account cannot be guaranteed to be unique, then fetching the account by key is no longer convenient, since you cannot be sure that you’ll fetch the right account. Since, as I mentioned, the uniqueness of a keyed contract is fundamentally incompatible with multi-domain architecture, giving up the uniqueness guarantee, and hence the convenience of contract keys, is necessary to realize the benefits of the Canton Network.

I would also argue that the additional development work that needs to be done to work around the lack of contract keys is not as big as it may initially seem. In your Daml 2 example, while you can get away with the choice in the rental contract template not requiring any arguments, to exercise the choice you still need to submit a ledger command that includes either a ContractId or a contract key for the rental agreement contract. These values need to come from somewhere. You need to query either the ledger or some off-ledger store for them, which is not unlike querying the PQS. And if you already have to query the PQS for the ContractId of a rental contract, querying it also for the ContractId of the account contract and passing the value as an argument to the choice doesn’t seem like that big a change.

Finally, you’re saying that a contract may get consumed between the time you query the PQS and the time the transaction gets evaluated on he ledger. This is true, but this risk is always there, whether you query the PQS before submitting a transaction or not. So, I don’t quite see any major change here between Daml 2 and Daml 3. Even with Daml 2 we recommend implementing an API layer to submit transactions to the ledger. One of the purposes of this API layer is to implement the retry behavior for transactions that fail because a contract used in the submitted transaction has been consumed in a previous transaction.

1 Like

So currently, there is no solution and no workaround for keeping all interactions on-ledger.
I hope that such a solution will come up soon, as in my opinion, it is a fundamental necessity to properly design and code smart contract applications in DAML.
Currently, if every DAML application remains dependent on a regular database (such as PQS or any other off-chain tool), I see that as defeating the purpose of working safely on the blockchain.

Regardless, @a_putkov, thank you for taking the time to answer my question.

I have to disagree with your conclusions. I think they’re rather misleading.

So currently, there is no solution and no workaround for keeping all interactions on-ledger. I hope that such a solution will come up soon, as in my opinion, it is a fundamental necessity to properly design and code smart contract applications in DAML.

Just like a database application cannnot exist without a database client reading and writing to the database, a smart contract application, where all interactions are kept on-ledger cannot exist either. A ledger is effectively a distributed state machine. Any state transition has to be initiated by a ledger client submitting transactions to the ledger. So, you always need an off-ledger component to interact with the ledger (at the very least to submit transactions). In all practical applications the ledger client also needs to read from the ledger.

Currently, if every DAML application remains dependent on a regular database (such as PQS or any other off-chain tool), I see that as defeating the purpose of working safely on the blockchain.

The use of PQS is not required. It is strongly recommended for scalability. PQS is an operational data store. If your active contract set and the volume of transactions are low, you can interrogate the PCS using the Ledger API without PQS. However, even with a small ledger and low transaction volume the use of PQS is preferred, because it provides scale without any downside. Whether you use PQS for ledger reads or not, you still have the exact same security, integrity, authorization and privacy guarantees that Canton provides.

You’re not entirely correct in saying that we always needed to use an external application for contract referencing. Let me give two very practical DAML 2 scenarios where external applications were not required, but in DAML 3 they now are.

1. Referencing a Contract by Key
As I mentioned in an earlier response, imagine a RentAgreement contract between a landlord and a tenant.
The tenant wants to exercise a MakePayment choice, which must validate some environment settings (local currency, weekend days, etc.), which are stored in a separate Configuration contract.

In DAML 2: We could store the Configuration contract key details in the RentAgreement, and then fetch it by key inside the MakePayment choice. No external application was needed to provide that information. And yes, I understand that an external application had to do the actual “exercise”, but it could do that without knowing any specific on-ledger information in order to exercise it.

In DAML 3: Since we cannot fetch by key anymore, we now need the external application to additionally pass the contract ID of the Configuration contract, which must be retrieved by ANOTHER external application, which I am responsible for (and not the customer). And this adds complexity and pushes more logic outside of DAML.

2. Contracts are not guaranteed to be active by the time I need them.
In DAML 3: In addition to storing and passing the contract ID externally. If that contract is archived by the time of exercising the choice, the transaction will fail. And making another attempt does not guarantee success, we need to repeat the process until it succeeds.

This means that contract referencing logic must be handled externally, again requiring API layers, additional storage, security mechanisms, and more moving parts.

Why This Matters
In DAML 2, many use cases could be handled entirely on-ledger, with no external dependencies.
In DAML 3, the inability to fetch by key forces developers to build and manage:

  • PQS or equivalent systems to track active contract IDs
  • API layers to query those IDs
  • Security and authentication for those APIs
  • Additional logic that used to reside cleanly within DAML

So no, we did not always need an external application. That was a significant benefit of DAML 2’s model, which has now been lost.