本文详细介绍了 f(x) 协议的 2.0 版本,重点在其新的 fxUSD 稳定币及其设计和稳定机制的改进,包括多种池、杠杆仓位、清算和赎回机制等。
我们继续我们的稳定币系列,今天的重点是 f(x) 协议 2.0 版。这是对之前 1.0 版(LUSD 稳定币)的升级。新的稳定币名为 fxUSD,并在设计和维持固定汇率机制上进行了许多改进。 f(x) 协议使用基于价格区间、清算、赎回、储备池和各种附加稳定性措施的稳定化方案。该方案的某些元素与 Liquity 协议 中的机制相似(例如针对最“危险”仓位的赎回和稳定池)。其他元素类似于在 Uniswap V3 中观察到的集中流动性概念,而一些则调用 Fluid DEX 的重新平衡(rebasing)来操作。某些方面尤其具有创新性,包括使用闪贷来杠杆化头寸。 此外,f(x) 与 Aave、Curve 和 Morpho 等外部协议进行了广泛集成。f(x) 协议的用户可以获得可持续的收益,并受益于独特的仓位杠杆机制。
这些机制非常有趣,因此让我们深入研究。
f(x) 协议使用多个池,每个池都以单一抵押资产(如 USDC/USDT 或 ETH/stETH/wstETH)运作,并与 fxUSD 稳定币互动。用户创建抵押债务头寸(xPOSITIONs)并铸造 fxUSD。协议文档中使用的一个关键术语是 NAV(净资产价值)——在抵押品方面的 fxUSD 代币和 xPOSITION 股份的“价格”。这个比率对于估算拥有的 fxUSD、xPOSITIONs 的价值以及用于维持 fxUSD 锚定的稳定化机制至关重要,后者会调整这些 NAV 以保持 fxUSD 对美元的固定汇率。
维持 fxUSD 稳定性的主要不变量是:
保持这一不变性确保抵押价格 s(左侧)的波动通过右侧的 fxUSD($n_f$)和 xPOSITIONs($n_x$)的 NAV 调整来抵消。在这里,xPOSITIONs 作为波动性“吸收器”,最小化其对 fxUSD 价格的影响。这个概念源自 f(x) 协议 1.0。2.0 版引入了“杠杆”头寸,使用户能够管理抵押债务头寸,杠杆范围最高可达 10 倍,具体取决于资产,使该协议成为强大的交易工具。此外,这些杠杆头寸对市场做市商的稳定化行为影响更大。
为了进一步理解,我们需要定义“xPOSITION 杠杆”,因为 f(x) 协议中有两种类型的杠杆比率:
当实时杠杆比率达到再平衡(rebalance)阈值时,触发再平衡操作,以将其调整回阈值。如果实时杠杆比率超过并达到清算阈值,系统将启动清算,赎回与受影响头寸Hook的所有 fxUSD。这些阈值根据每种抵押资产的波动性和相关风险进行了校准。如果总抵押比率降至 100% 以下,f(x) 协议将停止新 xPOSITION 的创建,但仍允许现有的 xPOSITION 被关闭。
在 f(x) 协议中,xPOSITION 遭组织成“杠杆区间”(类似于 Uniswap V3 的价格区间)。抵押价格的移动触发了这些区间内的再平衡(rebalancing)操作。
另一个关键的稳定机制是赎回,允许用户以 1 美元的价格将 fxUSD 兑换为抵押品。当 fxUSD 的价格低于 1 美元时,这一选项便变得具有吸引力。赎回优先处理杠杆最高的 xPOSITION,消除风险更高的头寸,同时稳定 fxUSD 价格。
稳定机制还包括稳定池,该池持有 fxUSD 和 USDC 余额。其在稳定性中的关键功能是为 xPOSITION 的再平衡和清算提供 fxUSD 和 USDC 代币。这些操作发生在稳定币价格超过 1 美元时(当抵押品变得更便宜),激励旨在推动稳定币价格下跌的操作。然而,这种操作,尤其是清算(或其“更轻”版本的再平衡),需要通过外部市场获取稳定币 — 通常会推动它们的价格上涨。为了应对这一点,f(x) 协议首先使用稳定池中的 fxUSD 代币(避免对外部市场的依赖),仅在稳定池耗尽时才求助于外部来源。
此外,f(x) 协议包括一个储备基金,由费率的一部分和协议收入资助。该储备旨在作为应对不利场景的保护,增强协议在潜在问题面前的稳定性。如果出现坏账并且无法用储备基金中的资金覆盖,损失将被“社会化”(按比例再分配给所有 xPOSITIONs)。
F(x) V2 还引入了 fxUSD 和 xPOSITIONs 的市场 — 即 fxUSD 和 xPOSITIONs 的衍生品。这为用户提供可持续收益选项,具有两个风险水平:fTokens 的低风险和低收益,以及 xTokens 的高风险但高收益。
该协议与多种外部 DeFi 项目集成:Aave(用于获取额外的 fxUSD 利息)、Morpho(用于零费用闪电贷)和 Curve(用来 fxUSD/USDC 价格预言机和再平衡交易)。
现在,让我们检查这些机制在代码中的实现方式。
协议所有池的入口点是 PoolManager.sol 合约,其管理多个 池,并将开立、关闭、调整和清算头寸的流程路由到相关池。每个池(示例)都有其特定的实施,使用自己的抵押代币、价格预言机、费用以及其他参数。
核心操作结构是 PoolStruct,它跟踪抵押和债务代币的当前和最大(容量)数量。抵押金额分为两个部分:“原始”抵押和根据代币利率调整的抵押(已存入部分)。每个与 xPOSITIONs 相关并修改债务或抵押的函数都会更新 PoolStruct。该结构对于计算池的抵押比率至关重要。PoolManager 的主要功能 - operate()、redeem()、rebalance() 和 liquidate() - 负责调整全局池金额,将代币转入或转出协议,并收取费用。这些操作的“池内”机制在每个池的具体实例中实现。
打开和调整 xPOSITIONs 是通过 operate() 函数执行的。该函数在内部 调用 相关池的 operate() 函数, 更新 池的债务和抵押值,收集协议费用,并 铸造或销毁 fxUSD 代币。在创建头寸时,在完成所有债务和抵押计算及检查后,此操作会将头寸“ 结算”到特定的“区域”。区域代表了特定的债务对抵押的比率。我们将稍后进一步探讨区域。
其他关键功能—— redeem()、rebalance() 和 liquidate() — 以类似方式运作:它们将执行委托到池的具体实例,更新全局池参数,并进行必要的转移、铸造或销毁。例如,转移和销毁 在 redeem() 被调用后执行,或在再平衡或清算之后执行 hooks。这些 hooks 将在稍后进行更详细的讨论。
另一个重要的功能是 harvest(),仅限于具有 HARVESTER_ROLE 的地址可访问。此功能从协议中收集 三种类型的费用:performanceFee、harvestBounty 和 pendingRewards。这些费用然后被 分配:performanceFee 归入财政,harvestBounty 发送到做市者地址,pendingRewards 用于激励外部 fxUSD AMM 池。
治理 控制 新池的注册,设置其最大容量,更新奖励拆分器,配置 stETH 等代币的利率提供者,并定义清算阈值。
现在我们需要查看 fxUSD 代币合同,该合同被所有 f(x) 协议合约广泛使用。
FxUSDRegeneracy 合约实现了 FxUSD 代币。fxUSD 是一种分数代币,跨多个市场结构化。每个市场由 FxMarketStruct 控制,用户在该市场中持有 fToken 股份。该合约还通过 StableReserveStruct 跟踪稳定币储备(例如,USDC)。
稳定机制的一个重要部分是 _checkMarketMintable() 函数。此检查在抵押比率低于稳定比率时阻止股份的铸造(因此阻止新的 fxUSD 的创建)如果 抵押品不足以支持 fxUSD。而当“fxUSD 供应量低且抵押品足够”时,允许铸造新的 fxUSD。
BasePool 的代码是 f(x) 协议的核心组成部分,实现在开立和调整 xPOSITIONs、处理赎回和清算机制。然后,BasePool 被专门的池如 AaveFundingPool 继承。该设计使池能够与具有可靠价格预言机的任何资产一起工作,以计算抵押债务比率。池 必须 重写并实现 _updateCollAndDebtIndex() 和 _deductProtocolFees() 函数,以独立管理抵押、债务和费用。BasePool 本身负责铸造和销毁 fxUSD,实施使用“集中流动性”模式(与价格区间)来运作抵押债务头寸,并重新平衡 fxUSD/抵押品储备。
BasePool 的主要功能如下,经过详细说明,通过 PoolManager 访问,并在对代币进行转移、铸造/销毁操作(在 FxUSDRegeneracy 中)以及更新全局“池内”参数的预操作和后操作 hooks 中执行。与此同时,BasePool 在其内部头寸、区域、数量和股份上进行操作。
其中一个关键功能是 operate(),它创建或调整 xPOSITIONs。通过提供 newRawColl 和 newRawDebt 参数,该函数可以添加或移除抵押和债务资产。第一步涉及 接收 给定抵押资产的 预言机 价格。每个 xPOSITION 与特定的价格区间相关联,且抵押、债务或预言价格的变化可能会将头寸移动到不同的区间。因此,初始时会 从 当前区间移除头寸。
每个 xPOSITION 的“测量”基于抵押和债务股份,而非原始数量。这就是为什么对抵押和债务的原始数量的所有操作都需要从股份到数量的转换(如 此处)。这种“基于股份”的方法简化了对头寸组的操作,处理可重新基础的资产如 stETH,并根据池代币的总供应量按比例分配费用或操作盈余,从而有效增加代币所有者的持有量。
在 验证 清算阈值(防止超过限制的借款或取款)后,协议执行抵押 操作,应用费用,更新抵押股份,然后执行债务 操作,调节债务股份。
最后,协议 检查 该头寸的债务比例是否在有效的不足抵押水平内。更新后的头寸可能具有新的抵押债务比率,关联到不同的价格区间,并在此时被 添加。头寸的细节被 更新,池的全局抵押和债务金额被刷新。
为了维护池的健康,f(x) 协议采用两种机制降低头寸和区间的债务比率。“更轻”的机制是 rebalance(),在 区间(它将多个头寸归为一组)或单个 头寸 的债务比例大于 rebalanceDebtRatio 但小于 liquidateDebtRatio 时使用。此机制通过尽早重校准债务比率来防止出现关键状态。在抵押价格变化逐渐的情况下,重新平衡可以避免清算,通过逐步调整头寸确保协议保持超额抵押。
单个头寸占有“总区间债务”和“总区间抵押”的一部分,这意味着单个头寸和区间的逻辑非常相似。“区间”级别的版本在总区间金额和股份上运作,而“头寸”级别的版本在单个头寸金额和股份上运作。在这两种情况下,股份都会转换为原始金额(此处或此处),并且在验证债务比率后,会使用 _getRawDebtToRebalance() 函数计算需要转换成抵押品的债务数量。此函数解答了“要消除多少债务以实现目标债务比率?”的问题。一旦目标债务和抵押数量转换回股份,功能行为略有不同:
这种“基于股份”的逻辑使得能够同时在单个区间内重新平衡多个头寸,而无需直接修改单个头寸。所有用户在一个区间内的头寸的债务和抵押股份保持不变,但新区间中的抵押和债务股份的“总供应”则不同,相应调整每个用户的债务-抵押比率。
下一步是 liquidate() 函数。此函数仅适用于特定头寸,且仅当债务比率超过 liquidateDebtRatio 时才能应用。当抵押价格变化重大且 rebalance() 未能及时解决问题时,会触发清算。与再平衡类似,该头寸会 从 其区间移除,所需的 债务 和 抵押 股份被重新计算(包括清算 奖金)。
清算的关键方面是 重新分配 潜在的坏账。在 f(x) 协议中,坏账是“社会化”的,意味着它从池的总债务中 减去 。此操作 增加 池的总债务指数,而这是这个过程不可避免的一个方面。
rebalance() 和 liquidate() 函数使用来自稳定池的 fxUSD 和 USDC。该机制确保清算和再平衡不会影响 fxUSD 在外部市场的价格,这是维护稳定的关键。多余的 fxUSD 会被 销毁,任何未获得的余额则用用户提供的 USDC 来 覆盖。
redeem() 函数是稳定机制的另一个关键部分。它在池级别操作,当 fxUSD 在协议外被低估时,通过将债务交换为抵押来赎回债务(对赎回者总是有利可图)。出于治理原因,赎回可以被 暂停,当池的债务比率超过 1 时则 不允许 。
总而言之,BasePool 管理这些关键功能:
• operate():创建和调整 xPOSITIONs。
• redeem():当债务被低估时,将债务交换为抵押。
• rebalance():在债务超过安全极限时校准备。
• liquidate():处理关键债务比率状态。
稳定功能的目标是在可能的情况下利用稳定池,确保 fxUSD 在外部市场的价格稳定。
AaveFundingPool 是从 BasePool 派生出的专用池,旨在与 Aave 协议互动并利用 Aave 的利率来获取 fxUSD。
函数 _updateCollAndDebtIndex() 根据 Aave 的 债务 更新利率,从 Aave 的借款人那里收取利息。当外部 fxUSD 价格过低时 (低于) , AaveFundingPool 启用资金:费用被应用于 xPOSITION 持有者,并传递到稳定池,使得 USDC 和 fxUSD 存款更具吸引力,从而增加抵押指数。此调整 减少 抵押股份的总量,提高用户的“每份抵押”价值,保护协议免于以更低价的 fxUSD 交换抵押。
费用在池级别通过 _deductProtocolFees() 函数处理。AaveFundingPool 使用两个不同的费用率:一个 应用 于抵押被添加时,另一个 应用 于抵押被移除时。这些费用由治理 设定。
该池实现稳定池,这是一种特别池,旨在与 fxUSD 和 USDC 稳定币一起运作。它不依赖于价格预言机,只有 一个 用于检测 USDC 脱钩事件,这时会阻止池中的操作(如这里所示 here)。
与 BasePool 不同,该池不管理头寸。相反,它仅向将 fxUSD 或 USDC 存入池的用户铸造股份。这可以通过 deposit() 函数实现,该函数将 fxUSD 或 USDC 代币从用户转移并铸造池股份。然后使用内部 _deposit() 函数,根据存入的代币类型(fxUSD 或 USDC)计算股份的最终数量。
稳定池中的 redeem 程序与 BasePool 中的不同。稳定池不使用价格区间。相反,redeem() 过程会燃烧用户的股份,并根据池的当前 分配 将基础代币退还给他们。
此外,稳定池中的 redeem() 函数只能在经过冷却期后调用,首先创建一个 redeemRequest() ( 每个用户一个)。此“锁定”期允许协议有足够时间有效管理大规模赎回,方便做市商观察到可能导致稳定池的重大用户活动。
在稳定池中不需要再平衡和清算程序。对于两个与同一价值Hook的稳定币而言,它们并不相关,其中 USDC 脱钩事件会暂停所有操作。尽管 rebalance() 和 liquidate() 函数存在于 FxUSDBasePool.sol 的代码中,但它们仅在由其他池使用时,将执行传递给 PoolManager,并仅在该情境下运行。FxUSDBasePool.sol 本身则缺少这些程序的实现。
稳定池逻辑的一个重要组成部分是 arbitrage() 函数,该函数仅可被 PegKeeper 调用(见下文)。该函数允许通过 PegKeeper 在 fxUSD 和 USDC 之间进行交换,PegKeeper 为此使用外部 AMM。其目的是在发生重大不平衡时调整池中 fxUSD 和 USDC 的数量。稳定池中的 fxUSD 代币对于再平衡和清算至关重要。它们的可用性确保这些操作可以进行,而无需从外部市场获取 fxUSD,这可能会抬高其价格并增加协议债务。
PegKeeper 合约是稳定机制的一部分。 它与 Curve 的 StableSwapNG 池互动,以获取 fxUSD 的价格,并使用 Curve 池的 price_oracle() 函数提供的 EMA 价格来从外部市场获取该价格。然后将该价格与“fxUSD 脱钩价格”进行比较,以允许/拒绝 借入 fxUSD 和 允许/拒绝 在 AaveFundingPool 中的资金。
这是一个带权限的合约,主要有两个角色: “回购”和“稳定”,在 fxUSD 锚定问题出现时被激活。PegKeeper 中的 stabilize() 函数在 Curve 的 AMM 内交换 fxUSD 和 USDC,以重新平衡稳定池中的 fxUSD/USDC 数量,并执行交换 操作。
另一方面, buyback() 函数从 FxUSDRegeneracy 稳定储备中提取 USDC,并使用 Curve 的 AMM 交换为 fxUSD。随后,它销毁 fxUSD,制造购买压力以提高其价格并减少供应。
在审核了关键合约后,让我们检查涉及稳定机制的功能。
f(x) 协议还包括“紧急”操作的 ReservePool、财务系统以及对 fxUSD 的激励机制等组件。然而,这些元素超出了本文的范围。
现在,在审核了稳定机制后,是时候探索 f(x) 协议的一项非常强大的功能——目标 xPOSITION 杠杆。 在创建头寸时铸造 fxUSD 使得这种机制相当引人瞩目。我们考虑一个“零和”示例(不包括费用、ETH 价格波动和其他复杂情况):
时光荏苒……
在 ETH 价格波动的情况下,这种机制允许用户通过杠杆作用开设多于实际拥有的抵押,而且只需承担他们最初的 1 ETH 风险。这种方法展示了传统金融中使用的杠杆方案在 DeFi 中的实现方式。
函数 openOrAddPositionFlashLoanV2() 打开这样的头寸,而函数 closeOrRemovePositionFlashLoanV2() 关闭它。两个函数都利用了 _invokeFlashloan() 函数,该函数可以与任何闪电贷合约配合使用。在当前版本中,它使用 Morpho,其不收取任何闪电贷费用。
现在,让我们检查协议中涉及的各种费用。
协议内部累积的所有费用流动 通过指定的 RevenuePool 合约,该合约将其分配给稳定池的质押者、外部 veFXN 代币持有者(Curve)、Aave 池和财政。
主要费用适用于用户存入或提取抵押时,例如在 operate() 函数中(参见 这里)。费用金额取决于特定池的实现。例如,当前使用的 AaveFundingPool 协议引入了“开启”费用 和“关闭”费用,这两个费用设定 于池的初始化阶段。这些费用占用添加或移除的抵押的一部分。
下一个费用是“赎回”费用,适用于 redeem() 函数。在此函数中,费用是由 治理 决定的,费用也占用抵押的一部分。
另一个费用涉及在 MarketV2 中铸造 fTokens/xTokens。该费用结构包含两个阶段:一个比率适用于低于阈值的数量,而另一个比率适用于超过此阈值的数量。MarketV2 在 fToken/xToken 赎回期间也应用费用,流程类似于铸造逻辑。
此外,还有与 harvest() 函数相关的费用。该函数收集池中的累积奖励、资金和业绩费用。这些费用包括 业绩费用,费用 归入财政,并且收获奖金归 用户进行收获。
f(x) 协议与各种外部 DeFi 协议集成,其中所有这些也会施加相应的费用。
这个 合约 是 f(x) 协议的另一个重要组成部分,通过收益产生的分享:fToken 和 xToken 为用户提供收益。这些分别代表“fxUSD派生的”和“xPOSITION派生的”代币。fToken 提供相对保守的收益和较低的风险,代表底层抵押的一部分(可以被添加或从协议中移除)。另一方面,xToken 代表协议内的杠杆头寸,承诺以更高的风险获得更高的潜在回报。如上所示,杠杆头寸运作时涉及的金额显著更大,承担更多费用。然而,这些头寸的波动性更高,其清算对 xToken 的收益影响更大,相较于 fToken。
fToken 函数由 mintFToken() 和 redeemFToken() 表示。这些函数利用了预设参数 stabilityRatio,用于控制在铸造/赎回操作中基础代币(USDC)的最大流入 或 流出。这些函数与财政进行互动(所有费用在此处收集),以接受/发送基础代币(USDC)并铸造/销毁 fTokens(请参见这里 和这里)。
xToken 的铸造操作类似于 fToken。这两个代币都利用财政中的 redeem() 函数,将底层资产及其累积收益一并返回。
f(x) 协议在其核心机制中并不引入“时间戳相关”的索引。用户头寸或激励中没有“线性递增”的值。时间戳仅作为“心跳”来更新 Aave 储备的快照、获取 fxUSD 价格 以及确定赎回请求的锁定期限。提供额外作用的奖励在此不作讨论。
与其他协议一样(这是我们经常提到的主题),数学函数在两个方向 进行四舍五入,既向上又向下。这确保了协议受益于边缘情况,避免在计算股份、数量和比例时与小值或大值相关的问题。
此外,采用二进制范围以实现高效的存储使用。例如,请参见 PoolStruct 以及该结构的用法。
f(x) 协议包括专门的视图函数,如 previewDeposit() 和 previewRedeem()。这些让用户在执行之前预览他们在协议中的作用结果。
f(x) V2 协议是一个引人入胜的稳定币示例,其稳定机制涉及动态重新平衡、赎回、清算、专门的稳定池及用户头寸,类似于“集中流动性”的结构。
它将传统借贷原语与质押、分享以及与外部 DeFi 项目的协作结合在一起。此外,它为用户头寸提供了强大的杠杆机制,使得闪电贷成为可能。
该协议无疑是最复杂的稳定币之一。实质上,f(x) 协议充当一个多功能 DeFi 工具集,围绕其主要工具 fxUSD 稳定币展开。这无疑是 DeFi 开发者和审计师值得研究的主题。
期待在我们的下一篇文章中与你相见!
MixBytes 是一支专业的区块链审计师和安全研究员团队,专注于为 EVM 可兼容和基于 Substrate 的项目提供全面的智能合约审计和技术咨询服务。请在 X 上关注我们,以获取最新行业趋势和见解。
- 原文链接: mixbytes.io/blog/modern-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!