The Compound V2 Jump Rate Model: Why the Kink Is at 80% Utilization
Compound V2's interest rate curve is piecewise linear with a kink at 80% utilization. The math, the parameters, and the economic rationale for each piece.
TL;DR
- Compound V2's
JumpRateModelproduces a piecewise linear borrow rate curve with a single discontinuity in slope at the kink point (80% utilization). - Below the kink: rates rise gently (
baseRate + utilization * multiplier). The protocol wants borrows to flow. - Above the kink: rates jump sharply (
baseRate + kink * multiplier + (utilization - kink) * jumpMultiplier). The protocol wants withdrawals to be possible. - Real production parameters on Ethereum mainnet are roughly:
baseRate = 2%,multiplier = 20%,jump multiplier = 200%. Tweak any of these and the market dynamics shift dramatically. - The 80% kink is a deliberate liquidity-buffer choice. It means at least 20% of supplied capital should usually be redeemable on demand. Above the kink, the protocol pays a premium specifically to refill that buffer.
Why this matters
If you're auditing a Compound V2 fork, the rate model is one of the first things to verify. Forks routinely:
- Move the kink to a different utilization (for example 90% to "let more capital be productive"), forgetting that this also moves the redemption buffer.
- Lower the jump multiplier to make rates "less aggressive", which is exactly the wrong direction. The jump's purpose is to make rates aggressive when liquidity is scarce.
- Set parameters in different units (per-year vs per-block, per-second vs per-block) and silently inflate or deflate the actual interest accrual by orders of magnitude. We covered the per-year-vs-per-block confusion in our Canto v2 mantissa article; the same class of bug appears here.
If you're a Web3 founder running a Compound-style lending protocol, understanding this model determines whether your protocol stays solvent under stress.
What the model does
JumpRateModel is a function. It takes the current pool state and returns a borrow rate. Specifically:
function getBorrowRate(uint cash, uint borrows, uint reserves)
public view returns (uint)
The inputs are:
cash: how much underlying token is sitting in the cToken contract right now (the redeemable buffer).borrows: how much underlying has been lent out.reserves: the protocol's reserve cut of past interest (does not count as available liquidity for new borrows).
From these the model computes utilization and then the rate.
Step 1: utilization
utilization = borrows / (cash + borrows - reserves)
Utilization is the fraction of supplied capital currently lent out. It ranges from 0 (no borrowing) to 1 (all capital lent out, no buffer).
The denominator excludes reserves because reserves are the protocol's accumulated fee and not redeemable by suppliers.
Step 2: the piecewise rate
if (utilization <= kink) {
rate = baseRate + utilization * multiplier
} else {
normalRate = baseRate + kink * multiplier
excessRate = (utilization - kink) * jumpMultiplier
rate = normalRate + excessRate
}
Below the kink, the rate grows linearly from baseRate (at 0% utilization) to baseRate + kink * multiplier (at 80% utilization). Above the kink, the slope changes: jumpMultiplier replaces multiplier.
In the deployed Compound V2 USDC market, the parameters were approximately:
| Parameter | Value | Annualized |
|---|---|---|
baseRate | per-block fraction | 2% / year |
multiplier | per-block fraction | 20% / year |
kink | 0.80 (raw fraction) | 80% utilization |
jumpMultiplier | per-block fraction | 200% / year |
So the rate curve looked like:
- 0% utilization: 2%
- 40% utilization: 2% + 0.4 * 20% = 10%
- 80% utilization (the kink): 2% + 0.8 * 20% = 18%
- 90% utilization: 18% + 0.1 * 200% = 38%
- 99% utilization: 18% + 0.19 * 200% = 56%
The discontinuity in slope at the kink is the entire point of the model. From 0% to 80%, the rate climbs gently, encouraging borrowing while liquidity is plentiful. From 80% to 100%, the rate climbs steeply, discouraging marginal borrows and rewarding lenders enough to attract more deposits.
Why the kink at 80%
The 80% kink is the most consequential parameter in the model. It implicitly defines the protocol's "comfortable" liquidity buffer.
If 80% is the kink, then the protocol expects 20% of supplied capital to be available for withdrawal at any moment. A supplier who deposits $1M and wants to redeem can do so as long as utilization remains below 80%. Above 80%, redemption may be partially or fully blocked depending on how concentrated the existing borrows are.
The 80% choice trades off two failure modes:
- Lower kink (e.g. 50%): more conservative buffer, more capital sitting idle, lower base APY for lenders.
- Higher kink (e.g. 95%): more capital productive, but the buffer shrinks, and a few large redemptions can push the protocol past the kink quickly. Once over, rates spike, but if everyone is locked into existing borrows, there is no immediate way to refill the buffer.
80% is roughly the empirical sweet spot for stablecoin and major-asset markets. Long-tail asset markets might use a lower kink because their borrowers are less reliable. Compound V2's actual deployed parameters varied per market.
Why the jump multiplier is so steep
The jump multiplier of 200% looks aggressive next to the multiplier of 20%. It is. That is the design.
The protocol's goal above the kink is to attract new supply and discourage marginal borrows, fast. A 38% rate at 90% utilization will:
- Push borrowers near liquidation to repay (they can't sustainably pay 38%).
- Pull new lenders in (38% APY is a generous yield on an audited blue-chip lending market).
- Prevent the long-tail of marginal borrowers from pushing utilization further.
If the jump multiplier were only 50%, the rate at 90% utilization would be 18% + 0.1 * 50% = 23%. Still rising, but slow enough that a sudden borrow demand shock could push utilization to 99% before the rate signal does its work. By that point, lenders trying to redeem are blocked, and the market is stuck in a high-utilization regime for hours or days until enough loans get repaid.
The 200% jump multiplier is engineered to make this scenario short-lived. It hurts borrowers who hold positions through the spike, but it rewards lenders willing to deposit in the panic, and it transitions the market back to healthy utilization within hours.
Common porting mistakes
Mistake 1: changing the kink without retuning the multipliers
A fork moves the kink to 90% to "let more capital be productive". Now the rate at 90% utilization (the new kink) is baseRate + 0.9 * multiplier. With the original multiplier = 20%, that's 20%. The fork shipped a market where the rate at 90% utilization is half what Compound V2's was, and the redemption buffer is half the size.
The fork's incentive structure is now wrong on both sides. Lenders earn less even when utilization is high. Borrowers face less penalty for pushing toward the cap. Withdrawals are blocked for a much smaller liquidity shock.
If you change the kink, you must also retune the multipliers to preserve the desired curve shape.
Mistake 2: per-year vs per-block confusion
Compound V2's actual stored parameters are per-block, computed from per-year human-readable rates by dividing by blocksPerYear. On Ethereum, blocksPerYear = 2,102,400 (assuming 15-second blocks).
A fork that copies the per-year human-readable rate (20%) directly into the per-block storage variable will accrue interest 2.1 million times faster than intended. We covered an instance of this in the Canto v2 article.
Mistake 3: cross-chain blocksPerYear
Compound V2 was written for Ethereum, where blocks are ~15 seconds. Forks ported to other chains often forget to update blocksPerYear. Venus on BSC inherited Compound V2's blocksPerYear = 2,102,400 constant, but BSC has 3-second blocks. Result: 5x interest inflation. We documented that bug in the Venus blocksPerYear article.
Related questions
What is utilization in Compound V2? Utilization is borrows / (cash + borrows - reserves), the fraction of supplied capital currently lent out. Higher utilization means less liquidity available for redemption, which the rate model penalizes via the jump above the kink.
Why is baseRate non-zero? A non-zero base rate ensures that even at 0% utilization, lenders earn a small yield. Without it, lending into an unused market would have zero return. With it, lenders are paid a small floor for capital availability alone.
What about the supply rate? The supply rate is derived from the borrow rate: supplyRate = borrowRate * utilization * (1 - reserveFactor). Lenders earn what borrowers pay, minus the protocol's reserve factor cut, prorated by utilization (because only the borrowed portion of supplied capital generates interest).
Are these parameters governable? In Compound V2, yes. The Comptroller can replace the entire rate model with a new one. Changing parameters mid-flight is risky because existing positions accrue interest under the new rates the moment governance commits.
What's different in Compound V3? Compound V3 (Comet) uses a different rate model with separate supply and borrow curves. The kink concept persists but parameter handling is more granular. Different design, different math; the V2 walkthrough above doesn't transfer directly.
Where to see this in Academy
Implementing the JumpRateModel correctly is one of the first build-section requirements in the Compound V2 reconstruction. The Academy's test suite validates:
- Utilization formula matches the spec.
- Rate at 0% utilization equals
baseRate. - Rate at the kink equals
baseRate + kink * multiplier. - Rate above the kink uses
jumpMultiplier, notmultiplier. - Per-block conversion: rates compound correctly when
blocksPerYear = 2,102,400for Ethereum.
When you rebuild Compound V2 line by line in Zealynx Academy, the JumpRateModel section is where you watch the test suite reject every incorrect parameter combination until you derive the right shape from first principles.
Tagged