Vortx
WebsiteDiscordTwitter/XLaunch App
  • Background
    • 👋Welcome to VORTX
    • ⁉️What is VORTX?
  • Platform Overview
    • 🚀Getting Started with VORTX
    • 🔎How Prediction Markets Work
    • 🆕Create New Markets
    • 🗒️How Market Resolutions Work
    • 💰Oracle Rewards
    • 💸Fees and Economics
  • Technical Details
    • 🔧Technical Overview
    • 🏭Factory Contract
  • 🔮Prediction Market Contract
  • 📘OrderBook Contract
  • About VORTX
    • 💵Tokenomics
Powered by GitBook

© 2025 VORTX. All rights reserved.

On this page
  • Core Functionality
  • Token Management
  • Market Resolution
  • Integration with OrderBook
  • Market Information
  • Token Contracts (YES/NO)
  • Events
  • Access Control
  • Mathematical Guarantees
  • Error Handling
  • Usage Examples

Prediction Market Contract

Each prediction market on VORTX is its own smart contract that manages the market's lifecycle, tokens, and resolution. This contract handles everything from initial liquidity to final payouts.

Core Functionality

Primary Purpose

  • Manage market metadata: Store title, description, end time, resolution criteria

  • Control YES/NO tokens: Mint, burn, and manage token supply

  • Handle liquidity: Process deposits and convert to tokens

  • Manage resolution: Track outcome and enable winner payouts

  • Integrate with OrderBook: Enable trading through central exchange

Key State Variables

string public title;                    // Market question
string public description;              // Detailed explanation
uint256 public endTime;                 // When trading stops
string public resolutionCriteria;      // How outcome is determined
address public creator;                 // Market creator address
IERC20 public usdt0Token;              // Collateral token
IERC20 public yesToken;                // YES outcome token
IERC20 public noToken;                 // NO outcome token
IOrderBook public orderBook;           // Central trading engine
uint256 public marketId;               // Unique identifier
bool public isResolved;                // Resolution status
bool public outcome;                   // Final result (true = YES wins)
uint256 public totalDeposited;         // Total USDT deposited
uint256 public resolutionTime;         // When market was resolved

Token Management

Initial Liquidity Processing

When market is created, converts USDT to YES/NO tokens.

function processInitialLiquidity(address recipient, uint256 amount) external onlyFactory {
    require(amount >= 100 * 10**6, "Minimum 100 USDT required");
    
    // Mint equal amounts of YES and NO tokens
    yesToken.mint(recipient, amount);
    noToken.mint(recipient, amount);
    
    totalDeposited += amount;
    
    emit LiquidityAdded(recipient, amount, amount, amount);
}

Deposit Function

Anyone can deposit USDT to get YES/NO tokens.

function deposit(uint256 amount) external {
    require(!isResolved, "Market already resolved");
    require(block.timestamp < endTime, "Market has ended");
    require(amount > 0, "Amount must be positive");
    
    // Transfer USDT from user
    require(usdt0Token.transferFrom(msg.sender, address(this), amount), "USDT transfer failed");
    
    // Mint equal YES and NO tokens
    yesToken.mint(msg.sender, amount);
    noToken.mint(msg.sender, amount);
    
    totalDeposited += amount;
    
    emit TokensMinted(msg.sender, amount, amount);
}

Why this matters: This is how market making works - deposit USDT, get both tokens, sell the side you disagree with.

Redeem Function

Burn YES/NO token pairs to get USDT back.

function redeem(uint256 amount) external {
    require(amount > 0, "Amount must be positive");
    
    // Check user has both token types
    require(yesToken.balanceOf(msg.sender) >= amount, "Insufficient YES tokens");
    require(noToken.balanceOf(msg.sender) >= amount, "Insufficient NO tokens");
    
    // Burn both token types
    yesToken.burnFrom(msg.sender, amount);
    noToken.burnFrom(msg.sender, amount);
    
    // Return USDT
    require(usdt0Token.transfer(msg.sender, amount), "USDT transfer failed");
    
    totalDeposited -= amount;
    
    emit TokensRedeemed(msg.sender, amount);
}

Use case: If you have both YES and NO tokens, you can always get your USDT back (guaranteed $1 per pair).

Market Resolution

Resolution Process

Only admin can resolve during Phase 1.

function resolve(bool _outcome) external onlyAdmin {
    require(!isResolved, "Already resolved");
    require(block.timestamp >= endTime, "Market still active");
    
    isResolved = true;
    outcome = _outcome;
    resolutionTime = block.timestamp;
    
    // Notify OrderBook that trading should stop
    orderBook.marketResolved(marketId);
    
    emit MarketResolved(marketId, _outcome, block.timestamp);
}

Claiming Winnings

Winners can redeem their tokens for USDT.

function claimWinnings() external {
    require(isResolved, "Market not resolved yet");
    
    IERC20 winningToken = outcome ? yesToken : noToken;
    uint256 winningAmount = winningToken.balanceOf(msg.sender);
    
    require(winningAmount > 0, "No winning tokens to claim");
    
    // Burn winning tokens
    winningToken.burnFrom(msg.sender, winningAmount);
    
    // Calculate payout (98% after 2% resolution fee)
    uint256 grossPayout = winningAmount;
    uint256 resolutionFee = (grossPayout * 200) / 10000; // 2%
    uint256 netPayout = grossPayout - resolutionFee;
    
    // Transfer net amount to winner
    require(usdt0Token.transfer(msg.sender, netPayout), "Payout failed");
    
    // Send resolution fee to oracle network
    require(usdt0Token.transfer(oracleManager, resolutionFee), "Fee transfer failed");
    
    emit WinningsClaimed(msg.sender, winningAmount, netPayout, resolutionFee);
}

Integration with OrderBook

Trading Authorization

Market authorizes OrderBook to move tokens during trades.

function authorizeTrading() external onlyFactory {
    // Give OrderBook permission to transfer tokens during trades
    yesToken.approve(address(orderBook), type(uint256).max);
    noToken.approve(address(orderBook), type(uint256).max);
}

Trade Execution Support

OrderBook calls these functions during trades.

function executeTokenTransfer(
    address tokenAddress,
    address from,
    address to,
    uint256 amount
) external onlyOrderBook {
    IERC20 token = IERC20(tokenAddress);
    require(token == yesToken || token == noToken, "Invalid token");
    
    // OrderBook handles the actual transfer logic
    // This function validates the market allows the trade
    require(!isResolved, "Market resolved");
    require(block.timestamp < endTime, "Market ended");
}

Market Information

View Functions

Public functions to read market state.

function getMarketInfo() external view returns (
    string memory _title,
    string memory _description,
    uint256 _endTime,
    bool _isResolved,
    bool _outcome,
    uint256 _totalDeposited,
    address _yesToken,
    address _noToken
) {
    return (title, description, endTime, isResolved, outcome, totalDeposited, address(yesToken), address(noToken));
}

function getTokenSupply() external view returns (uint256 yesSupply, uint256 noSupply) {
    return (yesToken.totalSupply(), noToken.totalSupply());
}

function timeUntilEnd() external view returns (uint256) {
    if (block.timestamp >= endTime) return 0;
    return endTime - block.timestamp;
}

Token Contracts (YES/NO)

MarketToken.sol

Each market deploys two instances of this token contract.

contract MarketToken is ERC20, Ownable {
    constructor(
        string memory name,
        string memory symbol,
        address marketAddress
    ) ERC20(name, symbol) {
        _transferOwnership(marketAddress);
    }
    
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
    
    function burnFrom(address from, uint256 amount) external onlyOwner {
        _burn(from, amount);
    }
}

Token naming convention:

  • YES tokens: "Market #123 YES" (symbol: "M123Y")

  • NO tokens: "Market #123 NO" (symbol: "M123N")

Events

Core Events

event LiquidityAdded(address indexed user, uint256 usdtAmount, uint256 yesTokens, uint256 noTokens);
event TokensMinted(address indexed user, uint256 yesAmount, uint256 noAmount);
event TokensRedeemed(address indexed user, uint256 amount);
event MarketResolved(uint256 indexed marketId, bool outcome, uint256 timestamp);
event WinningsClaimed(address indexed winner, uint256 tokenAmount, uint256 payout, uint256 fee);

Access Control

Permission Levels

modifier onlyFactory() {
    require(msg.sender == factory, "Only factory can call");
    _;
}

modifier onlyOrderBook() {
    require(msg.sender == address(orderBook), "Only OrderBook can call");
    _;
}

modifier onlyAdmin() {
    require(msg.sender == admin, "Only admin can call");
    _;
}

Mathematical Guarantees

Token Conservation

The contract enforces: Total YES supply = Total NO supply = Total USDT deposited

function verifyTokenConservation() external view returns (bool) {
    uint256 yesSupply = yesToken.totalSupply();
    uint256 noSupply = noToken.totalSupply();
    
    // These should always be equal
    return (yesSupply == noSupply && yesSupply == totalDeposited);
}

Arbitrage Prevention

Smart contract math prevents price manipulation:

  • If YES + NO ≠ $1, arbitrage opportunities exist

  • Users can deposit USDT → get both tokens → sell at profit

  • This naturally corrects any pricing imbalances

Error Handling

Common Revert Conditions

require(!isResolved, "Market already resolved");
require(block.timestamp < endTime, "Market has ended");
require(amount > 0, "Amount must be positive");
require(yesToken.balanceOf(msg.sender) >= amount, "Insufficient YES tokens");

Edge Case Handling

  • Market ends before resolution: Admin can still resolve

  • No trading volume: Market can still be resolved

  • Partial token holdings: Users can redeem any amount of pairs

Usage Examples

Market Making Strategy

// 1. Deposit USDT to get both tokens
await market.deposit(ethers.utils.parseEther("1000")); // Get 1000 YES + 1000 NO

// 2. Sell the side you disagree with
await yesToken.approve(orderBookAddress, ethers.utils.parseEther("1000"));
await orderBook.placeOrder(marketId, TokenType.YES, false, OrderType.MARKET, 0, ethers.utils.parseEther("1000"));

// 3. Keep NO tokens if you think outcome is NO

Claiming Winnings

// After market resolves in your favor
const winningBalance = await yesToken.balanceOf(userAddress);
if (winningBalance > 0) {
    await market.claimWinnings();
    // Receive 98% of token value in USDT
}

The PredictionMarket contract is the heart of each individual market, managing the entire lifecycle from creation to final payout while maintaining mathematical precision and security.

PreviousFactory ContractNextOrderBook Contract

Last updated 1 day ago

🔮