Introduction
The purpose of this analysis is to examine the changes introduced with Aave v4 through the lens of risk management and assess how the protocol’s risk management tools have evolved.
We begin with a brief introduction to the new Hub and Spoke model, followed by an overview of the new features that directly impact risk management, such as dynamic liquidation mechanics, dynamic reserve configurations, and risk premiums.
Next, we present a deep dive into the functions that govern risk parameter updates in Aave v4.
To consolidate these insights, we provide a comprehensive table of all risk parameters in Aave v4, including their definitions and the functions used to modify them.
Finally, we conclude with a review of the freezing functions that act as circuit breakers, enabling governance and Risk Service Providers to contain risk.
Hub-and-Spoke Model
Aave v4 introduces a modular architecture in which the Hub holds and manages the underlying liquidity, while Spokes serve as market-level execution environments. Assets are listed at the Hub and then selectively enabled on individual Spokes, where users supply, borrow, and interact with the market. The Hub maintains the accounting, interest accrual, and liquidity state, whereas Spokes enforce position level logic such as health factor checks, collateral usage and liquidation rules.
The Hub is like the central bank vault and global accountant— it holds the liquidity and keeps the master balance sheet across all markets.
The Spoke is like a local branch office and bookkeeper— it applies risk rules, tracks individual users and sends instructions to the Hub to move funds.
Hub Responsibilities (Liquidity + Global Accounting)
- Owns and holds the ERC20 liquidity (tokens).
- Maintains global accounting for each
assetId:liquidityaddedShares(supplies)drawnShares(borrows)premiumShares/premiumOffset/deficit
- Tracks how much liquidity has been drawn or added across all connected Spokes.
- Updates interest rate indices (
drawnIndex,drawnRate). - Executes actual token transfers (
safeTransferandsafeTransferFrom).
Spoke Responsibilities (Market-Level Debt Tracking)
- Keeps per-user position data:
UserPosition(supplied shares, borrowed shares, collateral enabled flags, etc.).
- Maintains per-reserve configuration:
- Static risk params (
paused,frozen,borrowable,collateralRisk). - Dynamic params (
collateralFactor,maxLiquidationBonus,liquidationFee).
- Static risk params (
- Enforces collateral checks, health factor logic, liquidation eligibility.
- Forwards calls like
borrow,repay,supply,withdrawinto the Hub while updating user-level balances.
Terminology Changes from Aave v3
Aave v4 introduces new terminology to reflect the Hub-and-Spoke architecture. While the basic concepts remain familiar from Aave v3, the new system distinguishes between user-facing actions on Spokes and liquidity/accounting actions on the Hub.
1. User vs. Spoke vs. Hub Actions
Users interact only with Spokes using the standard set of functions:
supply,borrow,repay,withdraw
Spokes, in turn, interact with the Hub to update global liquidity and accounting, using a different set of verbs:
add— when a user supplies to the Spoke, the Spoke adds liquidity into the Hub.draw— when a user borrows from the Spoke, the Spoke draws liquidity from the Hub.restore— when a user repays on the Spoke, the Spoke restores liquidity back into the Hub.remove— when a user withdraws on the Spoke, the Spoke removes liquidity from the Hub.
Example:
If a user supplies USDC to a Spoke, the Spoke calls hub.add(assetId, amount, user) to credit the Hub’s global balance sheet.
2. Add & Draw Caps
In Aave v4, the familiar supply and borrow caps from v3 are replaced by add and draw caps:
- Add Cap — the maximum amount of an asset a Spoke can add to the Hub (supply cap in v3 terms).
- Draw Cap — the maximum amount of an asset a Spoke can draw from the Hub (borrow cap in v3 terms).
These caps are defined per asset per Spoke, allowing control over how much liquidity each Spoke can contribute to or consume from the Hub.
3. Assets vs. Reserves
On the Hub side, new tokens are registered with the addAsset function and assigned a unique assetId. These are called Assets.
On the Spoke side, the same assetId is mapped into the Spoke via addReserve. In the Spoke context, they are called Reserves.
4. Collateral Factor replaces LT & LTV
In Aave v3, each reserve had both a Loan-to-Value (LTV) and a Liquidation Threshold (LT).
In Aave v4, there is a single parameter: the Collateral Factor (CF).
Collateral Factor (CF): Defines the maximum percentage of an asset’s value that can be used to back borrowing. Similar to LT.
5. Minor Naming Changes
Aave v4 also introduces a few parameter renamings:
reserveFactor→liquidityFee
Asset Onboarding Flow
The onboarding of an asset follows a two-step process:
1. Add Asset to the Hub
Assets are first registered at the Hub level with a unique assetId. This step defines the global reference for the asset and establishes shared liquidity across all Spokes connected to the Hub.
uint256 assetId = hub.addAsset(
underlying, // e.g., USDC (SC Address)
decimals, // e.g., 6
feeReceiver, // account that receives liquidity fees
irStrategy, // interest rate strategy contract
irData // encoded IR parameters
);
- A unique
assetIdis assigned (for example,assetId = 1for USDC). - This
assetIdis globally consistent across all Spokes linked to the Hub.
2. Add Reserve in Each Spoke
Each Spoke manages its own markets using reserveIds, which map back to Hub-level assetIds.
In Spoke A:
uint256 reserveIdA = spokeA.addReserve(
hub, // address of the Hub that holds the liquidity
assetId, // global asset identifier from the Hub (e.g., 1 = USDC)
priceSource, // oracle/price feed for the asset in this Spoke
config, // ReserveConfig: static parameters (paused, frozen, borrowable, collateralRisk)
dynamicConfig // DynamicReserveConfig: parameters like collateralFactor, maxLiquidationBonus, liquidationFee
);
Example: reserveIdA = 0 → assetId = 1 (USDC)
It is possible to have different oracles for the same asset across Spokes.
In Spoke B:
uint256 reserveIdB = spokeB.addReserve(
hub, // same Hub, so liquidity comes from the same pool
assetId, // global asset identifier (same USDC = 1)
priceSource, // oracle reference for this Spoke (can differ between Spokes)
config, // Spoke-specific ReserveConfig
dynamicConfig // Spoke-specific DynamicReserveConfig
);
Example: reserveIdB = 2 → assetId = 1 (USDC)
This makes it explicit that:
- The Hub owns the liquidity and global identity (
assetId). - Each Spoke sets its own local rules (
reserveId,config,dynamicConfig) for how the asset is treated in that risk market.
Example Mainnet Setup: Hubs, Spokes and Caps
The diagram below presents a simplified illustrative mainnet setup. It is not intended to reflect a real configuration, but rather to demonstrate how credit lines can be structured and to showcase the flexibility of the Hub and Spoke architecture.
Each Hub can connect to multiple Spokes and each Spoke can in turn connect to multiple Hubs, enabling many-to-many relationships.
For every (hub, assetId, spoke) pair, governance can configure independent add caps and draw caps, controlling how much liquidity flows in or out of the Hub through that Spoke.
Importantly, the same assetId cannot be added to the same Spoke more than once, preserving consistency of asset references.
The example also shows how special configurations are possible: for instance, Spoke-3 has an extra credit line to borrow USDC and USDT from Hub-1 with a 0 add cap and a 50M draw cap, allowing borrowing but preventing supply back into that Hub. This demonstrates how Hubs own global liquidity and Spokes enforce market-level rules while maintaining strict caps and mappings.
Asset Flows
Supply Flow
-
User → Spoke:
User calls
spoke.supply(reserveId, amount, userAddress). -
Spoke → Hub:
Spoke validates the action (checks caps, paused/frozen status), then calls
hub.add(assetId, amount, userAddress). -
Hub (msg.sender = Spoke):
Hub increases liquidity, mints
addedSharesto the Spoke’s account, and transfers ERC20 tokens from the user into the Hub’s vault. -
User → Position:
User’s position in the Spoke is updated to reflect their supplied amount (tracked in
suppliedShares).
Borrow Flow
-
User → Spoke:
User calls
spoke.borrow(reserveId, amount, userAddress). -
Spoke → Hub:
Spoke validates collateral, then calls
hub.draw(assetId, amount, userAddress). -
Hub (msg.sender = Spoke):
Hub deducts liquidity, increases Spoke’s debt (
drawnShares), and transfers ERC20 tokens. -
User → Wallet:
User wallet receives ERC20 tokens directly from the Hub.
New Features
1- Liquidation Mechanism
1.1- Dynamic Liquidation Bonus
In Aave v4, the liquidation bonus is dynamic:
- It starts small when the user is just below the liquidation threshold.
- It increases progressively as the health factor falls.
- The scaling is linear between the minimum and maximum bonus values
The bonus is calculated in the LiquidationLogic.calculateLiquidationBonus function.
bonus = f(healthFactorForMaxBonus,
liquidationBonusFactor,
healthFactor,
maxLiquidationBonus)
healthFactorForMaxBonus: the health factor at which the maximum bonus kicks in.
maxLiquidationBonus: the absolute ceiling set per reserve.
liquidationBonusFactor: controls how quickly the liquidation bonus ramps up by determining the minLiquidationBonus
The calculateLiquidationBonus function first computes the minimum liquidation bonus based on liquidationBonusFactor:
With maxLiquidationBonus and minLiquidationBonus defined, and knowing that the maximum LB applies once the health factor reaches healthFactorForMaxBonus, the function then performs a linear interpolation between minLiquidationBonus and maxLiquidationBonus as a function of health factor.
For example, with maxLiquidationBonus set to 15% and liquidationBonusFactor to 40%, the bonus rises linearly from a minimum of about 6% at a health factor of 1.0 to the 15% ceiling once the health factor drops to 0.9 (healthFactorForMaxBonus).
1.2- Dynamic Determination of Repayable Debt
In Aave v4 there is no longer a fixed close factor parameter. Instead, the protocol derives the maximum repayable debt for a single liquidation directly from risk parameters and the borrower’s position.
The maximum debt a liquidator can repay in a single call is the minimum of three quantities:
debtReserveBalance: the user’s outstanding debt balance in the repaid reserve.debtToCover: the liquidator’s stated intent, i.e. how much debt they are willing to repay.debtToTarget: the exact debt amount required to push the position’s health factor up to the configured target health factor of the spoke.
Once the liquidateUser function is called, the protocol computes the liquidation amounts inside _calculateLiquidationAmounts. This function calls _calculateMaxDebtToLiquidate to determine the repayable debt (debtToLiquidate).
The sequence to calculate debtToLiquidate is as follows:
- The function first computes
ensuring repayment cannot exceed either the user’s outstanding balance or the liquidator’s stated intention.
- It then computes
debtToTargetthrough_calculateDebtToTargetHealthFactor. This value represents the repayment needed for the borrower’s health factor to reach the target health factor of the Spoke. The formula is:
where
totalDebtInBaseCurrencyis the borrower’s total debt valued in the base currency,targetHFis the health factor target to be achieved after liquidation is performed, as configured for the Spoke,currentHFis the borrower’s current health factor,debtAssetPriceis the oracle price of the debt assetliquidationPenaltyis the liquidation bonus adjusted by the collateral factor.
This formula ensures that the position is not over-liquidated beyond what is necessary to restore target health factor.
- Finally, the function returns:
This design makes the effective close factor dynamic. Near the liquidation threshold, only small amounts of debt may be repaid since debtToTarget is low. As the position deteriorates, the repayable amount increases.
This design can prolong the unprofitability of liquidations when the health factor begins to drop below 1, because the debtToTarget remains low as positions first become eligible for liquidation. If the liquidationBonusFactor is also set too low, the liquidation bonus will be small at that point. This combination can delay liquidations, particularly for small positions.
2- Dynamic Risk Configuration
Dynamic Risk Configuration makes it possible to update market risk parameters (CF and LB) in real time, e.g, tightening conditions when liquidity dries up, while avoiding the downside of liquidations for existing positions.
Users with existing positions are only affected when they take on additional risk (borrow more, withdraw collateral, disable collateral). Otherwise, they continue using the old configuration unless governance or Risk SPs choose to enforce an update via updateDynamicReserveConfig call.
This feature allows multiple risk configurations to exist side by side for the same asset within a Spoke.
Example
Suppose a market is launched with an initial configKey: 0, where the CF is set to 75% and the LB is set to 5%.
After 180 days, governance (through Risk SPs) calls addDynamicReserveConfig, creating configKey: 1 with updated parameters: CF = 65% and LB = 10%.
- Existing positions created under
configKey: 0continue using the old parameters (CF = 75%, LB = 5%) until they perform a risk-increasing action (such as borrowing more, withdrawing collateral, or disabling collateral). - New positions created after
configKey: 1was activated are bound to the new parameters (CF = 65%, LB = 10%) - Governance or Risk SPs retain the ability to enforce updates across older positions if necessary, but by default, no immediate liquidations occur when parameters are changed.
Note: The system supports up to 65,536 (2^16) active configurations. If this limit is ever reached, the oldest configurations begin to be overwritten, which may affect users still bound to them.
3- Risk Premiums
Risk premiums introduce a more granular pricing of borrowing costs based on the quality of collateral supplied by users. They apply additional interest charges, on top of the utilization-based interest accrual, that reflect the composition and quality of a user’s collateral.
Update mechanics:
- RP_u is updated when user actions affect collateralization (borrow, repay, withdraw, liquidate).
- It can also be permissionlessly refreshed via
updateUserRiskPremiumif the user benefits from the update, or forcibly updated by governance if a position has accumulated excessive risk without user interaction. UpdateUserRiskPremiumis emitted every time RP_u gets updated.
Spoke Risk Premium
It represents the average risk across all open borrowing positions in a given Spoke. It allows each Spoke to reflect its overall risk posture, adjusting costs for suppliers based on how risky the pool of users in that Spoke is.
It is calculated as some form of average of the user risk premiums (RP_u) of all active/open borrow positions in that Spoke. Thus, if many users in the Spoke are collateralizing with riskier assets, the Spoke Risk Premium will be higher.
It helps to adjust supply side yields, a higher Spoke Risk Premium indicates greater risk in that Spoke, possibly leading to higher compensation for suppliers.
It gives risk managers a metric to monitor Spoke level risk accumulation.
5- Effects of Risk Premium on Supply Rate of an Asset
Pool’s utilization-based variable borrow rate (drawnRate)
The pool’s base rate comes from AssetInterestRateStrategy.calculateInterestRate
Usage ratio:
Piecewise linear curve:
Let base = baseVariableBorrowRate, s1 = variableRateSlope1, s2 = variableRateSlope2, and u* = optimalUsageRatio:
if u ≤ u*:
Everything is similar to how Aave v3 calculates the borrow rate for the asset, except for the addition of swept for reinvestment when utilized.
Per-user collateral risk premium (RP) layered on top
On the Spoke side, the user’s risk premium is applied by minting ghost “premium” debt shares proportional to the user’s drawn shares:
premiumShares = drawnShares.percentMulUp(userRiskPremium)
premiumOffset = hub.previewDrawByShares(assetId, premiumShares)
Because premium shares grow at the same drawnRate, the user’s effective borrow rate is:
where RP is the user’s risk premium (e.g., 5% → 0.05). Implementation-wise, the system realizes this via the extra premium shares; the math is equivalent to multiplying the base rate by 1+RP.
Premium shares are for “bookkeeping” (ghost principal) so that only the premium interest is actually paid.
Supply rate in the Hub
Suppliers are represented by added shares (addedShares) against a pool that contains:
where:
- liquidity: Idle underlying held in the Hub, immediately borrowable.
- swept: Assets moved to the reinvestment controller via
sweep(), still owned by the pool (counted in the base) and returnable withreclaim() - deficit: Recognized losses/bad debt reported by spokes
- totalOwed: What borrowers currently owe = drawn + premium.
- drawn = principal plus base interest (tracked via
drawnShares * drawnIndex). - premium = accrued premium interest from user risk premiums (interest-only “ghost” exposure).
- drawn = principal plus base interest (tracked via
Premium doesn’t change utilization but does increase interest flowing to LPs and is included in totalOwed
Linear growth of the drawn index at drawnRate applied to both:
drawnShares(base borrow)premiumShares(user risk premium component)
Putting it together, the instantaneous, net supply rate is:
Suppliers earn by the growth of the “added index”, i.e., the ratio totalAddedAssets / totalAddedShares. As totalOwed grows (borrower interest), LP asset-per-share grows.
The numerator uses interest-bearing exposures (drawn + premium exposure)
The denominator is the full LP base: liquidity + swept + deficit + totalOwed
Toy example
Pool setup:
- 3 borrowers with different risk premiums
- Liquidity = 60, Swept = 10, Deficit = 0
- Drawn: A=20, B=10, C=25 → Total drawn = 55
- Utilization u = 55/(60+55+10) = 55/125 = 0.44 = 44%
Rate curve:
base=2%, slope1=10%, slope2=60%, optimal=0.80 ⇒ u < u*
Borrower risk premiums (RP):
A=5%, B=15%, C=0%
Premium is modeled as ghost shares: effective exposure = drawn × (1+RP)
Users pay r_pool on total effective exposure (drawnShares + premiumShares)
so:
Per-borrower breakdown
| Borrower | Drawn | RP | Premium exposure (= Drawn×RP) | Effective exposure (Drawn+Premium) | User borrow APR | Annual interest (gross) |
|---|---|---|---|---|---|---|
| A | 20 | 5% | 1.00 | 21.00 | 7.875% | 20 × 7.875% = 1.575 |
| B | 10 | 15% | 1.50 | 11.50 | 8.625% | 10 × 8.625% = 0.8625 |
| C | 25 | 0% | 0.00 | 25.00 | 7.5% | 25 × 7.5% = 1.875 |
| Totals | 55 | — | 2.50 | 57.50 | — | 4.3125 |
RP doesn’t change utilization or r_pool. It adds interest-bearing exposure (premium shares) on top of drawn, so borrowers with higher RP pay proportionally more.
6- Deficit Reporting Mechanism
Aave v4 introduces a new mechanism for handling bad debt at the Hub level via the reportDeficit function. This feature ensures that when a Spoke market suffers losses (for example, liquidations that fail to fully cover a borrower’s debt), the Hub can formally account for the shortfall in its state.
function reportDeficit(
uint256 assetId,
uint256 drawnAmount,
uint256 premiumAmount,
PremiumDelta calldata premiumDelta
) external returns (uint256)
Purpose
- Allows Spokes to communicate unrecoverable debt to the Hub.
- Updates the Hub’s accounting by:
- Reducing drawn shares.
- Applying premium deltas.
- Increasing the Hub’s
deficitfor the specifiedassetId.
Risk Parameter Update Functions
This section summarizes the key functions in Aave v4 that allow governance and Risk SPs to update or enforce changes to risk-related parameters.
1- updateLiquidationConfig - Spoke
Updates the global liquidation parameters within a Spoke, which determine how liquidations occur.
function updateLiquidationConfig(LiquidationConfig calldata config) external restricted
Updates the global liquidation parameters, including:
targetHealthFactor: The health factor to which a user’s position should be restored after a successful liquidationliquidationBonusFactor: A scaling coefficient (basis points) that determines the minimum liquidation bonus and controls how quickly the bonus ramps up as a user’s health factor falls below the liquidation threshold. For example, if the maximum liquidation bonus is set to 10% and this factor is 60%, the minimum bonus will be 6%. A higher value therefore results in a higher floor and a steeper ramp, providing stronger incentives to liquidators earlier as positions become riskier.healthFactorForMaxBonus: The health factor level below which the liquidation bonus reaches its configured maximum (maxLiquidationBonusper asset).
Event Emitted
event UpdateLiquidationConfig(LiquidationConfig config);
Configurator Helper Functions
In practice, SPs or the Risk Oracle do not need to call updateLiquidationConfig directly. Instead, they can use SpokeConfigurator.sol, which provides cleaner interfaces for adjusting individual fields:
updateLiquidationTargetHealthFactor: Updates only the targetHealthFactor of the Spoke.
function updateLiquidationTargetHealthFactor(
address spoke,
uint256 targetHealthFactor
) external onlyOwner
updateHealthFactorForMaxBonus: Updates only the healthFactorForMaxBonus of the Spoke.
function updateHealthFactorForMaxBonus(
address spoke,
uint256 healthFactorForMaxBonus
) external onlyOwner
updateLiquidationBonusFactor: Updates only the liquidationBonusFactor for the Spoke.
function updateLiquidationBonusFactor(
address spoke,
uint256 liquidationBonusFactor
) external onlyOwner
2- updateReserveConfig - Spoke
Adjusts a single reserve’s static risk-related settings within a Spoke.
function updateReserveConfig(uint256 reserveId, ReserveConfig calldata config) external restricted
Updates:
pausedandfrozen: Operational flags to pause or freeze a market.borrowable: Whether borrowing is currently allowed for this asset.collateralRisk: Basis point value representing the risk of the asset when used as collateral. This feeds into the borrower’s risk premium, affecting the additional interest charged.
Event Emitted
event UpdateReserveConfig(uint256 indexed reserveId, ReserveConfig config);
Configurator Helper Functions
updatePaused: Updates only the paused flag of the reserve in the Spoke.
function updatePaused(address spoke, uint256 reserveId, bool paused) external onlyOwner
updateFrozen: Updates only the frozen flag of the reserve in the Spoke.
function updateFrozen(address spoke, uint256 reserveId, bool frozen) external onlyOwner
updateBorrowable: Updates only the borrowable flag of the reserve in the Spoke.
function updateBorrowable(address spoke, uint256 reserveId, bool borrowable) external onlyOwner
updateCollateralRisk: Updates only the collateralRisk parameter of the reserve in the Spoke.
function updateCollateralRisk(address spoke, uint256 reserveId, uint256 collateralRisk) external onlyOwner
pauseAllReserves / freezeAllReserves: Apply paused or frozen to every reserve in the Spoke.
function pauseAllReserves(address spoke) external onlyOwner
function freezeAllReserves(address spoke) external onlyOwner
3- addDynamicReserveConfig - Spoke
Creates a new version of the dynamic risk parameters for a reserve without modifying historical configurations.
function addDynamicReserveConfig(
uint256 reserveId,
DynamicReserveConfig calldata dynamicConfig
) external restricted returns (uint16)
Adds a new DynamicReserveConfig, which contains:
collateralFactor: Maximum percentage (bps) of the asset’s value that counts toward borrowing power.maxLiquidationBonus: Upper limit (bps) of the liquidation bonus (a 10% bonus is represented as 110_00).liquidationFee: Additional fee (bps) charged during liquidation, share of the protocol.
A new configKey is assigned and becomes the latest configuration for that reserve.
Event Emitted
event AddDynamicReserveConfig(
uint256 indexed reserveId,
uint16 indexed configKey,
DynamicReserveConfig config
);
Configurator Helper Functions
addCollateralFactor: Adds a new config with updated collateralFactor.
function addCollateralFactor(address spoke
, uint256 reserveId
, uint16 collateralFactor) external onlyOwner
addLiquidationBonus: Adds a new config with updated maxLiquidationBonus.
function addLiquidationBonus(address spoke
, uint256 reserveId
, uint256 liquidationBonus) external onlyOwner
addLiquidationFee: Adds a new config with updated liquidationFee.
function addLiquidationFee(address spoke
, uint256 reserveId
, uint256 liquidationFee) external onlyOwner
4- updateDynamicReserveConfig - Spoke
Updates an existing dynamic configuration key for a reserve.
function updateDynamicReserveConfig(
uint256 reserveId,
uint16 configKey,
DynamicReserveConfig calldata dynamicConfig
) external restricted
Modifies the same parameters as addDynamicReserveConfig (collateralFactor, maxLiquidationBonus and liquidationFee) for the specified configKey, allowing tuning of parameters already in use.
Event Emitted
actevent UpdateDynamicReserveConfig(
uint256 indexed reserveId,
uint16 indexed configKey,
DynamicReserveConfig config
);
Configurator Helper Functions
updateCollateralFactor: Updates only the collateralFactor of a given configKey.
function updateCollateralFactor(address spoke
, uint256 reserveId
, uint16 configKey
, uint16 collateralFactor) external onlyOwner
updateMaxLiquidationBonus: Updates only the maxLiquidationBonus of a given configKey.
function updateMaxLiquidationBonus(address spoke
, uint256 reserveId
, uint16 configKey
, uint256 maxLiquidationBonus) external onlyOwner
updateLiquidationFee: Updates only the liquidationFee of a given configKey.
function updateLiquidationFee(address spoke
, uint256 reserveId
, uint16 configKey
, uint256 liquidationFee) external onlyOwner
5- updateSpokeConfig - Hub
Configures the credit line caps and activity status for a given (assetId, spoke) pair. This function is critical in controlling how much liquidity a Spoke can add (supply) or draw (borrow) from a Hub.
function updateSpokeConfig(
uint256 assetId,
address spoke,
SpokeConfig calldata config
) external restricted
Updates the following parameters:
active: Boolean flag that determines whether the Spoke is active for this asset. If set to false, all economic activity is blocked, including supply (add), withdraw (remove), borrow (draw), repay (restore), deficit reporting, fee payment, and share transfers.addCap: Maximum amount of the asset that can be supplied (added) from this Spoke into the Hub. This is enforced in_validateAdd.drawCap: Maximum amount of the asset that can be borrowed (drawn) by this Spoke from the Hub. This is enforced in_validateDraw.
These caps are per-asset per-Spoke values, meaning each Spoke has independent addCap and drawCap for every asset it connects to.
Event Emitted
event UpdateSpokeConfig(uint256 indexed assetId
, address indexed spoke
, SpokeConfig config);
Configurator Helper Functions
In practice, SPs or the Risk Oracle does not need to call updateSpokeConfig directly. Instead, it should use HubConfigurator.sol, which provides cleaner interfaces for adjusting individual fields:
updateSpokeSupplyCap: Updates only the addCap for a given (assetId, spoke).
function updateSpokeSupplyCap(
address hub,
uint256 assetId,
address spoke,
uint256 addCap
) external onlyOwner
updateSpokeDrawCap: Updates only the drawCap for a given (assetId, spoke).
function updateSpokeDrawCap(
address hub,
uint256 assetId,
address spoke,
uint256 drawCap
) external onlyOwner
updateSpokeCaps: Updates both addCap and drawCap in a single call.
function updateSpokeCaps(
address hub,
uint256 assetId,
address spoke,
uint256 addCap,
uint256 drawCap
) external onlyOwner
6- updateAssetConfig - Hub
Configures core parameters of an asset at the Hub level. This function controls how liquidity is accounted for, where protocol fees are directed, and how interest rates are set.
function updateAssetConfig(
uint256 assetId,
AssetConfig calldata config,
bytes calldata irData
) external restricted
Updates the following parameters for the given assetId:
feeReceiver: Address that receives protocol fees associated with this asset.liquidityFee: Percentage fee charged on supplied liquidity before earnings are distributed to suppliers (functionally similar to the reserve factor in Aave v3).irStrategy: Address of the interest rate strategy contract that computes borrow and supply rates.reinvestmentController: Address allowed to sweep and reinvest idle liquidity of this asset.
Event Emitted
event UpdateAssetConfig(uint256 indexed assetId, AssetConfig config);
Configurator Helper Functions
updateLiquidityFee: Updates only the liquidityFee.
function updateLiquidityFee(
address hub,
uint256 assetId,
uint256 liquidityFee
) external onlyOwner
updateFeeReceiver: Updates the feeReceiver address.
function updateFeeReceiver(
address hub,
uint256 assetId,
address feeReceiver
) external onlyOwner
updateFeeConfig: Updates both liquidityFee and feeReceiver.
function updateFeeConfig(
address hub,
uint256 assetId,
uint256 liquidityFee,
address feeReceiver
) external onlyOwner
updateInterestRateStrategy: Updates the irStrategy contract and its associated data (irData).
function updateInterestRateStrategy(
address hub,
uint256 assetId,
address irStrategy,
bytes calldata irData
) external onlyOwner
updateReinvestmentController: Sets the reinvestmentController for the asset.
function updateReinvestmentController(
address hub,
uint256 assetId,
address reinvestmentController
) external onlyOwner
Adjustable Risk Parameters
This section outlines the key risk parameters in Aave v4 that are configurable by Risk SPs. The goal is to provide both a brief definition and practical intuition for how each parameter affects user outcomes and risk.
Risk Parameter Control Matrix
| Parameter | Definition | Function(s) | SC | Control Level |
|---|---|---|---|---|
Collateral Risk (CRᵢ) — collateralRisk |
Basis point value (0–1000%) representing intrinsic risk of an asset used as collateral. Higher values increase user borrowing cost. | updateReserveConfig /updateCollateralRisk |
Spoke.sol | Per reserve |
Collateral Factor (CF) — collateralFactor |
Max % of asset value (bps) that counts toward borrowing power. | addDynamicReserveConfig, updateDynamicReserveConfig, addCollateralFactor, updateCollateralFactor |
Spoke.sol | Per reserve |
Max Liquidation Bonus — maxLiquidationBonus |
Upper limit of liquidation incentive (bps) liquidators can receive. | addDynamicReserveConfig, updateDynamicReserveConfig, addLiquidationBonus, updateMaxLiquidationBonus |
Spoke.sol | Per reserve |
Liquidation Fee — liquidationFee |
Portion of the liquidation bonus (bps) that is redirected to the DAO treasury. | addDynamicReserveConfig, updateDynamicReserveConfig, addLiquidationFee, updateLiquidationFee |
Spoke.sol | Per reserve |
Target Health Factor — targetHealthFactor |
Health factor to which a user’s position is restored post-liquidation. | updateLiquidationConfig , updateLiquidationTargetHealthFactor |
Spoke.sol | Per Spoke |
Liquidation Bonus Factor — liquidationBonusFactor |
Scaling coefficient (bps) that controls minimum liquidation bonus. | updateLiquidationConfig , updateLiquidationBonusFactor |
Spoke.sol | Per Spoke |
HF for Max Bonus — healthFactorForMaxBonus |
Health factor threshold below which maximum liquidation bonus applies. | updateLiquidationConfig /updateHealthFactorForMaxBonus |
Spoke.sol | Per Spoke |
Paused Flag — paused |
Boolean that fully blocks a reserve (no supply, borrow, withdraw, repay). | updateReserveConfig / updatePaused/pauseAllReserves |
Spoke.sol | Per reserve |
Frozen Flag — frozen |
Boolean that blocks new supply and new borrows but allows withdraw/repay. | updateReserveConfig / updateFrozen / freezeAllReserves |
Spoke.sol | Per reserve |
Borrowable Flag — borrowable |
Boolean that determines if borrowing is enabled for a reserve. | updateReserveConfig / updateBorrowable |
Spoke.sol | Per reserve |
Fee Receiver — feeReceiver |
Address that collects liquidity fees for an asset. | updateAssetConfig /updateFeeReceiver / updateFeeConfig |
Hub.sol | Per asset |
Liquidity Fee — liquidityFee |
Protocol fee (bps) on liquidity before distributing supplier earnings (like reserve factor). | updateAssetConfig / updateLiquidityFee / updateFeeConfig |
Hub.sol | Per asset |
Interest Rate Strategy — irStrategy |
Contract that defines interest rate model for an asset. | updateAssetConfig / updateInterestRateStrategy |
Hub.sol | Per asset |
Reinvestment Controller — reinvestmentController |
Address authorized to sweep and reinvest idle liquidity. | updateAssetConfig / updateReinvestmentController |
Hub.sol | Per asset |
Add Cap — addCap |
Maximum amount of an asset that a Spoke can supply into the Hub. | updateSpokeConfig / updateSpokeSupplyCap/updateSpokeCaps |
Hub.sol | Per asset per Spoke |
Draw Cap — drawCap |
Maximum amount of an asset that a Spoke can borrow from the Hub. | updateSpokeConfig / updateSpokeDrawCap / updateSpokeCaps |
Hub.sol | Per asset per Spoke |
Active Flag (Spoke-Asset) — active |
Boolean indicating whether a Spoke is active for a specific asset. | updateSpokeConfig / updateSpokeActive/ pauseAsset /pauseSpoke |
Hub.sol | Per asset per Spoke |
Freezing Functions
Circuit breaker functions allow governance and Risk Stewards to contain risk by quickly halting or constraining activity. These tools operate at different levels of granularity (reserve, spoke, hub) and can either fully pause activity or freeze only certain flows.
Spoke Level
1- pauseAllReserves
function pauseAllReserves(address spoke) external onlyOwner
- Scope: All reserves in a Spoke.
- Effect: Sets
paused = truefor every reserve. - Impact: Blocks new supply, borrow, withdraw, and repay actions across the entire Spoke.
2- freezeAllReserves
function freezeAllReserves(address spoke) external onlyOwner
- Scope: All reserves in a Spoke.
- Effect: Sets
frozen = truefor every reserve. - Impact: Prevents new supply and new borrow actions, but withdrawals and repayments are still allowed.
3- updatePaused
function updatePaused(address spoke, uint256 reserveId, bool paused) external onlyOwner
- Scope: A single
(reserveId, spoke)pair. - Effect: Toggles the
pausedflag. - Impact: Full stop for that reserve only.
4- updateFrozen
function updateFrozen(address spoke, uint256 reserveId, bool frozen) external onlyOwner
- Scope: A single
(reserveId, spoke)pair. - Effect: Toggles the
frozenflag. - Impact: Blocks new supply and borrows, but withdrawals and repayments are still possible for that reserve.
Hub Level
1- freezeAsset
function freezeAsset(address hub, uint256 assetId) external onlyOwner
- Scope: A single
assetIdacross all **Spokes connected to a Hub. - Effect: Sets
addCapanddrawCapto 0 for every(assetId, spoke)pair. - Impact: Prevents new supply and borrowing for the asset, but allows withdrawals and repayments.
2- pauseAsset
function pauseAsset(address hub, uint256 assetId) external onlyOwner
- Scope: A single
assetIdacross all Spokes connected to a Hub. - Effect: Sets
active = falsefor every(assetId, spoke)pair. - Impact: Blocks all actions including supply, withdraw, borrow, repay, deficit reporting, fee payments, and share transfers.
3- pauseSpoke
function pauseSpoke(address hub, address spoke) external onlyOwner
- Scope: A single Spoke across all assets.
- Effect: Sets
active = falsefor every asset in that Spoke. - Impact: Full shutdown. Spoke cannot perform any actions for any asset.
4- freezeSpoke
function freezeSpoke(address hub, address spoke) external onlyOwner
- Scope: A single Spoke across all assets.
- Effect: Sets
addCap = 0anddrawCap = 0for every(assetId, spoke)pair. - Impact: New supply and borrow blocked, but withdrawals and repayments still allowed.
5- updateSpokeActive
function updateSpokeActive(address hub
, uint256 assetId
, address spoke
, bool active
) external onlyOwner
- Scope: A single
(assetId, spoke)pair. - Effect: Directly toggles the
activeflag for that specific asset-Spoke combination. - Impact: If set to
false, all actions are blocked (supply, withdraw, borrow, repay, deficit reporting, fee payments, and share transfers). If set back totrue, activity resumes under existing caps.


















