Canonical way to find transfers for an account in Daml Finance

In the ERC-20 standard, there is the Transfer event type which is emitted on every transfer of the token. Is there any close equivalent in Daml Finance? What type of events should I look for in the ledger transaction history to see what Holdings have gone in/out of my account, and what accounts they have come from, or have gone to? I was thinking to look for exercises of the Transfer choice on the Transferrable interface. However, it may be that in some cases the Holding is freely transferrable to other accounts under the same custodian. Someone can just transfer their Holding to me, and I won’t witness the exercise of Transfer on their Holding. I only see the create event of the Holding. Would I be better to make all transfers happen via a settlement Instruction, then I can see the creation of the Instruction and the exercise events on it?

1 Like

Hi @huw,

Your point is well-taken. In scenarios where the incoming controllers of the receiving account are empty, the newOwner would solely witness the credit/creation of the new holding. However, they would not be privy to the corresponding holding being debited/archived. To rectify this situation, we could incorporate the newOwner as choice observers in the following manner:

  nonconsuming choice Transfer : ContractId Transferable
    -- ^ Transfer a contract to a new owner.
    with
      actors : Parties
        -- ^ Parties authorizing the transfer.
      newOwnerAccount : AccountKey
        -- ^ Account contract id of the parties to transfer the contract to.
    observer newOwnerAccount.owner
    controller actors, getLockers this
    do
      transfer this self arg

I have added a github issue 837 where we can track this.

Please also find the following related forum question:

I hope that helps.

Johan

1 Like

Thanks @Johan_Sjodin . If you add newOwnerAccount.owner as observer, I’m still not sure if we can know exactly who the owner was of the Holding that was transferred. I can see who the actors are, but how do I know which one of them is the owner?

Hello @huw,

When newOwnerAccount.owner is an observer of the choice, it gains visibility over the entire transaction tree that stems from it. This includes being disclosed the execution of the Debit choice and the subsequent archival of the holding.

I validated this by incorporating newOwnerAccount.owner as a choice observer, and then running the test 4 script, provided here:

The table view, after execution, revealed that the archival of Alice’s holding is divulged to Bob. Without Bob being a choice observer, this transaction remains invisible. You can see a screenshot of the table view here for clarification:

Johan

Hi @Johan_Sjodin , correct me if I’m wrong here, but I think Bob will not know the contract payload (create arguments) of Alice’s holding because he did not witness the CreatedEvent for it. I know it displays in the Navigator, but I don’t believe there is any way to get this contract payload via the ledger API.

Hi @huw,

To make these actions visible through the ledger API, two potential options are:

  1. We could think of adding Bob as an observer to Alice’s holding just before it is debited/archived. By doing so, Bob would gain visibility into the holding arguments, rather than only witnessing the archival of Alice’s asset.
  2. You could also modify the transfer implementation to create a “TransferEvent” contract instance signed by both Alice and Bob, effectively enabling Bob to view it through the ledger API (the template could be archivable by anyone).

Otherwise you might want to check if the information you need can be extracted from the full transaction graph (in the transaction log).

I have added ^ to the same github issue 837 where we can track this.

Johan

@Johan_Sjodin so essentially you are saying that although Bob can’t see the contract arguments through the ledger API (unless one of your solutions is used), that information is still present on Bob’s participant node? I’m confused as to why it wouldn’t be exposed through the ledger API.

Hello @huw,

Indeed, the payload plays a crucial role in the transaction validation process within the participant node and it is at least present in the event log. However, it isn’t provided through the ledger API for archives (I suppose the main reason is to reduce the amount of data sent through the ledger API). By designating Bob as a choice observer, he can at least receive notifications about an archival occurring via the ledger API.

Best,
Johan

Hi @Johan_Sjodin , that’s great but also completely useless to me as a developer if it is not exposed through an API. For this reason it appears the choice observer solution is not a viable one for my usecase.

Hi @huw,

I agree that the current Transfer choice implementation does not fulfill your specific use case where Bob needs to see the payload of the incoming holding from Alice through the ledger API. I was thinking about a third solution which may better address your requirements:

Solution 3
Let the Custodian (for example) create a separate contract called ProxyTransfer for its clients, or specifically for Alice. This contract includes a choice for Alice to transfer holdings. The body of the proxy-transfer choice first adds Bob as an observer before the actual Transfer of the holding is made to Bob.

This solution is similar to Solution 1 ^^, but it separates the process of disclosing the holding (with its payload) from the Transfer choice of the holding. We are hesitant to include this logic directly within the Transfer choice because we understand that the requirement of seeing the payload (through the ledger API) may not always be necessary, especially when the incoming controllers for Bob’s account are empty.

Best,
Johan

@Johan_Sjodin I see what you are getting at. However I don’t believe that exact solution will work because the ledger API doesn’t expose transient contracts - Getting interface views for transient contracts. The Holding transferred to Bob is a transient contract, having been created within the choice body as a result of adding Bob as an observer. It would only work if we use a separate transaction to add Bob as an observer, which is not ideal due to loss of atomicity. Also, I should add that in our case we want to get Interface views, not just the contract payloads. Interface views generally make more sense since we want to keep the application generic and Daml finance uses them extensively. If all we needed was the contract payloads then the ProxyTransfer solution would work.

@huw You are right; adding Bob for a split second doesn’t help because the transient contracts are not exposed over the ledger API. However, you could let the proxy contract create a separate Event contract (6718) instance with any information you need, or alternatively, go for 2).

The approach I think we’ll take is to make everything (even a simple transfer) go via a Batch settlement. This way we can listen to the creation of Batch + SettlementInstruction - which clearly idenitfy where the asset is moving from/to. We also listen to the ExerciseEvents which occur when the Batch is settled. The SettlementInstruction can include the receiving party as an observer to let them know who sent them the asset. This way we have a solution that aligns with the standard workflows in the library and does not require a lot of customization. Also, the Batch can include a description/reference for the transaction, which would solve Reference/Comment in Daml Finance Transfer.

1 Like