How to do on ledger maker checker?

In a maker checker setup, does it make sense to have a Party that doesn’t have a User post initialisation, and only acts indirectly via a role template that guards it with a maker checker workflow ?

Using the simple Asset in the skeleton project as an example , some motivations here I have are:

  1. I don’t want to modify the Asset template’s Give choice, because Charlie may not care about institution 's internal maker checker process.
  2. Similarly they may not want to see Alice & Bob as signatories on Asset. If Institution changes its approval process to 2 of Alice, Bob, David, we probably don’t want to upgrade Asset as a result of that ?
  3. Reuse potentially complex maker-checker logic in one place, and not place it into every maker checker guarded choices
  4. Preserve the audit trail on Alice & Bob’s actions on ledger, vs elsewhere in the system

Code below:

module Main where

import Daml.Script

type AssetId = ContractId Asset

-- Unmodified Asset
template Asset
  with
    issuer : Party
    owner  : Party
    name   : Text
  where
    ensure name /= ""
    signatory issuer
    observer owner
    choice Give : AssetId
      with
        newOwner : Party
      controller owner
      do create this with
           owner = newOwner

template GiveProposed
  with
    actor : Party
    give: Give
    assetId: AssetId
    maker: Party
    checker: Party
  where
    signatory maker, actor
    observer checker

    choice Approve: AssetId
      controller checker
      do exercise assetId give

template MakerChecker
  with
    actor: Party
    maker: Party
    checker: Party
  where
    signatory actor
    observer maker, checker

    nonconsuming choice Give': ContractId GiveProposed
      with
        give: Give
        assetId: AssetId
      controller maker
        do create GiveProposed with
             give
             actor
             maker
             checker
             assetId

Daml Script:

run : Script ()
run = script do
-- user_setup_begin
  institution <- allocatePartyWithHint "Institution" (PartyIdHint "Institution")
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
  charlie <- allocatePartyWithHint "Charlie" (PartyIdHint "Charlie")
  aliceId <- validateUserId "alice"
  bobId <- validateUserId "bob"
  institutionId <- validateUserId "institution"
  charlieId <- validateUserId "bob"
  createUser (User aliceId (Some alice)) [CanActAs alice, CanReadAs institution]
  createUser (User bobId (Some bob)) [CanActAs bob, CanReadAs institution]
  createUser (User institutionId (Some institution)) [CanActAs institution]
-- user_setup_end
  aliceTV <- submitUser institutionId do
    createCmd Asset with
      issuer = institution
      owner = institution
      name = "TV"

  mcId <- submitUser institutionId do
    createCmd MakerChecker with
        actor = institution
        maker = alice
        checker = bob

  deleteUser institutionId
  --No one can act as the institution Party directly from here onwards

  --Alice is maker, Bob is Checker
  pId <- submit alice do
      exerciseCmd mcId Give' with
        give = Give with
          newOwner = charlie
        assetId = aliceTV
  submitUser bobId do
      exerciseCmd pId Approve

  pure ()

Yes, it’s not an uncommon pattern to have an “operator” party that’s only actively involved during application bootstrapping and upgrades, and during normal operations only signs through delegations.

Thank you !