npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@unstake-it/sol

v2.0.0

Published

Typescript SDK for the unstake.it solana program that allows users to instantly unstake their stake accounts for a fee.

Downloads

135

Readme

@unstake.it/sol

Typescript SDK for the unstake.it solana program that allows users to instantly unstake their stake accounts for a fee.

Built on anchor 0.24.2.

Contents:

Installation

npm

$ npm install @unstake-it/sol

yarn

$ yarn add @unstake-it/sol

Examples

Initialization

Initialize the anchor program and define address constants.

import { PublicKey } from "@solana/web3.js";
import { Program } from "@project-serum/anchor";
import { IDL_JSON as UNSTAKE_IDL_JSON, Unstake } from "@unstake-it/sol";

// our mainnet program address
const PROG_ID = new PublicKey("unpXTU2Ndrc7WWNyEhQWe4udTzSibLPi25SXv2xbCHQ");

// construct new Program with provider set to anchor's default getProvider()
const UNSTAKE_PROGRAM: Program<Unstake> = new Program(
  UNSTAKE_IDL_JSON as Unstake,
  PROG_ID
);

// our mainnet unstake liquidity pool
const UNSTAKE_POOL_ADDRESS = new PublicKey(
  "FypPtwbY3FUfzJUtXHSyVRokVKG2jKtH29FmK4ebxRSd"
);

Fetch Pool Data

const FETCHED_POOL_DATA = await UNSTAKE_PROGRAM.account.pool.fetch(
  UNSTAKE_POOL_ADDRESS
);
const { feeAuthority, lpMint, incomingStake } = FETCHED_POOL_DATA;

Fetch Available Liquidity

The liquidity currently available for unstaking and liquidity removal is simply the SOL balance of the pool's reserves account.

import { findPoolSolReserves } from "@unstake-it/sol";

// this address is 3rBnnH9TTgd3xwu48rnzGsaQkSr1hR64nY71DrDt6VrQ on mainnet
const POOL_RESERVES_ADDRESS = await findPoolSolReserves(
  UNSTAKE_PROGRAM,
  UNSTAKE_POOL_ADDRESS
);
const availableLiquidityLamports =
  await UNSTAKE_PROGRAM.provider.connection.getBalance(POOL_RESERVES_ADDRESS);

Fetch Fee Params

The fee params for the unstake pool is stored in a separate PDA.

import { findPoolFeeAccount } from "@unstake-it/sol";

// this address is 5Pcu8WeQa3VbBz2vdBT49Rj4gbS4hsnfzuL1LmuRaKFY on mainnet
const POOL_FEE_ADDRESS = await findPoolFeeAccount(
  UNSTAKE_PROGRAM,
  UNSTAKE_POOL_ADDRESS
);
const FETCHED_FEE_DATA = await UNSTAKE_PROGRAM.account.fee.fetch(
  POOL_FEE_ADDRESS
);

// `fee` is an enum containing different fields depending on the fee schedule variant.
// Recommend using `applyFee` to estimate fees (see example in #estimate-fees below)
const { fee } = FETCHED_FEE_DATA;

Fetch Protocol Fee Params

The global protocol fee params for the unstake pool is stored in a singleton PDA.

import { findProtocolFeeAccount } from "@unstake-it/sol";

// this address is 2hN9UhvRFVfPYKL6rZJ5YiLEPCLTpN755pgwDJHWgFbU on mainnet
const PROTOCOL_FEE_ADDRESS = await findProtocolFeeAccount(UNSTAKE_PROGRAM);
const FETCHED_PROTOCOL_FEE_DATA =
  await UNSTAKE_PROGRAM.account.protocolFee.fetch(PROTOCOL_FEE_ADDRESS);

const { destination, authority, feeRatio, referrerFeeRatio } =
  FETCHED_PROTOCOL_FEE_DATA;

// this account data is required for unstakeTx(), unstakeWsolTx() and previewUnstake()
const PROTOCOL_FEE = {
  publicKey: PROTOCOL_FEE_ADDRESS,
  account: FETCHED_PROTOCOL_FEE_DATA,
};

Previewing an Unstake

This function simply makes a simulateTransaction RPC call and reports the change in lamports to the destination account the transaction results in.

import { previewUnstake } from "@unstake-it/sol";

// stakeAccountPubkey defined elsewhere

const changeInLamports = await previewUnstake(UNSTAKE_PROGRAM, {
  poolAccount: UNSTAKE_POOL_ADDRESS,
  stakeAccount: stakeAccountPubkey,
  unstaker: UNSTAKE_PROGRAM.provider.wallet.publicKey,
  protocolFee: PROTOCOL_FEE,
});

Estimate Fees

Estimate fees for an unstake amount manually using the fetched fee params. This is useful in cases where previewUnstake can't be used, such as when the stake account to be unstaked hasn't been created yet.

import BN from "bn.js";
import {
  applyFee,
  applyProtocolFee,
  findPoolSolReserves,
} from "@unstake-it/sol";

const plannedUnstakeAmountLamports = new BN(1_000_000_000);

// this address is 3rBnnH9TTgd3xwu48rnzGsaQkSr1hR64nY71DrDt6VrQ on mainnet
const POOL_RESERVES_ADDRESS = await findPoolSolReserves(
  UNSTAKE_PROGRAM,
  UNSTAKE_POOL_ADDRESS
);
const availableLiquidityLamports =
  await UNSTAKE_PROGRAM.provider.connection.getBalance(POOL_RESERVES_ADDRESS);
const { incomingStake } = await UNSTAKE_PROGRAM.account.pool.fetch(
  UNSTAKE_POOL_ADDRESS
);

const estFeeDeductedLamports = applyFee(FETCHED_FEE_DATA, {
  poolIncomingStake: incomingStake,
  solReservesLamports: new BN(availableLiquidityLamports),
  stakeAccountLamports: plannedUnstakeAmountLamports,
});

// calculate the part of the fees that is levied as protocol and referral fees
const { protocolLamports, referrerLamports } = applyProtocolFee(
  FETCHED_PROTOCOL_FEE_DATA,
  estFeeDeductedLamports
);

Unstake

Create and send the unstake transaction

import BN from "bn.js";
import { deactivateStakeAccountTx, unstakeTx } from "@unstake-it/sol";
// use solana-stake-sdk's util functions for fetching and processing stake accounts
import { getStakeAccount, stakeAccountState } from "@soceanfi/solana-stake-sdk";

// stakeAccountPubkey defined elsewhere

const { data: stakeAccount } = await getStakeAccount(
  UNSTAKE_PROGRAM.provider.connection,
  stakeAccountPubkey
);

const tx = await unstakeTx(UNSTAKE_PROGRAM, {
  stakeAccount: stakeAccountPubkey,
  poolAccount: UNSTAKE_POOL_ADDRESS,
  unstaker: UNSTAKE_PROGRAM.provider.wallet.publicKey,
  protocolFee: PROTOCOL_FEE,

  // You can optionally add a `referrer` pubkey
  // to receive referral bonuses in SOL.
  // You can use `applyProtocolFee()` to calculate how much
  // this amount will be for a given unstake fee amount.
  // (see example in #estimate-fees above)
  referrer: MY_SOL_ACC_PUBKEY,
});

// deactivateStakeAccount is a permissionless crank instruction that allows
// the pool to deactivate the stake accounts it receives so that it can
// reclaim the SOL liquidity on the next epoch.
// You can help our operations by including it in the same transaction
// that unstakes the stake account (:
const {
  epochInfo: { epoch },
} = await UNSTAKE_PROGRAM.provider.connection.getEpochInfo();
const stakeState = stakeAccountState(stakeAccount, new BN(epoch));
if (stakeState === "active" || stakeState === "activating") {
  tx.add(
    await deactivateStakeAccountTx(unstakeProgram, {
      stakeAccount: stakeAccountPubkey,
      poolAccount: UNSTAKE_POOL_ADDRESS,
    })
  );
}

const signature = await UNSTAKE_PROGRAM.provider.sendAndConfirm(tx);

UnstakeWsol

We also provide an instruction for unstaking SOL to wrapped SOL token accounts. Its usage is the same as Unstake; simply replace unstakeTx() with unstakeWsolTx() in the example above.

Reclaim Stake

ReclaimStake is a permissionless crank instruction that returns SOL from previously unstaked stake accounts that have now successfully deactivated to the pool's SOL reserves. This increases the liquidity available for unstaking and gives users a better rate.

import { Transaction } from "@solana/web3.js";
import {
  fetchLiquidityPoolStakeAccounts,
  reclaimStakeAccountTx,
} from "@unstake-it/sol";

const { inactive: stakeAccountsToReclaim } =
  await fetchLiquidityPoolStakeAccounts(UNSTAKE_PROGRAM, UNSTAKE_POOL_ADDRESS);

// You can fit 5 reclaimStakeAccount instructions into one tx before running into packet size limits
const CHUNK_SIZE = 5;
const chunks = [];
for (let i = 0; i < stakeAccountsToReclaim.length; i += CHUNK_SIZE) {
  chunks.push(stakeAccountsToReclaim.slice(i, i + CHUNK_SIZE));
}

const signatures = await Promise.all(
  chunks.map(async (chunk) => {
    const txs = await Promise.all(({ accountId }) =>
      reclaimStakeAccountTx(UNSTAKE_PROGRAM, {
        poolAccount: UNSTAKE_POOL_ADDRESS,
        stakeAccount: accountId,
      })
    );
    const txToSend = txs.reduce(
      (combined, tx) => combined.add(tx),
      new Transaction()
    );
    return UNSTAKE_PROGRAM.provider.sendAndConfirm(txToSend);
  })
);

Take Flash Loan

Create a transaction that takes a flash loan, executes a passed transaction, and repays the flash loan with interest. Imagine if you wanted a transaction that takes a flash loan of 1000 SOL, arbs those 1000 SOL for 1 SOL profit and returns flash loan with interest. To do that, you would create the arb 1000 SOL for 1 SOL profit transaction with the assumption that the 1000 SOL is gonna be in your SOL account and then call takeFlashLoanTx(UNSTAKE_PROGRAM, 1000 SOL (in lamports), arbTransaction, flashLoanAccounts) to mutate the arbTransaction and then send it.

import { Transaction } from "@solana/web3.js";
import { takeFlashLoanTx, findProtocolFeeAccount } from "@unstake-it/sol";
import BN from "bn.js";

const [protocolFeeAddr] = await findProtocolFeeAccount(
  UNSTAKE_PROGRAM.programId
);
const protocolFee = {
  publicKey: protocolFeeAddr,
  account: await UNSTAKE_PROGRAM.account.protocolFee.fetch(protocolFeeAddr),
};

const arbTransaction = new Transaction();
// add your instructions to arbTransaction here
// ...

const tx = await takeFlashLoanTx(
  UNSTAKE_PROGRAM,
  new BN(1_000_000_000_000),
  arbTransaction,
  {
    to: wallet.publicKey,
    poolAccount: UNSTAKE_POOL_ADDRESS,
    protocolFee,
  }
);

return UNSTAKE_PROGRAM.provider.sendAndConfirm(tx);