TAIP-3: Asset Transfer
Table of Contents
Simple Summary
This specification provides the messaging details for a simple virtual asset transfer between two parties as part of a Transaction Authorization Protocol flow.
Abstract
This TAIP contains a specification for a metadata message about a virtual asset transaction for use as part of the Authorization Flow in TAP.
The primary type of transaction this defines supports the transfer of an amount of a fungible or non-fungible token from an originator to a beneficiary.
This TAIP only specifies the specifics of a Transfer rather than the complete Authorization Flow, as defined in TAIP-4.
Motivation
The Asset Transfer is a simple chain-agnostic representation of a typical virtual asset transaction and its parties. The vast majority of transactions performed by both custodial services and self-hosted wallets fall under this classification.
This specification builds on existing Chain Agnostic standards such as CAIP-10 Account Identifiers and CAIP-19 Asset Identifiers.
For developers of new blockchain protocols and token standards, make sure that you implement these and list them on the Chainagnostic Namespaces page for reference.
Specification
Asset Transfers are implemented in the body of a TAIP-2 message.
DIDComm type URI
The preliminary URI to be used in the TAIP-2 type header should be https://tap.rsvp/schema/taip-3#Transfer
.
Message Body
As specified in TAIP-2 the message body is JSON-LD. The following attributes are defined:
@context
- REQUIRED the JSON-LD contexthttps://tap.rsvp/schema/1.0
@type
- REQUIRED the JSON-LD typehttps://tap.rsvp/schema/1.0#Transfer
asset
- REQUIRED the CAIP-19 or DTI identifier of the assetamount
- OPTIONAL for NFTs and REQUIRED for fungible tokens. Specified as a string with the full amount as a decimal representation of the tokenoriginator
- REQUIRED an object representing the originating (aka the sender) party (see TAIP-6)beneficiary
- OPTIONAL an object representing the beneficiary (aka the recipient) party if different than theoriginator
(see TAIP-6)settlementId
- OPTIONAL a CAIP-220 identifier of the underlying settlement transaction on a blockchain. For more details see below.agents
- REQUIRED an array of identity objects representing the agents who help execute the transaction. See TAIP-5 for more.memo
- OPTIONAL a human readable UTF-8 string to be provided as-is by the originator to the beneficiary about the transferexpiry
- OPTIONAL a timestamp in ISO 8601 format indicating when the transfer request expires. After this time, if no authorization has occurred, the transfer should be considered invalid and funds will not be sent. Recipients’ agents SHOULD communicate this expiration deadline to their users when presenting the transfer for review.
Many of the attributes are optional and through the process of authorization can be expanded and modified collaboratively by the agents of a transaction.
Out-of-Band Initiation
To initiate a transfer with a party that hasn’t communicated before, agents MUST support Out-of-Band Messages. The OOB message allows sharing the Transfer request through URLs or QR codes.
OOB messages for Transfer requests:
- MUST use the
https://didcomm.org/out-of-band/2.0
protocol - MUST include the goal_code
tap.transfer
- SHOULD be shared as URLs according to the Out-of-Band message spec
- MUST include the Transfer message as a signed DIDComm message in the attachment
Example Out-of-Band message with Transfer request:
{
"type": "https://didcomm.org/out-of-band/2.0/invitation",
"id": "2e9e257c-2839-4fae-b0c4-dcd4e2159f4e",
"from": "did:web:originator.vasp",
"body": {
"goal_code": "tap.transfer",
"goal": "Process asset transfer",
"accept": ["didcomm/v2"]
},
"attachments": [{
"id": "transfer-request-1",
"mime_type": "application/didcomm-signed+json",
"data": {
"json": {
"payload": "eyJpZCI6IjU5OWY3MjIwLTYxNDktNGM0NS1hZGJiLTg2ZDk2YzhlMDYwOCIsInR5cGUiOiJodHRwczovL3RhcC5yc3ZwL3NjaGVtYS8xLjAjVHJhbnNmZXIiLCJmcm9tIjoiZGlkOndlYjpvcmlnaW5hdG9yLnZhc3AiLCJib2R5Ijp7IkBjb250ZXh0IjoiaHR0cHM6Ly90YXAucnN2cC9zY2hlbWEvMS4wIiwiQHR5cGUiOiJodHRwczovL3RhcC5yc3ZwL3NjaGVtYS8xLjAjVHJhbnNmZXIiLCJhc3NldCI6ImVpcDE1NToxL3NsaXA0NDo2MCIsImFtb3VudCI6IjEuMjMiLCJvcmlnaW5hdG9yIjp7IkBpZCI6ImRpZDplZzpib2IifSwiYWdlbnRzIjpbeyJAaWQiOiJkaWQ6d2ViOm9yaWdpbmF0b3IudmFzcCJ9XX19",
"signatures": [{
"protected": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDp3ZWI6b3JpZ2luYXRvci52YXNwI2tleS0xIn0",
"signature": "8KqJCbUdHMfN1etqy9_kyhVkw9h1Va3Z2vYu6dxZYuQM2XwYoG_5rFTvk3YH9xDRp5VxbXqnYYvKjHzjhBRzBw"
}]
}
}
}]
}
The json
field contains a signed JWS with:
payload
: Base64URL-encoded Transfer messagesignatures
: Array of signatures with protected header and signature value- The protected header includes the key identifier (
kid
) that can be resolved through the signer’s DID Document
The corresponding URL format would be either:
https://example.com/path?_oob=eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9...
Or using the shorter _oobid
parameter that references a previously published Out-of-Band message:
https://example.com/path?_oobid=2e9e257c-2839-4fae-b0c4-dcd4e2159f4e
Where the _oob
parameter contains the base64url-encoded Out-of-Band message, or the _oobid
parameter contains a unique identifier that can be resolved to retrieve the full Out-of-Band message.
Transfer Amounts
The amount of a transfer is specified as amount
and represents the decimal value of the asset. This approach is more intuitive for most users and application developers. It is the responsibility of library and tool developers to handle any necessary conversions when interacting with blockchain protocols that may require amounts in their smallest units.
As an example, ETH 1.23
should be encoded as "1.23"
.
settlementId
Use a simplified version of the format proposed by CAIP-220
block_address: chain_id + ":" [ + "block:"]? + "txn/" + transaction_id?
chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][])
transaction_id: [-%a-zA-Z0-9]{1,128}
eg:
eip155:1:tx/0x3edb98c24d46d148eb926c714f4fbaa117c47b0c0821f38bfce9763604457c33
First and Third party transfers
If the originator
is moving funds on their own behalf between their own wallets or accounts at different providers it is known as a first-party transfer. In this case only the originator
is REQUIRED. The beneficiary
can still OPTIONALLY be included for first-party transfers to indicate additional information such as account identifiers useful to the beneficiary institution.
If originator
and beneficiary
are different it is known as a third-party transfer.
Agent Roles
Agents can have specific roles vital to the execution of a transaction.
The following two roles can be used as attributes on Agents listed in the agents array:
SettlementAddress
, The blockchain wallet agent to settle a transaction toSourceAddress
The blockchain wallet agent used to send the transaction from
Neither of these is required but can be used to specify the blockchain wallets up front.
Rationale
This message type is designed to be as general and chain agnostic as possible and compatible with as little meta-data as possible, together with any required meta-data, that is useful for improving usability, safety, and record-keeping for a transaction.
Note this is designed to be used as an initial request message. The body attributes can be used as a representation of the internal state of a transaction by an agent, but do intentionally not represent the shared state.
It is also not intended to cover more complex transaction use cases, such as non-token transfer-related smart contract calls. This TAIP is encouraged to be forked and modified to create similar requests for Swaps, lending, and other everyday use cases.
Test Cases
The following is a minimal first-party request for a transfer of 1.23 ETH from a trading firm to the ethereum wallet with the address 0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb
:
{
"from":"did:web:originator.sample",
"type": "https://tap.rsvp/schema/1.0#Transfer",
"id": "...",
"to": ["did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"],
"body": {
"@context": "https://tap.rsvp/schema/1.0",
"@type": "https://tap.rsvp/schema/1.0#Transfer",
"asset": "eip155:1/slip44:60",
"originator":{
"@id":"did:web:originator.sample"
},
"amount": "1.23",
"agents":[
{
"@id":"did:web:originator.sample"
},
{
"@id":"did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb",
"role":"settlementAddress"
}
]
}
}
The following is a request for a first-party transfer of 1.23 ETH from a crypto exchange from a customer to the ethereum wallet with the address 0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb
:
{
"from":"did:web:originator.vasp",
"type": "https://tap.rsvp/schema/1.0#Transfer",
"id": "...",
"to": ["did:web:beneficiary.vasp", "did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"]
"body": {
"@context": "https://tap.rsvp/schema/1.0",
"@type": "https://tap.rsvp/schema/1.0#Transfer",
"asset": "eip155:1/slip44:60",
"originator":{
"@id":"did:eg:bob"
},
"amount": "1.23",
"agents":[
{
"@id":"did:web:originator.vasp"
},
{
"@id":"did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb",
"role":"settlementAddress"
}
]
}
}
The following is a request for a third-party transfer of 1.23 ETH from a crypto exchange from a customer to a customer at another hosted wallet, which does not include settlement information. This allows the parties negotiate settlement as part of the authorization flow:
{
"from":"did:web:originator.vasp",
"type": "https://tap.rsvp/schema/1.0#Transfer",
"id": "...",
"to": ["did:web:beneficiary.vasp", "did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"]
"body": {
"@context": "https://tap.rsvp/schema/1.0",
"@type": "https://tap.rsvp/schema/1.0#Transfer",
"asset": "eip155:1/slip44:60",
"originator":{
"@id":"did:eg:bob"
},
"beneficiary":{
"@id":"sms:+15105550101"
},
"amount": "1.23",
"agents":[
{
"@id":"did:web:originator.vasp"
},
{
"@id":"did:web:beneficiary.vasp"
}
]
}
}
The following is an example of a reasonably complete third-party transfer already that was already settled on the blockchain. An Agent could create these to backfill information about an already settled transaction.
{
"from":"did:web:originator.vasp",
"type": "https://tap.rsvp/schema/1.0#Transfer",
"id": "...",
"to": ["did:web:beneficiary.vasp", "did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb"],
"body": {
"@context": "https://tap.rsvp/schema/1.0",
"@type": "https://tap.rsvp/schema/1.0#Transfer",
"originator":{
"@id":"did:eg:bob"
},
"beneficiary":{
"@id":"did:eg:alice"
},
"asset": "eip155:1/slip44:60",
"amount": "1.23",
"settlementId":"eip155:1:tx/0x3edb98c24d46d148eb926c714f4fbaa117c47b0c0821f38bfce9763604457c33",
"agents":[
{
"@id":"did:web:originator.vasp",
},
{
"@id":"did:beneficiary.vasp",
},
{
"@id":"did:pkh:eip155:1:0x1234a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb",
"role":"settlementAddress"
}
]
}
}
Security Considerations
It is essential to understand that this represents a request to perform a transaction, and requires all parties to verify all information in it to authorize and settle a transaction safely. The agents are responsible for confirming this information, even post-settlement.
Privacy Considerations
This message can contain PII about end-users. Agents must understand their privacy duties under national law to safeguard their customers’ PII. As part of a TAIP-4 authorization flow, agents SHOULD evaluate the data privacy of any other agent before sharing PII with them.
Agents SHOULD minimize the use of end-user PII in this message, but it can be encrypted to specific trusted parties or agents separately. See TAIP-8 for more.
References
- TAIP-2 Defines the TAP Message structure
- TAIP-4 Defines the Transaction Authorization Protocol
- TAIP-5 Describes Transaction Agents
- TAIP-6 Describes Transaction Parties
- TAIP-8 Selective disclosure of PII
- CAIP-10 Describes chainagnostic Account ID Specification
- CAIP-19 Describes Chainagnostic Asset ID Specification
- DTI Digital Token Identifier standard
- JSON JavaScript Object Notation
- JSON-LD JSON Linked Data