Linking asset and account

I have a rookie question about modeling assets and accounts in Daml. I’m looking to create a primitive generic model that represents an asset with issuer and owner as signatories, and I’m looking to implement workflows with this asset, which require delegation of issuer’s authority to the owner and vice versa. An example of such workflow is an airdrop, where an issuer mints the asset to the owner without requiring explicit owner’s consent for each airdrop transaction.

I understand that delegation of authority in Daml is achieved by creating a role template with the party delegating the authority as a signatory and the party receiving delegated authority as a controller on the choices, which carry out the actions that require delegated authority. E.g. I can implement a role template named AssetHoldingAccount with issuer and owner as signatories, and with issuer as a controller on the choice named Airdrop, which creates the asset with issuer and owner. With this construct, once the AssetHoldingAccount contract has been created following propose/accept pattern, the issuer party can unilaterally exercise the Airdrop choice and create Asset contract with AssetHoldingAccount’s owner as a signatory. Owner’s consent is implied by way of the owner being a signatory on the AssetHoldingAccount contract.

Now I need to link the Asset and the AssetHoldingAccount templates. One way to do this is to have a common key for the two templates. Another way I can think of is to have the data from one contract to be part of the other. E.g. instead of using the type Party for the owner field in the Asset template, use the AssetHoldingAccount. This construct can have an intuitive interpretation as “the asset can only be created and held in an account, it cannot exist outside of an account”. It also allows to access the fields within AssetHoldingAccount contract from the Asset template to ensure for example that both have the same party owner.

I was advised against using this construct by @Leonid_Rozenberg on the grounds that with this construct it’s possible that the Asset contract may contain the data from the AssetHoldingAccount contract that does not exist on the ledger. I’m afraid I’m not quite following why this is necessarily a problem, and hence why this design is not recommended. I think the answer to this question may be of interest to more than me, hence I’m posting it on the forum in hope that someone can explain to me what is fundamentally wrong with the code snippet below.

template AssetHoldingAccount
  with
    issuer : Party
    symbol : Text
    owner : Party
  where
    signatory issuer, owner
    key (issuer, symbol, owner) : (Party, Text, Party)
    maintainer key._1
  nonconsuming choice Airdrop
      : ContractId Asset
      with
        quantity : Decimal
      controller issuer
      do
        create Asset with
          issuer
          owner
          symbol
          quantity

template Asset
  with
    issuer : Party
    symbol : Text
    owner : AssetHoldingAccount
    quantity : Decimal
  where
    signatory issuer, owner.owner
    ensure quantity > 0.0
    choice Transfer
      : ContractId AssetTransfer
      with
        newOwner : AssetHoldingAccount
      controller owner.owner
      do
        create AssetTransfer with
          asset = this
          newOwner

template AssetTransfer
  with
    asset : Asset
    newOwner : AssetHoldingAccount
  where
    signatory (signatory asset)
    observer (observer asset), newOwner.owner
    let
      targetAsset = asset with
        owner = newOwner
    ensure (ensure asset) && (ensure targetAsset)
    choice Transfer_Accept
      : ContractId Asset
      controller asset.issuer, newOwner.owner
      do
        create targetAsset
    choice Transfer_Cancel
      : ContractId Asset
      controller asset.owner.owner
      do
        create asset
    choice Transfer_Reject
      : ContractId Asset
      controller newOwner.owner
      do
        create asset
2 Likes

The potential issue with that type of setup is that nothing forces me to create an AssetTransfer by calling the Transfer choice on the Asset. I can just create the AssetTransfer directly using an Asset that never existed.

Now in your example that’s not really an issue. The signatories of AssetTransfer are the signatories of the oiginal Asset. So if they can create arbitrary assets anyway and call Transfer on them so this doesn’t really allow doing anything they couldn’t otherwise do.

But in other settings where the authorization is setup a bit differently, this can be an issue. The crucial point here is that having a value of type Asset does not mean that the signatories have agreed to creating it which can lead to misleading assumptions. One option is that Transfer does not directly consume the contract and AssetTransfer gets a reference to a contract id of the original asset. Then when accepting the transfer you can fetch the asset and check that it’s an actual asset and potentially perform more validation.

3 Likes

@cocreature ,
Thank you very much for your response. I’m afraid it doesn’t quite answer my question, which is about modeling asset and account relationship in Daml.

I’m trying to understand why using the AssetHoldingAccount type (as opposed to the Party type) as the value of owner field in Asset template is not recommended, and why the preferred pattern of establishing relationship between asset and account is through common key.

The potential issue with that type of setup is that nothing forces me to create an AssetTransfer by calling the Transfer choice on the Asset . I can just create the AssetTransfer directly using an Asset that never existed.

I appreciate this issue you pointed out. However, it applies whether the owner of the Asset contract is a Party or an AssetAccountHolder. The potential problem you pointed out is in the relationship between Asset and AssetTransfer, not between Asset and AssetHoldingAccount. Projecting the same problem to the relationship between Asset and AssetHoldingAccount, it is indeed very possible with my code snippet for an Asset contract to exist on the ledger with owner = AssetHoldingAccount and for that AssetHoldingAccount contract to not exist on the ledger.

One scenario where this is possible is if AssetHoldingAccount was created, then an Asset with this AssetHoldingAccount was created and then AssetHoldingAccount contract was archived. What I’m not following here is why should this be a problem? Or how having a common key between Asset and AssetHoldingAccount is better? With Asset and AssetHoldingAccount linked by common key it’s also quite possible for Asset to exist on the ledger and for the corresponding AssetHoldingAccount to not exist.
Perhaps the first question to answer should be whether the pattern of modeling asset/account relationship by using AssetHoldingAccount contract data in the Asset template, as in my code snippet, is indeed not recommended. Or is it?

I appreciate it may be quite hard to follow someone’s logic and intentions on a forum post, and I hope I’m making sense here. Please, let me know otherwise.

As I mentioned in my first post, there isn’t a fundamental issue with having the reference to the value instead of via a key or a contract id. If you are aware and willing to accept that the contract does not exists, this is perfectly fine and there are definitely cases where you want such a reference (your transfer is a reasonable example because the contract is already archived in the first of the transfer).

You are right that for keys and contract ids, the contract can also not exist. However, there I can (and should) validate this in a choice. Let’s say my transfer proposal has a reference via contract id. When accepting that, I can then fetch that contract, check that it’s active and perform other validation before archiving it. Keys are similar but they have the additional benefit that they are more loosely coupled meaning the referred contract can change while keeping the key without changing the referencing contract. That lose coupling is not always what you want but if you do, contract keys make it easier.

3 Likes