⚒️TypeScript SDK

Here are some code samples that are helpful in integrating BlazeStake using the stake pool TypeScript SDK!

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

Table of Contents

NPM Install

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

Browser Bundle

Development (unminified)

<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)

<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

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

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

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

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:

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

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

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)

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)

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.

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)

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 for a list of eligible validators! Other APIs are available on the Custom Liquid Staking APIs page.

Stake SOL through CLS

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)

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)

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.

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)

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);

Last updated