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
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.
@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
AssetTransferby calling theTransferchoice on theAsset. I can just create theAssetTransferdirectly using anAssetthat 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.