Skip to main content

Learning the Stacks.js Basics

Looking to building Stacks-ready web applications? Read more on Stacks Connect

To introduce the different functionality offered by Stacks.js, we'll walk through a few examples and concepts important to building on the Stacks blockchain.

Networks

Typically, we speak of "mainnet" and "testnet" as the networks of Stacks. Most wallets will be configured to "mainnet" by default, this is the actual blockchain that holds real STX tokens. As the name suggests, "testnet" is a network for testing. It's a separate blockchain state that holds test tokens, which have no value.

Developers are encouraged to use testnet for testing before rolling out applications and contracts to mainnet. There is even Devnet/Mocknet for working in a local development environment for development. Stacks.js functions can be configured to use whichever network you want.

import { StacksMainnet, StacksTestnet } from '@stacks/network';
const mainnet = new StacksMainnet();
const testnet = new StacksTestnet();

The constructors can also be passed a custom URL to an API, if you want to use a different API than the default.

import { StacksMainnet } from '@stacks/network';
const network = new StacksMainnet({ url: 'https://www.mystacksnode.com/' });

Accounts and Addresses

Connect 🌐

For web apps, you can request the user's address via Stacks Connect. Read more

Stacks.js uses the concept of an "account" to represent a user's identity on the blockchain. An account is identified by a unique address. The address is derived from the account's public key, which is derived from the account's private key.

A normal mainnet address starts with SP, and a testnet address starts with ST. e.g. SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159, ST2F4BK4GZH6YFBNHYDDGN4T1RKBA7DA1BJZPJEJJ

import { generateSecretKey } from '@stacks/wallet-sdk';

const mnemonic = generateSecretKey();
// aunt birth lounge misery utility blind holiday walnut fuel make gift parent gap picnic exact various express sphere family nerve oil drill engage youth

const wallet = await generateWallet({
secretKey: mnemonic,
password: 'secretpassword',
});

const account = wallet.accounts[0];
const mainnetAddress = getStxAddress({ account, transactionVersion: TransactionVersion.Mainnet });
const testnetAddress = getStxAddress({ account, transactionVersion: TransactionVersion.Testnet });

Transactions

The following shows how to create a simple transaction (STX transfer) using Stacks.js in different environments.

Using Connect 🌐

import { openSTXTransfer } from '@stacks/connect';
import { StacksTestnet } from '@stacks/network';
import { AnchorMode } from '@stacks/transactions';

openSTXTransfer({
network: new StacksTestnet(),

recipient: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // which address we are sending to
amount: 10000, // tokens, denominated in micro-STX
anchorMode: AnchorMode.Any,

onFinish: response => console.log(response.txid),
onCancel: () => console.log('User canceled'),
});

Using a private key 🔑

For full manual transaction signing, we need to provide the sender's private key. Treat the private key as a secret and never expose it to the public!

import { makeSTXTokenTransfer } from '@stacks/transactions';

const tx = await makeSTXTokenTransfer({
recipient: 'ST39MJ145BR6S8C315AG2BD61SJ16E208P1FDK3AK', // which address we are sending to
amount: 10000, // tokens, denominated in micro-STX
anchorMode: 'any',
senderKey: 'c3a2d3...0b1c2', // private key (typically derived from a mnemonic)
});

Anchor Mode / Block Type

In the examples above, we used AnchorMode.Any to indicate that the transaction can be "mined" in different ways. Stacks has two types of blocks: microblocks and (anchor) blocks.

  • Microblocks (off-chain) are faster, but less reliable. Microblocks can be confirmed quickly but are not final until the microblock is included in an anchor block.
  • Anchor Blocks (on-chain) are the normal Stacks block. They are slower, but more reliable. Anchor blocks are final and cannot be reverted.
// AnchorMode options
anchorMode: "offChainOnly" | "onChainOnly" | "any",

Post Conditions

In Stacks, transactions can have "post conditions". These are additional security to ensure the transaction was executed as expected.

Post conditions can't say anything about the end-state after a transaction, but they can verify that certain things happened during the transaction.

More precisely, post conditions can verify that:

  • STX tokens were transferred from an address
  • FTs/NFTs we transferred from an address
caution

Post conditions aren't perfect and can't always guarantee the receival of FTs/NFTs, since they only check senders.

An example adding a post condition (of an address sending 1000 uSTX).

import { Pc } from '@stacks/transactions';

const tx = await makeContractCall({
// ...
postConditions: [
Pc.principal('STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6').willSendEq(1000).ustx(),
],
});

Post Condition "Mode"

...aka "allow transfer of unspecified assets?"

In addition to the post conditions itself, we can also specify a "mode" for the transaction to verify asset transfers. The mode can be either Allow or Deny.

  • Allow means that the transaction can transfer any asset (assuming no conflicting post conditions).
  • Deny means the transaction will fail if any asset transfers (not specified in the post conditions) are attempted.
note

In either case, all post conditions will still be checked. By default, transactions are set to Deny mode for additional security.

Broadcasting

Connect 🌐

For web apps via Stacks Connect, the users' wallet will broadcast the transaction and return a txid. Read more

A finalized transaction can be broadcasted to the network or serialized (to a byte representation) using Stacks.js.

import { bytesToHex } from '@stacks/common';
import { makeSTXTokenTransfer, broadcastTransaction, AnchorMode } from '@stacks/transactions';

const broadcastResponse = await broadcastTransaction(transaction);
const txId = broadcastResponse.txid;

const serializedTx = tx.serialize(); // Uint8Array
const serializedTxHex = bytesToHex(serializedTx); // hex string