Sending tokens
Introduction
This tutorial walks you through the following steps:
- Specifying a sender key
- Generating a token transfer transaction
- Broadcasting the transaction to the network
- Checking transaction completion
- Confirming updates account balances (optional)
This tutorial is NodeJS-specific. If you would like to understand how to initiate a token transfer by constructing and broadcasting transactions using a different language/framework, please review the transactions guide.
Prerequisites
You will need NodeJS 8.12.0
or higher to complete this tutorial. You can verify your installation by opening up your terminal and run the following command:
node --version
You should also complete the Accounts Tutorial. The following steps assume we have access to an existing Stacks 2.0 account.
Step 1: Installing libraries
First, install all the required libraries:
npm install --save @stacks/transactions bn.js @stacks/blockchain-api-client cross-fetch
The API client is generated from the OpenAPI specification (openapi-generator). Many other languages and frameworks are be supported by the generator.
Step 2: Specifying a sender
In order to build and sign transactions, you will need a Stacks private key. You can easily generate a new, random Stacks 2.0 sender key (see "Generating an account" from the previous tutorial).
For this tutorial, we will use an existing Stacks account and instantiate the key object from a private key string:
import fetch from 'cross-fetch';
const BN = require('bn.js');
const {
makeSTXTokenTransfer,
createStacksPrivateKey,
broadcastTransaction,
estimateTransfer,
getNonce,
privateKeyToString,
} = require('@stacks/transactions');
const { StacksTestnet, StacksMainnet } = require('@stacks/network');
const { TransactionsApi, Configuration } = require('@stacks/blockchain-api-client');
const apiConfig = new Configuration({
fetchApi: fetch,
// for mainnet, replace `testnet` with `mainnet`
basePath: 'https://api.testnet.hiro.so',
});
const key = 'edf9aee84d9b7abc145504dde6726c64f369d37ee34ded868fabd876c26570bc01';
const senderKey = createStacksPrivateKey(key);
The code above also imports methods required for the next steps, including API configuration for the client library usage.
Step 3: Generating transaction
To generate a token transfer transaction, we will be using the makeSTXTokenTransfer()
transaction builder function:
const recipient = 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159';
// amount of Stacks (STX) tokens to send (in micro-STX). 1,000,000 micro-STX are worth 1 Stacks (STX) token
const amount = new BN(1000000);
// skip automatic fee estimation
const fee = new BN(2000);
// skip automatic nonce lookup
const nonce = new BN(0);
// override default setting to broadcast to the Testnet network
// for mainnet, use `StacksMainnet()`
const network = new StacksTestnet();
const memo = 'hello world';
const txOptions = {
recipient,
amount,
fee,
nonce,
senderKey: privateKeyToString(senderKey),
network,
memo,
};
...
const transaction = await makeSTXTokenTransfer(txOptions);
The generation method will need a few more pieces of information, as specified in the txOptions
object:
Parameter | Description | Optional |
---|---|---|
recipientAddress | The recipient Stacks address in c32check format | No |
amount | The amount of Stacks tokens to send denominated in microstacks | No |
fee | The fee that the sender is willing to pay for miners to process the transaction. Denominated in microstacks | Yes |
nonce | A nonce is an integer that needs to be incremented by 1 for each sequential transaction from the same account. Nonces start at 0 | Yes |
senderKey | A private key object | Yes |
network | Specifies whether the transaction is meant for Stacks Mainnet or Testnet | Yes |
memo | A memo string to attach additional information to the transaction. This data is limited to 33 bytes | Yes |
Estimating fees
If not specified, the transaction builder will automatically estimate the fee. Estimated fee rate is supplied by a Stacks node so network access is required.
Learn more about fees in the network guide
Another way to estimate the fee is to use the estimateTransfer()
function after you have constructed a transaction:
// get fee
const feeEstimate = estimateTransfer(transaction);
// set fee manually
transaction.setFee(feeEstimate);
By setting a fee in the transaction builder function, the automatic fee estimation step will be skipped.
Handling nonces
If not specified, the transaction builder will automatically lookup the latest nonce for the sender account. Automatic nonce handling also requires network access. The nonce should be tracked locally when creating multiple sequential transactions from the same account. A Stacks node only updates the nonce once a transaction has been mined.
The updated nonce for each account can be retrieved manually using the getNonce()
function:
const senderAddress = 'SJ2FYQ8Z7JY9BWYZ5WM53SKR6CK7WHJF0691NZ942';
const senderNonce = getNonce(senderAddress);
Step 4: Broadcasting transaction
Next, we will broadcast the transaction to the Testnet using the network
object we created earlier:
const broadcastResponse = await broadcastTransaction(transaction, network);
const txID = broadcastResponse.txid;
As soon as the broadcastTransaction
is completed, a JSON object with the transaction ID (txid
) is returned.
Keep in mind that the existence of a transaction ID does not mean the transaction has been successfully processed. Please review the transaction lifecycle for more details.
Serializing transactions
In case you would like to inspect the raw serialized transaction, you can call the serialize()
method:
const serializedTx = transaction.serialize().toString('hex');
Step 5: Checking completion
With the transaction ID, we can check the status of the transaction. Every transaction needs to be confirmed by the network and will be pending
as soon as it is broadcasted.
A transactions is completed once it is confirmed and the status changes to success
. Most transactions will be pending for several minutes before confirmed. You should implement polling in your app to refresh the status display.
const transactions = new TransactionsApi(apiConfig);
const txInfo = await transactions.getTransactionById({
txId,
});
console.log(txInfo);
The API will respond with transaction details, including the tx_status
property:
{
tx_id: '0x5f5318',
tx_type: 'token_transfer',
fee_rate: '180',
sender_address: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6',
sponsored: false,
post_condition_mode: 'deny',
tx_status: 'success',
block_hash: '0xe9b93259',
block_height: 2977,
burn_block_time: 1598915954,
burn_block_time_iso: '2020-08-31T23:19:14.000Z',
canonical: true,
tx_index: 1,
tx_result: { hex: '0x03', repr: 'true' },
token_transfer: {
recipient_address: 'ST9SW39M98MZXBGWSDVN228NW1NWENWCF321GWMK',
amount: '500000',
memo: '0x4661756'
},
events: [ { event_index: 0, event_type: 'stx_asset', asset: [ ... ] } ]
}
For all property formats and details, please review the API reference.
Step 6: Confirming balance (optional)
Now that the token transfer is confirmed, we can verify the new account balance on the sender address by following the "Getting account balances" steps from the previous tutorial.