A program for pooling together SOL to be staked by an off-chain agent running a Delegation Bot which redistributes the stakes across the network and tries to maximize censorship resistance and rewards.
SOL token holders can earn rewards and help secure the network by staking tokens to one or more validators. Rewards for staked tokens are based on the current inflation rate, total number of SOL staked on the network, and an individual validator’s uptime and commission (fee).
Stake pools are an alternative method of earning staking rewards. This on-chain program pools together SOL to be staked by a staker, allowing SOL holders to stake and earn rewards without managing stakes.
Additional information regarding staking and stake programming is available at:
This document is intended for the main actors of the stake pool system:
- manager: creates and manages the stake pool, earns fees, can update the fee, staker, and manager
- staker: adds and removes validators to the pool, rebalances stake among validators
- user: provides staked SOL into an existing stake pool
In its current iteration, the stake pool only processes totally active stakes. Deposits must come from fully active stakes, and withdrawals return a fully active stake account.
This means that stake pool managers, stakers, and users must be comfortable with creating and delegating stakes, which are more advanced operations than sending and receiving SPL tokens and SOL. Additional information on stake operations are available at:
To reach a wider audience of users, stake pool managers are encouraged to provide a market for their pool's tokens, through an AMM like Token Swap.
A stake pool manager creates a stake pool, and the staker includes validators that will receive delegations from the pool by creating "validator stake accounts" and activating a delegation on them. Once a validator stake account's delegation is active, the staker adds it to the stake pool.
At this point, users can participate with deposits. They must delegate a stake account to the one of the validators in the stake pool. Once it's active, the user can deposit their stake into the pool in exchange for SPL tokens representing their fractional ownership in pool. A percentage of the rewards earned by the pool goes to the pool manager as a fee.
Over time, as the stakes in the stake pool accrue staking rewards, the user's fractional ownership will be worth more than their initial deposit. Whenever the user chooses, they can withdraw activated stake in exchange for their SPL pool tokens.
The stake pool staker can add and remove validators, or rebalance the pool by decreasing the stake on a validator, waiting an epoch to move it into the stake pool's reserve account, then increasing the stake on another validator.
The staker operation to add a new validator requires roughly 1.003 SOL to create the stake account on a validator, so the stake pool staker will need liquidity on hand to fully manage the pool stakes.
Solana's programming model and the definitions of the Solana terms used in this document are available at:
The Stake Pool Program's source is available on github.
The following explains the instructions available in the Stake Pool Program along with examples using the command-line utility.
spl-stake-pool command-line utility can be used to experiment with SPL
tokens. Once you have Rust installed, run:
spl-stake-pool --help for a full description of available commands.
spl-stake-pool configuration is shared with the
solana command-line tool.
Cluster RPC URL
See Solana clusters for cluster-specific RPC URLs
See Keypair conventions for information on how to setup a keypair if you don't already have one.
Hardware Wallet URL (See URL spec)
If you would like to test a stake pool locally without having to wait for stakes
to activate and deactivate, you can run the stake pool locally using the
solana-test-validator tool with shorter epochs, and pulling the current program
from devnet, testnet, or mainnet.
Stake Pool Manager Examples
Create a stake pool
The stake pool manager controls the stake pool from a high level, and in exchange receives a fee in the form of SPL tokens. The manager sets the fee on creation. Let's create a pool with a 3% fee and a maximum of 1000 validator stake accounts:
The unique stake pool identifier is
The identifier for the stake pool's SPL token mint is
BoNneHKDrX9BHjjvSpPfnQyRjsnc9WFH71v8wrgCd7LB. The stake pool has full control
over the mint.
The pool creator's fee account identifier is
DgyZrAq88bnG1TNRxpgDQzWXpzEurCvfY2ukKFWBvADQ. Every epoch, as stake accounts
in the stake pool earn rewards, the program will mint SPL pool tokens
equal to 3% of the gains on that epoch into this account. If no gains were observed,
nothing will be deposited.
The reserve stake account identifier is
This account holds onto additional stake used when rebalancing between validators.
For a stake pool with 1000 validators, the cost to create a stake pool is less than 0.5 SOL.
The stake pool manager may pass their administrator privileges to another account.
At the same time, they may also change the SPL token account that receives fees
every epoch. The mint for the provided token account must be the SPL token mint,
BoNneHKDrX9BHjjvSpPfnQyRjsnc9WFH71v8wrgCd7LB in our example.
The stake pool manager may update the fee assessed every epoch, passing the numerator and denominator for the fraction that make up the fee. For a fee of 10%, they could run:
In order to protect stake pool depositors from malicious managers, the program applies the new fee for the following epoch. For example, if the fee is 1% at epoch 100, and the manager sets it to 10%, the manager will still gain 1% for the rewards earned during epoch 100. Starting with epoch 101, the manager will earn 10%.
In order to manage the stake accounts, the stake pool manager or staker can set the staker authority of the stake pool's managed accounts.
Now, the new staker can perform any normal stake pool operations, including adding and removing validators and rebalancing stake.
Important security note: the stake pool program only gives staking authority to the pool staker and always retains withdraw authority. Therefore, a malicious stake pool staker cannot steal funds from the stake pool.
Note: to avoid "disturbing the manager", the staker can also reassign their stake authority.
Stake Pool Staker Examples
Create a validator stake account
In order to accommodate large numbers of user deposits into the stake pool, the stake pool only manages one stake account per validator. To add a new validator to the stake pool, we first create a validator-associated stake account.
Looking at validators.app or other Solana validator
lists, we choose some validators at random and start with identity
8SQEcP4FaYQySktNQeyxF3w8pvArx3oMEh7fPrzkN9pu on vote account
2HUKQz7W2nXZSwrdX5RkfS2rLU4j1QZLjdGCHcoUKFh3. Let's create a validator stake account
delegated to that vote account.
In order to maximize censorship resistance, we want to distribute our SOL to as many validators as possible, so let's add a few more.
NOTE: These stake accounts have not been added to the stake pool yet. Stake pools only accept deposits from fully active (warmed-up) delegated stake accounts.
We can see the status of stake account using the Solana command-line utility.
The stake pool creates these special staking accounts with 1 SOL as the required
delegation amount. The stake and withdraw authorities are the keypair configured
--config flag, using the Solana CLI default key. More information
about the Solana CLI can be found on the
Now that we have created these delegated validator stake accounts, we need to wait an epoch for the delegation to activate.
Add validator stake account
As mentioned in the last step, the stake pool only manages one stake account per validator. Also, the stake pool only processes fully activated stake accounts. We created new validator stake accounts in the last step and staked them. Once the stake activates, we can add them to the stake pool.
Also, as mentioned in the last step, validator stake accounts must have exactly 1.00228288 SOL, 1 SOL for the delegation, and 0.00228288 SOL for the rent-exempt reserve. After activation, the validator stake account may have already gained some rewards, so we have to move those rewards off before adding the validator. Let's check our stake account again:
Since the delegated stake is now 1.004 SOL, we need to split that additional amount into another stake account before adding.
The staker is free to do whatever they like with the new split stake account. Most likely, they will want to deactivate it, wait an epoch, and then withdraw the additional lamports back into their account.
Now that the validator stake account has exactly 1 delegated SOL, we're ready to add this validator to the stake pool!
Users can start depositing their activated stakes into the stake pool, as
long as they are delegated to the same vote account, which was
FYQB64aEzSmECvnG8RVvdAXBxRnzrLvcA3R22aGH2hUN in this example. You can also
double-check that at any time using the Solana command-line utility.
Remove validator stake account
If the stake pool staker wants to stop delegating to a vote account, they can totally remove the validator stake account from the stake pool.
As with adding a validator, the validator stake account must have exactly 1.00228288 SOL (1 SOL delegated, 0.00228288 SOL for rent exemption) to be removed.
If that is not the case, the staker must first decrease the stake to that minimum amount.
Let's assume that the validator stake account delegated to
AUCzCaGAGjL3uyjFBtJs7KuJcgQWvNZu1Z2S9G3pw77G has a total delegated amount of
7.5 SOL. To reduce that number, the staker can run:
Now, let's try to remove validator
Unlike a normal withdrawal, the validator stake account is totally
removed from the stake pool and now belongs to the administrator. The authority
for the withdrawn stake account can also be specified using the
We can check the removed stake account:
Rebalance the stake pool
As time goes on, users will deposit to and withdraw from all of the stake accounts managed by the pool, and the stake pool staker may want to rebalance the stakes.
For example, let's say the staker wants the same delegation to every validator in the pool. When they look at the state of the pool, they see:
This isn't great! The last stake account,
has too much allocated. For their strategy, the staker wants the
SOL to be distributed evenly, meaning around
5.283319735 in each account. They need
Decrease validator stake
First, they need to decrease the amount on stake account
E5KBATUd21Dnjnh5sGFw5ngp9kdVXCcAAYMRe2WsVXie, delegated to
HJiC8iJ4Sj846SswQuauFJK93UvV6zp3c2T6jzGqzhhz, by total of
They decrease that amount of SOL:
Internally, this instruction splits and deactivates 6.153483916 SOL from the
validator stake account
E5KBATUd21Dnjnh5sGFw5ngp9kdVXCcAAYMRe2WsVXie into a
transient stake account, owned and managed entirely by the stake pool.
Once the stake is deactivated during the next epoch, the
update command will
automatically merge the transient stake account into a reserve stake account,
also entirely owned and managed by the stake pool.
Increase validator stake
Now that the reserve stake account has enough to perform the rebalance, the staker
can increase the stake on the two other validators,
They add 4.281036854 SOL to
And they add 1.872447062 SOL to
Internally, this instruction also uses transient stake accounts. This time, the stake pool splits from the reserve stake, into the transient stake account, then activates it to the appropriate validator.
One to two epochs later, once the transient stakes activate, the
automatically merges the transient stakes into the validator stake account, leaving
a fully rebalanced stake pool:
Due to staking rewards that accrued during the rebalancing process, the pool is not perfectly balanced. This is completely normal.
Set Preferred Deposit / Withdraw Validator
Since a stake pool accepts deposits to any of its stake accounts, and allows withdrawals from any of its stake accounts, it could be used by malicious arbitrageurs looking to maximize returns each epoch.
For example, if a stake pool has 1000 validators, an arbitrageur could stake to any one of those validators. At the end of the epoch, they can check which validator has the best performance, deposit their stake, and immediately withdraw from the highest performing validator. Once rewards are paid out, they can take their valuable stake, and deposit it back for more than they had.
To mitigate this arbitrage, a stake pool staker can set a preferred withdraw or deposit validator. Any deposits or withdrawals must go to the corresponding stake account, making this attack impossible without a lot of funds.
Let's set a preferred deposit validator stake account:
And then let's set the preferred withdraw validator stake account to the same one:
At any time, they may also unset the preferred validator:
The preferred validators are marked in the
List validator stake accounts
In order to deposit into the stake pool, a user must first delegate some stake to one of the validator stake accounts associated with the stake pool. The command-line utility has a special instruction for finding out which vote accounts are already associated with the stake pool.
Stake pools only accept deposits from active stake accounts, so we must first
create stake accounts and delegate them to one of the validators managed by the
stake pool. Using the
list command from the previous section, we see that
2HUKQz7W2nXZSwrdX5RkfS2rLU4j1QZLjdGCHcoUKFh3 is a valid vote account, so let's
create a stake account and delegate our stake there.
Two epochs later, when the stake is fully active and has received one epoch of rewards, we can deposit the stake into the stake pool.
The CLI will default to using the fee payer's
Associated Token Account for stake pool tokens.
Alternatively, you can create an SPL token account yourself and pass it as the
token-receiver for the command.
In return, the stake pool has minted us new pool tokens, representing our share of ownership in the pool. We can double-check our stake pool account using the SPL token command-line utility.
Every epoch, the network pays out rewards to stake accounts managed by the stake pool, increasing the value of pool tokens minted on deposit. In order to calculate the proper value of these stake pool tokens, we must update the total value managed by the stake pool every epoch.
If another user already updated the stake pool balance for the current epoch, we see a different output.
If no one updates the stake pool in the current epoch, all instructions, including deposit and withdraw, will fail. The update instruction is permissionless, so any user can run it before interacting with the pool. As a convenience, the CLI attempts to update before running any instruction on the stake pool.
If the stake pool transient stakes are in an unexpected state, and merges are
not possible, there is the option to only update the stake pool balances without
performing merges using the
Later on, whenever the transient stakes are ready to be merged, it is possible to
force another update in the same epoch using the
Whenever the user wants to recover their SOL plus accrued rewards, they can provide their pool tokens in exchange for an activated stake account.
Let's withdraw active staked SOL in exchange for 5 pool tokens.
The stake pool took 5 pool tokens, and in exchange the user received a fully
active stake account, delegated to
Let's double-check the status of the stake account:
Alternatively, the user can specify an existing uninitialized stake account to
receive their stake using the
By default, the withdraw command uses the
token-owner's associated token account to
source the pool tokens. It's possible to specify the SPL token account using
By default, the withdraw command will withdraw from the largest validator stake
accounts in the pool. It's also possible to specify a specific vote account for
the withdraw using the
Note that the associated validator stake account must have enough lamports to satisfy the pool token amount requested.
Special case: exiting pool with a delinquent staker
With the reserve stake, it's possible for a delinquent or malicious staker to
move all stake into the reserve through
decrease-validator-stake, so the
pool tokens will not gain rewards, and the stake pool users will not
be able to withdraw their funds.
To get around this case, it is also possible to withdraw from the stake pool's
reserve, but only if all of the validator stake accounts are at the minimum amount of
1 SOL + stake account rent exemption.
As mentioned earlier, the stake pool only processes active stakes. This feature maintains fungibility of stake pool tokens. Fully activated stakes are not equivalent to inactive, activating, or deactivating stakes due to the time cost of staking. Otherwise, malicious actors can deposit stake in one state and withdraw it in another state without waiting.
Transient stake accounts
Each validator gets one transient stake account, so the staker can only
perform one action at a time on a validator. It's impossible to increase
and decrease the stake on a validator at the same time. The staker must wait for
the existing transient stake account to get merged during an
before performing a new action.
Reserve stake account
Every stake pool is initialized with an undelegated reserve stake account, used to hold undelegated stake in process of rebalancing. After the staker decreases the stake on a validator, one epoch later, the update operation will merge the decreased stake into the reserve. Conversely, whenever the staker increases the stake on a validator, the lamports are drawn from the reserve stake account.
Safety of Funds
One of the primary aims of the stake pool program is to always allow pool token holders to withdraw their funds at any time.
To that end, let's look at the three classes of stake accounts in the stake pool system:
- validator stake: active stake accounts, one per validator in the pool
- transient stake: activating or deactivating stake accounts, merged into the reserve after deactivation, or into the validator stake after activation, one per validator
- reserve stake: inactive stake, to be used by the staker for rebalancing
Additionally, the staker may set a "preferred withdraw account", which forces users to withdraw from a particular stake account. This is to prevent malicious depositors from using the stake pool as a free conversion between validators.
When processing withdrawals, the order of priority goes:
- preferred withdraw validator stake account (if set)
- validator stake accounts
- transient stake accounts
- reserve stake account
If there is preferred withdraw validator, and that validator stake account has any SOL, a user must withdraw from that account.
If that account is empty, or the preferred withdraw validator stake account is not set, then the user must withdraw from any validator stake account.
If all validator stake accounts are empty, which may happen if the stake pool staker decreases the stake on all validators at once, then the user must withdraw from any transient stake account.
If all transient stake accounts are empty, then the user must withdraw from the reserve.
In this way, a user's funds are never at risk, and always redeemable.
Staking Credits Observed on Deposit
A deposited stake account's "credits observed" must match the destination account's "credits observed". Typically, this means you must wait an additional epoch after activation for your stake account to match up with the stake pool's account.
The Solana transaction processor has two important limitations:
- size of the overall transaction, limited to roughly 1 MTU / packet
- computation budget per instruction
A stake pool may manage hundreds of staking accounts, so it is impossible to update the total value of the stake pool in one instruction. Thankfully, the command-line utility breaks up transactions to avoid this issue for large pools.