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

Actions

Standalone action functions for tree-shakeable imports. These are the same functions available on clients, but usable without a client instance.

Public (Read-Only) Actions

import { getBalance, getNonce, readContract, getBlock, getBlockHeight } from "@secondlayer/stacks/actions";
 
const balance = await getBalance(client, { address: "SP2J6..." });
const nonce = await getNonce(client, { address: "SP2J6..." });
const height = await getBlockHeight(client);

getAccountHistory, getMempoolStats, and getNftHoldings cover paginated tx history, mempool stats, and NFT holdings as first-class, standalone-importable actions.

Node-Only Reads

getContractSource and getRawBlock hit a stacks-node's raw RPC (/v2/contracts/source, /v2/blocks/{height}) — not Hiro's extended API and not a tenant proxy, so client needs a direct node transport. getRawBlock is distinct from getBlock (which reads the indexed extended-API shape): it returns node/consensus fields like index_block_hash and miner_txid. Both return null on a 404 or missing data instead of throwing.

import { getContractSource, getRawBlock } from "@secondlayer/stacks/actions";
 
const src = await getContractSource(client, { contract: "SP2J6....my-contract" });
const block = await getRawBlock(client, { height: 150_000 });

Contract Reads

import { readContract } from "@secondlayer/stacks/actions";
import { Cl } from "@secondlayer/stacks/clarity";
 
const result = await readContract(client, {
  contractAddress: "SP2J6...",
  contractName: "my-contract",
  functionName: "get-balance",
  functionArgs: [Cl.principal("SP3FBR...")],
});

Typed Contracts

import { getContract } from "@secondlayer/stacks/actions";
 
const contract = getContract({
  client,
  address: "SP2J6...",
  name: "my-contract",
  abi: MY_ABI,
});
 
// Type-safe reads, calls, map lookups, and unsigned tx builds
const balance = await contract.read.getBalance({ account: "SP3FBR..." });
const txid = await contract.call.transfer({ to: "SP3FBR...", amount: 100n });
const entry = await contract.maps.tokenBalances("SP3FBR..."); // value or null
 
// Build an unsigned transaction for a wallet to sign later — never broadcasts.
// publicKey defaults to the client account; fee/nonce are auto-resolved when
// omitted (see ContractBuildCallOptions for fee/nonce/postConditions/sponsored).
const tx = await contract.buildCall.transfer(
  { to: "SP3FBR...", amount: 100n },
  { publicKey },
);

ABIs generated by sl codegen contracts are branded with TypedAbi, so all four namespaces surface the generated named type aliases in hovers and errors; hand-written as const ABIs get the same API via structural inference.

Wallet Actions

import { sendTransaction, transferStx, callContract } from "@secondlayer/stacks/actions";
 
const { txid } = await sendTransaction(client, { transaction: signedTx });
 
const txid = await transferStx(client, {
  recipient: "SP2J6...",
  amount: 1_000_000n,
});

Simulation

import { simulateCall, multicall } from "@secondlayer/stacks/actions";
 
// Dry-run a contract call
const result = await simulateCall(client, {
  contractAddress: "SP2J6...",
  contractName: "my-contract",
  functionName: "transfer",
  functionArgs: [Cl.uint(100)],
  sender: "SP3FBR...",
});
 
// Batch multiple reads
const results = await multicall(client, {
  calls: [
    { contractAddress: "SP2J6...", contractName: "token-a", functionName: "get-balance", functionArgs: [Cl.principal("SP3FBR...")] },
    { contractAddress: "SP2J6...", contractName: "token-b", functionName: "get-balance", functionArgs: [Cl.principal("SP3FBR...")] },
  ],
});