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 USDCAfter 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
Assert
After market expiry, anyone can submit a result, but they must post a bond to discourage malicious claims.
Challenge Window
A challenge window follows. If nobody disputes, the result is accepted automatically; if challenged, it moves into dispute resolution.
DVM Vote
The dispute is escalated to UMA DVM, where validators vote on the correct outcome.
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?
| Feature | Traditional Server (Web2) | YNpredict Smart Contract (Web3) |
|---|---|---|
| Logic Execution | if (user_won) { send_money() } | require(market.resolved); transfer(...) |
| Tamper Resistance | An operator can override user_won or refuse to execute payouts | Once deployed, the logic is fixed and market.outcome cannot be changed by the operator |
| Treasury Custody | Funds are mixed into company-controlled accounts | Funds stay locked in the contract and can only be claimed by holders of the correct winning shares |