现代 DEX - Fluid 是如何构建的

  • mixbytes
  • 发布于 2024-11-22 20:12
  • 阅读 38

本文详细介绍了Fluid DEX协议的设计和功能,特别是其如何利用流动性层来支持去中心化交易所(DEX)与借贷的结合。文章深入探讨了智能抵押品、智能债务的概念以及流动性提供的动态机制,同时分析了其代码实现和复杂的数学模型。Fluid DEX的特点包括动态的价格变化、流动性合约的优化以及法国LEND-Dex的借贷机制,显示出在DeFi领域的创新潜力。

引言

我们之前回顾了一个基于 Fluid 项目的流动性层构建的协议,称为 Fluid Vault 贷款协议。Fluid DEX 是另一个利用相同基础层的协议,展示了 Fluid 在开发 DeFi 项目时的多层次方法。Fluid DEX 的主要特点是能够使用贷款层的抵押/债务资产为 DEX 中的兑换提供流动性。这使得可以利用兑换费用来增强供应并减少债务。为了实现这些目标,Fluid DEX 采用了“智能债务”和“智能抵押”概念,这些概念是针对资产对而非单一资产工作的。高级概述可以在 这里 找到。

如前所述,Fluid 的基础流动性层提供了记账、重入保护、市场限速和其他基础机制,用于支持底层协议。Fluid DEX 作为第二个“协议层”,使用基础流动性层原语“结算”其资产。我们已经看到,跟踪用户债务/供应的机制可以在 DEX 中用来支持多次调用(例如:Uniswap V4Balancer V3),允许用户通过不同的池执行多个操作,进行兑换,和在单个多次调用中管理流动性。在这些操作中,协议只是跟踪所有被使用代币的累计出入债务,仅允许在多次调用结束时提供所有所需代币,组合所有操作。这种方法显著降低了复杂交易操作的 gas 成本并增强了协议的灵活性。

Fluid DEX 采用类似的方法;然而,基础的抵押/债务层更加复杂,并充当“会计师”,而第二个 DEX 层则作为“操作员”。

让我们来考察一下这个协议的技术设计。

高级设计

我们将跳过一些与 Fluid DEX 的基础流动性层相关的部分,因为在之前关于 Fluid Vault 的文章中已经讨论过。Fluid DEX 的核心机制是“DEX-on-lending”(其中“贷款”是在流动性层实现的,DEX 则在“协议”层上实现)。在这种设置中,单个 Fluid DEX 实例充当与贷款平台接口的唯一贷方/借方。在 Fluid DEX 中的兑换操作促进用户资产在流动性层中的结算,结合存款/取款/借贷/偿还功能来管理代币的“出入”操作。贷款协议的流动性提供者同时也是 DEX 的流动性提供者,而 DEX 的兑换则对抵押/债务资产分配进行再平衡。

作为贷款平台,Fluid DEX 利用“智能抵押”和“智能债务”,这意味着代币对可以作为抵押或债务,而不是单个代币,例如 wstETH-ETH 或 WBTC-cbBTC。因此,用户提供或借贷代币对。即便用户使用智能债务 USDT/USDC 借入 100 USDT,债务被视为持有 50 USDT/50 USDC 的头寸。这种分配可以通过涉及 USDT 或 USDC 的 DEX 交易进行更改。每次用户使用 Fluid DEX 兑换资产时,“智能”贷款供应/借贷头寸中的代币分配会相应调整。从兑换中获得的兑换费用会改变抵押或债务余额,或者增加供应 APR 或减少借款利率。这是 Fluid DEX 的主要概念(高层次的说明可以在 这里 找到)。

为了管理智能抵押和债务,Fluid DEX 中的兑换操作采用两个镜像池进行执行:一个是“供应代币1/借贷代币2”,另一个是“供应代币2/借贷代币1”。兑换过程包括“供应(抵押)& 还款(债务)”的“入”代币操作,和“提取(抵押)& 借贷(债务)”的“出”代币操作,这两个池在基础流动性层上有如下所示:

因此,Fluid 中每个独立的“dex”在流动性层端有两个底层的贷款池,而这个“dex”作为这两个贷款池的用户。在这个框架中,DEX 层根据类似 Uniswap V2 的不变量计算供应/借贷操作的金额,并在池之间对资产进行再平衡,以保持 token1->token2 和 token2->token1 的价格一致。

让我们更深入地探讨这段代码。

核心

Fluid DEX 的代码库与 Fluid Vault 相同:https://github.com/Instadapp/fluid-contracts-public/tree/main/contracts,DEX 协议代码位于 protocols/dex 目录下。

主要合约 main.sol 的 构造函数 包含设置专用合约的地址:

  • colOperations - 管理与抵押的“存款/提取”操作。
  • debtOperations - 管理与债务的“借款/偿还”操作。
  • perfectOperationsAndSwapOut - 处理“完美”的存款/提取/借款/偿还操作,以股份而非代币数量进行操作(保持池平衡)。此外,该合约管理“SwapOut”逻辑。
  • shift - 处理中心价格、阈值和区间的调整。
  • admin - 管理功能。

正如前面提到的,Fluid DEX 是一个“DEX-on-lending”,包括同时作为流动性提供的“贷款”操作(存款、提取、借款、偿还)。这就是为什么在“DEX”协议中也包含传统贷款操作。代码分为三个部分:“抵押”、“债务”和“完美”,将相似操作分组。Fluid DEX 中的抵押和债务操作在两个贷款池中使用再平衡,而“完美”操作无需要再平衡,因此被单独分隔为独立模块。

让我们继续讨论兑换。

兑换

Fluid DEX 的核心功能是 main.sol 中的 _swapIn() 和 perfectOperationsAndSwapOut.sol 中的 _swapOut(),它们是该协议中最复杂的功能。我们从 _swapIn() 开始。

Fluid DEX 限制对兑换价格变化超过 5% 的操作(这是另一个“流动”部分),在兑换操作的最后一个部分(在 _updateOracle() 函数中,我们稍后将讨论)执行 _priceDiffCheck()

Fluid DEX 的价格模型也是“流动”的,因为所有价格变动(甚至是区间和阈值的治理变更)都是根据先前价格的改变进行计算并逐渐执行的。当范围限制被达到时,价格值和范围开始连续地向新值移动。这要求跟踪以前的价格、时间的转移、价格转移的状态和其他变量,以控制池的“动态”行为。含有这些数据的 dex 状态被存储在两个主要变量中:

  • dexVariables:这里关注的是 两个 池的前两次兑换价格和它们之间的时间 差异,中心 价格(将在下面说明)和最后一次交互的时间。
  • dexVariables2:包含 标志(智能抵押/债务启用)、关于 费用 的信息,以及一个包含百分比的 ,确定围绕中心价格的范围(类似于集中流动性设置中的当前区间的上下价格范围)。另一个重要参数是转移的 时间,确定价格移动的速率(将在下面讨论)。下一个 的参数与中心价格的限制相关(可以外部获取、具有最大/最小限制、受到利用率限制等)。另一个显著的标志是 暂停 标志,它禁用所有“失衡”的操作;只能执行“完美”操作(那些不会改变池中代币的相对分配的操作)。

兑换过程参数 存储 在内存中的 SwapInMemory 结构中。

兑换过程中的第一站是 pex_ 结构,它保存协议的兑换价格。这些价格由 _getPricesAndExchangePrices() 函数计算,是 Fluid DEX 兑换的真正“核心”(稍后将讨论)。目前,需要注意的是,供应/借贷代币1/代币2 具有 分开 的价格。

正如先前所提到的,Fluid DEX 中的操作涉及 两个 兑换:在“抵押”和“债务”池中。因此,下一步是为兑换的两个部分准备储备。第 一部分 用于抵押,而第 二部分 用于债务(“虚拟”储备也将在下文中讨论)。

接下来的 部分 处理池中操作数量的限制。我们检查“入”转换的数量是否不会显著影响池的储备和价格。兑换结束时会执行 _priceDiffCheck(),但 Fluid 支持提前进行此检查。

然后进入计算“入”代币金额及其在池中的分配的部分(在一个池中,我们进行“存款”,在另一个池中进行“偿还”)。我们在 _swapRoutingIn() 函数中计算兑换数量,接受“入”数量 t 以及初始的、抵押和债务的“虚拟”储备。该函数的结果为系统方程提供解,同时确定兑换的哪一部分进入抵押、债务或两个池。这个结果形成这 个分支,指示多少数量链入“存款”,多少数量链入“偿还”。

接下来,我们需要计算“出”数量,以便在一个池中“提取”和在另一个池中“借款”。此外,我们需要检查这些所需的数量是否可用。这在“_amountOutCol”和“_amountOutDebt”部分中完成。

接下来,我们决定使用哪个池来确定兑换价格:抵押还是 债务。我们选择拥有较大兑换数量的作为价格来源,但仍需记住,在兑换结束时,给定资产在两个池中的最终价格将是相同的。

经过 转换 代币余额为正常数量后,并 设置 回调数据(如果代币通过回调进行传输),有两个与基础流动性层的操作。Fluid 的流动性层(所有 Fluid 协议共享)通过一个 operate() 函数处理所有操作:存款/提取/借款/偿还,由两个签名参数 supplyAmount_ 和 borrowAmount_ 决定(这些参数的负值会导致反向操作)。第一个操作是:

LIQUIDITY.operate(..., +supply_amount, -payback_amount, ...);

而第二个是:

LIQUIDITY.operate(..., -withdraw_amount, +borrowAmount, ...);

这些操作将所有“入”代币用于供应抵押和偿还债务,同时“出”代币则用于提取抵押和借款在协议的流动性(“贷款”)层之上。

接下来调用一个附加的钩子,如果抵押或债务池需要清算,则可以停止兑换。我们在贷款一侧有一个 Fluid DEX 的贷款头寸,这个头寸可能变得“不健康”,需要清算。在这种情况下,任何兑换在清算发生之前都应被中断。“健康”状态是通过该钩子进行检查的,其接口和说明可以在 这里 找到。然而,DEX 的组合、智能债务/抵押代币对的慎重选择,以及根据外部市场价格调整这些池中的资产的再平衡,使得这种情况不太可能发生。

兑换的下一部分包括 检查 流动性层中的利用率(total_debt/total_collateral 比)。函数 _utilizationVerify() 检查最终利用率是否 不超过 流动层中的限制(在流动性层的 operate() 函数中的利用率使用示例见 这里)。

兑换的最终部分涉及调用 _updateOracle()

在 perfectOperationsAndSwapOut.sol 模块中的 swapOut() 函数执行与 swapIn() 相同的操作。代码几乎是相同的,除了用 _swapRoutingOut() 函数计算出的出代币和调整存款/提取/借款/偿还金额的顺序不同。相同的限制、利用率检查和在兑换结束时进行的 _updateOracle() 调用也适用。

兑换价格

Fluid DEX 具有非常有趣的兑换价格计算动态方案。Fluid DEX 中资产之间价格的波动性最小,因为智能债务和智能抵押都使用绑定到相同价值的资产(例如 USDC/USDT、ETH/stETH、WBTC/cbBTC 等)。对于这样的资产,传统上在狭窄价格范围内操作的“集中”流动性优势更为明显。与此同时,兑换价格计算采用与 Uniswap V2 类似的恒定乘积公式 x∗y=k。这些属性使 Fluid DEX 采用一个价格范围和阈值动态变化的方案,同时在传统的恒定乘积模型中运行。

在每次操作(例如兑换)中,我们计算一个 centralPrice,在某些情况下可以从外部源获取(像 这里),例如对于 wstETH/ETH 等价对,可以从外部合约中获取 wstETH 的价格。

中心价格决定“上限”及“下限”价格,运作的方式类似于 Uniswap V3 池,所有真实流动性被集中在这个价格范围内。此外,还存在该范围外的“虚拟”储备。因此,当需要类似于 Uniswap V2 的不变性时,我们取中心价格周围的上限和下限价格,并利用这两个点(“上限”和“下限”)外推不变量曲线。然后,在 计算 “外部”储备时:

真实储备和“外部”储备的总和被称为“虚拟储备”,它们被用于计算目标不变量 x∗y=k 和兑换数量(如在 _swapRoutingIn()_swapRoutingOut() 函数中),其中使用虚拟储备而不是现实储备。此外,通过“硬性”阈值限制的价格变动用以防止兑换价格的突然变化。

中心价格的移动,同时决定了定义虚拟储备的“上限”和“下限”范围,逐步进行。当新中心价格 _ 开始朝着 上限下限 移动时,根据时间的差异开始移动。

还需提到的是,管理员函数 updateRangePercent()updateThresholdPercent(),更新相对的上下价格范围和阈值。这些更改也不像是即时进行的,而是开始了向目标值的连续移动( 是关于范围的代码, 是关于阈值的代码)。在这两个函数中,我们看到基于时间区别和状态“移动正在进行”或“移动已完成”的值的变化。

这些“流动”机制使 Fluid DEX 避免了价格范围的突然操作,并防止在不同“价格桶”之间实时流动性移动。

预言机

Fluid DEX 通过 oraclePrice() 函数向外部服务提供 TWAP 价格,演示了协议如何存储价格。这种方法在 DeFi 中并不常见 - Fluid DEX 并不是直接存储以前的价格,而只存储价格之间的百分比变化。例如:[currentPrice] -> [-0.12%] -> [+0.13%] -> [+0.07%] -> ... 。这种方法允许通过在小尺寸价格变化上进行单次循环返回不同时间范围的 TWAP。oraclePrice() 函数接受一个时间区间数组作为参数,并返回一个包含这些区间的 TWAP 的 Oracle[] 数组。这些 TWAP 包含所给区间的平均价格以及最高和最低价格。

oraclePrice() 首先通过 加载 最后一个价格(倒数第二个价格在循环的 [第二次](https://github.com/Instadapp/fluid-contracts-public/blob/f8a93859822cbe7ca7b9bac076c5e81fe1fcadaf/contracts/protocols/dex/poolT1/coreModule/core/main.sol#L688-L693)迭代中加载)。然后开始主要的 循环,遍历 oracle 插槽,并 计算 下一次价格使用 percentDiff_ 差异。

这些插槽的更新与 percentDiff 和最后价格是通过 _updateOracle() 函数在每次兑换结束时执行。

该函数中的关键部分是使用 _priceDiffCheck() 函数限制价格变动,将价格变动限制在最多 5% 内,这在 _updateOracle() 的所有逻辑分支中都可以见到(例如:[这里](https://github.com/Instadapp/fluid-contracts-public/blob/f8a93859822cbe7ca7b9bac076c5e81fe1fcadaf/contracts/protocols/dex/poolT1/coreModule/helpers/coreHelpers.sol#L826)、[这里](https://github.com/Instadapp/fluid-contracts-public/blob/f8a93859822cbe7ca7b9bac076c5e81fe1fcadaf/contracts/protocols/dex/poolT1/coreModule/helpers/coreHelpers.sol#L838)和 这里)。在 Fluid DEX 中进行的任何兑换都不能将兑换价格移动超过 5%,使得这个协议成为其他协议的可靠价格源,并提供良好的保护以防止价格预言机操控攻击,尽管它可能对迅速而重大的价格变动的响应更慢。

_updateOracle() 函数在 不是区块中的第一个兑换 时避免多余更新(我们此时仅更新最后价格)或者当 oracle 不活动 时。一般流程包括在 这里 计算 percentDiff、选择下一个“空闲”插槽(使用 oracle 插槽映射 数据 在 dexVariables 中),并存储签名的 percentDiff(这一部分)。对于 oracle“插槽”,有一个特殊情况,因为插槽之间的时间差使用 9 位存储,因此可以存储最大值 511 秒(约 8.5 分钟)。如果兑换稍后发生(时间间隔超过 511 秒),则使用两个 oracle 插槽存储价格差,允许即使在兑换之间的间隔很大时也保持常量的价格插槽数量(在这种情况下,我们简单地迭代较小数量的以前价格)。

提供流动性

由于 Fluid DEX 是一个“DEX-on-lending”协议,因此在该系统中提供流动性实质上涉及“借款”行动:在两个池中存款和偿还。第一个池中的代币作为抵押,而第二个池中的代币则用于债务。这些操作在 Fluid DEX 中分为两个模块:colOperations.soldebtOperations.sol,他们分别负责抵押和债务操作。在一个拥有“复合”智能债务(Smart Debt)和智能抵押品(Smart Collateral)的系统中,使用抵押品和债务进行操作仅在使用股份时是可行的,因为每次在去中心化交易所(DEX)执行的兑换中基础代币的分配可能会有所不同。此外,还存在“完美”操作和“非完美”操作。“完美”操作不会改变代币的分配、相对比例,因此也不需要额外的再平衡。 这些操作是 perfectOperationsAndSwapOut.sol 模块的一部分(_swapOut() 函数在该模块中存在,仅仅是因为它太大,不适合放在一个 main.sol 模块中)。

所有“完美”函数: depositPerfect(), withdrawPerfect(), borrowPerfect(), paybackPerfect() 只在输入参数中处理股份数量,且添加或移除流动性时遵循不改变代币分配的比例。这些函数的额外参数包括作为滑点保护的提供/接收代币的“最小/最大”数量。

这些函数相对简单,主要考虑计算和应用借款和提款限制(示例 见这里,或 见这里),这些操作适用于所有 Fluid 协议,而不仅仅是 DEX。Fluid 通过应用依赖于经过的时间的限制来限制流动性层的操作。这些限制保护协议防止在短时间内顺序执行的大规模流动性操纵,这是 DeFi 黑客攻击中的常见情形。

现在让我们探讨非完美操作。抵押品操作包括: deposit(), withdraw(), 和 withdrawPerfectInOneToken()。债务操作为: borrow(), payback(), 和 paybackPerfectInOneToken()。我们不会深入每个函数,因为它们的设计相似。完成计算后,它们依赖于两个核心函数: _depositOrPaybackInLiquidity() 用于“进”操作或 LIQUIDITY.operate() 用于“出”操作。所有这些函数包括滑点保护的 maxSharesAmt/minSharesAmt 参数。

但是,在所有“非完美”函数中有一个关键区别,这也解释了为什么像 paybackPerfectInOneToken() 这样的“完美”函数不包含在“完美”部分。所有这些函数在最后都会调用一个特殊的 _arbitrage() 函数。该函数确保抵押品和债务池在代币添加/移除影响分配时保持平衡。例如,如果我们有两个池: USDC(col)/USDT(debt) 和 USDT(col)/USDC(debt),那么向一个池添加 USDC 将导致池之间不同的 USDC<->USDT 兑换率。因此,需要将部分 USDC 转换为 USDT,以更新余额,以保持两池之间的稳定汇率。 _arbitrage() 函数首先通过 检索 池储备,判断这些储备是否与智能债务或智能抵押品相关联。套利仅在两者(智能抵押品和智能债务)都启用的情况下进行,否则它会简单地 更新 价格并返回。

然后,套利过程 调用 _swapRoutingIn() 函数,传入零个“输入”代币。在池不平衡的情况下,该函数返回应该发送到抵押品或债务池的代币部分,并指定需要调整哪个池的储备以平衡价格。在 一个 情况中,我们在抵押品池上操作,执行 token0 的 _deposit 和 token1 的提款( 见这里)。在 第二 积量中,它涉及到债务池,执行 token0 的 _payback 和 token1 的存入( 见这里)。最终 部分类似于兑换 - 验证最小价格变动并更新预言机。

实施细节

我们之前在关于 Fluid Vault 协议的 早期 文章中讨论了一些 Fluid 的实施细节。Fluid DEX 引入了一些额外的讨论点。

一个显著的方面是管理员函数的实现,这些函数可以在 core/main.sol 模块中调用,但是调用被转发到管理员函数的实现,通过 fallback() 函数(类似于某些代理)。fallback 函数首先检查用户是否 被授权,然后 设置 非重入标志,之后执行对 ADMIN_IMPLEMENTATION 的 delegatecall,通过 _spell() 函数。管理员函数受到 _onlyDelegateCall 修饰符的保护,仅能通过主要模块访问。管理员 模块 包含许多与设置各种限制、暂停兑换、在池间进行套利等相关的函数。

另一个值得关注的领域是新 DEX 部署的程序 见这里。它使用简单的 CREATE 操作码,这意味着使用工厂的随机数来计算部署合约的地址。因此,部署 DEX 的“地址”可以作为一个简单的 dexId 存储,而不是作为一个地址(20 字节)。该地址可以通过哈希与部署者地址结合的 dexId 进行 计算。这种技术方便以存储多个已部署合约的 ID,而不是地址的形式。

Fluid DEX 的实现中包括许多需要求解方程组的实例,例如在 _swapRoutingIn()_swapRoutingOut() 函数中。方程的解在注释中给出,提供了如何确定目标值的全面理解。

结论

在 Curve 的 LLamaLend 中,我们 见证了 “DEX 上的借贷”概念,而 Fluid DEX 是一个“借贷上的 DEX”系统。它使用一个复合抵押品和债务系统,包含两种代币,并采用 DEX 兑换来重新平衡抵押品和债务池。Fluid DEX 对操作量和价格变动施加动态限制,以保护协议免受突发攻击。这种架构允许供应 APR 的增加,降低借款利率(通过将兑换费加入抵押品并从债务中减去),并同时自动重新平衡债务和抵押品以达到更健康的用户头寸。

这些特性具有挑战性,协议的代码包含许多复杂的交互,涉及多个池,而不仅仅是一个池,在它们之间进行套利,及在不同执行层进行计算和限制的应用。

Fluid DEX 显然是 DeFi 领域的重要项目,清楚地表明未来的协议将结合 DEX 和借贷机制,而且算法金融背后的数学变得越来越迷人。现代 DeFi 正在快速发展,而传统银行和交易所在过时和不灵活的算法方面日益落后。愿我们都顺利!

在我们的下一篇文章中再见。

  • MixBytes 是谁?

MixBytes 是一支专业的区块链审计和安全研究团队,专注于为 EVM 兼容和 Substrate 基础项目提供全面的智能合约审计和技术咨询服务。请加入我们在 X 上,获取最新的行业趋势和见解。

  • 原文链接: mixbytes.io/blog/modern-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
mixbytes
mixbytes
Empowering Web3 businesses to build hack-resistant projects.