# Collateral Management Flow End-to-end execution flow for enabling or disabling a supplied **Token** as collateral in Aave V3. ## Quick Reference | Aspect | Details | |--------|---------| | **Entry Point** | `Pool.setUserUseReserveAsCollateral(asset, useAsCollateral)` | | **Key Transformations** | None (bitmask operation only) | | **State Changes** | `userConfig.data` bitmask updated via `setUsingAsCollateral` | | **Events Emitted** | `ReserveUsedAsCollateralEnabled` or `ReserveUsedAsCollateralDisabled` | --- ## Flow Diagram ```mermaid flowchart TD %% Styling definitions classDef validation fill:#ffcccc,stroke:#ff0000,stroke-width:2px classDef transformation fill:#ccffcc,stroke:#00aa00,stroke-width:2px classDef storage fill:#ccccff,stroke:#0000ff,stroke-width:2px classDef event fill:#ffffcc,stroke:#aaaa00,stroke-width:2px classDef error fill:#ff0000,stroke:#000000,color:#fff classDef decision fill:#ffe6cc,stroke:#ff8800,stroke-width:2px %% Entry point Entry["Pool.setUserUseReserveAsCollateral
asset, useAsCollateral"] --> Execute["SupplyLogic
executeUseReserveAsCollateral"] subgraph StateValidation ["1. State Validation"] direction TB ReserveCheck["ValidationLogic
validateSetUseReserveAsCollateral"] --> CheckActive{"Reserve
Active?"} class ReserveCheck validation CheckActive -->|No| ErrorInactive["REVERT
ReserveInactive"] class ErrorInactive error CheckActive -->|Yes| CheckPaused{"Reserve
Paused?"} CheckPaused -->|Yes| ErrorPaused["REVERT
ReservePaused"] class ErrorPaused error CheckPaused -->|No| StateCheck{"useAsCollateral ==
currentState?"} end Execute --> StateValidation StateCheck -->|Yes| NoOp["No operation
Return early"] StateCheck -->|No| Branch{"Enable or
Disable?"} class Branch decision subgraph EnableFlow ["2. Enable Collateral"] direction TB CheckBalance{"Underlying
Balance > 0?"} class CheckBalance validation CheckBalance -->|No| ErrorBalance["REVERT
UnderlyingBalanceZero"] class ErrorBalance error CheckBalance -->|Yes| ValidateUse["ValidationLogic
validateUseAsCollateral"] class ValidateUse validation ValidateUse -->|Fails| ErrorIsolation["REVERT
UserInIsolationModeOrLtvZero"] class ErrorIsolation error ValidateUse -->|Success| SetEnable["UserConfig
setUsingAsCollateral
bitmask |= bit"] class SetEnable storage SetEnable --> EventEnable["EMIT
ReserveUsedAsCollateralEnabled"] class EventEnable event end subgraph DisableFlow ["3. Disable Collateral"] direction TB SetDisable["UserConfig
setUsingAsCollateral
bitmask &= ~bit"] class SetDisable storage SetDisable --> HFCheck["ValidationLogic
validateHFAndLtvzero"] class HFCheck validation HFCheck -->|HF < 1e18| ErrorHF["REVERT
HealthFactorLowerThanLiquidationThreshold"] class ErrorHF error HFCheck -->|Has LTV0 Collateral| ErrorLtv["REVERT
LtvValidationFailed"] class ErrorLtv error HFCheck -->|Success| EventDisable["EMIT
ReserveUsedAsCollateralDisabled"] class EventDisable event end Branch -->|Enable| EnableFlow Branch -->|Disable| DisableFlow %% Error annotations %% CRITICAL: Disabling collateral requires HF check to prevent self-liquidation %% CRITICAL: Cannot disable if LTV0 collateral exists elsewhere ``` --- ## Step-by-Step Execution ### 1. Entry Point **File:** `contracts/protocol/pool/Pool.sol` ```solidity function setUserUseReserveAsCollateral( address asset, bool useAsCollateral ) public virtual override { SupplyLogic.executeUseReserveAsCollateral( _reserves, _reservesList, _eModeCategories, _usersConfig[msg.sender], msg.sender, asset, useAsCollateral, ADDRESSES_PROVIDER.getPriceOracle(), _usersEModeCategory[msg.sender] ); } ``` ### 2. Execute Use Reserve As Collateral **File:** `contracts/protocol/libraries/logic/SupplyLogic.sol` ```solidity function executeUseReserveAsCollateral( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap storage userConfig, address user, address asset, bool useAsCollateral, address priceOracle, uint8 userEModeCategory ) external { DataTypes.ReserveData storage reserve = reservesData[asset]; DataTypes.ReserveConfigurationMap memory reserveConfigCached = reserve.configuration; // Validate reserve state (active, not paused) ValidationLogic.validateSetUseReserveAsCollateral(reserveConfigCached); // Check if state is already as requested if (useAsCollateral == userConfig.isUsingAsCollateral(reserve.id)) return; if (useAsCollateral) { // ENABLING: Must have balance and pass collateral validation require( IAToken(reserve.aTokenAddress).scaledBalanceOf(user) != 0, Errors.UnderlyingBalanceZero() ); require( ValidationLogic.validateUseAsCollateral( reservesData, reservesList, eModeCategories, userConfig, reserveConfigCached, asset, userEModeCategory ), Errors.UserInIsolationModeOrLtvZero() ); userConfig.setUsingAsCollateral(reserve.id, asset, user, true); } else { // DISABLING: Update config first, then validate HF userConfig.setUsingAsCollateral(reserve.id, asset, user, false); ValidationLogic.validateHFAndLtvzero( reservesData, reservesList, eModeCategories, userConfig, asset, user, priceOracle, userEModeCategory ); } } ``` ### 3. Validate Set Use Reserve As Collateral **File:** `contracts/protocol/libraries/logic/ValidationLogic.sol` ```solidity function validateSetUseReserveAsCollateral( DataTypes.ReserveConfigurationMap memory reserveConfig ) internal pure { (bool isActive, , , bool isPaused) = reserveConfig.getFlags(); require(isActive, Errors.ReserveInactive()); require(!isPaused, Errors.ReservePaused()); } ``` ### 4. Validate Use As Collateral (Enable Path) **File:** `contracts/protocol/libraries/logic/ValidationLogic.sol` ```solidity function validateUseAsCollateral( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap memory userConfig, DataTypes.ReserveConfigurationMap memory reserveConfig, address asset, uint8 userEModeCategory ) internal view returns (bool) { // Check if asset can be used as collateral (LTV > 0) if (reserveConfig.getLtv() == 0) { return false; } // Check eMode compatibility if (userEModeCategory != 0) { DataTypes.EModeCategory memory eModeCategory = eModeCategories[userEModeCategory]; // In eMode, can only use collateral assets from the category if (!eModeCategory.collateralBitmap.getBit(reserveConfig.getReserveId())) { return false; } } // Check isolation mode restrictions if (userConfig.isUsingAsCollateralAny()) { // If user has any zero-LTV collateral, cannot add new collateral // unless it's the same asset } return true; } ``` ### 5. Set Using As Collateral (UserConfiguration) **File:** `contracts/protocol/libraries/configuration/UserConfiguration.sol` ```solidity function setUsingAsCollateral( DataTypes.UserConfigurationMap storage self, uint256 reserveIndex, address asset, address user, bool usingAsCollateral ) internal { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.InvalidReserveIndex()); // Calculate bit position: collateral flag is at (reserveIndex * 2) + 1 // Borrow flag is at (reserveIndex * 2) uint256 bit = 1 << ((reserveIndex << 1) + 1); if (usingAsCollateral) { self.data |= bit; // Set bit emit IPool.ReserveUsedAsCollateralEnabled(asset, user); } else { self.data &= ~bit; // Clear bit emit IPool.ReserveUsedAsCollateralDisabled(asset, user); } } } ``` ### 6. Validate HF And LTV Zero (Disable Path) **File:** `contracts/protocol/libraries/logic/ValidationLogic.sol` ```solidity function validateHFAndLtvzero( mapping(address => DataTypes.ReserveData) storage reservesData, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap memory userConfig, address asset, address from, address oracle, uint8 userEModeCategory ) internal view { (, bool hasZeroLtvCollateral) = validateHealthFactor( reservesData, reservesList, eModeCategories, userConfig, from, userEModeCategory, oracle ); // If the user has an LTV-zero asset, the selected asset must be the LTV0 asset. // This mechanism ensures that a multi-collateral position needs to withdraw/transfer // the LTV0 asset first. if (hasZeroLtvCollateral) { require( getUserReserveLtv( reservesData[asset], eModeCategories[userEModeCategory], userEModeCategory ) == 0, Errors.LtvValidationFailed() ); } } ``` ### 7. Is Using As Collateral Check **File:** `contracts/protocol/libraries/configuration/UserConfiguration.sol` ```solidity function isUsingAsCollateral( DataTypes.UserConfigurationMap memory self, uint256 reserveIndex ) internal pure returns (bool) { unchecked { require(reserveIndex < ReserveConfiguration.MAX_RESERVES_COUNT, Errors.InvalidReserveIndex()); // Check bit at position (reserveIndex * 2) + 1 return (self.data >> ((reserveIndex << 1) + 1)) & 1 != 0; } } ``` --- ## Amount Transformations Collateral management does not involve amount transformations. It is a configuration change that updates a bitmask in the `UserConfigurationMap` data structure. ### UserConfiguration Bitmap Structure ``` UserConfigurationMap.data (256 bits) Bits 0-1: Reserve 0 (bit 0 = borrowing, bit 1 = collateral) Bits 2-3: Reserve 1 (bit 2 = borrowing, bit 3 = collateral) Bits 4-5: Reserve 2 (bit 4 = borrowing, bit 5 = collateral) ... Bits 2n, 2n+1: Reserve n Collateral bit position = (reserveIndex << 1) + 1 ``` ### Bit Operations **Enabling Collateral:** ```solidity uint256 bit = 1 << ((reserveIndex << 1) + 1); self.data |= bit; // Sets the collateral bit to 1 ``` **Disabling Collateral:** ```solidity uint256 bit = 1 << ((reserveIndex << 1) + 1); self.data &= ~bit; // Clears the collateral bit to 0 ``` --- ## Event Details ### ReserveUsedAsCollateralEnabled Event Emitted when a user enables an **Asset** as collateral. ```solidity event ReserveUsedAsCollateralEnabled( address indexed reserve, // Asset address address indexed user // User address ); ``` ### ReserveUsedAsCollateralDisabled Event Emitted when a user disables an **Asset** as collateral. ```solidity event ReserveUsedAsCollateralDisabled( address indexed reserve, // Asset address address indexed user // User address ); ``` --- ## Error Conditions | Error | Condition | File | |-------|-----------|------| | `ReserveInactive` | Reserve is not active | ValidationLogic.sol | | `ReservePaused` | Reserve is paused | ValidationLogic.sol | | `UnderlyingBalanceZero` | User has no aToken balance when enabling | SupplyLogic.sol | | `UserInIsolationModeOrLtvZero` | Cannot enable as collateral (LTV=0 or isolation mode) | SupplyLogic.sol | | `HealthFactorLowerThanLiquidationThreshold` | Disabling would make HF < 1.0 | ValidationLogic.sol | | `LtvValidationFailed` | Has LTV0 collateral but trying to disable non-LTV0 **Asset** | ValidationLogic.sol | | `InvalidReserveIndex` | Reserve index exceeds maximum | UserConfiguration.sol | --- ## Related Flows - [Supply Flow](./supply.md) - Initial deposit that creates collateral - [Withdraw Flow](./withdraw.md) - Removing collateral from position - [Borrow Flow](./borrow.md) - Borrowing against collateral - [Liquidation Flow](./liquidation.md) - When collateral is seized --- ## Source File Locations ``` contracts/protocol/pool/Pool.sol contracts/protocol/libraries/logic/SupplyLogic.sol contracts/protocol/libraries/logic/ValidationLogic.sol contracts/protocol/libraries/configuration/UserConfiguration.sol contracts/protocol/libraries/types/DataTypes.sol ```