Skip to content
On this page

L3-6: Prediction Market(预测市场)

1. 问题

预测市场允许参与者对未来事件(如"2024 年大选谁会赢")进行交易,价格反映市场对事件发生概率的集体判断。链上实现面临三个核心挑战:(1) 如何创建代表"是"和"否"结果的金融工具;(2) 如何确保市场创建者和参与者无法操纵最终结果;(3) 获胜方的奖金分配如何确保公平。

与 Polymarket 等大型预测市场不同,这个挑战构建的是简化版本——固定价格购买 YES/NO 份额对,结果由任何人(而非去中心化预言机)提交。这引入了信任假设,但保留了预测市场核心的二元结果 + 份额赎回机制。

2. 原因

Polymarket 的成功(月交易量数十亿美元)验证了预测市场在 Web3 的产品-市场契合度。区块链提供无需许可的市场创建、透明的定价和无需信任的结算。在传统预测市场中,法律和地域限制是主要障碍——Polymarket 通过加密货币规避了部分限制。

对于开发者而言,预测市场是理解"条件代币"(conditional tokens)概念的优秀切入点。条件代币框架(CTF)将预测市场中的每个可能结果封装为可交易的 ERC-1155 代币。这个简化的实现聚焦于最核心的二元市场机制:购买份额、结算市场、赎回奖金。

3. 方案

PredictionMarket.sol 实现了基于 ETH 的二元预测市场。

创建市场createMarket() 接受问题描述和持续时间。市场结构包含:questionendTimeresolved(是否已结算)、isYes(结果)、totalYesShares/totalNoShares(总份额量)。

购买份额buyShares() 允许用户同时购买 YES 和 NO 份额。定价模型简化:每个份额对(1 YES + 1 NO)= SHARE_PRICE = 0.01 ETH。用户支付 (yesAmount + noAmount) * SHARE_PRICE ETH,多付的部分退还。这本质上是一个"购买整个市场敞口"的模型——用户持有两个方向的份额,可以在市场上卖出自己不想要的方向。

结算市场resolveMarket() 由任何人调用(在测试/教育环境中)。设置 isYes 标志位和 resolved = true。在生产环境中,结算应由去中心化预言机(如 UMA's Optimistic Oracle 或 Chainlink Functions)执行。

赎回奖金redeem() 允许持有获胜份额的用户按比例提取奖金。总奖池 = 所有份额的总购买成本 (totalYes + totalNo) * SHARE_PRICE。如果 YES 获胜,持有 YES 份额的用户获得:(myYesShares * totalPool) / totalYesShares

参考源文件:src/level3/PredictionMarket.sol

4. 遭遇的陷阱

  • 结算者可信度:任何人都可以调用 resolveMarket(),在缺乏权限控制的情况下,恶意行为者可以提交错误的结果
  • 份额对定价的流动性问题:当前模型强制用户同时购买 YES 和 NO 份额,无法单独购买某一个方向,限制了价格发现
  • 赎回后的份额状态redeem() 在发放奖金后不销毁份额,也不减少 totalYesShares/totalNoShares,可能导致后续赎回计算的混乱
  • 奖金池与合约余额脱节:奖池计算基于 totalShares * SHARE_PRICE,但合约实际余额可能因其他错误或直接转账而不一致
  • 手续费缺失:市场创建者没有获得任何经济激励(如交易手续费),缺乏可持续性

5. 陷阱的原因

结算可信度是去中心化预测市场的核心问题——"预言机问题"。在 PredictionMarket.sol 中,resolveMarket() 没有权限控制,任何人都可以调用。这是因为在教育设计中省略了权限管理(测试驱动开发中,任何人都应该能够结算以方便测试)。但在生产中,这会导致严重的操纵风险。

更深层的问题在于:很多事件的真实结果是主观的或需要链下验证的。例如"比特币在 2025 年涨停到 $200K"——何时算作涨停?依据什么价格数据源?这些都需要预言机来桥接。UMA 的 Optimistic Oracle 通过经济激励(挑战机制)来解决这个问题——结算者需要质押代币,如果结果被成功挑战,质押将被罚没。

份额对定价模型也值得讨论。当前强制同时购买 YES+NO 的模型被称为"二元 CLOB 的简化版"。在 Polymarket 的实际实现中,使用的是 CLOB(中央限价订单簿)+ CTF(条件代币框架),允许用户单独买卖 YES 或 NO 份额。价格通过订单簿的买卖盘口自然发现。YES 份额的价格在 0 到 1 美元之间浮动,反映市场对事件发生概率的判断。

6. 如何解决陷阱

预言机集成:在生产环境中,使用 UMA Optimistic Oracle 或 Chainlink Functions 来结算市场:

solidity
function resolveMarket(uint256 _marketId) external {
    // 从 UMA 预言机获取结果
    bool outcome = umaOracle.getOutcome(market.question);
    // 或使用 Chainlink Functions 请求链下数据
    // ... 后续逻辑
}

单独份额购买:修改 buyShares() 为独立的 buyYes()buyNo()

solidity
function buyYes(uint256 _marketId, uint256 _amount) external payable {
    uint256 cost = _amount * currentYesPrice; // 需要 AMM 定价模型
    positions[_marketId][msg.sender].yesShares += _amount;
    market.totalYesShares += _amount;
}

价格的发现可以通过 AMM 的 x*y=k 曲线,或 CLOB 的订单簿匹配。

经济激励:为市场创建者添加手续费分成:

solidity
uint256 public constant CREATOR_FEE_BPS = 100; // 1%
// 在 buyShares 中扣除手续费并转给 marketCreator

余额一致性:在 redeem() 中,从合约的实际余额扣除,而非使用公式计算的 totalPool

solidity
uint256 payout = (winningShares * address(this).balance) / totalWinningShares;

7. 技术要点

技术点说明
二元市场YES/NO 两个互斥结果
份额对定价1 YES + 1 NO = 0.01 ETH(简化模型)
赎回算法按比例分配:(myShares * totalPool) / winningShares
状态机创建 → 交易中 → 结算 → 赎回
防重入buySharesredeem 使用 nonReentrant
多付退款超额支付自动退还,改善 UX
预言机依赖结算结果需要外部数据输入

Built with AiAda