4.12 主网上线
一句话总结:主网上线不是终点,而是起点——多签部署+Timelock+监控=安全上线。
📊 学习进度
- 状态:⬜ 未开始
- 预计时长:3-4 小时
- 已完成:0/3 个模块
- 在整体流程中的位置:预测市场实战·第 9 阶段
📍 本章定位
- 服务方案:方案 3(核心 90%)
- 学习方式:🔥 推荐
- 在流程中的作用:部署主网、上线运营
- 核心知识点:合约部署、多签、Timelock、运营策略
- 预计时长:3-4 小时
- 完成后能做什么:能安全完成主网上线
人机分工
| 环节 | 谁做 | 重要度 | 说明 |
|---|---|---|---|
| 部署策略 | 🧑 人 | ⭐⭐⭐⭐⭐ | 决定部署方案 |
| 多签设置 | 🧑 人 | ⭐⭐⭐⭐⭐ | 安全核心 |
| 部署执行 | 🤖 AI | ⭐⭐⭐ | AI 执行脚本 |
| 上线监控 | 🤖 AI | ⭐⭐⭐⭐ | AI 实时监控 |
1. 上线前准备
1.1 上线检查清单
1.2 详细检查清单
| 序号 | 类别 | 检查项 | 状态 | 负责人 |
|---|---|---|---|---|
| 1 | 代码 | 合约审计报告 | ⬜ | 安全团队 |
| 2 | 代码 | 测试覆盖率 > 90% | ⬜ | 开发团队 |
| 3 | 代码 | Slither 扫描无高危 | ⬜ | 安全团队 |
| 4 | 代码 | 代码冻结,无新提交 | ⬜ | 开发团队 |
| 5 | 基础设施 | RPC 节点稳定运行 7 天 | ⬜ | 运维 |
| 6 | 基础设施 | 数据库备份策略 | ⬜ | 运维 |
| 7 | 基础设施 | 监控告警配置 | ⬜ | 运维 |
| 8 | 基础设施 | CDN 和域名配置 | ⬜ | 运维 |
| 9 | 安全 | 多签钱包创建 | ⬜ | 安全团队 |
| 10 | 安全 | Timelock 配置 | ⬜ | 安全团队 |
| 11 | 安全 | 紧急暂停测试 | ⬜ | 安全团队 |
| 12 | 安全 | 密钥安全存储 | ⬜ | 安全团队 |
| 13 | 运营 | 初始做市资金到位 | ⬜ | 运营 |
| 14 | 运营 | 首批事件准备 | ⬜ | 运营 |
| 15 | 运营 | 社区公告文案 | ⬜ | 运营 |
| 16 | 运营 | 客服响应流程 | ⬜ | 运营 |
2. 上线流程
2.1 上线步骤
2.2 部署清单
| 序号 | 任务 | 工具 | 验证 | 时间 |
|---|---|---|---|---|
| 1 | 部署 Factory 合约 | Hardhat | 合约地址确认 | 10 分钟 |
| 2 | 部署预言机合约 | Hardhat | 节点连接测试 | 10 分钟 |
| 3 | 部署做市合约 | Hardhat | 流动性测试 | 10 分钟 |
| 4 | 设置多签钱包 | Safe | 签名者确认 | 30 分钟 |
| 5 | 设置 Timelock | Timelock | 延迟时间确认 | 20 分钟 |
| 6 | 前端配置 | Next.js | 功能测试 | 30 分钟 |
| 7 | 监控配置 | Grafana | 告警测试 | 20 分钟 |
| 8 | 初始做市 | 脚本 | 流动性确认 | 30 分钟 |
| 9 | 功能验证 | 手动 | 全流程测试 | 60 分钟 |
总时间:约 4 小时
3. 安全措施
3.1 多签钱包
多签配置:
typescript
// Gnosis Safe 多签配置
const safeConfig = {
owners: [
'0xFounder...', // 创始人
'0xTechLead...', // 技术负责人
'0xSecurityLead...', // 安全负责人
'0xCommunity...', // 社区代表
'0xBackup...' // 备用
],
threshold: 3, // 需要 3 人签名
chainId: 8453 // Base
};3.2 Timelock 配置
solidity
// Timelock 合约配置
contract TimelockConfig {
uint256 public constant MIN_DELAY = 24 hours; // 最小延迟
uint256 public constant MAX_DELAY = 7 days; // 最大延迟
uint256 public constant DEFAULT_DELAY = 48 hours; // 默认延迟
// 升级流程:
// 1. 提交升级提案(需要多签)
// 2. 等待延迟期(48 小时)
// 3. 执行升级(需要多签)
// 紧急情况:
// 1. 紧急暂停(不需要延迟)
// 2. 紧急升级(需要所有签名者)
}3.3 紧急暂停机制
3.4 安全措施清单
| 措施 | 说明 | 必要性 | 实现方式 |
|---|---|---|---|
| 多签钱包 | 多人签名才能执行 | ⭐⭐⭐⭐⭐ | Gnosis Safe |
| Timelock | 合约升级延迟执行 | ⭐⭐⭐⭐⭐ | OpenZeppelin Timelock |
| 升级代理 | 可升级合约 | ⭐⭐⭐⭐ | TransparentProxy |
| 紧急暂停 | 紧急情况下暂停合约 | ⭐⭐⭐⭐ | Pausable |
| 限额机制 | 单笔交易限额 | ⭐⭐⭐ | 自定义 |
| 黑名单 | 恶意地址限制 | ⭐⭐ | AccessControl |
4. 部署脚本
4.1 主网部署脚本
typescript
// scripts/deploy-mainnet.ts
import { ethers } from "hardhat";
import { LedgerSigner } from "@anders-t/ethers-ledger";
async function main() {
// 使用硬件钱包签名(更安全)
const signer = new LedgerSigner(ethers.provider);
console.log("Deploying with account:", await signer.getAddress());
console.log("Balance:", ethers.formatEther(await ethers.provider.getBalance(await signer.getAddress())));
// 1. 部署 EventPod 实现
console.log("\n1. Deploying EventPod implementation...");
const EventPod = await ethers.getContractFactory("EventPod", signer);
const eventPod = await EventPod.deploy();
await eventPod.waitForDeployment();
console.log("EventPod:", await eventPod.getAddress());
// 2. 部署 EventFactory
console.log("\n2. Deploying EventFactory...");
const EventFactory = await ethers.getContractFactory("EventFactory", signer);
const eventFactory = await EventFactory.deploy(await eventPod.getAddress());
await eventFactory.waitForDeployment();
console.log("EventFactory:", await eventFactory.getAddress());
// 3. 部署 OrderBook 实现
console.log("\n3. Deploying OrderBook implementation...");
const OrderBook = await ethers.getContractFactory("OrderBook", signer);
const orderBook = await OrderBook.deploy();
await orderBook.waitForDeployment();
console.log("OrderBook:", await orderBook.getAddress());
// 4. 部署 OrderBookFactory
console.log("\n4. Deploying OrderBookFactory...");
const OrderBookFactory = await ethers.getContractFactory("OrderBookFactory", signer);
const orderBookFactory = await OrderBookFactory.deploy(await orderBook.getAddress());
await orderBookFactory.waitForDeployment();
console.log("OrderBookFactory:", await orderBookFactory.getAddress());
// 5. 部署 OracleManager
console.log("\n5. Deploying OracleManager...");
const OracleManager = await ethers.getContractFactory("OracleManager", signer);
const oracleManager = await OracleManager.deploy();
await oracleManager.waitForDeployment();
console.log("OracleManager:", await oracleManager.getAddress());
// 6. 部署 Timelock
console.log("\n6. Deploying Timelock...");
const Timelock = await ethers.getContractFactory("TimelockController", signer);
const timelock = await Timelock.deploy(
48 * 60 * 60, // 48 小时延迟
[await signer.getAddress()], // 提案者
[await signer.getAddress()], // 执行者
ethers.ZeroAddress // 管理员
);
await timelock.waitForDeployment();
console.log("Timelock:", await timelock.getAddress());
// 7. 转移所有权到 Timelock
console.log("\n7. Transferring ownership to Timelock...");
await eventFactory.transferOwnership(await timelock.getAddress());
await orderBookFactory.transferOwnership(await timelock.getAddress());
await oracleManager.transferOwnership(await timelock.getAddress());
console.log("Ownership transferred");
// 8. 保存部署信息
const deployment = {
network: "base",
chainId: "8453",
deployer: await signer.getAddress(),
timestamp: new Date().toISOString(),
contracts: {
eventPod: await eventPod.getAddress(),
eventFactory: await eventFactory.getAddress(),
orderBook: await orderBook.getAddress(),
orderBookFactory: await orderBookFactory.getAddress(),
oracleManager: await oracleManager.getAddress(),
timelock: await timelock.getAddress()
}
};
const fs = require('fs');
fs.writeFileSync(
'deployments/base-mainnet.json',
JSON.stringify(deployment, null, 2)
);
console.log("\n✅ Deployment complete!");
console.log("Deployment saved to: deployments/base-mainnet.json");
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});4.2 部署验证脚本
typescript
// scripts/verify-deployment.ts
import { ethers } from "hardhat";
import deployment from '../deployments/base-mainnet.json';
async function main() {
console.log("Verifying deployment...\n");
// 1. 验证合约代码
console.log("1. Verifying contract code...");
const contracts = Object.entries(deployment.contracts);
for (const [name, address] of contracts) {
const code = await ethers.provider.getCode(address);
const status = code !== '0x' ? '✅' : '❌';
console.log(` ${status} ${name}: ${address}`);
}
// 2. 验证权限
console.log("\n2. Verifying permissions...");
const eventFactory = await ethers.getContractAt("EventFactory", deployment.contracts.eventFactory);
const owner = await eventFactory.owner();
console.log(` EventFactory owner: ${owner}`);
console.log(` Expected: ${deployment.contracts.timelock}`);
console.log(` Status: ${owner === deployment.contracts.timelock ? '✅' : '❌'}`);
// 3. 验证功能
console.log("\n3. Verifying functionality...");
try {
// 尝试创建事件(应该失败,因为没有 operator 权限)
await eventFactory.createEvent("Test", "Test", "test", "test", Math.floor(Date.now() / 1000) + 86400);
console.log(" ❌ Should have reverted");
} catch (error) {
console.log(" ✅ Access control working");
}
console.log("\n✅ Verification complete!");
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});5. 上线后运营
5.1 首日运营清单
5.2 首日监控指标
| 指标 | 目标 | 告警阈值 | 说明 |
|---|---|---|---|
| 系统可用性 | 99.9% | <99% | 服务稳定性 |
| API 响应时间 | <500ms | >2s | 性能 |
| 交易成功率 | >99% | <95% | 交易功能 |
| 撮合延迟 | <100ms | >500ms | 撮合引擎 |
| Gas 消耗 | 正常 | 异常高 | 成本控制 |
| 用户投诉 | <10 | >50 | 用户体验 |
5.3 首周运营计划
| 天数 | 重点工作 | 目标 |
|---|---|---|
| D1 | 系统监控+用户反馈 | 系统稳定 |
| D2 | 做市参数优化 | 价差 < 2% |
| D3 | 首批事件结算 | 结算正常 |
| D4 | 用户增长推广 | DAU > 100 |
| D5 | 数据分析 | 优化方向明确 |
| D6 | 功能迭代 | 修复问题 |
| D7 | 周报总结 | 经验沉淀 |
6. 应急预案
6.1 应急响应流程
6.2 应急场景处理
| 场景 | 严重程度 | 响应时间 | 处理方式 |
|---|---|---|---|
| 合约漏洞 | P0 | 立即 | 紧急暂停+修复 |
| 资金异常 | P0 | 立即 | 紧急暂停+调查 |
| 系统宕机 | P1 | 30 分钟 | 切换备份+修复 |
| 预言机异常 | P1 | 1 小时 | 暂停结算+人工介入 |
| 做市异常 | P2 | 2 小时 | 调整参数+观察 |
| 性能下降 | P3 | 24 小时 | 优化+部署 |
6.3 应急联系人
| 角色 | 职责 | 联系方式 |
|---|---|---|
| 技术负责人 | 技术决策 | Telegram/电话 |
| 安全负责人 | 安全评估 | Telegram/电话 |
| 运营负责人 | 用户沟通 | Telegram/电话 |
| 法律顾问 | 合规咨询 | 邮件/电话 |
7. OPC 上线策略
7.1 轻量上线方案
作为 OPC,不需要复杂的上线流程。推荐轻量方案:
| 阶段 | 时间 | 资金 | 目标 |
|---|---|---|---|
| 测试网 | 1 周 | 0 | 功能验证 |
| 主网测试 | 3 天 | $100 | 真实环境验证 |
| 小额运营 | 1 周 | $1,000 | 流程验证 |
| 正式运营 | 持续 | $5,000+ | 正式上线 |
7.2 OPC 上线清单
- [ ] 测试网运行 7 天无问题
- [ ] 主网小额测试成功
- [ ] 监控系统就绪
- [ ] 多签钱包创建
- [ ] 初始做市资金到位
- [ ] 首批事件准备
- [ ] 社区公告准备
- [ ] 应急联系人确认
8. 应急预案详解
8.1 安全事件响应 SOP
当检测到安全事件时,必须按照以下标准流程执行:
应急联系人矩阵:
| 优先级 | 角色 | 响应时间 | 联系方式 | 备份 |
|---|---|---|---|---|
| P0 | 安全负责人 | 5 分钟 | Telegram + 电话 | 技术负责人 |
| P0 | 技术负责人 | 10 分钟 | Telegram + 电话 | 合约开发者 |
| P1 | 运营负责人 | 30 分钟 | Telegram | 社区经理 |
| P1 | 法律顾问 | 2 小时 | 邮件 + 电话 | 合规团队 |
8.2 资金异常处理
| 异常类型 | 检测方式 | 响应动作 | 恢复方案 |
|---|---|---|---|
| 合约余额突降 | 余额监控 | 立即暂停 | 审计+修复 |
| 异常大额提现 | 交易监控 | 暂停+人工审核 | 多签确认 |
| 做市资金耗尽 | 风控监控 | 暂停做市 | 补充资金 |
| Gas 费异常高 | Gas 监控 | 暂停链上操作 | 等待回落 |
资金监控脚本:
typescript
class FundMonitor {
private alertThreshold = 0.1; // 10% 变动告警
async checkBalance(contractAddress: string): Promise<void> {
const currentBalance = await this.getBalance(contractAddress);
const previousBalance = await this.getPreviousBalance(contractAddress);
const changeRate = Math.abs(currentBalance - previousBalance) / previousBalance;
if (changeRate > this.alertThreshold) {
await this.sendAlert({
type: 'balance_anomaly',
severity: changeRate > 0.5 ? 'critical' : 'warning',
message: `合约余额变动 ${(changeRate * 100).toFixed(1)}%`,
currentBalance,
previousBalance,
contractAddress
});
// 严重情况自动暂停
if (changeRate > 0.5) {
await this.emergencyPause(contractAddress);
}
}
}
}8.3 灾难恢复计划
| 灾难场景 | RTO | RPO | 恢复步骤 |
|---|---|---|---|
| 服务器全挂 | 4 小时 | 1 小时 | 切换备用区域 + 数据恢复 |
| 数据库损坏 | 2 小时 | 5 分钟 | 从备份恢复 + 链上重放 |
| 合约漏洞 | 1 小时 | 0 | 暂停 + 代理升级 |
| DNS 劫持 | 30 分钟 | 0 | 切换 DNS + Cloudflare |
| 密钥泄露 | 15 分钟 | 0 | 多签暂停 + 更换密钥 |
9. 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 部署失败 | Gas 不足 | 预留足够 Gas |
| 合约不兼容 | 版本问题 | 使用兼容版本 |
| 多签丢失签名者 | 签名者不足 | 预留备用签名者 |
| 用户投诉 | 体验问题 | 快速响应+修复 |
| 资金异常 | 合约漏洞 | 紧急暂停+修复 |
| 性能问题 | 流量超预期 | 扩容+优化 |
9. 下一步
完成主网上线后,进入 阶段 10:数据驱动
8.4 上线后安全监控
上线后需要持续监控合约安全状态。以下是关键监控指标和响应流程:
合约安全监控指标:
| 监控项 | 检测方式 | 告警阈值 | 响应动作 |
|---|---|---|---|
| 异常大额交易 | 链上事件监听 | 单笔 >$50k | 人工审核 |
| 合约余额变动 | 定时查询 | 变动 >10% | 立即告警 |
| 权限变更 | 事件监听 | 任何变更 | 立即告警 |
| Gas 异常 | 交易监控 | Gas >5x 正常 | 暂停操作 |
| 预言机延迟 | 心跳检测 | >1 小时无更新 | 人工介入 |
安全监控脚本:
typescript
class SecurityMonitor {
private provider: ethers.Provider;
private alertBot: AlertBot;
/**
* 监控合约余额异常
*/
async monitorBalanceAnomaly(): Promise<void> {
const contracts = [
{ name: 'OrderBookFactory', address: '0x...' },
{ name: 'FeeVault', address: '0x...' },
{ name: 'FundingFactory', address: '0x...' }
];
for (const contract of contracts) {
const currentBalance = await this.provider.getBalance(contract.address);
const previousBalance = await this.getPreviousBalance(contract.address);
const changeRate = Number(
(currentBalance - previousBalance) * 100n / previousBalance
);
if (Math.abs(changeRate) > 10) {
await this.alertBot.sendAlert({
severity: 'critical',
title: `Balance Anomaly: ${contract.name}`,
description: `Balance changed ${changeRate.toFixed(1)}%`,
contract: contract.address,
currentBalance: ethers.formatEther(currentBalance),
previousBalance: ethers.formatEther(previousBalance)
});
}
}
}
/**
* 监控权限变更事件
*/
async monitorPermissionChanges(): Promise<void> {
const roleGrantedABI = ['event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)'];
const iface = new ethers.Interface(roleGrantedABI);
// 订阅所有合约的 RoleGranted 事件
this.provider.on({
topics: [iface.getEvent('RoleGranted').topicHash]
}, async (log) => {
const parsed = iface.parseLog(log);
await this.alertBot.sendAlert({
severity: 'warning',
title: 'Permission Change Detected',
description: `Role ${parsed.args.role} granted to ${parsed.args.account}`,
txHash: log.transactionHash
});
});
}
/**
* 监控预言机心跳
*/
async monitorOracleHeartbeat(): Promise<void> {
const lastUpdate = await this.getLastOracleUpdate();
const hoursSinceUpdate = (Date.now() - lastUpdate) / (1000 * 60 * 60);
if (hoursSinceUpdate > 1) {
await this.alertBot.sendAlert({
severity: 'warning',
title: 'Oracle Heartbeat Missing',
description: `No oracle update for ${hoursSinceUpdate.toFixed(1)} hours`
});
}
}
}8.5 合约升级实战指南
当发现合约漏洞或需要新功能时,需要执行合约升级。以下是安全的升级流程:
升级前检查清单:
| 检查项 | 验证方式 | 通过标准 |
|---|---|---|
| 存储布局兼容 | forge inspect storage-layout | 新旧布局一致 |
| 函数选择器无冲突 | forge inspect abi | 无重复选择器 |
| 初始化函数保护 | 代码审查 | initializer 修饰符 |
| 测试覆盖率 | forge coverage | >95% |
| Fork 测试 | --fork-url 测试 | 所有测试通过 |
| 审计报告 | 第三方审计 | 无高危问题 |
升级执行脚本:
typescript
// scripts/upgrade.ts
async function upgradeContract(
proxyAddress: string,
newImplementation: string,
timelockAddress: string
): Promise<void> {
// 1. 提交升级提案
const timelock = await ethers.getContractAt('TimelockController', timelockAddress);
const upgradeCall = ethers.Interface.encodeFunctionData(
'upgradeTo',
[newImplementation]
);
const tx = await timelock.schedule(
proxyAddress, // target
0, // value
upgradeCall, // data
ethers.ZeroHash, // predecessor
ethers.ZeroHash, // salt
48 * 60 * 60 // delay: 48 hours
);
console.log('Upgrade proposal submitted:', tx.hash);
console.log('Waiting 48 hours for timelock...');
// 2. 等待延迟期
await tx.wait();
// 3. 执行升级(48 小时后)
// const executeTx = await timelock.execute(
// proxyAddress, 0, upgradeCall,
// ethers.ZeroHash, ethers.ZeroHash
// );
// console.log('Upgrade executed:', executeTx.hash);
}8.6 上线后常见问题处理
| 问题 | 症状 | 响应时间 | 处理方案 |
|---|---|---|---|
| 合约暂停 | 交易失败 | 立即 | 检查暂停原因+修复 |
| Gas 飙升 | 交易卡住 | 30 分钟 | 暂停链上操作+等待 |
| 预言机延迟 | 结算延迟 | 1 小时 | 人工介入+手动结算 |
| 流动性不足 | 价差过大 | 2 小时 | 补充做市资金 |
| 用户投诉 | 社区反馈 | 4 小时 | 快速响应+修复 |
上线后监控 Prompt(用于 AI 生成监控报告):
text
请根据以下监控数据生成一份上线后首日运营报告:
- 系统可用性:99.95%
- API 响应时间:P95 = 180ms
- 交易成功率:99.8%
- 活跃用户:150
- 交易量:$12,000
- 做市价差:1.8%
- 用户投诉:3 个
请分析数据,指出需要关注的问题,并给出改进建议。上线后运营 SOP:
typescript
// 上线后每日运营 SOP
const dailyOperationsSOP = [
{ time: '09:00', task: '检查系统状态和监控告警', priority: 'P0' },
{ time: '09:30', task: '查看昨日交易数据和用户反馈', priority: 'P0' },
{ time: '10:00', task: '检查做市库存和价差', priority: 'P1' },
{ time: '11:00', task: '处理用户工单和社区反馈', priority: 'P1' },
{ time: '14:00', task: '分析数据趋势,调整策略', priority: 'P2' },
{ time: '17:00', task: '生成每日运营报告', priority: 'P2' },
{ time: '18:00', task: '备份数据和日志', priority: 'P1' },
];8.7 合约验证与开源最佳实践
合约上线后,必须进行验证和开源,以建立用户信任:
typescript
// 合约验证脚本
async function verifyContracts(deployment: DeploymentInfo): Promise<void> {
const contracts = [
{ name: 'EventFactory', address: deployment.contracts.eventFactory },
{ name: 'OrderBookFactory', address: deployment.contracts.orderBookFactory },
{ name: 'OracleManager', address: deployment.contracts.oracleManager },
{ name: 'FeeVault', address: deployment.contracts.feeVault }
];
for (const contract of contracts) {
try {
await hre.run('verify:verify', {
address: contract.address,
constructorArguments: contract.constructorArgs
});
console.log(`✅ ${contract.name} verified`);
} catch (error) {
console.log(`❌ ${contract.name} verification failed:`, error);
}
}
}开源策略:
| 阶段 | 动作 | 原因 |
|---|---|---|
| 测试网 | 私有仓库 | 快速迭代 |
| 审计 | 提交审计 | 第三方验证 |
| 主网 | 开源合约 | 建立信任 |
| 运营 | 开源 SDK | 生态建设 |
开源 Checklist:
- [ ] 合约代码开源到 GitHub
- [ ] 编写详细的 README 文档
- [ ] 添加 LICENSE 文件(MIT 或 Apache 2.0)
- [ ] 提交合约验证到区块浏览器
- [ ] 发布安全审计报告
- [ ] 建立漏洞赏金计划
漏洞赏金计划:
| 漏洞级别 | 赏金 | 说明 |
|---|---|---|
| Critical | $10,000-50,000 | 资金被盗风险 |
| High | $5,000-10,000 | 功能异常 |
| Medium | $1,000-5,000 | 性能问题 |
| Low | $500-1,000 | 代码质量 |
参考与延伸
[11] Sourcify(2025)— 合约验证平台
[12] Code4rena(2025)— Web3 审计竞赛平台
[1] Gnosis Safe(2025)— 多签钱包
[2] OpenZeppelin Timelock(2025)— 时间锁合约
[3] Base Network(2025)— L2 网络
[4] Tenderly(2025)— 合约监控
[5] Blocknative(2025)— 交易监控
[6] Incident Response Guide(2025)— SANS 应急响应手册
[7] Disaster Recovery Planning(2010)— NIST 灾难恢复标准
[8] Ethereum Security(2025)— 以太坊安全最佳实践
[9] Immunefi(2025)— Web3 漏洞赏金平台
[10] Chainalysis(2025)— 链上监控和合规工具