Introducción y prerrequisitos
Compound es uno de los protocolos de préstamos más importantes en DeFi, habiendo inspirado el diseño de casi todos los protocolos de préstamos en varias blockchains. Este artículo explica la arquitectura de los contratos inteligentes de V3.
Dado que Compound es un protocolo de préstamos, asumimos que el lector está familiarizado con cómo funcionan las tasas de interés en DeFi y con las liquidaciones y el colateral en el contexto de los préstamos DeFi.
A diferencia de Compound V2, cada instancia de Compound solo presta un activo. Es decir, solo puedes tomar prestado USDC del mercado de USDC y solo puedes tomar prestado ETH del mercado de ETH – no puedes tomar prestado nada más. Para tomar prestados estos activos, necesitas proporcionar colateral de acuerdo con el ratio de colateralización especificado por el mercado (y ese ratio es establecido por la gobernanza).
El activo que se puede tomar prestado se llama activo base en Compound V3. Debido a que USDC es el activo más popular para tomar y dar en préstamo, usaremos USDC como el activo base en nuestros ejemplos prácticos. El contrato inteligente que maneja la lógica central de los préstamos y créditos es denominado Comet en su literatura y código base. Compound tiene instancias en la mainnet de Ethereum y en varias L2s: Polygon, Base y Arbitrum al momento de escribir este artículo. La lista de los mercados de Compound V3 disponibles está aquí.
Este artículo ofrece una descripción general de alto nivel sobre cómo usar este contrato inteligente y la arquitectura de los archivos de Solidity.
Parte 1: Uso de Compound
Hay tres acciones principales que se pueden realizar con Compound V3:
-
prestar el activo base
-
proporcionar colateral y tomar prestado el activo base
-
liquidar préstamos subcolateralizados.
Prestar USDC
El siguiente video muestra las acciones para prestar USDC en la red Polygon (enlace al mercado de USDC en Polygon).
Las personas que participan en el protocolo reciben recompensas en tokens COMP. La cuenta anterior ha acumulado 0.0004 recompensas en COMP hasta ahora, como lo indica la flecha rosa. Las recompensas aún no han sido reclamadas. Además, ha acumulado más de 6 centavos en intereses hasta el momento (4,602.0649 - 4,602.00 = 0.0649).

La discrepancia entre los precios en el recuadro verde y el recuadro amarillo se debe a que USDC no siempre equivale exactamente a $1.00. El gráfico a continuación muestra los recientes precios del oráculo de USDC / USD en Polygon de Chainlink y el lector puede ver que no siempre es exactamente un dólar.

Tomar prestado USDC
El siguiente video muestra el procedimiento para tomar un préstamo. Esta vez la cuenta proporcionó 0.06 ETH ($110) como colateral y tomó prestados $100 de USDC. Esto se hizo en el mercado de préstamos de la L2 BASE.
Al revisar la wallet del navegador, la cuenta ahora tiene 100 Base USDC en ella. USDCbC es USDC puenteado a Base.

El siguiente video muestra al usuario pagando de vuelta el USDC (antes de que se acumularan intereses significativamente) y retirando el colateral en ETH.
Entendiendo las tasas netas de endeudamiento y préstamo

En la captura de pantalla anterior, ten en cuenta que el APR neto de suministro (Net Supply APR, la tasa de interés de préstamo) es más alto que el APR neto de endeudamiento (Net Borrow APR) (círculos amarillos). Esto normalmente no es posible porque los prestamistas no pueden ganar más de lo que los prestatarios están pagando. Compound está incluyendo el valor de las recompensas en COMP dentro del cálculo. Específicamente, los prestatarios actualmente pagan un 6.71% de interés (círculo rojo superior) pero ganan un 2.58% de interés en COMP (círculo azul superior). 6.71 - 2.58 = 4.13%, que es el APR neto de endeudamiento.
De manera similar, los prestamistas actualmente ganan un 6.47% por prestar USDC (círculo rojo inferior), pero también ganan un 4.63% adicional del valor de las recompensas en COMP (círculo azul inferior). La suma de esos dos es 11.10%.
Dado que los prestatarios están pagando un 6.71% de interés en USDC y los prestamistas están cobrando un 6.47% de interés, un 0.24% de interés en USDC se destina al protocolo.
Las tasas de recompensa de COMP cambian según los votos de la gobernanza.
Parte 2: Navegando por el código base
Compound V3 tiene 4,304 líneas de código en Solidity, sin contar los comentarios ni los espacios en blanco.

Arquitectura de Compound V3 vista desde 10,000 pies
A continuación, hemos tomado una captura de pantalla del repositorio de GitHub de Compound V3.
- Todos los archivos resaltados en verde contienen la funcionalidad central de endeudamiento y préstamo. La relación de herencia entre ellos se mostrará más adelante. Comet es el contrato inteligente principal de préstamos y endeudamiento de Compound V3.
- Todos los archivos resaltados en azul forman el contrato inteligente que despliega nuevas instancias de Comet durante una actualización. Nuevamente, la relación de herencia entre ellos se mostrará más adelante.
- El archivo resaltado en rosa es el contrato que distribuye las recompensas.

El siguiente diagrama resume los contratos inteligentes desplegados que conforman Compound V3. Esta es solo una descripción general de alto nivel, más adelante se proporcionará una más detallada. Ten en cuenta que la codificación por colores coincide con los resaltados anteriores. Específicamente, el contrato principal de endeudamiento y préstamo (Comet) es verde, los contratos relacionados con el despliegue de nuevas instancias de Comet son azules y el contrato de recompensas es rosa.

La mayoría de los usuarios interactuarán con Compound V3 a través del proxy de comet (no mostrado en GitHub) o con el contrato de recompensas para ganar COMP por participar como prestamista o prestatario. Toda la lógica orientada al usuario se encuentra en el contrato comet, al cual el proxy de comet delega su funcionalidad.
Los contratos de configuración y fábrica sirven para desplegar nuevas instancias de comet cuando la gobernanza vota por una actualización.
Los contratos inteligentes con un asterisco (*) tienen varios contratos ancestros. En la siguiente sección, mostramos la cadena de herencia para Comet.
Compound V3 tiene un método inusual para actualizar parámetros basándose en la gobernanza
Los modelos de tasas de interés pueden ser cambiados por la gobernanza si las condiciones criptoeconómicas cambian, al igual que los factores de liquidación. Compound almacena toda esta información en variables inmutables, no en el almacenamiento (storage). Para alterar estos valores, se debe desplegar una nueva instancia de Comet, y el proxy se actualiza al nuevo contrato de implementación.
Esta puede parecer una elección de diseño extraña, pero tiene varias ventajas:
- las variables inmutables son mucho más eficientes en gas que las variables de almacenamiento
- Los contratos centrales no necesitan saturarse con funciones setter
Examinaremos el ciclo de vida de un cambio de parámetro más adelante.
Herencia de Comet
Los ancestros de Comet se muestran en el diagrama a continuación. En esta sección daremos una descripción general de alto nivel de cada uno de los contratos ancestros.

CometMath.sol (arriba a la izquierda elipse gris)
Comet math simplemente contiene un conjunto de funciones para castear enteros sin signo a enteros sin signo de menor valor de bits y revertir si esto causara un desbordamiento. Por ejemplo, si hacemos un cast de uint256 a uint104, pero el valor del uint256 es mayor de lo que se puede almacenar en uint104, la transacción se revertirá. Verás funciones como safe64 esparcidas por todo el código base. Con suerte, estas son lo suficientemente fáciles de entender sin mayor explicación. El archivo es pequeño, por lo que lo mostramos en su totalidad aquí:

CometStorage.sol (elipse roja)
Todas y cada una de las variables de almacenamiento utilizadas por Comet están definidas en CometStorage.sol y en ningún otro lugar. Es decir, ningún otro contrato en la cadena de herencia de Comet, además de CometStorage, tiene variables de almacenamiento.
CometCore.sol (segunda fila elipse gris)
CometCore.sol define cómo Compound rastrea e interpreta la acumulación de intereses para prestamistas y prestatarios. También define algunas constantes globales. Profundizaremos más en este archivo cuando hablemos del Valor Principal y el Valor Presente.
CometMainInterface.sol (izquierda elipse azul)
Como su nombre lo indica, CometMainInterface.sol es simplemente un archivo de interfaz, excepto por la peculiaridad de que está definido como un contrato abstracto. Dado que las interfaces se explican por sí mismas, omitimos la discusión sobre esto.
Comet.sol (elipse verde)
Comet.sol es la estrella del espectáculo. Proporciona todas las funciones de cara al público para que los usuarios presten, tomen prestado, paguen y liquiden préstamos.
CometExt es una extensión de Comet mediante delegatecall
Para evitar alcanzar el límite de despliegue de 24 KB, Comet delega varias funciones adicionales a CometExt utilizando el patrón fallback-extension. Por ejemplo, la función name() no está en Comet.sol y, por lo tanto, no se puede ver en Etherscan.
Sin embargo, si llamamos a esa función a través de Foundry cast, podemos ver que el contrato se comporta como si existiera.

Lo que sucede es que esa llamada de función llega a la función fallback y delega la llamada a CometExt, el cual tiene una función llamada name() dentro de él.
Es importante que CometExt respete la disposición del almacenamiento de Comet y lo extienda, sin entrar en conflicto. Esto se logra haciendo que CometExt imite la estructura de herencia de Comet.
Recuerda, la herencia es solo una descripción semántica — cuando los contratos se despliegan, toda la cadena de herencia se convierte en un solo contrato. No es posible que un contrato desplegado herede de otro contrato desplegado.
A continuación se muestra un diagrama más detallado que ilustra la relación entre Comet y CometExt.

Emisión de recompensas
Solo hay un contrato para manejar la emisión de recompensas que no son de intereses, mostrado en el recuadro rosa.

Los prestamistas y prestatarios son recompensados con tokens COMP por su participación en el ecosistema. El contrato Comet hace un seguimiento de la participación, pero no maneja la emisión de recompensas. El contrato Reward lee el estado de Comet y emite los tokens COMP. La tasa a la que se emiten las recompensas son parámetros dentro del contrato Reward que son establecidos por la gobernanza.
Ciclo de vida de una actualización de parámetros
Como se mencionó anteriormente, Comet está parametrizado mediante variables inmutables. Cambiar estos parámetros requiere actualizar el proxy para que apunte a una nueva implementación desplegada por los contratos de Configuración.
En la práctica, cambiar las curvas de las tasas de interés es muy poco frecuente. El contrato en la mainnet solo ha experimentado tres actualizaciones en su curva de tasas de interés:
https://compound.finance/governance/proposals/162 (30 de mayo de 2023)
https://compound.finance/governance/proposals/168 (17 de julio de 2023)
https://compound.finance/governance/proposals/201 (11 de diciembre de 2023)
Las tasas de interés no son los únicos parámetros que pueden cambiar – otros notables incluyen el cambio de los ratios de liquidación e incluso los activos colaterales permitidos. El lector curioso puede revisar las propuestas de gobernanza.
El GIF a continuación muestra cómo está estructurado el contrato inteligente de Configuración y cómo la gobernanza despliega nuevas instancias de Comet con parámetros actualizados.

CometConfiguration.sol y ConfiguratorStorage.sol
CometConfiguration.sol define el struct que parametriza todo el comportamiento de Comet. CometStorage simplemente almacena esos structs.
Si has leído nuestro artículo sobre Cómo funcionan las tasas de interés en DeFi y el Colateral y Liquidaciones en DeFi, entonces es de esperar que los nombres de las variables en el struct se expliquen por sí mismos en su mayoría. ConfiguratorStorage hereda estas definiciones y almacena los structs en mapeos.
Estas variables de almacenamiento no son parte de Comet. El contrato Configurator despliega instancias de Comet utilizando estas configuraciones almacenadas.
A continuación mostramos una captura del código de CometConfiguration y ConfiguratorStorage.

Esta es una forma mucho más preferible de desplegar nuevas instancias de Comet en lugar de proporcionar un struct extremadamente grande en la calldata.
Cuando se despliega una nueva instancia de Comet con una actualización, solo necesitamos actualizar una variable de almacenamiento particular, dejando las demás sin cambios. Por ejemplo, si cambiamos el umbral de liquidación para el colateral wBTC, solo esa variable de almacenamiento en el configurador se verá afectada.
Configurator.sol
El contrato Configurator.sol hereda de ConfigurationStorage y proporciona funciones setter exclusivas para la gobernanza. El archivo Configurator.sol es bastante grande, por lo que no lo mostramos en su totalidad. Sin embargo, con solo mirar los eventos definidos en él, podemos darnos una buena idea de lo que hace el contrato: actualizar campos individuales en el struct que parametriza el despliegue de una nueva instancia de Comet.

Configurator.sol también contiene la función deploy() para desplegar nuevas instancias de Comet.

CometFactory en el código anterior se define en CometFactory.sol y es bastante pequeño, así que mostramos el archivo en su totalidad. La función “clone” es un poco engañosa porque no crea clones proxy – crea una nueva instancia.

Hacemos referencia a la misma animación de antes para unir toda nuestra discusión previa.

Ejemplo real de un cambio de parámetro
Usemos la Propuesta 162 de la gobernanza como ejemplo. Vemos que llamó a algunas funciones setter en el Configurator para actualizar los parámetros, y luego desplegó una nueva instancia de Comet en el paso final (paso 8).

Todo junto
A continuación, mostramos la relación entre todos los archivos y cómo interactúan entre sí. Los archivos de interfaz se omiten.

Aprende más con RareSkills
Por favor, consulta nuestro bootcamp de blockchain para obtener más información.
Publicado originalmente el 3 de enero de 2024