How do I refer to both a contract and its ID without duplicating code?

I have a contract for a Habit, which I want to use to track how often I exercise. It looks something like this:

template Habit
  with
    owner : Party
    name : Text
  where
    signatory owner

    controller owner can
      nonconsuming Habit_Record : ContractId Recording
        with
          dateCompleted : Date
        do
          validateRecording dateCompleted
          create Recording with
            habit = this
            dateCompleted

Next, I need to create a recording for each day in which I exercise:

template Recording
  with
    habit : Habit
    dateCompleted : Date
  where
    signatory habit.owner

However, I could easily have two habits with the same name, or I could archive one and create a new one with the same name. It’s important to me to keep a strong reference from Recording to Habit. I can do this by storing the contract ID, but I still need to store the contract data for the signatory.

template Recording
  with
    habitId : ContractId Habit
    habit : Habit
    dateCompleted : Date
  where
    signatory habit.owner

I have 3 issues with this:

  1. This feels to me like duplication. I wish I could just store one.
  2. There’s no way to validate (in an ensure clause or similar) that the contract denoted by the contract ID contains the same data as my habit field. I need to do it in the choice constructing it. This brings me to #3.
  3. I don’t see a way to get the contract ID of this in a choice. Perhaps I’m missing something. Is there any way to acquire this information, or does it need to be passed in? If I need to pass it in, why? It feels like the sort of thing the participant could figure out at this stage (similar to getTime).

So my question is: how do I store the contract ID of one contract in another without having to resort to duplication and extra validation?

Let me start with the easy one: The Contract ID of this is self.

As for 1. and 2., there is currently no way not to duplicate owner. The relation between Habit and Recording feels relational anyway, in which case I’d use contract keys:

template Habit
  with
    owner : Party
    name : Text
  where
    signatory owner

    key this : Habit
    maintainer key.owner

    controller owner can
      nonconsuming Habit_Record : ContractId Recording
        with
          dateCompleted : Date
        do
          validateRecording dateCompleted
          create Recording with
            habit = this
            dateCompleted

template Recording
  with
    habit : Habit
    dateCompleted : Date
  where
    signatory habit.owner

Whenever you need to get hold of the ContractId of your habit, just use lookupByKey. Whenever you need to exercise, use exerciseByKey.

1 Like

self. Of course. My brain knew that, but refused to yield the information until you wrote it down.

Keys seem like an elegant approach to this problem that mean I don’t have to think about IDs at all, which is quite nice. If the object is unique, I’m happy. I’ll give it a try, thanks!

1 Like