Enriched Daml finance Batch by wrapping a daml-finance Batch contract

Hello,

We have an use case where we would like to enrich the daml-finance Batch with an aging feature. The logic is simple: if the settler tries to settle the Batch and any of the instructions have not been allocated/approved, we don’t want to fail. Instead, we want to increment an Int field age.

Our initial idea was to create a new interface AgedBatch with a extra field age in the view, and a new Settle' choice with the signature below. So in case something is wrong, a new AgedBatch with age incremented is created.

choice Settle' : Either (ContractId AgedBatch) [ContractId Holding.I]

Regarding the implementation, we basically would copy/paste from daml-finance codebase.
This would work, but we are considering a second approach.

The idea is to have a template which wraps a daml-finance Batch contract like that:

template AgedBatch
  with
    settler : Party
    age : Int
    batchCid : ContractId Batch.I
  where
    signatory settler

    choice Settle' : Either (ContractId AgedBatch) [ContractId Holding.I]
      controller settler
      do
        try
          Right <$> exercise batchCid Batch.Settle with
            actors = singleton settler
        catch
          GeneralError _msg -> Left <$> create this with counter = this.counter + 1          
          -- add more exceptions here

But the downside in the above model is that we cannot prevent the settler to settle using the wrapped batchCid instead of the choice Settle' in the enriched Batch.

Any ideas?

Thanks!
Jose Velasco

Just an idea: Don’t store a ContractId Batch.I, but a Batch - the non-interface version. Then in your try, do a create and an exercise. That way you can also make AgedBatch implement the Batch.I interface and use it interchangeably with the standard Batch.

template AgedBatch
  with
    settler : Party
    age : Int
    batch : Batch
  where
    signatory settler

    interface instance Batch.I for AgedBatch where
      view = view (toInterface @Batch.I batch)
      settle s = 
        try do
          cid <- create batch
          exercise (toInterfaceContractId @Batch.I cid) s
        catch
          GeneralError _msg -> do
            create this with age = age + 1 
            return []         
          -- add more exceptions here
      cancel c = do
        cid <- create batch
        exercise (toInterfaceContractId @Batch.I cid) c

You incur a cost of one extra create, but functionally it’ll do what you want. If you don’t want to incur that, you can copy&paste the settle and cancel implementations from default Batch.

1 Like

Thanks @bernhard
I see your point but if I create a Batch and is settled immediately it will fail because the instructions associated need to be allocated/approved in a separate process. Maybe I’m missing something…

Your AgedBatch takes place of the original Batch here. Because it also implements the Batch.I interface, you can use it interchangeably. The instructions would be created/allocated/approved in a separate process as with a normal Batch. The only difference here is that the Settle choice does something different: if settlement fails, it increments the age.

If this doesn’t make sense, I most likely misunderstood what you are trying to accomplish.

1 Like

Thanks for the clarification @bernhard
It makes totally sense.