# TypeScript SDK

Source code: <https://github.com/solana-labs/solana-program-library/tree/master/stake-pool/js>

## Table of Contents

* [NPM Install](#npm-install)
* [Browser Bundle](#browser-bundle)
  * [Development (unminified)](#development-unminified)
  * [Production (minified)](#production-minified)
* [Import](#import)
  * [CommonJS](#commonjs)
  * [ES6](#es6)
  * [Browser Bundle](#browser-bundle-1)
* [Setup](#setup)
* [Note about SOL Pay API](#note-about-sol-pay-api)
* [Stake Pool Info](#stake-pool-info)
* [Standard Liquid Staking](#standard-liquid-staking)
  * [Stake SOL](#stake-sol)
  * [Stake SOL (with referral)](#stake-sol-with-referral)
  * [Stake SOL (from stake account)](#stake-sol-from-stake-account)
  * [Unstake SOL (instant)](#unstake-sol-instant)
  * [Unstake SOL (delayed)](#unstake-sol-delayed)
* [Custom Liquid Staking](#custom-liquid-staking)
  * [Stake SOL through CLS](#stake-sol-through-cls)
  * [Stake SOL through CLS (with referral)](#stake-sol-through-cls-with-referral)
  * [Stake SOL through CLS (from stake account)](#stake-sol-through-cls-from-stake-account)
  * [Unstake SOL through CLS (instant)](#unstake-sol-through-cls-instant)
  * [Unstake SOL through CLS (delayed)](#unstake-sol-through-cls-delayed)

## NPM Install

```bash
npm install @solana/web3.js
npm install @solana/spl-stake-pool
npm install node-fetch@2
```

## Browser Bundle

### Development (unminified)

```html
<script src="https://unpkg.com/@solana/web3.js@latest/lib/index.iife.js"></script>
<script src="https://unpkg.com/@solana/spl-stake-pool@latest/dist/index.iife.js"></script>
```

### Production (minified)

```html
<script src="https://unpkg.com/@solana/web3.js@latest/lib/index.iife.min.js"></script>
<script src="https://unpkg.com/@solana/spl-stake-pool@latest/dist/index.iife.min.js"></script>
```

## Import

### CommonJS

```javascript
const solanaWeb3 = require('@solana/web3.js');
console.log(solanaWeb3);
const solanaStakePool = require('@solana/spl-stake-pool');
console.log(solanaStakePool);
const fetch = require('node-fetch');
console.log(fetch);
```

### ES6

```javascript
import * as solanaWeb3 from '@solana/web3.js';
console.log(solanaWeb3);
import * as solanaStakePool from '@solana/spl-stake-pool';
console.log(solanaStakePool);
import fetch from 'node-fetch';
console.log(fetch);
```

### Browser Bundle

```javascript
// `solanaWeb3` is provided in the global namespace by the script bundle
console.log(solanaWeb3);
// `solanaStakePool` is provided in the global namespace by the script bundle
console.log(solanaStakePool);
// `fetch` is provided in the global namespace by the browser
console.log(fetch);
```

## Setup

```javascript
const { Connection, Transaction, Keypair, SystemProgram, PublicKey, LAMPORTS_PER_SOL, sendAndConfirmRawTransaction, TransactionInstruction } = solanaWeb3;
const { getStakePoolAccount, updateStakePool, depositSol, depositStake, withdrawSol, withdrawStake, stakePoolInfo } = solanaStakePool;

const connection = new Connection(<INSERT YOUR RPC URL HERE>);
const BLAZESTAKE_POOL = new PublicKey("stk9ApL5HeVAwPLr3TLhDXdZS8ptVu7zp6ov8HFDuMi");
const SOLPAY_API_ACTIVATION = new PublicKey("7f18MLpvAp48ifA1B8q8FBdrGQhyt9u5Lku2VBYejzJL");

let wallet = new PublicKey(<INSERT USER ADDRESS HERE>);

function updatePool() {
    return new Promise(async (resolve, reject) => {
        try {
            let result = await (await fetch(
                "https://stake.solblaze.org/api/v1/update_pool?network=mainnet-beta"
            )).json();
            if(result.success) {
                resolve();
            } else {
                reject();
            }
        } catch(err) {
            reject();
        }
    });
}
```

## Note about SOL Pay API

You may notice the following block of code in many of the code snippets on this page:

```javascript
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
```

This is an optional instruction that transfers 5000 lamports (0.000005 SOL, equivalent to one transaction fee) to the SOLPAY\_API\_ACTIVATION address. This serves two purposes:

1. It's a tip to the BlazeStake crank, which has to make transactions at the end of the epoch to rebalance the pool and merge pending stakes. Since the crank has to pay transaction fees when merging pending stakes at the end of each epoch, this small transfer serves as a tip to the crank to cover that fee.
2. It tags the transaction on-chain to give it access to our [SOL Pay API](https://solpay-docs.solblaze.org/reference/api-reference) in case you are interested in using this API to easily query balance changes in that transaction.

If you do not want to do either of the two items listed above, feel free to remove this optional transfer from your code!

## Stake Pool Info

```javascript
let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);

let solanaAmount = info.details.reserveStakeLamports;
for(let i = 0; i < info.details.stakeAccounts.length; i++) {
    solanaAmount += parseInt(info.details.stakeAccounts[i].validatorLamports);
}
let tokenAmount = parseInt(info.poolTokenSupply);
let conversion = solanaAmount / tokenAmount;
console.log(`Conversion: 1 bSOL = ${conversion} SOL`);

let validators = (await (await fetch("https://stake.solblaze.org/api/v1/validator_count")).json()).count
console.log(`Number of validators: ${validators}`);

console.log(`Total staked SOL (TVL): ${solanaAmount / LAMPORTS_PER_SOL}`);
console.log(`Total bSOL (Supply): ${tokenAmount / LAMPORTS_PER_SOL}`);
```

## Standard Liquid Staking

### Stake SOL

```javascript
let lamports = <INSERT AMOUNT IN LAMPORTS TO STAKE>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}


let depositTx = await depositSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    lamports,
    undefined,
    wallet
);

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
console.log(txid);
```

### Stake SOL (with referral)

```javascript
let lamports = <INSERT AMOUNT IN LAMPORTS TO STAKE>;
let referral = new PublicKey(<INSERT REFERRAL BSOL TOKEN ACCOUNT HERE>);
let referral_addr = <INSERT OWNER OF ABOVE TOKEN ACCOUNT HERE>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let depositTx = await depositSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    lamports,
    undefined,
    referral
);

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
await fetch(
    `https://stake.solblaze.org/api/v1/referral_stake?ref=${referral_addr}&txid=${txid}`
);
console.log(txid);
```

### Stake SOL (from stake account)

```javascript
let stakeAccount = new PublicKey(<INSERT STAKE ACCOUNT HERE>);

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let stakeAccountData = await connection.getParsedAccountInfo(stakeAccount);
let stakeAccountValidator = new PublicKey(
    stakeAccountData.value.data.parsed.info.stake.delegation.voter
);

let depositTx = await depositStake(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    stakeAccountValidator,
    stakeAccount
);

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
console.log(txid);
```

### Unstake SOL (instant)

Unstaking instantly relies on limited liquid unstake liquidity available at the stake pool source. For large unstakes, it's better to use delayed unstake or use [unstake.it's SDK](https://npmjs.com/package/@unstake-it/sol).

```javascript
let amount = <INSERT DECIMAL AMOUNT OF BSOL TO UNSTAKE>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let withdrawTx = await withdrawSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    wallet,
    amount
);

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...withdrawTx.instructions);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = withdrawTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
console.log(txid);
```

### Unstake SOL (delayed)

```javascript
let amount = <INSERT DECIMAL AMOUNT OF BSOL TO UNSTAKE>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let withdrawTx = await withdrawStake(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    amount,
    false
);

let max_per_tx = 2;
let transactions = [];
let instructions = withdrawTx.instructions;
for(let i = 0; i < (instructions.length - 1) / 2; i += max_per_tx) {
    if(i == 0) {
        let transaction = new Transaction();
        transaction.add(SystemProgram.transfer({
            fromPubkey: wallet,
            toPubkey: SOLPAY_API_ACTIVATION,
            lamports: 5000
        }));
        transaction.add(
            ...instructions.slice(0, 1 + (max_per_tx * 2))
        );
        
        // INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
        // transaction = await signTransaction(transaction)
        
        let signers = [...withdrawTx.signers.slice(0, 1 + max_per_tx)];
        if(signers.length > 0) {
            transaction.partialSign(...signers);
        }
        transactions.push(transaction);
    } else {
        let transaction = new Transaction();
        transaction.add(SystemProgram.transfer({
            fromPubkey: wallet,
            toPubkey: SOLPAY_API_ACTIVATION,
            lamports: 5000
        }));
        transaction.add(
            ...instructions.slice((i * 2) + 1, (i * 2) + 1 + (max_per_tx * 2))
        );
        
        // INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
        // transaction = await signTransaction(transaction)
        
        let signers = [
            withdrawTx.signers[0],
            ...withdrawTx.signers.slice(i + 1, i + 1 + max_per_tx)
        ];
        if(signers.length > 0) {
            transaction.partialSign(...signers);
        }
        transactions.push(transaction);
    }
}

let txids = await Promise.all(
    transactions.map(async (transaction) => {
        return await sendAndConfirmRawTransaction(connection, transaction.serialize());
    })
);
console.log(txids);
```

## Custom Liquid Staking

Custom Liquid Staking (CLS) allows you to liquid stake to specific validators! The SOL deposited to the pool during a CLS transaction will be delegated to the specified validator as long as the bSOL stays in the user's control (either through staying in the wallet or being used in a supported DeFi integration like Orca, Raydium, and Saber).

Make sure to query [stake.solblaze.org/api/v1/cls\_eligible\_validators](https://stake.solblaze.org/api/v1/cls_eligible_validators) for a list of eligible validators! Other APIs are available on the [Custom Liquid Staking APIs](/developers/custom-liquid-staking-apis.md) page.

### Stake SOL through CLS

```javascript
let lamports = <INSERT AMOUNT IN LAMPORTS TO STAKE>;
let validator = <INSERT VOTE ACCOUNT OF VALIDATOR>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}


let depositTx = await depositSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    lamports,
    undefined,
    wallet
);

let memo = JSON.stringify({
    type: "cls/validator_stake/lamports",
    value: {
        validator: validator
    }
});

let memoInstruction = new TransactionInstruction({
    keys: [{
        pubkey: wallet,
        isSigner: true,
        isWritable: true
    }],
    programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
    data: (new TextEncoder()).encode(memo)
});

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);
transaction.add(memoInstruction);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
await fetch(
    `https://stake.solblaze.org/api/v1/cls_stake?validator=${validator}&txid=${txid}`
);
console.log(txid);
```

### Stake SOL through CLS (with referral)

```javascript
let lamports = <INSERT AMOUNT IN LAMPORTS TO STAKE>;
let referral = new PublicKey(<INSERT REFERRAL BSOL TOKEN ACCOUNT HERE>);
let referral_addr = <INSERT OWNER OF ABOVE TOKEN ACCOUNT HERE>;
let validator = <INSERT VOTE ACCOUNT OF VALIDATOR>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let depositTx = await depositSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    lamports,
    undefined,
    referral
);

let memo = JSON.stringify({
    type: "cls/validator_stake/lamports",
    value: {
        validator: validator
    }
});

let memoInstruction = new TransactionInstruction({
    keys: [{
        pubkey: wallet,
        isSigner: true,
        isWritable: true
    }],
    programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
    data: (new TextEncoder()).encode(memo)
});

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);
transaction.add(memoInstruction);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
await fetch(
    `https://stake.solblaze.org/api/v1/cls_stake?validator=${validator}&txid=${txid}`
);
await fetch(
    `https://stake.solblaze.org/api/v1/referral_stake?ref=${referral_addr}&txid=${txid}`
);
console.log(txid);
```

### Stake SOL through CLS (from stake account)

```javascript
let stakeAccount = new PublicKey(<INSERT STAKE ACCOUNT HERE>);
let validator = new PublicKey(<INSERT VOTE ACCOUNT OF VALIDATOR FOR CLS>);

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let stakeAccountData = await connection.getParsedAccountInfo(stakeAccount);
let stakeAccountValidator = new PublicKey(
    stakeAccountData.value.data.parsed.info.stake.delegation.voter
);

let depositTx = await depositStake(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    stakeAccountValidator,
    stakeAccount
);

let memo = JSON.stringify({
    type: "cls/validator_stake/stake_accounts",
    value: {
        validator: validator
    }
});

let memoInstruction = new TransactionInstruction({
    keys: [{
        pubkey: wallet,
        isSigner: true,
        isWritable: true
    }],
    programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
    data: (new TextEncoder()).encode(memo)
});

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...depositTx.instructions);
transaction.add(memoInstruction);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = depositTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
await fetch(
    `https://stake.solblaze.org/api/v1/cls_stake?validator=${validator}&txid=${txid}`
);
console.log(txid);
```

### Unstake SOL through CLS (instant)

Unstaking instantly relies on limited liquid unstake liquidity available at the stake pool source. For large unstakes, it's better to use delayed unstake.

```javascript
let amount = <INSERT DECIMAL AMOUNT OF BSOL TO UNSTAKE>;
let validator = <INSERT VOTE ACCOUNT OF VALIDATOR>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let withdrawTx = await withdrawSol(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    wallet,
    amount
);

let memo = JSON.stringify({
    type: "cls/validator_unstake/lamports",
    value: {
        validator: validator
    }
});

let memoInstruction = new TransactionInstruction({
    keys: [{
        pubkey: wallet,
        isSigner: true,
        isWritable: true
    }],
    programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
    data: (new TextEncoder()).encode(memo)
});

let transaction = new Transaction();
transaction.add(SystemProgram.transfer({
    fromPubkey: wallet,
    toPubkey: SOLPAY_API_ACTIVATION,
    lamports: 5000
}));
transaction.add(...withdrawTx.instructions);
transaction.add(memoInstruction);

// INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
// transaction = await signTransaction(transaction)

let signers = withdrawTx.signers;
if(signers.length > 0) {
    transaction.partialSign(...signers);
}

let txid = await sendAndConfirmRawTransaction(connection, transaction.serialize());
await fetch(
    `https://stake.solblaze.org/api/v1/cls_unstake?validator=${validator}&txid=${txid}`
);
console.log(txid);
```

### Unstake SOL through CLS (delayed)

```javascript
let amount = <INSERT DECIMAL AMOUNT OF BSOL TO UNSTAKE>;
let validator = <INSERT VOTE ACCOUNT OF VALIDATOR>;

let info = await stakePoolInfo(connection, BLAZESTAKE_POOL);
if(info.details.updateRequired) {
    await updatePool();
}

let withdrawTx = await withdrawStake(
    connection,
    BLAZESTAKE_POOL,
    wallet,
    amount,
    false
);

let memo = JSON.stringify({
    type: "cls/validator_unstake/stake_accounts",
    value: {
        validator: validator
    }
});

let max_per_tx = 2;
let transactions = [];
let instructions = withdrawTx.instructions;
for(let i = 0; i < (instructions.length - 1) / 2; i += max_per_tx) {
    let memoInstruction = new TransactionInstruction({
        keys: [{
            pubkey: wallet,
            isSigner: true,
            isWritable: true
        }],
        programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
        data: (new TextEncoder()).encode(memo)
    });
    if(i == 0) {
        let transaction = new Transaction();
        transaction.add(SystemProgram.transfer({
            fromPubkey: wallet,
            toPubkey: SOLPAY_API_ACTIVATION,
            lamports: 5000
        }));
        transaction.add(
            ...instructions.slice(0, 1 + (max_per_tx * 2))
        );
        transaction.add(memoInstruction);
        
        // INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
        // transaction = await signTransaction(transaction)
        
        let signers = [...withdrawTx.signers.slice(0, 1 + max_per_tx)];
        if(signers.length > 0) {
            transaction.partialSign(...signers);
        }
        transactions.push(transaction);
    } else {
        let transaction = new Transaction();
        transaction.add(SystemProgram.transfer({
            fromPubkey: wallet,
            toPubkey: SOLPAY_API_ACTIVATION,
            lamports: 5000
        }));
        transaction.add(
            ...instructions.slice((i * 2) + 1, (i * 2) + 1 + (max_per_tx * 2))
        );
        transaction.add(memoInstruction);
        
        // INSERT YOUR CODE HERE TO SIGN A TRANSACTION WITH A WALLET
        // transaction = await signTransaction(transaction)
        
        let signers = [
            withdrawTx.signers[0],
            ...withdrawTx.signers.slice(i + 1, i + 1 + max_per_tx)
        ];
        if(signers.length > 0) {
            transaction.partialSign(...signers);
        }
        transactions.push(transaction);
    }
}

let txids = await Promise.all(
    transactions.map(async (transaction) => {
        return await sendAndConfirmRawTransaction(connection, transaction.serialize());
    })
);
await fetch(
    `https://stake.solblaze.org/api/v1/cls_unstake?validator=${validator}&txid=${txids.join(",")}`
);
console.log(txids);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://stake-docs.solblaze.org/developers/typescript-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
