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

@solsonar/solana-clmm-helpers

v0.1.0

Published

Synchronous tick array PDA derivation, multi-tick swap math, and helpers for Solana concentrated liquidity DEXes (Orca Whirlpool, Raydium CLMM).

Downloads

36

Readme

@solsonar/solana-clmm-helpers

Synchronous, zero-RPC tick array PDA derivation for Solana concentrated liquidity DEXes: Orca Whirlpool and Raydium CLMM.

import { whirlpool, raydiumClmm } from '@solsonar/solana-clmm-helpers';
import { PublicKey } from '@solana/web3.js';

// Derive 3 forward + 3 reverse tick arrays for a Whirlpool from its current state
const whirlpoolPool = new PublicKey('Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE');
const arrays = whirlpool.deriveTickArrays(whirlpoolPool, /* tickCurrent */ -24812, /* tickSpacing */ 4);
// → [{ startTick: -24992, address: PublicKey }, ..., { startTick: -23936, address: PublicKey }]

// Same for Raydium CLMM (different program, different PDA encoding)
const clmmPool = new PublicKey('3UwfrdLTpAjxTRni1boc5HUWe6hzc4HgE5yLdvEp2Noc');
const clmmArrays = raydiumClmm.deriveTickArrays(clmmPool, /* tickCurrent */ -96869, /* tickSpacing */ 10);
// → [{ startTick: -97200, address: PublicKey }, ..., { startTick: -95400, address: PublicKey }]

What this package does

Three pure functions per DEX (Whirlpool and Raydium CLMM), no I/O, no SDK runtime calls:

  1. getStartTick(currTick, tickSpacing) — computes the start tick index of the array containing currTick. Rounds toward -Infinity for negative ticks (matches on-chain convention).

  2. getTickArrayAddress(pool, startTickIndex, programId?) — derives the PDA for a single tick array. Encodings differ per DEX:

    • Whirlpool seeds: ["tick_array", pool, start_tick.toString()]
    • Raydium CLMM seeds: ["tick_array", pool, i32_BE(start_tick)] (big-endian, not little-endian)
  3. deriveTickArrays(pool, currTick, tickSpacing, opts?) — returns 3 forward + 3 reverse tick array addresses (configurable via opts.forwardCount / opts.reverseCount). Filters out indices outside [MIN_TICK_INDEX, MAX_TICK_INDEX].

What this package does not do

  • No RPC calls. Caller fetches pool state once (via getAccountInfo or a Yellowstone subscription) and passes tick_current + tick_spacing in.
  • No swap math. This package only derives addresses; pair it with a CLMM quote engine for output amounts.
  • No bitmap_extension parsing yet. Phase 1 returns the naive ±N range from current tick — uninitialized arrays in that range simply won't have on-chain accounts and the consumer must handle that. Bitmap-aware skipping is on the roadmap.

Why

Backrun arbitrage bots need tick array account addresses to build a CLMM swap instruction. Yellowstone subscriptions can populate the cache reactively when a pool sees swap traffic, but pools targeted only as the sell leg of an arbitrage may never be seen in shred triggers — leaving the cache empty when the executor needs them. Eager preload via these helpers fills that gap.

The math is small but the encoding is easy to get wrong. Reference Cetipoo's Rust bot uses seed = "pool_tick_array" for the bitmap extension PDA — a different concept that's easy to confuse with regular tick array PDAs (seed = "tick_array"). This package uses the verified seed and encoding for each DEX, validated against real on-chain accounts.

API

Whirlpool

import { whirlpool } from '@solsonar/solana-clmm-helpers';

whirlpool.WHIRLPOOL_PROGRAM_ID    // PublicKey
whirlpool.TICK_ARRAY_SIZE         // 88
whirlpool.MIN_TICK_INDEX          // -443636
whirlpool.MAX_TICK_INDEX          //  443636

whirlpool.getStartTick(currTick, tickSpacing) // → number
whirlpool.getTickArrayAddress(pool, startTickIndex, programId?) // → PublicKey
whirlpool.deriveTickArrays(pool, currTick, tickSpacing, {
  forwardCount: 3,    // default 3
  reverseCount: 3,    // default 3
  programId: ...,     // default WHIRLPOOL_PROGRAM_ID
}) // → Array<{ startTick: number, address: PublicKey }>

Raydium CLMM

import { raydiumClmm } from '@solsonar/solana-clmm-helpers';

raydiumClmm.RAYDIUM_CLMM_PROGRAM_ID  // PublicKey
raydiumClmm.TICK_ARRAY_SIZE          // 60
raydiumClmm.MIN_TICK_INDEX           // -443636
raydiumClmm.MAX_TICK_INDEX           //  443636

// Same shape as Whirlpool
raydiumClmm.getStartTick(currTick, tickSpacing)
raydiumClmm.getTickArrayAddress(pool, startTickIndex, programId?)
raydiumClmm.deriveTickArrays(pool, currTick, tickSpacing, opts?)

Verified against mainnet

The PDA derivation is verified against real on-chain accounts at publish time:

  • Whirlpool SOL/USDC Czfq3xZZ... → 6/6 tick array addresses match
  • Raydium CLMM BONK/USDC 3UwfrdLT... → 6/6 tick array addresses match

See test/whirlpool.test.js and test/raydium-clmm.test.js for the full address-by-address assertions.

License

MIT