Transfers with headless wallet
Introduction
This article is a tutorial to assist developers in sending transfers using Hathor headless wallet. We will cover:
- which are the ways for transferring assets; and
- how to perform each of them.
By the end of this tutorial, you will have sent your first transfer.
Glossary
These terms are used throughout this article:
-
Transfer is a process by which ownership of assets is reassigned to a new owner.
-
Participant is a person or entity participating in a blockchain transaction.
-
Remitter is a participant of a transfer from which assets are transferred.
-
Recipient is a participant of a transfer to which assets are transferred.
-
Trade is an exchange of predefined and agreed amounts of multiple assets among multiple parties.
For example, Alice and Bob are participants in a transfer. Alice, the remitter, will transfer X amount of token A to Bob, the recipient.
Background
As presented in the previous section, a transfer is a process by which ownership of assets is reassigned to a new owner. Not all blockchain transactions are transfers — e.g., token creation, token minting, token melting, and contract creation are blockchain transactions that are not transfers.
Ways for sending transfers
There are five different ways to send a transfer:
- simple;
- custom;
- multi-signature;
- read-only wallet; and
- atomic swaps.
Which way you will choose depends on the context. Multi-signature and read-only wallet ways are used when the remitter is using a multi-signature wallet or a wallet in the read-only mode, respectively. Atomic swaps are used for trades. In the next subsection, we discuss simple and custom transfers.
Note that each of these five ways requires the usage of a different set of API operations from Hathor headless wallet API.
Simple and custom ways
The simple way for sending transfers allows the remitter to send some quantity of a single token (either HTR, a custom fungible token, or an NFT) to a single recipient. In other words, it can send just a single transfer of tokens per blockchain transaction. Moreover, the remitter cannot choose which of his UTXOs will be spent (as transaction inputs). Thus, it is a straightforward but limited way to send a transfer.
In turn, the custom way of sending transfers allows the remitter to batch multiple transfers into a single blockchain transaction — e.g., instead of making four blockchain transactions to send four transfers to four different people in the simple way, you group them into one blockchain transaction. Each grouped transfer has its recipient address, token, and quantity. Moreover, the remitter can choose which of his UTXOs will be spent (as transaction inputs). Thus, one can say it is a way that provides the possibility to group multiple single transfers and provides a higher level of customization (upon the blockchain transaction and wallet usage).
Here, we will cover sending transfers in the simple and custom ways. To perform a transfer in one of the three other ways, see:
Prerequisites
To execute this tutorial, you must meet the following prerequisites:
- Hathor headless wallet v0.19.2
- Have three different wallets on Hathor Network testnet.
- One of these wallets must have at least a small amount of a custom fungible token and a small number of an NFT. We will use these quantities to perform transfers among the wallets.
Overview of the tasks
In this tutorial, we will perform four different tasks. In the first one, we will perform a simple transfer. In the other three, we will perform custom transfers exploring the different customization possibilities.
Let's suppose three persons, Alice, Bob, and Charlie. In all four tasks, Alice will send transfers to Bob or to both Bob and Charlie. Thus, Alice's wallet will be the transfer's remitter, whereas Bob's and Charlie's will be the transfer's recipients.
Tasks to be executed
- Alice sends transfer in the simple way.
- Alice sends transfer in the custom way to multiple recipients.
- Alice sends transfer in the custom way selecting the inputs by hand.
- Alice sends transfer in the custom way selecting the inputs with UTXOs filters.
Tasks execution
Now, it's time to get your hands dirty. In each of the next four sections, we will describe one of the listed tasks. But first, let's define a set of premises.
Let the wallet with small quantities of tokens be Alice's wallet, whereas the others be Bob's and Charlie's.
<Placeholders>
: in the code samples of this article, as in all Hathor docs, <placeholders>
are always wrapped by angle brackets < >
. You shall interpret or replace a <placeholder>
with a value according to the context. Whenever replacing a <placeholder>
like this one with a value, do not wrap the value with quotes. Quotes, when necessary, will be indicated, wrapping the "<placeholder>"
like this one.
Let B be a custom fungible token and C a non-fungible token (NFT). Let <B_UID>
and <C_UID>
be the respective UIDs of tokens B and C.
Identification of tokens: while using Hathor headless wallet API, if some API operation requires a token UID as a parameter or within its request body, and this is not provided, it becomes implicit that such API request relates to 'HTR'.
Let X and Y be the respective quantities of tokens B and C that Alice will send.
Representing the quantity of tokens: in Hathor headless wallet API requests and responses, the standard to represent any amount of fungible tokens — i.e., the value
property of inputs and outputs objects — is using an integer number whose two last digits are the cents — e.g., 10 HTR becomes '1000', 10.50 HTR becomes '1050', and so forth.
In turn, the standard to represent any number of non-fungible tokens (NFTs) is using an integer that indeed stands for an integer number — e.g., '10' of some NFT stands for ten units of that token.
Let <Xxx>
be the correct representation of X, where the last two digits represent the decimal part. Let <Y>
be the correct representation of Y.
Finally, suppose you have already started Alice's, Bob's, and Charlie's wallets.
Task 1: Alice sends transfer in the simple way
Alice wants to transfer X amount of a custom fungible token B to Bob. Use Alice's wallet to perform the following:
curl -X POST -H 'X-Wallet-Id: alice' \
-H 'Content-Type: application/json' \
-d '{
"address" : "<bob_address>",
"value" : <Xxx>,
"token" : "<B_UID>"
}' \
http://localhost:8000/wallet/simple-send-tx | jq
If <bob_address>
and <B_UID>
are valid, and Alice has an amount of token B funds greater than X, the transaction will be confirmed:
{
"success": true,
"inputs": [
...
],
"outputs": [
...
],
...
"tokens": [
"<B_UID>"
],
"hash": "<tx_task_1_id>",
"_dataToSignCache": null
}
The hash of the transaction is its 'id'. In this case, transaction id is <tx_task_1_id>
. It can be used by Alice, Bob, and anyone to check transaction data, its status, etc. For example, Bob can check if he has already received Alice's transfer.
Task 2: Alice sends transfer in the custom way to multiple recipients
Alice wants to send two transfers into a single blockchain transaction. She wants to transfer X amount of a custom fungible token B to Bob and Y number of an NFT C to Charlie. Use Alice's wallet to perform the following:
curl -X POST \
-H 'Content-Type: application/json' \
-H 'X-Wallet-Id: alice' \
-d '{
"outputs": [
{
"address": "<bob_address>",
"value": <Xxx>,
"token": "<B_UID>"
},
{
"address": "<charlie_address>",
"value": <Y>,
"token": "<C_UID>"
}
]
}' \
http://localhost:8000/wallet/send-tx | jq
If <bob_address>
, <charlie_address>
, <B_UID>
, and <C_UID>
are valid, and Alice has an amount of token B funds greater than X, and a number of token C funds greater than Y, the transaction will be confirmed:
{
"success": true,
"inputs": [
...
],
"outputs": [
{
"value": <Xxx>,
"script": {
...
},
...
},
{
"value": <Zzz>,
"script": {
...
},
...
},
{
"value": <Y>,
"script": {
...
},
...
},
{
"value": <W>,
...
},
...
}
],
...
"tokens": [
"<B_UID>",
"<C_UID>"
],
"hash": "<tx_task_2_id>",
"_dataToSignCache": null
}
In the previous API response, beyond the values <Xxx>
and <Y>
, we also see <Zzz>
and <W>
as outputs. These are Alice's change due to spending UTXOs (as inputs) that had greater quantities than necessary. These changes are new UTXOs. An UTXO is identified by its transaction id plus its position in the array of the outputs of such transaction — i.e., its index.
For example, Alice may select the UTXO with Z amount of token B in a new transaction. She will identify it using its transaction id <tx_task_2_id>
and its index 1. This selection is precisely what we will do in the next task.
Task 3: Alice sends transfer in the custom way selecting the inputs by hand
In the two previous tasks, the wallet application automatically made the selection of which UTXOs had been spent (as inputs). This may or may not be desirable. It is up to each use case to decide in which situations use each behavior. In tasks 3 and 4, we will make this selection but using a different method in each one.
Alice wants to send two transfers into a single blockchain transaction. She wants to transfer X amount of a custom fungible token B to Bob and Y number of an NFT C to Charlie. She checked her set of available UTXOs and chose a number i of them to use in this transaction.
Use Alice's wallet to perform the following:
curl -X POST \
-H 'Content-Type: application/json' \
-H 'X-Wallet-Id: alice' \
-d '{
"outputs": [
{
"address": "<bob_address>",
"value": <Xxx>,
"token": "<B_UID>"
},
{
"address": "<charlie_address>",
"value": <Y>,
"token": "<C_UID>"
}
],
"inputs": [
{
"hash": "<tx_id_input_1>",
"index": <index_input_1>
},
{
"hash": "<tx_id_input_2>",
"index": <index_input_2>
},
...
{
"hash": "<tx_id_input_i>",
"index": <index_input_i>
}
]
}' \
http://localhost:8000/wallet/send-tx | jq
The transaction will be confirmed if all data is valid and Alice has the required funds. In the previous API request and following API response, we see the UTXOs Alice selected being used as inputs in this new transaction. Each UTXO is identified by its transaction id and index in the array of outputs of such transaction:
{
"success": true,
"inputs": [
{
...
"tx_id": "<tx_id_input_1>"
},
{
...
"tx_id": "<tx_id_input_2>"
},
...
{
...
"tx_id": "<tx_id_input_i>"
},
],
"outputs":[
{
"value": <Xxx>,
"script": {
...
},
...
},
{
"value": <Jjj>,
"script": {
...
},
...
},
{
"value": <Y>,
"script": {
...
},
...
},
{
"value": <K>,
"script": {
...
},
...
}
],
...
"parents": [
...
],
"tokens": [
"<B_UID>",
"<C_UID>"
],
"hash": "<tx_task_3_id>",
"_dataToSignCache": null
}
In the previous API response, beyond the values <Xxx>
and <Y>
, we also see <Jjj>
and <K>
as outputs. As we discussed in the previous task, these are Alice's change. If a transaction has change outputs and no change address was specified in the API request, the wallet application automatically selects the first available address. This is what happened in this task. In the next task, we will provide the change address in the request body of the transaction.
Task 4: Alice sends transfer in the custom way selecting the inputs with UTXOs filters
As already discussed, in this task, we will also send a custom transfer selecting the inputs. However, in the previous task, we provided a set of UTXOs as inputs. This time, we will create a query with conditions to filter a set of UTXOs. Then, the wallet application will automatically pick a subset from the resultant set of UTXOs.
Alice wants to send two transfers into a single blockchain transaction. She wants to transfer X amount of a custom fungible token B to Bob and Y number of an NFT C to Charlie. Also, she wants to fulfill these two quantities of tokens only spending UTXOs available at her address <alice_address_m>
. And finally, she wants to receive her change at address <alice_address_o>
. Use Alice's wallet to perform the following:
curl -X POST \
-H 'Content-Type: application/json' \
-H 'X-Wallet-Id: alice' \
-d '{
"outputs": [
{
"address": "<bob_address>",
"value": <Xxx>,
"token": "<B_UID>"
},
{
"address": "<charlie_address>",
"value": <Y>,
"token": "<C_UID>"
}
],
"inputs": [
{
"type": "query",
"filter_address": "<alice_address_m>"
}
],
"change_address": "<alice_address_o>"
}' \
http://localhost:8000/wallet/send-tx | jq
In this example, we are using only the filter_address
filter. There are four possible filters:
max_utxos
: to define the maximum number of UTXOs to be retrieved in the query.filter_address
: retrieve only UTXOs from this address.amount_smaller_than
: retrieve only UTXOs with value smaller than this.amount_bigger_than
: retrieve only UTXOs with value bigger than this. You can use any combination of these four filters to create a query.
To filter UTXOs, you should use the key/pair "type": "query"
, and define the whole query in the first input
object of the array of inputs
. Moreover, you should use only a single input
object in the array. The application will disregard any subsequent input
object in the array. Finally, you should not use the properties hash
and index
along the key/pair "type": "query"
.
If all data is valid and Alice has the required funds within the set of UTXOs available at the address <alice_address_m>
, the transaction will be confirmed:
{
"success": true,
"inputs": [
{
...
"tx_id": "<tx_id_input_1>"
},
{
...
"tx_id": "<tx_id_input_2>"
},
...
{
...
"tx_id": "<tx_id_input_i>"
},
],
"outputs": [
{
"value": <Xxx>,
"script": {
...
},
...
},
{
"value": <Qqq>,
"script": {
...
},
...
},
{
"value": <Y>,
"script": {
...
},
...
},
{
"value": <P>,
"script": {
...
},
...
}
],
...
"parents": [
...
],
"tokens": [
"<B_UID>",
"<C_UID>"
],
"hash": "<tx_task_4_id>",
"_dataToSignCache": null
}
The query we did, filtered the UTXOs by address. Note that this is only one of the multiple filters one can use to create the query. Furthermore, one can create a query with a combination of multiple of these filters. To know all possible filters available, see Hathor headless wallet API reference: send a custom transfer.
Tasks completed
At this point, you completed your first transfer using Hathor headless wallet. With these four tasks, you have an example of the usage of simple transfers and three examples of the usage of custom transfers.
Key takeaways
However, although this tutorial has covered the most common usages of the simple and custom transfers, we did not exhaust what is possible to do with the presented API operations nor explored all its possible parameters. For a comprehensive view of these API operations, see Hathor headless wallet API: send a simple transfer and Hathor headless wallet API: send a custom transfer.
What's next?
Hathor headless wallet HTTP API reference: to consult while implementing transfers into your use case.