@stacks/stacking
Library for PoX Stacking.
Installation
npm install @stacks/stacking
Initialization
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';
const network = new StacksTestnet();
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH';
const client = new StackingClient(address, network);
Signing
Coming with epoch 2.5 and Nakamoto, stackers are required to include signer signatures for their stacking transactions.
Signers need to be run along side Stacks nodes to participate in the PoX consensus.
The StackingClient
provides a helper method to create a signer signature for a stacking transaction.
const currentCycle = 83;
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP';
const cycles = 3;
const maxAmount = 100000000000n;
const authId = 702;
const signerPrivateKey = makeRandomPrivKey();
const signature = client.signPoxSignature({
topic: 'stack-stx',
poxAddress,
rewardCycle: currentCycle,
period: cycles,
maxAmount,
authId,
signerPrivateKey,
});
[!WARNING]
Make sure to replace signerPrivateKey
with the signer private key of your setup and keep it private.
Topics
Signatures include a topic, so they can only be used for the intended transaction type. The following topics are available:
stack-stx
stack-extend
stack-increase
agg-commit
agg-increase
Stack STX
Check stacking eligibility
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP';
const cycles = 3;
const stackingEligibility = await client.canStack({ poxAddress, cycles });
Build and broadcast a solo-stacking transaction
[!NOTE]
For the cycles given, the signer-key cannot be changed.
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP';
const cycles = 3;
const amountMicroStx = 100000000000n;
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const burnBlockHeight = 2000;
const maxAmountMicroStx = amountMicroStx;
const authId = 3;
const signerPrivateKey = makeRandomPrivKey();
const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data);
const signerSignature = client.signPoxSignature({
topic: 'stack-stx',
rewardCycle: await client.getPoxInfo().reward_cycle_id,
poxAddress,
period: cycles,
maxAmount,
authId,
signerPrivateKey,
});
const stackingResult = await client.stack({
amountMicroStx,
poxAddress,
cycles,
burnBlockHeight,
signerKey,
signerSignature,
maxAmountMicroStx,
authId,
privateKey,
});
Extend stacking
Extends previously stacked funds without cooldown.
const extendCycles = 3;
const poxAddress = 'mvuYDknzDtPgGqm2GnbAbmGMLwiyW3AwFP';
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const maxAmountMicroStx = 100000000000n;
const authId = 4;
const signerPrivateKey = makeRandomPrivKey();
const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data);
const signerSignature = client.signPoxSignature({
topic: 'stack-extend',
poxAddress,
period: extendCycles,
maxAmount,
authId,
signerPrivateKey,
});
const extendResult = await client.stackExtend({
extendCycles,
poxAddress,
signerKey,
signerSignature,
maxAmount,
authId,
privateKey,
});
Increase amount stacked
Increases the amount of funds stacked/locked after previously stacking.
const increaseBy = 3000000;
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const maxAmount = increaseBy;
const authId = 5;
const signerPrivateKey = makeRandomPrivKey();
const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data);
const signerSignature = client.signPoxSignature({
topic: 'stack-increase',
poxAddress,
period: extendCycles,
maxAmount,
authId,
signerPrivateKey,
});
const increaseResults = await client.stackIncrease({
increaseBy,
signerKey,
signerSignature,
maxAmount,
authId,
privateKey,
});
Client helpers
Will Stacking be executed in the next cycle?
const stackingEnabledNextCycle = await client.isStackingEnabledNextCycle();
How long (in seconds) is a Stacking cycle?
const cycleDuration = await client.getCycleDuration();
How much estimated time is left (in seconds) to submit a stacking transaction for the upcoming reward cycle?
const seconds = await client.getSecondsUntilStackingDeadline();
Does account have sufficient STX to meet minimum threshold?
const hasMinStxAmount = await client.hasMinimumStx();
Get PoX info
const poxInfo = await client.getPoxInfo();
Get Stacks node info
const coreInfo = await client.getCoreInfo();
Get account balance
const responseBalanceInfo = await client.getAccountBalance();
Get account balance locked
const responseBalanceLockedInfo = await client.getAccountBalanceLocked();
Get account balances (from API)
const responseBalancesInfo = await client.getAccountExtendedBalances();
Get account stacking status
const stackingStatus = await client.getStatus();
Get PoX operation info (current period and PoX contract versions)
const poxOperationInfo = await client.getPoxOperationInfo();
Delegated stacking
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
Stacking in a pool
If you are the account owner ("stacker"), you can delegate or revoke delegation rights.
Delegate STX
const delegateTo = 'ST2MCYPWTFMD2MGR5YY695EJG0G1R4J2BTJPRGM7H';
const untilBurnBlockHeight = 5000;
const amountMicroStx = 100000000000n;
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const delegetateResponse = await client.delegateStx({
amountMicroStx,
delegateTo,
untilBurnBlockHeight,
privateKey,
});
Revoke delegation
const privateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const revokeResponse = await client.revokeDelegateStx(privateKey);
Operating a pool / Stacking for others
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 "pool").
The following examples refer to the "delegate" 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 delegated STX
Stack STX, which have been previously delegated to the pool.
This step only locks the funds (partial stacking) and doesn't require a signer-key/signature yet.
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';
const network = new StacksTestnet();
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH';
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74';
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z';
const amountMicroStx = 100000000000n;
const burnBlockHeight = 2000;
const cycles = 3;
const nonce = await getNonce(poolAddress, network);
const poolClient = new StackingClient(poolAddress, network);
const delegetateStackResponses = await poolClient.delegateStackStx({
stacker: address,
amountMicroStx,
poxAddress: poolBtcAddress,
burnBlockHeight,
cycles,
privateKey: poolPrivateKey,
nonce,
});
Extend delegated STX
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';
const network = new StacksTestnet();
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH';
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74';
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z';
const extendCount = 3;
const nonce = await getNonce(poolAddress, network);
const poolClient = new StackingClient(poolAddress, network);
const delegetateExtendResponses = await poolClient.delegateStackExtend({
extendCount,
stacker: address,
poxAddress: poolBtcAddress,
privateKey: poolPrivateKey,
nonce,
});
Increase delegated STX
Increase the locked amount of delegated STX stacked.
import { getNonce } from '@stacks/transactions';
import { StacksTestnet, StacksMainnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';
const network = new StacksTestnet();
const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH';
const poolAddress = 'ST22X605P0QX2BJC3NXEENXDPFCNJPHE02DTX5V74';
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z';
const increaseBy = 3;
const nonce = await getNonce(poolAddress, network);
const poolClient = new StackingClient(poolAddress, network);
const delegetateIncreaseResponses = await poolClient.delegateStackIncrease({
increaseBy,
stacker: address,
poxAddress: poolBtcAddress,
privateKey: poolPrivateKey,
nonce,
});
Commit to stacking
The result of this commit transaction will contain the index of the pools reward set entry.
const rewardCycle = 12;
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z';
const poolPrivateKey = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const maxAmountMicroStx = 100000000000n;
const authId = 7;
const signerPrivateKey = makeRandomPrivKey();
const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data);
const signerSignature = poolClient.signPoxSignature({
topic: 'agg-commit',
poxAddress: poolBtcAddress,
rewardCycle,
period: 1,
maxAmount,
authId,
signerPrivateKey,
});
const delegetateCommitResponse = await poolClient.stackAggregationCommitIndexed({
poxAddress: poolBtcAddress,
rewardCycle,
signerKey,
signerSignature,
maxAmount,
authId,
privateKey: poolPrivateKey,
});
Increase existing commitment
Increase partially stacked STX via the index of the reward set entry.
const rewardCycle = 12;
const rewardIndex = 3;
const poolBtcAddress = 'msiYwJCvXEzjgq6hDwD9ueBka6MTfN962Z';
const privateKeyDelegate = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const maxAmountMicroStx = 100000000000n;
const authId = 8;
const signerPrivateKey = makeRandomPrivKey();
const signerKey = getPublicKeyFromPrivate(signerPrivateKey.data);
const signerSignature = poolClient.signPoxSignature({
topic: 'agg-increase',
poxAddress: poolBtcAddress,
rewardCycle,
period: 1,
maxAmount,
authId,
signerPrivateKey,
});
const delegetateIncreaseResponse = await poolClient.stackAggregationIncrease({
poxAddress: poolBtcAddress,
rewardCycle,
rewardIndex,
signerKey,
signerSignature,
maxAmount,
authId,
privateKey: privateKeyDelegate,
});
Pool helpers
Get burnchain rewards
import { StacksTestnet, StacksMainnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441';
const network = new StacksTestnet();
const client = new StackingClient(address, network);
const options = { limit: 2, offset: 0 };
const rewards = await client.getRewardsForBtcAddress(options);
Get burnchain rewards total
import { StacksTestnet, StacksMainnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441';
const network = new StacksTestnet();
const client = new StackingClient(address, network);
const total = await client.getRewardsTotalForBtcAddress();
Get burnchain reward holders
import { StacksTestnet, StacksMainnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441';
const network = new StacksTestnet();
const client = new StackingClient(address, network);
const options = { limit: 2, offset: 0 };
const rewardHolders = await client.getRewardHoldersForBtcAddress(options);
Get reward set by index
import { StacksTestnet, StacksMainnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';
const address = 'myfTfju9XSMRusaY2qTitSEMSchsWRA441';
const network = new StacksTestnet();
const client = new StackingClient(address, network);
const rewardSetItem = await client.getRewardSet({
rewardCyleId: 49,
rewardSetIndex: 3,
});