Skip to main content

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 \ge 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

  1. Alice sends transfer in the simple way.
  2. Alice sends transfer in the custom way to multiple recipients.
  3. Alice sends transfer in the custom way selecting the inputs by hand.
  4. 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.

tip

<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.

tip

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.

tip

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:

API request
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:

API response
{
"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:

API request
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:

API response
{
"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:

API request
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:

API response
{
"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:

API request
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.
warning

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:

API response
{
"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.