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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@solsdk/liquidity_sdk

v1.2.5

Published

Orca Whirlpool clmm library for automated position management

Readme

@solsdk/liquidity_sdk

A robust TypeScript SDK for managing and analyzing concentrated liquidity positions on Solana's Orca Whirlpools. Designed for reliability, performance, and developer experience.

Features

  • Effortless retrieval and analysis of concentrated liquidity positions for any wallet
  • Accurate calculation of position balances and price ranges
  • Seamless conversion between tick indices and prices
  • Real-time fee quote fetching and human-readable formatting
  • Comprehensive position status analysis (in-range, out-of-range)
  • Automated position management with rebalancing capabilities
  • Token swapping and portfolio management utilities
  • Built with strict TypeScript types and best practices

Installation

npm install @solsdk/liquidity_sdk
# or
yarn add @solsdk/liquidity_sdk
# or
bun add @solsdk/liquidity_sdk

Quick Start

Fetching Positions

import { getOrcaPositions } from "@solsdk/liquidity_sdk";
import { createSolanaRpc, mainnet } from "@solana/kit";

// Initialize RPC connection
const rpc = createSolanaRpc(mainnet("https://api.mainnet-beta.solana.com"));

// Specify wallet address
const walletAddress = "YOUR_WALLET_ADDRESS";

// Fetch all positions for the wallet
const positions = await getOrcaPositions(walletAddress, rpc);

for (const position of positions) {
  console.log(`Position: ${position.name}`);
  console.log(`Current Price: ${position.currentMarketPrice}`);
  console.log(`In Range: ${position.isInRange}`);
  console.log(`Lower Price: ${position.lowerPrice}`);
  console.log(`Upper Price: ${position.upperPrice}`);
}

Fetching Detailed Positions with Analysis

import { getDetailedPositions } from "@solsdk/liquidity_sdk";
import { createSolanaRpc, mainnet } from "@solana/kit";

const rpc = createSolanaRpc(mainnet("https://api.mainnet-beta.solana.com"));
const walletAddress = "YOUR_WALLET_ADDRESS";

// Fetch detailed positions with additional analysis
const detailedPositions = await getDetailedPositions(walletAddress, rpc);

for (const position of detailedPositions) {
  console.log(`Position: ${position.name}`);
  console.log(`Token A Amount: ${position.tokenAAmount}`);
  console.log(`Token B Amount: ${position.tokenBAmount}`);
  console.log(`Position Value USD: $${position.positionValueUSD.est}`);
  console.log(`Total Fees USD: $${position.totalFeesUSD}`);
  console.log(`Relative Position: ${position.relativePosition}`);
  console.log(`Range: ${position.range}`);
}

Opening a New Position

import {
  openPosition,
  fetchOrcaPoolByAddress,
  getOnChainPool,
  setWhirlpoolsConfig,
  setDefaultFunder
} from "@solsdk/liquidity_sdk";
import {
  createKeyPairSignerFromBytes,
  createSolanaRpc,
  mainnet,
  address
} from "@solana/kit";
import * as dotenv from "dotenv";

dotenv.config();

async function openNewPosition() {
  // Set up Orca Whirlpools configuration
  await setWhirlpoolsConfig("solanaMainnet");
  
  const bytes = await loadKeypairFromFile("./path/to/keypair.json");
  const wallet = await createKeyPairSignerFromBytes(bytes);
  const rpcUrl = process.env.RPC_URL || "https://api.mainnet-beta.solana.com";
  const rpc = createSolanaRpc(mainnet(rpcUrl));
  
  // Set default funder for transactions
  setDefaultFunder(wallet);

  const whirlpoolAddress = address("POOL_ADDRESS");
  const pool = await fetchOrcaPoolByAddress(whirlpoolAddress);
  const onChainPool = await getOnChainPool(pool, rpc);

  const result = await openPosition({
    rpc,
    whirlpoolAddress,
    params: { tokenA: 1000000n }, // Amount in smallest units
    price: onChainPool.price,
    lowerMultiple: 0.9,
    upperMultiple: 1.1,
    slippageToleranceBps: 100,
    wallet,
  });
  
  console.log(`Position created with mint: ${result.positionMint}`);
  console.log(`Transaction signature: ${result.signature}`);
}

openNewPosition();

Opening Position with Base Token

import {
  openPositionWithBaseToken,
  fetchNonZeroTokenBalances,
  SOL_MINT_ADDRESS,
  USDC_MINT_ADDRESS
} from "@solsdk/liquidity_sdk";
import { createSolanaRpc, mainnet, address } from "@solana/kit";

const rpc = createSolanaRpc(mainnet("https://api.mainnet-beta.solana.com"));
const wallet = await createKeyPairSignerFromBytes(bytes);
const whirlpoolAddress = address("POOL_ADDRESS");

// Get wallet balances
const balances = await fetchNonZeroTokenBalances(wallet.address);
const usdcBalance = balances.find(b => b.address === USDC_MINT_ADDRESS);

if (usdcBalance) {
  const result = await openPositionWithBaseToken({
    rpc,
    whirlpoolAddress,
    wallet,
    walletByteArray: bytes,
    baseTokenAddress: address(USDC_MINT_ADDRESS),
    baseTokenAmount: usdcBalance.balance.uiAmount,
    lowerMultiple: 0.95, // 5% below current price
    upperMultiple: 1.05, // 5% above current price
    maxGasUSD: 10,
    maxPriceImpact: 0.01 // 1% max slippage
  });
  
  console.log(`Position opened: ${result.positionMint}`);
  console.log(`Swap loss: ${result.swapLoss}`);
}

Closing Positions

import { closePositionAndHarvestYield, closePositionGracefully } from "@solsdk/liquidity_sdk";

// Close a specific position
const closeResult = await closePositionAndHarvestYield(rpc, wallet, position);
console.log(`Position closed: ${closeResult.signature}`);
console.log(`Fee USD: ${closeResult.details.feeUSD}`);

// Close position with error handling and retries
const gracefulResult = await closePositionGracefully(rpc, wallet, position, 3);
if (gracefulResult) {
  console.log(`Position closed gracefully: ${gracefulResult.signature}`);
}

Token Management

import {
  fetchTokensWithPrices,
  fetchTokensWithBalanceByWallet,
  getUSDPrice
} from "@solsdk/liquidity_sdk";

// Fetch tokens with USD prices
const tokensWithPrices = await fetchTokensWithPrices(walletAddress);
const totalValue = tokensWithPrices.reduce((sum, token) => sum + token.usdValue, 0);
console.log(`Total portfolio value: $${totalValue.toFixed(2)}`);

// Get USD price for a specific token
const solPrice = await getUSDPrice({ mintAddress: SOL_MINT_ADDRESS });
console.log(`SOL price: $${solPrice}`);

// Fetch all token balances
const allBalances = await fetchTokensWithBalanceByWallet(walletAddress);
for (const token of allBalances) {
  console.log(`${token.symbol}: ${token.balance.uiAmountString}`);
}

API Reference

Core Functions

Position Management

  • getOrcaPositions(walletAddress, rpc, pools?, funder?) - Fetch basic Orca positions
  • getDetailedPositions(walletAddress, rpc, pools?, rangeChoices?, funder?) - Fetch positions with detailed analysis
  • openPosition(params) - Open a new liquidity position
  • openPositionWithBaseToken(params) - Open position using a base token with automatic swapping
  • closePositionAndHarvestYield(rpc, wallet, position) - Close position and harvest fees
  • closePositionGracefully(rpc, wallet, position, maxRetries?) - Close position with error handling

Pool Information

  • fetchOrcaPools() - Fetch all available Orca pools
  • fetchOrcaPoolByAddress(address) - Fetch specific pool by address
  • getOnChainPool(whirlpool, rpc) - Get on-chain pool data with current price
  • getLiquidityInTicks(params) - Get liquidity distribution across ticks

Token Operations

  • fetchTokensWithPrices(walletAddress, rpcUrl?) - Get tokens with USD prices
  • fetchTokensWithBalanceByWallet(walletAddress, rpcUrl?) - Get all token balances
  • fetchNonZeroTokenBalances(walletAddress, rpcUrl?) - Get non-zero token balances with prices
  • getUSDPrice({ mintAddress }) - Get USD price for a token

Utility Functions

  • convertRawToDecimal(rawAmount, decimals) - Convert raw token amount to decimal
  • convertDecimalToRaw(decimalAmount, decimals) - Convert decimal amount to raw
  • analyzePositionBalance(position) - Analyze position relative to price range
  • divergenceLoss(p, p_i, p_a, p_b, depositA, depositB) - Calculate impermanent loss
  • getEstimatedYield(params) - Calculate estimated position yield

Types and Interfaces

Position Types

interface OrcaPosition {
  isInRange: boolean;
  name: string;
  fees: { feeAmountA: number; feeAmountB: number };
  address: string;
  data: {
    liquidity: string;
    positionMint: string;
    tickLowerIndex: number;
    tickUpperIndex: number;
  };
  tokenA: WhirlpoolToken;
  tokenB: WhirlpoolToken;
  currentMarketPrice: string;
  lowerPrice: number;
  upperPrice: number;
  whirlpool: {
    address: string;
    price: string;
    tickSpacing: number;
  };
}

interface DetailedPosition extends OrcaPosition {
  createdAt: Date;
  tokenAPrice: number;
  tokenBPrice: number;
  tokenAAmount: number;
  tokenBAmount: number;
  positionValueUSD: { min: number; est: number };
  totalFeesUSD: number;
  relativePosition: number;
  range: number;
}

Parameter Types

interface OpenPositionParams {
  rpc: any;
  whirlpoolAddress: Address;
  params: IncreaseLiquidityQuoteParam;
  price: number;
  lowerMultiple: number;
  upperMultiple: number;
  slippageToleranceBps: number;
  wallet: TransactionSigner;
  swapDustToAddress?: string;
  walletByteArray?: Uint8Array;
  maxGasUSD?: number;
}

interface OpenUSDCPositionParams {
  rpc: Rpc;
  whirlpoolAddress: Address;
  wallet: TransactionSigner;
  walletByteArray: Uint8Array;
  baseTokenAddress: Address;
  baseTokenAmount: number;
  lowerMultiple: number;
  upperMultiple: number;
  maxSwapAttempts?: number;
  maxGasUSD?: number;
  maxPriceImpact?: number;
  swapDustToAddress?: Address;
}

Constants

// Token addresses
export const SOL_MINT_ADDRESS = "So11111111111111111111111111111111111111112";
export const USDC_MINT_ADDRESS = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
export const PYUSD_MINT_ADDRESS = "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo";

// Program IDs
export const TOKEN_PROGRAM_ID: Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
export const ASSOCIATED_TOKEN_PROGRAM_ID: Address<"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL">;

// Error codes
export const TOKEN_MAX_EXCEEDED_ERROR: bigint;
export const TOKEN_MIN_SUBCEEDED_ERROR: bigint;
export const INVALID_START_TICK_ERROR: bigint;
export const INSUFFICIENT_FUNDS_ERROR = 1n;

Common Use Cases

Automated Position Rebalancing

import {
  getDetailedPositions,
  closePositionGracefully,
  openPositionWithBaseToken,
  SOL_MINT_ADDRESS,
  USDC_MINT_ADDRESS
} from "@solsdk/liquidity_sdk";

async function rebalancePositions(walletAddress: string, rpc: any, wallet: TransactionSigner) {
  const positions = await getDetailedPositions(walletAddress, rpc);
  const baseTokens = [SOL_MINT_ADDRESS, USDC_MINT_ADDRESS];
  
  for (const position of positions) {
    // Check if position is out of range or at extreme ends
    if (!position.isInRange || position.relativePosition <= 0.1 || position.relativePosition >= 0.9) {
      console.log(`Rebalancing position: ${position.name}`);
      
      // Determine base token (prefer stablecoins)
      const isTokenABase = baseTokens.includes(position.tokenA.address);
      const baseToken = isTokenABase ? position.tokenA : position.tokenB;
      
      // Close existing position
      const closeResult = await closePositionGracefully(rpc, wallet, position);
      if (!closeResult) continue;
      
      // Get updated balances
      const balances = await fetchNonZeroTokenBalances(walletAddress);
      const baseBalance = balances.find(b => b.address === baseToken.address);
      
      if (baseBalance && baseBalance.balance.uiAmount > 0) {
        // Open new position with tighter range
        await openPositionWithBaseToken({
          rpc,
          whirlpoolAddress: address(position.whirlpool.address),
          wallet,
          walletByteArray: bytes,
          baseTokenAddress: address(baseToken.address),
          baseTokenAmount: baseBalance.balance.uiAmount,
          lowerMultiple: 0.98, // 2% below current price
          upperMultiple: 1.02, // 2% above current price
          maxGasUSD: 10
        });
      }
    }
  }
}

Portfolio Analysis

import { getDetailedPositions, fetchTokensWithPrices } from "@solsdk/liquidity_sdk";

async function analyzePortfolio(walletAddress: string, rpc: any) {
  // Get all positions
  const positions = await getDetailedPositions(walletAddress, rpc);
  
  // Get token balances
  const tokens = await fetchTokensWithPrices(walletAddress);
  
  // Calculate total values
  const totalPositionValue = positions.reduce((sum, pos) => sum + pos.positionValueUSD.est, 0);
  const totalTokenValue = tokens.reduce((sum, token) => sum + token.usdValue, 0);
  const totalFees = positions.reduce((sum, pos) => sum + pos.totalFeesUSD, 0);
  
  console.log(`Portfolio Analysis:`);
  console.log(`Total Position Value: $${totalPositionValue.toFixed(2)}`);
  console.log(`Total Token Value: $${totalTokenValue.toFixed(2)}`);
  console.log(`Total Portfolio Value: $${(totalPositionValue + totalTokenValue).toFixed(2)}`);
  console.log(`Total Fees Earned: $${totalFees.toFixed(2)}`);
  
  // Analyze position health
  const inRangePositions = positions.filter(p => p.isInRange);
  const outOfRangePositions = positions.filter(p => !p.isInRange);
  
  console.log(`\nPosition Health:`);
  console.log(`In Range: ${inRangePositions.length}/${positions.length}`);
  console.log(`Out of Range: ${outOfRangePositions.length}/${positions.length}`);
  
  // Show position details
  for (const position of positions) {
    console.log(`\n${position.name}:`);
    console.log(`  Value: $${position.positionValueUSD.est.toFixed(2)}`);
    console.log(`  In Range: ${position.isInRange}`);
    console.log(`  Relative Position: ${(position.relativePosition * 100).toFixed(1)}%`);
    console.log(`  Range Width: ${(position.range * 100).toFixed(1)}%`);
    console.log(`  Fees: $${position.totalFeesUSD.toFixed(2)}`);
  }
}

Yield Calculation

import { getEstimatedYield, divergenceLoss } from "@solsdk/liquidity_sdk";

// Calculate estimated yield for a position
const yieldEstimate = await getEstimatedYield({
  poolAddress: "POOL_ADDRESS",
  tokenAAmountUSD: 1000,
  tokenBAmountUSD: 1000,
  range: 0.1, // 10% total range
  statsType: "24h"
});

console.log(`Estimated 24h yield: ${yieldEstimate}%`);

// Calculate impermanent loss
const ilResult = divergenceLoss(
  1.05,    // current price (5% higher)
  1.0,     // initial price
  0.95,    // lower bound
  1.05,    // upper bound
  1000,    // initial deposit A
  1000     // initial deposit B
);

console.log(`Impermanent Loss: ${ilResult.totalIL.toFixed(2)}%`);
console.log(`LP Value: $${ilResult.lpValue.toFixed(2)}`);
console.log(`HODL Value: $${ilResult.holdValue.toFixed(2)}`);

Error Handling Best Practices

import { OrcaError, SolanaError } from "@solsdk/liquidity_sdk";

async function safePositionOperation() {
  try {
    const result = await openPosition(params);
    return result;
  } catch (error) {
    if (error instanceof OrcaError) {
      console.error(`Orca Error [${error.code}]: ${error.message}`);
      
      // Handle specific error codes
      if (error.code === TOKEN_MAX_EXCEEDED_ERROR) {
        console.log("Token amount exceeds maximum, reducing amount...");
        // Retry with smaller amount
      }
    } else if (error instanceof SolanaError) {
      console.error(`Solana Error: ${error.message}`);
      console.error(`Cause:`, error.cause);
      
      // Handle network or transaction errors
      if (error.message.includes("insufficient funds")) {
        console.log("Insufficient funds for transaction");
      }
    } else {
      console.error(`Unexpected error:`, error);
    }
    
    throw error;
  }
}

Advanced Features

Liquidity Depth Visualization

The library includes tools for analyzing liquidity distribution:

import { getLiquidityInTicks } from "@solsdk/liquidity_sdk";

const liquidityData = await getLiquidityInTicks({
  poolAddress: address("POOL_ADDRESS"),
  rpc
});

console.log(`Current price: ${liquidityData.currentPrice}`);
console.log(`Liquidity points: ${liquidityData.data.length}`);

// Export for visualization
const fs = require('fs');
fs.writeFileSync('liquidity_points.json', JSON.stringify(liquidityData.data, null, 2));

The exported data can be used with visualization tools to create liquidity depth charts showing bid/ask depth around the current price.

Transaction Simulation

import { simulateTransaction } from "@solsdk/liquidity_sdk";

// Simulate transaction before execution
const simulation = await simulateTransaction(rpc, wallet, instructions);
console.log(`Estimated fee: $${simulation.estimatedFee}`);

Why @solsdk/liquidity_sdk?

  • Strict Type Safety: 100% TypeScript, no any, no type assertions, no surprises
  • Modern API: Clean, minimal, and intuitive interface using latest Solana libraries
  • Performance: Optimized for speed and low-latency operations
  • Reliability: Built with best practices (KISS, DRY, SOLID) and thoroughly tested
  • Developer Experience: Clear error messages, predictable behavior, and comprehensive type hints
  • Production Ready: Used in live trading environments with robust error handling
  • Comprehensive: Covers the full lifecycle from position analysis to automated management

Requirements

  • Node.js 16+ or Bun
  • TypeScript 5.0+
  • Solana RPC endpoint (mainnet or devnet)
  • Wallet keypair for transaction signing

License

MIT


Ready to get started?
Install @solsdk/liquidity_sdk and build your next Solana liquidity management tool with confidence!