Skip to content

feat: Add bitcoin-staking package#1854

Draft
jannik-stacks wants to merge 6 commits intomainfrom
feat/bitcoin-staking
Draft

feat: Add bitcoin-staking package#1854
jannik-stacks wants to merge 6 commits intomainfrom
feat/bitcoin-staking

Conversation

@jannik-stacks
Copy link
Copy Markdown
Collaborator

@jannik-stacks jannik-stacks commented Apr 9, 2026

wip 🚧

Add @stacks/bitcoin-staking package for PoX-5 staking flows

Summary

This PR adds a new @stacks/bitcoin-staking package with client-side helpers for PoX-5 staking flows. It covers unsigned transaction builders, SIP-018 signer authorization helpers, BTC address and PoX tuple conversion, locking script utilities, and read helpers for PoX/staker state.

It also wires the package into the monorepo TypeScript paths and Typedoc entrypoints.

What Changed

  • adds a new packages/bitcoin-staking workspace package and exports its public API from src/index.ts
  • adds unsigned builders for solo staking, pooled staking, signer key grants, signer key revocation, and pool registration
  • adds signing and verification helpers for PoX-5 signer authorizations and signer key grants
  • adds a BtcAddress namespace for parsing/stringifying BTC addresses and converting them to PoX tuples
  • adds helpers for building default unlock scripts, PoX-5 locking scripts, P2WSH addresses, and unlock heights
  • adds fetch helpers for /v2/pox, get-staker-info, and get-staker-in-cycle

Example Flow

import {
  buildDefaultUnlockScript,
  buildLockingScript,
  buildStakeTx,
  computeUnlockHeight,
  fetchPoxInfo,
  lockingScriptToP2wsh,
  Pox5SignatureTopic,
  signPox5Authorization,
} from '@stacks/bitcoin-staking';

async function createSoloStake() {
  const network = 'testnet';
  const stxAddress = 'ST000000000000000000002AMW42H';
  const publicKey = '025c8f3e1f0d4e6d0d0dcb3e7f9b2b7e4d6e2a4c9f87d3813b4a9b1c2d3e4f5a6b';
  const privateKey = 'your-private-key';
  const poxAddress = 'tb1qexampleaddresshere';
  const signerKey = publicKey;
  const amountUstx = 100_000_000n;
  const maxAmount = amountUstx;
  const authId = 1n;
  const numCycles = 1;

  const pox = await fetchPoxInfo({ network });

  const unlockBytes = buildDefaultUnlockScript(publicKey);
  const unlockHeight = computeUnlockHeight({
    firstBurnchainBlockHeight: pox.firstBurnchainBlockHeight,
    rewardCycleLength: pox.rewardCycleLength,
    firstRewardCycle: pox.rewardCycleId + 1,
    numCycles,
  });

  const lockingScript = buildLockingScript({
    stxAddress,
    unlockHeight,
    unlockBytes,
  });

  const lockingAddress = lockingScriptToP2wsh(lockingScript, network);

  const signerSignature = signPox5Authorization({
    topic: Pox5SignatureTopic.Stake,
    poxAddress,
    rewardCycle: pox.rewardCycleId + 1,
    period: numCycles,
    maxAmount,
    authId,
    network,
    privateKey,
  });

  const unsignedTx = await buildStakeTx({
    publicKey,
    fee: 1000,
    nonce: 0,
    network,
    amountUstx,
    poxAddress,
    signerKey,
    signerSignature,
    maxAmount,
    authId,
    numCycles,
    unlockBytes,
    startBurnHt: pox.currentBurnchainBlockHeight,
  });

  return {
    lockingAddress,
    unsignedTx,
  };
}

Follow-ups

  • replace the placeholder POX_5_CONTRACT constant with the final deployed contract identifier
  • add broader test coverage for builders, signer helpers, address conversion, and fetch parsing

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 13, 2026

Codecov Report

❌ Patch coverage is 94.00000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/bitcoin-staking/src/network.ts 50.00% 1 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants