Sending tokens

Learn how to transfer tokens
beginners
15 minutes

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)

Requirements

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 --versionnode --version

You should also complete the Managing 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 @blockstack/stacks-transactions bn.js @stacks/blockchain-api-client cross-fetchnpm install --save @blockstack/stacks-transactions bn.js @stacks/blockchain-api-client cross-fetch

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, StacksTestnet, privateKeyToString, } = require('@blockstack/stacks-transactions'); const { TransactionsApi, Configuration } = require('@stacks/blockchain-api-client'); const apiConfig = new Configuration({ fetchApi: fetch, basePath: 'https://stacks-node-api.blockstack.org', }); const key = 'edf9aee84d9b7abc145504dde6726c64f369d37ee34ded868fabd876c26570bc01'; const senderKey = createStacksPrivateKey(key);import fetch from 'cross-fetch';const BN = require('bn.js');const { makeSTXTokenTransfer, createStacksPrivateKey, broadcastTransaction, estimateTransfer, getNonce, StacksTestnet, privateKeyToString,} = require('@blockstack/stacks-transactions');const { TransactionsApi, Configuration } = require('@stacks/blockchain-api-client');const apiConfig = new Configuration({ fetchApi: fetch, basePath: 'https://stacks-node-api.blockstack.org',});const key = 'edf9aee84d9b7abc145504dde6726c64f369d37ee34ded868fabd876c26570bc01';const senderKey = createStacksPrivateKey(key);

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 const network = new StacksTestnet(); const memo = 'hello world'; const txOptions = { recipient, amount, fee, nonce, senderKey: privateKeyToString(senderKey), network, memo, }; ... const transaction = await makeSTXTokenTransfer(txOptions);const recipient = 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159';// amount of Stacks (STX) tokens to send (in micro-STX). 1,000,000 micro-STX are worth 1 Stacks (STX) tokenconst amount = new BN(1000000);// skip automatic fee estimationconst fee = new BN(2000);// skip automatic nonce lookupconst nonce = new BN(0);// override default setting to broadcast to the Testnet networkconst 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

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.

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);// get feeconst feeEstimate = estimateTransfer(transaction);// set fee manuallytransaction.setFee(feeEstimate);

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:

getNonce(senderAddress);getNonce(senderAddress);

Step 4: Broadcasting transaction

Next, we will broadcast the transaction to the Testnet using the network object we created earlier:

const txId = await broadcastTransaction(transaction, testnet);const txId = await broadcastTransaction(transaction, testnet);

As soon as the broadcastTransaction is completed, a transaction ID is returned.

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');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.

const transactions = new TransactionsApi(apiConfig); const txInfo = await transactions.getTransactionById({ txId, }); console.log(txInfo);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: [ ... ] } ] }{ 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.

Previous
Managing accounts
Next
Running a testnet node