Appearance
TypeScript SDK
The @jpool/sdk package lets you interact with the JPool stake pool programmatically. You can deposit and withdraw SOL, direct stake to specific validators, query pool state and exchange rates, and look up direct stake records.
CLI alternative
For one-off operations from the command line, see the CLI Reference. The SDK is designed for building applications and services on top of JPool.
Installation
bash
npm install @jpool/sdkbash
pnpm add @jpool/sdkbash
yarn add @jpool/sdkPeer dependency: @solana/web3.js v1.x.
Quick start
typescript
import { Connection, clusterApiUrl, LAMPORTS_PER_SOL } from '@solana/web3.js'
import { JPoolClient } from '@jpool/sdk'
const connection = new Connection(clusterApiUrl('mainnet-beta'))
const client = new JPoolClient(connection)
const poolInfo = await client.poolInfo()
console.log('Total staked:', Number(poolInfo.totalLamports) / LAMPORTS_PER_SOL, 'SOL')
const rate = client.poolTokenRate(poolInfo)
console.log('Exchange rate:', rate, 'lamports per pool token')Configuration
The JPoolClient constructor accepts an optional configuration object:
typescript
import { STAKE_POOL_PROGRAM_ID } from '@solana/spl-stake-pool'
const client = new JPoolClient(connection, {
poolAddress: new PublicKey('CUSTOM_POOL_ADDRESS'),
programId: STAKE_POOL_PROGRAM_ID,
refCode: 'YOUR_REFERRAL_CODE',
})| Option | Type | Default | Description |
|---|---|---|---|
poolAddress | PublicKey | JPool pool address | Stake pool account to interact with |
programId | PublicKey | SPL Stake Pool program | Stake pool program ID |
refCode | string | — | Referral code included in transaction memos for attribution |
debug | boolean | false | Enable debug logging |
You can update individual options after construction. The configure method returns the client instance, so calls can be chained:
typescript
client
.configure('refCode', 'NEW_CODE')
.configure('debug', true)Depositing SOL
Use depositSol to stake SOL and receive liquid pool tokens (JSOL). The method accepts a DepositSolProps object:
typescript
import { Transaction, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js'
const userPublicKey = new PublicKey('YOUR_WALLET_ADDRESS')
const { instructions, signers } = await client.depositSol({
from: userPublicKey,
lamports: 1 * LAMPORTS_PER_SOL,
})
const transaction = new Transaction().add(...instructions)
const { blockhash } = await connection.getLatestBlockhash()
transaction.recentBlockhash = blockhash
transaction.feePayer = userPublicKey
// Sign with ephemeral signers, then with your wallet
transaction.sign(...signers)
// ... sign with wallet and sendParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
from | PublicKey | Yes | Wallet depositing SOL and receiving pool tokens |
lamports | number | Yes | Amount of SOL to deposit, in lamports |
directVote | PublicKey | No | Validator to direct your stake to |
ephemeralAddress | PublicKey | No | Custom ephemeral keypair address |
Direct staking on deposit
Pass a directVote to direct your stake to a specific validator while still receiving liquid pool tokens:
typescript
const { instructions, signers } = await client.depositSol({
from: userPublicKey,
lamports: 5 * LAMPORTS_PER_SOL,
directVote: new PublicKey('VALIDATOR_VOTE_ACCOUNT'),
})For more on how direct staking works, see Direct Staking and Direct Stake Integration.
Depositing a stake account
Use depositStake to convert an existing delegated stake account into pool tokens. The method accepts a DepositStakeProps object:
typescript
const { instructions, signers } = await client.depositStake({
authority: userPublicKey,
vote: new PublicKey('VALIDATOR_VOTE_ACCOUNT'),
stake: new PublicKey('STAKE_ACCOUNT_ADDRESS'),
})The stake account must already be delegated and in an active state.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
authority | PublicKey | Yes | Owner (withdraw authority) of the stake account |
vote | PublicKey | Yes | Validator vote account the stake is delegated to |
stake | PublicKey | Yes | Stake account to deposit |
directVote | PublicKey | No | Validator to direct your stake to after deposit |
Withdrawing SOL
Use withdrawSol to burn pool tokens and receive SOL from the pool reserve. The method accepts a WithdrawSolProps object:
typescript
const { instructions, signers } = await client.withdrawSol({
from: userPublicKey,
amount: poolTokensToBurn,
})Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
from | PublicKey | Yes | Wallet that owns the pool tokens and will receive SOL |
amount | number | Yes | Number of pool tokens to burn (not SOL) |
withdrawAuthority | PublicKey | No | Custom withdraw authority |
ephemeralTransferAuthority | PublicKey | No | Custom ephemeral transfer authority |
directId | PublicKey | No | Direct stake identifier for unstaking from a specific validator |
WARNING
The amount parameter is in pool tokens, not SOL. To estimate the SOL you will receive, multiply by the pool token rate:
typescript
const poolInfo = await client.poolInfo()
const rate = client.poolTokenRate(poolInfo)
const expectedSOL = poolTokenAmount * rateWithdrawing stake
Use withdrawStake to burn pool tokens and receive a delegated stake account instead of liquid SOL. The method accepts a WithdrawStakeProps object:
typescript
const { instructions, signers } = await client.withdrawStake({
from: userPublicKey,
amount: poolTokensToBurn,
})The resulting stake account will be delegated to a validator from the pool. You will need to deactivate it and wait for the cooldown (roughly one epoch) before the SOL becomes available.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
from | PublicKey | Yes | Wallet that owns the pool tokens |
amount | number | Yes | Number of pool tokens to burn (not SOL) |
ephemeralTransferAuthority | PublicKey | No | Custom ephemeral transfer authority |
directId | PublicKey | No | Direct stake identifier for unstaking from a specific validator |
Transaction signing
All transaction-building methods (depositSol, depositStake, withdrawSol, withdrawStake) return { instructions, signers }:
instructions: an array ofTransactionInstructionobjects to add to your transaction.signers: ephemeral keypairs created by the SPL library for temporary operations. These must sign the transaction along with your wallet.
The full signing flow:
typescript
// 1. Get instructions and signers from an SDK method
const { instructions, signers } = await client.depositSol({ from, lamports })
// 2. Build the transaction
const transaction = new Transaction().add(...instructions)
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash()
transaction.recentBlockhash = blockhash
transaction.lastValidBlockHeight = lastValidBlockHeight
transaction.feePayer = from
// 3. Sign with ephemeral signers
transaction.sign(...signers)
// 4. Sign with your wallet (Phantom, Solflare, etc.)
// 5. Send the transaction
const signature = await connection.sendRawTransaction(transaction.serialize())
await connection.confirmTransaction(signature)Querying pool data
Pool info
Fetch the current state of the stake pool:
typescript
const poolInfo = await client.poolInfo()Key fields on the returned StakePoolInfo object:
| Field | Type | Description |
|---|---|---|
totalLamports | bigint | Total SOL under pool management |
poolTokenSupply | bigint | Total pool tokens in circulation |
poolMint | PublicKey | Pool token mint address |
reserveStake | PublicKey | Reserve stake account address |
solDepositFee | number | SOL deposit fee as a decimal (0.03 = 3%) |
solWithdrawalFee | number | SOL withdrawal fee as a decimal |
stakeDepositFee | number | Stake account deposit fee as a decimal |
stakeWithdrawalFee | number | Stake account withdrawal fee as a decimal |
epochFee | number | Per-epoch management fee as a decimal |
lastUpdateEpoch | bigint | Epoch when the pool was last updated |
All StakePoolInfo fields
| Field | Type | Description |
|---|---|---|
manager | PublicKey | Pool manager who can modify pool settings |
staker | PublicKey | Staker who can manage stake accounts |
stakeDepositAuthority | PublicKey | Authority for stake deposits |
stakeWithdrawBumpSeed | number | Bump seed for the stake withdrawal authority PDA |
validatorList | PublicKey | Validator list account |
reserveStake | PublicKey | Reserve stake account |
poolMint | PublicKey | Pool token mint |
managerFeeAccount | PublicKey | Account that receives manager fees |
tokenProgramId | PublicKey | Token program ID |
totalLamports | bigint | Total SOL under pool management |
poolTokenSupply | bigint | Total pool tokens in circulation |
lastUpdateEpoch | bigint | Epoch when the pool was last updated |
lastEpochPoolTokenSupply | bigint | Pool token supply in the previous epoch |
lastEpochTotalLamports | bigint | Total lamports in the previous epoch |
lockupUnixTimestamp | bigint | Unix timestamp when the lockup expires |
lockupEpoch | bigint | Epoch when the lockup expires |
lockupCustodian | PublicKey | Custodian who can unlock before lockup expires |
epochFee | number | Per-epoch management fee as a decimal |
nextEpochFee | number | undefined | Next epoch's fee if scheduled for change |
preferredDepositValidatorVoteAddress | PublicKey | undefined | Preferred validator for deposits |
preferredWithdrawValidatorVoteAddress | PublicKey | undefined | Preferred validator for withdrawals |
stakeDepositFee | number | Fee on stake deposits as a decimal |
stakeWithdrawalFee | number | Fee on stake withdrawals as a decimal |
nextStakeWithdrawalFee | number | undefined | Next epoch's stake withdrawal fee if scheduled |
stakeReferralFee | number | Referral fee percentage (0-100) for stake operations |
solDepositAuthority | PublicKey | undefined | Authority that can perform SOL deposits |
solDepositFee | number | Fee on SOL deposits as a decimal |
solReferralFee | number | Referral fee percentage (0-100) for SOL deposits |
solWithdrawAuthority | PublicKey | undefined | Authority that can perform SOL withdrawals |
solWithdrawalFee | number | Fee on SOL withdrawals as a decimal |
nextSolWithdrawalFee | number | undefined | Next epoch's SOL withdrawal fee if scheduled |
Exchange rate
Calculate the current rate between pool tokens and lamports:
typescript
const rate = client.poolTokenRate(poolInfo)
// rate > 1 means the pool has earned staking rewardsReturns 1.0 if the pool token supply is zero.
Reserve balance
Check the pool's liquid SOL reserve. Returns undefined if the reserve account does not exist.
typescript
const reserveLamports = await client.reserveBalance(poolInfo)Fees
Deposit and withdrawal fees are automatically deducted from operations. To check current fees:
typescript
const poolInfo = await client.poolInfo()
console.log('SOL deposit fee:', poolInfo.solDepositFee * 100, '%')
console.log('SOL withdrawal fee:', poolInfo.solWithdrawalFee * 100, '%')
console.log('Stake deposit fee:', poolInfo.stakeDepositFee * 100, '%')
console.log('Stake withdrawal fee:', poolInfo.stakeWithdrawalFee * 100, '%')Direct stakes and wallet bindings
Querying direct stakes
Look up direct stake records by wallet or validator. At least one filter is required. The method accepts a DirectStakesQuery object.
typescript
// By wallet
const stakes = await client.getDirectStakes({
wallet: userPublicKey,
})
// By validator
const validatorStakes = await client.getDirectStakes({
vote: validatorVoteAccount,
})Querying wallet bindings
Look up wallet binding records. Exactly one of wallet or vote must be provided. The method accepts a WalletBindingsQuery object.
typescript
// By wallet
const bindings = await client.getWalletBindings({
wallet: userPublicKey,
})
// By validator
const validatorBindings = await client.getWalletBindings({
vote: validatorVoteAccount,
})For creating and managing direct stakes and wallet bindings programmatically, see Direct Stake Integration.
Constants
The SDK exports the JPool pool address and token mint:
typescript
import { POOL_ADDRESS, POOL_MINT_ADDRESS } from '@jpool/sdk'
console.log('Pool address:', POOL_ADDRESS.toBase58())
// CtMyWsrUtAwXWiGr9WjHT5fC3p3fgV8cyGpLTo2LJzG1
console.log('Pool token mint:', POOL_MINT_ADDRESS.toBase58())
// 7Q2afV64in6N6SeZsAAB81TJzwDoD6zpqmHkzi9DcavnYou can also access the SDK version:
typescript
console.log('SDK version:', JPoolClient.version)