Why is `fetchByKey` not returning `Optional (ContractId T, T)`

Assuming that I want to fetch the data of a contract by key while

  • I don’t know if the contract exists
  • if it doesn’t I would want to provide a neat error message
    I could do the following
optContractId <- lookupByKey @MyTemplate key
assertMsg "Contract of MyTemplate not found" (isSome optContractId)
(contractId, contractData) <- fetchByKey @MyTemplate key

of course, the last fetchByKey can be replaced with a fetch since we already have the right ContractId. But is there a neater way of doing this? It feels odd to have to run two ‘round trips’ to the ledger.

Why isn’t there a version of fetchByKey that returns Optional (ContractId T, T)?

2 Likes

One of the fundamental differences between fetchByKey and lookupByKey is that the latter can make non-existence assertions, the former cannot. Accordingly they have different authorization and validation rules. Thus a variant that returns an Optional (ContractId T, T) would really be a variant of lookupByKey.

The answer why where is no such variant boils down to complexity of the DAML Ledger model, but I don’t think we got the primitives perfectly right. When we introduced fetchByKey, we didn’t need a new primitive. FetchByKey gets resolved to a simple Fetch node on ledger. lookupByKey does need a new node type to record a non-existence of a key. Recording only the pair (key, cid) preserves more privacy than recording (key, cid, args) and privacy is important to us. But in actual fact it would be sufficient to record (key, bool) since once you know that a key exists, you can do a fetchByKey. I therefore think the primitive should really have been visibleByKey. You can implement lookupByKey and the variant you are suggesting from that and fetchByKey.

2 Likes