返回论坛

EOS REX 深度解析:从源码视角拆解买卖机制(下)

查找币 学术研究 安全研究 Web3安全 区块链安全

查找币安全研究院

钱包恢复评估 | 链上取证分析 | Web3 事件响应
以合法授权、证据保全、隐私保护和可复核流程为前提,不要求用户在线提交完整私钥或助记词。

查看研究院 研究报告中心
## 前言 在上一篇文章中,我们系统梳理了 EOS REX 系统中买卖 REX 的核心流程,覆盖了 `deposit`、`withdraw`、`buyrex`、`add_to_rex_pool` 等关键函数。然而,由于篇幅限制,仍有部分技术细节未能展开。本文作为系列续篇,将聚焦于 `sellrex` 和 `fill_rex_order` 两个函数的内部实现,深入剖析其安全边界与潜在风险。 ## 前情回顾:核心函数一览 在进入深度分析之前,我们先快速回顾上一篇文章中介绍的核心函数: - **deposit**:将 EOS 转换为 SEOS(预备金),用于后续操作。 - **withdraw**:将 SEOS 换回 EOS,实现资金回笼。 - **buyrex**:从用户预备金中扣除相应份额,用于购买 REX。 - **sellrex**:卖出已解锁的 REX,将本金与收益转入用户预备金账户。 - **add_to_rex_pool**:将用户购买的 REX 存入资金池,并根据池内状态计算可购数量,由 `buyrex` 调用。 - **fill_rex_order**:处理用户的卖出订单,计算并分配收益。 其中,`sellrex` 和 `fill_rex_order` 是本文分析的重点。 ## sellrex 函数:卖出逻辑的深度拆解 `sellrex` 函数的实现逻辑如下: ```cpp void system_contract::sellrex( const name& from, const asset& rex ) { require_auth( from ); runrex(2); auto bitr = _rexbalance.require_find( from.value, "user must first buyrex" ); check( rex.amount > 0 && rex.symbol == bitr->rex_balance.symbol, "asset must be a positive amount of (REX, 4)" ); process_rex_maturities( bitr ); // 收获已成熟的 REX check( rex.amount <= bitr->matured_rex, "insufficient available rex" ); // 只能卖出已成熟的 REX auto current_order = fill_rex_order( bitr, rex ); // 获取出租 EOS 的分红 asset pending_sell_order = update_rex_account( from, current_order.proceeds, current_order.stake_change ); // 订单未成功处理时,进入队列 if ( !current_order.success ) { auto oitr = _rexorders.find( from.value ); if ( oitr == _rexorders.end() ) { oitr = _rexorders.emplace( from, [&]( auto& order ) { order.owner = from; order.rex_requested = rex; order.is_open = true; order.proceeds = asset( 0, core_symbol() ); order.stake_change = asset( 0, core_symbol() ); order.order_time = current_time_point(); }); } else { _rexorders.modify( oitr, same_payer, [&]( auto& order ) { order.rex_requested.amount += rex.amount; }); } pending_sell_order.amount = oitr->rex_requested.amount; } check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" ); // 添加 dummy action 以在 trace 中显示 if ( current_order.success ) { dispatch_inline( null_account, "sellresult"_n, { }, std::make_tuple(...)); } } ``` ### 关键逻辑分析 1. **成熟度检查**:`process_rex_maturities` 函数负责将已过锁定期的 REX 标记为成熟状态。只有成熟的 REX 才能被卖出,这是系统防止流动性冲击的重要机制。 2. **订单填充**:`fill_rex_order` 是核心收益计算函数。它根据当前资金池状态,计算用户卖出 REX 后应得的本金与收益。若资金池资金不足,订单将进入排队队列。 3. **队列机制**:当订单无法立即填充时,系统会将其加入 `_rexorders` 表。若用户已有未完成订单,新请求的 REX 数量会累加到原订单中。这一设计看似合理,但隐藏着风险。 ### 潜在安全隐患 在源码的末尾,存在一个关键的 `check` 语句: ```cpp check( pending_sell_order.amount <= bitr->matured_rex, "insufficient funds for current and scheduled orders" ); ``` 该校验的目的是确保挂单的 REX 总量不超过用户已成熟的 REX。然而,当资金池资金不足时,系统会跳过 `if ( !current_order.success )` 块内的所有步骤,直接挂起订单。这就意味着: - **恶意用户可以在资金池资金不足时,反复调用 `sellrex` 并叠加挂单金额**。 - 只要资金池始终无法满足支付,用户就能持续增加挂单的 REX 数量,直至超过其实际拥有的成熟 REX。 虽然最终在更新 `rex_balance` 时,`asset` 结构体的溢出检测会阻止交易成功,但这会导致一个严重后果:**未完成的卖单成为坏账,整个 REX 系统将陷入停滞**。具体原因在于,挂单队列中的订单会阻塞后续的所有卖出操作,直到资金池恢复流动性或订单被手动清理。这一设计缺陷在极端市场条件下可能被利用,造成系统性风险。 ## fill_rex_order 函数:收益计算的幕后逻辑 `fill_rex_order` 是 REX 收益分配的核心。其核心逻辑如下: - **计算可分配收益**:根据资金池中的 EOS 总量与 REX 总供应量,计算每单位 REX 对应的价值。 - **处理订单**:若资金池余额足够,则将用户卖出的 REX 转换为等值 EOS(含收益),并更新资金池状态。 - **返回结果**:返回一个包含 `proceeds`(收益)、`stake_change`(质押变化)和 `success`(是否成功)的结构体。 ### 关键点 - **收益分配**:`proceeds` 包含本金与分红,分红来自 REX 资金池中出租 EOS 产生的利息。 - **资金池平衡**:若资金池余额不足,`success` 标记为 `false`,触发挂单逻辑。 ## 总结与思考 通过本文的深入分析,我们可以看到 EOS REX 的设计在追求效率与安全之间做出了权衡。`sellrex` 的队列机制虽然在正常情况下能平滑处理流动性不足,但其设计缺陷可能导致系统级风险。对于开发者而言,理解这些边界条件至关重要,尤其是在高并发或极端市场环境下。 作为安全团队,我们建议: 1. **对挂单队列进行容量限制**,防止单个用户无限叠加订单。 2. **增加资金池余额的实时监控机制**,在资金不足时及时预警或暂停卖出操作。 3. **完善订单清理逻辑**,避免坏账长期阻塞系统。 本文仅从技术角度分析 EOS REX 的实现细节,不构成任何投资建议。投资者应充分了解相关风险,理性决策。 --- **参考资料**: - EOS Authority REX 测试与实现细节:https://eosauthority.com/blog/REX_progress_with_testing_and_implementation_details **往期文章**: - EOS REX 安全系列之从源码开始玩转 REX(一) --- *本文由查找币安全团队整理发布*
在论坛中查看和回复