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

@gpool/sdk

v0.14.0

Published

TypeScript SDK for GPool permissionless games, instant-play channels, and pooled player vaults

Readme

@gpool/sdk

Typed client for the GPool Protocol. Products can use a permanent personal bankroll, a private pooled vault, or a publicly funded vault against the same House Vault and Game Engine.

The client covers conservative House share pricing, upgradeable Beacon Player Vaults, configurable atomic games, permissionless settlement/refunds, and complete participant exits. Network deployment constants and faucet helpers are optional references rather than protocol assumptions.

createGPoolChannelClient adds instant multi-round sessions: choose an amount directly from the connected wallet, play up to 128 locally signed rounds across registered games, and replay one net close. The SDK creates or reuses the wallet's permanent Personal Vault underneath the flow and moves only the missing amount in the same opening transaction. createEphemeralChannelSigner creates the bounded session key so a connected wallet is not prompted on every round.

Install

pnpm add @gpool/sdk viem

Protocol client

import {
  availableRolloverHouseRisk,
  createGPoolProtocolClient,
  getGameResolutionAction,
  maxChannelCollateral,
  maxChannelWager,
  protocolMonadTestnetChannelApiUrl,
  protocolMonadTestnetDeployment,
  requiredChannelHouseRisk,
  recommendedRolloverHouseRisk,
  safeRolloverHouseRisk,
} from "@gpool/sdk";

const gpool = createGPoolProtocolClient({
  addresses: protocolMonadTestnetDeployment,
  publicClient,
  walletClient,
  confirmations: 2,
});

// Public coordinator entry; never depend on a product Worker URL.
const channelApi = protocolMonadTestnetChannelApiUrl;

Play USDC faucet

const faucet = await gpool.getTestUsdcFaucetStatus(account);
if (faucet.available) await gpool.claimTestUsdc();

House liquidity

const house = await gpool.getHouse();
// The Monad Testnet legacy House is withdraw-only.
if (!house.depositsClosed) throw new Error("unexpected legacy House state");
const position = await gpool.getHousePosition(account);
await gpool.withdrawHouse(position.maxWithdraw);

New LP capital uses a V2 Risk Market with an explicit module policy, first-loss balance and capacity limit. Do not route new deposits into the legacy House proxy.

Permanent personal bankroll

const existing = await gpool.getPersonalVault(account);
const personal = existing ?? (await gpool.createPersonalVault()).playerVault;

await gpool.approvePersonalVault(25_000_000n);
await gpool.fundPersonalVault(25_000_000n);

// The same vault stays Active after a complete withdrawal.
await gpool.withdrawPersonalVault(personal, 25_000_000n);

fundPersonalVault creates the connected wallet's vault on first use, so consumer products can normally skip a separate creation screen. An unresolved single-game bet must settle or refund before withdrawal. An open Channel locks only its reserved Session collateral; any separate idle Personal Vault balance remains withdrawable.

Consumer products do not need a separate personal-funding screen. Resolve the existing or deterministic vault address, quote the live limit, and atomically move the missing wallet balance while opening:

const house = await gpool.getHouse();
const currentSessionLimit = maxChannelCollateral(house);
const maxHouseRisk = requiredChannelHouseRisk(collateral);
if (collateral > currentSessionLimit) throw new Error("Session exceeds live House coverage");

const personal = await gpool.getPersonalVault(account);
const saved = personal ? (await gpool.getVault(personal)).managedAssets : 0n;
const walletFunding = collateral > saved ? collateral - saved : 0n;
const playerVault = await gpool.getPersonalVaultAddress(account);
const opened = await channels.openWalletFundedPersonalChannel(walletFunding, openInput);
const session = channels.createSession({
  channelId: opened.channelId,
  collateral,
  maxHouseRisk,
  seedCommitment,
  seedProvider: opened.seedProvider,
  signer: sessionSigner,
  personalVault: opened.playerVault,
});

// The default risk line equals the Session bankroll. Each game's live maximum
// then follows its registered top payout instead of reserving a blanket 20×.
const maxWager = maxChannelWager({
  collateral,
  cumulativeNet: session.cumulativeNet,
  totalFees: session.totalFees,
  maxHouseRisk,
  maxPayoutBps: selectedGame.maxPayoutBps,
});

// Carry the full winning balance forward while reserving only the risk the
// House can accept after releasing the old Channel.
const rolloverCapacity = availableRolloverHouseRisk({
  house,
  currentCollateral: session.collateral,
  currentMaxHouseRisk: session.maxHouseRisk,
  playerPayout: session.collateral + session.cumulativeNet,
  totalFees: session.totalFees,
});
const safeCapacity = safeRolloverHouseRisk(rolloverCapacity);
const nextRiskLine = recommendedRolloverHouseRisk({
  availableHouseRisk: rolloverCapacity,
  collateral: session.collateral + session.cumulativeNet,
  wager: selectedWager,
  maxPayoutBps: selectedGame.maxPayoutBps,
  feeBps: selectedGame.totalFeeBps,
});

// prepareBet/appendRevealedRound stay offchain for every round.
await channels.closePersonalChannel(opened.playerVault, session, providerCloseSignature);

// If cooperative settlement is unavailable, the SDK routes the owner's
// proposal through the Personal Vault, which is the onchain Channel player.
await channels.proposeClose(session);
// After the challenge period:
await channels.finalizeClose(session);

// After expiry, anyone may start the timeout path. A completed transcript can
// still replace the empty timeout proposal during the challenge period.
await channels.requestTimeoutClose(session);

The canonical GPool coordinator retains each completed signed step. Consumer products can close cooperatively in one transaction; if the player leaves, the coordinator proposes the latest complete transcript at expiry and finalizes it after the challenge window. A zero-balance Session is queued immediately, so neither Personal Vault state nor House exposure can remain locked indefinitely.

Verify a settled Channel round

import { verifyChannelRound } from "@gpool/sdk";

const verified = await verifyChannelRound({
  publicClient,
  channelEngine,
  gameModuleRegistry,
  channelId,
  step,
  previousSeedCommitment,
  previousTranscriptRoot,
});

console.log(verified.entropy);

This independently recovers the player and Seed Provider signatures, anchors the reveal to the Channel's onchain seed commitment, derives the entropy, asks the immutable game version for the canonical outcome and payout, and recomputes the transcript root. A product should separately confirm the Session's ChannelClosed transaction receipt, as the GPool Activity page does.

Private multiplayer vault

const created = await gpool.createPlayerVault({
  label: "Friday friends",
  minParticipants: 2,
  maxParticipants: 6,
  publicFunding: false,
});

await gpool.allowParticipant(created.playerVault, friendAddress);
await gpool.approveVault(created.playerVault, 100_000_000n);
await gpool.fundVault(created.playerVault, 100_000_000n);
await gpool.activateVault(created.playerVault);

Game and exit

const configs = await gpool.listGameConfigs();
const tables = await gpool.listVaultSnapshots(30);
const game = await gpool.openGame(playerVault, configs[0].kind, 1, 25_000_000n);
const opened = await gpool.getGame(game.gameId);
const rules = await gpool.getGameEngineRules();
const action = getGameResolutionAction(opened, await publicClient.getBlockNumber(), rules.blockhashWindow);
if (action === "settle") await gpool.settleGame(game.gameId);
if (action === "cancel-expired") await gpool.cancelExpiredGame(game.gameId);

await gpool.closeVault(playerVault);
await gpool.claimVault(playerVault);

Build and verify a game

import { deriveGameOutcome, validateGameDefinition } from "@gpool/sdk";

const definition = validateGameDefinition({
  kind: 12,
  modulus: 100,
  active: true,
  name: "Quarter",
  choices: [
    { choice: 0, winStart: 0, winSpan: 25, payoutBps: 36_000, active: true, label: "Low" },
    { choice: 1, winStart: 75, winSpan: 25, payoutBps: 36_000, active: true, label: "High" },
  ],
});

// Requires the Engine DEFAULT_ADMIN_ROLE and kind === getGameKindCount().
// The helper registers disabled-first, writes dense choices, then activates
// only after every receipt confirms.
await gpool.registerGameDefinition(definition);

const outcome = deriveGameOutcome({
  blockHash,
  gameId: game.id,
  playerVault: game.playerVault,
  stake: game.stake,
  modulus: game.modulus,
  winStart: game.winStart,
  winSpan: game.winSpan,
  grossPayout: game.payout,
});

Use verifyGameResult to compare a recorded target, won, and player payout with the independently derived result. Builder inputs are checked against the same range, dense-index, payout, and maximum 92% expected-player-return constraint enforced onchain. Lower RTP is valid; House-banked edge must be at least 8%.

Cancelled funding returns each contribution exactly. A closed vault distributes the ending bankroll pro rata, with the last claimant receiving the rounding remainder. Expired block entropy returns the complete stake.

Raw USDC transfers do not create House shares or Player Vault contributions. Use the SDK funding methods; openWalletFundedPersonalChannel uses the test asset's checked transfer-and-call path when available and falls back to ERC-2612 permit for compatible assets.

Full guides cover accounting, solvency, entropy, upgrade trust, events, operations, and builder conformance.

The archived native-MON P2P room client is isolated under @gpool/sdk/legacy; it is not part of the House protocol.

Risk Markets V2 model

Permissionless registration does not imply automatic LP underwriting. The SDK ships the V2 contract ABIs, exact-table risk model, EIP-712 messages, quote helpers, and the funded Monad Testnet canary addresses:

import {
  RiskClass,
  RiskPolicyStatus,
  UnderwritingStatus,
  applyRealizedGgrWaterfall,
  deriveFiniteOutcomeRiskMetrics,
  prepareRiskOpenChannel,
  protocolMonadTestnetDeployment,
  quoteDemandLinkedDepositCapacity,
  quoteUnderwriting,
  riskChannelEngineV2Abi,
  riskMarketV2Abi,
  validateChannelRiskBucketBinding,
} from "@gpool/sdk";

const canary = protocolMonadTestnetDeployment.riskMarketsV2;

const quote = quoteUnderwriting(policy, moduleUnderwriting, {
  requestedChannelRisk,
  currentBucketExposure,
  allocatedCapital,
  theoreticalRtpBps,
  registeredMaximumPayoutBps,
  lockDurationSeconds,
});

const exactRisk = deriveFiniteOutcomeRiskMetrics({
  moduleId,
  moduleVersion,
  rulesHash,
  outcomes,
});

Policies version risk class, accepted RTP, payout and lock limits, module and bucket exposure caps, minimum developer first-loss, and a utilization-based capital price. Tail, oracle, skill, and prediction risk have stricter default guardrails than low-variance games. Deposit capacity follows observed settled wager demand and existing reservations, preventing idle TVL from diluting LPs.

A V2 Channel binds exactly one underwriting market, moduleId, immutable moduleVersion/codehash, and policyVersion. A product that switches games must use a sponsored close-and-rollover into a fresh risk bucket; it cannot switch modules offchain while retaining the old capacity reservation.

House performance claims use applyRealizedGgrWaterfall, which derives GGR from settled wagers minus actual gross payouts. Losses consume developer first-loss, reserve, then LP NAV. Later positive GGR repairs the module deficit, reserve target, and LP high-water mark before LP/protocol/developer surplus shares accrue. Wager volume by itself never creates a developer or protocol House-performance claim; fixed settlement and time-weighted capital fees are quoted separately from reserved risk.

The exported V2 deployment has a verified 32-round live open/close receipt and now serves Coin Flip through /api/channel/v2/*. createGPoolRiskChannelClient handles the direct-wallet Session lifecycle, local outcome verification, transcript reconciliation, Session-signed atomic rollover and cooperative close. The public recovery index lets a product detect an existing wallet Session before opening another one. Other modules remain on the legacy route until their own policies and first-loss capacity are active.