Skip to content
On this page

L3-3: Options(期权合约)

1. 问题

期权是传统金融中万亿美元级别的衍生品市场,但链上实现面临着独特的挑战。核心问题是:如何在没有中心化清算所的情况下,确保期权卖方一定会履约?在 TradFi 中,期权清算所(OCC)作为中央对手方担保所有交易。在 DeFi 中,智能合约必须通过托管机制来消除对手方风险——卖方提前锁定全额抵押品,买方提前支付权利金。

但这带来了第二个问题:如何公平地计算行权收益?链上没有"市场收盘价",需要依赖预言机提供结算价格。同时,欧式期权只能在到期日行权(与随时可行权的美式期权相对),这需要合约精确地管理时间窗口和状态转换。

2. 原因

DeFi 期权协议(如 Opyn、Ribbon、Hegic)正在将万亿美元级别的传统市场引入链上。链上期权的核心优势是消除对手方风险——卖方无法违约,买方无法拒绝支付。理解期权的铸造(write)→购买(buy)→行权(exercise)生命周期是进入 DeFi 衍生品开发的基础。

对开发者而言,期权合约是一个优秀的练习:它涉及状态机设计(Active → Purchased → Exercised)、支付结算逻辑、抵押品管理和时间锁。这些模式在更复杂的结构化产品和永续合约中反复出现。

3. 方案

EuropeanOption.sol 实现了完整的欧式期权系统,支持看涨(CALL)和看跌(PUT)两种类型。

状态机设计:Option 结构体维护了四个状态:ACTIVE(卖方已锁定抵押品,等待购买)、PURCHASED(买方已支付权利金,等待到期行权)、EXERCISED(已行权结算)和 EXPIRED(预留但未使用)。

创建期权(Write)createOption() 由卖方调用,指定期权类型、行权价、数量、权利金和到期时间。卖方需要立即将标的资产(amount)转入合约作为抵押品。抵押品在行权时用于支付买方收益。

购买期权(Buy):买方调用 purchaseOption() 支付权利金给卖方。购买后期权状态从 ACTIVE 变为 PURCHASED,买方地址记录为 holder。购买必须在到期前完成。

行权(Exercise):只能在到期后由买方调用。exercise() 接收一个结算价格(_settlementPrice,实际应用中来自预言机),计算收益:

  • 看涨期权(CALL):ITM 条件 = settlementPrice > strikePrice,收益 = (settlementPrice - strikePrice) * amount / settlementPrice
  • 看跌期权(PUT):ITM 条件 = settlementPrice < strikePrice,收益 = (strikePrice - settlementPrice) * amount / strikePrice

如果期权是虚值(OTM),收益为 0。剩余抵押品退还给卖方。

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

4. 遭遇的陷阱

  • 结算价格被操纵exercise()_settlementPrice 由调用者直接传入,恶意调用者可能传入对自己有利的价格
  • 抵押品不足覆盖最大损失:看涨期权卖方的最大损失理论上是无限的(标的资产价格可无限上涨),但合约中锁定的抵押品是固定的 amount
  • 到期时间验证不精确:使用 block.timestamp 比较时,矿工可以在一小段范围内操纵区块时间戳
  • 买入后权利金不退还:如果期权在到期前变得深度虚值,买方无法在到期前出售或放弃期权
  • 权利金用代币支付但金额可为零:没有强制下限,卖方可能创建零权利金的期权并被抢先交易

5. 陷阱的原因

结算价格操纵是链上期权最核心的安全问题。在 EuropeanOption.sol 的当前实现中,_settlementPrice 作为参数直接传入 exercise() 函数。这是因为简化了预言机集成——在实际生产环境中,结算价格应由去中心化预言机(如 Chainlink)提供,并通过 onlyOracle 修饰符保护。

然而,即使使用 Chainlink,也存在潜在的操纵风险:在到期时刻,攻击者可能通过闪电贷瞬时操纵池价格,影响 Chainlink 的报价(特别是对于流动性不足的资产)。这需要协议层面的防护,如使用 TWAP(时间加权平均价格)而非瞬时价格。

抵押品不足覆盖最大损失的问题在于:看涨期权的收益公式 (S - K) * amount / S 在 S 趋近无穷大时趋近 amount。这意味着如果 amount 是固定的,卖方的最大损失就是全部抵押品。对于看跌期权,收益 (K - S) * amount / K 在 S=0 时的最大值为 amount。所以合约中的抵押品锁定量恰好覆盖了最大可能收益——这是正确的设计。

6. 如何解决陷阱

结算价格安全:在生产环境中,将 settlementPrice 替换为预言机查询:

solidity
function exercise(uint256 _optionId) external nonReentrant {
    // 使用 Chainlink 预言机获取结算价格
    uint256 settlementPrice = oracle.latestAnswer();
    require(block.timestamp >= option.expiry, "Not expired");
    // ... 后续逻辑不变
}

同时,建议在到期后引入一个小的等待窗口(如 1 小时),防止在预言机更新期间行权。

权利金下限:在 createOption 中添加最小权利金检查,防止零费用期权的垃圾创建。

到期时间安全:使用 block.timestamp >= option.expiry(而非 >),允许在到期时刻精确行权。block.timestamp 的操纵窗口在 30 秒以内,对于日级别的期权到期日来说影响可忽略。

7. 技术要点

技术点说明
欧式 vs 美式欧式只能在到期日行权,美式到期前均可
状态机ACTIVE → PURCHASED → EXERCISED
抵押品托管卖方锁定 amount 代币在合约中
CALL 行权收益max(settlementPrice - strike, 0) * amount / settlementPrice
PUT 行权收益max(strike - settlementPrice, 0) * amount / strike
抵押品退回行权后剩余抵押品退还给卖方
ReentrancyGuard购买和行权使用 nonReentrant
自定义错误NotWriterNotHolderOptionExpired

Built with AiAda