Skip to main content

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)
note

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
info

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);
note

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:

ParameterDescriptionOptional
recipientAddressThe recipient Stacks address in c32check formatNo
amountThe amount of Stacks tokens to send denominated in microstacksNo
feeThe fee that the sender is willing to pay for miners to process the transaction. Denominated in microstacksYes
nonceA nonce is an integer that needs to be incremented by 1 for each sequential transaction from the same account. Nonces start at 0Yes
senderKeyA private key objectYes
networkSpecifies whether the transaction is meant for Stacks Mainnet or TestnetYes
memoA memo string to attach additional information to the transaction. This data is limited to 33 bytesYes

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.

tip

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);
note

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.

note

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.

note

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.