Skip to main content

Learning the Stacks.js Basics

Looking to build a Stacks-ready web application? 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 applications, 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.

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'),
});

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

More precisely, adding post conditions to a transaction can ensure that:

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

Post conditions aren't perfect. They can't say anything about the end-state after a transaction. So, they can't guarantee the receival of FTs/NFTs, since they only check for sending.

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 applications, the users' wallet can broadcast transactions created via Stacks Connect. 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