Module 08: Prediction Markets
Prerequisites
- Wallet funded with USDB (→01), client initialized (→02)
- Understand Stable+ mechanics — Predict+ tokens are a Stable+ subtype
- Optional but useful: hold ≥ 5 staked tokens if you want to vote in resolution disputes
Next steps after a market action
- Bet on an outcome? Track shares with
getUserShares(); redeem after resolution - Bought a Predict+ token? Borrow against it (→05) and deploy elsewhere
- Created a market? Build the full creator stack via Module 13; compare to token creation
- Want to resolve markets for bounty? See the deep dive in Module 14
1. Why Prediction Markets on BASIS
BASIS prediction markets are structurally different from platforms like Polymarket. Three differences define the edge:
Uncapped payouts. On Polymarket, winning shares always pay exactly $1 — volume affects liquidity, not your return. On BASIS, all pools (every outcome, plus the general pot) merge into one big pot on resolution. Your payout is (your winning shares / total winning shares) × entire merged pot. A share bought at 5c can pay $4+ if the pot is large enough. The economics are better on trade one, at any volume level, because the structure is different.
Instant AMM liquidity. Traditional platforms need a counterparty for every trade. BASIS uses a one-directional AMM — buys go through the AMM, sells go through the order book. Because the AMM only handles buys, virtual liquidity can be set arbitrarily high without real capital backing it. Every market has instant fills from creation, including niche topics and off-peak hours.
Multiple independent income streams. Traditional platforms give you one role: bettor. BASIS opens at minimum three independent profit paths from a single market simultaneously — see Module 13 for full role breakdowns.
The Two Assets Every Market Creates
Every prediction market creates two completely separate assets:
| Asset | What It Is | How It Profits |
|---|---|---|
| Predict+ token | A Stable+ subtype. Market token. Price can only go up, driven by trading volume. | Volume play — appreciates regardless of which outcome wins. Trade on the DEX like any token. |
| Outcome shares | What you buy to bet on a specific result. One set per outcome. | Conviction play — proportional share of the merged pot on resolution. |
These are not the same thing. Buying the Predict+ token is not a bet. Buying outcome shares is not token trading. They have different mechanics, different flows, and different risk profiles.
Who Benefits
| Role | How | What You Need |
|---|---|---|
| Creator | 20% of net trading fees forever (0.1% of trade volume) | A compelling question |
| Bettor | Uncapped payout from winning outcome shares | Conviction on an outcome |
| Trader | Predict+ token appreciates from market volume | Read on activity levels |
| Resolver | Bounty pool for proposing and finalizing correct outcomes | Accurate information |
| Leveraged player | Collateralise Predict+ tokens → borrow USDB → buy outcome shares | Any of the above |
2. How It Works
Market Structure
- A market has a question, up to 150 outcomes (creative market designs in Module 13), an end time (0 = open-ended, or a future timestamp ≥ now + 60s), and a seed amount in $10 increments. Seed minimums vary by market type: Basis-managed public markets = 50 USDB, creator-created public markets = 10 USDB, creator-created private markets = 0 USDB (no minimum). The seed funds the resolution bounty and the general pot — it is not liquidity for the outcome pools.
- At creation, each outcome gets its own AMM pool seeded with virtual liquidity at launch — creator pays nothing for it. Real liquidity accumulates as users buy into outcomes.
- Trading fees are 1.5% gross (compare to Floor+ fee structure). Of that: 1% feeds back into the prediction ecosystem (0.95% → general pot, 0.05% → bounty pool). The remaining 0.5% is the net platform fee, split: creator 20%, staking vault 16%, platform 60%, reward-phase buyers 4%. Creator ends up with 0.1% of all trade volume.
Buying Shares
Shares are bought via AMM — instant fill, no counterparty. Price per share reflects implied probability. As more capital flows into one outcome, shares become more expensive (lower potential upside) and other outcomes become relatively cheaper.
Selling Shares
Selling goes through the P2P order book. You list at your chosen price. Because resolution value can far exceed the AMM buy price (e.g., bought at 5c, resolution value $4+), both sides of an order book trade can be genuinely profitable — a dynamic fixed-payout platforms cannot produce.
On Resolution: The Merged Pot
When a market resolves:
- All outcome pools (winners and losers) + the accumulated general pot merge into one big pot.
- Winning outcome share holders receive proportional payouts:
(your shares / total winning shares) × total pot. - If your outcome lost: no shares to redeem.
Special resolution outcomes (see Module 14 for edge cases):
- INVALID (254): Proportional refund to all participants.
- EARLY (253): Market resets — round increments, fresh proposal cycle. Only the disputer can propose EARLY.
Resolution Lifecycle
Market end time passes
↓
Anyone calls proposeOutcome(marketToken, outcomeId) — posts 5 USDB bond
↓
Challenge period (PROPOSAL_PERIOD — target: 2 hours*)
├── No dispute → finalizeUncontested() → Proposer: bond back + 100% bounty → Winners redeem
└── Disputed (5 USDB bond) → Voting period (DISPUTE_PERIOD — target: 24 hours*)
↓
Staked voters vote (≥ 5 tokens staked, one-staker-one-vote, 70% supermajority)
↓
finalizeMarket() → Winners redeem → Correct side claims bonds + bounty
Current testing values are 30 minutes for all periods (Phase 1 only — production targets in later phases: 2h proposal, 24h dispute/voting, 1h veto). Read values from the contract at runtime — do not hardcode. See Resolver Read Methods.
3. Actions
3a. Create a Market
client.predictionMarkets.createMarketWithMetadata(options) — creates the market and registers metadata on IPFS in one call. Requires SIWE authentication.
JS:
const market = await client.predictionMarkets.createMarketWithMetadata({
marketName: "Will ETH reach $10k this month?",
symbol: "ETH10K", // MUST BE CAPITALISED
endTime: BigInt(Math.floor(Date.now() / 1000) + 86400 * 30),
optionNames: ["Yes", "No"],
maintoken: client.mainTokenAddress,
seedAmount: parseUnits("10", 18), // $10 increments. Min: 50 USDB (Basis-managed public), 10 USDB (creator public), 0 (creator private)
description: "ETH price prediction.",
imageUrl: "https://example.com/eth.jpg",
});
const marketToken = market.marketTokenAddress;
Python:
import time
market = client.prediction_markets.create_market_with_metadata(
market_name="Will ETH reach $10k this month?",
symbol="ETH10K",
end_time=int(time.time()) + 86400 * 30,
option_names=["Yes", "No"],
maintoken=client.main_token_address,
seed_amount=10 * 10**18, # $10 increments. Min: 50 (Basis-managed public), 10 (creator public), 0 (creator private)
)
market_token = market["market_token_address"]
| Parameter | Required | Notes |
|---|---|---|
marketName | yes | Question/title |
symbol | yes | Must be CAPITALISED (e.g., "ETH10K", not "eth10k") |
endTime | yes | Unix timestamp. 0 = open-ended. If set, must be ≥ now + 60s |
optionNames | yes | Array of outcome names, up to 150 |
maintoken | yes | client.mainTokenAddress |
seedAmount | no | USDB seed in 18 decimals. Must be in $10 increments across all tiers. Minimums by market type: Basis-managed public = 50 USDB, creator public = 10 USDB, creator private = 0 (no minimum). Funds the resolution bounty + general pot — not outcome-pool liquidity (pools are virtual at launch) |
description, imageUrl, website, telegram, twitterx | no | Metadata |
frozen | no | true = only whitelisted wallets can buy until unfrozen (see whitelist pattern) |
bonding | no | Reward-phase allocation in USDB (same mechanic as usdbForBonding on token creation; see reward-phase pricing) |
Returns: { hash, receipt, marketTokenAddress, imageUrl, metadata }
For private markets (creator-managed resolution), use client.privateMarkets.createMarketWithMetadata() instead — same parameters plus privateEvent: true to restrict buying to whitelisted addresses. See Section 4.
3b. Bet (Buy Outcome Shares)
client.predictionMarkets.buy(marketToken, outcomeId, inputToken, inputAmount, minUsdb, minShares) — buys shares for a specific outcome. Auto-approves input token (see approval patterns in Module 02).
JS:
// Preview current prices first
const outcomes = await client.marketReader.getAllOutcomes(
"0x396216fc9d2c220afD227B59097cf97B7dEaCb57", // MarketTrading contract
marketToken
);
const yesPrice = outcomes[0].pricePerShare;
const fiveUsdb = parseUnits("5", 18);
const expectedShares = fiveUsdb * BigInt(1e18) / yesPrice;
const minShares = expectedShares * 98n / 100n; // 2% slippage tolerance
const result = await client.predictionMarkets.buy(
marketToken,
0, // outcomeId (0-based)
USDB,
fiveUsdb,
0n, // minUsdb (for non-USDB inputs only)
minShares
);
Python:
outcomes = client.market_reader.get_all_outcomes(
"0x396216fc9d2c220afD227B59097cf97B7dEaCb57", market_token
)
yes_price = int(outcomes[0]["pricePerShare"])
five_usdb = 5 * 10**18
expected_shares = five_usdb * 10**18 // yes_price
min_shares = expected_shares * 98 // 100 # 2% slippage
result = client.prediction_markets.buy(market_token, 0, USDB, five_usdb, 0, min_shares)
| Param | Description |
|---|---|
marketToken | Market token address |
outcomeId | Outcome index, 0-based |
inputToken | Token to pay with (typically USDB address) |
inputAmount | Amount to spend (18 decimals) |
minUsdb | Min USDB equivalent — use for non-USDB inputs, else 0 |
minShares | Min shares to receive — always set in production (see slippage tolerance →04) |
Pre-flight checks: verify outcomeId is within range (getNumOutcomes()), verify market is not resolved (getMarketData().resolved).
3c. Order Book (Sell / P2P Trade)
The order book handles peer-to-peer limit orders for outcome shares. Use it to sell shares before resolution or to buy shares at a specific price. (Advanced order book strategies for traders → Module 13.)
List a Sell Order
client.orderBook.listOrder(marketToken, outcomeId, amount, pricePerShare)
// List 100 shares of outcome 0 at 0.60 USDB per share
const result = await client.orderBook.listOrder(
marketToken, 0, parseUnits("100", 18), parseUnits("0.6", 18)
);
result = client.order_book.list_order(
market_token, 0, 100 * 10**18, 600_000_000_000_000_000 # 0.60 USDB
)
Fill an Order
client.orderBook.buyOrder(marketToken, orderId, fill) — fill is the USDB amount to spend.
client.orderBook.buyMultipleOrders(marketToken, orderIds, usdbAmount) — fill several orders in one tx.
Cancel an Order
client.orderBook.cancelOrder(marketToken, orderId)
Hybrid Fill (Order Book + AMM)
client.predictionMarkets.buyOrdersAndContract(marketToken, outcomeId, orderIds, inputToken, totalInput, minShares) — fills the listed orders first, then buys remaining from AMM, all in one transaction.
Order Book Reads
| Method | Returns |
|---|---|
client.orderBook.getBuyOrderCost(marketToken, orderId, fill) | { baseUsdb, buyerTax, totalCostToBuyer, netToSeller } |
client.orderBook.getBuyOrderAmountsOut(marketToken, orderId, usdbAmount) | { fill, baseUsdb, buyerTax, totalCostToBuyer } |
client.predictionMarkets.getBuyOrderAmountsOut(marketToken, orderId, usdbAmount) | Preview order fill from prediction module |
3d. Redeem (Collect Winnings)
client.predictionMarkets.redeem(marketToken) — after resolution, winning share holders call this to claim their proportional share of the merged pot. Free.
const receipt = await client.predictionMarkets.redeem(marketToken);
// Parse redeemed USDB from transfer logs:
const redeemed = parseEventLogs({ abi: erc20Abi, logs: receipt.logs })
.find(e => e.eventName === 'Transfer' && e.args.to === wallet)?.args.value;
receipt = client.prediction_markets.redeem(market_token)
If your outcome did not win, there is nothing to redeem. "No winning shares" error = your bet lost.
3e. Basic Resolution
Full resolution strategy is covered in Module 14. Here is the mechanical flow.
Step 1 — Discover Markets Needing Resolution
const markets = await client.api.getTokens({ isPrediction: true, limit: 100 });
const needsProposal = markets.data.filter(m => m.predictionStatus === "awaiting_proposal");
const inDispute = markets.data.filter(m => m.predictionStatus === "disputed");
predictionStatus values: "active" | "awaiting_proposal" | "proposed" | "disputed" | "resolved"
Step 2 — Propose an Outcome
client.resolver.proposeOutcome(marketToken, outcomeId) — posts 5 USDB bond automatically. If uncontested after the challenge period, the proposer calls finalizeUncontested() and receives bond back + 100% of the bounty pool.
Also available as client.resolver.propose() — identical behavior.
Step 3 — Finalize (Uncontested)
client.resolver.finalizeUncontested(marketToken) — anyone can call after the challenge period expires with no dispute.
Step 4 — If Disputed
client.resolver.dispute(marketToken, newOutcomeId) — dispute the current proposal with an alternative outcome. Posts 5 USDB bond. Triggers the voting period.
Self-dispute is allowed — you can dispute your own proposal to correct a mistake (cost: one extra bond).
Step 5 — Vote
To vote you must first stake: client.resolver.stake(token) — pass any active ecosystem token address. The SDK auto-reads MIN_STAKE_AMOUNT (currently 5 tokens) and approves. One vote per staker regardless of stake size above minimum.
Then: client.resolver.vote(marketToken, outcomeId)
After voting, staked tokens are locked for 24 hours (VOTE_LOCK_DURATION). Check all loan expiry dates before voting — you cannot unstake to repay a loan during the lock window.
Finalize after voting: client.resolver.finalizeMarket(marketToken) — requires quorum met and 70% supermajority.
Step 6 — Claim Bounty
client.resolver.claimBounty(marketToken) — claim if you were the uncontested proposer or voted on the winning side.
Complete Example (JS)
// 1. Discover and propose
const markets = await client.api.getTokens({ isPrediction: true, limit: 100 });
const market = markets.data.find(m => m.predictionStatus === "awaiting_proposal");
await client.resolver.proposeOutcome(market.address, 0); // 0 = "Yes"
// 2. Wait for challenge period, then try to finalize
try {
await client.resolver.finalizeUncontested(market.address);
// Bond returned + 100% bounty
} catch (e) {
// Was disputed — stake and vote
await client.resolver.stake("0xEcosystemTokenAddress");
await client.resolver.vote(market.address, 0);
// Wait for voting period, then finalize
await client.resolver.finalizeMarket(market.address);
}
// 3. Claim bounty
await client.resolver.claimBounty(market.address);
Resolver Read Methods
| Method | Returns |
|---|---|
isResolved(marketToken) | boolean |
getFinalOutcome(marketToken) | number — winning outcome index |
isInDispute(marketToken) | boolean |
isInVeto(marketToken) | boolean |
getCurrentRound(marketToken) | number |
getDisputeData(marketToken) | Dispute details including proposalEndTime |
getUserStake(marketToken, user) | string |
isVoter(marketToken, user) | boolean |
getVoteCount(marketToken, outcomeId) | number |
hasVoted(marketToken, user) | boolean |
getVoterChoice(marketToken, user) | number |
getBountyPerVote(marketToken) | string |
hasClaimed(marketToken, user) | boolean |
4. Private Markets
Private markets use creator-managed resolution (skipping the public dispute/vote flow). The creator and their designated voters resolve the market directly.
Create: client.privateMarkets.createMarketWithMetadata(options) — same parameters as public plus:
privateEvent: true— restricts buying to whitelisted addresses only (same whitelist pattern as gated tokens)
Manage voters: client.privateMarkets.manageVoter(marketToken, voter, add) — add=true adds, add=false removes.
Resolution flow: After end time, eligible voters call client.privateMarkets.vote(marketToken, outcomeId). Voting window is 15 minutes from the first vote. Majority wins. Anyone calls client.privateMarkets.finalize(marketToken) after the window.
Important: Private markets do NOT show predictionStatus === "awaiting_proposal". To detect: check isPrivate in the API response. Private markets waiting for resolution will show an end time in the past with no finalized outcome.
| Method | Description |
|---|---|
vote(marketToken, outcomeId) | Cast resolution vote (creator + whitelisted voters) |
finalize(marketToken) | Finalize after voting window ends |
claimBounty(marketToken) | Claim resolver bounty |
manageVoter(marketToken, voter, add) | Add/remove voter |
togglePrivateEventBuyers(marketToken, buyers, status) | Whitelist/unwhitelist buyer addresses |
disableFreeze(marketToken) | Open frozen market to public |
manageWhitelist(marketToken, wallets, amount, tag, status) | Manage frozen market whitelist |
Private markets share all prediction market and order book read methods.
5. Read Methods
Prediction Markets (client.predictionMarkets)
| Method | Returns | Notes |
|---|---|---|
getMarketData(marketToken) | MarketData struct | Includes resolved, finalOutcome, generalPot, isPrivate, endTime |
getOutcome(marketToken, outcomeId) | Outcome struct | { virtualReserve, totalCost, circulatingShares } |
getUserShares(marketToken, user, outcomeId) | bigint | Shares held, 18 decimals |
getNumOutcomes(marketToken) | bigint | Total outcome count |
getOptionNames(marketToken) | string[] | Outcome names array |
hasBettedOnMarket(marketToken, user) | boolean | |
getBountyPool(marketToken) | bigint | USDB in bounty pool, 18 decimals |
getGeneralPot(marketToken) | bigint | USDB accumulated in general pot |
getInitialReserves(numOutcomes) | [bigint, bigint] | [perOutcomeReserve, totalReserve] |
getBuyOrderAmountsOut(marketToken, orderId, usdbAmount) | { fill, baseUsdb, buyerTax, totalCostToBuyer } | Preview order book fill |
Market Reader (client.marketReader)
| Method | Returns | Notes |
|---|---|---|
getAllOutcomes(routerAddress, marketToken) | OutcomeInfo[] | Full outcome data including pricePerShare, probability, hasWon. Router = 0x396216fc9d2c220afD227B59097cf97B7dEaCb57 |
estimateSharesOut(routerAddress, marketToken, outcomeId, usdbAmount, orderIds, user) | bigint | Estimated shares from combined AMM + order book fill |
getPotentialPayout(routerAddress, marketToken, outcomeId, sharesAmount, estimatedUsdbToPool) | [bigint, bigint] | [holdPayout, simulatedAmmPayout] |
Implied probability from getAllOutcomes: Number(outcome.probability) / 1e18 * 100 gives percentage.
Market Reader Aggregates (client.api)
// All prediction markets
const all = await client.api.getTokens({ isPrediction: true, limit: 100 });
// Filter by status
const active = all.data.filter(m => m.predictionStatus === "active");
6. Fees
| Action | Cost | Goes To |
|---|---|---|
| Market creation | Seed amount (USDB, $10 increments) | Bounty pool + general pot (outcome pools are virtual at launch — no liquidity deposit required) |
| Buy outcome shares | 1.5% gross | 1% → prediction ecosystem (0.95% general pot, 0.05% bounty); 0.5% net fee (creator 20%, staking →06 16%, platform 60%, reward buyers 4%) |
| Predict+ token trading | 1.5% gross (same structure) | Same as above |
| List order / cancel order | Free | — |
| Fill order | Buyer tax (included in buyerTax field) | Platform |
| Resolution proposal | 5 USDB bond | Returned if uncontested. Winner gets both bonds if disputed |
| Dispute | 5 USDB bond | Winner of dispute gets both bonds |
| Veto | 5 USDB bond | One per market, post-voting window only |
| Stake to vote | 5 tokens minimum (any active ecosystem token) | Staked, returned on unstake (after 24h lock post-vote) |
| Redeem | Free | — |
Creator earnings: 20% of net trading fees = 0.1% of all trade volume on Predict+ tokens (vs 0.3% on Floor+ tokens, because 2/3 of the gross fee on Predict+ goes back into the prediction ecosystem).
No surge tax on Predict+ tokens.
7. Errors
| Error | Cause | Fix |
|---|---|---|
Seed must be in $10 increments | seedAmount not divisible by 10 USDB | Round seed to nearest $10 |
Seed below minimum | Seed < minimum for market type (50 USDB Basis-managed public / 10 USDB creator public / 0 creator private) | Increase seed to match your tier |
Invalid outcome ID | outcomeId ≥ getNumOutcomes() | Check getNumOutcomes() before buy |
Market already resolved | Buying shares on a resolved market | Check getMarketData().resolved |
Slippage exceeded | minShares not met | Increase tolerance or re-preview price |
Market not ended | Proposing before endTime | Wait for end time |
Already proposed | Another proposal already active | Check getDisputeData() for existing proposal |
Challenge period active | Calling finalizeUncontested before period expires | Wait for PROPOSAL_PERIOD to elapse |
Not in dispute | Calling vote/finalize before a dispute is filed | File dispute first |
Voting period not ended | Calling finalizeMarket before DISPUTE_PERIOD expires | Wait for voting period |
Tie - vote more | No 70% supermajority reached | More voters needed |
Quorum not met | Insufficient voters | Wait for more votes |
Already voted | Voting twice on same market | One vote per staker |
Insufficient stake | Voting without ≥ 5 tokens staked | Call stake(token) first |
Cannot fill own order | Trying to fill your own order book listing | Use a different wallet |
No winning shares | Calling redeem with shares in a losing outcome | Outcome lost — nothing to redeem |
Symbol must be capitalised | Lowercase symbol in createMarketWithMetadata | Use "ETH10K" not "eth10k" |
8. Strategy Context
Stacking: Paths C and D
Prediction markets fit into two stacking paths. Use these as building blocks inside multi-stack strategies (full stacking reference → Module 12).
Path C — Predict+ Token Path:
USDB → buy Predict+ token → borrow USDB against it
- Position held: Predict+ token (appreciates from market volume regardless of outcome)
- USDB recovered: ~96.5% of input (1.5% buy tax + 2% loan origination)
- Risk: loan expiry + market must stay active
- Points: Trade + Prediction + Lending categories (airdrop scoring)
Path D — Outcome Bet Path:
USDB → buy outcome shares → [wait for resolution] → USDB (if correct)
- Position held: outcome shares
- USDB recovered: only on correct outcome — but payout is uncapped
- Risk: you lose if the outcome is wrong
- Points: Prediction category (airdrop scoring)
Combining C and D (the Quick Stack — see also Module 12 §5):
- Buy Predict+ tokens → borrow USDB
- Use borrowed USDB to buy outcome shares on the same market
- Two positions from one bag: token appreciating from volume, shares waiting on resolution
- Win the bet → collect winnings → repay loan → still own the Predict+ tokens
Three-stack fee reality check: After Path A (wSTASIS →06) → Path C (Predict+) → Path D (bet), you've deployed ~91-92 cents of every dollar across three positions with ~8-9 cents total in fees. Three stacks is the sweet spot before fees bite meaningfully.
Creator vs Bettor vs Trader
Three independent roles, each profitable standalone. Combining them is the real edge.
Creator (passive): Earn 20% of net fees from all trading forever. Create questions that generate genuine disagreement. A dead market costs gas with zero return.
Bettor (conviction): Uncapped payouts reward early conviction. Buy before consensus forms. Use the order book to exit if conviction changes — you can list shares at your own price, and both sides of the trade can genuinely profit because of the uncapped resolution value.
Trader (volume): Buy Predict+ tokens on high-activity markets. You're betting on controversy and volume, not outcomes. Predict+ tokens are a Stable+ subtype — price can only go up. Hold through the market lifecycle and sell after resolution for maximum appreciation.
The maximum extraction play: create the market + buy Predict+ tokens + bet on an outcome + resolve it yourself. Four independent income streams from one market.
Full roles, combined strategies, and capital recycling loops → Module 13. Advanced resolution, bounty hunting, veto mechanics → Module 14.
Key Facts (Quick Reference)
| Parameter | Value |
|---|---|
| Max outcomes per market | 150 |
| Seed increment | $10 USDB |
| Min seed (Basis-managed public) | 50 USDB |
| Min seed (creator public) | 10 USDB |
| Min seed (creator private) | 0 (no minimum) |
| Creator fee share | 20% of net fee = 0.1% of trade volume |
| Proposal bond | 5 USDB |
| Dispute bond | 5 USDB (no escalation) |
| Min stake to vote | 5 tokens (any active ecosystem token) |
| Voting rule | One-staker-one-vote, 70% supermajority |
| Vote lock duration | 24 hours after voting |
| Uncapped payouts | All pools merge on resolution |
| No surge tax | Predict+ tokens are exempt |
Error Handling
Pre-Flight Checks
Before prediction market operations:
- Seed in $10 increments across all tiers. Minimums: 50 USDB (Basis-managed public), 10 USDB (creator public), 0 (creator private)
- End time: 0 (open-ended) or ≥ now + 60 seconds
- Check outcome IDs exist before betting/proposing (use
getMarketData()) - For resolution: staked ≥ 5 tokens in resolver, market in correct phase, not in veto
- Check USDB balance before seeding or betting
- Simulate order book fills before large orders
Common Errors
| Error | When | Fix |
|---|---|---|
"Seed below minimum" / "Seed low" | createMarket() | Increase seed to match tier min (50 Basis-managed public / 10 creator public / 0 creator private) |
"Seed must be in increments of 10 USD" | createMarket() | Use multiples of 10 ($50, $100, not $55) |
"End time error" | createMarket() | Set 0 (no end) or future timestamp ≥ now + 60s |
"Bad outcome" / "Invalid outcome" | Betting, proposing, voting | Check getMarketInfo() for valid outcome IDs |
"min shares not met" / "min usdc not met" | Prediction buy/sell | Increase slippage tolerance or reduce size. Note: "usdc" is a legacy contract string — it means USDB. |
"market resolved" | Trading on resolved market | Redeem winning shares or accept loss |
"Already proposed" | proposeResolution() | Dispute it (5 USDB) or wait for quiet period |
"Already in dispute" | disputeResolution() | Vote instead of filing another dispute |
"No proposal to dispute" | disputeResolution() | Propose a resolution yourself first |
"No winning shares" / "No payout" | redeem() | Your bet lost — nothing to claim |
"Not voting" | vote() | Market not in dispute phase; wait for dispute |
"Must stake 5 tokens to vote" | vote() | Stake ≥ 5 tokens in resolver first |
"Stake locked due to recent vote" | Unstake from resolver | Wait for cooldown to expire (24h after voting) |
"cannot vote during veto" | vote() during veto | Wait for veto period to end |
"Order inactive or market resolved" | Filling limit order | Browse active orders for this market |
"Not your order" | Cancelling limit order | Only cancel your own orders |
"Cannot fill own order" | Filling own order | Use different wallet, or cancel and re-place |
"Insufficient available shares" | Selling/listing shares | Check userShares balance first |
Recovery Flow: Market Resolution Disputed
- Your proposal was disputed — you lose nothing (bond is at risk only if outcome is overturned)
- Wait for voting phase to begin
- If you have ≥ 5 staked tokens: vote for your proposed outcome
- Rally other stakers to vote (70% supermajority needed)
- If voting resolves in your favor: you earn the bounty. If not: bond is forfeited.
Recovery Flow: Shares Have No Value After Resolution
- Check which outcome won:
getMarketData()→finalOutcome - If you hold winning shares: call
redeem()— payout = your share of entire merged pool - If you hold losing shares: nothing to claim. Learn from the loss.
- Check if you also hold Predict+ tokens: these may still have value (Stable+ mechanics — price only goes up; sell on the DEX →04)