La forma intuitiva de rastrear los depósitos de los prestamistas es registrar la cantidad de USDC que depositaron y el momento en que lo hicieron. Compound V3 no hace esto.
En su lugar, de manera similar al SushiSwap Masterchef Staking Algorithm, Compound V3 rastrea la ganancia hipotética de un dólar prestado “desde el principio de los tiempos”. (Los lectores que aún no estén familiarizados con este algoritmo deberían leer el recurso enlazado).
La ganancia hipotética de un dólar prestado desde el principio de los tiempos se rastrea en el baseSupplyIndex (en CometStorage.sol). Se comporta de manera muy similar al “rewardPerTokenAccumulator” de Sushiswap. Comienza en 1.0 y cada vez que ocurre una operación que cambia el estado (depósito, retiro, préstamo, etc.), se incrementa de forma proporcional al tiempo transcurrido y la tasa de interés durante ese período. Por ejemplo, si pasaron 100 segundos y la tasa de interés fue de 0.001 por segundo (poco realista por lo alta, pero fácil para razonar), entonces baseSupplyIndex se actualizará a 1.1. Específicamente, se utiliza la siguiente fórmula:
baseSupplyIndex += supplyInterestRatePerSecond(utilization) × secondsElapsed
El único lugar donde baseSupplyIndex cambia es en la línea 403 en Comet.sol, dentro de accruedInterestIndices.

Debido a que las tasas de interés son una función directa de la utilización, baseSupplyIndex aumenta más rápido durante los períodos de alta utilización y más lento durante los períodos de baja utilización.
El siguiente gráfico hipotético muestra el baseSupplyIndex y el baseBorrowIndex aumentando a diferentes ritmos dependiendo de la utilización. Generalmente, los prestatarios pagan un interés mayor del que reciben los prestamistas, por lo que el baseBorrowIndex aumenta más rápido.

El siguiente ejemplo ilustra cómo se utiliza esta variable.
Ejemplo y Terminología
Alice deposita $1,000 en un momento en que el baseSupplyIndex es 2.5. No se le acredita haber depositado $1,000, en su lugar se le acredita haber depositado $400, que es su depósito dividido por el baseSupplyIndex actual ($1,000 ÷ 2.5). Alice tiene un “valor principal” de $400 en su cuenta (cuadro amarillo). Este es el valor que Compound almacena para los usuarios en CometStorage.sol.

Si ella retirara de inmediato, Compound calcularía su saldo como $400 multiplicado por el baseSupplyIndex actual, que es 2.5, por lo que retiraría $1,000. Compound llama al valor principal multiplicado por el baseSupplyIndex el “valor presente”.
Compound V3 no “recuerda” que su depósito real original fue de $1,000. Esto está implícito porque el baseSupplyIndex está actualmente en 2.5 y su depósito está registrado como $400.
El valor en dólares “reducido a escala” o “escalado hacia atrás” que almacena Compound V3 se llama el “valor principal”. Cuando multiplicamos el valor principal por el baseSupplyIndex actual, o “escalamos hacia adelante”, obtenemos el “valor presente”.
Los lectores con experiencia en finanzas tradicionales pueden encontrar confuso el uso de los términos “valor principal” y “valor presente” por parte de Compound — sugerimos no intentar relacionar los términos con sus significados tradicionales y simplemente aceptar el uso que les da Compound.
Si ella esperara hasta que el baseSupplyIndex aumentara a 3.0, el valor principal seguiría siendo $400, pero el valor presente aumentaría a $1,200 ($400 x 3.0 = 1,200).

En CometCore.sol, vemos que:
-
el “valor principal” se calcula dividiendo el “valor presente” por el
baseSupplyIndex -
el “valor presente” se calcula multiplicando el “valor principal” por el
baseSupplyIndex.

Así que, para resumir los términos importantes:
Valor principal
El valor principal es el USDC depositado dividido por el valor del baseSupplyIndex en el momento del depósito. Este se mantiene en una variable de almacenamiento asociada con la cuenta del usuario y no cambia a menos que el usuario deposite o retire. El valor principal es generalmente menor que el depósito real porque baseSupplyIndex siempre es mayor o igual a 1.
Valor presente
El valor presente es el valor principal multiplicado por el valor actual del baseSupplyIndex. Este valor no se almacena en ninguna parte, sino que se calcula sobre la marcha.
El valor principal y el valor presente son dos de los conceptos más críticos para entender Compound V3.
El valor principal es la única variable que se rastrea para los prestamistas
Revisemos el struct del que tomamos captura de pantalla arriba:

La variable baseTrackingAccrued es utilizada por CometRewards para determinar cuánto COMP otorgar a la cuenta por su participación en el protocolo. La variable baseTrackingIndex está relacionada con eso, pero actualmente no se utiliza. La variable assetsIn solo se utiliza para los prestatarios como un indicador de si se utilizan ciertos activos colaterales. La variable _reserved no se utiliza.
Por lo tanto, principal es la única variable esencial para la contabilidad del prestamista. Ten en cuenta que esta es una variable con signo — para los prestatarios es negativa. Para los prestatarios, también necesitamos rastrear cuánto colateral han depositado, pero eso es tema para otro artículo.
balanceOf() — consultando el saldo positivo del prestamista
Para ilustrar que Compound V3 almacena el valor principal, pero valora la cuenta en el valor presente, considera la función balanceOf() en Comet.sol que se muestra a continuación.
Primero, leerá el baseSupplyIndex actualizado sin modificarlo, ya que se trata de una función de vista (view function). Luego lee el saldo principal del prestamista y lo multiplica por el baseSupplyIndex.
El interés acumulado es una función del tiempo y la utilización, por lo que mientras la utilización no sea cero, cada vez que se consulte balanceOf, devolverá un valor más alto.

La siguiente captura de pantalla muestra la relación entre el saldo del prestamista en la dapp de Compound V3 y el valor de retorno de balanceOf en Etherscan superpuesto para esa misma dirección. Ambos valores están resaltados en un cuadro naranja.

Ejemplo Visual de baseSupplyIndex
Cuando un prestamista deposita, el saldo en USDC de su depósito se divide por el baseSupplyIndex para producir el valor principal, que es lo que se almacena. Cuanto más tarde depositen, mayor será el baseSupplyIndex y se les acreditará un valor principal más bajo.
El valor principal es estático (a menos que depositen o retiren), pero el valor presente es el valor principal * baseSupplyIndex, y ese valor aumenta constantemente.
En el gráfico a continuación, Alice depositó $1 cuando el baseSupplyIndex era igual a 1.01, por lo que su valor principal es 0.99 (1 ÷ 1.01). Bob también depositó $1, pero en un momento posterior cuando el baseSupplyIndex tenía un valor de 1.03 (1 ÷ 0.97). Por lo tanto, su valor principal fue de 0.97.
Tanto Bob como Alice depositaron la misma cantidad: $1. Pero debido a que Bob depositó más tarde, su valor principal es más bajo.

supplyBase() y withdrawBase()
Cuando un usuario suministra (o retira) el activo base, el valor principal se restablece. Por ejemplo, si Alice depositó $10 cuando el baseSupplyIndex era 10, su valor principal sería de $1. Ahora supongamos que en un momento en que el índice baseSupplyIndex aumenta a 20, su valor presente para su cuenta sería de $20. Ella agrega $10 más. Su nuevo valor presente debería ser de $30.
¿Cuál debería ser su valor principal? (¡Piénsalo por un segundo!)
El baseSupplyIndex es actualmente 20, el valor presente es actualmente $30, por lo que su valor principal debería ser $1.5 ($30 ÷ 20).
Con este ejemplo en mente, mostramos la función supplyBase() de Comet.sol línea 829 que se llama cuando un prestamista deposita.

Cuando Alice deposita, leemos su valor principal (cuadro naranja), lo convertimos al valor presente (cuadro verde), y le sumamos la cantidad recién depositada (cuadro azul). Esto se convierte de un valor presente a un valor principal almacenado en dstPrincipalNew (cuadro amarillo) el cual guardamos para que sea el nuevo valor principal para ese usuario (cuadro rojo)
La función updateBasePrincipal() (cuadro rojo) de arriba simplemente actualiza el struct UserBasic asociado con el usuario, sobrescribiendo el antiguo principal con dstNewPrincipal.
La función opuesta, withdrawBase() (Comet.sol línea 1051), aplica la misma lógica a la inversa.
¿Qué pasa con baseBorrowIndex?
De manera similar a cómo baseSupplyIndex rastrea la ganancia de un dólar prestado desde el principio de los tiempos, baseBorrowIndex rastrea la acumulación de deuda por un dólar tomado prestado desde el principio de los tiempos. Los prestamistas y prestatarios tienen diferentes curvas de tasas de interés, por lo que se necesitan dos variables separadas para rastrearlos.
¿Cuándo se desbordarán los índices?
Tanto baseSupplyIndex como baseBorrowIndex se tratan como números de punto fijo con 15 decimales, por lo que 1e15 se trata como 1.0. El número más grande que puede contener un número de 104 bits con signo es 1.014e31. Por lo tanto, el número más grande que puede contener el acumulador es 1.014e16 (con 15 decimales).
Supongamos que el protocolo de préstamos nunca superará el 100% de APR. Tomaría 53 años para que el índice se desborde. Usando una tasa de interés más realista del 10%, tomaría 386 años para que un dólar se capitalice a 10 billones de dólares.
Es bastante razonable asumir que Compound se actualizará a la versión 4 antes de que llegue ese día.
Aprende Más con RareSkills
Por favor, consulta nuestro Solidity Bootcamp para aprender conceptos más avanzados de Solidity.
Publicado originalmente el 5 de enero de 2024