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

pnp-core

v0.1.0

Published

Lean TypeScript SDK for the PnP prediction-market protocol on Solana (P2P/parimutuel + Pythagorean AMM).

Readme

pnp-sdk-core

A small, friendly TypeScript SDK for the PnP prediction-market protocol on Solana.

Every function is named for what it does, and every input/output is a plain object with labelled fields — so you can read a call and know exactly what's happening without digging into Solana internals.

  • P2P / parimutuel markets (V3) — client.p2p.*
  • Pythagorean / AMM markets (V2) — client.amm.*

One program, live on mainnet and devnet: 8PyE2dizL52ga7ytqLtqRyjwWp4yXEx8M5Z4BAHgHuTb.


Install

npm install pnp-sdk-core

Connect

import { PnpClient } from "pnp-sdk-core";
import { Keypair } from "@solana/web3.js";

// Read-only (reads + address lookups, no signing):
const reader = PnpClient.mainnet();

// With a signer — pass a Keypair (Node/scripts) or any browser wallet:
const client = PnpClient.devnet({ wallet: myKeypair, rpcUrl: process.env.RPC_URL });

A note on amounts

All token amounts are in base units (the smallest unit of the collateral, like cents for a dollar). USDC has 6 decimals, so 100 USDC = 100_000000. Use the helper so you don't count zeros:

import { uiToBaseUnits } from "pnp-sdk-core";
uiToBaseUnits(100, 6);   // 100_000000n  (100 USDC)

The whole lifecycle in one glance

import { PnpClient, uiToBaseUnits } from "pnp-sdk-core";

// 1. A creator opens a market and bets YES with 100 USDC
const creator = PnpClient.devnet({ wallet: creatorKp });
const { market } = await creator.p2p.createMarket({
  question: "Will ETH close above $5,000 on 2027-01-01?",
  initialAmount: uiToBaseUnits(100, 6),
  side: "yes",
  endTime: new Date("2027-01-01T00:00:00Z"),
});

// 2. Someone else bets NO with 50 USDC
const bettor = PnpClient.devnet({ wallet: bettorKp });
await bettor.p2p.buy({ market, side: "no", amount: uiToBaseUnits(50, 6) });

// 3. Anyone can read the live state
const info = await reader.p2p.getMarket(market);
console.log(info.status, info.impliedYesProbability);

// 4. After it ends, an authorized settler declares the winner
await creator.p2p.settle({ market, winner: "yes" });

// 5. Winners claim their payout
await bettor.p2p.redeem({ market });

Function reference (client.p2p.*)

Each write function returns a signature (the transaction id) plus the key things it touched. "Signer" is which wallet must be attached to the client.

Create & bet

createMarket({ question, initialAmount, side, endTime, ... }) — signer: creator Opens a new market. You back one side with initialAmount of collateral; others take the other side. Uses the protocol's default oracle (safe). → { signature, marketId, market, yesMint, noMint }

createMarketCustom({ ...same, oracle }) — signer: creator Same as createMarket, but you pick the market's oracle. ⚠️ That wallet can settle the market — only use it when you deliberately want a custom settler. → { signature, marketId, market, yesMint, noMint, extension }

buy({ market, side, amount, minTokensOut? }) — signer: bettor Place a bet: spend amount collateral to get side tokens. minTokensOut is an optional slippage floor (default 0 = accept any). → { signature, market, side }

Money out

creatorWithdraw({ market }) — signer: creator Take back the part of your creator stake that no one ever bet against (unmatched collateral). → { signature, market }

redeem({ market }) — signer: winner Claim your winnings after the market is settled. A 2% fee is taken (60% to the creator, 40% to the protocol); you get the rest. → { signature, market }

Manage (authorized wallets)

settle({ market, winner }) — signer: oracle/admin Declare the winning side ("yes" or "no"). Only an authorized settler can call it. → { signature, market, winner }

setEndTime({ market, endTime }) — signer: admin/oracle Change when the market ends. endTime is a Date or unix seconds; can be in the past to make it settleable now. → { signature, market, endTime }

updateCreator({ market, newCreator }) — signer: current creator Hand the creator role (and future creator fees) to another wallet. → { signature, market, newCreator }

A market's numeric market id or its PDA address both work anywhere a market is expected.

Settlement criteria (off-chain, no Solana tx)

setSettlementCriteria({ market, criteria, data? }) — signer: creator Attach the rules for how a market resolves. Your wallet signs a message (not a transaction) and it's sent to the PnP criteria server, which checks you're the market's creator and stores it for the oracle. criteria is human-readable text; data is optional structured JSON for the oracle. Needs a wallet that can sign messages (a Keypair, or a browser wallet with signMessage). → { ok, criteria: { market_address, cluster, creator, settlement_criteria, settlement_data, updated_at } }

await client.p2p.setSettlementCriteria({
  market,
  criteria: "Resolves YES if ETH >= $5,000 on 2027-01-01 (UTC) per CoinGecko.",
  data: { source: "coingecko", asset: "ethereum", threshold: 5000 },
});

getSettlementCriteria(market) — no signer Read a market's stored criteria. Returns the record, or null if none has been set. → { market_address, cluster, creator, settlement_criteria, settlement_data, updated_at } | null

const c = await client.p2p.getSettlementCriteria(market);
if (c) console.log(c.settlement_criteria);

Read (no signer needed)

getMarket(id | pubkey) → a P2PMarket snapshot:

| field | meaning | |---|---| | status | "open" · "ended" (awaiting settlement) · "resolved" | | question | the market's question | | creator, creatorSide | who made it and which side they back | | winner | "yes" / "no" once resolved, else null | | yesPot, noPot, totalPot | collateral staked on each side (base units) | | impliedYesProbability | YES's share of the pool, 0..1 | | endTime, creationTime | timestamps (unix seconds) | | yesMint, noMint, collateralMint | token addresses | | oddsBps, oracle | custom odds / oracle if set (else defaults) |

fetchGlobalConfig() → the protocol's global settings. nextMarketId() → the id the next created market will get.


Pythagorean / AMM markets (client.amm.*)

A second market type: instead of two matched pots, an AMM issues YES/NO tokens off a Pythagorean bonding curve (reserves R = √(YES² + NO²), so yesPrice² + noPrice² = 1).

createMarket({ question, initialLiquidity, endTime }) — signer: creator Open an AMM market with 50/50 odds and the protocol oracle. → { signature, marketId, market }

createMarketCustom({ ...same, oddsBps?, oracle? }) — signer: creator Set custom YES odds (oddsBps, 100–9900; default 5000 = 50/50) and/or a custom oracle ⚠️. → { signature, marketId, market, extension }

createCoinMarket({ question, coinAddress, chain?, initialLiquidity, endTime, oddsBps?, oracle? }) A market about a specific token. Stores the type in the question as "<question>? <chain>:<coinAddress>" (default chain "solana"), exactly like the PnP frontend so the oracle/indexer recognize it.

createYoutubeMarket({ question, youtubeLink, initialLiquidity, endTime, oddsBps?, oracle? }) A market about a YouTube video. Stores the type as "<question>? <youtubeLink>".

await client.amm.createCoinMarket({
  question: "Will BONK 2x by Friday",
  coinAddress: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",  // chain defaults to "solana"
  initialLiquidity: uiToBaseUnits(50, 6),
  endTime: new Date("2027-01-01"),
});
// on-chain question: "Will BONK 2x by Friday? solana:DezXAZ8z7Pnr…"

readPrice(market){ yesPrice, noPrice } — each in [0,1] (matches the on-chain price getter). readMultiplier(market){ yesMultiplier, noMultiplier } — potential payout multiples (1 + (otherSupply / thisSupply)²). getMarket(market) → full AmmMarket (status, reserves, supplies, mints, prices, multipliers). fetchGlobalConfig() · nextMarketId() — shared with P2P.

const reader = PnpClient.mainnet();
const { yesPrice, noPrice } = await reader.amm.readPrice(9);
const { yesMultiplier, noMultiplier } = await reader.amm.readMultiplier(9);

Fresh AMM markets read 0.5 / 0.5 and 2× / 2× until the first trade mints decision tokens.

Oracle status (client.oracle.*)

After a market is created, your oracle's AI decides whether it can be objectively resolved and generates settlement criteria. These reads (via the PnP indexer — no wallet needed) let you watch that. This is the ORACLE's decision — distinct from the creator-set criteria on client.p2p.*.

getResolvableStatus(market)"checking" | "resolvable" | "unresolvable" checking = the AI is still processing; the others are its verdict. market is a PDA or base58 address.

getSettlementCriteria(market) → the AI's { resolvable, criteria, reasoning?, category? } or null (null while still checking).

waitForResolvable(market, { intervalMs?, timeoutMs? }) — the feedback loop: polls until the AI finishes, then returns { status: "resolvable" | "unresolvable", criteria } (throws on timeout).

const { market } = await client.amm.createMarket({ /* … */ });
const { status, criteria } = await client.oracle.waitForResolvable(market);
if (status === "resolvable") console.log("live!", criteria);
else console.log("oracle rejected — refunds enabled");

Wallets

  • Node / scripts: pass a Keypair as wallet.
  • Browser: pass your wallet-adapter wallet (anything with publicKey + signTransaction).
  • No wallet: reads and address derivations still work; write functions throw a clear error.

What's intentionally not here

Whitelist-gate management (init/add-taker/close gate) is owned by the frontend/admin, not the SDK. buy still works on gated markets — it passes the gate automatically.

See PLAN.md for build status and what's next.