Skip to content

Add CreatePreapproval choice to SimpleTokenRules for multi-participant preapproval creation #3

@IvanNik

Description

@IvanNik

Problem

TransferPreapproval requires signatory admin, receiver. Creating it via create_contract needs both parties in act_as. In a multi-participant Canton deployment - where admin and receiver are hosted on separate participants - neither participant can authorize both parties. A submission from the admin's participant cannot include the receiver's authorization, and vice versa.

This means the direct transfer path in SimpleTokenRules (which requires a preapproval CID in extraArgs.context) is unusable in production Canton topologies. Only the two-step transfer path works out of the box.

The existing tests use submitMulti [admin, receiver] (a Daml Script convenience that bypasses participant boundaries), which works in a single-ledger test environment but doesn't reflect real multi-participant deployments.

Proposed Solution

Add a nonconsuming choice CreatePreapproval to SimpleTokenRules. Since SimpleTokenRules already has signatory admin, the receiver can exercise this choice via disclosed contracts to create a TransferPreapproval with both signatures - each party signing on their own participant.

    nonconsuming choice CreatePreapproval : ContractId TransferPreapproval
      with
        receiver : Party
        instrumentId : InstrumentId
        expiresAt : Optional Time
        meta : Metadata
      controller receiver
      do
        assertMsg "Instrument not supported by this factory"
          (instrumentId.id `elem` supportedInstruments)
        assertMsg "instrumentId.admin must match factory admin"
          (instrumentId.admin == admin)
        create TransferPreapproval with admin; receiver; instrumentId; expiresAt; meta

How it works

Admin creates SimpleTokenRules on their participant (already exists)
Receiver obtains the SimpleTokenRules disclosed contract blob
Receiver exercises CreatePreapproval on their participant using the disclosed contract
Admin's authorization flows from SimpleTokenRules signatory; receiver's authorization comes from being the choice controller
The resulting TransferPreapproval has signatory admin, receiver - both signatures obtained without multi-party submission

Multi-participant flow (with disclosed contracts)

Receiver's participant                      Admin's participant
        |                                          |
        |  ← obtains SimpleTokenRules blob →       |
        |                                          |
        |-- exercise CreatePreapproval ----------->|
        |   (disclosed SimpleTokenRules)           |
        |                                          |
        |  TransferPreapproval created             |
        |  (signatory: admin + receiver)           |

After the preapproval exists, any sender can do a direct (one-step) transfer to the receiver by passing the preapproval CID in extraArgs.context.values["transfer-preapproval"] - exactly as the existing directTransfer path in Rules.daml expects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions