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

@openpredict/metapredict-widgets

v0.1.3

Published

Embeddable React widgets for the metapredict prediction market — markets feed, predict / fund / claim lifecycle drop-in.

Readme

@openpredict/metapredict-widgets

Embeddable React widgets for the metapredict prediction market. Drop the components into any React app and get the same live markets metapredict.xyz renders — markets feed, single-market lifecycle widget, price chart, claims list, history.

Pre-mainnet. Defaults connect to Solana devnet and the production Metapredict API. Mount the <NetworkBadge> to remind users.

Install

pnpm add @openpredict/metapredict-widgets
# or: npm i @openpredict/metapredict-widgets
# or: yarn add @openpredict/metapredict-widgets

Peer deps: react@>=18 <20, react-dom. Everything else (@openpredict/metapredict-sdk, @solana/kit, @solana/react-hooks, @solana-program/token, lightweight-charts, vaul, zustand, clsx, tailwind-merge) is bundled.

Every read + write flows through @openpredict/metapredict-sdk — the public HTTP API at www.metapredict.xyz/api/*. No tanstack-query closure, no Convex client, no on-chain codama deep imports.

Quick start

"use client";

import { MetapredictProvider, MarketsFeed } from "@openpredict/metapredict-widgets";
import "@openpredict/metapredict-widgets/styles.css";

export default function App() {
  return (
    <MetapredictProvider>
      <MarketsFeed onSelect={(slug) => console.log("picked", slug)} />
    </MetapredictProvider>
  );
}

No config needed — MetapredictProvider defaults to the live Metapredict API and Solana devnet RPC.

For a full reference app see apps/sdk-demo/ in the repo (Next.js 16 / React 19, ships zero env config).

Provider

<MetapredictProvider> mounts (outside-in):

  1. ResolvedConfigContext — exposes the resolved API/RPC/cluster config to the widget tree.
  2. KitSolanaProvider@solana/react-hooks with wallet-standard auto-discovery (Phantom, Solflare, Backpack, …) when walletMode is "internal".
  3. <SDKProvider> — holds the MetapredictClient used for all reads, subscriptions, and tx-builds.
  4. <div class="mpx-root"> — theme + style scope.
  5. <PredictFlowHost /> — singleton drawer host shared across all MarketWidget instances on the page.
<MetapredictProvider config={{
  apiUrl?: "https://metapredict.xyz/api",                       // optional, prod default
  apiKey?: "mp_pk_anon_xxx",                                    // optional bearer for higher rate limits
  rpcUrl?: "https://api.devnet.solana.com",                     // optional; SPL balance reads + wallet signing
  cluster?: "devnet" | "mainnet" | "testnet" | "localnet",      // inferred if omitted
  theme?: { cssVars: { "--mpx-color-accent": "#facc15" } },     // optional
  walletMode?: "internal" | "external",                          // "internal" (default) mounts our wallet provider; "external" assumes the host already mounted KitSolanaProvider above.
}}>
  {children}
</MetapredictProvider>

The host does not need to mount tanstack-query or Solana providers itself. walletMode: "external" is the one opt-out: skip the widget's KitSolanaProvider and the widget will read the active wallet via useWalletConnection() from the host's tree.

Components

<MarketsFeed>

Lists active markets from the public API. The feed polls every 30s and paginates through the full topic catalog before filtering active topics.

<MarketsFeed
  onSelect={(slug) => /* ... */}
  renderItem={(market) => /* optional override */}
  headerSlot={/* optional */}
  footerSlot={/* optional */}
  showCountdown={true}    // default
/>

<MarketWidget>

Single-market lifecycle drop-in: question + pools + Predict YES/NO + wallet button. Opens connect/fund/predict drawers via the singleton predict-flow store.

<MarketWidget
  topicSlug="btc-15m"
  defaultSide?: "yes" | "no"
  headerSlot?: ReactNode
  showCountdown?: boolean
/>

<MarketChart>

Live price history rendered with lightweight-charts. Reads topic.streamSlug and subscribes to api.streams.getSamplesInRange. Non-crypto markets render a clean empty state (no fake series).

<MarketChart
  topicSlug="btc-15m"
  windowMinutes?: 60   // default
  height?: 240         // default
/>

<ClaimsList>

Every prediction the connected wallet can still claim. One-click claim per row via the useClaim hook.

<ClaimsList
  walletPubkey?: string                  // override; defaults to connected wallet
  onSelectMarket?: (slug: string) => void
/>

<PositionsHistory>

Full prediction timeline for the wallet — active, won, lost, tie, claimed, pending, failed. Filter tabs (All / Active / Won / Lost), status pill per row, inline Claim button on winning rows, link to the on-chain tx via Solana Explorer (cluster-aware).

<PositionsHistory
  walletPubkey?: string
  onSelectMarket?: (slug: string) => void
  limit?: 50
/>

<AddFunds>

Inline funding UI — buys PAPER with devSOL via the hoisted fluid swap_single instruction. Live oracle quote + slippage floor. Mounted automatically inside <MarketWidget>'s fund drawer; exposed standalone for advanced layouts.

<AddFunds onFunded={() => /* called when swap confirms */} />

<NetworkBadge>

Tiny pill showing the current cluster (DEVNET / MAINNET / TESTNET / LOCALNET).

<NetworkBadge hideOnMainnet={true} />

<WidgetWalletButton>

Connect / disconnect via wallet-standard auto-discovery. Used inside <MarketWidget>; exposed standalone for header bars.

<WidgetWalletButton />

<PredictDrawer>

Pure presentational drawer — owns no data hooks. Driven by the singleton predict-flow store. Exposed for advanced layouts that want to reuse the visual but own the data flow.

Hooks

| Hook | Returns | Notes | |---|---|---| | useMarketsFeed() | { data, isLoading, error } | Active topics from the public API. | | useMarket(topicSlug) | { data: { topic, round } \| null } | Single topic + its current round. | | useUserActivity({ userPubkey, status, limit }) | UserActivityRow[] | Prediction history joined with rounds + topics. status: "active" \| "claimed" \| "all". | | useStreamHistory({ streamSlug, fromMs?, toMs?, limit? }) | { points: { time, value }[], isLoading } | Deduped, sorted, lightweight-charts-ready. | | useStreamLatest(streamSlug) | Latest tick | Useful as a chart overlay marker. | | useTokenBalance({ mint, decimals?, refreshIntervalMs? }) | { balance } | SPL balance via getTokenAccountBalance. null while disconnected, 0 if ATA missing. | | useSigner() | Stable kit TransactionSigner for the active wallet. | | | useWidgetWallet() | { publicKey, connected, status, isReady } | | | usePredictTransaction() | tanstack mutation | Builds predict ix, derives next nonce, sends + confirms. | | usePaperSwap() + quotePaperOut({ solIn, … }) | devSOL → PAPER swap mutation + pure quote fn | | | useClaim() | tanstack mutation | Claim a winning prediction. | | useWidgetConfig() | { rpcUrl, convexUrl, cluster } | Resolved provider config. |

Theming

Override CSS variables on .mpx-root via the theme.cssVars prop. Three brand-sampled themes ship built in (JUPITER_THEME, POLYMARKET_THEME, KALSHI_THEME); custom themes are a flat Record<string, string>.

import {
  MetapredictProvider,
  JUPITER_THEME,
  BUILT_IN_THEMES,
} from "@openpredict/metapredict-widgets";

<MetapredictProvider config={{
  theme: {
    cssVars: {
      "--mpx-color-bg": "#0a0a0a",
      "--mpx-color-fg": "#fafafa",
      "--mpx-color-accent": "#facc15",
      "--mpx-radius": "0px",
    },
  },
}} />

// or pick a built-in:
<MetapredictProvider config={{ theme: JUPITER_THEME }} />

Theme prop changes hot-swap CSS-var values without recreating the SDK/Solana clients — safe to bind to per-user state.

Token vocabulary:

  • Colors: --mpx-color-{bg,card,fg,muted,border,accent,yes,no,tie,error,warn}
  • Radii: --mpx-radius, --mpx-radius-sm, --mpx-radius-pill, --mpx-radius-button
  • Spacing: --mpx-spacing, --mpx-card-padding
  • Fonts: --mpx-font-{mono,sans,display}, --mpx-font-size, --mpx-font-size-lg
  • Buttons: --mpx-button-{yes,no,primary}-{bg,fg,shadow}, --mpx-button-padding-{y,x}, --mpx-button-font-{size,weight}

Errors

import { WidgetTxError, isWidgetTxError } from "@openpredict/metapredict-widgets";

try {
  await predict.mutateAsync({ ... });
} catch (e) {
  if (isWidgetTxError(e) && e.code === "no-signer") {
    /* user-facing: not connected */
  }
}

What's bundled

Bundled: @openpredict/metapredict-sdk (the public HTTP API client), @solana/kit, @solana/react-hooks, @solana-program/token (SPL ATA derivation for useTokenBalance), vaul, lightweight-charts, lucide-react, zustand, clsx, tailwind-merge.

Not bundled: @coral-xyz/anchor, @solana/web3.js, convex, @tanstack/react-query, the codama-rendered on-chain SDK, fluid swap_single. The widget package consumes the HTTP API and signs locally with the user's wallet — none of the on-chain ix builders are pulled into the browser bundle.

Bundle size: ~89KB ESM / ~99KB CJS (down from ~200KB pre-API-pivot — convex + tanstack-query + codama were doing the heavy lifting).

Network

Defaults: API at https://metapredict.xyz/api + Solana devnet RPC for SPL balance reads + wallet signing. Override per-instance via the provider config prop. Real on-chain markets, real PAPER token, real fluid swap pool. Pre-mainnet — use <NetworkBadge> to remind users.

How it works under the hood

  • Reads: mp.listTopics, mp.getTopic, mp.getActiveRound, mp.getChart, mp.getUserActivity, mp.listClaimables — all plain HTTP GETs against /*.
  • Live updates: mp.subscribe.activeRound, mp.subscribe.userPredictions, mp.subscribe.streamPrice — SSE streams with auto-reconnect / exponential backoff inside the SDK.
  • Writes: mp.buildPredictTx / mp.buildClaimTx / mp.buildSwapTx return base64-encoded transactions with blockhash + accounts already wired by the server. The widget decodes the bytes, has the connected wallet add its signature via modifyAndSignTransactions, re-encodes, and POSTs to mp.submitTx (/tx/submit). No local RPC submit, no nonce probing, no PDA derivation in the browser.

License

Apache-2.0. See LICENSE for the full text.