YNpredict Technical Whitepaper

A code-level explanation of how smart contracts and oracle design enforce decentralization, automation, and immutability.

1. Core Logic: Atomic Splitting

Pricing Formula

Price(Yes) + Price(No) = 1.00 USDC

After depositing 1 USDC into the contract, a user can mint 1 Yes share and 1 No share. Together they form a complete set, where one side will end at $1 and the other at $0.

Technical Standard: YNpredict follows the Gnosis Conditional Token Framework (CTF).

2. Smart Contract Code Examples

Contract Structure

contract YNpredictMarket {
    IERC20 public collateralToken;  // USDC
    IOracle public oracle;          // Oracle interface

    struct Market {
        string question;
        uint256 endTime;
        bool resolved;
        bool outcome;
    }

    mapping(bytes32 => Market) public markets;
    mapping(bytes32 => mapping(address => uint256)) public yesBalances;
    mapping(bytes32 => mapping(address => uint256)) public noBalances;
}

Mint Function

Core rule: 1 USDC = 1 YES + 1 NO, keeping the system fully collateralized.

function mintShares(bytes32 marketId, uint256 amount) external {
    require(!markets[marketId].resolved, "Market already resolved");

    require(collateralToken.transferFrom(
        msg.sender, address(this), amount
    ), "Transfer failed");

    yesBalances[marketId][msg.sender] += amount;
    noBalances[marketId][msg.sender] += amount;

    emit TokensMinted(marketId, msg.sender, amount);
}

Oracle Resolution

The contract cannot observe off-chain reality, so it relies on an oracle to supply the final result.

function resolveMarket(bytes32 marketId) external {
    Market storage market = markets[marketId];
    require(!market.resolved, "Already resolved");
    require(block.timestamp >= market.endTime, "Not yet ended");

    require(oracle.isResolved(marketId), "Oracle not ready");

    bool result = oracle.getOutcome(marketId);

    market.outcome = result;
    market.resolved = true;

    emit MarketResolved(marketId, result);
}

Winnings Redemption

The code guarantees that only winning positions can redeem collateral after settlement.

function redeemWinnings(bytes32 marketId) external {
    Market storage market = markets[marketId];
    require(market.resolved, "Market not resolved yet");

    uint256 payout = 0;

    if (market.outcome == true) {
        payout = yesBalances[marketId][msg.sender];
        yesBalances[marketId][msg.sender] = 0;
    } else {
        payout = noBalances[marketId][msg.sender];
        noBalances[marketId][msg.sender] = 0;
    }

    require(payout > 0, "No winnings to claim");
    require(collateralToken.transfer(msg.sender, payout), "Transfer failed");

    emit WinningsClaimed(marketId, msg.sender, payout);
}

3. UMA Optimistic Oracle Flow

1

Assert

After market expiry, anyone can submit a result, but they must post a bond to discourage malicious claims.

2

Challenge Window

A challenge window follows. If nobody disputes, the result is accepted automatically; if challenged, it moves into dispute resolution.

No challenge: the result is accepted and forwarded to the contract
Challenged: a challenger posts a matching bond
3

DVM Vote

The dispute is escalated to UMA DVM, where validators vote on the correct outcome.

4

Finality

Once the decision is written on-chain, the YNpredict contract reads it and settles automatically.

4. Why Is This Safer Than a Traditional Server?

FeatureTraditional Server (Web2)YNpredict Smart Contract (Web3)
Logic Executionif (user_won) { send_money() }require(market.resolved); transfer(...)
Tamper ResistanceAn operator can override user_won or refuse to execute payoutsOnce deployed, the logic is fixed and market.outcome cannot be changed by the operator
Treasury CustodyFunds are mixed into company-controlled accountsFunds stay locked in the contract and can only be claimed by holders of the correct winning shares