Generate business id using daml function

Hi everyone,

It was required me to generate custom business ids for each new contract of a given Template.

Imagine the following template:

template Policy with
    business_id : Text
    data : Text
    owner : Party
    system : Party

    where
        signatory owner
        observer system
        key (system, business_id) : (Party, Text)
        maintainer key._1

    controller system can 
        Policy_Gen_Business_Id : ContractId Policy
            do
                create this with business_id = genBusinessId

What I have imagined to solve this problem, would be to have a choice for a system party, where it would execute the function genBusinessId.
This function would have the algorithm to generate an unique business_id.

Since it should be generated once upon a contract creation, is there a better approach for this problem ?

Note: It is also required to not use an external algorithm to generate this business id, the algorithm needs to be “inside” daml code

There are a few options depending on your usecase:

  1. The easiest way to get unique identifiers in Daml is to rely on contract ids instead of trying to create separate identifiers. The main downside here is that you cannot use contract ids in keys and that you cannot convert it to a Text in Daml.
  2. If that’s not an option, my recommendation would usually be to generate something like a UUID outside of Daml. You mentioned that this isn’t an option but maybe you could expand on why that’s the case? Maybe we can find a solution that allows you to go down that route.
  3. If neither of those two options work for your usecase then you can try generating it in Daml. The simplest example of doing something like that is to have a contract with an incrementing counter for your ids. Keep in mind though that first, you rely on the signatories of T to go through IdCounter instead of creating T directly. So if you don’t trust them you cannot rely on the ids being unique and second, you are creating a sequential bottleneck because everything goes through IdCounter now. If that becomes an issue you could try sharding across multiple identifier generators and have each add a prefix before their counter.
module Main where

import Daml.Script
import DA.Assert ((===))

template IdCounter
  with
    current : Int
    p : Party
  where
    signatory p
    key p : Party
    maintainer key

    choice CreateT : ContractId T
      controller p
      do cid <- create (T current p)
         create this with current = current + 1
         pure cid

template T
  with
    id : Int
    p : Party
  where
    signatory p

test = do
  p <- allocateParty "p"
  submit p $ createCmd (IdCounter 0 p)
  submit p $ exerciseByKeyCmd @IdCounter p CreateT
  submit p $ exerciseByKeyCmd @IdCounter p CreateT
  submit p $ exerciseByKeyCmd @IdCounter p CreateT
  ts <- query @T p
  map snd ts === [T 0 p, T 1 p, T 2 p]
  pure ()
1 Like

Thank you for your response. It was very helpful