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:
- Does it have some semantic meaning or is it just a unique string?
- Who sets it initially when the contract is first created and how do they chose it?
- 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
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
with
issuer : Party
owner : Party
id : Text
quantity : Int
where
ensure quantity > 0
signatory issuer, owner
key (issuer, owner, id) : (Party, Party, Text)
maintainer key._2
choice Split : (ContractId Asset, ContractId TransferProposal)
with
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
newOwner
pure (asset, transferred)
template TransferProposal
with
asset : Asset
newOwner : Party
where
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