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

@raptortrade/priorityfee

v0.2.12

Published

TypeScript bindings for the Solana priority-fee operators on Raptor.

Readme

@raptortrade/priorityfee

TypeScript bindings for the Solana priority-fee operators on Raptor.

The package exposes typed Shapes and feature helpers for each emitted view. This README focuses on the recommendation views, which changed shape in 0.x - see Migration: 0.x congestion columns removed below.

Recommendation views

Two views, both produced by the priorityfee_recommendation / priorityfee_account_recommendation operators:

| RQL view | Helper | Row type | |----------|--------|----------| | solana::priorityfee::recommendation::rolling::{1s,10s,30s,1min,5min,1hr} | recommendation(duration) | RecommendationRow | | solana::priorityfee::account::recommendation::rolling::{1s,10s,30s,1min,5min,1hr} | accountRecommendation(account, duration) | AccountRecommendationRow |

Each row contains six fee tiers (min, low, medium, high, very_high, max), two landing-probability views per tier ({tier}_landing_rank_pct and {tier}_landing_inclusion_pct), and a small set of context fields. Full schema in src/schema.ts / src/types.ts.

Block-fill metrics

The recommendation row now exposes two continuous block-fill metrics derived from the most recent ~50 blocks the operator has observed:

interface RecommendationRow {
  // ... tier columns elided ...

  /** Median per-block `100 * cu_consumed / cu_budgeted` across the recent block ring. */
  median_block_fill_pct: Float8Value;

  /** Share (%) of blocks in the ring at or above the saturation threshold (0.75). */
  saturated_block_share_pct: Float8Value;

  fee_volatility: Float8Value;
  avg_cu_per_tx: Uint8Value;
}

What each metric tells you

  • median_block_fill_pct captures the typical block: a high value means most recent blocks are nearly full, so paying a priority fee is increasingly necessary to outbid the field.
  • saturated_block_share_pct captures spikiness: a low median with a high saturated share means the leader pipeline has hot blocks competing with cold blocks, which is precisely when priority bidding pays off.

Both are computed over the same 50-block ring used by {tier}_landing_inclusion_pct. The saturation cutoffs differ between the two metrics, on purpose:

  • {tier}_landing_inclusion_pct uses a 0.90 slack gate, answering "did the block have so much room that any tx clears regardless of fee".
  • saturated_block_share_pct uses a 0.75 cutoff, answering "was the block actually full of real work". The lower value compensates for users systematically over-requesting compute_unit_limit: a tx that consumes 80k CU often requests 200-300k, so the per-block cu_consumed / cu_budgeted ratio runs in the 30-70% range even on busy networks. A 0.90 cutoff for the saturation metric would never trip in practice; 0.75 catches the bias without being trigger-happy.

Reading the two side by side: landing_inclusion_pct is the inclusion model conditioned on a specific tier price; the block-fill metrics describe the underlying block conditions that drive that model.

Picking thresholds

The metrics are continuous; no level is implied. Bucket on the consumer side however suits your policy. Examples:

// Trader-style: only bump fees when blocks are visibly tight.
const shouldBidUp = median_block_fill_pct >= 70;

// Latency-sensitive: bump even if blocks aren't full on average,
// because spiky periods leak into low-fee landings.
const shouldBidUp = saturated_block_share_pct >= 30;

// Belt-and-suspenders: only bid up when both signals agree.
const shouldBidUp = median_block_fill_pct >= 70 && saturated_block_share_pct >= 30;

If you need a trend signal (the old congestion_trend was -1/0/+1 deltas of the discrete level), compute the delta from successive emissions yourself. With continuous metrics the comparison is more meaningful: a 5%-point drop in median_block_fill_pct is a real change; a discrete level dropping from 3 to 2 might mean anything between a 1% and a 30% shift.

Per-account caveat

saturated_block_share_pct on AccountRecommendationRow is conditional on the account being active in the block. Quiet blocks where the account submitted nothing are not observed. For sparse accounts the metric is noisy; for active accounts it surfaces account-specific contention.

Migration: 0.x congestion columns removed

Previous versions emitted two columns that have been removed:

| Removed | Type | Replacement | |---------|------|-------------| | congestion_level | Uint1Value (0-4) | median_block_fill_pct: Float8Value (0-100) and/or saturated_block_share_pct: Float8Value (0-100) | | congestion_trend | Int1Value (-1/0/+1) | Compute from successive emissions if needed |

Why the change

The old congestion_level was computed as max(rate_level, util_level) where:

  • rate_level thresholded transactions/sec against fixed buckets [200, 400, 700, 1000]. Solana blocks are CU-bounded, not tx-count-bounded, so this signal isn't grounded in any property of the runtime - 200 lightweight votes and 200 heavy DeFi swaps would score the same.
  • util_level thresholded total_cu_consumed / total_cu_budgeted summed across the transactions in the window. That measures how well-tuned users' CU limits are, not how full blocks were. A window where every tx over-budgets by 2x but blocks are 30% full would report util = 50% and trip the level - opposite of reality.

The discrete max(...) was also lossy: a congestion_level=3 could come from either driver, and the two imply different fee strategies (broad fee competition vs. spiky leader pressure).

The new metrics use per-slot summaries from the operator's existing block ring - the same primitive landing_inclusion_pct already uses - and stay continuous so consumers can threshold them however they want.

Migration recipes

// If you displayed a 5-bucket bar:
const oldLevel = (row: { congestion_level: number }) => row.congestion_level;

// Approximation from the new metrics:
const newLevel = (row: RecommendationRow) => {
  const fill = Number(row.median_block_fill_pct);
  if (fill < 30) return 0;       // Quiet
  if (fill < 55) return 1;       // Light
  if (fill < 75) return 2;       // Moderate
  if (fill < 90) return 3;       // Busy
  return 4;                      // Saturated
};

// Trend (was congestion_trend):
let prev: number | null = null;
function trend(row: RecommendationRow): -1 | 0 | 1 {
  const cur = Number(row.median_block_fill_pct);
  if (prev == null) { prev = cur; return 0; }
  const sign = cur > prev + 1 ? 1 : cur < prev - 1 ? -1 : 0;
  prev = cur;
  return sign;
}

The 1-percentage-point hysteresis above is illustrative; tune it against the cadence of your view (1s updates jitter, 1hr updates barely move).

Stats views still emit congestion_level

StatsRow (from priorityfee_stats / priorityfee_account_stats, queried via stats(...)) keeps congestion_level for now - the stats operators are a different code path and weren't part of this change. If you consume both views in the same code path, treat them as separate types.