Transfer Stablecoins from your Brale account to other parties on the Canton Network.
Transfer Overview
Stablecoins issued by Brale are compatible with the Canton Token Standard, which specifies an interface which can be used to transfer any tokens that have smart contract Templates that comply in the same manner.
This is a 2-step process, in which the transfer is offered by the sender creating a Contract instance which implements the TransferInstruction interface and the recipient exercising the Accept choice on the TransferInstruction contract.
For transfers to parties also custodied by Brale, this acceptance is peformed automatically.
For Canton network transfers to parties not custodied by Brale, a User with permission to ActAs the recipient party must use the Ledger API. Support for manually accepting transfers is included in the Canton Utility UI as well as other Token Standard-compliant wallet applications.
Offers must be accepted within 72 hours or they will expire and be automaticaly withdrawn.
Accepting a Transfer via the Ledger and Token Standard APIs
Acceptance can be automated by using the Ledger and Token Standard APIs to retrieve the necessary contextual information and exercise the Accept choice. The below example shows how to accept an offer using the HTTP JSON Ledger API of the recipient's participant.
Step 1: Retrieve Pending Transfers
First, query your node to list all pending TransferInstructions associated with your party. This allows you to identify the specific transfer you intend to accept.
To query for the transfer instructions, it is first necessary to get the current ledger end offset.
curl -s GET \
--url "${HTTP_JSON_API}/v2/state/ledger-end" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${RECEIVER_TOKEN}"
Note the value of offset in the response, which will be used in the request to obtain the TransferInstructions.
curl -s \
--url "${HTTP_JSON_API}/v2/state/active-contracts" \
--header "Authorization: Bearer ${RECEIVER_TOKEN}" \
--header "Content-Type: application/json" \
--request POST \
--data @- <<EOF
{
"verbose": false,
"activeAtOffset": "${OFFSET}",
"filter": {
"filtersByParty": {
"${RECEIVER_PARTY_ID}": {
"cumulative": [{
"identifierFilter": {
"InterfaceFilter": {
"value": {
"interfaceId":"55ba4deb0ad4662c4168b39859738a0e91388d252286480c7331b3f71a517281:Splice.Api.Token.TransferInstructionV1:TransferInstruction",
"includeInterfaceView": true,
"includeCreatedEventBlob": false
}
}
}
}]
}
}
}
}
EOF
Find the TransferInstruction you want to accept in the response and note it's contractId and interfaceId, which will be used in subsequent steps.
Step 2: Get the Contextual Data Needed to Accept the Transfer
In order to exercise the choice to accept the transfer, it is necessary to obtain some contextual information about it that cannot easily be obtained using the ledger, for which a off-ledger Token Standard API has been implemented.
curl -s \
--url "https://api.utilities.digitalasset-dev.com/api/token-standard/v0/registrars/party-28dc4516-b5ca-44ff-86c7-2107e90a6807%3A%3A1220b8301e18aa8a401d6e34e6c20f8b0243183c514373bca8f1b6b9270246341a9e/registry/transfer-instruction/v1/${TRANSFERINSTRUCTION_CONTRACT_ID}/choice-contexts/accept" \
--header "Content-Type: application/json" \
--request POST \
--data @- <<EOF
{
"meta":{
},
"excludeDebugFields": true
}
EOF
Note the choiceContextData and disclosedContracts in the response, which will need to be provided to the accept choice.
Step 3: Accept the Transfer Using the Ledger API
Once all the contextual information is obtained, the Accept choice of the TransferInstruction can be exercised via the ledger API to complete the transfer.
curl -s \
--url "${HTTP_JSON_API}/v2/commands/submit-and-wait-for-transaction" \
--header "Authorization: Bearer ${RECEIVER_TOKEN}" \
--header "Content-Type: application/json" \
--request POST \
--data @- <<EOF
{
"commands":{
"commands":[
{
"ExerciseCommand":{
"templateId":"${TRANSFERINSTRUCTION_INTERFACE_ID}",
"contractId":${TRANSFERINSTRUCTION_CONTRACT_ID},
"choice":"TransferInstruction_Accept",
"choiceArgument":{
"extraArgs": {
"context": $CHOICECONTEXTDATA,
"meta":{
"values":{
}
}
}
}
}
}
],
"workflowId":"",
"userId":"${RECEIVER_USER_ID}",
"commandId":"$(uuidgen | tr -d '\n')",
"deduplicationPeriod":{
"DeduplicationDuration":{
"value":{
"seconds":30,
"nanos":0
}
}
},
"actAs":[
"${RECEIVER_PARTY_ID}"
],
"readAs":[
],
"submissionId":"$(uuidgen | tr -d '\n')",
"disclosedContracts": ${DISCLOSEDCONTRACTS},
"domainId":"",
"packageIdSelectionPreference":[]
}
}
EOF