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

@solworks/poll-sdk

v1.4.1

Published

SDK for the Poll.fun program.

Downloads

824

Readme

@solworks/poll-sdk

TypeScript SDK for the betmore-core-program Solana/Anchor program (Poll.fun / TG-Bet). Wraps account derivation, instruction building, and transaction sending for V2/V3 (binary) and V4 (multi-outcome, pari-mutuel) bets.

import { SDK } from '@solworks/poll-sdk';

const sdk = SDK.build({ connection, wallet, commitment: 'confirmed' });

The package builds from the program's generated IDL: npm run build copies ../betmore-core-program/target/idl/* + target/types/* into src/, then runs tsc. Re-run it after any program rebuild so the bundled IDL/types match the deployed program — see Regenerating the IDL & types for the full pipeline.


V4 bets

A V4 bet is multi-outcome (up to 64 outcomes, each a ≤32-byte ASCII label) and pari-mutuel — the pot is split among backers of the winning outcome at settlement; there are no quoted odds. Every V4 program instruction is exposed two ways:

  • sdk.<name>V4(params) — builds, signs, and sends the transaction (returns a signature).
  • sdk.instructions.v4.<name>(params) — returns just the instruction, for callers that batch / optimize their own transactions (e.g. program-api's tx builder).

| Instruction | rpc method | ix-builder | |---|---|---| | initialize bet | initializeBetV4 | instructions.v4.initializeBet | | initialize World Cup bet | initializeWorldCupBetV4 | instructions.v4.initializeWorldCupBet | | add outcome | addOutcomeV4 | instructions.v4.addOutcome | | place wager | placeWagerV4 | instructions.v4.placeWager | | initiate vote | initiateVoteV4 | instructions.v4.initiateVote | | place vote | placeVoteV4 | instructions.v4.placeVote | | settle batch | settleBetBatchV4 | instructions.v4.settleBetBatch | | admin resolve | adminResolveBetV4 | instructions.v4.adminResolveBet | | admin refund batch | adminRefundBetV4 | instructions.v4.adminRefundBet | | close bet | closeBetV4 | instructions.v4.closeBet | | close pool | closePoolV4 | instructions.v4.closePool | | try cancel wager | tryCancelWagerV4 | instructions.v4.tryCancelWager |

Addresses: sdk.addresses.betV4.get(wagerId, owner), sdk.addresses.poolAuthorityV4.get(wagerId, owner). Account fetch: sdk.accounts.betV4.single(betAddress). Status enums map via SDK.convertRustEnumValueToTSEnumValue(value, MarketStatus).


Resolution modes (and the votingDisabled flag)

A V4 bet resolves in one of three ways, selected at creation:

| Mode | How it's created | Who resolves | |---|---|---| | Community vote (default) | isCreatorResolver: false | Any wagerer can initiateVoteV4 once minimumVoteCount active wagers exist; majority placeVoteV4 decides. | | Creator resolver | isCreatorResolver: true | Only the bet creator initiates + casts the deciding vote. | | Admin-resolve-only (new) | isCreatorResolver: false, votingDisabled: true | No voting at all — only the protocol admin authority via adminResolveBetV4. |

What votingDisabled does

InitializeBetV4Params.votingDisabled?: boolean (default false). When true, the on-chain program rejects every voting instruction — both initiateVoteV4 and placeVoteV4 fail with the VotingDisabled error (surfaced as the message "Voting is disabled for this bet"), for community and creator paths alike. The only way such a bet can reach a resolved state is adminResolveBetV4, which requires the signer to be the protocol's withdraw_authority.

This exists for admin/oracle-resolved markets such as World Cup pools, where the outcome is a real-world result fed by a trusted backend rather than a vote. It closes the pot-drain/collusion vector: with voting hard-disabled, neither the creator nor a colluding set of members can resolve the bet to themselves.

Backward-compatible: omit votingDisabled (or pass false) and the bet behaves exactly as before. The flag is forwarded by both initializeBetV4 and instructions.v4.initializeBet.

Create an admin-resolved bet

const { bet } = await sdk.initializeBetV4({
  question: 'Who will win the World Cup?',
  expectedUserCount: 50,
  minimumVoteCount: 2,        // irrelevant once voting is disabled; keep a valid value (>=2 for non-creator-resolver)
  isCreatorResolver: false,   // creator does NOT resolve
  votingDisabled: true,       // <-- hard-disable all voting; admin-resolve-only
  initialOutcomes: ['Brazil', 'Argentina', /* ... up to 64 ASCII labels <=32 bytes */],
});

Any subsequent initiateVoteV4 / placeVoteV4 on that bet throws VotingDisabled.

Resolve it (protocol admin authority only)

await sdk.adminResolveBetV4({
  bet,                 // PublicKey of the V4 bet
  outcomeIndex: 6,     // winning outcome (0-based, < outcomes.length)
  signers: [adminKeypair],          // must be the protocol withdraw_authority
  payerOverride: adminKeypair.publicKey,
});
// bet.status -> Resolved, bet.resolvedOutcomeIndex -> 6
// (if the winning outcome had zero backers, the program resolves to a refund-all state instead)

adminResolveBetV4 only acts on a bet in Pending/Resolving status. Settlement/payout then runs via settleBetBatchV4 (max 5 users/batch).


Operational limits & edge cases (V4)

These are real constraints surfaced by the 48-outcome / 50-wager scale tests. They matter most for large multi-outcome bets like World Cup pools.

Compute budget

Large bets have a big account to deserialize / realloc / iterate, so the 200k default compute is not enough past ~40 outcomes. The SDK raises the compute-unit limit on the heavy V4 methods: initializeBetV4, addOutcomeV4, placeWagerV4, adminResolveBetV4600k; settleBetBatchV41M. (The limit instruction is fixed-length, so this does not affect tx size.)

Transaction size (legacy txs cap at 1232 bytes)

  1. You cannot create a many-outcome bet in a single initializeBetV4 tx. ~40+ string labels as instruction data + accounts + the compute prefix exceed 1232 bytes (real names like "Bosnia and Herzegovina" make it worse). Seed the outcome set incrementally instead — see below.
  2. Settlement fits ≤ 3 users per batch. settleBetBatchV4 carries 5 fixed user slots; with 4+ distinct users plus the compute prefix the tx exceeds 1232 bytes. Use usersPerBatch <= 3 — a 50-member pool settles in ceil(50/3) = 17 batches. Same for adminRefundBetV4.

Creating a large-outcome bet (the fix for #1)

Initialize with a couple of outcomes, then addOutcomeV4 the rest before any wager (outcomes lock on the first wager):

const { bet } = await sdk.initializeBetV4({ /* ... */ initialOutcomes: ['Brazil', 'Argentina'], votingDisabled: true });
for (const label of remainingTeams) {        // the other 46 teams
  await sdk.addOutcomeV4({ bet, label, signers: [creator] });
}
// ...then wagers. (All outcomes must be added before the first wager.)

Alternatives if the per-create tx count matters: use short on-chain labels (e.g. flag codes / indices) and map to display names off-chain — outcomeIndex is the canonical key anyway, the label is only for display + the wager label check; or add a batched add_outcomes instruction (program change) to add several labels per tx. (v0 txs + address lookup tables only compress account keys, not the label data, so they don't reliably solve this on their own.)

World Cup pools: initializeWorldCupBetV4 (the chosen fix)

For World Cup pools the program hardcodes the 48 teams (WORLD_CUP_OUTCOME_LABELS in controllers/bet_v4.rs) and exposes a dedicated initialize_world_cup_bet_v4 instruction. The create tx carries no outcome labels — the program seeds all 48 — so a full pool is created in one transaction, with the real team names on-chain. This path always forces votingDisabled: true and non-creator-resolver (pools are admin/oracle resolved), so it can't be misconfigured.

const { bet } = await sdk.initializeWorldCupBetV4({
  question: 'Who will win the World Cup?',
  expectedUserCount: 50,
  minimumVoteCount: 2,   // irrelevant once voting is disabled; keep valid (>=2)
});
// bet.outcomes -> 48 teams (alphabetical), bet.votingDisabled === true

The on-chain label order MUST stay index-aligned with the off-chain team table (api/src/common/world-cup-teams.ts); the backend resolves a pool by mapping the winning team name → outcomeIndex via that table. Append-only once any pool exists on-chain.

Regenerating the IDL & types

src/idl/betmore_core_program.json and src/types/betmore_core_program.ts are committed, generated artifacts — do not hand-edit them. The .ts file is a ~17k-line camelCase type helper Anchor derives from the JSON IDL (see its header comment); the JSON is the canonical IDL. Both are produced by the Anchor program build, not by this package.

The full pipeline after any change to betmore-core-program:

# 1. Rebuild the program (writes target/idl/* and target/types/*)
cd betmore-core-program && anchor build          # or, from repo root: npm run build

# 2. Copy the fresh IDL + types into this package, then compile
cd ../betmore-core-sdk && npm run build          # runs `npm run copy` then `tsc`

npm run build chains copy-idl (cp ../betmore-core-program/target/idl/* src/idl/) and copy-types (cp ../betmore-core-program/target/types/* src/types/) before tsc, so a plain npm run build here always refreshes both files from the latest program build. Run npm run copy on its own to refresh without compiling.

Always regenerate after a program rebuild so the bundled IDL/types match the deployed program. The IDL metadata.version/address in the generated files should match the on-chain program you are targeting.

On-chain notes

  • The votingDisabled flag lives on the BetV4 account (carved from reserved padding, so adding it did not change the account size; pre-existing bets deserialize to false).
  • Adding the flag + its VotingDisabled error is purely additive to the program — no V3 instruction, account, or error code changed.