Compound emite recompensas en tokens COMP a prestamistas y prestatarios en proporción a su participación en los préstamos y créditos de un mercado.
El algoritmo es extremadamente similar al MasterChef Staking Algorithm, por lo que el lector debería familiarizarse con él primero.
Descripción general de alto nivel de las recompensas de Compound V3
De manera similar a MasterChef, el contrato de recompensas de Compound V3 rastrea cuánto ha ganado un hipotético USDC “en staking” desde el principio de los tiempos. Aquí, “hacer staking” puede significar tanto pedir prestado como prestar; ambas actividades son recompensadas.
De forma análoga al baseSupplyIndex de Compound V3 o al rewardPerTokenAcc de MasterChef, las recompensas de Compound tienen trackingSupplyIndex y trackingBorrowIndex que rastrean las recompensas para un USDC (prestado o tomado en préstamo) desde el principio de los tiempos.
Al igual que en MasterChef, la cantidad de recompensas que un solo USDC recauda se “diluye” cuando se hace “staking” de más USDC y viceversa.
A diferencia de MasterChef, la recompensa por unidad de tiempo se establece con las variables inmutables baseSupplyTrackingSupplySpeed y baseTrackingBorrowSpeed. La Gobernanza tiene la opción de “reescalar” las recompensas que se distribuyen.
Los usuarios reclaman recompensas de Comet Rewards, un contrato separado del contrato principal de préstamos de Comet.
Compound no emite recompensas si la cantidad total prestada o la cantidad total tomada en préstamo están por debajo de ciertos umbrales por razones que discutiremos más adelante.
Al igual que en MasterChef, las recompensas no se distribuyen automáticamente, deben ser reclamadas en una transacción separada. La captura de pantalla a continuación muestra el frontend para reclamar tokens COMP acumulados, con la acción para reclamar los tokens en el círculo amarillo.

El contrato Comet Rewards requiere recargas ocasionales
El contrato Comet Rewards no acuña tokens COMP, depende de que la Gobernanza le transfiera tokens. Hay un suministro total de 10 millones de tokens COMP en circulación y todos ellos ya han sido acuñados. Una cantidad significativa del suministro está en manos de la gobernanza. Periódicamente, se transfieren tokens COMP desde la tesorería de la gobernanza al contrato de recompensas. Puedes ver las siguientes transacciones de gobernanza que “recargan” el contrato de recompensas.
https://compound.finance/governance/proposals/194 (Nov 21, 2023)
https://compound.finance/governance/proposals/164 (June 29, 2023)
La dirección en mainnet para el contrato de recompensas es
0x1B0e765F6224C21223AeA2af16c1C46E38885a40
Debido a que hay un suministro fijo, las recompensas de COMP por la participación en el ecosistema no pueden continuar indefinidamente a menos que la gobernanza compre tokens COMP en el mercado abierto.
En la captura de pantalla de Etherscan a continuación vemos que la mayoría de las transacciones con el contrato son para reclamar tokens COMP (cuadro azul), y que el contrato actualmente tiene ~73,000 tokens COMP (flecha azul).

trackingSupplyIndex y trackingBorrowIndex se comportan como rewardPerTokenAcc
El gráfico a continuación debería resultar familiar por MasterChef. Cuantos más USDC haya “en staking”, menos recompensa recibirá cada token porque solo se emite una cantidad fija en cada período (determinada por trackingSupplyIndex y trackingBorrowIndex).
Una variación digna de mención es que si la cantidad de USDC suministrada o prestada (línea rosa) está por debajo del umbral de baseMinForRewards (texto rojo y línea discontinua), entonces el USDC no acumula recompensas, y el trackingSupplyIndex (o trackingBorrowIndex) no aumenta para esa actualización de estado.

Estas variables no son públicas, pero se pueden recuperar a través de la función pública totalsBasic() en CometExt. Dado que CometExt es un contrato separado al que Comet emite delegatecalls, no podemos recuperar los valores a través de Etherscan. En su lugar usamos cast de Foundry para recuperarlos, como muestra la captura de pantalla a continuación.

baseMinForRewards
baseMinForRewards se define en la línea 86 de Comet.sol

Compound no emite recompensas a prestamistas o prestatarios si hay menos de 1 millón de dólares (1e12 USDC ya que USDC tiene 6 decimales) prestados. Del mismo modo, un USDC tomado en préstamo no acumulará recompensas de COMP si hay menos de 1 millón de dólares prestados.

baseMinRewards existe para prevenir el desbordamiento del acumulador
Recuerda que la recompensa acumulada por token es inversamente proporcional a la cantidad de tokens en staking. Si el suministro total de tokens en staking es pequeño, el acumulador acumulará rápidamente, desbordándose posiblemente demasiado rápido.
Si eres un auditor, esta puede ser una vulnerabilidad media bastante pasada por alto porque las pruebas no detectan fácilmente los desbordamientos del acumulador. Necesitas asegurarte de que el acumulador no se desborde durante varios años y esto significa que la tasa de recompensa debe ser pequeña o la cantidad en staking debe ser grande.
accrueInternal() revisado
El trackingSupplyIndex y el trackingBorrowIndex se actualizan cada vez que se llama a accrueInternal().
El código a continuación implementa la lógica descrita en las secciones anteriores. Las condiciones if en los cuadros rojos evitan que trackingSupplyIndex o trackingBorrowIndex acumulen más recompensas si la cantidad suministrada o prestada está por debajo de baseMinForRewards. El baseTrackingSupplySpeed y el baseTrackingBorrowSpeed (cuadros azules) son variables inmutables, por lo que la cantidad en la que se incrementan los índices solo depende de timeElapsed e (inversamente) de totalSupplyBase (o totalBorrowBase).

Puedes pensar en baseTrackingSupplySpeed y baseTrackingBorrowSpeed como la “recompensa por unidad de tiempo”. Al multiplicarse por timeElapsed, se calcula la cantidad de recompensas acumuladas para un solo USDC participante. Finalmente, dividir ese resultado entre totalSupplyBase o totalBorrowBase diluye ese USDC en base a la cantidad total.
baseSupplyTrackingSpeed y baseTrackingBorrowSpeed
Estas variables son análogas al rewardPerBlock de MasterChef. Especifican qué tan rápido aumentan los acumuladores descritos en la sección anterior.
Podemos recuperar sus valores desde el contrato Proxy de Comet en Etherscan.

Ambas variables usan trackingIndexScale para sus decimales y, según Etherscan, trackingIndexScale es 1e15:

Dado que son números de punto fijo de 15 decimales, sus valores son los siguientes:
baseTrackingSupplySpeed = 2.979166666666e-03
baseTrackingBorrowSpeed = 4.414467592592e-03
Las definiciones de las variables (con comentarios sobre la escala) de Comet.sol se muestran en la captura de pantalla a continuación:

Rastreando recompensas a nivel de usuario: baseTrackingAccrued y baseTrackingIndex
Al igual que MasterChef, las recompensas de Compound acumulan recompensas para una cuenta cuando esa cuenta realiza una transacción que cambia el estado. Y también como en MasterChef, las recompensas que acumula el usuario son proporcionales a su saldo y a cuánto cambió el “índice” o “acumulador” desde la última vez que el usuario realizó una operación de cambio de estado.
Veamos nuevamente el struct del usuario:

baseTrackingIndex es el valor de trackingSupplyIndex o trackingBorrowIndex en el momento en que se actualizó por última vez el struct de almacenamiento UserBasic, dependiendo de si la cuenta es prestamista o prestataria respectivamente. El delta entre el trackingSupplyIndex (o trackingBorrowIndex) actual y el valor almacenado por el usuario de baseTrackingIndex determina cuántas recompensas acumularán para esa transacción. Considera el gráfico a continuación:

Cada vez que un usuario hace algo que cambiará su principal, se hace una llamada a la función interna updateBasePrincipal(). La función determinará cuánto ha cambiado el trackingSupplyIndex o el trackingBorrowIndex desde la última actualización y acumulará las recompensas en el baseTrackingAccrued del usuario en consecuencia. La función se muestra a continuación:

En resumen, baseTrackingIndex es el valor del índice cuando el usuario se actualizó por última vez. baseTrackingAccrued es el total de las recompensas adeudadas al usuario desde que participó en el protocolo, independientemente de los reclamos pasados, que se anulan con la deuda de recompensas rastreada en el contrato de recompensas.
¿Qué es accrualDescaleFactor?
En el código anterior, vemos que las recompensas acumuladas del usuario se dividen entre accrualDescaleFactor.
Esto permite que tanto ETH como USDC se rastreen en la misma escala. Dado que ETH tiene 18 decimales y USDC tiene 6 decimales, el baseTrackingAccrued de ETH se divide entre 1e12 para que efectivamente tenga el mismo número de “decimales” que USDC. Esto permite a baseTrackingAccrued rastrear ambos activos en la misma escala.
Reclamando recompensas
Para reclamar las recompensas, un usuario simplemente llama a la función claim() en CometReward.sol. El mapping rewardsClaimed (cuadro rojo) se comporta como el rewardDebt de MasterChef.

¿Para qué es el argumento shouldAccrue?
Si alguien está reclamando recompensas como la única acción en una transacción, entonces shouldAccrue (cuadro verde) debería ser true. Sin embargo, si es después de llamadas a otras funciones, entonces otras llamadas a funciones que cambian el estado llamarán a accrueAccount(), haciendo innecesaria otra llamada.
getRewardAccrued() (CometRewards.sol)
En el cuadro azul de arriba, getRewardAccrued determina cuánto pagar al usuario. Esto simplemente consulta el baseTrackingAccrued desde el struct de usuario en Comet. CometRewards luego lo resta por su deuda de recompensas (rewardsClaimed) y le paga al usuario la diferencia.

Peculiaridades en el propio token COMP
El token COMP que distribuye el contrato de recompensas no almacena los saldos como un uint256 como lo hacen la mayoría de los tokens ERC 20, sino más bien como un uint96.

Si intentas hacer un transfer o approve por una cantidad mayor al valor máximo de uint96, la transacción se revertirá.

Aprende más con RareSkills
Por favor, consulta nuestro bootcamp de Solidity para aprender más sobre el desarrollo avanzado de contratos inteligentes.
Publicado originalmente el 10 de enero de 2024