Stacks SDK
Build, sign, and broadcast Stacks transactions from TypeScript. Where @secondlayer/sdk reads decoded chain data, @secondlayer/stacks writes to the chain.
bun add @secondlayer/stacksimport { createWalletClient, http, mainnet } from "@secondlayer/stacks";
import { privateKeyToAccount } from "@secondlayer/stacks/accounts";
import { Cl } from "@secondlayer/stacks/clarity";
const client = createWalletClient({
account: privateKeyToAccount(process.env.KEY!),
chain: mainnet,
transport: http(),
});
const txid = await client.callContract({
contractAddress: "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9",
contractName: "usda-token",
functionName: "transfer",
functionArgs: [
Cl.uint(1_000_000),
Cl.principal(client.account.address),
Cl.principal("SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7"),
Cl.none(),
],
});Fees estimated, nonces managed.
Same getContract client against an in-process Clarinet VM (@stacks/clarinet-sdk is an optional peer of @secondlayer/stacks/simnet).
import { initSimnet } from "@stacks/clarinet-sdk";
import { createPublicClient } from "@secondlayer/stacks";
import { getContract } from "@secondlayer/stacks/actions";
import { SIP010_ABI } from "@secondlayer/stacks/clarity";
import { simnet, simnetChain } from "@secondlayer/stacks/simnet";
const session = await initSimnet("./Clarinet.toml");
const client = createPublicClient({
chain: simnetChain,
transport: simnet(session),
});
const deployer = session.getAccounts().get("deployer")!;
const token = getContract({
client,
address: deployer,
name: "usda-token",
abi: SIP010_ABI,
});
await token.read.getBalance({ account: deployer });| Limit | Behavior |
|---|---|
/extended | not served |
| watches | throw SimnetUnsupportedError |
| fees | NoEstimateAvailable; wallet actions use 'min' |
On-chain assertions the network enforces: move more than you allowed and the transaction aborts instead of settling. Attach postConditions to any callContract, transferStx, or deployContract call (mode defaults to deny).
import { Pc } from "@secondlayer/stacks/postconditions";
const txid = await client.callContract({
// …call as above
postConditions: [
Pc.origin()
.willSendLte(1_000_000)
.ft("SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.usda-token", "usda"),
],
});Or pass plain objects, the same shape stacks.js uses, portable between SDKs:
postConditions: [
{
type: "stx-postcondition",
address: "origin",
condition: "lte",
amount: 1_000_000,
},
]| Type | Protects | Condition |
|---|---|---|
stx-postcondition | STX movement | eq, gt, gte, lt, lte + amount |
ft-postcondition | SIP-010 token movement | same, scoped to asset |
nft-postcondition | SIP-009 asset movement | sent, not-sent + assetId |
staking-postcondition | How much STX a tx may stake or re-parameterize (SIP-045): stake, register-for-bond, stake-update | eq–lte + amount |
pox-postcondition | Non-locking PoX state changes (SIP-045): unstake, announce-l1-early-exit, bond-registration updates | will-not-perform, may-perform, will-perform |
SIP-045 staking post-conditions
Bound a pox-5 call from both sides:
const txid = await client.callContract({
contractAddress: "SP000000000000000000002Q6VF78",
contractName: "pox-5",
functionName: "register-for-bond",
functionArgs: [/* … */],
postConditions: [
{
type: "staking-postcondition",
address: "origin",
condition: "lte",
amount: 500_000_000_000n,
},
{
type: "pox-postcondition",
address: "origin",
condition: "will-not-perform",
},
],
});Activates at Epoch 4.0
Wire format ships in @secondlayer/stacks@2.10.0; the network accepts these only after SIP-045 activates.
Decoding is strict: deserializing a transaction with an unknown post-condition type throws instead of silently misreading the bytes after it. Your indexer sees an error, never corrupt data.
The pox-5 calls these guard have their own typed surface; see PoX-5 Bitcoin Staking.
- Typed contracts:
getContract()binds an ABI to a client for typed reads (read.*), broadcasts (call.*), unsigned wallet-signs-later transactions (buildCall.*), and map lookups (maps.*); generate branded ABIs withsecondlayer codegen contractsfor named-alias hovers. - Clarity values:
Cl.*constructors and decoding via@secondlayer/stacks/clarity. - Bitcoin SPV: merkle proofs and Bitcoin payment verification, see Bitcoin SPV.
- PoX-5 staking: SIP-045 bonds, staking, L1 lockup scripts, signer grants, see PoX-5 Bitcoin Staking.
- Accounts:
privateKeyToAccount,mnemonicToAccount, WalletConnect providers via@secondlayer/stacks/accountsand@secondlayer/stacks/connect. - Multi-sig:
createMultiSigClientfor m-of-n signing flows.