清算

本指南将逐步介绍如何在 金星 协议上执行清算。清算是指从抵押不足的账户中扣押抵押品以偿还未偿债务。本指南主要面向旨在自动化清算流程的开发者或机器人。

账户流动性计算

金星 协议使用两种计算方法来确定账户流动性:抵押因子 (CF) 和清算阈值 (LT)。

抵押因子 (CF)

抵押因子是指可用于偿还贷款的资金百分比。核心池中的 Comptroller.getAccountLiquidity 和独立池中的 Comptroller.getBorrowingPower 会返回账户的流动性。它们使用 Resilient Oracle 来检索资产价值和市场抵押因子,从而确定可用于抵押的资金百分比。仅依靠 getBorrowingPower 不足以识别独立池中需要清算的账户。

清算阈值 (LT)

清算阈值 (LT) 表示账户抵押不足,触发清算的临界点。在核心池中,LT 与抵押因子相同。在独立池中,可以使用 Comptroller.getAccountLiquidity 获取 LT。

清算人通常使用外部监控系统或其他策略来准确识别抵押不足的账户。

遍历所有账户并检查每个账户的抵押因子 (CF) 和清算阈值 (LT) 效率极低。

清算人应该依赖链下计算,并维护账户和余额的链下映射。使用 PoolLens 中的 vTokenBalancesAllvTokenUnderlyingPriceAll 等函数可以高效地检索与账户关联的多个 vToken 的余额和价格。

通过结合这些函数获取的信息,可以准确识别适合清算的抵押不足账户。

最低可清算抵押品

Comptroller.minLiquidatableCollat​​eral 变量表示常规(非批量)清算所需的最低抵押品金额(以美元计)。抵押品低于此阈值的账户可能不符合非批量清算的条件。该值以美元为单位(小数点后 18 位)。

查找抵押不足的账户

执行清算的第一步是识别抵押不足的账户。以下是查找抵押不足账户的建议方法:

  1. 创建账户余额记录:为了高效地识别抵押不足的账户,清算人可以通过索引市场事件来维护账户和余额的链下映射,从而检测新的仓位并更新现有仓位:

  • Mint:代币铸造时触发的事件。

  • Redeem:代币赎回时触发的事件。

  • Borrow:基础资产借入时触发的事件。

  • RepayBorrow:借款偿还时触发的事件。通过监听这些事件,清算人可以跟踪仓位变化并相应地更新用户余额。

建议使用子图来索引这些事件。

  1. 获取账户余额:使用 PoolLens(核心池上的 VenusLens)提供的 vTokenBalancesAll 函数来检索与账户关联的多个 vToken 的供应量和借款余额。此函数接受一个 vToken 数组和账户地址作为参数。

  2. 获取底层资产价格:使用 PoolLens(核心池上的 VenusLens)提供的 vTokenUnderlyingPriceAll 函数来获取多个 vToken 的底层资产价格。此函数接受一个 vToken 数组作为参数。

  3. 计算流动性缺口:利用步骤 2 中获得的供应量和借款余额,以及步骤 3 中获得的底层资产价格,计算每个账户的流动性缺口。这可以通过计算余额和价格的标量积,并将其与 LT 值进行比较来实现。

通过这种方法,您可以根据 CF 和 LT 的计算结果有效地识别抵押不足的账户。

执行清算

一旦识别出抵押不足的账户,即可使用 liquidateBorrowliquidateAccounthealAccount 函数启动清算流程。liquidateBorrow 由相关的 vToken 合约(核心池上的清算人合约)提供,而 liquidateAccounthealAccount 由 Comptroller 提供。以下是相关步骤概述:

  1. 计算清算金额:确定待偿还的债务金额和待扣押的抵押品。这通常通过检查借款人的债务余额、市场抵押品系数以及任何折扣或清算激励措施来计算。

  2. 执行清算时,可以根据账户的抵押品最低可清算抵押品偿付能力调用三种不同的清算类型:

  • 抵押品 > minLiquidatableCollat​​eral --> liquidateBorrow():在相关的 vToken 合约上调用 liquidateBorrow 函数。此函数需要几个参数,包括借款人的地址、清算人的地址、待偿还的债务金额和待扣押的抵押品。请参阅 vToken 合约文档,了解该函数所需参数的具体细节。

示例

已知:

  • 抵押率:50%

  • 平仓率:50%

  • 清算门槛:60%

  • 借款金额:13,000 美元

  • 抵押金额:20,000 美元

  • 清算激励:110%

  • 协议份额百分比:5%

借款金额比清算门槛(12,000 美元)高出 1,000 美元,因此该头寸符合清算条件。清算时需偿还最高 6,500 美元(借款金额 * 平仓率)。假设清算人启动清算程序,偿还金额为 1,000 美元。我们来计算抵押品扣押金额(从借​​款人抵押品中扣押的金额):

抵押品扣押金额 = 偿还金额 * 清算激励

抵押品扣押金额 = 1,000 美元 * 1.1

抵押品扣押金额 = 1,100 美元

因此,如果借款资产价值达到 13,000 美元,偿还金额为 1,000 美元,考虑到 10% 的清算激励,扣押的抵押品总额将为 1,100 美元。为了计算清算人将获得的金额,我们需要考虑核心池中的 treasuryPercentMantissa 或独立池中的 protocolSeizeShareMantissa。此变量(“协议份额百分比”)设定了分配给协议的抵押品百分比。假设清算协议的份额为 5%,并计算清算人收到的金额:

“清算人收到的金额 = 抵押品金额 - 协议份额”

“协议份额 = (抵押品金额 / 清算激励) * 协议份额百分比”

“协议份额 = ($1,100 / 1.1) * 0.05 = $50”

“清算人收到的金额 = $1,100 - $50 = $1,050”

综上所述,清算人将为清算提供 $1,000。清算完成后,清算人将收到 $1,050,剩余的 $50 将分配给协议。

  • 抵押品金额小于最小可清算抵押品金额且账户有偿付能力 --> liquidateAccount():此函数清算借款人的所有借款。

示例

  • 抵押率:50%

  • 清算门槛:60%

  • 最低可清算抵押品:100 美元

  • 借款金额:60 美元

  • 抵押金额:90 美元

由于借款金额 >= 54 美元,该头寸已符合清算条件。我们应该调用 liquidateAccount() 函数,因为抵押品 < 100 美元 且账户无偿付能力

  • 抵押品 < 最低可清算抵押品且账户无偿付能力 --> healAccount() 函数:此函数将从借款人处扣押所有剩余抵押品,要求发起清算的人员(msg.sender)偿还借款人现有债务,并将任何剩余债务视为坏账。发送方必须偿还一定比例的债务,计算公式为 抵押品 / (借款 * 清算激励)

示例

  • 抵押品比例:50%

  • 清算阈值:60%

  • 最低可清算抵押品:100 美元

  • 借款金额:90 美元

  • 抵押品金额:60 美元

该头寸符合清算条件,且抵押品金额低于 100 美元,但账户资不抵债,因此我们需要调用 healAccount() 函数,以确保剩余债务(30 美元)被协议视为坏账。

  1. 处理清算结果:调用 liquidateBorrow 函数后,监控交易是否成功,并处理任何由此产生的事件或错误。清算成功后,被扣押的抵押品将转移至清算人的地址,并从借款人的账户中偿还债务。

强制清算

通常情况下,账户只有在抵押不足的情况下才有资格被清算,如前几节所述。但如果某个市场或市场中的某个账户启用了“强制清算”功能,则情况有所不同。在这种情况下,即使账户的健康度大于 1(即账户抵押充足),该市场中的借款头寸也可能被清算。此外,关闭因子检查将被忽略,允许在一次交易中清算 100% 的债务。

此功能基于 Compound V2 的实现此处。Compound V2 允许在抵押因子为零、储备因子为 100% 且借款暂停时对市场进行“强制清算”。Venus 定义了一个功能标志来启用/禁用“强制清算”,该标志可直接通过 VIP 配置,而非基于其他参数。Compound 社区在此帖中讨论了此功能。

在 Venus 中,强制清算可以针对整个市场启用(市场中的所有借款头寸都将被强制清算),也可以针对市场中的单个账户启用(仅特定账户的借款可以在市场中被强制清算)。

要检查整个市场是否启用强制清算,可以调用资金池的 Comptroller 合约中的 Comptroller.isForcedLiquidationEnabled(address vToken) 函数,并传入市场地址。要检查市场中单个账户是否启用强制清算,可以使用 Comptroller.isForcedLiquidationEnabledForUser(address borrower, address vToken) 函数,并将账户地址和市场地址作为参数传入。

可用性:自 VIP-172 版本起,核心资金池已启用整个市场的强制清算;自 VIP-186 版本起,独立资金池已启用整个市场的强制清算。自 VIP-210 起,强制清算仅适用于核心资金池中的个人账户。

示例

已知:

  • vUSDT 抵押率:80%

  • 1 USDT = 1 BUSD = 1 USDC = $1(为简化起见)

  • 平仓率:50%

  • 清算激励:10%

  • 用户持有以下仓位:

  • 供应:500 USDT

  • 借入:200 BUSD

  • 借入:100 USDC

该用户的健康率为 (500 * 0.8) / (200 + 100) = 1.33。因此,在正常情况下,该用户不符合强制清算的条件。

现在,假设我们启用BUSD市场的强制清算(通过VIP)。那么:

  • 任何人都可以清算前一位用户的 BUSD 头寸。此外,平仓因子限制将不予考虑。因此,可以进行以下清算:

  • 偿还金额:200 BUSD

  • 扣押抵押品:USDT

  • 通过此次清算,将从用户的抵押品中扣押 220 USDT(偿还金额 + 清算奖励)。

  • 清算后,用户的全局头寸将为:

  • 供应量:280 USDT(500 USDT - 清算期间扣押的 220 USDT)

  • 借入:0 BUSD

  • 借入:100 USDC

  • 因此,新的健康比率将为 (280 * 0.8 / 100) = 2.24。他们仍然不符合常规清算的条件。

  • 由于 USDC 市场未启用强制清算,因此 USDC 债务无法清算(因为健康度大于 1)。

强制优先清算 VAI 债务

在前文中,清算是对所有账户进行的,不考虑具体的 VAI 债务金额。forceVAILiquidate 功能通过强制清算 VAI 债务超过 minLiquidatableVAI 的借款人来增强清算流程。强制清算 VAI 债务有助于协议更好地管理风险,并防止因 VAI 债务过度累积而造成的潜在损失。

对于有未偿 VAI 债务的借款人,强制清算 VAI 首先会进行检查,以确保只有符合条件的账户才会被清算,然后再启动清算流程。

检查

  1. 检查 Comptroller 中 VAI 清算是否未暂停。

  2. forceVAILiquidate 标志是否设置为 true。

  3. 验证借款人的 VAI 债务是否大于可清算 VAI 的最低金额(初始值为 1,000 VAI)。

示例 1

已知:

  • 此功能已启用

  • 用户负债如下,可被清算:

  • 2,000 VAI

  • 5,000 USDT

清算人必须先清算 VAI 头寸,因为其大于 minLiquidatableVAI。如果清算人尝试先清算 USDT 头寸,则清算交易将被撤销。

示例 2

已知:

  • 此功能已启用

  • 用户负债如下,可被清算:

  • 500 VAI

  • 5,000 USDT

清算人可以先清算 VAI 头寸 USDT 头寸。两次清算均可成功,因为 VAI 债务小于 minLiquidatableVAI

自动收益分配

在核心池中,清算收益以 vToken 的形式转移至清算人合约。在清算交易期间,清算人合约将尝试用 vToken 兑换协议在清算激励中所占的份额,以换取相应的底层代币。如果兑换成功,底层代币将被发送至 ProtocolShareReserve 合约。但是,如果兑换失败,底层代币将被添加到待赎回列表中,清算人合约将在后续的清算交易中再次尝试赎回这些待赎回的代币。

分配扣押金额

扣押的抵押品将在清算人和 ProtocolShareReserve 合约之间进行分配:

  1. 清算人份额:

  • 清算人将获得指定份额的抵押品作为激励。

  1. ProtocolShareReserve 合约份额:

  • 剩余的抵押品将发送至 ProtocolShareReserve 合约。

  1. 转换(如适用):

  • BNB:

  • 如果扣押的抵押品是 BNB,则在发送至 ProtocolShareReserve 合约之前,会将其转换为 Wrapped BNB (wBNB)。

  • 其他 VToken:

  • 对于其他 VToken,其底层代币将被赎回并转移至 ProtocolShareReserve 合约。

赎回处理

清算人合约会尝试以底层代币的形式赎回协议在 VToken 清算激励中所占的份额:

  1. 赎回成功:

  • 如果赎回成功,则底层代币将被发送至 ProtocolShareReserve 合约。

  1. 赎回失败:

  • 如果由于流动性不足或其他原因导致赎回失败,则代表待赎回的 VToken 将被添加到列表中以供后续处理。

待赎回管理

后续清算交易将利用待赎回列表:在每次清算期间,清算人合约会尝试将待赎回的 VToken 兑换为其底层代币,并将这些底层代币发送到 ProtocolShareReserve 合约。

Last updated