Update of existing DAML Contract with new data elements

I am experimenting with DAML and React and experienced an error

My DAML contains the following code

template CitizenRole
  with 
    operator : Party
    citizen: Party
    citizendetails : CitizenDetails
    verifiablecredentials : VerifiableCredentials
  where
    signatory citizen
    key citizen : Party
    maintainer key
         SetVerifiableCredentials : ContractId CitizenRole
        with
          newverifiablecredentials : VerifiableCredentials
        do  
          (citizenRoleCid, oldCitizenRole) <- fetchByKey @CitizenRole (citizen)
          archive citizenRoleCid
          create CitizenRole with citizen = key oldCitizenRole; citizendetails = oldCitizenRole.citizendetails ; operator = operator ; verifiablecredentials = newverifiablecredentials
      

data VerifiableCredentials = VerifiableCredentials
  with
      connectionid : Text
      holder_did : Text
      issuer_did : Text

        deriving (Eq, Show)

a CitizenRoleContract has been previously set with all the required data elements provided

Now I want to make an update to the “verifiablecredentials” data for this contract by executing a ledger.exercise function in REACT

ledger.exercise(Main.CitizenRole.SetVerifableCredentials, curContractId, { citizen, newverifiablecredentials })

This currently gives an error code

0: "io.grpc.StatusRuntimeException: INVALID_ARGUMENT: Command interpretation error in LF-DAMLe: dependency error: couldn't find contract AbsoluteContractId(003eb283f097836e9b2d0af2d3ebe74caa51748446ce7470477d5146d66e0a8bb2). Details: N/A."

This contract is currently in the ledger

Any suggestions?
I am not sure if citizen must be part of the arguments in the ledger.exercise or not ?
It is though the key for which the following daml code is executed

(citizenRoleCid, oldCitizenRole) <- fetchByKey @CitizenRole (citizen)

Any insights on what is wrong here ?

If you want to learn how you can extend and upgrade DAML models take a look at https://docs.daml.com/upgrade/index.html.

Did you modify your model?

There are a few potential reasons for this error:

  1. The contract simply does not exist. Either it has been archived or it has never been created. How did you obtain curContractId?
  2. The contract is not visible to you. Your contract only has citizen as the signatory and no observer afaik so only citizen can exercise a choice on the contract.
  3. A mismatch between the template id, i.e., your contract id is for a different template. This could happen if you actually did modify your template and recompiled. In that case I recommend going through the documentation I linked above. If you make any change to your template, you get a different template with a different package id. If you want to upgrade an existing contract to the new template you need to do so explicitly by archiving the old one and creating a new one with the same data (+ any extra fields or changes you added).
1 Like

If we take a look a the choice you are exercising:

The first line attempts to perform a fetch-by-key using the key of the current contract, in otherwords the contract is trying to fetch itself. The second line then tries to archive the current contract retrieved by the first line.

  1. You are in a consuming choice. The current contract is archived before the choice runs. This is why the transaction will fail as you are trying to fetch an archived contract.
  2. Even if you were in a post-consuming or non-consuming choice, you never need to do a fetch like this. The contents of the current contract (ie. oldCitizenRole) is always available as this; and, the contract-id of the current contract is always available as self. So the fetch was redundant.
  3. In consuming choices (both std, and post) the contract is automatically archived if the transaction commits. So the explicit call to archive is redundant, and will result in a similar error to the fetch.

Finally, when creating new records using the with syntax, you have access to punning, so anywhere you would write (var = var) you can just drop the assignment. It makes things much less cluttered that way, especially as the fields of a contract are in scope within that contract’s choices, so you don’t even need this to refer to them, you only need it if referring to the contract record in its entirety.

I mention this last bit as with can also be used as a copy-constructor when creating records of the same type with updated contents. As this is what you are trying to do here, what you wanted is much simpler when you take advantage of these feature:

Using punning:

      SetVerifiableCredentials : ContractId CitizenRole
        with
          newverifiablecredentials : VerifiableCredentials
        do  
          create CitizenRole with citizen; citizendetails; operator; verifiablecredentials = newverifiablecredentials

Even simpler using copy-constructor:

      SetVerifiableCredentials : ContractId CitizenRole
        with
          newverifiablecredentials : VerifiableCredentials
        do  
          create this with verifiablecredentials = newverifiablecredentials
3 Likes

Good catch @Andrae!

1 Like

That is a lot easier indeed. Will test it out

2 Likes