Create a Bet
Initialize a new prediction market for users to wager on.
What You'll Do
Create a new onchain bet (prediction market) with a question and parameters.
Prerequisites
- SDK initialized with a wallet
- Program user account created
- Wallet funded with SOL and USDC
Create a Bet
Use the initializeBetV2 method to create a new bet:
import { SDK } from "@solworks/poll-sdk";
const txHash = await sdk.initializeBetV2({
question: "Will Bitcoin hit $100,000 by end of Q1 2026?",
expectedUserCount: 10,
minimumVoteCount: 2,
isCreatorResolver: false,
signers: [wallet],
});
console.log("Bet created:", txHash);Parameters Reference
| Parameter | Type | Constraints | Description |
|---|---|---|---|
question | string | Max 256 characters | The betting question |
expectedUserCount | number | 1-50 | Maximum participants allowed |
minimumVoteCount | number | 1 to expectedUserCount | Votes needed to resolve |
isCreatorResolver | boolean | - | Resolution mode |
signers | Keypair[] | Required | Transaction signers |
Parameter Details
question (max 256 characters)
The prediction market question. This is stored onchain and cannot be changed after creation.
Constraints:
- Maximum 256 characters (enforced onchain)
- Cannot be empty
- Should be unambiguous with clear resolution criteria
Good examples:
- "Will BTC be above $100,000 on March 31, 2026 at 00:00 UTC?"
- "Will the Lakers win the 2026 NBA Championship?"
Bad examples:
- "BTC moon?" (too vague)
- "Will crypto do well?" (no clear resolution criteria)
expectedUserCount (1-50)
The maximum number of participants who can place wagers on this bet.
Constraints:
- Minimum: 1
- Maximum: 50
- Once set, cannot be changed
How it works:
- Each user can place multiple wagers, but each wager counts toward this limit
- When the wager count reaches
expectedUserCount, no more wagers are accepted - Set this based on your expected audience size
Recommendations:
- Small private bets: 2-5
- Group bets: 10-20
- Public markets: 50
minimumVoteCount (1 to expectedUserCount)
The minimum number of votes required to reach consensus and resolve the bet.
Constraints:
- Minimum: 1
- Maximum: must be ≤ expectedUserCount
- Cannot be 0
How resolution works:
1. Any participant can initiate voting
2. All participants with wagers can cast one vote each
3. Once minimumVoteCount votes are cast AND there's a majority, the bet resolves
4. If votes are split (no majority), more votes are needed
Example scenarios:
| expectedUserCount | minimumVoteCount | Resolution |
|---|---|---|
| 10 | 2 | Resolves when 2+ votes agree on outcome |
| 10 | 6 | Requires 6+ votes with majority to resolve |
| 50 | 25 | Requires 25+ votes with majority |
Recommendations:
- Set to at least 2 for fairness
- Higher values = more democratic but slower resolution
- Consider:
Math.ceil(expectedUserCount / 2)for majority requirement
isCreatorResolver
Determines who can resolve the bet outcome.
When false (Community Resolution):
- All participants vote on the outcome
- Bet resolves when
minimumVoteCountis reached with consensus - More decentralized and trustless
- Recommended for public markets
When true (Creator Resolution):
- The bet creator can directly set the outcome
- No community voting required
- Faster resolution
- Requires trust in the creator
- Good for private bets or trusted oracles
// Community-resolved bet (democratic)
await sdk.initializeBetV2({
question: "Will Team A win the championship?",
expectedUserCount: 20,
minimumVoteCount: 10,
isCreatorResolver: false, // Community votes
signers: [wallet],
});
// Creator-resolved bet (trusted oracle)
await sdk.initializeBetV2({
question: "What will be the final score?",
expectedUserCount: 50,
minimumVoteCount: 1,
isCreatorResolver: true, // Creator decides
signers: [wallet],
});On-Chain Limits
| Limit | Value |
|---|---|
| Question length | 256 characters |
| Max participants | 50 |
| Max wagers per bet | 50 |
| Max votes per bet | 50 |
Get Bet Address
After creating, derive the bet address:
const userAddress = sdk.addresses.user.get(wallet.publicKey);
const userAccount = await sdk.accounts.user.single(userAddress);
// wagerId is assigned sequentially per user
const wagerId = userAccount.nextWagerId - 1;
const betAddress = sdk.addresses.betV2.get(wagerId, wallet.publicKey);
console.log("Bet Address:", betAddress.toBase58());Saving the Bet Address
The bet address is a Program Derived Address (PDA) that uniquely identifies your bet onchain. You must save this address to interact with the bet later.
// After creating a bet, save the address
const result = await sdk.initializeBetV2({
question: "Will BTC hit $100k?",
expectedUserCount: 10,
minimumVoteCount: 2,
isCreatorResolver: false,
signers: [wallet],
});
// The result contains the bet address
const betAddress = result.bet;
// Save to your database
await db.bets.create({
address: betAddress.toBase58(),
question: "Will BTC hit $100k?",
creatorWallet: wallet.publicKey.toBase58(),
createdAt: new Date(),
});Why save the address?
- The address is required to place wagers, initiate voting, cast votes, and settle
- Users need the address to join and interact with the bet
- You cannot derive the address without knowing the wagerId and creator
What to store:
| Field | Purpose |
|---|---|
address | The bet's onchain address (base58 string) |
question | For display (also stored onchain) |
creatorWallet | To verify ownership |
wagerId | To re-derive the address if needed |
createdAt | For sorting/filtering |
Re-deriving the Address
If you have the wagerId and creator wallet, you can re-derive the address:
// Re-derive from stored wagerId and creator
const betAddress = sdk.addresses.betV2.get(
storedWagerId,
new PublicKey(storedCreatorWallet)
);Fetch Bet Details
Retrieve the bet account data to check its current state:
const bet = await sdk.accounts.betV2.single(betAddress);
console.log("Bet Details:");
console.log("- Question:", bet.question);
console.log("- Status:", bet.status);
console.log("- Creator Resolver:", bet.isCreatorResolver);
console.log("- Expected Users:", bet.expectedUserCount);
console.log("- Min Votes:", bet.minimumVoteCount);
console.log("- Total For:", bet.totalOiFor.toNumber() / 1e6, "USDC");
console.log("- Total Against:", bet.totalOiAgainst.toNumber() / 1e6, "USDC");
console.log("- Wager Count:", bet.wagers.length);Tracking Bet State Over Time
Poll the bet account to track changes and update your UI:
import { MarketStatus } from "@solworks/poll-sdk";
async function trackBet(betAddress: PublicKey) {
const bet = await sdk.accounts.betV2.single(betAddress);
// Check current status
switch (bet.status) {
case MarketStatus.Pending:
console.log("Accepting wagers");
console.log("Wagers:", bet.wagers.length);
break;
case MarketStatus.Resolving:
console.log("Voting in progress");
console.log("Votes:", bet.votes.length, "/", bet.minimumVoteCount);
break;
case MarketStatus.Resolved:
console.log("Outcome:", bet.resolvedOutcome);
console.log("Ready for settlement");
break;
case MarketStatus.Distributed:
console.log("Settlement complete");
break;
}
return bet;
}
// Poll for updates (e.g., every 5 seconds)
setInterval(async () => {
const bet = await trackBet(betAddress);
updateUI(bet);
}, 5000);Key fields to monitor:
| Field | What it tells you |
|---|---|
status | Current lifecycle stage |
wagers.length | Number of participants |
totalOiFor / totalOiAgainst | Pool sizes per side |
votes.length | Votes cast during resolution |
resolvedOutcome | Final outcome (after resolution) |
updatedAt | Last onchain update timestamp |
Bet Account Structure
The bet account includes:
interface BetAccount {
creator: PublicKey;
createdAt: BN;
updatedAt: BN;
status: MarketStatus;
resolvedOutcome: Outcome;
question: string; // Max 256 chars
expectedUserCount: number; // 1-50
minimumVoteCount: number; // 1 to expectedUserCount
isCreatorResolver: boolean; // Resolution mode
totalOiFor: BN; // Total "For" wagers in lamports
totalOiAgainst: BN; // Total "Against" wagers in lamports
poolAddress: PublicKey; // Escrow holding USDC
wagers: Wager[]; // Max 50 wagers
votes: Vote[]; // Max 50 votes
wagerId: BN;
}Error Cases
| Error | Cause |
|---|---|
QuestionIsEmpty | Question string is empty |
ExpectedUserCountIsZero | expectedUserCount is 0 |
ExpectedUserCountIsGreaterThanFive | expectedUserCount > 50 |
MinimumVoteCountIsZero | minimumVoteCount is 0 |
MinimumVoteCountIsGreaterThanExpectedUserCount | minimumVoteCount > expectedUserCount |
Best Practices
- Clear questions: Write unambiguous questions with clear resolution criteria and dates
- Appropriate participant count: Set
expectedUserCountbased on your expected audience - Balanced vote requirements: Set
minimumVoteCountto ensure fair resolution - Choose resolution mode wisely: Use
isCreatorResolver: trueonly when you have a trusted oracle
What Happens
When you create a bet:
1. Bet account is created at a derived PDA
2. Pool (escrow) account is created for holding wagers
3. Bet status is set to "Pending"
4. Creator is recorded as the bet owner
5. User's nextWagerId is incremented
Next Steps
Your bet is live and accepting wagers. Continue to learn how to place wagers.