Skip to content

4.7 智能合约

一句话总结:Factory 模式批量生产市场,预言机保证结果可信——这是链上核心。

📊 学习进度

  • 状态:⬜ 未开始
  • 预计时长:6-8 小时
  • 已完成:0/3 个模块
  • 在整体流程中的位置:预测市场实战·第 4 阶段

📍 本章定位

  • 服务方案:方案 3(核心 90%)
  • 学习方式:⭐ 必学
  • 在流程中的作用:实现预测市场的链上逻辑
  • 核心知识点:预言机集成、Factory 模式、Gas 优化
  • 预计时长:6-8 小时
  • 完成后能做什么:能开发完整的预测市场智能合约

人机分工

环节谁做重要度说明
合约架构设计🧑 人⭐⭐⭐⭐⭐设计 Factory 模式
预言机设计🧑 人⭐⭐⭐⭐⭐设计验证机制
代码实现🤖 AI⭐⭐⭐⭐AI 生成代码
代码审核🧑 人⭐⭐⭐⭐⭐必须人审
Gas 优化🤖 AI⭐⭐⭐AI 建议优化

1. Factory 模式架构

1.1 整体架构图

1.2 Factory 产出关系

Factory产出功能关键特性
EventVoteFactoryeventVotePod事件投票投票完成后用户认可才进入下一步
EventFactoryeventPod事件创建管理事件元数据
FundingFactoryfundingPod资金管理管理市场资金池
OrderBookFactoryorderBookPod订单簿收取 0.5% 手续费
FeeVaultFactoryfeeVaultPod手续费收取 0.25% 管理费
AdminFeeVault管理费池平台收入

1.3 Factory 模式的优势

对比维度单一合约Factory 模式
隔离性一个出问题全部停互不影响
扩展性只能做一个可以批量创建
独立性共享资源各自独立
收益结构统一分配各自独立
升级影响全部受影响可独立升级

2. 核心合约实现

2.1 EventVoteFactory 合约

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

/// @title EventVoteFactory - 事件投票工厂
/// @notice 批量创建事件投票 Pod
contract EventVoteFactory is Ownable {
    address public immutable implementation;
    address[] public pods;
    
    event PodCreated(address indexed pod, uint256 indexed podId);
    
    constructor(address _implementation) Ownable(msg.sender) {
        implementation = _implementation;
    }
    
    /// @notice 创建新的事件投票 Pod
    /// @param title 事件标题
    /// @param description 事件描述
    /// @param endTime 投票截止时间
    /// @return pod 新创建的 Pod 地址
    function createPod(
        string calldata title,
        string calldata description,
        uint256 endTime
    ) external onlyOwner returns (address pod) {
        // 使用 Clones 库最小代理模式,节省 Gas
        pod = Clones.clone(implementation);
        
        // 初始化 Pod
        IEventVotePod(pod).initialize(title, description, endTime, msg.sender);
        
        pods.push(pod);
        emit PodCreated(pod, pods.length - 1);
    }
    
    /// @notice 获取所有 Pod 地址
    function getAllPods() external view returns (address[] memory) {
        return pods;
    }
    
    /// @notice 获取 Pod 数量
    function podCount() external view returns (uint256) {
        return pods.length;
    }
}

interface IEventVotePod {
    function initialize(
        string calldata title,
        string calldata description,
        uint256 endTime,
        address creator
    ) external;
}

2.2 EventFactory 合约

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

/// @title EventFactory - 事件工厂
/// @notice 管理事件的创建和元数据
contract EventFactory is AccessControl {
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    
    address public immutable eventImplementation;
    address[] public events;
    
    struct EventInfo {
        string title;
        string description;
        string category;
        string resolutionSource;
        uint256 endTime;
        address creator;
        bool resolved;
        bool outcome;
    }
    
    event EventCreated(uint256 indexed eventId, address indexed eventAddress, string title);
    event EventResolved(uint256 indexed eventId, bool outcome);
    
    constructor(address _eventImplementation) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        eventImplementation = _eventImplementation;
    }
    
    /// @notice 创建新事件
    /// @param title 事件标题
    /// @param description 事件描述
    /// @param category 事件分类
    /// @param resolutionSource 结果验证来源
    /// @param endTime 结算时间
    /// @return eventId 事件 ID
    function createEvent(
        string calldata title,
        string calldata description,
        string calldata category,
        string calldata resolutionSource,
        uint256 endTime
    ) external onlyRole(OPERATOR_ROLE) returns (uint256 eventId) {
        require(endTime > block.timestamp, "End time must be in the future");
        
        eventId = events.length;
        address eventAddr = Clones.clone(eventImplementation);
        
        IEventPod(eventAddr).initialize(
            title, description, category, resolutionSource, endTime, msg.sender
        );
        
        events.push(eventAddr);
        emit EventCreated(eventId, eventAddr, title);
    }
    
    /// @notice 解决事件
    /// @param eventId 事件 ID
    /// @param outcome 结果(true=YES, false=NO)
    function resolveEvent(uint256 eventId, bool outcome) 
        external 
        onlyRole(OPERATOR_ROLE) 
    {
        require(eventId < events.length, "Invalid event ID");
        IEventPod(events[eventId]).resolve(outcome);
        emit EventResolved(eventId, outcome);
    }
}

interface IEventPod {
    function initialize(
        string calldata title,
        string calldata description,
        string calldata category,
        string calldata resolutionSource,
        uint256 endTime,
        address creator
    ) external;
    
    function resolve(bool outcome) external;
}

2.3 OrderBookFactory 合约

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

/// @title OrderBookFactory - 订单簿工厂
/// @notice 管理订单簿的创建和交易撮合
contract OrderBookFactory is AccessControl {
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    
    address public immutable orderBookImplementation;
    address public feeVault;
    uint256 public constant FEE_BPS = 50; // 0.5%
    
    struct OrderBook {
        address eventAddress;
        address tokenYes;
        address tokenNo;
        uint256 totalVolume;
    }
    
    mapping(uint256 => OrderBook) public orderBooks;
    
    event OrderBookCreated(uint256 indexed eventId, address orderBook);
    event Trade(
        uint256 indexed eventId,
        address indexed trader,
        bool isBuy,
        bool isYes,
        uint256 amount,
        uint256 price,
        uint256 fee
    );
    
    constructor(address _orderBookImplementation) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        orderBookImplementation = _orderBookImplementation;
    }
    
    /// @notice 为事件创建订单簿
    /// @param eventId 事件 ID
    /// @param eventAddress 事件合约地址
    function createOrderBook(
        uint256 eventId,
        address eventAddress
    ) external onlyRole(OPERATOR_ROLE) returns (address) {
        require(orderBooks[eventId].eventAddress == address(0), "Already exists");
        
        address ob = Clones.clone(orderBookImplementation);
        IOrderBook(ob).initialize(eventAddress, feeVault, FEE_BPS);
        
        orderBooks[eventId] = OrderBook({
            eventAddress: eventAddress,
            tokenYes: IOrderBook(ob).tokenYes(),
            tokenNo: IOrderBook(ob).tokenNo(),
            totalVolume: 0
        });
        
        emit OrderBookCreated(eventId, ob);
        return ob;
    }
    
    /// @notice 设置手续费接收地址
    function setFeeVault(address _feeVault) external onlyRole(DEFAULT_ADMIN_ROLE) {
        feeVault = _feeVault;
    }
}

interface IOrderBook {
    function initialize(address eventAddr, address feeVault, uint256 feeBps) external;
    function tokenYes() external view returns (address);
    function tokenNo() external view returns (address);
}

3. 预言机与验证系统

3.1 预言机架构

3.2 验证机制

步骤说明关键参数
1. 节点质押所有 oracle-node 先进行质押最低质押 1000 USDC
2. 结果提交多个节点提交结果至少 3 个节点
3. 汇总决策oracle-manager 汇总多数一致即通过
4. 合约验证oracle-services-manager + zk-verifierzk 证明正确性
5. 奖惩成功奖励,失败罚没质押奖励 = 质押 * 5%

3.3 Oracle Manager 合约

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/// @title OracleManager - 预言机管理器
/// @notice 管理预言机节点和结果汇总
contract OracleManager is AccessControl, ReentrancyGuard {
    bytes32 public constant NODE_ROLE = keccak256("NODE_ROLE");
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    
    uint256 public constant MIN_NODES = 3;
    uint256 public constant DISPUTE_PERIOD = 24 hours;
    
    struct Resolution {
        uint256 eventId;
        bool outcome;
        uint256 submitTime;
        uint256 agreeCount;
        uint256 disagreeCount;
        bool finalized;
        mapping(address => bool) hasVoted;
    }
    
    // eventId => Resolution
    mapping(uint256 => Resolution) public resolutions;
    
    event ResultSubmitted(uint256 indexed eventId, address indexed node, bool outcome);
    event ResultFinalized(uint256 indexed eventId, bool outcome);
    event NodeSlashed(address indexed node, uint256 amount);
    
    /// @notice 提交事件结果
    /// @param eventId 事件 ID
    /// @param outcome 预测结果
    function submitResult(uint256 eventId, bool outcome) 
        external 
        onlyRole(NODE_ROLE) 
    {
        Resolution storage res = resolutions[eventId];
        require(!res.hasVoted[msg.sender], "Already voted");
        require(!res.finalized, "Already finalized");
        
        res.hasVoted[msg.sender] = true;
        
        if (res.submitTime == 0) {
            res.eventId = eventId;
            res.outcome = outcome;
            res.submitTime = block.timestamp;
            res.agreeCount = 1;
        } else if (res.outcome == outcome) {
            res.agreeCount++;
        } else {
            res.disagreeCount++;
        }
        
        emit ResultSubmitted(eventId, msg.sender, outcome);
        
        // 自动终结条件:同意数超过最小节点数且无争议
        if (res.agreeCount >= MIN_NODES && res.disagreeCount == 0) {
            _finalize(eventId);
        }
    }
    
    /// @notice 终结结果(争议期结束后)
    /// @param eventId 事件 ID
    function finalizeResult(uint256 eventId) external {
        Resolution storage res = resolutions[eventId];
        require(!res.finalized, "Already finalized");
        require(block.timestamp >= res.submitTime + DISPUTE_PERIOD, "Dispute period not ended");
        
        _finalize(eventId);
    }
    
    function _finalize(uint256 eventId) internal {
        Resolution storage res = resolutions[eventId];
        res.finalized = true;
        
        // 奖励同意多数的节点,罚没少数节点
        if (res.agreeCount > res.disagreeCount) {
            // 多数节点获得奖励
            emit ResultFinalized(eventId, res.outcome);
        } else {
            // 争议过大,需要人工介入
            emit ResultFinalized(eventId, res.outcome);
        }
    }
    
    /// @notice 检查结果是否已终结
    function isFinalized(uint256 eventId) external view returns (bool) {
        return resolutions[eventId].finalized;
    }
    
    /// @notice 获取终结结果
    function getOutcome(uint256 eventId) external view returns (bool) {
        require(resolutions[eventId].finalized, "Not finalized");
        return resolutions[eventId].outcome;
    }
}

3.4 zk-verifier 集成

zk-verifier 的价值

  • 把结果确认机制做成"可证明"的,而不是停留在口头共识层面
  • 防止预言机节点合谋作恶
  • 提供密码学级别的安全保障

4. 合约安全模式

4.1 安全清单

安全措施说明实现方式优先级
重入攻击防护防止递归调用ReentrancyGuard⭐⭐⭐⭐⭐
整数溢出防止数值异常Solidity 0.8+ 内置⭐⭐⭐⭐⭐
访问控制限制关键函数AccessControl⭐⭐⭐⭐⭐
暂停机制紧急情况暂停Pausable⭐⭐⭐⭐
代理升级可升级合约TransparentProxy⭐⭐⭐⭐
多签管理关键操作多签Gnosis Safe⭐⭐⭐⭐⭐

4.2 紧急暂停合约

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

/// @title EmergencyPause - 紧急暂停管理
contract EmergencyPause is Pausable, AccessControl {
    bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
    
    event EmergencyPaused(address indexed by, string reason);
    event EmergencyUnpaused(address indexed by);
    
    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(GUARDIAN_ROLE, msg.sender);
    }
    
    /// @notice 紧急暂停
    /// @param reason 暂停原因
    function emergencyPause(string calldata reason) external onlyRole(GUARDIAN_ROLE) {
        _pause();
        emit EmergencyPaused(msg.sender, reason);
    }
    
    /// @notice 恢复运行
    function emergencyUnpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _unpause();
        emit EmergencyUnpaused(msg.sender);
    }
}

4.3 合约升级模式

solidity
// 使用 TransparentProxy 模式实现可升级
// 1. 部署实现合约(逻辑)
// 2. 部署代理合约(存储)
// 3. 通过代理调用实现合约

// 升级流程:
// 1. 部署新的实现合约
// 2. 通过 Timelock 提交升级提案
// 3. 等待延迟期(24-48 小时)
// 4. 多签确认执行升级

5. 合约开发流程

5.1 完整开发流程

5.2 AI 辅助合约开发

AI 生成合约的 Prompt 示例

text
请帮我生成一个预测市场的订单簿合约,要求:

1. 支持限价单和市价单
2. 使用价格优先、时间优先的撮合逻辑
3. 收取 0.5% 手续费
4. 支持批量结算
5. 使用 ReentrancyGuard 防重入
6. 使用 AccessControl 控制权限

请输出完整的 Solidity 代码,包含详细注释。

AI 审计合约的 Prompt 示例

text
请审计以下 Solidity 合约,检查以下安全问题:

1. 重入攻击
2. 整数溢出
3. 访问控制
4. 前端运行风险
5. 闪电贷攻击
6. 价格操纵

合约代码:
[paste contract code]

请列出所有发现的问题,按严重程度排序,并提供修复建议。

5.3 测试策略

测试类型工具覆盖目标说明
单元测试Hardhat90%+每个函数独立测试
集成测试Hardhat80%+模块间交互测试
模糊测试Foundry关键函数随机输入测试
安全审计Slither全部静态分析
形式化验证Certora核心逻辑数学证明正确性

测试用例示例

typescript
import { expect } from "chai";
import { ethers } from "hardhat";

describe("OrderBook", function () {
  it("should create order and match", async function () {
    const [buyer, seller] = await ethers.getSigners();
    
    // 创建订单簿
    const OrderBook = await ethers.getContractFactory("OrderBook");
    const ob = await OrderBook.deploy();
    
    // 买单
    await ob.connect(buyer).placeOrder(true, 6000, 100); // YES @ $0.60
    // 卖单
    await ob.connect(seller).placeOrder(false, 6000, 100); // NO @ $0.60
    
    // 验证撮合
    const trades = await ob.getTrades();
    expect(trades.length).to.equal(1);
    expect(trades[0].price).to.equal(6000);
  });
});

6. Gas 优化详解

6.1 存储优化

优化策略说明节省
使用 mapping 替代 array减少存储槽30-50%
打包变量多个小变量放一个 slot20-30%
使用 immutable构造函数赋值读取免费
使用 calldata不修改的参数读取便宜
solidity
// 优化前:使用 array
address[] public users; // 每次访问需要计算偏移

// 优化后:使用 mapping
mapping(uint256 => address) public users; // 直接访问

// 变量打包示例
// 优化前:3 个 slot
uint256 a; // slot 0
uint256 b; // slot 1
bool c;    // slot 2

// 优化后:2 个 slot
uint256 a;   // slot 0
uint128 b;   // slot 1 (前半)
bool c;      // slot 1 (后半)
uint128 d;   // slot 1 (后半)

6.2 批处理优化

solidity
// 单笔结算:每笔一个交易
function settle(uint256 eventId, address user, uint256 amount) external {
    _settle(eventId, user, amount); // ~50,000 Gas
}

// 批量结算:多笔一个交易
function batchSettle(
    uint256 eventId,
    address[] calldata users,
    uint256[] calldata amounts
) external {
    for (uint256 i = 0; i < users.length; i++) {
        _settle(eventId, users[i], amounts[i]);
    }
    // 100 笔 ~2,000,000 Gas,平均每笔 20,000 Gas(节省 60%)
}

7. 合约安全最佳实践

7.1 常见攻击向量与防御

预测市场合约面临的独特安全挑战:

攻击向量攻击方式防御措施严重程度
重入攻击回调中重复提取资金ReentrancyGuard + CEI 模式P0
前端运行监听 mempool 抢先交易提交-揭示方案 + 私有交易P0
闪电贷借贷操纵价格TWAP + 区块延迟结算P1
预言机操纵贿赂节点提交假结果多节点 + zk 验证 + 质押罚没P0
整数溢出数值计算异常Solidity 0.8+ + SafeMathP1
权限提升未授权操作AccessControl + 多签P0

CEI(Checks-Effects-Interactions)模式

solidity
// 安全的结算函数
function settle(uint256 eventId, address user) external {
    // Checks: 验证条件
    require(isResolved[eventId], "Not resolved");
    require(balances[eventId][user] > 0, "No balance");
    require(!settled[eventId][user], "Already settled");
    
    // Effects: 更新状态(在外部调用之前)
    uint256 payout = calculatePayout(eventId, user);
    settled[eventId][user] = true;
    balances[eventId][user] = 0;
    
    // Interactions: 外部调用(最后执行)
    (bool success, ) = user.call{value: payout}("");
    require(success, "Transfer failed");
}

7.2 合约升级安全

使用 TransparentProxy 模式时的安全清单:

solidity
// 升级安全检查
contract UpgradeSafety {
    // 1. 存储布局兼容性
    // 新版本不能改变已有变量的顺序和类型
    
    // 2. 初始化函数保护
    bool private _initialized;
    
    function initialize() external {
        require(!_initialized, "Already initialized");
        _initialized = true;
        // 初始化逻辑
    }
    
    // 3. 升级权限控制
    // 只有 Timelock 才能升级
    function upgrade(address newImplementation) external onlyTimelock {
        _authorizeUpgrade(newImplementation);
    }
}

升级前检查清单

检查项说明工具
存储布局新旧版本变量顺序一致OpenZeppelin Upgrades Plugin
初始化防止重复初始化initializer 修饰符
函数选择器无冲突forge inspect
测试覆盖升级路径测试Hardhat + Fork 测试
审计报告第三方审计Slither + 人工审核

7.3 Gas 优化进阶

solidity
// 优化 1: 使用 calldata 替代 memory
function createEvent(
    string calldata title,      // 比 memory 便宜
    string calldata description,
    uint256 endTime
) external { /* ... */ }

// 优化 2: 批量操作减少固定开销
function batchCreateEvents(
    string[] calldata titles,
    uint256[] calldata endTimes
) external {
    uint256 length = titles.length;
    for (uint256 i = 0; i < length; ) {
        _createEvent(titles[i], endTimes[i]);
        unchecked { ++i; } // 节省 Gas
    }
}

// 优化 3: 位运算替代数学运算
// 优化前: price / 2
// 优化后: price >> 1

// 优化 4: 短路求值
require(
    block.timestamp < endTime && isOperator(msg.sender),
    "Invalid"
);

8. 常见问题

问题原因解决方案
Gas 过高合约逻辑复杂优化存储、批处理
预言机延迟节点网络问题多节点+超时机制
合约漏洞AI 生成的代码不安全必须人工审核
升级困难没有代理模式使用 TransparentProxy
权限混乱角色定义不清使用 AccessControl
重入攻击未防护使用 ReentrancyGuard

8. 下一步

完成智能合约后,进入 阶段 5:撮合引擎

7.4 FeeVault 合约实现

FeeVault 是预测市场经济模型的核心,负责手续费的收取和分配:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title FeeVault - 手续费金库
/// @notice 管理交易手续费的收取和分配
contract FeeVault is AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;
    
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    
    IERC20 public immutable token;
    uint256 public totalFees;
    
    // 分配比例(基点,10000 = 100%)
    uint256 public platformShare = 7000;   // 70% 平台
    uint256 public makerShare = 3000;      // 30% 做市商
    
    address public platformWallet;
    mapping(address => uint256) public makerFees;
    
    event FeeCollected(address indexed orderBook, uint256 amount);
    event FeeDistributed(address indexed maker, uint256 amount);
    event SharesUpdated(uint256 platformShare, uint256 makerShare);
    
    constructor(address _token, address _platformWallet) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        token = IERC20(_token);
        platformWallet = _platformWallet;
    }
    
    /// @notice 收取手续费
    /// @param orderBook 订单簿合约地址
    /// @param maker 做市商地址
    /// @param fee 手续费金额
    function collectFee(
        address orderBook,
        address maker,
        uint256 fee
    ) external onlyRole(OPERATOR_ROLE) {
        // 从订单簿转入手续费
        IERC20(token).safeTransferFrom(orderBook, address(this), fee);
        
        // 记录做市商手续费
        uint256 makerFee = (fee * makerShare) / 10000;
        makerFees[maker] += makerFee;
        
        totalFees += fee;
        emit FeeCollected(orderBook, fee);
    }
    
    /// @notice 做市商提取手续费
    function withdrawMakerFees() external nonReentrant {
        uint256 amount = makerFees[msg.sender];
        require(amount > 0, "No fees to withdraw");
        
        makerFees[msg.sender] = 0;
        token.safeTransfer(msg.sender, amount);
        
        emit FeeDistributed(msg.sender, amount);
    }
    
    /// @notice 平台提取手续费
    function withdrawPlatformFees() external nonReentrant {
        uint256 platformFees = (totalFees * platformShare) / 10000;
        require(platformFees > 0, "No fees to withdraw");
        
        totalFees = 0;
        token.safeTransfer(platformWallet, platformFees);
    }
    
    /// @notice 更新分配比例
    function updateShares(uint256 _platformShare, uint256 _makerShare) 
        external 
        onlyRole(DEFAULT_ADMIN_ROLE) 
    {
        require(_platformShare + _makerShare == 10000, "Invalid shares");
        platformShare = _platformShare;
        makerShare = _makerShare;
        emit SharesUpdated(_platformShare, _makerShare);
    }
}

FeeVault 经济模型分析

交易金额手续费(0.5%)平台收入(70%)做市商收入(30%)
$100$0.50$0.35$0.15
$1,000$5.00$3.50$1.50
$10,000$50.00$35.00$15.00
$100,000$500.00$350.00$150.00

7.5 FundingFactory 合约实现

FundingFactory 管理市场资金池,负责资金的存入、提取和结算分配:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title FundingFactory - 资金池工厂
/// @notice 管理预测市场资金池
contract FundingFactory is AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;
    
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    
    IERC20 public immutable token;
    
    struct FundingPool {
        address eventAddress;
        uint256 totalDeposited;
        uint256 totalSettled;
        bool settled;
        bool outcome; // true = YES, false = NO
    }
    
    mapping(uint256 => FundingPool) public pools;
    mapping(uint256 => mapping(address => uint256)) public deposits;
    mapping(uint256 => mapping(address => bool)) public claimed;
    
    event Deposited(uint256 indexed eventId, address indexed user, uint256 amount);
    event Settled(uint256 indexed eventId, bool outcome);
    event Claimed(uint256 indexed eventId, address indexed user, uint256 amount);
    
    constructor(address _token) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        token = IERC20(_token);
    }
    
    /// @notice 用户存入资金
    function deposit(uint256 eventId, uint256 amount) external {
        require(!pools[eventId].settled, "Already settled");
        
        token.safeTransferFrom(msg.sender, address(this), amount);
        deposits[eventId][msg.sender] += amount;
        pools[eventId].totalDeposited += amount;
        
        emit Deposited(eventId, msg.sender, amount);
    }
    
    /// @notice 结算资金池
    function settle(uint256 eventId, bool outcome) 
        external 
        onlyRole(OPERATOR_ROLE) 
    {
        require(!pools[eventId].settled, "Already settled");
        
        pools[eventId].settled = true;
        pools[eventId].outcome = outcome;
        
        emit Settled(eventId, outcome);
    }
    
    /// @notice 用户提取收益
    function claim(uint256 eventId) external nonReentrant {
        require(pools[eventId].settled, "Not settled");
        require(!claimed[eventId][msg.sender], "Already claimed");
        
        uint256 deposit = deposits[eventId][msg.sender];
        require(deposit > 0, "No deposit");
        
        // 计算收益:赢家通吃
        uint256 payout = calculatePayout(eventId, msg.sender, deposit);
        
        claimed[eventId][msg.sender] = true;
        pools[eventId].totalSettled += payout;
        
        token.safeTransfer(msg.sender, payout);
        
        emit Claimed(eventId, msg.sender, payout);
    }
    
    function calculatePayout(
        uint256 eventId, 
        address user, 
        uint256 deposit
    ) internal view returns (uint256) {
        FundingPool storage pool = pools[eventId];
        
        // 简化的收益计算:赢家按比例分配总池
        // 实际实现需要根据 YES/NO 持仓比例计算
        return deposit * 2; // 简化:假设赢家获得双倍
    }
}

7.6 合约安全审计 Checklist

在部署主网前,必须完成以下安全审计清单:

检查项检查方法通过标准工具
重入攻击Slither + 人工审查无 reentrancy 调用Slither
整数溢出编译器检查 + 测试Solidity 0.8+ 内置Hardhat
访问控制角色矩阵审查所有敏感函数有权限控制人工
前端运行交易模拟使用私有交易池Flashbots
闪电贷攻击模拟TWAP + 区块延迟Foundry fork
预言机操纵多节点验证至少 3 个独立节点自定义
存储布局forge inspect升级兼容OpenZeppelin
Gas 优化Gas 报告每笔交易 <$0.10Hardhat Gas Reporter

AI 辅助审计 Prompt

text
请作为智能合约安全审计专家,审计以下合约代码。重点关注:
1. 资金安全:是否有资金被盗风险
2. 权限控制:是否有未授权操作风险
3. 经济攻击:闪电贷、三明治攻击等
4. 预言机风险:价格操纵、延迟等
5. 升级安全:存储布局、初始化等

请按严重程度(Critical/High/Medium/Low/Informational)分类列出所有问题。

7.7 合约事件设计最佳实践

智能合约事件是链上数据监听的基础。良好的事件设计能简化数据管道的实现:

solidity
// 良好的事件设计
contract OrderBook {
    // 事件包含足够信息,减少外部查询
    event OrderPlaced(
        uint256 indexed eventId,
        address indexed trader,
        bool isBuy,
        bool isYes,
        uint256 amount,
        uint256 price,
        uint256 timestamp
    );
    
    // 使用 indexed 参数优化日志过滤
    event TradeExecuted(
        uint256 indexed eventId,
        address indexed buyer,
        address indexed seller,
        uint256 amount,
        uint256 price,
        uint256 fee
    );
    
    // 结算事件包含最终结果
    event MarketSettled(
        uint256 indexed eventId,
        bool outcome,
        uint256 totalPayout,
        uint256 timestamp
    );
}

事件设计原则

原则说明示例
使用 indexed关键字段索引化eventId, trader
包含完整信息减少外部查询price, amount, fee
命名清晰动词+名词格式OrderPlaced, TradeExecuted
时间戳链上时间记录block.timestamp
索引数量最多 3 个 indexedeventId, buyer, seller

Gas 优化提示:indexed 参数比非 indexed 更便宜,但日志数据不能直接在链上访问。


参考与延伸

[11] Solidity Events Best Practices(2025)— Solidity 事件最佳实践

[12] Ethereum Yellow Paper(2014)— 以太坊黄皮书,理解 EVM 和日志机制

[1] OpenZeppelin Contracts(2025)— 安全合约库

[2] Hardhat Documentation(2025)— 合约开发框架

[3] Solidity Documentation(2025)— Solidity 官方文档

[4] Slither(2025)— 合约安全分析工具

[5] UMA(2025)— 乐观预言机协议

[6] Consensys Smart Contract Best Practices(2025)— 合约安全最佳实践

[7] Trail of Bits Security Reviews(2025)— 安全审计报告

[8] EIP-1967: Proxy Storage Slots(2019)— 代理合约标准

[9] OpenZeppelin Upgrades(2025)— 合约升级工具

[10] Aztec Protocol(2025)— zk 验证技术参考

OPC 超级个体实战指南