- Staking SOL natively is a two-step flow: create and initialize a stake account you own, then delegate it to a validator’s vote account.
- You build both transactions with
@solana/web3.js(StakeProgram.createAccountandStakeProgram.delegate) and send them through a Chainstack Solana node. - Fund the stake account with the rent-exempt reserve plus the amount you want to stake, and stake at least the network minimum — currently 1 SOL (
getStakeMinimumDelegation). Delegating less fails withInsufficientDelegation. - Delegation is not instant: your stake warms up and becomes active at the start of the next epoch, and rewards accrue from then on.
How delegation works on Solana
Solana uses delegated proof of stake. You do not hand SOL to a validator — instead you create a stake account that you fully control, then delegate the SOL held in it to a validator’s vote account. The validator produces and votes on blocks; you earn a share of the staking rewards, minus the validator’s commission. A stake account is a regular account owned by the Stake program, with three things that matter here:- Authorities — a staker (can delegate, deactivate, and split the stake) and a withdrawer (can withdraw the SOL and change either authority). You hold both, so you keep custody the whole time.
- Rent-exempt reserve — like every Solana account, a stake account must hold a minimum balance to stay rent-exempt. This reserve sits on top of the delegated amount and is not staked.
- Delegation — once you delegate, the account records the validator’s vote account, the delegated amount, and the epoch the stake activates in.
- Minimum delegation — the network rejects delegations below
getStakeMinimumDelegation(), currently 1 SOL (1,000,000,000 lamports). Delegating less returns the Stake program errorInsufficientDelegation(custom error0xc). - Rent exemption — a 200-byte stake account needs 2,282,880 lamports (about 0.00228288 SOL) to be rent-exempt, from
getMinimumBalanceForRentExemption(StakeProgram.space).
2,282,880 + 1,000,000,000 = 1,002,282,880 lamports (about 1.00228288 SOL), plus a little SOL in your wallet for transaction fees.
Prerequisites
- A Chainstack account and a deployed Solana node
- Node.js 18 or above
- A funded Solana wallet — a bit more than the amount you plan to stake, so around 1.05 SOL to stake the 1 SOL minimum and cover fees
Deploy a Chainstack Solana node
Sign up with Chainstack
Deploy a node
View node access and credentials
Connection constructor when you connect to your node.
Set up the project
Create a project directory and install@solana/web3.js. Add bs58 only if you import a base58 secret key (for example, one exported from a wallet like Phantom).
Shell
require). Each step is a fragment that runs inside an async function so it can use await — the complete script assembles them into one runnable file. The same calls work unchanged with ES module import syntax.
Connect to your Solana node
Initialize aConnection with your Chainstack HTTPS and WSS endpoints and a commitment level. confirmed is a good default — it waits for the transaction to be confirmed by the cluster without waiting for full finalization.
Pass the WSS endpoint explicitly. @solana/web3.js opens a WebSocket to confirm transactions; with only the HTTPS endpoint it derives a WebSocket URL from that host, which does not match a Chainstack node’s separate WSS endpoint, and sendAndConfirmTransaction then times out with TransactionExpiredBlockheightExceededError.
connection.js
Load your wallet
Your wallet pays the transaction fees, funds the stake account, and becomes the stake account’s staker and withdrawer authority. Load it withKeypair.fromSecretKey.
Pick the form that matches how your key is stored:
wallet.js
Choose a validator
You delegate to a validator’s vote account, not its identity address. List active validators withgetVoteAccounts and pick one:
validators.js
Create the stake account
Creating a stake account is one transaction that both allocates the account and initializes its authorities.StakeProgram.createAccount returns a ready-to-send Transaction containing two instructions — a System program createAccount and a Stake program Initialize.
Fund it with the rent-exempt reserve plus the amount you want to stake, and set both authorities to your wallet with Authorized. Pass a Lockup of new Lockup(0, 0, PublicKey.default) for no lockup — a zero timestamp and epoch mean the stake is never locked, and the default custodian address is 11111111111111111111111111111111.
create-stake-account.js
Delegate the stake
With the stake account created and initialized, delegate it to your chosen validator’s vote account.StakeProgram.delegate returns a Transaction with a single Stake program DelegateStake instruction, signed by the staker authority — your wallet.
delegate.js
Verify the delegation
Read the stake account back withgetParsedAccountInfo. Right after delegating, the account type is delegated and deactivationEpoch is 18446744073709551615 (the maximum u64, meaning the stake is not scheduled to deactivate).
verify.js
Example output
activationEpoch. Track the current epoch with getEpochInfo. To unstake later, the staker authority calls StakeProgram.deactivate (the stake cools down over the next epoch), then the withdrawer calls StakeProgram.withdraw to move the SOL back out.
Full script
The complete flow, ready to run withnode stake.js after setting your endpoint, key path, and validator vote account:
stake.js
The script prints two signatures and a
delegated stake account whose delegation.voter matches your chosen validator’s vote account. Look either signature up on a Solana explorer to confirm it on-chain.What’s next
Solana tooling
Libraries, clients, and tools for building on Solana with Chainstack.
Solana API methods
The full JSON-RPC method reference, including
getVoteAccounts and getEpochInfo.