Aave v4: New Features and Risk Parameter Analysis

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:
    • liquidity
    • addedShares (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 (safeTransfer and safeTransferFrom).

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).
  • Enforces collateral checks, health factor logic, liquidation eligibility.
  • Forwards calls like borrow, repay, supply, withdraw into 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:

  • reserveFactorliquidityFee

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 assetId is assigned (for example, assetId = 1 for USDC).
  • This assetId is 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 addedShares to 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:

  1. debtReserveBalance: the user’s outstanding debt balance in the repaid reserve.
  2. debtToCover: the liquidator’s stated intent, i.e. how much debt they are willing to repay.
  3. 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:

  1. The function first computes

ensuring repayment cannot exceed either the user’s outstanding balance or the liquidator’s stated intention.

  1. It then computes debtToTarget through _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

  • totalDebtInBaseCurrency is the borrower’s total debt valued in the base currency,
  • targetHF is the health factor target to be achieved after liquidation is performed, as configured for the Spoke,
  • currentHF is the borrower’s current health factor,
  • debtAssetPrice is the oracle price of the debt asset
  • liquidationPenalty is 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.

  1. 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: 0 continue 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: 1 was 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 updateUserRiskPremium if the user benefits from the update, or forcibly updated by governance if a position has accumulated excessive risk without user interaction.
  • UpdateUserRiskPremium is 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 with reclaim()
  • 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).

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 deficit for the specified assetId.

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 liquidation
  • liquidationBonusFactor : 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 (maxLiquidationBonus per 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:

  • paused and frozen: 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 = true for 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 = true for 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 paused flag.
  • 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 frozen flag.
  • 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 assetId across all **Spokes connected to a Hub.
  • Effect: Sets addCap and drawCap to 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 assetId across all Spokes connected to a Hub.
  • Effect: Sets active = false for 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 = false for 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 = 0 and drawCap = 0 for 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 active flag 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 to true, activity resumes under existing caps.
6 Likes