
介绍
去中心化永续合约协议通过利用共享流动性池和基于预言机的定价,在链上复制高杠杆的衍生品交易。与 AMM 现货交易不同,永续系统涉及复杂的保证金账户、动态 PnL 计算和清算机制。无论是四舍五入错误还是预言机延迟,微小的逻辑偏差都可能导致协议层面的偿付能力不足或用户投资组合的彻底清算。
本手册旨在解构核心系统架构,分析风险场景,并为智能合约安全审计员和区块链安全研究人员提供实用的审计清单。
核心技术架构
去中心化永续合约的核心在于将传统 CEX 的高性能撮合引擎逻辑转换为基于异步执行和基于流动性池的交易对手动态的链上模型。该架构不仅必须管理资产状态,还必须处理复杂的订单状态和预言机延迟。
核心功能模块
1. 订单管理
负责捕获用户的交易意图,参数化请求,存储它们,并等待稍后执行。
- 增加订单:包括开仓或增加抵押品。
- 减少订单:包括部分/全部平仓或提取抵押品。
- 市价:在允许的滑点范围内立即执行;通常使用两步执行模型来减轻预言机抢跑。
- 限价:仅在预言机价格达到指定阈值时执行
- 止盈/止损:特殊的减少类型的限价订单,当价格被突破时完全或部分平仓。
- 兑换订单:转换抵押资产(例如,USDT → BTC 抵押品),通常是开仓前的先决步骤
...
2. 仓位管理
这是系统的核心账本,记录每个用户的仓位状态。它包括 size (以美元计价的仓位价值), collateral (抵押品数量), averagePrice (平均入场价格), entryFundingIndex (入场时的资金费率指数) 等。
仓位的 PnL 计算:实时计算未实现的盈亏。
- 多仓 PnL = size * (currentPrice — averagePrice) / averagePrice
- 空仓 PnL = size * (averagePrice — currentPrice) / averagePrice
当减少或关闭仓位时,需要计算与该部分仓位对应的已实现盈亏,并将其结算到仓位抵押品数据中:
Position.collateral += PnL (仓位产生的盈亏) + FundingFee (仓位产生的资金费率) — OrderExecuteFee (减少仓位时收取的费用)
3. 交易执行和撮合
由于链上计算成本高昂以及对预言机喂价的依赖,DEX 通常不使用订单簿 (CLOB) 模型,而是采用 "Keeper 触发 + 预言机定价" 的执行机制。
- 请求队列:用户提交订单后,交易不会立即执行,而是进入队列(或存储在待处理状态)。
- Keepers (负责执行订单的角色): 链下机器人监控队列,当条件满足时(例如,时间锁到期或价格触发),它们会发送交易并调用合约以执行订单并执行匹配。
4. 流动性池
在去中心化永续合约协议中,流动性池不仅是用户提供流动性的容器,也是市场上所有交易者的全局交易对手。
4.1 虚拟流动性和资产支持:
- LP 铸造/销毁:流动性提供者存入资产(如 USDC、ETH 等)以铸造 LP Token。LP Token 价格(汇率)由以下公式确定:P_LP = (TotalAssets + UnrealizedPnL) / TotalSupply
- 零和博弈:交易者的损失变成 LP 的利润,而交易者的利润从 LP 池中支付。
- 风险隔离:稳定币池和波动资产池通常在逻辑上分离,或者通过加权来设置敞口上限,以防止单个资产的极端价格波动导致流动性风险。
4.2 动态费用 / 再平衡:
- 为了保持池中资产组成的平衡,系统根据当前资产权重与目标权重之间的偏差来调整交易费用。
- 机制:如果 ETH 权重过高,交易 ETH → USDC 将产生高额费用(惩罚)。相反,如果 ETH 权重较低,交易可能享受较低甚至零费用作为激励。
4.3 全局债务跟踪:
- 系统不直接持有空头仓位的抵押品 (例如 USDC),而是记录 "全局空头仓位金额"。
- 担保美元:这是一个关键的会计变量,代表多头仓位锁定的资产价值 + 空头仓位的抵押品价值。它确保流动性池保持偿付能力。
5. 预言机模块
预言机模块负责提供抗操纵定价,并作为系统的风险控制核心。
5.1 价格聚合:
- 双源验证:通常将链上预言机价格(参考价格),如 Chainlink/Pyth,与 Keeper 提交的 CEX 价格(快速价格)相结合。
- 价差保护:当 |FastPrice − ChainlinkPrice| > SpreadThreshold 时,系统强制回退到 Chainlink 定价或暂停交易,以防止恶意 Keeper 提交价格。
5.2 最高/最低价格逻辑:
为了防止短期预言机波动导致针对协议的套利,系统维护两个价格变量:MaxPrice 和 MinPrice。
- 多头:开仓/增加仓位使用 MaxPrice(更高成本的入场),平仓/减少仓位使用 MinPrice(更低回报的退出)。
- 空头:开仓/增加仓位使用 MinPrice,平仓/减少仓位使用 MaxPrice。
5.3 置信区间和陈旧性检查:
- 预言机必须检查 updatedAt 时间戳,并拒绝任何超过心跳间隔的陈旧价格(最好不超过 30 秒)。
6. 资金费率和借款费率
由于链上每秒向所有用户收费是不可行的,因此系统采用累积指数算法进行延迟结算,这也激励用户以有助于维持流动性平衡的方式进行交易。
6.1 借款费率:
- 定义:为占用池中的流动性而支付的利息。
- 公式:BorrowRate = UtilizationRate * RateFactor。
- 累积逻辑:系统维护一个全局变量 cumulativeBorrowFeePerToken。每当有人对仓位进行操作时,系统计算 deltaTime * borrowRate 并将其添加到全局变量中。
6.2 资金费率:
- 定义:一种平衡多头和空头力量的费用。当多头超过空头时,多头向空头(或协议)支付费用,反之亦然。
- 公式:FundingRate = (Longs — Shorts) / PoolSize * Factor。
6.3 用户结算流程:
- 入场记录:当用户开仓时,记录 entryFundingIndex = globalCumulativeIndex。
- 持有期间:globalCumulativeIndex 随时间增加。
- 出场结算:当用户平仓或调整仓位时,应付费用 = PositionSize * (currentGlobalIndex — entryFundingIndex)。
- 费用扣除顺序:费用直接从用户的抵押品中扣除。如果抵押品不足以支付费用,则会触发清算。
关键机制的深入分析
A. 订单创建和触发机制
两步执行:
提交:用户发送交易,扣除抵押品,并记录 Request 密钥。在此阶段,执行价格尚未最终确定 —— 仅锁定预期操作。
执行:Keepers 获取最新的预言机价格并调用执行函数。系统比较 minOut 或 triggerPrice;如果满足条件,则将操作应用于用户的仓位,否则取消订单。
通过 Keeper 执行可防止用户在同一区块内进行抢跑 —— 例如,看到预言机价格更新,以低价开仓并立即以高价平仓以进行套利(但是,如果 Keepers 恶意行事或受到损害,则仍然存在风险)。
B. 止盈 / 止损 (TP/SL) 逻辑
TP/SL 本质上是 "有条件的减少仓位订单"。
逻辑:用户设置 triggerPrice 和 triggerAboveThreshold。
- 多头 TP:triggerPrice > averagePrice, triggerAboveThreshold = true。
- 多头 SL:triggerPrice < averagePrice, triggerAboveThreshold = false。
执行:Keepers 轮询链上订单,一旦预言机价格满足条件(例如,markPrice >= triggerPrice),则触发减少仓位逻辑。
C. 清算
当用户的保证金率低于维持保证金要求时触发。
触发条件:
(Collateral − Losses) / PositionSize < MaintenanceMarginRatio
流程:
1. 清算人(帮助执行用户清算的实体)调用清算函数,例如 liquidatePosition。
2. 合约使用标记价格检查用户的当前保证金率。
3. 如果验证通过,则强制平仓。
4. 剩余的抵押品被清算费用扣除(支付给清算人),剩余余额转移到流动性池(作为 LP 的利润)。
D. 自动减仓 (ADL)
在极端市场条件下,如果流动性池资不抵债(即,用户损失超过其抵押品且池无法覆盖),系统可能会触发 ADL。
机制:基于按盈利百分比和杠杆水平的排名,系统强制平仓最高利润的相反仓位,释放流动性以弥补损失。
交互流程
以下示例演示了使用 "市价多头" 订单从订单创建到执行的完整交互流程:
1. 用户发起订单创建:
- 调用 Router.createIncreasePosition(…)。
- 传入订单创建所需的参数,例如 amountIn (抵押品数量), sizeDelta (杠杆后的仓位大小), acceptablePrice (反映用户愿意接受的最大滑点的触发价格) 等。
- 结果:抵押品代币转账到指定的合约(例如,Vault 或 OrderBook)。基于用户参数生成订单数据并存储,映射到相应的 OrderKey 或 OrderId。发出 CreateIncreasePosition 事件。
2. Keeper 监听并执行订单:
- 链下脚本检测到 CreateIncreasePosition 事件。
- 等待预言机价格更新(或他们自己提交价格数据以更新价格)。
- 执行交易以进行交易撮合(例如,调用 PositionManager.executeIncreasePosition)。
3. 合约验证:
- 价格检查:获取当前的标记价格并验证 Mark Price <= acceptablePrice(以防止超出用户预期的过度滑点)。
- 杠杆检查:计算新状态下的杠杆,以确保其不超过 MaxLeverage。
- 流动性池检查:确保总多头仓位和可用流动性不超过其限制。
4. 结算和状态更新:
- 更新用户的仓位数据,例如,增加 position.size 和 position.collateral,并记录仓位方向。
- 扣除订单执行费用,例如开仓费用、借款费用等。
- 更新流动性池余额和储备金(用于 AUM 计算)。
- 结果:交易成功,Keeper 收到执行费用。如果失败,则取消订单并将资金返还给用户。
审计清单
预言机 & 定价审计
- 价格时效性验证: 检查是否使用了 latestRoundData 而不是过时的 latestAnswer。必须验证 updatedAt 时间戳,或者应检查预言机价格心跳阈值,以防止使用陈旧价格执行。
- 极端价格选择验证: 系统应选择相对于用户仓位方向的 "最保守" 价格(例如,当用户做多时,系统应使用较低的标记价格来确定清算)。如果此逻辑颠倒,用户可能会利用价格差异进行无风险套利。
- 多源验证: 确认标记价格计算是否包含 CEX 和 AMM 价格比较,例如,当 abs(cexPrice — ammPrice) > threshold 时启用保护模式。确保预言机使用多个可信来源(例如 Chainlink 和 Pyth)以避免依赖于可能失败或被操纵的单一价格来源。
- 精度处理: 检查所有涉及代币小数位(例如,USDC:6 个小数位,ETH:18 个小数位)和 USD 精度(通常为 30 个小数位)的乘法/除法运算,以防止潜在的精度损失。
- 动态小数位检索和转换: 验证预言机小数位是否被动态检索和转换而不是硬编码(例如,小数位 -8)。对于 Pyth 预言机,还要动态检索置信度参数 (conf) 并验证它。
- Pyth 指数处理: 对于 Pyth 预言机,确保正确处理负指数(例如,price * 10^abs(exp))。
- L2 排序器适配: 如果部署在 L2 上,检查系统是否监控特定链的排序器运行时间以防止停机风险,并验证排序器地址的正确性。
- TWAP 和现货价格检查: 建议清算逻辑使用 TWAP 或中位数价格,以防止闪电贷攻击操纵现货价格以触发恶意清算。
- 抵押品价格检索验证: 在去中心化永续合约中,可能允许多种稳定币作为抵押品,通常假设每种稳定币价值 1 美元。交易资产价格也可能以美元计价。在极端市场条件下,稳定币可能会脱锚,导致协议被利用进行恶意清算或套利。因此,在以美元计价的假设下,验证系统是否正确地从预言机检索抵押品价格并将其转换为交易资产价格。
订单管理审计
- 执行费用覆盖检查: 验证用户发送的 msg.value 是否足以支付 Keeper 的 gas 费。特别注意估算在 L2 上操作时的 L1 数据成本。
- ETH/原生代币退款逻辑: 如果订单执行失败或被取消,确保绑定的 ETH 可以全额退还给用户,防止资金被困在合约中。
- 订单更新时立即触发检查: 当用户设置止盈/止损 (TP/SL) 价格时,验证订单价格和方向。例如,当为多头仓位设置止盈时,确保 TP 价格高于当前市场价格和原始订单触发价格;当设置止损时,确保 SL 价格低于当前市场价格和原始触发价格(空头仓位则相反)。否则,订单可能会在创建后立即执行。
- 订单存在性验证: 在执行或取消订单之前,检查订单是否存在以防止重复执行或空指针操作。
- 订单所有权验证: 在修改或取消订单时,严格验证 msg.sender 是否为订单所有者,以防止恶意取消他人的订单。
- 最小订单大小限制: 设置 minOrderSize 以防止攻击者通过大量微型(灰尘)订单发起 DoS 攻击或耗尽 Keeper 资源。
- 订单参数边界检查: 创建订单时,验证滑点是否合理且 triggerPrice 是否为非零值,防止无效订单阻塞队列。
- 杠杆计算中的溢出和精度损失检查: 在计算 (size * price) / collateral 时,确保 size * price 不超过 uint256 限制(罕见但可能被恶意参数触发)。还要检查可能绕过最大杠杆限制的除法舍入截断。
仓位管理审计
- 杠杆限制: 系统必须在开仓和增加仓位时强制执行 size / margin 不超过最大杠杆 (MaxLeverage),以防止在极端市场条件下发生清算损失。
- 仓位存在性和所有权验证: 在关键操作(例如平仓)之前,系统必须验证仓位是否存在并且调用者是所有者或 Keeper 授权的角色,以防止对无效仓位进行操作。
- PnL 计算: 验证多头和空头仓位的 PnL 公式是否对称。注意:检查未实现 PnL 是否正确包含在 AUM 中。
- 保证金扣除: 验证在 decreasePosition 期间,扣除的保证金首先用于支付借款费用和资金费用,而不是返回给用户;在清算或减少场景中,检查保证金扣除后的池会计是否与实际资产余额匹配,以防止资不抵债。
- 清算激励不足: 如果清算费用完全由协议捕获或无法支付 gas 成本,Keeper 将拒绝执行清算,导致坏账累积。
- 市场平衡机制检查: 如果大量开仓或平仓交易会影响市场流动性或在执行后加剧现有的多空不平衡,则应在开仓或平仓时收取额外费用,以激励市场朝着平衡和稳定方向发展。
- 资金费率累积检查: 验证资金费率累积指数的更新频率和精度,并确保在任何仓位更改之前更新全局指数。
- 借款费用扣除逻辑检查: 在增加抵押品时,系统不得错误地重新扣除已在仓位上结算的借款费用。
- 维持保证金率检查: 在提取抵押品或增加仓位大小时,检查仓位是否保持健康的维持保证金率,即确保 remainingCollateral >= size * MM_Ratio。
- 全局未平仓合约上限检查: 系统必须验证全局多头和空头未平仓合约是否超过流动性池的容量;如果超过,则必须拒绝开仓。
- ADL 机制检查: 理想情况下,协议应在平仓期间应用 ADL,以避免全局 PnL 超过池余额并导致坏账。
交易逻辑审计
- PnL 计算一致性: 检查多头和空头、开仓和平仓的 PnL 公式在所有场景(包括 ADL 和清算)中是否保持一致,避免双重扣除。
- 提交-揭示: 用户提交请求和 Keeper 执行请求必须放在不同的区块中(或具有最小时间间隔),以防止原子级别的抢跑套利。
- Gas 消耗风险检查: 如果平仓需要 Keeper 将原生代币转账给用户,则必须验证 gas 消耗,以防止恶意用户耗尽 Keeper 的 gas 并阻止其他交易的执行。
- 签名重放检查: 如果签名用于链下匹配和链上结算,则必须检查 chainId、递增 nonce 或截止日期,以防止重用同一签名。
- 执行回调重入保护: 如果系统包含回调函数(例如,executeOrderCallback),则必须检查重入保护,以避免在回调中再次对仓位进行操作。
- ADL 逻辑独立性: 如果 ADL 逻辑重用标准减少仓位函数,并且该函数包含仓位健康检查 —— 受 ADL 影响的仓位通常处于风险边界 —— 这将导致 ADL 执行失败并阻止系统坏账的清除。ADL 必须具有跳过常规清算检查的执行路径,而是使用独立的减少函数来确保在极端市场条件下强制平仓。
- 订单修改即时性: 修改订单(例如价格或数量)时,系统必须立即使用当前市场价格评估是否满足触发条件。
- 滑点执行保护: 在 executeOrder 中,必须将当前价格与用户定义的可接受价格进行比较; 如果滑点超过允许的范围,则应取消订单而不是强制执行。
- 自动减仓 (ADL) 的正常触发: 当流动性不足时应触发 ADL,并且必须正确检查交易对手的仓位状态以防止不正确的匹配。
流动性池 & LP 审计
- AUM 计算: 审查 getAum 函数。确保它区分 maxPrice(用于铸造 LP)和 minPrice(用于销毁 LP)以防止套利。在计算 LP 价格时,必须包括未实现 PnL。
- 直接转账会计干扰: 如果合约依赖于 token.balanceOf(address(this)) 来确定用户存款而不是内部会计,攻击者可以直接将代币转入合约以干扰会计逻辑(例如,导致不正确的 feeReserves 计算)。
- 闪电贷操纵防御: 禁止在同一交易中铸造和销毁 LP 代币(添加一个 Cooldown 或基于区块的限制)以防止利用预言机更新延迟的套利。
- 资金隔离: 确保费用储备金和流动性池资金在逻辑上分离,防止用户提取属于协议收入的资金。
- 重入保护: 所有涉及资金流出的函数(例如 removeLiquidity, decreasePosition)都应应用 nonReentrant 修饰符。
- ERC-4626 利率膨胀攻击: 检查是否应用了 "最小流动性锁定"(例如,铸造 & 销毁 1000 wei)或 "虚拟偏移" 机制,以防止首次存款的膨胀攻击。
- USDT 兼容性: 使用 SafeERC20 库进行代币转账以支持不返回布尔值的非标准代币。
- 代币白名单: 检查是否允许 rebase 或通货紧缩代币。如果支持,验证系统是否使用基于余额差异的会计而不是基于参数金额的会计。
- 单一入口点: 确保底层合约(Vault / PositionManager)中的关键写入操作只能通过 Router 或 Executor 调用,以防止绕过费用或价格检查。
治理升级 & 权限审计
- 时间锁完整性: 验证关键参数更改(例如,setGov,withdrawFees)必须通过具有合理延迟(例如,24 小时)的时间锁。
- 存储槽冲突检查: 如果合约是可升级的(使用代理模式),验证存储布局是否不会引入槽冲突。
- 治理提案阈值验证: 治理决策必须要求法定人数和延迟,以避免闪电贷投票操纵。不正确的配置可能导致治理变得无效。
- 重复投票检查: 确保投票逻辑防止同一成员多次投票,或防止投票代币被转移和重用以再次投票。
- 初始化函数保护: 初始化函数必须使用 initializer 修饰符并且不能多次执行。对于实现合约,应在构造函数中禁用初始化。
- 两步所有权转移: 像 setOwner 或 setGov 这样的函数应采用两步机制(pendingOwner → acceptOwner)以防止错误定向所有权转移。
- 复制交易风险: 如果支持复制交易,验证 "操作员" 具有来自 "受控账户" 的明确授权,以防止未经授权的镜像操作。
- 资金提款权限: 对于 IDO 或众筹模块,验证提款函数是否丢失或永久锁定。
- 委托 - 转发逻辑: 如果协议将执行委托给转发角色,确保委托和转发者的权限范围正确,并验证转发的 calldata(例如,来自地址)不能被恶意构造以触发对其他用户账户的未经授权的操作。
- 角色责任分离: 诸如 Keeper、Liquidator 和 Admin 之类的角色必须明确分离。例如,Keeper 永远不应能够修改预言机地址。
- 敏感参数限制: 像 setMaxLeverage、setFees、setFundingRateFactor 这样的函数必须强制执行硬编码的上限/下限(例如,费用上限 ≤ 10%)以防止恶意或意外的错误配置。
- 关键分配的零地址检查: 诸如 setGov 或 setVault 这样的函数必须验证分配的系统合约地址不是 address(0) 或不正确的地址,以避免永久性失去控制。
- 铸造/销毁权限审计: 验证衍生品或协议代币的铸造和销毁只能由授权合约调用。
- 紧急暂停能力: 协议应支持暂停功能,但暂停不得阻止用户提取抵押品或增加保证金(以避免强制清算)。
- 核心权限强制执行: 诸如价格更新、订单更新、仓位更新和流动性提款之类的关键函数必须严格执行访问控制,防止未经授权的数据突变影响其他用户。
其他
- Solidity 版本锁定: 使用固定的 Solidity 版本(例如,0.8.19 而不是 ^0.8.0)以避免编译器版本不一致引入意外的错误。
- 算术溢出检查: 虽然 Solidity 0.8+ 提供了内置的溢出保护,但在使用 unchecked 块或执行类型转换(例如,int256 到 uint256)时需要特别小心以防止溢出。
- 来自循环的 Gas 限制 DoS: 避免在诸如批量清算或奖励结算之类的函数中迭代无界数组,以防止 Gas 限制耗尽,这可能会永久禁用功能。
- 事件日志记录正确性: 所有状态更改操作 —— 尤其是那些涉及资金和配置参数的操作 —— 必须发出带有正确参数的事件以支持链下索引。
- 外部调用返回值验证: 对于任何低级调用(例如 address.call{value: …}),始终验证返回的成功值。
- 冗余代码清理: 删除未使用的变量、函数和第三方导入以减少攻击面和部署成本。
- 奖励分配逻辑: 在计算奖励分配时,验证 rewardPerToken 是否正确处理 totalSupply == 0 的情况以避免除以零错误。
- 防止推荐循环: 确保用户不能将自己设置为自己的推荐人(自我推荐),并且不能构建循环推荐循环(例如,A → B → A)以获取奖励。
结论
去中心化永续合约协议的安全性不仅取决于 Solidity 代码的稳健性,还取决于其金融逻辑架构的完整性。从毫秒级的预言机价格延迟到 AUM 计算的细微偏差,任何被忽视的细节都可能被闪电贷放大为灾难性的经济漏洞。
审计师必须以对抗性的思维方式进行思考 —— 而不是仅仅验证是否符合预期设计 —— 而是通过扮演一个真正的攻击者的角色:利用价格操纵、舍入错误或基于 MEV 的抢跑来从协议中提取价值。这种心态揭示了更深层次的风险,并能够主动缓解风险。
持续监控建议:鉴于链上协议的可组合 "DeFi-Lego" 性质,强烈建议团队在主网部署后实施自动链上监控 —— 或利用诸如 SlowMist 的 MistEye 之类的解决方案 —— 以补充静态审计并实时检测异常。
关于 SlowMist
SlowMist 是一家专注于区块链安全的情报公司,成立于 2018 年 1 月。该公司由一个拥有超过十年网络安全经验的团队创立,旨在成为一股全球力量。我们的目标是为每个人尽可能地确保区块链生态系统的安全。我们现在是一家著名的国际区块链安全公司,曾参与过各种知名的项目,如 HashKey Exchange、OSL、MEEX、BGE、BTCBOX、Bitget、BHEX.SG、OKX、Binance、HTX、Amber Group、Crypto.com 等。
SlowMist 提供各种服务,包括但不限于安全审计、威胁信息、防御部署、安全顾问和其他安全相关服务。我们还提供 AML(反洗钱)软件、MistEye(安全监控)、SlowMist Hacked(加密黑客档案)、FireWall.x(智能合约防火墙)和其他 SaaS 产品。我们与国内外公司建立了合作伙伴关系,如 Akamai、BitDefender、RC²、TianJi Partners、IPIP 等。我们在加密货币犯罪调查方面的广泛工作已被国际组织和政府机构引用,包括联合国安全理事会和联合国毒品和犯罪问题办公室。
通过提供针对个别项目定制的全面安全解决方案,我们可以识别风险并防止它们发生。我们的团队能够发现并发布几个高风险的区块链安全漏洞。通过这样做,我们可以传播意识并提高区块链生态系统中的安全标准。