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

@provable-games/metagame-sdk

v0.1.12

Published

Shared types, utilities, and headless hooks for Provable Games metagame SDKs

Readme

@provable-games/metagame-sdk

Shared types, utilities, and headless React hooks for Provable Games metagame UIs — powering Budokan (tournaments) and Bokendo (quests).

Features

  • Unified typesToken, Prize, EntryFee, Participant, StatusTimestamps
  • Utilities — Address formatting, number display, prize aggregation, status computation, entry fee calculations, qualification evaluation, proof building, extension config parsing
  • Headless hooks — Pagination, search, token selection, status indicators, score tables, prize tables, entry fee preview, entry/extension qualification
  • RPC utilities — On-chain extension and fee/prize config reading via Starknet RPC
  • Tree-shakeable — Main entry has zero React dependency; hooks and RPC are separate entry points
  • ESM + CJS — Dual build with full TypeScript declarations

Install

npm install @provable-games/metagame-sdk
# or
bun add @provable-games/metagame-sdk

Optional peer dependencies (install only what you use):

npm install react      # For hooks (/react entry)
npm install starknet   # For RPC utilities (/rpc entry)

Quick Start

Utilities (no React needed)

import {
  indexAddress,
  formatNumber,
  formatPrizeAmount,
  computeStatus,
  groupPrizesByToken,
  getOrdinalSuffix,
} from "@provable-games/metagame-sdk";

// Normalize a Starknet address
indexAddress("0x00000abc"); // "0xabc"

// Format numbers with smart suffixes
formatNumber(1500000); // "1.5m"
formatNumber(2500);    // "2.5k"

// Compute tournament/quest status from timestamps
const status = computeStatus({
  registrationStart: 1000,
  registrationEnd: 2000,
  start: 3000,
  end: 4000,
}, Date.now() / 1000);
// { label: "Live", variant: "active", isActive: true, countdown: { ... } }

// Group prizes by token
const grouped = groupPrizesByToken(prizes);
// { "0xabc": { type: "erc20", address: "0xabc", totalAmount: "150" } }

// Ordinal suffixes
getOrdinalSuffix(1);  // "st"
getOrdinalSuffix(22); // "nd"

Entry Fee & Prize Calculations

import {
  calculateEntryFeeBreakdown,
  distributePool,
  buildEntryFeePrizes,
  calculateTotalPrizeValueUSD,
  calculatePaidPlaces,
} from "@provable-games/metagame-sdk";

// Break down entry fee into creator/protocol/prize shares
const breakdown = calculateEntryFeeBreakdown(entryFee, participantCount);

// Calculate prize pool distribution
const distribution = distributePool(totalPool, positions, weight);

Qualification & Proofs

import {
  evaluateQualification,
  buildQualificationProof,
  identifyExtensionType,
  parseTournamentValidatorConfig,
} from "@provable-games/metagame-sdk";

// Check if a user qualifies for entry
const result = evaluateQualification(requirement, userState);

// Build proof for on-chain verification
const proof = buildQualificationProof(qualificationData);

// Parse extension configs from on-chain data
const config = parseTournamentValidatorConfig(rawConfig);

React Hooks

import { useStatusIndicator, useTokenSelector, usePagination } from "@provable-games/metagame-sdk/react";

// Auto-refreshing status indicator
function StatusBadge({ timestamps }) {
  const status = useStatusIndicator(timestamps); // refreshes every second
  return <span className={status.variant}>{status.label}</span>;
}

// Token selector with search + pagination
function TokenPicker({ tokens }) {
  const { search, setSearch, filteredTokens, select, pagination, getTokenProps } =
    useTokenSelector({ tokens, tokenType: "erc20", pageSize: 10 });

  return (
    <div>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      {filteredTokens.map((token) => (
        <div key={token.address} {...getTokenProps(token)}>
          {token.symbol}
        </div>
      ))}
      <button onClick={pagination.prev} disabled={!pagination.hasPrev}>Prev</button>
      <button onClick={pagination.next} disabled={!pagination.hasNext}>Next</button>
    </div>
  );
}

// Entry qualification check
import { useEntryQualification } from "@provable-games/metagame-sdk/react";

function EntryGate({ requirement, userAddress }) {
  const { qualified, reason, loading } = useEntryQualification({ requirement, userAddress });
  if (loading) return <span>Checking...</span>;
  return qualified ? <button>Enter</button> : <span>{reason}</span>;
}

RPC Utilities

import { readExtensionConfig, readFeeAndPrizeExtensions } from "@provable-games/metagame-sdk/rpc";

// Read extension configuration from on-chain contracts
const config = await readExtensionConfig(provider, extensionAddress);

API Reference

Types

| Type | Fields | |------|--------| | Token | address, name, symbol, tokenType, decimals?, logoUrl? | | Prize | id, position, tokenAddress, tokenType, amount, sponsorAddress | | EntryFee | tokenAddress, amount, creatorShare?, refundShare? | | EntryRequirement | requirementType, tokenAddress?, entryLimit? | | Participant | address, rank?, score?, name?, entryNumber? | | StatusTimestamps | start, end, registrationStart?, registrationEnd?, submissionEnd?, completed?, unlocked? | | StatusResult | label, variant, isActive, countdown |

Utilities

| Function | Description | |----------|-------------| | indexAddress(address) | Strip leading zeros from hex address | | padAddress(address) | Pad address to 66 characters | | displayAddress(address) | Truncate to 0x1234...abcd | | bigintToHex(value) | Convert bigint/number/string to hex | | formatNumber(num) | Smart formatting with k/m suffixes | | formatPrizeAmount(num) | Precision-aware prize display | | formatUsdValue(value) | USD display with <0.01 handling | | formatScore(num) | Score display formatting | | formatTime(seconds) | Human-readable duration ("2 Days", "3 Hours") | | getOrdinalSuffix(n) | Returns "st", "nd", "rd", "th" | | calculatePayouts(places, weight) | Weighted payout percentages summing to 100 | | calculateDistribution(positions, weight, ...) | Basis-point distribution matching contract logic | | groupPrizesByToken(prizes) | Group prizes by token address | | calculatePrizeValue(amount, decimals, price) | Compute USD value of a prize | | computeStatus(timestamps, now?) | Derive status label, variant, and countdown | | isBefore(date1, date2) | Date comparison | | calculateEntryFeeBreakdown(fee, count) | Break down entry fee into shares | | distributePool(pool, positions, weight) | Distribute prize pool across positions | | aggregatePrizesByPosition(prizes) | Group prizes by leaderboard position | | aggregatePrizesBySponsor(prizes) | Group prizes by sponsor address | | filterClaimablePrizes(prizes, ...) | Filter prizes claimable by a participant | | filterZeroPrizes(prizes) | Remove zero-amount prizes | | parseNFTBulkInput(input) | Parse bulk NFT token ID input | | getExtensionAddresses(chain) | Get known extension contract addresses | | identifyExtensionType(address) | Identify extension type from address | | parseTournamentValidatorConfig(config) | Parse tournament validator config | | parseERC20BalanceValidatorConfig(config) | Parse ERC20 balance validator config | | parseOpusTrovesValidatorConfig(config) | Parse Opus Troves validator config | | evaluateQualification(requirement, state) | Evaluate entry qualification | | buildQualificationProof(data) | Build proof for on-chain verification | | buildEntryFeePrizes(config) | Build prize list from entry fee config | | calculateTotalPrizeValueUSD(prizes, ...) | Calculate total prize pool USD value | | calculatePaidPlaces(prizes) | Count positions with non-zero prizes | | buildParticipationMap(registrations) | Map addresses to participation counts | | buildWinMap(leaderboards) | Map addresses to win counts | | resolveTournamentQualifications(input) | Resolve tournament validator qualifications | | calculateOpusTrovesEntries(troves) | Calculate entries from Opus Troves | | findBannableEntries(entries, ...) | Find entries eligible for banning |

React Hooks (/react entry)

| Hook | Description | |------|-------------| | useDebounce(value, delay) | Debounce any value | | usePagination({ totalItems, pageSize? }) | Page state with next/prev/reset | | useSearchFilter({ items, searchFields, debounceMs? }) | Filtered list with debounced search | | useTokenSelector({ tokens, tokenType?, pageSize? }) | Token search + selection + pagination | | useStatusIndicator(timestamps, refreshMs?) | Auto-refreshing status computation | | useScoreTable({ participants, pageSize?, sortField? }) | Sorted, searchable, paginated score table | | usePrizeTable({ prizes, ... }) | Prize display with grouping and pagination | | useEntryFeePreview({ entryFee, ... }) | Entry fee breakdown preview | | useEntryQualification({ requirement, ... }) | Entry qualification check | | useExtensionQualification({ extension, ... }) | Extension-based qualification evaluation | | useOpusTrovesBannableEntries({ entries, ... }) | Identify bannable Opus Troves entries |

RPC Utilities (/rpc entry)

| Function | Description | |----------|-------------| | readExtensionConfig(provider, address) | Read extension config from chain | | readFeeAndPrizeExtensions(provider, ...) | Read fee and prize extension data |

Development

npm install
npm run build        # ESM + CJS to dist/
npm run typecheck    # TypeScript validation
npm test             # Unit tests
npm run dev          # Watch mode

Publishing

Automated via GitHub Actions — create a GitHub release to trigger npm publish.

License

MIT