Aave V4 Risk Premium
Aave V4 introduces a sophisticated Risk Premium system where borrowing costs depend on the quality of collateral used. Higher-quality collateral (like ETH) gets lower rates, while riskier collateral pays more.
Overview
┌──────────────────────────────────────────────────────────────┐ │ RISK PREMIUM │ ├──────────────────────────────────────────────────────────────┤ │ │ │ Interest Rate = Base Rate × (1 + User Risk Premium) │ │ │ │ Example: │ │ Base Rate = 5% │ │ Risk Premium = 20% (0.20) │ │ User Rate = 5% × 1.20 = 6% │ │ │ │ Collateral Quality Spectrum: │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ETH (CR=0) ─────────────────────────── SHITCOIN (CR=1000_00)│ │ │ Low Risk High Risk │ │ │ │ Low Premium High Premium │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────┘
Key Concepts
Collateral Risk (CR)
Each asset has a Collateral Risk value (0 to 1000_00 BPS):
// Collateral Risk is per-reserve, per-Spoke
struct Reserve {
// ...
uint24 collateralRisk; // 0 = highest quality, 1000_00 = lowest quality
}
// Examples:
// ETH: CR = 0 (risk-free reference)
// wstETH: CR = 5_00 (0.05% - very low risk)
// WBTC: CR = 10_00 (0.10% - low risk)
// USDC: CR = 0 (stablecoin - no risk premium)
// LINK: CR = 50_00 (0.50% - moderate risk)
// NewToken: CR = 200_00 (2.00% - higher risk)
User Risk Premium (RP)
The User Risk Premium is a weighted average of Collateral Risk across the user's collateral:
RP_u = Σ(CR_i × CollateralValue_i) / Σ(CollateralValue_i)
Where the sum only includes enough collateral to cover the user's debt (sorted by risk, lowest first).
Risk Premium Algorithm
/// @notice Calculate user's risk premium
/// @dev Covers debt with lowest-risk collateral first
function _calculateUserRiskPremium(address user) internal view returns (uint24) {
PositionStatus storage status = _positionStatus[user];
// Get total debt value
uint256 totalDebtValue = _getTotalDebtValue(user);
if (totalDebtValue == 0) return 0;
// Sort collateral by Collateral Risk (ascending)
CollateralInfo[] memory collaterals = _getCollateralsSortedByRisk(user);
// Iterate and cover debt with lowest-risk collateral first
uint256 coveredDebt = 0;
uint256 weightedRiskSum = 0;
uint256 weightedValueSum = 0;
for (uint256 i = 0; i < collaterals.length; i++) {
uint256 remainingDebt = totalDebtValue - coveredDebt;
if (remainingDebt == 0) break;
uint256 collateralValue = collaterals[i].value;
uint24 collateralRisk = collaterals[i].risk;
if (remainingDebt >= collateralValue) {
// Use full collateral
weightedRiskSum += uint256(collateralRisk) * collateralValue;
weightedValueSum += collateralValue;
coveredDebt += collateralValue;
} else {
// Use partial collateral (just enough to cover debt)
weightedRiskSum += uint256(collateralRisk) * remainingDebt;
weightedValueSum += remainingDebt;
coveredDebt += remainingDebt;
}
}
// Calculate weighted average
return uint24(weightedRiskSum / weightedValueSum);
}
Example Calculation
User Position: - Debt: $10,000 USDC - Collateral: - $8,000 ETH (CR = 0) - $5,000 LINK (CR = 50_00) - $3,000 NewToken (CR = 200_00) Step 1: Sort by CR (ascending): ETH, LINK, NewToken Step 2: Cover $10,000 debt: - Use $8,000 ETH (covers $8,000) - Use $2,000 LINK (covers remaining $2,000) Step 3: Calculate weighted average: RP = (0 × $8,000 + 50_00 × $2,000) / $10,000 RP = 100_000 / 10,000 RP = 10_00 (0.10%) User's borrow rate = Base Rate × (1 + 0.10%)
Premium Accounting
V4 tracks premium debt separately from base debt using shares and offsets:
Premium Structure
// In Hub (Asset-level)
struct Asset {
uint256 premiumShares; // Total premium shares across all Spokes
uint256 premiumOffsetRay; // Total premium offset (RAY)
}
// In Spoke (User-level)
struct UserPosition {
uint256 premiumSharesRay; // User's premium shares (RAY precision)
uint256 premiumOffsetRay; // User's premium offset (RAY)
}
Premium Offset Mechanism
At borrow time, premium shares are created with an offset that makes initial premium debt = 0:
// When borrowing:
function _addPremiumShares(
UserPosition storage position,
uint256 drawnShares,
uint256 riskPremium
) internal {
// Calculate premium shares = drawn shares × risk premium
uint256 newPremiumShares = drawnShares.rayMul(riskPremium);
// Get current premium share value in assets
uint256 premiumAssetValue = _getPremiumAssetValue(newPremiumShares);
// Set offset equal to asset value (so premium debt starts at 0)
position.premiumSharesRay += newPremiumShares;
position.premiumOffsetRay += premiumAssetValue;
}
Premium Debt Growth
Premium Debt = Premium Shares Value - Premium Offset At borrow time: Premium Shares Value = X Premium Offset = X Premium Debt = X - X = 0 After time passes (interest accrues): Premium Shares Value = X × (1 + interest) Premium Offset = X (unchanged) Premium Debt = X × (1 + interest) - X = X × interest
Premium Delta
When repaying or refreshing premium, a PremiumDelta is used:
struct PremiumDelta {
int256 sharesDelta; // Change in premium shares
int256 offsetRayDelta; // Change in premium offset (RAY)
uint256 restoredPremiumRay; // Premium amount being repaid (RAY)
}
// Example: Full premium repayment
PremiumDelta memory delta = PremiumDelta({
sharesDelta: -int256(position.premiumSharesRay),
offsetRayDelta: -int256(position.premiumOffsetRay),
restoredPremiumRay: premiumDebt.rayMul(WadRayMath.RAY)
});
Risk Premium Updates
When Risk Premium Updates
The User Risk Premium is recalculated when:
- •Borrow: New premium shares are added
- •Repay: Premium shares may be reduced
- •Collateral change: Enabling/disabling affects RP
- •Price change: Oracle updates affect collateral values
- •Manual update:
updateUserRiskPremium()
Lazy Updates
For gas efficiency, V4 uses lazy updates - the RP isn't continuously updated:
/// @notice Permissionlessly update user's risk premium
function updateUserRiskPremium(address user) external {
uint24 newRiskPremium = _calculateUserRiskPremium(user);
// Refresh premium shares with new risk premium
_refreshUserPremium(user, newRiskPremium);
emit UpdateUserRiskPremium(user, newRiskPremium);
}
Forced Updates
Governance can force-update a user's risk premium:
/// @notice Governor can force update user's risk premium
function forceUpdateUserRiskPremium(address user) external restricted {
uint24 newRiskPremium = _calculateUserRiskPremium(user);
_refreshUserPremium(user, newRiskPremium);
}
Interest Model
Total Interest
User Borrow Rate = Base Rate × (1 + Risk Premium) Where: - Base Rate = f(utilization) [from Hub interest rate strategy] - Risk Premium = User-specific based on collateral quality
Debt Composition
// Total Debt = Drawn Debt + Premium Debt // Drawn Debt = principal + base interest uint256 drawnDebt = position.drawnShares.rayMul(hub.getDrawnIndex()); // Premium Debt = risk premium interest uint256 premiumDebt = position.premiumSharesRay.rayMul(hub.getDrawnIndex()) - position.premiumOffsetRay; uint256 totalDebt = drawnDebt + premiumDebt;
Configuration
Setting Collateral Risk
struct ReserveConfig {
uint24 collateralRisk; // 0-1000_00 BPS
// ... other fields
}
function updateReserveConfig(
uint256 reserveId,
ReserveConfig calldata config
) external restricted {
Reserve storage reserve = _getReserve(reserveId);
reserve.collateralRisk = config.collateralRisk;
// ...
}
Risk Premium Threshold (Hub)
// Hub can set a max risk premium threshold
uint24 public constant MAX_RISK_PREMIUM_THRESHOLD = type(uint24).max;
// Used in validation
function _validateRiskPremium(uint24 riskPremium) internal view {
require(riskPremium <= MAX_RISK_PREMIUM_THRESHOLD, ExcessiveRiskPremium());
}
Benefits
┌──────────────────────────────────────────────────────────────┐ │ RISK PREMIUM BENEFITS │ ├──────────────────────────────────────────────────────────────┤ │ │ │ For Protocol: │ │ • Accurately prices risk │ │ • Attracts higher-quality collateral │ │ • Reduces systemic risk │ │ │ │ For Suppliers: │ │ • Higher yields from risky collateral borrowers │ │ │ │ For Borrowers with Good Collateral: │ │ • Lower borrowing costs │ │ • Incentive to use ETH/stables as collateral │ │ │ │ For Borrowers with Risky Collateral: │ │ • Fair pricing for risk taken │ │ • Still able to borrow (at higher cost) │ │ │ └──────────────────────────────────────────────────────────────┘
Events
event UpdateUserRiskPremium(address indexed user, uint24 riskPremium); event UpdateReserveCollateralRisk(uint256 indexed reserveId, uint24 collateralRisk);
Reference Files
- •
src/spoke/Spoke.sol- Risk premium calculations - •
src/hub/Hub.sol- Premium share accounting - •
src/hub/libraries/Premium.sol- Premium math utilities - •
src/spoke/libraries/UserPositionDebt.sol- User debt calculations - •
docs/overview.md- Risk premium algorithm