cUSDC V3(Compound V3)作为非标准Rebasing代币,CometExt.sol

文章详细介绍了Compound V3合约的行为,它类似于一种可调整供应量的ERC 20代币,即借贷平台的USDC余额的现值可以被转移。文章还讨论了Compound V3实现的ERC 20函数及其工作原理。

The Compound V3 合约表现得像一个重基 ERC 20 代币。重基代币是指具有算法调整供给而不是固定供给的代币。这儿的“代币”表示正的 USDC 余额的现值。也就是说,贷方可以将其本金的现值转移到其他地址,就如同它是一个 ERC 20 代币一样。由于本金的价值通常因利息累积而增加,这个 ERC 20 代币的价值随着时间的推移而向上重基。

Compound V3 不使用代币保管标准(例如 ERC-4626)来追踪借贷池的“份额”。

正如我们在讨论本金和现值时指出的,用户可能已经存入 100 USDC 但因利息累积而获得 110 USDC 的信用——这 110 代表现值。正是这个单位的账目由 Compound V3 的 ERC20 功能管理。

前提条件

用户必须熟悉利息指数和 Compound V3 的 现值和本金价值的概念。由于 Comet 是我们在这里讨论的主要智能合约的名称,本文中将 Compound V3 和 Comet 交替使用。

本文结构

每个标题将讨论 Compound V3 实现的一个 ERC 20 功能,以及它如何实现该功能。一些功能并不是 ERC 20 标准的一部分,但对本讨论是相关的。

totalSupply 和 totalBorrow (Comet.sol)

totalBorrow,顾名思义,是借入的 USDC 总额。也就是说,它是债务的现值。我们可以通过合约返回的结果和我们在 Compound 平台上看到的来证实这一点。这不仅仅是借款人从平台提取的 USDC 数量——它还包括借入的 USDC 上累积的利息。

下面我们展示一张 Compound V3 用户界面的截图,显示了这个值,以及 Etherscan 返回的 totalBorrow() 函数的结果。

total borrowing screenshot compound v3

类似地,totalSupply() 并不是贷方存入 Compound 的 USDC 数量——而是 现值 的总存款。

下面截图中的 totalSupply 和 totalBorrow 代码 应该能帮助你清晰理解现值之间的关系。

totalSupply\(\) 和 totalBorrow\(\) 函数

下面我们截图了两次对 totalSupply 的查询。请注意,右侧第二张截图中的 totalSupply 已经增加。

totalSupply\(\) increasing

totalSupply() 函数的行为与 ERC 20 的 totalSupply() 相同。

借款人无法转移债务,因此 totalBorrow 不用于任何代币类接口。

balanceOf (Comet.sol)

balanceOf 在我们关于本金和现值的讨论中已经涵盖,因此在此不再赘述。

transfer 和 transferFrom (Comet.sol)

transfer 和 transferFrom 都会转移贷方的现值。金额参数以现值来测量。

transfer\(\) 和 transferFrom\(\)

这两个函数在内部都会调用 transferInternal。在 Compound V3 中,转移整个余额的正确方法是转移 uint256 的最大值。指定整个余额可能有些棘手,因为它每秒钟都会增加。

transferInternal\(\)

请注意,借款人没有将抵押品转移到其他地址的机制,因为 transferCollateral 仅是内部的。此函数用于清算。

剩余的 ERC 20 函数在 CometExt.sol 中

由于 24 kb 的部署限制,Comet 将其部分功能拆分到 CometExt.sol 中,使用了 fallback 扩展模式。CometExt 中的大多数功能与 ERC 20 功能相关。

approve (CometExt.sol)

cUSDCv3 的 approve() 功能是非标准的,因为它只接受 type(uint256).max 或零。由于账户余额因重基而不断变化,因此不可能为某个地址给予正好是整个余额的授权,因为随着利息的累积,余额会不断增加。

approve\(\)

approve() 在内部调用 allowInternal,这一点值得单独考察。

allow() 和 allowInternal()

allow() 函数不是 ERC20 的一部分,但它的行为与 approve() 类似,都是给一个地址最大授权。approve() 和 allow() 在内部都使用 allowInternal() 来实现对一个地址最大授权。

因为 approve 实际上是二元的,allow 的行为类似于 approve,只是它接受一个布尔参数以对全值进行批准,而不是 uint256。

allow\(\) 和 allowInternal\(\) 函数

“allowances” 存储变量保存在 CometStorage.sol 中,称为 isAllowed

isAllowed\(\) 存储变量

在 Compound V3 中,授权是全有或全无的。这就是为什么 approve 只接受最大 uint256 值的原因。与传统的 ERC 20 代币不同,这里没有存储授权作为数字的存储变量。

allowance (CometExt.sol)

授权是二元的,你只有对最大 uint256 值或零的批准。其他任何值都会导致 revert。

hasPermission 仅仅返回一个布尔值,表示一个地址是否拥有无限的审批或完全没有。

allowBySig() 是一个非标准的 ERC 20 permit() 函数

CometStorage 将 mapping(address => uint256) 公共 userNonce 作为公共变量暴露,而不是 EIP 2612 中指定的 nonces(address owner) external returns (uint)

name() 和 symbol() (CometExt.sol)

name()symbol() 函数是可选的 ERC 20 函数,返回字符串。

CometExt.sol 并不将这些值存储在字符串变量中,而是为节省 Gas 而将其存储在不可变的 bytes32 变量中。Solidity 不允许直接将 bytes32 转换为字符串,因此不可变变量在运行时通过下面的代码转换成字符串。

关于 Solidity 中的 bytes1bytes2,...,和 bytes32 数据类型不为人知的细节是,它们可以像字节数组一样按字节级别进行索引。例如:

contract Example {
    bytes32 immutable x = 0x3300000000000000000000000000000000000000000000000000000000000000;

    function main() external pure returns (bytes1) {
        return x[0]; // 返回 0x33
    }
}

CometExt 在内存中初始化一个字节数组,并复制 name32symbol32 不可变变量中存储的字符,然后将其转换为字符串。

name\(\) 和 symbol\(\) 函数

在 CometExt 中调用函数

Etherscan 知道的 ABI 不知道这些函数,因此它们不会出现在 Comet 函数列表中。然而,由于它们将调用 Comet 的回调,它们可以被调用并最终转到 CometExt 合同。

以下是使用 Foundry 的 cast 调用 name()symbol() 的示例。

cast call foundry

结论

CometV3 的行为类似于一个重基 ERC 20 代币,表示贷方的正余额。这个正余额可以像普通的 ERC 20 代币一样转移到其他地址。approve() 函数是非标准的——它只能做无限的批准或根本不做。类似地,用于无Gas批准的 permit() 函数也是非标准的。

在 RareSkills 了解更多

查看我们的区块链训练营以获取更多信息。

最初发布于 2024 年 1 月 7 日

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

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/