本文详细介绍了 Curve LlamaLend 协议,围绕 CrvUSD 稳定币构建的高效借贷系统,强调了其独特的 AMM 设计及软清算机制,这些机制可以有效保持用户资产的健康状况并减少传统清算的风险。文章还探讨了协议的核心合约结构和功能,以及在市场波动时的操作逻辑。通过对技术细节的深入分析,突出了 Curve LlamaLend 在 DeFi 领域的重要性和潜在应用。
这系列文章中的下一个借贷协议是 Curve LlamaLend。这是一个非常有趣的借贷协议,围绕 CrvUSD 稳定币构建,并使用其核心组件:一个 AMM。CrvUSD 的特点包括一个引人入胜的数学模型和稳定化机制,这些机制与价格区间(类似于 Uniswap V3 中使用的tick)一起工作,但方式不同。此外,CrvUSD 采用软清算,完全不同于传统的清算。该协议主要用 Vyper 开发,以及许多其他显著的功能。
Curve LLamaLend 是一组市场,以 CrvUSD 作为稳定币进行借贷:
让我们仔细看看吧!
Curve 借贷使用与 CrvUSD 相同的机制和代码。这是有道理的,因为大多数算法稳定币的债务/抵押品头寸与借贷非常相似。主要区别在于 crvUSD 中铸造新的稳定币,而在借贷中,则是从借贷池中提取:
这两种机制的“核心”是 LLAMMA,一个设计非常有趣的 AMM。在讨论借贷时,我们不能忽视 LLAMMA 的机制,因为它为整个协议添加了独特的属性。为了更好地理解 CrvUSD 的稳定化机制,这种机制当前被用作 Curve 借贷中的可借资产,查阅 CrvUSD 白皮书是非常有用的。
LLAMMA 中的流动性以“区间”的形式组织,这类似于 Uniswap V3 的tick(在这里描述),每个区间负责自己的价格范围。Curve 的 AMM 在单一价格区间内的运作类似于 Uniswap V3。交换的操作也“交叉”区间,当前区间左侧仅剩可借资产的区间,右侧则是抵押品的区间:
但在稳定币和借贷协议中,我们总是有一个抵押资产的外部预言机价格。这些协议中的 AMM 不仅被用作 DEX,还被用作能够将用户头寸靠近“健康”状态的稳定化机制。CrvUSD(和借贷)的这一美丽理念是设计一个 AMM,刺激市场制造商持续交换抵押品与可借代币的方向,从而导致用户头寸变得更“健康”。
让我们看一张白皮书中的图像:
我们看到外部预言机价格 p 0, “当前区间”价格范围(白色部分),以及我们的 AMM 中的价格 p AMM。当一个交换移动到下一个区间(退出白色区域)时,AMM 的价格相比线性变化更为陡峭(绿色和黄色部分)。因此,左移和右移当前区间的操作会“向市场前瞻”,设置买入/卖出价格低于/高于市场。这刺激交易机器人做出更大规模的交换,并更快地接近市场价格。
这种当前区间外价格的非线性变化是通过改变整个(!)价格网格来实现的。与 Uniswap V3 不同的是,Uniswap V3 中tick之间的间隔是固定的。在 Curve 中,附加到当前区间上下方的价格是非线性变化的,这为市场制造商提供了更多的激励,在“正确”的方向上进行交换。
用户的抵押资产被放置在区间中,LLAMMA 中的交换同时将抵押品转化为可借代币,反之亦然。如果这一操作朝着正确的方向进行,它将购买增加其价格的“降低”抵押品(当抵押品价格下降,用户变得更加“不健康”时),反之则是出售抵押品。
这种设计使得 Curve 的 AMM 能够“支持”借贷机制,形成自动清算的激励和“软清算”。因此,CrvUSD 中的清算(如果价格没有大幅波动)实际上并不是清算。它是一种持续的抵押资产与债务资产的交换,反之亦然,通过交易机器人将用户头寸调整向“健康”状态。在价格突然变化的情况下,Curve 也有传统的“硬清算”,类似于其他协议,允许为了偿还用户的债务而清算其抵押品。
软清算使得用户的头寸并不固定,因为用户的抵押品可以分为两部分:一部分为抵押资产(例如,WETH),另一部分为稳定币(例如,CrvUSD),这两者之间的比例根据抵押品价格的变化而变化。
这种设计非常优美,但在价格急剧变化且 AMM 价格与预言机价格之间的差异显著时也会出现一些有趣的情况。在这种情况下,用户的抵押品可能完全转化为稳定币。在这种情况下,用户头寸的“健康”变得非常奇怪,比如将 CrvUSD 作为抵押,将 CrvUSD 作为债务,这增加了一些复杂性。
现在,让我们看看协议是如何构成的。
Curve LLamaLend 铸造/借贷的核心是三种核心合约的组合:Vault、AMM(LLAMMA)和 Controller。这些合约共同构成一个借贷市场,支持两种代币 — 一种可借用,另一种作为抵押品。该组合有两种部署变体:OneWayFactory 和 TwoWayFactory。
OneWayFactory 为 crvUSD 和某些抵押代币创建一个借贷市场。另一方面,TwoWayFactory 创建两个相互连接的借贷市场,使代币的角色“相反”(“多头”市场和“空头”市场)。例如,在第一个市场中,crvUSD 作为可借代币,WETH 作为抵押品,而在第二个市场中,角色反转:WETH 成为可借代币,crvUSD 作为抵押品。
第一步是初始化工厂 在这里, 在这里我们设置 AMM、Controller 和 Vault 合约实现的地址,以及预言机地址、货币政策参数和其他设置。初始化后,工厂可以部署新的借贷市场(目前仅支持 CrvUSD 作为稳定币)。
工厂包括几个创建 Vault 的方法(用户共享持有合约),使用已经 存在的 AMM 池(如常规 Curve 的交换池)作为价格预言机或使用一个 自定义的 价格预言机。每个新的借贷市场创建都需要设置 A 参数(放大系数),该参数作为 Uniswap V3 tick中 tickSpacing 的类似(在文章中描述 在这里)。
接下来是创建 Vault,该过程涉及在 这里 创建 AMM 和 Controller。
在 TwoWayLendingFactory 的情况下,我们创建 两个 vault。这里的关键点是两个借贷市场使用 相同 的价格预言机。使用两个不同价格预言机的设置使得 TwoWayFactory 尤其易受预言机价格操纵攻击,因为即使是小的价格变化也可能导致 vault 之间的套利机会放大。
借贷的主要驱动是在 AMM.vy 合约中实现的。LLAMMA 的最重要方面与市场价格和内部 AMM 价格之间的联系有关,这在 这里 中有描述。
LLAMMA 中最重要的函数是 _p_oracle_up(),计算公式为 p_base * ((A - 1) / A) ** n — 对于第 n 个区间的上限价格(代码中的公式计算比较复杂)。该函数控制每个价格区间的价格范围,当修改时会“偏移”所有区间的价格范围分布。所有其他区间的“上”和“下”价格(预言机和 AMM)都是从此函数派生的。
LLAMMA 中的主要工作函数是 _exchange(),与 Uniswap 类似,具有两个 分支,用于根据所需的“输入”或“输出”代币量计算目标交换参数。主循环在这里 通过 经过区间,更新每个交叉区间的储备。之后,AMM 的 active_band 被改变。
接下来在 LLAMMA 中是 deposit_range() 函数,它从用户处获取抵押品并放入区间范围内(从 n1 到 n2)。我们将所有抵押品分为等额部分( y_per_band)并且 对于每个 区间, 增加 用户的份额并 更新 区间的总份额。
你可能还记得,在 Uniswap V3 中提供流动性没有使用“对于每个tick”的操作。在这里我们无法避免这样的“每区间”的操作,因为价格区间的“漂浮”网格和软清算。我们需要每个用户在每个区间内的确切份额,无论该区间是否完全由抵押代币或稳定币组成。否则,就必须类似于 Uniswap V3 提供流动性以适应tick区间。
withdraw() 函数的工作方式类似于存款,迭代各个区间,移除流动性和份额。
Curve 借贷的 Controller 部分在 Controller.vy 中呈现,持有用户 债务 的贷款映射,并持有贷方提供的可借代币。当用户 存入() 在 Vault 中时,借来的代币将转移到 Controller 中。之后,Controller 将这些资金转移给用户,在 这里 创建贷款。
Controller 中的贷款是通过 _create_loan() 函数创建的,其中可以看到参数 N,控制着抵押品将被分配到的区间数量。当债务被准备时,我们计算起始区间 n1,从中开始抵押品将放置到下一个 N 个区间中。在存储贷款信息后,Controller 调用 AMM.deposit_range() 函数,将抵押品直接放入 LLAMMA。
与其他协议一样,Vault 是一个 ERC-4626 合约(在 Vyper 中没有 ERC-4626 的参考实现),并为最终用户铸造 Vault 份额。所有 ERC-4626 函数均存在;其中两个最重要的函数(_convert_to_assets 和 _convert_to_shares)在 [这里](https://github.com/curvefi/curve-stablecoin/blob/e82ac1ae7ae29feef0255f9fbb016d1d76159ee5/contracts/lending/Vault.vy#L280-L307)。现在可以提到使用 DEAD_SHARES 来保护 Vault 免受 通货膨胀攻击。
Vault 可以集成到 Controller 中,但借贷是在 CrvUSD 之后出现的,因此作为外部模块添加,负责贷方/借方的份额。
Curve 在其生态系统中有多种 预言机。虽然可以与 Chainlink 或其他预言机进行交互,但最自然的方法是使用 Curve 自有的池,这些池已经有了显著的流动性。
例如,WETH/CrvUSD 借贷 AMM 使用 CryptoFromPool.vy 合约(在 地址)从 crvUSD-WETH-CRV 池获取价格。
CrvUSD 中预言机设计的重要部分是通过 limit_p_o() 函数限制预言机价格的变化。预言机价格的剧烈波动可能会对用户的头寸健康和 AMM 损失产生有趣的影响(将在我们的审计报告中详细描述)。
预言机价格的增量也会影响交换的动态费用(函数 get_dynamic_fee()),缓解极端价格变化和三明治攻击按造成的 AMM 的 significt 损失的风险。
首先是用户头寸的“健康”。主要函数是 health(内部 在这里),它应该是正的。第一个部分与其他协议的计算方式相似:它是抵押品/债务 比率。然而,我们还需要考虑最高用户抵押区间的价格差,这影响了抵押品价格(以及用户的健康)。该逻辑的这一部分是 通过 完全标志启用的。该标志几乎在所有地方都是“开启”的,除了 repay() 部分,在那里需要“纯”健康比率计算,以避免在清算期间与 AMM 价格的操作。
借贷和借款的 APY(基于借款利率)不是固定的,而是由货币政策确定,该政策以几种 变体 形式展现(最新的是 SemilogMonetaryPolicy 和 SecondaryMonetaryPolicy)。例如,借贷 池 CRV/CrvUSD 使用 SemilogMonetaryPolicy.vy。该政策计算在 AMM 中使用的利率 在 并且依赖于市场利用率比( total_debt/total_collateral)。货币政策可以由工厂管理员 更改。
Curve 项目通常在其实现中具有许多有趣的细节。让我们讨论其中一些。
Vault 和 controller 直接跟踪基础代币余额。正如我们在 Euler V2 文章中所见,有些协议选择维护自身的内部基础代币余额副本,而不是简单使用 balanceOf()。这两种方法都有效:“余额副本”更能抵御“捐赠”攻击,但在保持两个相同余额副本方面成本较高,可能会有潜在问题。直接使用 balanceOf() 方法更简单,对于用户来说成本更低,但可能更容易遭受“捐赠”攻击。
用户的tick信息,持有每个tick的份额信息,以 128 位值存储。读取用户tick的实现 在这里,用户头寸 存储 作为地址 => 结构体 UserTicks 的映射,其中 userTicks 持有tick范围(作为 2x128 = 256 变量)和该范围内每个tick下的份额数组。
Controller 中的 users_to_liquidate() 函数返回可“强制清算”的用户列表。这类函数通常在具有清算的协议中不会出现,清算的机器人创建者通常会自行跟踪可清算的头寸。该函数有助于他们,但并不显著,因为清算机器人主要关注“未来”清算,截取价格变化并尝试通过 MEV 首先进行清算。
LLAMMA 还可以通过附加一个特殊的流动性挖矿奖励合约来激励池,为流动性挖矿者提供奖励。这是通过在 这里 注册 self.liquidity_mining_callback 完成的,并在诸多函数中调用,例如 在 deposit_range() 中的调用。这个特性源于最初的 Curve 池,并在项目中发挥了重要作用,促进了其代币的利用。
LLAMMA 使用 预先计算的 针对参数 A 的平方根和对数值,以避免在合约中计算这些表达式。
Curve LlamaLend 是一个先进的 AMM 和借贷协议的独特组合,解决了 DeFi 中用户清算等真实问题。LLAMMA,作为借贷协议的核心,为用户提供了对抗硬清算的安全网,在非极端市场条件下自动转化其抵押品,以改善其头寸健康。现有的 Curve 借贷市场几乎完全避免了“坏债务”问题,即使在波动性池中,硬清算的数量也极少。
该项目围绕 CrvUSD 稳定币构建,但可以与其他任何稳定币一起使用。我们相信,基于 LLAMMA 稳定机制的其他稳定币市场可能会在不久的将来到来。所有这些功能使得 Curve 借贷对任何 DeFi 开发者或审计者都非常吸引人。不要错过。我们将在接下来的文章中继续探索重要的借贷协议。在此之前,请保持关注!
MixBytes 是一支专注于为 EVM 兼容和基于 Substrate 的项目提供全面的智能合约审计和技术顾问服务的专家区块链审计团队。加入我们的 X,及时了解最新的行业动态和 insights。
- 原文链接: mixbytes.io/blog/modern-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!