Core Concepts
Understand the fundamental building blocks of the Poll protocol.
Overview
The Poll SDK wraps the Poll Anchor program on Solana. Every action — creating a bet, placing a wager, voting, settling — corresponds to a Solana transaction. This guide explains the key on-chain concepts you need to understand before building.
Program User
Every wallet interacting with the protocol must first create a Program User account. This account tracks statistics and links your wallet to on-chain activity:
const userAddress = sdk.addresses.user.get(wallet.publicKey);
const user = await sdk.accounts.user.single(userAddress);
console.log("Total wagers:", user.totalWagersCount);
console.log("Total wagered:", user.totalWageredAmount);Bet
A Bet is a prediction market. It has a question, collects wagers, and resolves through a voting mechanism:
The topic being predicted (max 280 chars)
For (Yes) and Against (No)
User positions with USDC amounts
Resolution votes from participants
Lifecycle state from Draft to Distributed
Escrow account holding all wagered USDC
const bet = await sdk.accounts.betV2.single(betAddress);
console.log("Question:", bet.question);
console.log("Total For:", bet.totalOiFor);
console.log("Total Against:", bet.totalOiAgainst);Multi-outcome bets (V4)
Binary bets have exactly two sides, For and Against. V4 bets generalize this to up to 64 named outcomes — for example, every team in a tournament. They share the same pari-mutuel pool, lifecycle, and settlement model; the difference is how outcomes are represented. Good for tournament winners, group-stage standings, award shows, races with many runners, and any “who/which one” question with more than two answers:
An array of up to 64 labels (e.g. "Brazil", "Spain")
A 0-based number replacing Outcome.For/Against everywhere
The winning index, or null for a refund-all
Optional admin-resolve-only mode (no community vote)
// Create a multi-outcome bet
const { bet } = await sdk.initializeBetV4({
question: "Who wins Group A?",
expectedUserCount: 10,
minimumVoteCount: 2,
isCreatorResolver: false,
initialOutcomes: ["Brazil", "Spain", "France"],
signers: [wallet],
});
// Wager and vote by outcome index instead of For/Against
await sdk.placeWagerV4({ bet, amount: "25000000", outcomeIndex: 0, signers: [wallet] });
const mapped = sdk.accounts.mapBetV4(await sdk.accounts.betV4.single(bet));
mapped.outcomes.forEach((o, i) => console.log(i, o.label, o.totalOi));Wager
A Wager is a user position on a specific bet outcome. The pool of all wagers determines payout ratios — winners split the total pot proportionally:
On multi-outcome (V4) bets the side field is replaced by a numeric outcomeIndex. A user holds one wager per bet, so adding more stake must use the same outcome.
Voting & Resolution
Bets resolve through consensus voting among participants:
- 1Any participant initiates voting via
initiateVoteV2() - 2Participants (or designated resolvers) cast votes via
placeVoteV2() - 3Once
minimumVoteCountreached with consensus, outcome is finalized - 4Settlement distributes winnings minus protocol fees via
settleBetBatchV2()
import { Outcome } from "@solworks/poll-sdk";
// Outcome options
Outcome.NotResolvedYet; // 0 - Default state
Outcome.For; // 1 - "Yes" wins
Outcome.Against; // 2 - "No" wins
Outcome.Tied; // 3 - Push, refunds issuedMarket Status
Bets progress through defined states:
| Status | Description |
|---|---|
| Draft | Initial state, not yet active |
| Pending | Active, accepting wagers |
| Resolving | Voting in progress |
| Resolved | Outcome determined, awaiting settlement |
| Distributed | Payouts complete |
| Canceled | Bet canceled |
| Refunded | All wagers returned |
import { MarketStatus } from "@solworks/poll-sdk";
if (bet.status === MarketStatus.Pending) {
console.log("Bet is accepting wagers");
}Program Derived Addresses
The SDK provides helpers to derive all on-chain account addresses deterministically:
// Protocol config
const protocolAddress = sdk.addresses.protocol.get();
// User account
const userAddress = sdk.addresses.user.get(wallet.publicKey);
// Bet account
const betAddress = sdk.addresses.betV2.get(wagerId, creator);
// Pool (escrow) address
const poolAddress = sdk.addresses.poolAuthority.get(wagerId, creator);
// Multi-outcome (V4) bet + pool
const betV4Address = sdk.addresses.betV4.get(wagerId, creator);
const poolV4Address = sdk.addresses.poolAuthorityV4.get(wagerId, creator);USDC & Tokens
All wagers use USDC (an SPL token). The SDK handles token account lookups automatically. Amounts are expressed in human-readable USDC (e.g. 10 = 10 USDC) unless you pass rawAmount:
// Human-readable — 10 USDC
await sdk.placeWagerV2({ bet, amount: 10, side: Outcome.For, signers });
// Raw — 10,000,000 units (same as 10 USDC)
await sdk.placeWagerV2({ bet, rawAmount: 10_000_000, side: Outcome.For, signers });