El contrato de Compound V3 se comporta como un token ERC-20 de rebase (rebasing token). Un token de rebase es un token que tiene un suministro ajustado algorítmicamente en lugar de uno fijo. El “token” aquí representa el valor presente (present value) de los saldos positivos de USDC. Es decir, los prestamistas pueden transferir el valor presente de su capital principal a otras direcciones como si fuera un token ERC-20. Dado que el valor del capital generalmente aumenta debido a la acumulación de intereses, este token ERC-20 realiza un rebase al alza con el tiempo.
Compound V3 no utiliza un estándar de bóveda de tokens (p. ej. ERC-4626) para rastrear las “acciones” del pool de préstamos.
Como señalamos en nuestra discusión sobre el capital y el valor presente, un usuario puede haber depositado 100 USDC pero tener un crédito de 110 USDC debido a los intereses acumulados — los 110 son el valor presente. Es esta unidad de cuenta la que gestiona la funcionalidad ERC-20 de Compound V3.
Requisitos previos
Los usuarios deben estar familiarizados con los índices de interés y la noción de Compound V3 de valor presente y valor del capital. Los términos Compound V3 y Comet se usan indistintamente en este artículo ya que Comet es el nombre del contrato inteligente principal que tiene las funciones que discutimos aquí.
Estructura de este artículo
Cada encabezado discutirá una función ERC-20 que implementa Compound V3, y cómo implementa dicha función. Algunas de las funciones no son parte del estándar ERC-20 pero son relevantes para esta discusión.
totalSupply y totalBorrow (Comet.sol)
El totalBorrow, como su nombre lo indica, es la cantidad total de USDC tomado en préstamo. Es decir, es el valor presente de la deuda. Podemos corroborar eso con lo que devuelve el contrato y lo que vemos en la plataforma de Compound. Esta no es solo la cantidad de USDC que los prestatarios retiraron de la plataforma — incluye los intereses acumulados sobre los USDC tomados en préstamo.
A continuación mostramos una captura de pantalla de la interfaz de usuario de Compound V3 que muestra este valor y Etherscan devolviendo el resultado de la función totalBorrow().

De manera similar, totalSupply() no es la cantidad de USDC que los prestamistas depositaron en Compound — es el valor presente de los depósitos totales.
El código de totalSupply y totalBorrow en la captura de pantalla a continuación debería aclarar la relación con el valor presente.

A continuación, hemos tomado captura de pantalla de dos consultas de totalSupply. Note que el totalSupply ha aumentado en la segunda captura de pantalla a la derecha.

La función totalSupply() se comporta como totalSupply() de ERC-20.
Los prestatarios no pueden transferir deuda, por lo que totalBorrow no se utiliza para ninguna interfaz de tipo token.
balanceOf (Comet.sol)
BalanceOf ya se cubrió en nuestra discusión sobre el capital y el valor presente, por lo que no la repetiremos aquí.
transfer y transferFrom (Comet.sol)
Tanto transfer como transferFrom transferirán el valor presente del prestamista. El parámetro amount se mide en valor presente.

Ambas funciones llaman a transferInternal bajo el capó. La forma correcta de transferir el saldo completo en Compound V3 es transferir el valor máximo de uint256. Especificar el saldo completo puede ser un poco complicado porque aumenta cada segundo.

Tenga en cuenta que no hay un mecanismo para que los prestatarios transfieran garantías a otras direcciones, ya que transferCollateral es solo interna. Esta función se utiliza para liquidaciones.
El resto de las funciones ERC-20 se encuentran en CometExt.sol
Debido al límite de despliegue de 24 KB, Comet separa parte de su funcionalidad en CometExt.sol utilizando el patrón de extensión fallback. La mayoría de las funciones en CometExt están relacionadas con la funcionalidad ERC-20.
approve (CometExt.sol)
La funcionalidad approve() para cUSDCv3 no es estándar ya que solo acepta type(uint256).max o cero. Dado que el saldo de una cuenta cambia constantemente debido al rebase, no es posible otorgarle a alguien una asignación exactamente por la totalidad del saldo, ya que el saldo sigue aumentando a medida que se acumulan los intereses.

approve() llama a allowInternal bajo el capó, lo cual vale la pena examinar por separado
allow() y allowInternal()
La función allow() no es parte de ERC-20, pero se comporta de la misma manera que approve() en el sentido de que le otorga a una dirección la asignación máxima. Tanto approve() como allow() usan allowInternal() bajo el capó para lograr otorgar a una dirección la asignación máxima.
Dado que approve es esencialmente binaria, allow se comporta como approve, excepto que toma un argumento booleano para otorgar la aprobación por el valor total en lugar de un uint256.

La variable de almacenamiento de las “asignaciones” se mantiene en CometStorage.sol como isAllowed.

La asignación es todo o nada en Compound V3. Es por eso que approve solo acepta el valor máximo de uint256. No hay una variable de almacenamiento para guardar la asignación como un número de la forma en que lo hacen los tokens ERC-20 tradicionales.
allowance (CometExt.sol)
La asignación es binaria, solo se tiene aprobación para el valor máximo de uint256 o cero. Cualquier otro valor revertirá.
hasPermission simplemente devuelve un valor booleano que indica que una dirección tiene aprobaciones ilimitadas o ninguna en absoluto.
allowBySig() es una función permit() de ERC-20 no estándar
CometStorage expone mapping(address => uint256) public userNonce como una variable pública en lugar del nonces(address owner) external returns (uint) especificado por el EIP 2612.
name() y symbol() (CometExt.sol)
Las funciones name() y symbol() son funciones ERC-20 opcionales que devuelven strings.
CometExt.sol no almacena estos valores en variables de tipo string, sino en variables bytes32 inmutables por motivos de eficiencia de gas. Solidity no permite convertir directamente de bytes32 a strings, por lo que las variables inmutables se convierten a strings sobre la marcha usando el código a continuación.
Un detalle menos conocido sobre los tipos de datos bytes1, bytes2, …, y bytes32 en Solidity es que pueden ser indexados a nivel de byte como los arreglos de bytes. Por ejemplo,
contract Example {
bytes32 immutable x = 0x3300000000000000000000000000000000000000000000000000000000000000;
function main() external pure returns (bytes1) {
return x[0]; // returns 0x33
}
}
CometExt inicializa un arreglo de bytes en memoria y copia los caracteres almacenados en las variables inmutables bytes32 name32 y symbol32, para luego convertirlos en un string.

Llamando a funciones en CometExt
El ABI conocido por Etherscan no conoce estas funciones, por lo que no aparecerán en la lista de funciones de Comet. Sin embargo, pueden ser llamadas ya que alcanzarán el fallback de Comet y serán delegadas al contrato CometExt.
Aquí hay un ejemplo llamando a name() y symbol() usando cast de Foundry.

Conclusión
CometV3 se comporta como un token ERC-20 de rebase que representa los saldos positivos de los prestamistas. Este saldo positivo se puede transferir a otras direcciones como un token ERC-20 normal. La función approve() no es estándar — solo puede hacer una aprobación infinita o ninguna en absoluto. De manera similar, la función permit() para aprobaciones sin gas es no estándar.
Aprende más con RareSkills
Consulta nuestro bootcamp de blockchain para obtener más información.
Publicado originalmente el 7 de enero de 2024