Split with keys?

Say I have a fungible contract (Asset) with a key (Id) maintained by the owner (Party). This Asset has a quantity (int).

When I want to implement the typical split operation there will be a problem because the ‘current’ Asset already exists with the same key and owner as the resulting split would create (well it would create two contracts).

The reason to use the key here is to be able to fetch by key.

Am I approaching this in the wrong way?

1 Like

Hi @perbergman,
You definitely cannot create two contracts with the same key in the split an you already noticed.
A way out of this is to change the ID for at least one of them which clearly sidesteps the issue but of course it raises the question of which one needs to change and to what To figure out what you need to change it to, i think it’s helpful to ask what the meaning of the ID here is:

  1. Does it have some semantic meaning or is it just a unique string?
  2. Who sets it initially when the contract is first created and how do they chose it?
  3. Assuming you could create two contracts with the same key in the split, which of the two do you expect to be returned by a following fetchByKey?
1 Like
  1. Yes, coming from an external system where it is a unique id.
  2. Key is based on the external id.
  3. The owner would change, so no problem there.


  1. Contract1 created by the issuer and owner Alice, the key is key1, quantity is 1000. Should the maintainer be the owner?
  2. Split by 100, new owner Bob: Contract2 with issuer/owner/quantity/key Alice/Alice/900/key1, Contract3 with Alice/Bob/100/key1.
  3. So fetchByKey Alice/key1 or Bob/key1.
1 Like

Usually you want both the issuer and the owner to be signatories on your asset (otherwise the issuer can archive your asset without you agreeing to it). That does make the split & transfer (it’s really both merged in one) a bit tricky since you don’t have authorization from the new owner.

As usual there are different ways to get the authorization but a relatively straightforward one is to create a TransferProposal that the new owner can then accept. Note that rejection of this proposal looks a bit more complex than usual since the choice has to merge it back into the other asset to avoid there being two contracts with the same key.

module Main where

import Daml.Script

template Asset
    issuer : Party
    owner : Party
    id : Text
    quantity : Int
    ensure quantity > 0
    signatory issuer, owner
    key (issuer, owner, id) : (Party, Party, Text)
    maintainer key._2
    choice Split : (ContractId Asset, ContractId TransferProposal)
        newOwner : Party
        transferred : Int
      controller owner
      do assert (transferred > 0)
         asset <- create this with 
           quantity = quantity - transferred
         transferred <- create TransferProposal with
           asset = this with
             quantity = transferred
         pure (asset, transferred)

template TransferProposal
    asset : Asset
    newOwner : Party
    signatory (signatory asset)
    observer newOwner
    choice Accept : ContractId Asset
      controller newOwner
      do create asset with owner = newOwner
    choice Reject : ContractId Asset
      controller owner asset
      do optCid <- lookupByKey @Asset (key asset)
         case optCid of
           None -> create asset
           Some cid -> do
             -- We need to merge with the existing asset with the
             -- same key.
             activeAsset <- fetch cid
             archive cid
             create asset with
               quantity = asset.quantity + activeAsset.quantity

text = do
  bank <- allocateParty "Bank"
  alice <- allocateParty "Alice"
  bob <- allocateParty "Bob"
  asset <- submitMulti [bank, alice] [] $ createCmd Asset with
    issuer = bank
    owner = alice
    id = "key1"
    quantity = 1000
  (_, proposal) <- submit alice $ exerciseCmd asset Split with
    newOwner = bob
    transferred = 100
  submit bob $ exerciseCmd proposal Accept
  pure ()
1 Like


I will ingest this…