Compound V2 Overview
This article presents the architecture and the behavior of Compound V2, a DeFi protocol on Ethereum for supplying or borrowing assets. The content of this article is mainly taken up and annotated from the following resources:
- A free online course made by Campbell Harvey.
- Compound V2 Documentation: docs.compound.finance/v2/
- Compound whitepaper
- Compound GitHub
- Understanding Compound protocol’s interest rates
- RareSkills - DeFi Lending: Liquidations and Collateral
Introduction
This article presents one of the first versions of Compound: Compound V2.
This version has been replaced by Compound V3 (Comet) on August 2022.
Here the main points come from the whitepaper
Compound is a protocol initially based on the Ethereum blockchain that establishes money markets:
-
Money markets are pools of assets with algorithmically derived interest rates, based on the supply and demand for the asset.
- Suppliers (and borrowers) of an asset interact directly with the protocol earning (and paying) a floating interest rate, without having to negotiate terms such as maturity, interest rate, or collateral with a peer or counterparty.
- Each money market is unique to an Ethereum asset such as Ether or an ERC-20 such as Dai.
- Unlike an exchange or peer-to-peer platform, where a user’s assets are matched and lent to another user, the Compound protocol aggregates the supply of each user; when a user supplies an asset, it becomes a fungible resource.
- This approach offers significantly more liquidity than direct lending;
- unless every asset in a market is borrowed, users can withdraw their assets at any time, without waiting for a specific loan to mature.
-
Assets supplied to a market are represented by an ERC-20 token balance (cToken), which entitles the owner to an increasing quantity of the underlying asset. As the money market accrues interest, which is a function of borrowing demand, cTokens become convertible into an increasing amount of the underlying asset. In this way, earning interest is as simple as holding an ERC-20 cToken.
- Market View for Tether

Difference between V2 and V3
Compound V3 has been deployed on August 2022
Here the main resource is the Compound Academy.
- The most significant improvement was moving away from the pooled-risk model, which allowed users to borrow any asset but constantly rehypothecated collateral. This model was vulnerable to a single bad asset or oracle update draining all assets from the protocol.
- In Compound III, each market deployment offers a single borrowable asset, and the supplied collateral remains the property of the borrower, except during liquidation. This new approach increases capital efficiency and reduces risk since the collateral is more “useful” when the borrower knows the specific asset being borrowed in advance.
- The first release of Compound III allows borrowing of USDC using ETH, WBTC, LINK, UNI, and COMP as collateral. Although borrowers will not earn interest on their collateral, they will be able to borrow more with lower risks of liquidation and penalties while spending less on gas fees.
- wETH market is also live now, with
Coinbase Wrapped Staked ETH(cbETH)andLido Staked ETH(stETH)as collateral.
| Feature | Compound V2 | Compound V3 (Comet) |
|---|---|---|
| Asset model | Pooled-risk: multiple borrowable assets per deployment | Single borrowable base asset per market deployment |
| Deployment | Single deployment of protocol contracts on a blockchain | Each market is a separate instance of new Comet contracts |
| Hack risk | Relatively high — a single bad asset or oracle update can drain all funds | Relatively low — markets are completely isolated |
| Collateral interest | Lenders earn interest on all supplied assets | Only the base asset earns interest; collateral does not yield interest |
| Interest rate model | Supply rate derived from borrow rate and reserve factor | Separate, independent supply and borrow rate curves — governance controls each independently |
| Interest accrual | Per-block (multiplierPerBlock, baseRatePerBlock) |
Per-second (supplyPerSecondInterestRateSlopeLow, etc.) — important for multi-chain deployments where block times vary |
| Collateral factors | Single collateralFactor per asset |
Two per asset: BorrowCollateralFactor (to initiate a borrow) and LiquidateCollateralFactor (for liquidation checks, always greater than borrow CF) |
| Supply caps | No cap per asset | SupplyCap per collateral asset — limits maximum depositable collateral |
| Minimum borrow | No minimum | BorrowMin enforced to prevent dust borrow positions |
| Liquidation | External liquidators call liquidateBorrowFresh() to repay debt and seize cToken collateral at a discount |
Protocol absorbs underwater positions via Absorb() — debt is transferred to the protocol and collateral is sold via BuyCollateral(); absorber receives a gas-based incentive (AbsorbTip) |
| Account delegation | No equivalent | Allow() and AllowBySig() (EIP-712) for fine-grained permission delegation to other addresses |
| Contract architecture | Multiple contracts: one CToken per market + shared Comptroller + separate InterestRateModel | Monolithic Comet.sol with CometExt.sol for overflow functions via DELEGATECALL |
| Configuration | Mutable storage, changeable via governance transactions | Constants are immutable at deploy time; changing parameters requires deploying a new Comet implementation |
| Multi-chain | Primarily Ethereum mainnet | Designed for multi-chain deployment (Arbitrum, Optimism, Polygon, Scroll, Linea, Ronin) with bridge receiver contracts |
| Pause granularity | Protocol-level pause | Per-function pause: Supply, Transfer, Withdraw, Absorb, and Buy can be paused independently |
| Batching | No equivalent | Bulker.sol batches multiple Comet operations in a single transaction |
Reference: compound.education/guides - compound-v2-vs-v3-compound/4
Protocol Architecture
Compound V2 is composed of several key contracts:
cToken Contracts
Each supported asset has its own cToken contract (e.g., cDAI, cETH, cUSDC). These contracts handle minting and redeeming cTokens, borrowing and repaying assets, and accruing interest per block. Before executing any operation, a cToken delegates permission checks to the Comptroller.
Comptroller
The Comptroller is the risk management contract of Compound V2. It acts as a central controller that validates permissible user actions across all cToken markets. Before any operation (supply, borrow, redeem, liquidate) executes, the cToken calls the Comptroller to verify the action is allowed.
Key responsibilities:
- Enforcing that borrowers hold sufficient collateral across all active markets
- Maintaining the list of supported markets and their collateral factors
- Managing the close factor and liquidation incentive
- Distributing COMP token rewards to suppliers and borrowers
See Comptroller.sol and docs.compound.finance/v2/comptroller.
Interest Rate Models
V2 supports two interest rate model variants:
JumpRateModel: The standard model used by most markets. Features a kink beyond which the slope steepens sharply to incentivize repayment and maintain liquidity. Covered in detail in the Supply and borrow rates section.WhitePaperInterestRateModel: The original model from the Compound whitepaper. Uses a simpler formula with only a base rate and a linear slope — no kink. This was the initial model before the JumpRate model was introduced and is still available as an alternative.
Governance Contracts
Compound governance has evolved through two contract versions:
GovernorAlpha: The original governance contract, establishing the core proposal and voting mechanism.GovernorBravo: The upgraded version, which added support for voting reason strings (voters can explain their vote on-chain), improved proposal cancellation logic, and adjustable governance parameters (voting delay, voting period, and proposal threshold) via governance itself without requiring a contract redeployment.
Formal Verification
Compound V2 includes formal verification specifications powered by Certora. The Certora Prover mathematically verifies key protocol invariants, providing an additional security layer beyond traditional audits. Source: V2 README.md.
Overcollateralization
- The concept of credit rating cannot be applied because Ethereum accounts are pseudonymous. It is impossible to enforce repayment in the event of a loan default.
- As a result, all loans are overcollateralized in a collateral asset different from the one being borrowed.
- If a borrower falls below their collateralization ratio (CR), their position is liquidated to pay back their debt.
- The debt can be liquidated by a keeper. The keeper receives in return a bonus.
Collateralization ratios and factors
-
The CR is calculated via a collateral factor (CF)
-
The collateral factor is the percentage of the collateral value that can be borrowed
- This CF is decided through a governance proposal
- Each ERC-20 asset on the platform has its own collateral factor ranging from zero to 90
- A CF of zero means an asset cannot be used as collateral but it can still be borrowed
- The required CR for a single collateral type is calculated as 100 divided by the CF
- Volatile assets generally have lower collateral factors, which mandate higher CR due to increased risk of a price movement that could lead to undercollateralization
- An account can use multiple collateral types at once, in which case the CR is calculated as 100 divided by the weighted average of the collateral types by their relative sizes (denominated in a common currency) in the portfolio
See also docs.compound.finance - collateral-factor
CR as a reserve multiplier
- The CR is similar to a reserve multiplier in traditional banking, constraining the amount of “borrow” dollars that can be in the system relative to the “real” supply.
- For instance, there is occasionally more DAI in Compound than is actually supplied by MakerDAO, because users are borrowing and resupplying or selling to others who resupply.
- Importantly, all MakerDAO supply is ultimately backed by real collateral and there is no way to borrow more collateral value than has been supplied
Example 1:
- An investor deposits 100 DAI with a CR of 90
- This transaction alone corresponds to a required CR of 111%.
- Assuming 1 DAI is worth 1 USD, the investor can borrow up to 90 USD worth of any other asset in Compound
Example 2:
-
If the user borrows the maximum, and the price of the borrowed asset increases at all, the position is subject to liquidation.
-
Suppose the user also deposits two ETH with a CR of 60 and a price of $200/ETH.
-
The total supply balance is now $500, with 80% being ETH and 20% being DAI. The required CR is
Supply and borrow rates
- The supply and borrow interest rates are compounded every block (~12 seconds)
- They are determined by the utilization percentage in the market
- Utilization rate is used as an input parameter to a formula that determines the interest rates
- The remaining parameters are set by Compound Governance
Utilization rate
All interest rates in Compound are determined as a function of a metric known as the utilization rate. The utilization rate U_a for a money market a is defined as: \(\begin{aligned}[b] U_a = Borrows_a / (Cash_a + Borrows_a - Reserves_a) \end{aligned}\)
- Borrows_a refers to the amount of a borrowed.
- Cash_a refers to the amount of a left in the system, available to be borrowed
- Reserves_a refers to the amount of a that Compound keeps as profit.
Code
See compound-finance/compound-protocol - JumpRateModel.sol#L56C3-L62C8
uint256 private constant BASE = 1e18;
/**
* @notice Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`
* @param cash The amount of cash in the market
* @param borrows The amount of borrows in the market
* @param reserves The amount of reserves in the market (currently unused)
* @return The utilization rate as a mantissa between [0, BASE]
*/
function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) {
// Utilization rate is 0 when there are no borrows
if (borrows == 0) {
return 0;
}
return borrows * BASE / (cash + borrows - reserves);
}
Example
For example: given that reserves are 0,
- Alice supplies $500 USDC
- Bob supplies $500 USDC
- Charles borrows $100 USDC
Before Charles borrows, the total cash in the market is Cash_a = 1000 (Alice’s 500 + Bob’s 500).
After Charles borrows 100 USDC, the remaining available cash drops to cash_a = 900. The utilization rate is then:
Note: the denominator cash + borrows - reserves = 900 + 100 - 0 = 1000 represents the total amount the protocol owes to suppliers (effective total supply), not the current available cash.
Borrow rate formula
- It is an increasing linear function with a y-intercept known as the base rate that represents the borrow rate at 0% borrow demand and a slope representing the rate of change of the rates
- These parameters are different for each ERC-20 asset supported by the platforms
- Some markets include also a kink:
- a kink is a utilization ratio beyond which the slope steepens.
- kink is the utilization point at which the jump multiplier is applied
-
These formulas can be used to reduce the cost of borrowing up to the kink and then increase the cost of borrowing after the kink to incentivize a minimum level of liquidity.
- Formula
Note: this formula is simplified and does not include the kink parameter.
- Formula with kink parameter
Where \(\begin{aligned}[b] U = \frac{B}{C + B - R} \end{aligned}\)
\[\begin{aligned}[b] \text{BASE} = 10^{18} \end{aligned}\]Code
See compound-finance - JumpRateModel.sol#L79
/**
* @notice Calculates the current borrow rate per block, with the error code expected by the market
* @param cash The amount of cash in the market
* @param borrows The amount of borrows in the market
* @param reserves The amount of reserves in the market
* @return The borrow rate percentage per block as a mantissa (scaled by BASE)
*/
function getBorrowRate(uint cash, uint borrows, uint reserves) override public view returns (uint) {
uint util = utilizationRate(cash, borrows, reserves);
if (util <= kink) {
return (util * multiplierPerBlock / BASE) + baseRatePerBlock;
} else {
uint normalRate = (kink * multiplierPerBlock / BASE) + baseRatePerBlock;
uint excessUtil = util - kink;
return (excessUtil * jumpMultiplierPerBlock/ BASE) + normalRate;
}
}
Supply interest formula
High level formula
\[\begin{aligned}[b] Supply~ interest ~rate = utilisation ~ratio * borrow~ interest ~rate * (1 - reserveFactor) \end{aligned}\]-
Reserve factor
- The reserve factor defines the portion of borrower interest that is converted into reserves.
- This is the % of the borrow payments not given to the suppliers and instead set aside in a reserve pool that acts as insurance in the case of a borrower default.
-
This reserve can be withdrawn or transferred through the protocol’s governance.
- See https://docs.compound.finance/v2/ctokens/#total-reserves
-
In an extreme price movement, many positions may become undercollateralized in that they have insufficient funds to repay the suppliers. In the event of such a scenario, the suppliers would be repaid using the assets in the reserve pool
Implementation
Explanation
Here are the differences with the formula:
In Solidity, all values are stored as scaled integers, typically with: \(\begin{aligned}[b] Base = 10^{18} \end{aligned}\)
So the real implementation is: \(\begin{aligned}[b] SupplyRate=\frac {U \cdot rb \cdot (BASE-f)}{BASE^2} \end{aligned}\)
- U:
Utilisation rate -
rb: borrow rate
- f: reserve factor
Utilisation rate
\[\begin{aligned}[b] U = \frac{B}{C + B - R} \end{aligned}\]where:
- B = Borrow
The total amount currently borrowed by users.
- C = cash
The amount of available liquidity in the market (funds that are currently not borrowed).
- R = reserves
The portion of interest that has been set aside for the protocol, not available to suppliers.
Code
/**
* @notice Calculates the current supply rate per block
* @param cash The amount of cash in the market
* @param borrows The amount of borrows in the market
* @param reserves The amount of reserves in the market
* @param reserveFactorMantissa The current reserve factor for the market
* @return The supply rate percentage per block as a mantissa (scaled by BASE)
*/
function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) override public view returns (uint) {
uint oneMinusReserveFactor = BASE - reserveFactorMantissa;
uint borrowRate = getBorrowRate(cash, borrows, reserves);
uint rateToPool = borrowRate * oneMinusReserveFactor / BASE;
return utilizationRate(cash, borrows, reserves) * rateToPool / BASE;
}
Example 1
In the DAI market, 100 million is supplied and 50 millions is borrowed.
-
Base rate = 1%
-
Slope = 10%
At 50 million borrowed, utilization is 50%
\[\begin{aligned}[b] borrow~ interest~ rate = utilization * Slope + base ~rate .\end{aligned}\] \[\begin{aligned}[b] borrow~ interest~ rate = 0.5 * 0.1 + 0.01 = 0.06 = 6\% .\end{aligned}\]maximum supply rate with a reserve factor of zero:
\[\begin{aligned}[b] maximum~supply ~rate = utilization~ ratio * borrow~ interest ~rate \end{aligned}\] \[\begin{aligned}[b] 0.5 * 0.06 = 0.03 ~or~ 3\% \end{aligned}\]Example 2
- The borrow rate is not a marginal rate, it is a rate for all borrowers
- If an initial borrower does $25 millions. The rate would be .25*0.1 + 0.01 = 3.5%.
- Then suppose another borrower enters the market with another $25 million loan
- The rate increases to 6% for all borrowers
Example 3 - Reserve Factor
- If the reserve factor is set to 10, then 10% of the borrow interest is diverted to a DAI reserve pool, lowering the supply interest rate to 2.7%
| Label | Value |
|---|---|
| Borrow rate | = 6% |
| Total interest | = .06 * 50m = 3m |
| Reserve .1 x 3m | = .3m |
| Distributions to suppliers | = 2.7 m |
- 6% of borrow interest of 50 million is equal to 3 million of borrow payment
- Distributing 3 millions of payments to 100 millions of suppliers implies a 3% interest rate to all suppliers.
- With 10% diverted to the reserve (300,000), then there is only 2.7 millions of payments to the suppliers
Example 4 - Kink
- The base rate remains at 1%
- The borrow interest rate
- The supply rate (assuming a reserve factor of zero) is
Advantage of Compound
- Unlock value of asset without selling it, like a home equity line of credit (HELOC)
- Easily engineer levered long or short positions
- Suppose you are bearish on price of ETH
- Deposit stablecoin like USDC or DAI
- Borrow ETH
- Sell ETH for stablecoin
- If price of ETH falls, you can use your stablecoin to buy (cheap) ETH to pay off debt
cTokens
- The compound protocol must escrow tokens as a depositor in order to maintain that liquidity for the platform itself and to keep track of each person’s ownership stake in each market
- A naive approach would be to keep track of the number inside a contract
- A better approach would be to tokenize the user’s share
- Compound does this using a cToken, and this is one of the platform’s important innovations
Burn and mint
cTokens are minted and burned
-
Compound’s cToken is an ERC-20 in its own right that represents an ownership stake in the underlying Compound market
- For example,
cDAIcorresponds to the Compound DAI marketcETHcorresponds to the Compound ETH market
- Both tokens are minted and burned in proportion to the funds added and removed from the underlying market as a means to track the amount belonging to a specific investor
cTokens can be traded
- Given interest payments continually accrue to suppliers, these tokens are always worth more than the underlying asset
- cTokens can be traded on their own like a normal ERC-20 assets
- Other protocols can seamlessly integrate with Compound simply by holding cTokens and allows users to deploy their cTokens directly into other opportunities, such as using a cToken as collateral for MakerDAO vault
- Instead of using ETH only as collateral, an investor can use cETH and earn lending interest on the ETH collateral
Example 1
There are 2,000 DAI in the Compound DAI market a total 500 cDAI represents the ownership in the market; this ratio of cDAI is not determinative and could just as easily be 500,000 cDAI
1 cDAI = 2,000 / 500 = 4 DAI
If a trader comes in and deposits 1,000 DAI to a supply of 3,000, the supply increases by 50% and Compound mints 50% more which corresponds to 250 cDAI (1000 / 4 = 250 cDAI)
Example 2
Currently, 1 cDAI = 4 DAI, but after interest accrues the ratio will change.
Let interest = 10%, at year end, 3_300 DAI because 3_000 * 10% = 300
Trader redeems 250 cDAI for 1,100 DAI.
Remark
- The trader can also deploy cDAI in place of DAI so the DAI is not sitting idle but earning interest via the Compound pool.
- For example, the trader could deploy cDAI as the necessary collateral to open a perpetual futures position on DYdX or she could market make on Uniswap using a cDAI trading pair
Governance parameters
- The many different parameters of Compound’s functionality, such as the collateral factor, reserve factor, base rate, slope and kink, can all be tuned
- The entity capable of tuning these parameters is Compound governance
- Compound governance has the power to change parameters, add new markets, freeze the ability to initiate new deposits or borrow in a market, and even upgrade some of the contract code itself
Governance
- Importantly, under the current Comptroller, Compound governance cannot steal funds or prevent users from withdrawing. Because the Comptroller is upgradeable, a governance vote could change that (see Risks). However, they can manage funds inside the reserve according to the documentation
- In the early stages of Compound’s growth, governance was controlled by developer admins, similar to any tech startup
- Technically, this meant that the first version of Compound was not fully decentralized
Governance parameters
- A strong development goal of Compound, as with most DeFi protocols, was to remove developer admin access and release the protocol to the leadership of a DAO via a governance token
- The token allowed shareholders and community members to collectively become Compound Governance and propose upgrades or parameter tuning
- A quorum agreement is required for any change to be implemented
- A proposal needs at least 400,000 COMP worth of votes in support to reach quorum (4% of total eventual supply)
Code
See github.com/compound-finance - contracts/Governance/GovernorAlpha.sol#L8
/// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed
function quorumVotes() public pure returns (uint) { return 400000e18; } // 400,000 = 4% of Comp
GovernorBravo
Compound later upgraded to GovernorBravo, which introduced several improvements over GovernorAlpha:
- Support for voting reason strings — voters can explain their vote on-chain
- Improved proposal cancellation logic
- Adjustable governance parameters (voting delay, voting period, and proposal threshold) via governance itself, without requiring a contract redeployment
Formal Verification
Compound V2 includes formal verification specifications powered by Certora. The Certora Prover mathematically verifies key protocol invariants, providing an additional security layer beyond traditional audits.
COMP token
- Compound implemented this new governance system in MAY 2020 via the COMP token
- COMP is used to vote on protocol updates, such as parameter tuning, adding new asset support, and functionality upgrades (similar to MKR for MakerDAO)
- On June 15, 2020, the 7th governance proposal passed which provided for distribution of COMP tokens to users of the platform based on the borrow volume per market.
- The COMP token is distributed to both suppliers and borrowers, and acts as a subsidization of rates.
Liquidation
If the value of an account’s borrowing outstanding exceeds their borrowing capacity, a portion of the outstanding borrowing may be repaid in exchange for the user’s cToken collateral, at the current market price minus a liquidation discount;
- this incentivizes an ecosystem of arbitrageurs to quickly step in to reduce the borrower’s exposure, and eliminate the protocol’s risk.
- The proportion eligible to be closed, a close factor, is the portion of the borrowed asset that can be repaid, and ranges from 0 to 1, such as 25%.
- The liquidation process may continue to be called until the user’s borrowing is less than their borrowing capacity.
- Any Ethereum address that possesses the borrowed asset may invoke the liquidation function, exchanging their asset for the borrower’s cToken collateral.
- As both users, both assets, and prices are all contained within the Compound protocol, liquidation is frictionless and does not rely on any outside systems or order-books.
Code
From github.com/compound-finance - CToken.sol#L721
/**
* @notice The liquidator liquidates the borrowers collateral.
* The collateral seized is transferred to the liquidator.
* @param borrower The borrower of this cToken to be liquidated
* @param liquidator The address repaying the borrow and seizing collateral
* @param cTokenCollateral The market in which to seize collateral from the borrower
* @param repayAmount The amount of the underlying borrowed asset to repay
*/
function liquidateBorrowFresh(address liquidator, address borrower, uint repayAmount, CTokenInterface cTokenCollateral) internal {
//Fail if liquidate not allowed
uint allowed = comptroller.liquidateBorrowAllowed(address(this), address(cTokenCollateral), liquidator, borrower, repayAmount);
if (allowed != 0) {
revert LiquidateComptrollerRejection(allowed);
}
// Verify market's block number equals current block number **/
if (accrualBlockNumber != getBlockNumber()) {
revert LiquidateFreshnessCheck();
}
//Verify cTokenCollateral market's block number equals current block number
if (cTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
revert LiquidateCollateralFreshnessCheck();
}
// Fail if borrower = liquidator
if (borrower == liquidator) {
revert LiquidateLiquidatorIsBorrower();
}
// Fail if repayAmount = 0
if (repayAmount == 0) {
revert LiquidateCloseAmountIsZero();
}
// Fail if repayAmount = -1
if (repayAmount == type(uint).max) {
revert LiquidateCloseAmountIsUintMax();
}
// Fail if repayBorrow fails ^
uint actualRepayAmount = repayBorrowFresh(liquidator, borrower, repayAmount);
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
....
(rest of the functions available on Compound GitHub)
Comparison with Traditional Finance
Other platforms use Compound
- The Compound protocol can no longer be turned off and will exist on Ethereum as long as Ethereum exists
- Other platforms can easily escrow funds in Compound to provide additional value to their users or enable novel business models
- Easy, instant access to yield or borrow liquidity on different Ethereum tokens makes Compound an important platform in DeFi.
Compound VS traditional finance
Centralized control
-
Traditional finance problem: Borrowing and lending are controlled by institutions
-
Compound solution: Compound rates are determined algorithmically and gives control of market parameters to COMP stakeholders incentivized to provide value to users
Limited access
- Traditional finance problem: Difficulty in accessing high-yield USD investment opportunities or competitive borrowing
- Compound solution: Open ability to borrow or lend any supported assets at competitive algorithmically determined rate (temporally subsidized by COMP distribution)
Inefficiency
- Traditional finance problem: Suboptimal rates for borrowing and lending due to inflated costs
- Compound solution: Algorithmically pooled and optimized interest rates
Lack of Interoperability
- Cannot repurpose supplied positions for other investment opportunities
- Compound solution: tokenized positions via cTokens can be used to turn static assets into yield-generating assets
Opacity
Traditional finance problem: under collateralization of lending institutions
Compound solution: Transparent collateralization ratios of borrowers visible to entire ecosystem
These advantages come with the risks listed in Risks: a Comptroller that governance can replace, one oracle for all markets whose feeds a 5-of-9 multisig can change, pooled collateral, withdrawals limited to each market’s cash, and bad debt that falls on the last suppliers to redeem.
Screenshot
From August 26, 2024
- Dashboard view


- Collateral factor is 0% meaning this is not possible to use tether as a collateral
- There are $2.26 Millions set in the reserved!!!!

- The CR is high (82.5%) indicated that Ether is considered as a safe asset

- The CR is pretty low, only 20% meaning that this asset is considered as more volatile
Annex
Risks
This section lists what can go wrong for a supplier or a borrower in Compound V2. It starts from what Compound publishes (the V2 documentation, its governance proposals and the incidents covered on this site), then adds what the deployed contracts show. The on-chain values below were read on 22 October 2026 at Ethereum block 26,248,478. Points that come from this article’s own reasoning rather than from a source are marked (derived).
One fact frames the rest: at that block, the two markets checked, cUSDC and cETH, had minting and borrowing paused by the Comptroller (mintGuardianPaused and borrowGuardianPaused both true) and a reserve factor of 100% (reserveFactorMantissa() = 1e18). With the supply rate formula of the Supply interest formula section, a reserve factor of 100% means suppliers in these markets earn no interest: all interest paid by the remaining borrowers goes to the reserves (derived). V2 is being wound down in favour of Compound III; the other 18 listed markets were not checked.
Smart contracts and transparency
- Verified, audited, formally specified. The Unitroller, the Comptroller implementation, cETH, cUSDC, the price oracle and the governance contracts all have verified source on Etherscan. The repository includes Certora specifications (see Formal Verification), and Compound’s documentation lists external audits of V2; which audits covered which version of the Comptroller was not checked for this article (see Gaps).
- Upgradeable Comptroller, both sides. The Comptroller sits behind the
Unitrollerproxy (0x3d98…Cd3B), whosecomptrollerImplementation()pointed to0xBafE…B526(a contract namedComptroller). Upgradeability let Compound fix bugs and add features such as COMP distribution and borrow caps without migrating users. It also means whoever holdsadmin()can replace the whole risk engine, includingredeemAllowed, so the statement in the Governance section that governance “cannot prevent users from withdrawing” holds only as long as no upgrade changes that logic (derived). - cTokens are partly fixed. cETH (
CEther) and cUSDC (CErc20) are not proxies, so their own code cannot be swapped, but theiradmin()(the Timelock) can still change their Comptroller, interest-rate model and reserve factor. Later markets use theCErc20Delegatorpattern, whose implementation the admin can replace. - Upgrades can carry bugs. Proposal 062 (September 2021) upgraded the Comptroller with a boundary error in COMP distribution that paid out about $80$M of COMP at first, up to $147$M by Rekt’s tally, and the fix had to go through the same vote and timelock (Crypto hacks 2021, technical concepts). The safeguard against a malicious upgrade also slowed the repair.
- Empty-market rounding. The cToken exchange rate divides the market’s assets by the cToken supply, and forks of V2 lost funds when a newly listed market had almost no supply (Hundred Finance in 2023, Sonne Finance in 2024; see Crypto hacks 2024: technical concepts). Compound’s own markets are large and mature, so this applies mainly to forks and to any new listing (derived).
External dependencies
- Price oracle. The Comptroller’s
oracle()returned0x8CF4…55d9, a contract namedPriceOraclethat maps each cToken to a Chainlink feed or to a fixed price. cUSDC reads the USDC / USD feed and cETH the ETH / USD feed. ItsgetUnderlyingPriceignores the feed’supdatedAt, so a stale price is accepted as current, and only a zero or negative answer is rejected. Every borrow limit and liquidation in all markets depends on this one contract (derived from the source). - History of oracle events. On 26 November 2020, a brief DAI price spike on Coinbase Pro, reported by the oracle Compound used then, caused about $89$M of liquidations (Crypto hacks 2020). How a lending market passes such price moves on to positions is covered in Oracle kinds, pricing and lending positions.
- Underlying tokens. Each market inherits the risks of its token: USDC and USDT issuers can freeze addresses, including a cToken contract, and a token with transfer hooks can re-enter (a pattern that hit Compound forks such as Cream; see the 2021 technical concepts article above).
- Chain. Liquidations are first-come transactions on Ethereum; during congestion they can be delayed, and liquidators compete through MEV. Interest accrues per block, so the rates assume roughly 12-second blocks (derived).
Depeg
- Stablecoin markets. Compound does not hold any peg itself. Because the oracle reads a market feed for USDC rather than a fixed 1 dollar, a depeg of a stablecoin used as collateral lowers borrowing power and can trigger liquidations, while a depeg of a borrowed stablecoin lowers the debt’s value (derived).
- cToken exchange rate. A cToken is not pegged; its exchange rate is
(cash + totalBorrows - totalReserves) / totalSupplyand normally only grows. Bad debt stays insidetotalBorrows, so the exchange rate does not fall when a loan becomes uncollectable: the shortfall appears only when the last suppliers try to redeem (derived). Protocols that accept cTokens as collateral (see cTokens can be traded) price them at that rate.
Collateral and market risk
- Pooled risk. As the Difference between V2 and V3 section says, one market’s bad asset or bad price affects every market, since collateral in any market backs borrows in all others.
- How far a price can fall. cETH had a collateral factor of 82.5% (
markets(cETH)= 8.25e17) and the liquidation incentive was 1.08 (liquidationIncentiveMantissa()). A position borrowed to the limit is liquidatable as soon as ETH moves against it, and its collateral is worth less than its debt after a fall of 1 - 0.825 = 17.5%; liquidating the whole debt stops being profitable at a fall of 1 - 0.825 × 1.08 = 10.9% (derived). The close factor of 50% (closeFactorMantissa()= 5e17) means each liquidation repays at most half the debt. - Reflexive loops. Liquidators who seize collateral usually sell it, which pushes down the price the oracle reports and can make more positions liquidatable. Users who borrow and resupply the same asset (the CR as a reserve multiplier section) amplify this (derived).
Liquidity
- Utilisation caps withdrawals. A supplier can only redeem what is in the market’s cash. At the block read, cUSDC had 2,980,377.91 USDC of cash against 4,681,014.56 USDC of borrows and 181,280.60 USDC of reserves, a utilisation of 4,681,014.56 / (2,980,377.91 + 4,681,014.56 - 181,280.60) = 62.58%; only 39.84% of the 7,480,111.88 USDC owed to suppliers could leave at once. cETH had 21,443.79 ETH of cash and 151.22 ETH of borrows, a utilisation of 0.70%.
- No new supply to refill cash. With minting paused, cash only grows through repayments and liquidations. The Kink mechanism raises the borrow rate to push borrowers to repay, but a borrower who accepts the rate keeps the funds (derived). At 100% utilisation, no supplier can withdraw until someone repays.
Centralisation and governance
- Who controls the upgrade. The Unitroller’s
admin(), the cTokens’admin()and the GovernorBravo’sadmin()all returned the Timelock0x6d90…3925, withdelay()= 172,800 seconds (2 days) and a grace period of 14 days. The Timelock’s ownadmin()was no longer GovernorBravo (0xc0Da…6529, still deployed with 393 proposals) but0x309a…c8C0, aTransparentUpgradeableProxypointing to a contract namedCompoundGovernorbuilt on OpenZeppelin Governor, whose ProxyAdmin is owned by the Timelock. - Thresholds. On
CompoundGovernor, a proposal needs 25,000 COMP of delegated votes (proposalThreshold()), 0.25% of the 10,000,000 COMP supply, and 400,000 COMP of votes in favour for quorum (4%). The voting delay is 13,140 blocks and the voting period 19,710 blocks; at 12 seconds per block that is about 1 day 20 hours and 2 days 18 hours, so a proposal needs at least about 6 days 13 hours from submission to execution including the timelock (derived). These are the same values as the GovernorAlpha quorum in the Governance parameters section. - A multisig with direct power.
pauseGuardian(),borrowCapGuardian(),whitelistGuardian()andproposalGuardian()all returned0xbbf3…012c, a Safe with a threshold of 5 of 9 owners. In the Comptroller it can pause but only the admin can unpause. It is alsoowner()of the price oracle, and the oracle’supdateConfigPriceFeedandupdateConfigFixedPriceareonlyOwnerwith no timelock, so five signers can change the price of any market without a vote (derived from the source). - Voting power and capture. Voting power follows COMP delegation, so a holder or coalition with 400,000 COMP of votes in favour, more than the opposition, can pass any proposal, including a Comptroller upgrade or a transfer of reserves. In July 2024 a group known as Humpy or the “Golden Boys” passed a proposal to move about 499,000 COMP from the treasury to a vault they controlled, and the episode ended in a negotiated agreement rather than a technical block. This summary relies on general knowledge, not on a source read for this article (see Gaps).
Loss sharing
- No insurance layer. Compound V2 has no stability pool or insurance fund that absorbs bad debt by rule. Reserves (181,280.60 USDC in cUSDC at the block read) can be used, but only if governance votes to do so (derived).
- Losses fall on the last suppliers. Bad debt stays in
totalBorrowsand is not written off, so suppliers who redeem early receive full value and those who redeem last find the cash missing. With pooled risk, suppliers of a market who never borrowed can absorb losses created by a bad asset in another market (derived).
Regulatory
- None specific found. Compound V2 has no issuer, no KYC and is governed by token holders, and the design does not raise a specific question beyond the general treatment of decentralised lending, discussed in MiCA explained. Front ends and the reserve’s use are where an authority would most likely look (derived).
Summary
| Risk | Where it comes from | What the design does about it | What remains |
|---|---|---|---|
| Logic replaced by governance | Unitroller proxy, Timelock as admin | 2-day timelock, 400,000 COMP quorum | A captured vote can change withdrawal rules |
| Buggy upgrade | Proposal 062 type error | Certora specs, audits, review | Fix needs the same path of about 6.5 days |
| Oracle failure or stale price | One Chainlink-based oracle for all markets | Rejects zero or negative prices | No staleness check, owner multisig can reset feeds |
| Pooled collateral | Any market backs all borrows | Per-asset collateral factors, borrow caps | One bad asset or price hits every market |
| Liquidity | Cash only, mint paused | Kink raises borrow rates | cUSDC: 39.84% of supply withdrawable at once |
| Bad debt | Insolvent positions stay in borrows | Liquidation incentive of 8%, close factor 50% | Losses fall on the last redeemers |
| Multisig power | 5-of-9 Safe as guardian and oracle owner | Guardian cannot unpause | Can change prices with no vote |
| Regulatory | Issuer-less lending | None | None specific found |
Gaps
The following points were not settled by the sources used for the Risks section and should be checked before relying on it.
| Gap | Why it matters | What to read or verify |
|---|---|---|
| Audit coverage of the deployed Comptroller | The article names Certora but not which audits covered the implementation at 0xBafE…B526. |
The security page of docs.compound.finance/v2 and the audit reports it links, matched against the deployed source. |
| The Humpy / “Golden Boys” episode of 2024 | The Risks section summarises it from general knowledge (amount, outcome). | Compound governance proposal 289 and its forum thread on comp.xyz, and the follow-up agreement. |
| Status of the V2 wind-down | Paused mint and borrow and a 100% reserve factor were only checked on cUSDC and cETH. | mintGuardianPaused, borrowGuardianPaused and reserveFactorMantissa on the other 18 markets of getAllMarkets(), and the proposals that set them. |
| Migration from GovernorBravo to CompoundGovernor | The Timelock admin is now CompoundGovernor, but the article’s governance sections describe GovernorAlpha and GovernorBravo only. |
The proposal that called setPendingAdmin on the Timelock and the CompoundGovernor repository. |
| “Governance cannot prevent withdrawals” | The Governance section states it, now qualified as holding under the current Comptroller, since an upgrade could change redeemAllowed. |
The Compound documentation passage it comes from and the admin functions of the current Comptroller. |
| Oracle owner powers | The multisig can set feeds and fixed prices on the oracle with no timelock; whether a social process limits this was not found. | The Compound community multisig charter on comp.xyz and the oracle’s onlyOwner call history on Etherscan. |
| Oracle staleness | getUnderlyingPrice ignores updatedAt; the effect during a feed outage was not tested. |
The heartbeat of the USDC / USD and ETH / USD Chainlink feeds and any Compound discussion of stale prices. |
| Block time assumption | Durations of the voting delay and period assume 12-second blocks. | Average block time over a recent period, or the governor’s own documentation. |