---
url: 'https://docs.jpool.one/tech-docs/stake-pool-sdk.md'
description: >-
  Stake Pool SDK for depositing, withdrawing, and querying the JPool stake pool
  on Solana
---

# Stake Pool 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.

::: tip CLI alternative
For one-off operations from the command line, see the [CLI Reference](./cli-reference.md). The SDK is designed for building applications and services on top of JPool.
:::

## Installation

::: code-group

```bash [npm]
npm install @jpool/sdk
```

```bash [pnpm]
pnpm add @jpool/sdk
```

```bash [yarn]
yarn add @jpool/sdk
```

:::

Peer 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 send
```

### Parameters

| 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 [Incentivized Liquid Staking](../user-docs/strategies/direct-staking.md) and [Direct Stake Integration](./direct-stake-integration.md).

## 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 * rate
```

:::

## Withdrawing 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 of `TransactionInstruction` objects 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 |

| 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 rewards
```

Returns `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](./direct-stake-integration.md).

## 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())
// 7Q2afV64in6N6SeZsAAB81TJzwDoD6zpqmHkzi9Dcavn
```

You can also access the SDK version:

```typescript
console.log('SDK version:', JPoolClient.version)
```
