Operate a Pool
This doc assumes you are familiar with stacking at a conceptual level. If not, you may want to read the Stacking concept guide.
The guide below applies to those who want to operate a pool, meaning they want to have stackers delegate STX tokens to them. If you choose to operate a pool you will either need to run your own signer or collaborate with one.
Pool Operator Stacking Flow
For pool operators, the flow is a bit different than solo stacking. Remember that as a pool operator, other stackers are delegating their STX to you to stack on behalf of them. This additional role adds a couple of extra steps to your stacking flow if operating as a pool operator.
Similar to the changes to solo Stacking, the big difference for delegation flows is the inclusion of signer keys and signatures. Because pool operators need to make transactions to “finalize” a delegation, these new arguments add new complexities to the stacking flow.
Delegator initiates delegation
This step is not required to apply to pool operators/signers. It is included here to illustrate the end-to-end flow, but if you are operating as a pool operator/signer you will not have to perform this step. Instead, users delegate their STX to you as the pool operator.
The first step, where the delegator sets up their delegation to a pool operator, is to call delegate-stx
. This function does not directly delegate the STX, but rather allows the pool operator to issue the stacking lock on behalf of the user calling this function. You can think of calling this function as the delegator giving permission to the pool operator to stack on their behalf.
The arguments here are unchanged from previous versions of PoX:
Amount: Denoted in uSTX (1 STX = 1,000,000 uSTX)
Delegate to: the STX address of the pool operator they're delegating to. Note that this is different from the “signer key” used. Instead, this is the STX address that is used to make PoX transactions.
Until burn height: an optional argument representing the BTC block height when the delegation expires. If none is used, the delegation permission expires only when explicitly revoked.
Pox Address: an optional BTC address that, if specified, the signer must use to accept this delegation
Pool operator “activates” the delegation
Just as in the older PoX contract, after a delegator calls the delegate-stx
function, the pool operator calls delegate-stack-stx
to commit the delegator’s STX.
The arguments are:
Stacker: the STX address of the delegator
Amount: denoted in uSTX (1 STX = 1,000,000 uSTX)
Pox Address: The BTC address of the pool operator where they will receive the BTC rewards. If the delegator has set his own BTC address in the
delegate-stx
call, this address will have to be the same one, otherwise the contract call will fail.Start burn height: The BTC block height in which delegation can begin. This field is used to ensure that an old transaction intended for an earlier cycle will fail, and also prevents callers from "post-dating" the call to a future cycle. The best option here is to add 1 or 2 to the current BTC block height when you initiate this transaction. Note that the delegation will not actively be stacked at this block height, but whatever reward cycle is passed in the aggregation commit function (explained below).
Lock period: number of cycles to lock for. If the delegator provided the until burn height argument, then the end of these cycles cannot be past the expiration provided. The maximum lock period that a pool operator can provide in this function call is 12.
This step also allows the pool operator to proactively choose which Stackers they’ll accept delegation from. For “closed” pools, the pool operator will only call this function for approved Stackers. It is up to each pool operator who runs a closed pool to implement this process.
This step can be repeated for multiple Stackers before going to the next step.
If you look at the function source code, you'll see that the delegate-stack-stx
function sets the stacker's first reward cycle to be the next reward cycle.
When generating your signature and your stack-aggregation-commit-indexed
transaction, you'll want to make sure that the reward cycles match.
So if you are in cycle 557 when you call the delegate-stack-stx
function, you'll want to pass in cycle 558 or higher when you generate your signature and your stack-aggregation-commit-indexed
transaction.
With stack-aggregation-commit-indexed
, the reward-cycle
arg is saying "I'm commiting these stacks to be stacked in cycle N". But the delegate-stack-stx
transaction gets you setup for next cycles, aka 558 and higher.
Also make sure that, when you generate your signature, you use 558 or higher as the reward cycle. In solo stacking methods, you use the current reward cycle in the signature, but not for stack-aggregation-commit-indexed
. This is because with stack-aggregation-commit-indexed
you can commit stacks for future cycles, not just the N+1 cycle.
Pool operator “commits” delegated STX
The next step is for the pool operator to call stack-aggregation-commit-indexed
.
In the contract source code, you'll notice a similarly named function called stack-aggregation-commit
. This is a legacy function that makes it difficult to increase the stacking amount, as it does not return the reward index of the stacking slot, which is required in order to call the stack-aggregation-increase
function. We recommend using stack-aggregation-commit-indexed
.
At this point, the STX are committed to the pool operator, and the pool operator has some “aggregate balance” of committed STX. The pool operator is not actually eligible for reward slots and signer initialization until this step is finished.
The pool operator cannot call this function until the total number of STX committed is larger than the minimum threshold required to Stack. This minimum stacking threshold is a function of the total number of STX stacked divided by the available number of reward slots.
This number varies and can be found by visiting the pox endpoint of Hiro's API at https://api.hiro.so/v2/pox and looking at the min_threshold_ustx
field. (1 STX = 1,000,000 uSTX).
Once the threshold is reached, the pool operator calls stack-aggregation-commit-indexed
. This is the point where you as the pool operator must provide your signer key and signer key signature. The arguments are:
Pox Address: the BTC address to receive rewards
Reward-cycle: a reward cycle in the future (see the note above on passing the correct reward cycle)
Signer public key: the public key of your signer (remember that this may be different than the address you are using to operate the pool, but this step is how you associate the two together)
Signer signature: A signature proving control of your signing key (details on how to do this are below)
Max Amount: This parameter is used to validate the signer signature provided. It represents the maximum number of uSTX (1 stx = 1,000,000 uSTX) that can be stacked in this transaction.
Auth Id: This parameter is used to validate the signer signature provided. It is a random integer that prevents the re-use of this particular signer signature.
In the Definitions and Roles section in the previous document, we described how the pool operator and signer may be the same entity, but not necessarily have the same address.
Signers who are also pool operators and wish to have STX delegated to them should have a separate keychain associated with their pool operator account in order to make Stacking transactions such as delegate-stack-stx
and stack-aggregation-commit-indexed
.
So, as a signing entity operating a pool, you should have two accounts:
Your pool operator account, which you will use to conduct all of the stacking operations we have covered here.
Your signer account, which is what you used to set up your signer. This signer public key is what you will pass in to the above aggregation commit function, and is also the key you will use when generating your signer signature.
If you are operating as a signer and a pool operator, you should have a separate key because you might need to rotate your signer key when necessary.
The PoX contract is designed to support rotating the signer key without needing your Stackers to un-stack and re-stack later. You cannot rotate a pool operator key without needing to wait for all delegated Stackers to un-stack and finally re-stack to the new pool operator address.
Because of this limitation of being unable to rotate pool operator addresses, it’s highly recommended to have a separate pool operator key. The benefit of a separate pool operator key is that it can easily be used in existing wallets, including hardware wallets.
Once this succeeds, the signer is eligible for reward slots. The number of reward slots depends on the amount of STX committed to this signer. Even if the signer commits more than the “global minimum”, the minimum amount to receive a slot depends on the amount of STX locked for each cycle.
To act as a signer, each step up to this point must be taken before the prepare phase of the next cycle begins. It is crucial that the signer software is running.
Pool operator increases amount committed
Even after the signer commits to a certain amount of STX in the previous step, the signer can increase this amount once more delegations are received. The initial steps must be taken for each Stacker (delegate-stx
and then delegate-stack-stx
), and then stack-aggregation-increase
can be called with the index returned from the first stack-aggregation-commit-indexed
call and a new signature.
Step by Step Stacking Guide
Now that you are familiar with the overall stacking flow and the different roles played, let's dive into the step-by-step guide for actually conducting the stacking process as a pool operator.
There are several ways you can go about stacking. This guide will cover using Leather Earn, which is a stacking web application and the simplest option.
Additionally, you can choose to call the stacking functions directly from the deployed contract in the explorer.
The fields and process will be the same, but the UI will differ.
Finally, you can stack using JS and the @stacks/stacking package if you prefer. Again, the functions and parameters will be the same, you will just be writing your JS code directly instead of using a UI.
If you are interested in using this method, you'll want to follow the stacking guide created by Hiro, and be sure to integrate the new parameters described in Hiro's Nakamoto update doc.
Step 1: Run or work with a signer
This is a necessary prerequisite to stacking as a pool operator. You will either need to run your own signer or work with one and have them conduct step 2 on your behalf and give you their signer signature.
Running a signer involves setting up a hosted production environment that includes both a Stacks Node and the Stacks Signer. For more information, refer to the running a signer doc.
Once the signer software is running, you'll to keep track of the stacks_private_key
that you used when configuring your signer software. This will be used in subsequent Stacking transactions.
In the note above about pool operator vs signer keys, this corresponds to your signer key, not your pool operator wallet
Step 2: Generate a signer key signature
Overview of signer keys and signatures
The main difference with Stacking in Nakamoto is that the Signer needs to include new parameters in their Stacking transactions. These are Clarity transactions that pool operators will call when interacting with the pox-4.clar
contract. Interacting with that contract is how you as a pool operator actually stack the delegated STX tokens.
The current pox-4 contract address can be found by visiting the following endpoint of the Hiro API: https://api.hiro.so/v2/pox.
You can then visit the Stacks Explorer to view the contract and pass in the contract id.
You may want to review this contract to familiarize yourself with it.
Here is an overview of the new fields you will need to pass in some of your stacking transactions:
signer-key
: the public key that corresponds to thestacks_private_key
your signer is using.signer-signature
: a signature that demonstrates that you actually control yoursigner-key
. Because signer keys need to be unique, this is also a safety check to ensure that other Stackers can’t use someone else’s signer key.max-amount
: The maximum amount of uSTX (1 STX = 1,000,000 uSTX) that can be locked in the transaction that uses this signature. For example, if callingstack-aggregation-increase
, then this parameter dictates the maximum amount of uSTX that can be used to add more locked STX to the already committed position.auth-id
: a random integer that prevents re-use of a particular signature, similar to how nonces are used with transactions.
Signer signatures are signatures created using a particular signer key. They demonstrate that the controller of that signer key is allowing a Stacker to use their signer's public key. The signer signature’s message hash is created using the following data:
method
: a string that indicates the Stacking method that is allowed to utilize this signature. The valid options arestack-stx
,stack-extend
,stack-increase
,agg-commit
(forstack-aggregation-commit-indexed
) andagg-increase
(forstack-aggregation-increase
).max-amount
: described above.auth-id
: described above.period
: a value between 1 and 12, which indicates how long the Stacker is allowed to lock their STX for in this particular Stacking transaction. Foragg-commit
, this must be equal to 1.reward-cycle
: This represents the reward cycle in which the Stacking transaction can be confirmed (forstack-aggregation-commit-indexed
, this has to be set to 1).pox-address
: The Bitcoin address that is allowed to be used for receiving rewards. This corresponds to the Bitcoin address associated with your signerconfig
: This represents the signer configuration file path where thestacks_private_key
is located, and it is used for signing the generated signature.
Now that we have an overview of role and contents of signatures, let's see how to actually generate them. You have several options available.
Generating your signature with Degen Lab's stacks.tools
Degen Lab has a signature generation tool that will generate a signature using their signer. This is the quickest and simplest option. To generate a signature using this method, all you need to do is visit their signature tool and pass in the relevant information as covered on the page.
Generating your signature with stacks.js
The @stacks/stacking NPM package provides interfaces to generate and use signer signatures.
There is a function called signPoxSignature
that will allow you to generate this signature and pass it in to the stacking function.
More information and code samples can be found on Hiro's Nakamoto docs.
Generating your signature using the stacks-signer CLI
If you already have your signer configured and set up, you can use the stacks-signer
CLI to generate this signature. You can either SSH into your running signer or use the stacks-signer
CLI locally. If using the CLI locally, you will need to ensure you have a matching configuration file located on your filesystem. Having a matching configuration file is important to ensure that the signer public key you make in Stacking transactions is the same as in your hosted signer.
The CLI command is:
These arguments match those described in section Overview of signer keys and signatures, with the addition of:
--json
, to optionally output the resulting signature in JSON.
You can use the following command to generate a random 32
bit integer as auth-id
:
Once the generate-stacking-signature
command is run, the CLI will output a JSON:
You will use the JSON when calling Stacking transactions from your pool operator address as outlined above. Remember that this may be different than your signer address.
Generating your signature with Leather Earn
Leather Earn is a web application that provides an easy-to-use interface for stacking and generating signatures. We'll cover using Leather Earn for stacking at the end of this document, here we will cover how to use it to generate a signature.
At the time of writing, this has only been tested using the Leather wallet.
You can visit earn.leather.io to generate a signer key signature. Make sure you’re connected to the correct network. To generate a signer key signature, it’s important that you’ve logged in Leather with the same secret key that was used to generate your signer key, not the account that will serve as your pool operator address. Once you’ve setup that account on Leather, you can log in to Leather Earn. Click the link “Signer key signature” at the bottom of the page. This will open the “generate a signer key signature” page.
The fields are:
Reward cycle:
For all solo stacking transactions, this must equal the current reward cycle, not the cycle in which they will start stacking. The field defaults to the current reward cycle.
For stack-aggregation-commit-indexed, this field must equal the cycle used in that function’s “reward cycle” argument. Typically, that equates to current_cycle + 1.
Bitcoin address: the PoX reward address that can be used
Topic: the stacking function that will use this signature
Max amount: max amount of STX that can be used. Defaults to “max possible amount”
Auth ID: defaults to random int
Duration: must match the number of cycles used in the stacking transaction. For stack-aggregation-commit-indexed, use “1”.
Each of these fields must be exactly matched in order for the Stacking transaction to work. Future updates to Leather Earn will verify the signature before the transaction is made.
Click the “generate signature” button to popup a Leather page where you can generate the signature. Once you submit that popup, Leather Earn will have the signer key and signature you generated.
After you sign that message, you'll see the information you can use in your Stacking transactions, including the signer public key and signature.
You can click the “copy” icon next to “signer details to share with stackers”. This will copy a JSON string, which can be directly pasted into the Leather Earn page where you make your Stacking transaction. Alternatively, this information can be entered manually.
We'll cover the Leather Earn pages for actually making those transactions in the next section of this document.
Using a hardware or software wallet to generate signatures
When the signer is configured with a stacks_private_key
, the signer may want to be able to use that key in a wallet to make stacking signatures.
If the signer uses a tool like @stacks/cli to generate the key, the CLI also outputs a mnemonic (aka “seed phrase”) that can be imported into a wallet. Because the Stacks CLI uses the standard derivation path for generating Stacks keys, any Stacks wallet will default to having that same private key when the wallet is imported from a derivation path. Similarly, if a hardware wallet is setup with that mnemonic, then the Signer can use a wallet like Leather to make stacking signatures.
The workflow for using a setting up a wallet to generate signatures would be:
Use @stacks/cli to generate the keychain and private key.
Typically, when using a hardware wallet, it’s better to generate the mnemonic on the hardware wallet. For this use case, however, the signer software needs the private key, and hardware wallets (by design) don’t allow exporting private keys.
Take the
privateKey
from the CLI output and add it to your signer’s configuration.Take the mnemonic (24 words) and either:
Setup a new hardware wallet with this mnemonic
Store it somewhere securely, like a password manager. When the signer needs to generate signatures for Stacking transactions, they can import it into either Leather or XVerse.
When the user needs to generate signatures:
Setup your wallet with your signer key’s private key. Either:
Setup your Leather wallet with a Ledger hardware wallet
Import your mnemonic into Leather, XVerse, or another Stacks wallet
Open an app that has stacking signature functionality built-in
Connect your wallet to the app (aka sign in)
In the app, enter your PoX address and “submit”
The app will popup a window in your wallet that prompts you to sign the information
The app will show clear information about what you’re signing
Create the signature
If using a Ledger, confirm on your device
The app will display two results:
Your signer key, which is the public key associated with your signer’s key
Your signer signature
Finally, make a Stacking transaction using the signer key and signer signature.
Now that you have your signer signature generated, it's time to start stacking. This process will vary depending on your chosen method. We've included instructions for solo stacking using Leather Earn below.
Step 3: Stack as a pool operator
The first step with delegated stacking involves a stacker delegating their Stacks to a specific pool operator. Stackers can do this by visiting the “Stack in a pool” page on Leather Earn.
As the pool operator, you must provide a STX address (a “pool admin address”) that will manage delegations. As discussed in previous sections, this is a separate address from the signer’s private key, and this can be any address. This address is what will be used when making transactions to confirm and aggregate delegated STX.
Pool operators can log in to Leather Earn and visit https://earn.leather.io/pool-admin to make pool management transactions.
delegate-stack-stx
Once a user has delegated to a pool operator, the pool operator must call delegate-stack-stx
for each individual stacker.
stack-aggregation-commit
Once a pool has enough delegated STX to become a signer, the pool admin needs to visit the Stack Aggregation Commit
page on Leather Earn. The pool operator enters the following information:
Reward cycle: the reward cycle where the operator is “commiting” delegated STX. This must be done for every individual reward cycle where the pool will be acting as a signer.
BTC address
New fields:
Signer public key
Signer key signature (generated in a previous step using the signer key)
Auth ID
Max amount
Once this transaction has been confirmed, the pool operator is eligible to be a signer for an upcoming reward cycle.
For more on the relationship between automated signing and manual stacking transactions, be sure to check out the main Stack STX doc.
Last updated
Was this helpful?