Aave V3 Pool Operations
The Pool contract is the main entry point for user interactions with Aave V3. This skill covers core operations: supply, withdraw, borrow, repay, and liquidation.
Pool Contract Overview
solidity
abstract contract Pool is VersionedInitializable, PoolStorage, IPool {
using ReserveLogic for DataTypes.ReserveData;
IPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
// Core operations delegate to logic libraries
// SupplyLogic, BorrowLogic, LiquidationLogic, FlashLoanLogic, etc.
}
Supply Operations
supply()
Deposit assets to earn interest:
solidity
function supply(
address asset, // Token to supply
uint256 amount, // Amount to supply
address onBehalfOf, // Recipient of aTokens
uint16 referralCode // Referral tracking
) public virtual;
// With EIP-2612 permit
function supplyWithPermit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) public virtual;
Supply Flow
code
┌──────────────────────────────────────────────────────────────┐ │ SUPPLY FLOW │ ├──────────────────────────────────────────────────────────────┤ │ 1. Validate: asset active, not paused, not frozen │ │ 2. Validate: supply cap not exceeded │ │ 3. Update reserve indexes (accrue interest) │ │ 4. Transfer underlying from user to aToken contract │ │ 5. Mint aTokens to onBehalfOf (scaled amount) │ │ 6. Update virtual underlying balance │ │ 7. Update interest rates │ │ 8. Enable asset as collateral (if first supply) │ └──────────────────────────────────────────────────────────────┘
SupplyLogic
solidity
library SupplyLogic {
function executeSupply(
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(uint256 => address) storage reservesList,
DataTypes.UserConfigurationMap storage userConfig,
DataTypes.ExecuteSupplyParams memory params
) external {
DataTypes.ReserveData storage reserve = reservesData[params.asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
// Validate supply
ValidationLogic.validateSupply(reserveCache, reserve, params.amount);
// Update interest rates and virtual balance
reserve.updateInterestRatesAndVirtualBalance(
reserveCache,
params.asset,
params.amount, // liquidity added
0 // liquidity taken
);
// Transfer and mint
IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, params.amount);
bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
msg.sender,
params.onBehalfOf,
params.amount,
reserveCache.nextLiquidityIndex
);
// Auto-enable as collateral on first supply
if (isFirstSupply) {
if (ValidationLogic.validateAutomaticUseAsCollateral(...)) {
userConfig.setUsingAsCollateral(reserve.id, true);
}
}
}
}
Withdraw Operations
withdraw()
Withdraw supplied assets:
solidity
function withdraw(
address asset, // Token to withdraw
uint256 amount, // Amount (type(uint256).max for all)
address to // Recipient of underlying
) public virtual returns (uint256); // Actual amount withdrawn
Withdraw Flow
code
┌──────────────────────────────────────────────────────────────┐ │ WITHDRAW FLOW │ ├──────────────────────────────────────────────────────────────┤ │ 1. Update reserve indexes │ │ 2. Calculate actual amount (handle max) │ │ 3. Validate health factor remains >= 1 │ │ 4. Burn aTokens from user │ │ 5. Transfer underlying to recipient │ │ 6. Update virtual balance │ │ 7. Update interest rates │ │ 8. Disable as collateral if balance becomes 0 │ └──────────────────────────────────────────────────────────────┘
Borrow Operations
borrow()
Borrow assets against collateral:
solidity
function borrow(
address asset, // Token to borrow
uint256 amount, // Amount to borrow
uint256 interestRateMode, // 2 = Variable (1 = deprecated stable)
uint16 referralCode, // Referral tracking
address onBehalfOf // Debt recipient (requires credit delegation)
) public virtual;
Borrow Flow
code
┌──────────────────────────────────────────────────────────────┐ │ BORROW FLOW │ ├──────────────────────────────────────────────────────────────┤ │ 1. Validate: asset active, not paused, borrowing enabled │ │ 2. Validate: borrow cap not exceeded │ │ 3. Validate: health factor will remain >= 1 │ │ 4. Validate: interest rate mode is VARIABLE │ │ 5. Validate: borrowable in user's eMode (if applicable) │ │ 6. Update reserve indexes │ │ 7. Mint debt tokens to onBehalfOf │ │ 8. Transfer underlying to msg.sender │ │ 9. Update interest rates │ │ 10. Handle isolation mode debt ceiling │ └──────────────────────────────────────────────────────────────┘
BorrowLogic
solidity
library BorrowLogic {
function executeBorrow(
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(uint256 => address) storage reservesList,
mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
DataTypes.UserConfigurationMap storage userConfig,
DataTypes.ExecuteBorrowParams memory params
) public {
DataTypes.ReserveData storage reserve = reservesData[params.asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
// Comprehensive validation
ValidationLogic.validateBorrow(
reservesData,
reservesList,
eModeCategories,
DataTypes.ValidateBorrowParams({
reserveCache: reserveCache,
userConfig: userConfig,
asset: params.asset,
userAddress: params.onBehalfOf,
amount: params.amount,
interestRateMode: params.interestRateMode,
reservesCount: params.reservesCount,
oracle: params.oracle,
userEModeCategory: params.userEModeCategory,
priceOracleSentinel: params.priceOracleSentinel,
isolationModeActive: isolationModeActive,
isolationModeCollateralAddress: isolationModeCollateralAddress,
isolationModeDebtCeiling: isolationModeDebtCeiling
})
);
// Mint debt tokens
IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint(
params.user,
params.onBehalfOf,
params.amount,
reserveCache.nextVariableBorrowIndex
);
// Update rates and transfer
reserve.updateInterestRatesAndVirtualBalance(
reserveCache,
params.asset,
0, // liquidity added
params.amount // liquidity taken
);
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(params.user, params.amount);
}
}
Repay Operations
repay()
Repay borrowed assets:
solidity
function repay(
address asset, // Token to repay
uint256 amount, // Amount (type(uint256).max for all)
uint256 interestRateMode, // 2 = Variable
address onBehalfOf // Debt owner to repay for
) public virtual returns (uint256); // Actual amount repaid
// With permit
function repayWithPermit(
address asset,
uint256 amount,
uint256 interestRateMode,
address onBehalfOf,
uint256 deadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) public virtual returns (uint256);
// Repay using aTokens (no transfer needed)
function repayWithATokens(
address asset,
uint256 amount,
uint256 interestRateMode
) public virtual returns (uint256);
Repay Flow
code
┌──────────────────────────────────────────────────────────────┐ │ REPAY FLOW │ ├──────────────────────────────────────────────────────────────┤ │ 1. Update reserve indexes │ │ 2. Calculate actual repay amount (cap at debt) │ │ 3. Burn debt tokens from onBehalfOf │ │ 4. Transfer underlying from user (or burn aTokens) │ │ 5. Update virtual balance │ │ 6. Update interest rates │ │ 7. Reduce isolation mode debt if applicable │ └──────────────────────────────────────────────────────────────┘
Liquidation
liquidationCall()
Liquidate an undercollateralized position:
solidity
function liquidationCall(
address collateralAsset, // Collateral to seize
address debtAsset, // Debt to repay
address user, // User to liquidate
uint256 debtToCover, // Amount of debt to repay
bool receiveAToken // Receive aTokens or underlying
) public virtual;
Liquidation Flow
code
┌──────────────────────────────────────────────────────────────┐ │ LIQUIDATION FLOW │ ├──────────────────────────────────────────────────────────────┤ │ 1. Validate: user health factor < 1 │ │ 2. Validate: liquidation grace period has passed │ │ 3. Calculate max liquidatable debt (close factor) │ │ 4. Calculate collateral to receive (+ liquidation bonus) │ │ 5. Burn debt tokens from user │ │ 6. Transfer debt payment from liquidator │ │ 7. Transfer collateral to liquidator (aTokens or underlying) │ │ 8. Handle deficit if bad debt occurs │ │ 9. Update interest rates │ └──────────────────────────────────────────────────────────────┘
LiquidationLogic
solidity
library LiquidationLogic {
// Close factor: max 50% of debt can be liquidated per call
uint256 internal constant DEFAULT_LIQUIDATION_CLOSE_FACTOR = 0.5e4;
// When HF < this threshold, 100% can be liquidated
uint256 public constant CLOSE_FACTOR_HF_THRESHOLD = 0.95e18;
function executeLiquidationCall(
mapping(address => DataTypes.ReserveData) storage reservesData,
mapping(uint256 => address) storage reservesList,
mapping(address => DataTypes.UserConfigurationMap) storage usersConfig,
mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
DataTypes.ExecuteLiquidationCallParams memory params
) external {
// Validate liquidation is allowed
LiquidationCallLocalVars memory vars;
(vars.userGlobalCollateral, vars.userGlobalDebt, , , , vars.healthFactor) =
GenericLogic.calculateUserAccountData(...);
require(vars.healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD, Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD);
// Calculate amounts
(vars.actualDebtToLiquidate, vars.actualCollateralToLiquidate, vars.liquidationProtocolFeeAmount) =
_calculateAvailableCollateralToLiquidate(...);
// Execute liquidation
_burnDebtTokens(...);
_liquidateCollateral(...);
}
}
Collateral Management
setUserUseReserveAsCollateral()
Enable/disable an asset as collateral:
solidity
function setUserUseReserveAsCollateral(
address asset,
bool useAsCollateral
) public virtual;
Constraints:
- •Cannot disable if it would cause health factor < 1
- •Cannot disable if asset is the only collateral backing debt
Account Data
getUserAccountData()
Get user's aggregate account information:
solidity
function getUserAccountData(address user) external view returns (
uint256 totalCollateralBase, // Total collateral in base currency
uint256 totalDebtBase, // Total debt in base currency
uint256 availableBorrowsBase, // Remaining borrow capacity
uint256 currentLiquidationThreshold, // Weighted average LT
uint256 ltv, // Current LTV
uint256 healthFactor // HF (< 1 means liquidatable)
);
Health Factor Calculation
solidity
// Health Factor = Σ(Collateral × LT) / Total Debt // If HF < 1, position can be liquidated // Example: // Collateral: 10 ETH @ $2000 = $20,000 // LT: 82.5% // Debt: $15,000 // HF = ($20,000 × 0.825) / $15,000 = 1.10
Interest Rate Mode
Aave V3.2+ only supports variable rate:
solidity
enum InterestRateMode {
NONE,
__DEPRECATED, // Was stable rate
VARIABLE
}
Events
solidity
event Supply(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referralCode); event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); event Borrow(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, DataTypes.InterestRateMode interestRateMode, uint256 borrowRate, uint16 indexed referralCode); event Repay(address indexed reserve, address indexed user, address indexed repayer, uint256 amount, bool useATokens); event LiquidationCall(address indexed collateralAsset, address indexed debtAsset, address indexed user, uint256 debtToCover, uint256 liquidatedCollateralAmount, address liquidator, bool receiveAToken);
Reference Files
- •
src/contracts/protocol/pool/Pool.sol- Main Pool contract - •
src/contracts/protocol/libraries/logic/SupplyLogic.sol- Supply logic - •
src/contracts/protocol/libraries/logic/BorrowLogic.sol- Borrow logic - •
src/contracts/protocol/libraries/logic/LiquidationLogic.sol- Liquidation logic - •
src/contracts/protocol/libraries/logic/ValidationLogic.sol- Validation logic - •
src/contracts/protocol/libraries/logic/GenericLogic.sol- Account calculations