本文详细介绍了Compound V3奖励机制,重点分析了与MasterChef Staking算法的相似性及不同之处。文章涵盖了奖励的计算方式、合约结构、用户奖励的索取机制以及潜在的安全隐患,并通过多个截图和示例说明了相关的参数与算法逻辑,深入探讨了Compound系统中奖励的逐步累积与发放过程。
Compound issues rewards in COMP tokens to lenders and borrowers in proportion to their share of the a market’s lending and borrowing.
该算法与 MasterChef Staking Algorithm 非常相似,因此读者应该先熟悉这个算法。
与 MasterChef 类似,Compound V3 奖励合约跟踪一个假设的“质押” USDC 自创立以来赚取了多少。在这里,“质押”可以意味着借款或贷款——这两种活动都会获得奖励。
类似于 Compound V3 的 baseSupplyIndex
或 MasterChef 的 rewardPerTokenAcc
,Compound 奖励有 trackingSupplyIndex
和 trackingBorrowIndex
,分别跟踪自创立以来每个 USDC(借出或借入)获得的奖励。
像 MasterChef 一样,单个 USDC 收集到的奖励在更多 USDC 被“质押”时会“稀释”,反之亦然。
与 MasterChef 不同的是,单位时间的奖励是通过不可变变量 baseSupplyTrackingSupplySpeed
和 baseTrackingBorrowSpeed
设置的。治理有权“重新调整”分发的奖励。
用户从 Comet Rewards 中索取奖励,该合约与主要的 Comet 借贷合约是分开的。
如果借款总额或贷款总额低于某些阈值,Compound 不会发放奖励,原因我们将在稍后讨论。
与 MasterChef 一样,奖励不会自动分配,必须在单独的交易中声明。以下截图显示了索取累计的 COMP 代币的前端,标出索取代币的操作(黄色圆圈)。
Comet Rewards 合约不生成 COMP 代币,而是依赖于治理将代币转移到它那里。当前流通中总共有 1000 万个 COMP 代币,所有代币都已铸造。很大一部分供应量由治理持有。定期,COMP 代币从治理金库转移到奖励合约。你可以看到以下“补充”奖励合约的治理交易。
<https://compound.finance/governance/proposals/194> (2023年11月21日)
<https://compound.finance/governance/proposals/164> (2023年6月29日)
奖励合约的主网地址为
0x1B0e765F6224C21223AeA2af16c1C46E38885a40
由于供应量固定,生态系统参与的 COMP 奖励无法无限期继续,除非治理在公开市场上购买 COMP 代币。
在下面的 Etherscan 截图中,我们看到与合约的大部分交易是索取 COMP 代币(蓝色框),并且该合约当前持有 ~73,000 个 COMP 代币(蓝色箭头)。
下面的图应该是 MasterChef 中熟悉的。更多的 USDC 被“质押”时,每个代币获得的奖励就越少,因为每个周期(由 trackingSupplyIndex
和 trackingBorrowIndex
决定)只发行固定的金额。
一个值得注意的变化是,如果供应或借用的 USDC 金额(粉色线)低于baseMinForRewards
(红色文字和虚线)阈值,则 USDC 不会累积奖励,并且在该状态更新中 trackingSupplyIndex
(或 trackingBorrowIndex
)不会增加。
这些变量不是公开的,但可以通过 CometExt 中的 totalsBasic()
公共函数检索。由于 CometExt 是一个单独的合约,Comet 向其发出委托调用,我们无法通过 Etherscan 获取这些值。相反,我们使用来自 Foundry 的 cast 来检索它们,如下截图所示。
baseMinForRewards 在 Comet.sol 的第 86 行 中定义。
如果借出金额少于 100 万美元(1e12 USDC,因为 USDC 有 6 位小数),Compound 不会向贷款者或借款人发放奖励。同样,如果借用的 USDC 少于 100 万美元,则不会累积 COMP 奖励。
请记住,按代币累积的奖励与质押的代币数量呈反比。如果质押的总代币数量较小,则累积器将快速累积,可能溢出过快。
如果你是审计员,这可能是一个相对被忽视的中等漏洞,因为测试不容易捕捉累积器溢出。你需要确保累积器几年来不会溢出,这意味着要么奖励率需要较小,要么质押的数量需要较大。
每当调用 accrueInternal()
时,trackingSupplyIndex
和 trackingBorrowIndex
会被更新。
下面的代码实现了上述部分描述的逻辑。红框中的 if
条件防止 trackingSupplyIndex
或 trackingBorrowIndex
在供应或借款金额低于 baseMinForRewards
时累积更多奖励。baseTrackingSupplySpeed
和 baseTrackingBorowSpeed
(蓝框)是不可变变量,因此索引的增量仅取决于 timeElapsed
和(反比于)totalSupplyBase
(或 totalBorrowBase
)。
你可以将 baseTrackingSupplySpeed
和 baseTrackingBorrowSpeed
视为“单位时间内的奖励”。当乘以 timeElapsed
时,此计算得出单个参与的 USDC 累积的奖励总额。最后,将结果除以 totalSupplyBase
或 totalBorrowBase
,就基于总量稀释了该 USDC。
这些变量类似于 MasterChef 的 rewardPerBlock
。它们指定上述部分描述的累积器增加的速度。
我们可以从 Comet Etherscan 代理合约 中检索它们的值。
这两个变量都使用 trackingIndexScale 作为其小数位,而 根据 Etherscan,trackingIndexScale 为 1e15:
由于它们是 15 位小数的 定点数,其值如下:
baseTrackingSupplySpeed = 2.979166666666e-03
baseTrackingBorrowSpeed = 4.414467592592e-03
从 Comet.sol 截图显示的变量定义(带有关于小数位的注释)如下:
像 MasterChef 一样,Compound Rewards 在账户进行状态更改交易时会累积奖励。同样像 MasterChef,用户累积的奖励与其余额及“索引”或“累积器”自上次执行状态更改操作以来的变化成正比。
让我们再次看看用户结构体:
baseTrackingIndex
是在 UserBasic 存储结构最后一次更新时 trackingSupplyIndex
或 trackingBorrowIndex
的值,具体取决于账户是借款者还是贷款者。当前的 trackingSupplyIndex
(或 trackingBorrowIndex
)与用户存储的 baseTrackingIndex
之间的差额决定了他们在该交易中将获得多少奖励。请看下面的图示:
每当用户执行将更改其本金的操作时,会调用内部函数 updateBasePrincipal()
。该函数将确定自上次更新以来 trackingSupplyIndex
或 trackingBorrowIndex
的变化量,并相应地将奖励累积到用户的 baseTrackingAccrued
中。该函数如下:
总之,baseTrackingIndex
是用户上次更新时的索引值。baseTrackingAccrued
是用户参与协议以来,因其过去的索赔(在奖励合约中记录的奖励债务)而减轻的总奖励。
在上面的代码中,我们看到用户的累积奖励被除以 accrualDescaleFactor
。
这使得 ETH 和 USDC 能在相同的尺度上进行跟踪。由于 ETH 有 18 位小数而 USDC 有 6 位小数,将 ETH 的 baseTrackingAccrued 除以 1e12,使其有效上与 USDC 具有相同的“小数”数量。这样可以使 baseTrackingAccrued
在相同的尺度上跟踪这两种资产。
要索取奖励,用户只需在 CometReward.sol 中调用 claim()
函数。rewardsClaimed
映射(红框)像 MasterChef 中的 rewardDebt。
如果有人在交易中唯一执行索取奖励的操作,则 shouldAccrue
(绿色框)应为 true
。然而,如果在其他函数调用之后,则其他状态更改函数将调用 accrueAccount()
,使得另一次调用不必要。
在上面的蓝框中,getRewardAccrued
用于确定支付用户的金额。它简单地查询 Comet 中用户结构体的 baseTrackingAccrued
。随后 CometRewards 从中减去其奖励债务(rewardsClaimed
),并将差额支付给用户。
奖励合约分发的 COMP 代币 并不像大多数 ERC 20 代币那样将余额存储为 uint256,而是存储为 uint96。
如果你尝试 transfer
或 approve
超过 uint96 最大值的金额,则交易将会回滚。
请查看我们的 solidity bootcamp 以了解更多高级智能合约开发。
原文发表于 2024年1月10日
- 原文链接: rareskills.io/post/compo...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!