Library for PoX Stacking.
Note : Not all methods are available before the 2.1 fork. These will throw if used on a 2.1 chain.
npm pnpm yarn
npm install @stacks/stacking
Initialize a StackingClient
to interact with the Stacking contract.
Note: The StackingClient
sets its transactions AnchorMode
to Any
by default.
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
// the stacks STX address
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH' ;
const client = new StackingClient (address, network);
// a BTC address for reward payouts
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP' ;
// number cycles to stack
const cycles = 3 ;
// Refer to initialization section to create client instance
const stackingEligibility = await client. canStack ({ poxAddress, cycles });
// {
// eligible: false,
// reason: 'ERR_STACKING_INVALID_LOCK_PERIOD',
// }
// a BTC address for reward payouts
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP' ;
// number cycles to stack
const cycles = 3 ;
// how much to stack, in microSTX
const amountMicroStx = 100000000000 n ;
// private key for transaction signing
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
// block height at which to stack
const burnBlockHeight = 2000 ;
// signer key
const signerPrivateKey = makeRandomPrivKey ();
const signerKey = getPublicKeyFromPrivate (signerPrivateKey.data);
// Refer to initialization section to create client instance
const signerSignature = client. signPoxSignature ({
topic: 'stack-stx' ,
rewardCycle: await client. getPoxInfo ().reward_cycle_id,
poxAddress,
period: cycles,
signerPrivateKey,
});
const stackingResults = await client. stack ({
amountMicroStx,
poxAddress,
cycles,
privateKey,
burnBlockHeight,
signerKey,
signerSignature,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
Extends previously stacked funds without cooldown.
// number cycles to extend stacking by
const extendCycles = 3 ;
// a BTC address for reward payouts
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP' ;
// private key for transaction signing
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const extendResults = await client. stackExtend ({
extendCycles,
poxAddress,
privateKey,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
Increases the amount of funds stacked/locked after previously stacking.
// how much to increase by, in microSTX
const increaseBy = 3000000 ;
// private key for transaction signing
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const increaseResults = await client. stackIncrease ({
increaseBy,
privateKey,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
const stackingEnabledNextCycle = await client. isStackingEnabledNextCycle ();
// true / false
const cycleDuration = await client. getCycleDuration ();
// 120
const seconds = await client. getSecondsUntilStackingDeadline ();
// 600000
const hasMinStxAmount = await client. hasMinimumStx ();
// true / false
const poxInfo = await client. getPoxInfo ();
// {
// contract_id: 'ST000000000000000000002AMW42H.pox',
// first_burnchain_block_height: 0,
// min_amount_ustx: 83335083333333,
// prepare_cycle_length: 30,
// rejection_fraction: 3333333333333333,
// reward_cycle_id: 17,
// reward_cycle_length: 120,
// rejection_votes_left_required: 0,
// total_liquid_supply_ustx: 40000840000000000
// }
const coreInfo = await client. getCoreInfo ();
// {
// peer_version: 385875968,
// pox_consensus: 'bb88a6e6e65fa7c974d3f6e91a941d05cc3dff8e',
// burn_block_height: 2133,
// stable_pox_consensus: '2284451c3e623237def1f8caed1c11fa46b6f0cc',
// stable_burn_block_height: 2132,
// server_version: 'blockstack-core 0.0.1 => 23.0.0.0 (HEAD:a4deb7a+, release build, linux [x86_64])',
// network_id: 2147483648,
// parent_network_id: 3669344250,
// stacks_tip_height: 1797,
// stacks_tip: '016df36c6a154cb6114c469a28cc0ce8b415a7af0527f13f15e66e27aa480f94',
// stacks_tip_consensus_hash: 'bb88a6e6e65fa7c974d3f6e91a941d05cc3dff8e',
// unanchored_tip: '6b93d2c62fc07cf44302d4928211944d2debf476e5c71fb725fb298a037323cc',
// exit_at_block_height: null
// }
const responseBalanceInfo = await client. getAccountBalance ();
// 800000000000
const responseBalanceLockedInfo = await client. getAccountBalanceLocked ();
// 40000000000
const responseBalancesInfo = await client. getAccountExtendedBalances ();
// {
// stx: {
// balance: '1000000',
// total_sent: '0',
// total_received: '1000000',
// lock_tx_id: '0xec94e7d20af8979b44d17a0520c126bf742b999a0fc7ddbcbe0ab21b228ecc8c',
// locked: '50000',
// lock_height: 100,
// burnchain_lock_height: 100,
// burnchain_unlock_height: 200,
// },
// fungible_tokens: {},
// non_fungible_tokens: {},
// }
const stackingStatus = await client. getStatus ();
// {
// stacked: true,
// details: {
// first_reward_cycle: 18,
// lock_period: 10,
// unlock_height: 3020,
// pox_address: {
// version: '00',
// hashbytes: '05cf52a44bf3e6829b4f8c221cc675355bf83b7d'
// }
// }
// }
const poxOperationInfo = await client. getPoxOperationInfo ();
// {
// period: 'Period3',
// pox1: {
// contract_id: 'ST000000000000000000002AMW42H.pox',
// activation_burnchain_block_height: 0,
// first_reward_cycle_id: 0,
// },
// pox2: {
// contract_id: 'ST000000000000000000002AMW42H.pox-2',
// activation_burnchain_block_height: 120,
// first_reward_cycle_id: 25,
// },
// }
These are the methods for creating the required transactions for delegated stacking:
sequenceDiagram
User->>Stacks Blockchain: tx: `delegateStx`<br>Delegate funds to pool,<br>by pool's STX address
Stacks Blockchain-->Pool Operator: Monitored by
Pool Operator->>Stacks Blockchain: tx: `delegateStackStx`<br>Lock delegated funds
Pool Operator->>Stacks Blockchain: tx: `stackAggregationCommit`<br>Commit stacking for each cycle
If you are the account owner ("stacker"), you can delegate or revoke delegation rights.
// STX address of the pool/pool
const delegateTo = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H' ;
// burn height at which the delegation relationship should be revoked (optional)
const untilBurnBlockHeight = 5000 ;
// how much to stack, in microSTX
const amountMicroStx = 100000000000 n ;
// private key for transaction signing
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const delegetateResponse = await client. delegateStx ({
amountMicroStx,
delegateTo,
untilBurnBlockHeight, // optional
privateKey,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
// private key for transaction signing
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const revokeResponse = await client. revokeDelegateStx (privateKey);
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
If you are a pool operator (or wish to stack with someone else's funds), you can stack ("lock up") tokens for your users and commit to stacking participation for upcoming reward cycles.
These users need to first "delegate" some or all of their funds to you (the "delegator").
The following examples refer to the "delegator" as pool, but in practice a delegator can also stack for only single or few individuals.
Even a group of friends could stack together and share a multi-sig BTC wallet for payouts.
Stack STX, which have been previously delegated to the pool.
This step only locks the funds (partial stacking).
The pool operator will also need to "commit" to a reward cycle.
import { getNonce } from '@stacks/transactions' ;
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
// the stacks STX address
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH' ;
// pools would initiate a different client
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74' ;
// pool private key for transaction signing
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
// the BTC address for reward payouts
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z' ;
// how much to stack, in microSTX
const amountMicroStx = 100000000000 n ;
// block height at which to stack
const burnBlockHeight = 2000 ;
// number cycles to stack
const cycles = 3 ;
// if you call this method multiple times in the same block, you need to increase the nonce manually
let nonce = await getNonce (poolAddress, network);
nonce = nonce + 1 n ;
const poolClient = new StackingClient (poolAddress, network);
const delegetateStackResponses = await poolClient. delegateStackStx ({
stacker: address,
amountMicroStx,
poxAddress: poolBtcAddress,
burnBlockHeight,
cycles,
privateKey: poolPrivateKey,
nonce, // optional
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
Extend stacking of STX previously delegated to the pool.
import { getNonce } from '@stacks/transactions' ;
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
// the stacks STX address
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH' ;
// pools would initiate a different client
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74' ;
// pool private key for transaction signing
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
// the BTC address for reward payouts
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z' ;
// number of cycles to extend by
const extendCount = 3 ;
// if you call this method multiple times in the same block, you need to increase the nonce manually
let nonce = await getNonce (poolAddress, network);
nonce = nonce + 1 n ;
const poolClient = new StackingClient (poolAddress, network);
const delegetateExtendResponses = await poolClient. delegateStackExtend ({
extendCount,
stacker: address,
poxAddress: poolBtcAddress,
privateKey: poolPrivateKey,
nonce, // optional
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
Increase the locked amount of delegated STX stacked.
import { getNonce } from '@stacks/transactions' ;
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
// the stacks STX address
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH' ;
// pools would initiate a different client
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74' ;
// pool private key for transaction signing
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
// the BTC address for reward payouts
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z' ;
// amount to increase by, in microSTX
const increaseBy = 3 ;
// if you call this method multiple times in the same block, you need to increase the nonce manually
let nonce = await getNonce (poolAddress, network);
nonce = nonce + 1 n ;
const poolClient = new StackingClient (poolAddress, network);
const delegetateIncreaseResponses = await poolClient. delegateStackIncrease ({
increaseBy,
stacker: address,
poxAddress: poolBtcAddress,
privateKey: poolPrivateKey,
nonce, // optional
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
The result of this commit transaction will contain the index of the pools reward set entry.
// reward cycle id to commit to
const rewardCycle = 12 ;
// the BTC address for reward payouts
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z' ;
// Private key
const privateKeyDelegate = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const delegetateCommitResponse = await poolClient. stackAggregationCommitIndexed ({
rewardCycle,
poxAddress: poolBtcAddress,
privateKey: privateKeyDelegate,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
Increase partially stacked STX via the index of the reward set entry.
// reward cycle id to commit to
const rewardCycle = 12 ;
// reward set entry index
const rewardIndex = 3 ;
// the BTC address for reward payouts
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z' ;
// Private key
const privateKeyDelegate = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001' ;
const delegetateIncreaseResponse = await poolClient. stackAggregationIncrease ({
rewardCycle,
rewardIndex,
poxAddress: poolBtcAddress,
privateKey: privateKeyDelegate,
});
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
const client = new StackingClient (address, network);
const options = { limit: 2 , offset: 0 };
const rewards = await client. getRewardsForBtcAddress (options);
// {
// limit: 2,
// offset: 0,
// results: [
// {
// canonical: true,
// burn_block_hash: '0x000000000000002083ca8303a2262d09a824cecb34b78f13a04787e4f05441d3',
// burn_block_height: 2004622,
// burn_amount: '0',
// reward_recipient: 'myfTfju9XSMRusaY2qTitSEMSchsWRA441',
// reward_amount: '20000',
// reward_index: 0
// },
// {
// canonical: true,
// burn_block_hash: '0x000000000000002f72213de621f9daf60d76aed3902a811561d06373b2fa6123',
// burn_block_height: 2004621,
// burn_amount: '0',
// reward_recipient: 'myfTfju9XSMRusaY2qTitSEMSchsWRA441',
// reward_amount: '20000',
// reward_index: 0
// }
// ]
// };
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
const client = new StackingClient (address, network);
const total = await client. getRewardsTotalForBtcAddress ();
// {
// reward_recipient: 'myfTfju9XSMRusaY2qTitSEMSchsWRA441',
// reward_amount: '0'
// }
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
const client = new StackingClient (address, network);
const options = { limit: 2 , offset: 0 };
const rewardHolders = await client. getRewardHoldersForBtcAddress (options);
// {
// limit: 2,
// offset: 0,
// total: 46,
// results: [
// {
// canonical: true,
// burn_block_hash: '0x000000000000002083ca8303a2262d09a824cecb34b78f13a04787e4f05441d3',
// burn_block_height: 2004622,
// address: 'myfTfju9XSMRusaY2qTitSEMSchsWRA441',
// slot_index: 1
// },
// {
// canonical: true,
// burn_block_hash: '0x000000000000002083ca8303a2262d09a824cecb34b78f13a04787e4f05441d3',
// burn_block_height: 2004622,
// address: 'myfTfju9XSMRusaY2qTitSEMSchsWRA441',
// slot_index: 0
// }
// ]
// };
import { StacksTestnet, StacksMainnet } from '@stacks/network' ;
import { StackingClient } from '@stacks/stacking' ;
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441' ;
// for mainnet: const network = new StacksMainnet();
const network = new StacksTestnet ();
const client = new StackingClient (address, network);
const rewardSetItem = await client. getRewardSet ({
rewardCyleId: 49 ,
rewardSetIndex: 3 ,
});
// {
// pox_address: {
// version: 0,
// hashbytes: [ 67, 89, 107, 83, 134, 244, 102, 134, 62, 37, 101, 141, 223, 148, 189, 15, 173, 171, 0, 72 ]
// },
// total_ustx: 1875230000000000
// }