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:

typescript
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:

Question

The topic being predicted (max 280 chars)

Options

For (Yes) and Against (No)

Wagers

User positions with USDC amounts

Votes

Resolution votes from participants

Status

Lifecycle state from Draft to Distributed

Pool

Escrow account holding all wagered USDC

typescript
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:

Outcomes

An array of up to 64 labels (e.g. "Brazil", "Spain")

outcomeIndex

A 0-based number replacing Outcome.For/Against everywhere

resolvedOutcomeIndex

The winning index, or null for a refund-all

votingDisabled

Optional admin-resolve-only mode (no community vote)

typescript
// 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:

amountUSDC locked in escrow
sideOutcome.For or Outcome.Against
statusOpen → SettledWin or SettledLoss

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:

  1. 1Any participant initiates voting via initiateVoteV2()
  2. 2Participants (or designated resolvers) cast votes via placeVoteV2()
  3. 3Once minimumVoteCount reached with consensus, outcome is finalized
  4. 4Settlement distributes winnings minus protocol fees via settleBetBatchV2()
typescript
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 issued

Market Status

Bets progress through defined states:

StatusDescription
DraftInitial state, not yet active
PendingActive, accepting wagers
ResolvingVoting in progress
ResolvedOutcome determined, awaiting settlement
DistributedPayouts complete
CanceledBet canceled
RefundedAll wagers returned
typescript
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:

typescript
// 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:

typescript
// 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 });