Documentation Index
Fetch the complete documentation index at: https://cantonfoundation-mintlify-validate-diagnosis.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Reference: Contract Keys
Contract keys are an optional addition to templates. They let you specify a way of identifying contracts using the parameters to the template — similar to a key in a database. Contract keys do not change and can be used to refer to a contract even when the contract ID changes. As a contract is updated via archive and create operations, the currently active contract(s) can easily be referenced via the contract key.In Canton 3.x, it is possible for multiple active contracts of the same template to share the same key.The general usage is that key uniqueness is guaranteed outside of the Daml engine — for example, in the Daml business logic or at the backend client level (e.g., a unique account number or invoice number). Because of this, the primary key-based API (
lookupByKey, fetchByKey, exerciseByKey) are optimized for the common case where there is at most a key references one contract.What Can Be a Contract Key
The key can be an arbitrary serializable expression that does not contain contract IDs. However, it must include every party that you want to use as amaintainer (see Specify Maintainers below).
It’s best to use simple types for your keys like Text or Int, rather than a list or more complex type.
Specify Maintainers
If you specify a contract key for a template, you must also specify amaintainer or maintainers, in a similar way to specifying signatories or observers. The maintainers “own” the key in the same way the signatories “own” a contract. Just like signatories of contracts prevent double spends or use of false contract data, maintainers of keys ensure consistent key lookups. Since the key is part of the contract, the maintainers must be signatories of the contract. However, maintainers are computed from the key instead of the template arguments. In the example above, the bank is ultimately the maintainer of the key.
Since multiple templates may use the same key type, some key-related functions must be annotated using the @ContractType as shown in the examples below.
When you are writing Daml models, the maintainers matter since they affect authorization — much like signatories and observers. You don’t need to do anything to “maintain” the keys. Validators hosting the maintainers of a key involved in a transaction verify that contracts are retrieved in a consistent order for that key within the transaction.
Contract Lookups
The primary purpose of contract keys is to provide a stable, and possibly meaningful, identifier that can be used in Daml to fetch contracts. The main functions for key-based lookups arefetchByKey, lookupByKey, and exerciseByKey, all available by default.
When multiple contracts share the same key, these functions return the first contract according to the following lookup order:
- Contracts created within the current transaction, starting with the most recent.
- Explicitly disclosed contracts, in the order provided in the command.
- Contracts known to the participant, in any order. (The current implementation returns them in recency order, but this is not guaranteed and should not be relied on.)
DA.ContractKeys module provides lookupNByKey and lookupAllByKey (see Multi-Contract Key Lookups below).
Because disclosed contracts are prioritized over known contracts, you can use disclosures to ensure a specific retrieval order during command submission.
fetchByKey
(fetchedContractId, fetchedContract) <- fetchByKey @ContractType contractKey
Use fetchByKey to fetch the ID and data of the first contract with the specified key (according to the lookup order above). It is an alternative to fetch and behaves the same in most ways.
It returns a tuple of the ID and the contract object (containing all its data).
Like fetch, fetchByKey needs to be authorized by at least one stakeholder.
fetchByKey fails and aborts the transaction with a CONTRACT_KEY_NOT_FOUND error if no contract with the given key is visible to the submitting party.
lookupByKey
optionalContractId <- lookupByKey @ContractType contractKey
Use lookupByKey to check whether a contract with the specified key exists. If it does exist, lookupByKey returns Some contractId, where contractId is the ID of the first contract matching the key (according to the lookup order above); otherwise, it returns None.
lookupByKey requires authorization from all maintainers of the key. This is necessary so that confirming participants hosting the maintainers can verify that contracts are retrieved in a consistent order for the given key within the transaction.
More precisely:
lookupByKeyreturnsSome contractIdif a contract with the given key exists and the submitter is a stakeholder on that contract, and authorization from all maintainers is present.lookupByKeyreturnsNoneif no contract with the given key exists (or none is visible to the submitter), and authorization from all maintainers is present.lookupByKeyaborts the transaction if authorization from any maintainer is missing.
exerciseByKey
exerciseByKey @ContractType contractKey
Use exerciseByKey to exercise a choice on the first contract with the given key (according to the lookup order above). Just like exercise, running exerciseByKey requires visibility of the contract and authorization from the controllers of the choice.
Multi-Contract Key Lookups
For use cases where multiple contracts may share the same key, theDA.ContractKeys module provides:
lookupNByKey @ContractType n key— looks up up toncontracts with the given key, returned in the lookup order described above.lookupAllByKey @ContractType key— looks up all contracts with the given key.
import DA.ContractKeys to your module.
Performance is optimized for the common case of zero or one contract per key. The multi-contract functions (
lookupNByKey, lookupAllByKey) may be less performant when many contracts share a key.Daml Script Functions
In addition to the Daml language primitives above (which run inside transactions), there are Daml Script functions for querying keys outside of transactions:queryByKey @ContractType party key— looks up a contract by key and returns its ID and data. Runs as a top-levelScriptaction.queryNByKey @ContractType party n key— looks up up toncontracts by key and returns their IDs and data. Runs as a top-levelScriptaction.exerciseByKeyCmd @ContractType key choiceArg— exercises a choice on the first contract with the given key. This is aCommandsaction and must be used inside asubmitblock, where it can be combined with other commands.