本文深入探讨了1inch的Limit Order Protocol,Fusion和Fusion+协议,着重分析了其原理、实现及在去中心化金融中的应用,尤其是在订单填充、跨链交换和荷兰拍卖等方面的创新和技术细节,提供了深刻的技术见解和丰富的实例说明。
本文将继续我们关于现代去中心化交易所(DEX)的系列介绍。今天,我们将重点关注1inch的另一个DEX生态系统。我们将回顾三个1inch协议,因为它们都是相互构建的:限价订单协议(Limit Order Protocol)和Fusion/Fusion+协议。之前,我们已经评审了一系列“直接兑换”协议,例如 Uniswap V4、Balancer V3、Fluid DEX,现在我们将重点关注“基于订单”的项目。我们上一篇文章讨论了 CoW Swap。
1inch限价订单协议(LOP)在本系列中继续采用“基于订单”的方法。限价订单是一种兑换订单,不仅包含代币的输入和输出金额,还包括额外的条件,如订单截止日期、目标价格限制或外部调用的数据。这允许实现几乎任何程序化逻辑,以确定一个订单是否可以被解决者(resolver)执行。这些订单在以下情况下极为有用:“如果市场在接下来的10天内达到这个价格则执行交易”或“为我的订单发起荷兰拍卖”,等等。LOP中的特殊“模式”和订单类型也可以在其他协议中使用,以实现高级DeFi机制,如拍卖和跨链兑换。
此外,“基于订单”的兑换方法可以降低费用,并提供出色的用户体验,使用户能够绕过原生代币的费用。此外,由于Active使用了离链签名,通过离链解析者处理订单,而仅在链上进行“结算”逻辑,因此“基于订单”的兑换解决方案对于跨链兑换非常有用。在这些情况下,我们可以在订单验证逻辑中包含对外部链的“资金提供证明”的检查,从而为单链和跨链兑换维护类似的基础设施。
好了,不再说标准营销话术了,让我们进入代码吧!
该协议的主要代码库在 这里,核心功能存在于 OrderMixin.sol 合约中。
限价订单协议(LOP)最重要的方面是订单的验证。用户(创建者)创建一个订单,设置限价条件,签署该订单并发布。接单者(taker)接受此订单,验证它并通过提供所需的资产量来履行它。接单者无法在链上填充订单,除非满足所有创建者的要求,该过程将在订单履行过程中在链上进行验证。在创建订单时,创建者可以:
此功能不仅允许直接使用LOP进行交易,还允许其作为其他协议的结算层,特别是那些在结算之前处理离链订单的协议。
该协议的主要实体是 Order 结构体。订单标识符(bytes32 orderHash)在大多数与订单相关的功能中使用,是通过对其序列化的内存表示进行 哈希 确定性的推导出的。关键字段当然是出单者/接单者资产和金额。此外,还有一个特殊的salt参数用于维护附加限价订单数据的完整性,我们稍后会提到。订单的附加条件编码于 MakerTraits 结构体中,该结构体在MakerTraitsLib.sol中描述,见 这里。
所有这些功能的附加数据位于订单的 扩展 中 - 这是一组附加数据,伴随着订单。它不包含在“基础”订单结构中,但通过salt参数(其中包含扩展内容的哈希)与相应订单在密码学上是连接的。扩展与订单一同处理,包含不同类型的附加数据,如额外外部调用的参数。
还有一个接单者参数的 TakerTraits 包,创建者会在填充订单的过程中将其添加。我们将稍后进行审查。现在,让我们继续进行填充订单的过程,因为这是协议的关键方面,通过了解这一点,我们可以轻松审查填充订单所需的协议的其他部分。
用于结算订单的函数有四个(描述见 这里),它们的操作类似,但在传递的离链签名和额外参数上有所不同。
fillOrder()和 fillOrderArgs() 检查 用户设置的离链签名集 (v, r, s)。fillOrder() 向接单者传递 空 args(扩展),而 fillOrderArgs() 则 设置 附加参数。
fillContractOrder() 和 fillContractOrderArgs() 在接单者参数(扩展)上工作类似,但检查 bytes calldata 签名,这是合同签订时按照 EIP-1271 创建的签名。
这两个函数组接下来分别调用 _fillOrder() 或 _fillOrderContract(),分别执行 “一级” 检查, 检查 剩余金额(如果订单允许部分填充),并尝试 应用 创建者的权限(用于用户签名的订单)。
两个函数组均以调用主要的 _fill() 函数结束 - 我们的主要目标。
首先,进行了一系列不同的 验证。我们首先检查扩展的 有效性,使用 isValidExtension() 函数,检查扩展内容是否与订单的盐参数相关联,以保护创建者免受接单者对扩展参数的可能更改。
接下来是与订单发送者和过期参数相关的 一系列 检查。
之后是 谓词检查 - 一个由创建者定义的外部调用,用来确定是否可以履行该订单。它使用 _staticCallForUint() 函数对目标进行静态(!)调用,该调用必须返回 “1” 才能继续处理订单。此功能允许创作者对其订单添加几乎任何“限”条件,并按照他们的意愿配置限价订单条件。
下一个部分是处理创建者和接单者的金额。与DEX类似,有 两 条支路:一条是接单者使用“确切的创建者金额”(类似于 Uniswap 的 swapExactIn()),另一条是“确切的接单者金额”(类似于 swapExactOut())。两个支路都计算出创建者和接单者的目标金额( 这里 和 这里),并检查是否超过 TakingAmountTooHigh 或 MakingAmountTooHigh 的阈值。选择 TakerTraits 中走哪一条支路是接单者的责任,使其能够选择如何填充订单。
最后,进行 检查,以确保金额非零,并且如果订单不允许部分填充,则保证金额对应于完整的订单金额。
然后,通过 BitInvalidator 检查 失效状态。此功能允许创建者为给定的创建者地址设置 失效位图,使其能够使同一创建者所做的其他订单失效。例如,来自一个包的一个已填充订单可以使其他所有订单失效,从而实现“从一个包中填充一个订单”的策略。BitInvalidator 可以使用 nonce 标识符对订单进行分组,或者采用创建者的 epoch 概念,允许交易服务在一个 epoch 中发布多个订单,然后移动 epoch,失效所有“旧”订单,并在下一个 epoch 中继续。有关此类失效的更多信息,可以参考 这里。
接下来,根据 剩余 创建者的金额,对订单进行失效,这很简单。
然后是 预交互(如果在 MakerTraits 中设置)。在这里,相关数据从订单的扩展中加载,并调用监听器地址(如果提供),否则调用创建者的地址。我们将检查不同的扩展交互,但在这一部分,我们可以查看 preInteraction() 函数,这简单提供所需的批准。
接下来是 转移 资产从创建者到接单者。这可能涉及到 WETH 的包裹/解包,并使用两种转移方法:
然后 进行 TakerInteraction,允许接单者在接收资产后添加任何所需的结算动作。其接口在 这里 中声明。
接下来, 转移 资产从接单者到创建者(或创建者设置的接收者)。这包括处理 WETH 的包裹/解包操作,使用两种转移方式:基于 Permit2 和带后缀的 transferFrom (与前面从创建者到接单者的转移相似)。
最后,[后续交互](https://github.com/1inch/limit-order-proto...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!