Enhanced Transfers¶
In this tutorial, we delve deeper into the concepts that were introduced in our getting-started tutorials. In particular, we will extend on the transfer tutorial.
We begin by understanding the simple form of settlement. It transpires when a customer’s funds are transferred to another account within the same bank. Consequently, the sender’s account is debited (balance decreases), and the recipient’s account is credited (balance increases). This process is internally managed within the bank’s systems and usually occurs instantly, as it doesn’t require interaction with external systems or institutions.
In Daml Finance, such fund transfers are not necessarily represented by a settlement workflow that involves allocating and approving instructions. Instead, a “direct” transfer of funds can occur between two parties, such as Alice and Bob. This transfer debits the sending account and atomically credits the receiving account.
Next, we will explore how to configure the controllers responsible for authorizing incoming transfers (credits) and outgoing transfers (debits) of holdings to an account.
Configuring Account Controllers¶
The Controllers data type specifies the parties that need to authorize incoming and outgoing transfers to an account.
For this tutorial, we provide four example scripts illustrating various incoming and outgoing controller settings:
Script | Incoming Controllers | Outgoing Controllers |
---|---|---|
runDualControlTransfer runDiscretionaryTransfer runSovereignTransfer runUnilateralTransfer |
Anyone Custodian Owner Anyone |
Both (owner and custodian) Custodian Owner Owner |
Each script begins by running a setup script runSetupTransferRequestWith
that requests a
transfer of a holding from Alice to Bob at the Bank. The setup script takes a configuration as input
to set up Alice’s and Bob’s account controllers, as outlined in the table above.
The last step of the setup script creates a transfer request of a holding from Alice to Bob:
let
transferRequest = Transfer.Request with
instructor -- This is equivalent to writing instructor = instructor
receiverAccount = bobAccount
transferableCid = aliceHoldingCid
accepted = mempty
observers = fromList [alice, bob, bank]
transferRequestCid <- submit instructor do createCmd transferRequest
The transfer Request
template is designed for the stepwise collection of the necessary
authorizations for transferring a holding to a new owner:
template Request
with
instructor : Party
-- ^ The party who creates the transfer request, but doesn't necessarily have to authorize it.
receiverAccount : AccountKey
-- ^ The account to which the holding is sent.
transferableCid : ContractId Transferable.I
-- ^ The holding instance to be sent.
accepted : Set Party
-- ^ Current set of parties that accept the transfer.
observers : Set Party
-- ^ Observers.
where
signatory instructor, accepted
observer observers
choice Accept : ContractId Request
with
actors : Set Party
controller actors
do
create this with accepted = actors <> this.accepted
choice Effectuate : ContractId Transferable.I
with
actors : Set Party
controller actors
do
exercise transferableCid Transferable.Transfer with
actors = actors <> this.accepted
newOwnerAccount = receiverAccount
Dual Control¶
In the runDualControlTransfer
script, both the custodian and the owner of an account must
authorize outgoing transfers (debits), while incoming transfers (credits) require no authorization.
This script begins by setting up accounts accordingly and creating a transfer request instance:
let
dualControl = AccountControllers with
incoming = Anyone
outgoing = Both
SetupTransferRequest{bank; alice; bob; instructor; transferRequestCid} <-
runSetupTransferRequestWith dualControl
To execute the transfer, both the Bank and Alice must authorize:
transferRequestCid <- submit bank do
exerciseCmd transferRequestCid Transfer.Accept with actors = singleton bank
submit alice do
exerciseCmd transferRequestCid Transfer.Effectuate with actors = singleton alice
Discretionary¶
The runDiscretionaryTransfer
script specifies that the custodian controls both incoming and
outgoing transfers:
let
discretionary = AccountControllers with
incoming = Custodian
outgoing = Custodian
setupState@SetupTransferRequest{bank; alice; bob; instructor; transferRequestCid} <-
runSetupTransferRequestWith discretionary
Following the setup, the Bank can execute the transfer single-handedly:
submit bank do
exerciseCmd transferRequestCid Transfer.Effectuate with actors = singleton bank
Sovereign¶
In the runSovereignTransfer
script, the owner controls both incoming and outgoing transfers:
let
sovereign = AccountControllers with
incoming = Owner
outgoing = Owner
SetupTransferRequest{bank; alice; bob; instructor; transferRequestCid} <-
runSetupTransferRequestWith sovereign
As Alice is the outgoing controller of the sending account, and Bob is the incoming controller of the receiving account, both need to authorize the transfer:
transferRequestCid <- submit bob do
exerciseCmd transferRequestCid Transfer.Accept with actors = singleton bob
submit alice do
exerciseCmd transferRequestCid Transfer.Effectuate with actors = singleton alice
Unilateral¶
In our final example script, runUnilateralTransfer, the owner controls outgoing transfers, while incoming transfers require no additional authorization:
let
unilateral = AccountControllers with
incoming = Anyone
outgoing = Owner
SetupTransferRequest{bank; alice; bob; instructor; transferRequestCid} <-
runSetupTransferRequestWith unilateral
Once the setup is complete, Alice can independently execute the transfer to Bob:
transferRequestCid <- submit alice do
exerciseCmd transferRequestCid Transfer.Effectuate with actors = singleton alice
Summary¶
By now, you should understand how to configure incoming and outgoing controllers for accounts based on your requirements. Key concepts to remember include:
- To execute a transfer between a sender and a receiver, the outgoing controllers of the sending account and the incoming controllers of the receiving account need to authorize it.
- The required authorization can be provided by a generalized propose-accept template, which allows more than one party to accept.
Ownership transfers usually occur as part of a larger financial transaction. The next tutorials will guide you on how to create such a transaction and how to settle it atomically.