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

@piratecrewfun/pirate-sdk

v2.0.0

Published

TypeScript SDK for the PirateCrew Blockchain API (v2). Noun-based REST, snake_case wire shapes, cursor pagination, polymorphic resources, typed errors, webhook signature verification.

Readme

@piratecrewfun/pirate-sdk

TypeScript client for the PirateCrew Blockchain API (v2). Noun-based REST, response/error envelopes, cursor pagination, polymorphic resources, typed errors, and a webhook signature verifier. Talks to api.piratecrew.fun; full reference at docs.piratecrew.fun.

Heads-up: 2.0 is a breaking rewrite. v1.x (/v1/*) is no longer wrapped. See Migrating from 1.x below.

Install

npm install @piratecrewfun/pirate-sdk

No peer dependencies. Pure ESM, Node 20+.

Quick start

import { PirateCrew } from "@piratecrewfun/pirate-sdk";

const pc = new PirateCrew({ apiKey: process.env.PIRATE_API_KEY! });

// Fetch a pool — polymorphic across DBC and DAMM v2; check `pool.type`.
const pool = await pc.pools.get("7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU");

// Cursor-paginate every airdrop, transparently following next_cursor.
for await (const airdrop of pc.airdrops.iterate({ limit: 50 })) {
  console.log(airdrop.id, airdrop.status);
}

// Build an unsigned claim tx for an end-user wallet.
const claim = await pc.airdrops.claims.create("N3hL…AsU:0", {
  claimer: "Bw5K…fG2",
  platform_id: "main",
  amount: "1000000000",
  platform_fee: 0,
  swap_to_sol: false,
  mode: "unsigned",
});
// claim.transaction is a base64 VersionedTransaction — sign client-side, then:
// await pc.transactions.submit({ transaction: signedBase64, bundle: false });

Options

new PirateCrew({
  apiKey: "pk_live_…",          // required
  baseUrl: "https://api.piratecrew.fun", // override for staging / local
  timeout: 30_000,              // ms, default 30s
  fetch: globalThis.fetch,      // inject a custom fetch
  defaultHeaders: { "X-Tenant": "acme" }, // applied to every request
});

Namespaces

| Namespace | Endpoints | |---|---| | pc.pools | list / iterate / create / get / verify / curveProgress / feeMetrics / createFeeClaim / listFeeClaims / iterateFeeClaims | | pc.airdrops | list / iterate / create / get + .claims.create + .vaultWithdrawals.create | | pc.merkleTrees | create / getProof | | pc.stakes | create / release / get | | pc.nfts | create / burn | | pc.goldLocks | create / release | | pc.tokens | create / get + .authorities.revoke(mint, "mint" \| "freeze", …) | | pc.transactions | submit / get + .simulations.create | | pc.swaps | quote / submit | | pc.pdas | platform / treasury / airdropConfig / claimAirdrop / userNftConfig / goldLockConfig / userLockConfig / goldVault / veGoldMint / userVeGold | | pc.accounts | get / batchFetch | | pc.webhookSubscriptions | list / iterate / create / get / update / delete / test / eventTypes |

Every method returns the unwrapped data field from the response envelope. The meta field (for request_id, external_uid, metadata) is currently exposed only on list() (via ListPage). Reach into pc.client for raw { data, meta } access if you need the request id.

Snake_case + u64-safe amounts

All wire shapes use snake_case, mirroring the OpenAPI spec verbatim:

await pc.swaps.quote({
  input_mint: "So11111111111111111111111111111111111111112",
  output_mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  amount: "1000000000",   // string — u64-safe, may exceed Number.MAX_SAFE_INTEGER
  slippage_bps: 50,
  swap_mode: "ExactIn",
});

Token / lamport quantities (amount, lamports, partner_quote_fee, …) are decimal strings, not numbers — they routinely exceed Number.MAX_SAFE_INTEGER.

Idempotency, request IDs, metadata

Every write method accepts an optional extras argument:

await pc.tokens.create(
  { payer, name, symbol, decimals: 6, initial_supply: "1000000000000", uri, revoke_mint_authority: false, revoke_freeze_authority: true, mode: "unsigned" },
  {
    idempotencyKey: crypto.randomUUID(),         // → Idempotency-Key header
    requestId: "req_my_correlation_id",          // → X-Request-Id header
    metadata: { campaign: "q2_2026", batch: "42" }, // → echoed on response + webhook events
    externalUid: "tx_abc123",                    // legacy field; prefer `metadata`
    signal: controller.signal,                   // AbortSignal
  }
);

Pagination

Two flavors. Use list() for one page of control; use iterate() to stream every page transparently:

// Manual:
const { data, meta } = await pc.pools.list({ limit: 25, type: "dbc" });
if (meta.has_more) {
  const next = await pc.pools.list({ limit: 25, type: "dbc", cursor: meta.next_cursor! });
}

// Auto-paginating async iterator:
for await (const pool of pc.pools.iterate({ type: "dbc" })) {
  console.log(pool.address, pool.status);
}

Errors

Non-2xx responses throw a typed PirateApiError with the full v2 error envelope flattened onto the instance:

import { PirateCrew, PirateApiError, PirateNetworkError } from "@piratecrewfun/pirate-sdk";

try {
  await pc.pools.get("11111111111111111111111111111111");
} catch (err) {
  if (err instanceof PirateApiError) {
    console.error(err.status);      // 403
    console.error(err.code);        // "forbidden"
    console.error(err.message);     // "Invalid or revoked API key"
    console.error(err.param);       // optional offending field
    console.error(err.doc_url);     // optional link to docs
    console.error(err.request_id);  // server-generated id — quote in bug reports
  } else if (err instanceof PirateNetworkError) {
    // DNS, socket, timeout — server never saw the request
  }
}

Webhook signature verification

The PirateCrew API signs every webhook delivery with HMAC-SHA256 and ships the result in X-Pirate-Signature: t=<unix>,v1=<hex>. The signing string is ${timestamp}.${rawBody}.

import express from "express";
import { verifyWebhookSignature } from "@piratecrewfun/pirate-sdk";

const app = express();

app.post(
  "/webhooks/piratecrew",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifyWebhookSignature({
      rawBody: req.body,                         // Buffer from express.raw
      header: req.header("X-Pirate-Signature")!,
      secret: process.env.PIRATE_WEBHOOK_SECRET!,
      // tolerance: 300, // seconds; default 300
    });
    if (!ok) return res.status(401).end();
    const event = JSON.parse(req.body.toString("utf8"));
    // handle event …
    res.status(204).end();
  }
);

verifyWebhookSignature performs a constant-time compare via crypto.timingSafeEqual and rejects timestamps older than tolerance seconds (replay protection). Returns false on mismatch / stale; throws on a malformed header.

Migrating from 1.x

1.x talked to /v1/* with hand-written request shapes. 2.x is a clean rewrite against /v2/* and is not API-compatible — but the underlying flows are the same, with cleaner names.

| 1.x call | 2.x equivalent | |---|---| | sdk.createCoin(...) (end-to-end helper) | pc.pools.create(...) → sign locally → pc.transactions.submit(...)pc.pools.verify(address, ...) | | sdk.pools.getState(addr) | pc.pools.get(addr) | | sdk.pools.getCurveProgress(addr) | pc.pools.curveProgress(addr) | | sdk.fees.claim({ kind: "dbc", ... }) | pc.pools.createFeeClaim(addr, { kind: "dbc", ... }) | | sdk.fees.split({ poolAddress, ... }) | pc.pools.createFeeClaim(addr, { kind: "dbc", splits: [...] }) (atomic claim+split) | | sdk.airdrops.initialize({...}) | pc.airdrops.create({...}) | | sdk.airdrops.claim({...}) | pc.airdrops.claims.create(airdropId, {...}) | | sdk.airdrops.withdrawUnclaimed({...}) | pc.airdrops.vaultWithdrawals.create(airdropId, {...}) | | sdk.staking.stake(...) / unstake(...) | pc.stakes.create(...) / pc.stakes.release(asset, ...) | | sdk.nfts.mint(...) / burn(...) | pc.nfts.create(...) / pc.nfts.burn(asset, ...) | | sdk.gold.lock(...) / unlock(...) | pc.goldLocks.create(...) / pc.goldLocks.release(user, ...) | | sdk.tokens.create(...) | pc.tokens.create(...) | | sdk.tokens.revokeAuthority(mint, type) | pc.tokens.authorities.revoke(mint, type, ...) | | sdk.swap.quote(...) / swap(...) | pc.swaps.quote(...) / pc.swaps.submit(...) | | sdk.tx.submit(...) | pc.transactions.submit({ transaction, bundle: false }) | | sdk.tx.submitJito(...) | pc.transactions.submit({ signed_transactions: [...], bundle: true }) | | sdk.tx.simulate(...) | pc.transactions.simulations.create(...) | | sdk.tx.status(sig) | pc.transactions.get(sig) | | sdk.accounts.get(addr) / batch(addresses) | pc.accounts.get(addr) / batchFetch({ addresses }) | | sdk.accounts.derivePda({ name, args }) | pc.pdas.<name>(args) — one method per PDA kind |

Other breaking changes

  • Field casing: snake_case throughout (was mixed). Read straight from the OpenAPI spec.
  • Amounts: token / lamport quantities are decimal strings (was number). Don't coerce.
  • Responses: every method returns the unwrapped data (was the raw HTTP body). Use pc.client.get/post/... for { data, meta } if you need request_id.
  • Errors: throws PirateApiError (was APIError). New fields: code, param, doc_url, request_id.
  • Wallet handling removed: 2.x doesn't take a secretKey — the SDK no longer signs. Use any web3.js wallet to sign the base64 transactions returned by mode: "unsigned" endpoints, then call pc.transactions.submit(...). This is a deliberate decoupling — the SDK is now pure HTTP.
  • Network/RPC config removed: no network, rpcUrl, commitment. The SDK never talks to Solana RPC directly anymore.

Reference

License

MIT