Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Getting started

Install @secondlayer/stacks and create a client against a Stacks network.

bun add @secondlayer/stacks
import { createPublicClient, http } from "@secondlayer/stacks";
import { mainnet } from "@secondlayer/stacks/chains";
 
const client = createPublicClient({
  chain: mainnet,
  transport: http(),
});
 
const balance = await client.getBalance({
  address: "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7",
});

Clients and transports live at the package root (not separate export subpaths). The sections below are the module notes for each.

Clients

Client factories and composable action decorators.

Public Client (Read-Only)

import { createPublicClient, http, mainnet } from "@secondlayer/stacks";
 
const client = createPublicClient({
  chain: mainnet,
  transport: http(),
});
 
const balance = await client.getBalance({ address: "SP2J6..." });
const height = await client.getBlockHeight();

Wallet Client

import { createWalletClient, http, mainnet } from "@secondlayer/stacks";
import { privateKeyToAccount } from "@secondlayer/stacks/accounts";
 
const client = createWalletClient({
  account: privateKeyToAccount("0x..."),
  chain: mainnet,
  transport: http(),
});
 
const { txid } = await client.sendTransaction({ transaction: signedTx });

Multi-Sig Client

import { createMultiSigClient, http, mainnet } from "@secondlayer/stacks";
 
const client = createMultiSigClient({
  publicKeys: [pk1, pk2, pk3],
  signaturesRequired: 2,
  chain: mainnet,
  transport: http(),
});

Extending with Extensions

import { bns } from "@secondlayer/stacks/bns";
import { pox } from "@secondlayer/stacks/pox";
 
const client = createPublicClient({
  chain: mainnet,
  transport: http(),
}).extend(bns()).extend(pox());
 
await client.bns.resolveName("alice.btc");
await client.pox.getPoxInfo();

Custom Decorators

const myActions = (client) => ({
  myCustomAction: () => client.readContract({ ... }),
});
 
const client = createPublicClient({ ... }).extend(myActions);

Transports

Transport layer for communicating with Stacks nodes.

HTTP (Default)

import { http } from "@secondlayer/stacks";
 
// Uses chain's default RPC URL
const transport = http();
 
// Custom URL
const transport = http("https://my-node.example.com");
 
// With options
const transport = http("https://my-node.example.com", {
  apiKey: "my-api-key",
  timeout: 30_000,
  retryCount: 3,
  retryDelay: 1_000,
});

Non-2xx responses throw a typed HttpRequestError (.status attached) instead of resolving with the error body — check e.status rather than reading client.request(...)'s return value for failure. Retries cover 5xx, network errors, and 429.

WebSocket

import { webSocket } from "@secondlayer/stacks";
 
const transport = webSocket();
 
// Custom URL
const transport = webSocket("wss://my-node.example.com");

Fallback

Tries transports in order, falls back on failure.

import { fallback, http } from "@secondlayer/stacks";
 
const transport = fallback([
  http("https://primary-node.com"),
  http("https://backup-node.com"),
]);

Simnet

Separate entry. @stacks/clarinet-sdk is an optional peer of that entry only.

import { initSimnet } from "@stacks/clarinet-sdk";
import { createPublicClient } from "@secondlayer/stacks";
import { simnet, simnetChain } from "@secondlayer/stacks/simnet";
 
const session = await initSimnet("./Clarinet.toml");
const client = createPublicClient({
  chain: simnetChain,
  transport: simnet(session),
});

Custom

import { custom } from "@secondlayer/stacks";
 
const transport = custom({
  async request(path, options) {
    const response = await fetch(`https://my-api.com${path}`, {
      method: options?.method ?? "GET",
      body: options?.body ? JSON.stringify(options.body) : undefined,
    });
    return response.json();
  },
});