Introduction
With the rapid development of the decentralized finance (DeFi) ecosystem, AAVE V2, as one of the leading decentralized lending protocols, has always been at the forefront of the industry in providing innovative lending and liquidity management solutions. Its unique trustless mechanism and efficient capital utilization have attracted a large number of users and institutions to participate. However, with the popularity of its application and the gradual expansion of the scale of funds involved, the importance of security audits and risk control measures has become increasingly prominent. This manual will explore the core design, key functions and related audit points of the AAVE V2 protocol in depth.
Project Background Overview
AAVE V2 is an open lending platform built on the Ethereum blockchain that allows users to deposit various ERC-20 tokens and earn interest from them, while also allowing them to borrow tokens in the market in the form of interest payments. By introducing the concept of "interest rate market", AAVE V2 realizes decentralized fund pool management and automated interest rate adjustment mechanism. In addition, AAVE V2 also provides advanced functions such as flash loans, mortgage loans and token swaps to meet the diverse needs of users, further consolidating its leading position in the DeFi field.
Project Architecture Analysis

The overall architecture design of AAVE V2 revolves around key functions such as users, fund flow management, mortgage mechanism, liquidation process and interest rate strategy, aiming to provide efficient and secure decentralized lending services. The following is a comprehensive analysis:
User Operation Process
Users:Users can perform various operations such as deposits, borrowing, repayment, withdrawal, exchange of lending rate models, flash loans, and entrusted credit. When users interact with the protocol, the corresponding aTokens will be automatically minted or destroyed according to their operations, representing their deposit rights in the protocol, and they will receive income according to the interest rate strategy.
Entrusted Credit:Users can entrust their credit lines to other users, which expands the flexibility and usage scenarios of the protocol.
Core Components
LendingPool:As the core module, it is responsible for processing all user operation requests, including deposits, borrowing, repayment, exchange of lending rate models, flash loans, and liquidation, and updating interest rates and status.
Collateral Manager:Manages collateral assets to ensure that user borrowing behavior is safe and controllable. When collateral assets are insufficient, the liquidation process will be triggered to protect the overall liquidity of the system.
Libraries:Encapsulates savings logic, verification logic, and general functional logic, such as calculations for liquidation and lending operations, to provide support for LendingPool.
GenericLogic calculates and verifies user status, including asset evaluation, collateral value calculation, health coefficient and other operations.
ReserveLogic is used to manage the reserve pool, track and update the deposit amount, borrowing amount and current interest rate of each asset.
ValidationLogic is responsible for verifying whether the user's operation complies with the protocol rules, and strictly checks the collateral and liabilities when the user deposits, borrows, repays, liquidates, flash loans, switches debt mode, etc.
Debt and Tokenization
Debt Tokens:Used to track the user's borrowing liabilities, 1:1 with the amount of loaned funds. Debt tokens have fixed interest and variable interest options (such as DebtDAI Stable, DebtDAI Variable, etc.), and debt tokens are not transferable.
aTokens:When users deposit assets, 1:1 aTokens are generated to anchor the underlying assets, and these aTokens will continue to appreciate to reflect the interest earned on the deposit. The ratio introduced here is stored together with the principal balance as a ratio, called scaled balance (ScB).
The formula is as follows:

Price and interest rate
Oracles Proxy:Rely on external oracles (Chainlink) to provide asset market price data to evaluate the value of user mortgage assets and ensure the pricing accuracy of lending behavior and the stability of the system.
Lending Rate Oracle:Provide dynamic lending rates based on the system status and market conditions to optimize capital utilization and liquidity.
Configuration and Management
Configurator:Used to configure system parameters, such as risk parameters and lending limits for different assets, and manage various operations of reserves, including activation, borrowing, pledging, freezing, updating parameters, and enabling or disabling functions in emergency situations. Ensure that the protocol can be dynamically adjusted according to market changes.
Other key functions
Liquidation Manager:When the value of the user's collateral drops below the liquidation threshold, manage liquidation operations to protect the system's funds. Liquidators can receive rewards through liquidation operations.
Reserves Balances:Store the system's reserve fund data for calculating and adjusting interest rate strategies.
Interest Rate Strategy
Interest Rate Strategy:Dynamically adjust interest rates to achieve optimal capital allocation based on market and user needs, while taking liquidity risks into account to ensure the flexibility and stability of the system under different market conditions.
Although there are two interest rate models (stable and floating), their model calculations are similar to an inflection point model. The slope1 at the inflection point optimal utilization and the slope2 exceeding the optimal utilization are calculated in segments, and under this condition, they are also divided into fixed interest rate model and variable interest rate model.

The formula is as follows:



Process sorting
Deposit
Users call The deposit function of the LendingPool contract is used for deposits. The function accepts four parameters: asset address, deposit amount, recipient address, and referral code. First, verify that the contract is not in an enabled state, then verify that the deposit amount must be greater than 0 through ValidationLogic.validateDeposit, and confirm that the reserve is in an activated state and is not frozen. The system then updates the reserve state, calls reserve.updateState() to update the liquidity accumulation index and variable borrowing index, and calculates the interest generated during the time period, part of which will be minted and transferred to the protocol treasury.
The formula is as follows:

Then reserve.updateInterestRates is used to dynamically adjust the liquidity interest rate, stable borrowing rate, and variable borrowing rate according to the latest supply and demand relationship (all calculated and updated by the DefaultReserveInterestRateStrategy.calculateInterestRates function). In the asset transfer phase, the system transfers the user's underlying assets to the aToken contract, and at the same time casts an equal amount of aToken to the onBehalfOf address submitted by the user. Among them, aToken uses a scaling mechanism (scaled balance) to handle interest accumulation. If it is the user's first deposit, the system will automatically mark the asset as the user's collateral.
Compared with Compound, the deposit process of AAVE V2 has the following main features:
Supports the specified recipient address (onBehalfOf).
Deposit verification is performed through the ValidationLogic contract.
Update the liquidity accumulation index and variable borrowing index to calculate and allocate the protocol treasury interest.
Simultaneously adjust the three interest rates of liquidity, stable borrowing and variable borrowing.
Use aToken's scaling mechanism (scaled balance) to handle interest.
The first deposit is automatically marked as collateral.
Withdrawal
Users withdraw by calling the withdraw function. First, get the reserve data of the specified asset, including the corresponding aToken address, and check the balance of this user in aToken. Next, call the ValidationLogic.validateWithdraw function to verify the withdrawal request, including checking whether the withdrawal amount is valid, whether the user's balance is sufficient, whether the reserve is active, etc. Among them, GenericLogic.balanceDecreaseAllowed is used to check the user's health factor and whether the withdrawal affects the collateral, which is similar to the role of the getHypotheticalAccountLiquidityInternal function in compound. In the balanceDecreaseAllowed function, calculateUserAccountData and calculateHealthFactorFromBalances functions calculate the liquidation threshold after the funds are withdrawn and check the user's total collateral, total loan amount, and the user's current health factor to determine whether the user's health factor is in a safe state within the liquidity threshold.
The HF calculation formula is as follows:

Then update the status of the reserve, update the interest rate, and pass the withdrawal amount to the function. If the withdrawal amount requested by the user is equal to its current balance, update the user configuration and mark the reserve as no longer used as collateral. Finally, destroy the user's aToken and transfer the withdrawn assets to the specified address.
Compared to Compound, AAVE V2's withdrawal process has the following main features:
Using aToken to represent the user's deposit in the protocol, withdrawals actually destroy aToken.
Allows users to withdraw to a specified address (through the to parameter), increasing flexibility.
Provides options for partial and full withdrawals.
In withdrawal verification, AAVE uses a more complex balanceDecreaseAllowed function to check the impact of withdrawals on the user's overall collateral status.
AAVE's withdrawal process directly updates the interest rate, rather than updating it through the accrueInterest function like Compound.
Borrowing
Users borrow through the borrow function. When executing a borrow, the current price of the asset is obtained from the price oracle first, and the borrowed amount is converted into ETH equivalent. Then, ValidationLogic.validateBorrow is checked and GenericLogic.calculateUserAccountData is used to check whether the user's borrowing is legal. The total collateral assets, total debt, current loan-to-value ratio (LTV), liquidation threshold and health factor, and market stability of the onBehalfOf address are calculated (similar to Compound's getHypotheticalAccountLiquidityInternal), and whether there are enough collateral assets for borrowing. reserve.updateState updates the reserve status, such as interest rate and borrowing index (this step is similar to accrueInterest in Compound), which is used to calculate and update interest.
Then, debt is generated according to the interestRateMode (stable interest rate or floating interest rate) selected by the user, and token contracts with different interest rate models are selected to mint tokens. At the same time, a check is also performed when the token is minted. If the onBehalfOf address is not the caller, its lending authorization for the calling user will be subtracted in the token contract. If it is the user's first borrowing, it will be configured as an active borrower. After the DebtToken is minted to the user, the protocol will update the borrowing interest rate through updateInterestRates to reflect the new interest rate after the borrowing and the changes in the reserve pool. If the user requests to release the underlying assets of the loan, the protocol will transfer the assets directly to the user.
Compared with Compound, the lending process of AAVE V2 has the following main features:
Supports both stable and variable interest rate modes.
Uses a separate verification logic contract for lending verification.
Uses debt tokens (DebtToken) to represent the user's borrowing.
Supports credit delegation, allowing users to borrow on behalf of other addresses.
Repayment
Users repay through the repay function, first obtaining the user's current debt (including stable debt stableDebt and floating debt variableDebt). According to the interest rate mode selected by the user (stable or floating), ValidationLogic.validateRepay verifies the legitimacy of the user's repayment operation, including whether the user's debt balance is sufficient for repayment. The specific debt type (stable interest rate or floating interest rate) for repayment is determined according to the interest rate mode selected by the user. If the amount to be repaid by the user is less than the current debt balance, the system will use the repayment amount provided by the user to make a partial repayment; otherwise, all debts will be repaid. Update the state of the reserve updateState, which is used to calculate and update the interest, loan amount, and loan index in the protocol. Then burn the corresponding stable debt tokens and update the borrowing rate through updateInterestRates. At this time, if all the user's debts (including stable and floating debts) are zero after repayment, the user's borrowing status will be marked as false, indicating that the user is no longer borrowing. Finally, the user transfers the repayment amount from his account to the aToken contract address of the protocol.
Compared with Compound, the repayment process of AAVE V2 has the following main features:
Supports repayments in both stable and floating interest rate modes.
Uses DebtToken to represent and manage debt, and burns the corresponding debt tokens when repaying.
Supports partial repayment and full repayment, and handles stable debt and floating debt separately.
Supports users to repay other addresses through credit delegation.
Liquidation
Users perform liquidation through the liquidationCall function of lendingpool, which calls the liquidationCall function of LendingPoolCollateralManager through proxy mode to ensure the successful execution of the function. First, GenericLogic.calculateUserAccountData obtains the reserve data of collateral assets and debt assets and the user's configuration information, calculates the user's health factor, and obtains the user's current stable and variable liabilities through getUserCurrentDebt.
ValidationLogic.validateLiquidationCall function verifies the legitimacy of the liquidation call, including checking the user's health factor, debt status, and collateral configuration. If the health factor is less than the threshold, it has been used as collateral, and both debts are not 0, then the verification passes. Then calculate the user's maximum liquidable debt and determine the actual amount of debt that needs to be liquidated. If the debt to be liquidated exceeds the user's available collateral, the liquidation amount will be adjusted.
If the liquidator chooses to accept the underlying assets pledged by the liquidated person, it is necessary to ensure that there is sufficient liquidity in the collateral reserve. Update the status of the debt reserve and burn the corresponding number of variable and stable debt tokens based on whether the liquidator accepts aToken. Update the interest rate of the debt to reflect the market situation after liquidation. Liquidator Rewards If you choose to receive aToken, the liquidator will receive the corresponding number of aTokens. If you do not accept aToken, update its collateral status and the interest rate of the collateral, burn the corresponding number of aTokens from the user account, and transfer the underlying assets to the liquidator. Finally, transfer the debt assets required for liquidation from the liquidator to the corresponding reserve aToken to complete the liquidation process.
Compared with Compound, the liquidation process of AAVE V2 has the following main features:
Supports the combined liquidation of multiple collaterals and debt assets.
Allows liquidators to choose to receive aToken or underlying assets.
The liquidation process is more modular, separating verification logic, calculation logic, etc. into different functions.
Supports liquidation of two types of debt: stable interest rates and variable interest rates.
Flash Loan
Users can make flash loans through the flashLoan function of lendingpool. As a flash loan in a lending agreement, the current flash loan can be repaid immediately or as a debt for subsequent repayment, which is determined by the different modes parameters passed in. 0 is immediate repayment, 1 is as a stable debt, and 2 is a floating debt.
The function first checks the input parameter matching through ValidationLogic.validateFlashloan, calculates the premium cost required for the flash loan, and directly transfers the required aToken to the recipient address. Call the recipient's executeOperation operation to implement the preset flash loan. The flash loan operation implemented by AAVE already includes exchange, exchange liquidation, and exchange repayment operations. After executeOperation completes the above operations, the amount of flash loans to be repaid and the corresponding fees are recorded. If the user chooses to repay the funds in non-debt mode: the system updates the reserve status, accumulates reserve liquidity, and updates the liquidity index. Finally, the funds and fees are transferred from the requester to the reserve pool. If the user chooses to process in debt mode, _executeBorrow is called to open the corresponding debt position.
Switch debt mode
In AAVE V2, users can switch between stable rate mode and floating rate mode through the swapBorrowRateMode function. First, get the user's current stable rate debt and floating rate debt on the target asset through the getUserCurrentDebt function to determine the user's debt status. Then call the ValidationLogic.validateSwapRateMode function to verify whether the switching operation is legal. It checks whether the user has enough stable or floating debt to support the mode switch, ensuring that the switching target mode is consistent with the asset configuration and the user's debt situation. Call reserve.updateState to update the status of the asset reserve to ensure that the reserve data is up to date. Then it is the mutual conversion of the two debt tokens, burning stable debt tokens to cast floating debt tokens or burning floating debt tokens to cast stable debt tokens. After the conversion is completed, reserve.updateInterestRates updates the target asset interest rate to ensure that it reflects the current market status and changes in user debt.
Security Vulnerability Checklist
Rounding Vulnerabilities Caused by Empty Markets
In both AAVE and Compound, there are vulnerabilities caused by loss of precision in empty markets. If there is an empty market (that is, no users are borrowing or lending in the market), since the value of liquidityIndex in the cumulateToLiquidityIndex function depends on the number of underlying asset tokens corresponding to the contract, an attacker can manipulate the price of aToken by depositing a large number of underlying asset tokens into the contract through flash loans.
Similar to the first hack of Compound fork project Hundred Finance, in the HopeLend incident, the attacker first manipulated liquidityIndex to control the value of hETHWBTC to WBTC to 1:1, and then increased the value of liquidityIndex by exchanging underlying assets and borrowing. Then, the _handleFlashLoanRepayment function was continuously called through a circular flash loan. In the cumulateToLiquidityIndex function, the precision loss of rayDiv will further amplify the value of reserve.liquidityIndex, and ultimately amplify the amount of WBTC that can be redeemed. (Attack transaction: https://etherscan.io/tx/0x1a7ee0a7efc70ed7429edef069a1dd001fbff378748d91f17ab1876dc6d10392)
Audit points: During the audit, it is necessary to pay attention to whether the calculation method of the exchange rate is easy to manipulate and whether the rounding method is appropriate. At the same time, it is recommended that the project team mint aToken immediately after the new market is created to prevent the market from being empty and manipulated.
Reentrancy vulnerability caused by ERC677 / ERC777 tokens
Similar to the second hack of the Compound fork project Hundred Finance, in the Agave attack, the attacker called the liquidateCall function to liquidate himself without any debt. The token to be liquidated is the ERC-677 standard token used on the Gnosis Chain. When transferring this type of token, the function of the receiving address will be called externally, so the liquidation contract calls the attack contract. During this process, the attack contract deposited 2728 WETH obtained through flash loans, minted 2728 aWETH, and used this as collateral to borrow all available assets in the Agave project. After the external call, the liquidationCall function directly liquidated the 2728 aWETH previously deposited by the attacker and transferred it to the liquidator.

(Reference source: https://x.com/danielvf/status/1503756428212936710; Attack transaction: https://gnosis.blockscout.com/tx/0xa262141abcf7c127b88b4042aee8bf601f4f3372c9471dbd75cb54e76524f18e)
Audit points: During the audit, it is necessary to pay attention to whether the relevant codes of the lending function comply with the CEI (Checks-Effects-Interactions) specification or whether there is an anti-reentry lock, and the impact of tokens with callback functions needs to be considered.
Risk of price manipulation caused by inappropriate oracle mechanism
In the Blizz Finance project hack, as the price of LUNA continued to plummet at the time, the Chainlink price information used by the protocol became inaccurate, resulting in the ability to borrow funds with expensive LUNA collateral. At the same time, the project had no existing fail-safe mechanism, and although it seemed that the alarm had been sounded in advance, no preventive measures were established in time to prevent losses. When the price fell below this level, anyone could buy a large amount of LUNA at the market price (well below $0.10) and use it as collateral (worth $0.10) to borrow funds from the platform.
(Reference source: https://x.com/BlizzFinance/status/1524911400992243761)
Audit points: During the audit, it is necessary to pay attention to whether the oracle price feeding mechanism used to calculate the value of the collateral is easily manipulated by the outside world. It is recommended that the project party use multiple price sources for comprehensive evaluation to avoid the risks caused by a single price source. At the same time, it is also necessary to pay attention to whether the project has a reasonable suspension mechanism to prevent such emergencies.
Unexpected problems introduced by external calls to peripheral contracts
In the interaction between the AAVE protocol and the Para Swap protocol, the _buyOnParaSwap function of the Aave Collateral Repay Adapter V3 contract has multiple security risks. The function sets the limit of assetToSwapFrom to maxAmountToSwap on tokenTransferProxy by calling the safeApprove method, but does not consider the case where no exchange or partial exchange is performed, resulting in the remaining amount remaining unchanged when the limit is not fully used. In addition, the function relies on an external contract call (augustus.call(buyCalldata)) to perform the exchange, and does not fully verify and restrict the paraswapData parameter, allowing attackers to manipulate the decoded buyCalldata and augustus contract address through maliciously constructed paraswapData, bypassing the expected exchange logic or avoiding the exchange altogether. Because the function does not reduce or check the token limit of assetToSwapFrom after the exchange, even if the exchange fails or is bypassed, the attacker can still use the unchanged high limit to withdraw tokens from the contract, thereby achieving unauthorized funds transfer. The lack of verification of input data and exchange results, as well as the failure to effectively manage token limits, led to the contract being exploited by attackers.

(Attack transaction: https://etherscan.io/tx/0xc27c3ec61c61309c9af35af062a834e0d6914f9352113617400577c0f2b0e9de)
Audit points: During the audit, special attention should be paid to the interaction code with external third-party protocols. Focus on evaluating whether the input and output of external contracts are strictly restricted, whether the interaction logic has a potential impact on the core model of the protocol or the security of funds, and whether the input data is cleaned and verified to prevent malicious data from causing security issues. By strictly reviewing the code logic of external interactions and the data verification mechanism, the risk of such vulnerabilities can be effectively reduced.
Token and interest rate strategy compatibility issues
During the deployment of AAVE on the Polygon chain, the incompatibility of the InterestRateStrategy settings caused functional abnormalities and an incompatible interest rate strategy was mistakenly set for WETH.
The interface in the incorrectly set InterestRateStrategy contract is as follows:

The code implemented by AAVE V2’s LendingPool is as follows:
(Reference source: https://x.com/mookim_eth/status/1659589328727859205)
Due to interface incompatibility, the new InterestRateStrategy cannot be called normally by LendingPool, which directly leads to AAVE V2’s WETH pool function is interrupted and users cannot deposit or withdraw ETH.
Audit points: During the audit, it is necessary to ensure that the interfaces of key components in the code (or fork) are fully compatible. At the same time, although the above problems are not caused by the multi-chain characteristics, it is still necessary to pay attention to whether the characteristics of different chains will cause unexpected results during the audit.
Dust token issue
AAVE deposits and withdrawals are implemented by setting usingAsCollateral in the setUsingAsCollateral function, so as to flexibly manage the collateral strategy. When an external protocol or contract borrows funds for the first time through the AAVE lending function, the lending function will set usingAsCollateral to true. When the external protocol or contract fully withdraws funds from AAVE, the usingAsCollateral state of the protocol handler in AAVE will be set to false. But in fact, when AAVE calculates the number of aTokens that need to be burned for withdrawal, there may be very few aTokens left in the protocol handler due to arithmetic precision errors. Therefore, when the protocol handler deposits to AAVE next time, usingAsCollateral will not change and will still be set to true. Since there is no interface in the protocol handler contract to call the setUserUseReserveAsCollateral function, this may cause the protocol handler to no longer be able to perform borrowing operations.
Audit Points: When auditing, you need to have a full understanding of the protocol being called, and fully understand its characteristics, and determine whether there are certain compatibility issues with its interaction with external protocols, such as token compatibility, call implementation logic compatibility, etc.
Written at the end
This manual deeply explores the core design, key functions and related audit points of the AAVE V2 protocol. We hope that this manual can better help developers and security researchers identify potential risks and ensure the safe operation of the protocol. Due to space limitations, this article omits some code and images. Readers can click the "read original text" at the end of the article to jump to GitHub to read the full version (https://github.com/slowmist/AAVE-V2-Security-Audit-Checklist).