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

gum-sdk

v0.2.0

Published

TypeScript SDK for the Bubblegum prediction-market token launchpad on Solana (bonding-curve $TICKER + virtual-liquidity YES/NO CPMM).

Readme

gum-sdk

TypeScript SDK for the Bubblegum prediction-market token launchpad on Solana — a two-axis market: a bonding-curve $TICKER token feeding a virtual-liquidity YES/NO decision-token CPMM.

It wraps two Anchor programs:

| Program | mainnet-beta | devnet | |---|---|---| | bubblegum_curve | 71ywu6cFWETLyiz1KcuMwq2wfguYfra7b1bCPinVqKm3 | En6VRZMMqRcQ12squMf8wt5qqbKhcuaLKANxb5SUvxqN | | bubblegum_cpmm | Dyw8vChGKLMWkkuHZBKS8EdLj489dcVxgHyUWBuCVp3Y | 7g1LRg6NWDmuGaebmP42gq5CyeysteRRr5ZtZ42rJ6eq |

Program IDs are read from each bundled IDL's address field — the single source of truth. The SDK never fetches an IDL at runtime and never hardcodes a program ID separately from its IDL.

📖 Full developer guide with runnable scripts for every flow → DOCS.md (launch · buy/sell · buy-with-$TICKER · redeem-to-$TICKER · claim prize · settle · refund).

Status

  • Phase 1 — read path: network config, PDA derivation, typed account fetch/decode (bigint boundary), pure quote math, event parsing/streaming, typed errors.
  • Phase 2 — write path: instruction builders for all 31 instructions (18 curve + 13 cpmm) with automatic account/ATA resolution, composite curve→CPMM quotes, and high-level flows (launchMarket, buy, sell, swap, buyDirect/sellDirect, settle, redeemAndClaim, refund, graduate). Builders return { instructions, signers } — the SDK never signs or sends.
  • 🚧 Next: devnet e2e + math/PDA parity test suites; optional Meteora graduation pipeline.

Install

npm install gum-sdk @coral-xyz/anchor @solana/web3.js

@coral-xyz/anchor (>=0.31 <0.33) and @solana/web3.js (^1.95) are peer dependencies.

Quickest start — just an RPC URL + a private key

No Connection, no Keypair, no wiring. Pass an RPC endpoint and your wallet private key; then call one-liners that take human units (SOL, tokens, days) and build + sign + send + confirm for you:

import { Bubblegum } from "gum-sdk";

const gum = Bubblegum.connect({
  rpc: process.env.RPC_URL!,             // any Solana RPC endpoint
  privateKey: process.env.PRIVATE_KEY!,  // base58 (Phantom/CLI export) or a "[..]" byte array
  // network is inferred from the URL ("…devnet…" → devnet); pass network: "devnet" to force it
});

// launch a coin
const { conditionId, conditionIdHex, tickerMint } = await gum.launch({
  question: "Will SOL close above $250 this year?",
  name: "SOL 250", ticker: "SOL250",
  metadataUri: "https://.../meta.json",
  durationDays: 7,            // or endTime: Date | unix-seconds bigint
  // creator: somePubkey,     // optional fee recipient (devnet); omit ⇒ you are the creator
});

await gum.buy(conditionId, "yes", 0.5);     // buy YES with 0.5 SOL
await gum.sell(conditionId, "yes", 1000);   // sell 1000 YES tokens
await gum.swap(conditionId, "yes", 250);    // swap 250 YES → NO
const odds = await gum.odds(conditionId);   // { yes, no } in [0,1]

// settlement lifecycle
await gum.settle(conditionId, "yes");       // signer must be the cpmm admin
await gum.redeem(conditionId);              // winner: single-exit (claim bonus XOR redeem)
// await gum.refund(conditionId);           // if it expired un-graduated (market-value unwind)

// cranker / admin
// await gum.finalizeBonus(conditionId);    // graduator/admin: snapshot the bonus pool
// await gum.sweepBonus(conditionId);       // admin: recover leftover bonus after all winners exit

conditionId accepts a hex string or a Buffer; every call returns { signature, … } (plus quote/expectedOut for trades). Reads are on gum.client (accounts, pdas, quotes). Tunables: Bubblegum.connect({ …, slippageBps, priorityMicroLamports, commitment, network }) and per-trade { slippageBps, deadlineSecs }.

Browser / wallet-adapter: instead of a private key, use the lower-level client with any { publicKey, signTransaction } wallet: BubblegumClient.mainnet(connection).withWallet(wallet). Drop to the builder / flow layers when you need to batch instructions or a custom send strategy (return-instructions-only).

Token metadata — where metadataUri goes

metadataUri is a URL to a JSON file you host — the SDK/program never uploads anything. At launch the program stores that URI in the $TICKER mint's Token-2022 metadata extension (on-chain); wallets / explorers / your UI fetch the JSON to show the name, symbol, and image. Keep it ≤ 200 chars.

Host the JSON (and the image it points at) anywhere public: IPFS (Pinata / nft.storage / web3.storage), Arweave, or your own CDN / S3 / R2 — then pass the resulting https://… (or ipfs://…) URL.

// metadata.json (Metaplex fungible-token shape)
{
  "name": "BTC 100k",
  "symbol": "BTC100",
  "description": "Will BTC close above $100k this year?",
  "image": "https://your-cdn/btc100.png"
}

Typical flow: upload the image → get its URL → put it in metadata.json → upload that → pass its URL as metadataUri. (The Bubblegum frontend automates this via its own uploader/R2; a standalone SDK user brings their own hosting.)

Networks

devnet and mainnet run the same program version; only the program IDs differ, so every PDA (curve, market, mints, vaults, config) resolves to a different address per network. The SDK derives all of them from the chosen network's bundled IDL — pick a network at construction and everything follows:

BubblegumClient.mainnet(conn);                 // mainnet-beta
BubblegumClient.devnet(conn);                  // devnet
BubblegumClient.forNetwork("devnet", conn);    // env-driven switch

Watch for per-market / per-network values that are NOT constants: graduation threshold (devnet 10 SOL vs mainnet 75 SOL — read BondingCurve.graduationLamports), permissioned (devnet false, mainnet true), and the graduator key.

Quick start

import { Connection } from "@solana/web3.js";
import { BubblegumClient } from "gum-sdk";

// mainnet needs a paid RPC (Helius / Triton / QuickNode); public RPC rate-limits.
const conn = new Connection(process.env.RPC_URL!, "confirmed");
const sdk = BubblegumClient.mainnet(conn);

// Read protocol config
const cfg = await sdk.accounts.curveGlobalConfig();
console.log(cfg.graduator.toBase58(), cfg.permissioned, cfg.defaultGraduationLamports);

// Derive every PDA for a market from its 32-byte condition_id
const pdas = sdk.pdas.forMarket(conditionId);

// Read a market and preview a trade (pure math — no RPC)
const { curve, market } = await sdk.accounts.marketView(conditionId);
const { tokensOut } = sdk.quote.buyExactIn({
  virtualSol: curve.virtualSol,
  virtualTokens: curve.virtualTokens,
  solIn: 1_000_000_000n, // 1 SOL (net of fee)
  tokensRemainingReal: /* CURVE_SELLABLE_TOKENS - curve.realTokensSold */ 0n,
});

Write path

Builders take intent and resolve every account, ATA, and fee vault. They return instructions; you sign and send.

import { BubblegumClient, buildTransaction } from "gum-sdk";

const sdk = BubblegumClient.mainnet(conn);

// Launch a market (prepareMarket + activateMarket in one atomic tx)
const { conditionId, instructions } = await sdk.flows.launchMarket({
  payer: wallet.publicKey,          // tx signer; condition_id binds to the payer
  // creator: someOtherPubkey,      // optional fee recipient (devnet decouple); omit ⇒ payer is the creator
  question: "Will it rain in NYC on July 4?",
  name: "NYC Rain", ticker: "RAIN",
  metadataUri: "https://.../metadata.json",
  endTime: BigInt(Math.floor(Date.now() / 1000) + 7 * 86_400),
});

// Buy YES with 0.5 SOL — quote + minOut + ATA setup handled for you
const { instructions: buyIxs, quote } = await sdk.flows.buy({
  conditionId, user: wallet.publicKey, side: "yes",
  solIn: 500_000_000n, slippageBps: 50,
});
console.log(`expect ~${quote.sideTokensOut} YES (min ${quote.minSideOut})`);

const tx = await buildTransaction({ connection: conn, feePayer: wallet.publicKey, instructions: buyIxs });
// ... wallet.signTransaction(tx) and send.

// Winner payout (claimWinnerBonus before redeemPosition, in one tx)
const { instructions: redeemIxs } = await sdk.flows.redeemAndClaim({ conditionId, user: wallet.publicKey });

// Low-level single builders are on sdk.curve.* / sdk.cpmm.*
const settle = await sdk.cpmm.settleMarket({ conditionId, authority, winningSide: "yes" });

Subpath exports (tree-shakeable)

import { quoteBuyExactIn, TICKER_SCALE } from "gum-sdk/math"; // pure, no Anchor (~1 KB)
import { computeConditionId, BubblegumPdas } from "gum-sdk/pdas"; // derivation only

Layout

src/
  config.ts      networks → program IDs (from IDL), config PDAs, external IDs
  client.ts      BubblegumClient facade (pdas/accounts/events/quote + curve/cpmm/flows)
  idl/           bundled live IDLs (devnet + mainnet)
  pdas.ts        seeds, condition_id, per-market PDA derivation        [pure]
  math/          curve + cpmm + composite curve→cpmm quotes            [pure]
  accounts/      typed fetch + decode (bigint, string enums)
  events.ts      Anchor log parse + WS subscribe
  errors.ts      CurveErrors / CpmmErrors, isCurveError, decodeError   [pure]
  instructions/  curve (18) + cpmm (13) builders + account resolver
  flows/         high-level launch / trade / settle / redeem flows
  tx/            optional compute-budget + transaction-assembly helpers
  util.ts        nonce, deadline, unit conversions, launch validation

Notes for integrators

  • Mints are Token-2022. Token name/symbol/uri live on the mint's metadata extension, not a Metaplex PDA.
  • Graduation threshold is per-market — read BondingCurve.graduationLamports, don't assume 75 SOL (devnet default is 10 SOL).
  • Pre-graduation trading routes through the curve (buy_decision / sell_decision), which CPIs the CPMM. Direct CPMM trading is blocked until the curve reaches its graduation threshold.
  • Winner payout is single-exit. Once the bonus is finalized, redeemAndClaim (and gum.redeem) calls claimWinnerBonus, which CPIs redeem_position internally (burns all winning tokens + pays reserve + bonus) — it never also calls redeemPosition for the same user. Before finalization it just redeems.
  • Refunds unwind at true market value. An expired, non-graduated market refunds via cpmm burn_for_refund → curve sell math (not a naive pro-rata split), so cheap-side tokens can't drain honest holders' principal.

License

MIT