Skip to main content

Overview

The PredictionMarketV2 contract manages the complete lifecycle of prediction markets, from creation through resolution and payout. It implements on-chain Levenshtein distance calculation for winner determination and features pull-based fee collection to prevent griefing attacks. Contract Address: TBD (Deployment pending)

Constants

ConstantValueDescription
PLATFORM_FEE_BPS700Platform fee in basis points (7%)
MIN_BET0.001 etherMinimum bet amount
BETTING_CUTOFF1 hourBetting closes 1 hour before market end
MIN_SUBMISSIONS2Minimum submissions required for resolution
MAX_TEXT_LENGTH280Maximum prediction text length (characters)

Market Creation

createMarket

Creates a new prediction market for a social media actor.
function createMarket(
    string calldata _actorHandle,
    uint256 _duration
) external whenNotPaused returns (uint256)
_actorHandle
string
required
Social media handle being predicted (e.g., “@elonmusk”)
_duration
uint256
required
Market duration in seconds. Must be between 1 hour and 30 days.
marketId
uint256
The ID of the newly created market
Emits: MarketCreated(uint256 indexed marketId, string actorHandle, uint256 endTime, address creator) Errors:
  • InvalidDuration() - Duration not between 1 hour and 30 days
Example:
// Create market for 7 days
uint256 marketId = predictionMarket.createMarket("@elonmusk", 7 days);

Submission Management

createSubmission

Submit a prediction and stake ETH on it.
function createSubmission(
    uint256 _marketId,
    string calldata _predictedText
) external payable whenNotPaused nonReentrant returns (uint256)
_marketId
uint256
required
The market ID to submit to
_predictedText
string
required
Your prediction of what the actor will say. Must be 1-280 characters.
msg.value
uint256
required
ETH amount to stake. Must be at least MIN_BET (0.001 ETH).
submissionId
uint256
The ID of the newly created submission
Emits: SubmissionCreated(uint256 indexed submissionId, uint256 indexed marketId, address submitter, string predictedText, uint256 amount) Errors:
  • MarketNotFound() - Market ID doesn’t exist
  • MarketAlreadyResolved() - Market already resolved
  • MarketEnded() - Market end time passed
  • BettingCutoffPassed() - Within 1 hour of market end
  • InsufficientBet() - Bet below minimum
  • EmptyPrediction() - Prediction text is empty
  • PredictionTooLong() - Prediction exceeds 280 characters
Example:
// Submit prediction with 0.01 ETH stake
uint256 submissionId = predictionMarket.createSubmission{value: 0.01 ether}(
    marketId,
    "gm"
);

Market Resolution

resolveMarket

Resolve a market by providing the actual text. Only callable by contract owner.
function resolveMarket(
    uint256 _marketId,
    string calldata _actualText
) external onlyOwner
_marketId
uint256
required
The market ID to resolve
_actualText
string
required
The actual text the actor posted
Emits: MarketResolved(uint256 indexed marketId, uint256 winningSubmissionId, string actualText, uint256 winningDistance) Resolution Logic:
  • Winner determined by minimum Levenshtein distance to actual text
  • First submitter wins ties (deterministic)
  • Requires at least 2 submissions
Errors:
  • MarketNotFound() - Market ID doesn’t exist
  • MarketAlreadyResolved() - Market already resolved
  • MarketNotEnded() - Market end time not reached
  • MinimumSubmissionsNotMet() - Less than 2 submissions
Example:
// Resolve market with actual tweet text
predictionMarket.resolveMarket(marketId, "gm frens");

Payout Claims

claimPayout

Claim payout for a winning submission.
function claimPayout(uint256 _submissionId) external nonReentrant
_submissionId
uint256
required
The submission ID to claim payout for
Emits: PayoutClaimed(uint256 indexed submissionId, address indexed claimer, uint256 amount) Payout Calculation:
  • Winner receives: totalPool * (100 - 7) / 100 = 93% of pool
  • Platform receives: totalPool * 7 / 100 = 7% fee
  • Fees accumulated for pull-based withdrawal
Errors:
  • SubmissionNotFound() - Submission ID doesn’t exist
  • MarketNotEnded() - Market not resolved yet
  • NotWinningSubmission() - Not the winning submission
  • AlreadyClaimed() - Payout already claimed
  • TransferFailed() - ETH transfer failed
Example:
// Claim payout for winning submission
predictionMarket.claimPayout(submissionId);

refundSingleSubmission

Refund the only submission when market ends with single entry. Prevents unfair fee loss when there’s no competition.
function refundSingleSubmission(uint256 _marketId) external nonReentrant
_marketId
uint256
required
The market with single submission
Emits: SingleSubmissionRefunded(uint256 indexed marketId, uint256 indexed submissionId, address submitter, uint256 amount) Errors:
  • MarketNotFound() - Market ID doesn’t exist
  • MarketAlreadyResolved() - Market already resolved
  • MarketNotEndedForRefund() - Market hasn’t ended yet
  • NotSingleSubmission() - Market has more than 1 submission
  • AlreadyClaimed() - Already refunded
  • TransferFailed() - ETH transfer failed

Fee Management

withdrawFees

Withdraw accumulated fees using pull-based pattern.
function withdrawFees() external nonReentrant
Emits: FeesWithdrawn(address indexed recipient, uint256 amount) Errors:
  • NoFeesToWithdraw() - No fees available for caller
  • TransferFailed() - ETH transfer failed
Example:
// Withdraw accumulated fees
predictionMarket.withdrawFees();

View Functions

getMarketSubmissions

Get all submission IDs for a market.
function getMarketSubmissions(uint256 _marketId) external view returns (uint256[] memory)
_marketId
uint256
required
The market ID
submissionIds
uint256[]
Array of submission IDs for the market

getUserSubmissions

Get all submission IDs for a user.
function getUserSubmissions(address _user) external view returns (uint256[] memory)
_user
address
required
The user address
submissionIds
uint256[]
Array of submission IDs for the user

getMarketDetails

Get comprehensive market information.
function getMarketDetails(uint256 _marketId) external view returns (
    string memory actorHandle,
    uint256 endTime,
    uint256 totalPool,
    bool resolved,
    uint256 winningSubmissionId,
    address creator,
    uint256[] memory submissionIds
)
_marketId
uint256
required
The market ID
actorHandle
string
The social media handle being predicted
endTime
uint256
Unix timestamp when the market ends
totalPool
uint256
Total ETH staked in the market (in wei)
resolved
bool
Whether the market has been resolved
winningSubmissionId
uint256
The winning submission ID (0 if unresolved)
creator
address
Address that created the market
submissionIds
uint256[]
All submission IDs for this market

getSubmissionDetails

Get comprehensive submission information.
function getSubmissionDetails(uint256 _submissionId) external view returns (
    uint256 marketId,
    address submitter,
    string memory predictedText,
    uint256 amount,
    bool claimed
)
_submissionId
uint256
required
The submission ID
marketId
uint256
The market this submission belongs to
submitter
address
Address that created the submission
predictedText
string
The predicted text
amount
uint256
ETH staked on this prediction (in wei)
claimed
bool
Whether payout has been claimed

Admin Functions

setFeeRecipient

Update the fee recipient address. Only callable by contract owner.
function setFeeRecipient(address _newRecipient) external onlyOwner
_newRecipient
address
required
New address to receive platform fees

pause

Pause the contract in emergency situations. Only callable by contract owner.
function pause() external onlyOwner
When paused, createMarket and createSubmission cannot be called.

unpause

Unpause the contract. Only callable by contract owner.
function unpause() external onlyOwner

emergencyWithdraw

Emergency withdraw for unresolved markets. Returns funds to submitters. Only callable by contract owner.
function emergencyWithdraw(uint256 _marketId) external onlyOwner nonReentrant
_marketId
uint256
required
The market to emergency withdraw from
Requirements:
  • Only callable 7 days after market end time
  • Market must not be resolved
  • Returns full stake to each submitter (no fees taken)
Errors:
  • MarketNotFound() - Market ID doesn’t exist
  • MarketAlreadyResolved() - Market already resolved
  • MarketNotEnded() - Not 7 days past market end

Utility Functions

levenshteinDistance

Calculate Levenshtein (edit) distance between two strings. Pure function, callable off-chain.
function levenshteinDistance(
    string memory a,
    string memory b
) public pure returns (uint256)
a
string
required
First string
b
string
required
Second string
distance
uint256
The edit distance between the strings
Gas Cost: O(m*n) where m and n are string lengths. Recommended max length: ~100 characters each. Example:
uint256 distance = predictionMarket.levenshteinDistance("hello", "hallo");
// Returns: 1

State Variables

marketCount

uint256 public marketCount
Total number of markets created (also serves as next market ID).

submissionCount

uint256 public submissionCount
Total number of submissions created (also serves as next submission ID).

markets

mapping(uint256 => Market) public markets
Mapping from market ID to Market struct.

submissions

mapping(uint256 => Submission) public submissions
Mapping from submission ID to Submission struct.

pendingFees

mapping(address => uint256) public pendingFees
Accumulated fees available for withdrawal by address.

feeRecipient

address public feeRecipient
Address designated to receive platform fees.

Build docs developers (and LLMs) love