Summary
Introducing Aave v3.4, a new version of Aave v3 (currently v3.3) including the following changes and features:
- Migration of the custom GHO on v3 Core to a standard a/v Token, with no migration required by users.
- Addition of Multi-call support on the Aave pool.
- Introduction of a new Position Manager role, to a limited subset of actions, optionally configurable by every user.
- Multiple code-size, gas, math precision, and miscellaneous optimisations.
- Removal of the direct flash loan fee injection to suppliers to reduce liquidity index increase complexity, and overall, the security model of the protocol. Opening for the DAO to re-distribute the fees in an async manner with suppliers and/or Umbrella stakers protecting the asset.
- Upgrading the core protocol smart contracts to Solidity 0.8.27.
Context
The development cycle of upgrades on a protocol like Aave is, by itself, full of nuances and strategic decisions. Sometimes, the upgrades are oriented to optimisation of the smart contracts infrastructure, and sometimes more oriented to enabling new features and improving in user/developer experience of the protocol.
Historically, the major “sub-versions” of Aave v3 focused on the following:
- Aave v3.1. Infrastructural, very oriented to security improvements, with the introduction of virtual accounting and the deprecation of stable rate borrowing.
- Aave v3.2. Feature-rich, focusing on the introduction of Liquid eModes, which radically changed the usage and configuration of assets listed on Aave pools.
- Aave v3.3. Again, infrastructural, but this time focused on optimising liquidations and starting to account for bad debt accrued on the protocol (deficit).
v3.4 is a bit of an exception, as it has a pretty good balance between UX/DevEx-oriented changes and infrastructure. The rationale is that given the criticality of the liquidations component touched on in v3.3, we decided not to include certain features and optimisations we had in a very advanced stage of development and pack them together in v3.4.
Aave v3.4 features
1. Standardisation of a/v GHO tokens on v3 Core
Since the listing of GHO on Aave v3 Ethereum as a directly mintable asset via a facilitator, we didn’t feel too comfortable with the approach, as it added certain complexity to the protocol.
This feeling got confirmed on the different upgrades of v3, as GHO became the permanent exception case that usually needed to be handled ad-hoc.
Here is a quick summary on how GHO on Aave v3 Core Ethereum looks at the moment:
- GHO aToken (aGHO). It is a special implementation that has very little in common with other aTokens, more oriented to trigger various hooks on actions, instead of being an accounting/transferability layer. These inconsistencies materialise in a different utilisation model in the pool, the inability to flashloan GHO natively on Aave, and multiple other minor quirks.
- GHO vToken (vGHO). While superficially working similarly to standard vTokens, vGHO also has some special mechanics that lead to complexity and potential bug surface.
In contrast to other vTokens, the debt does not accrue as yield on the aToken (as aToken totalSupply is always 0), but instead accrues only on the vToken and is collected by the Aave Collector on repayment.
This behaviour is required in order to support the discount model, but adds again very important complexity, which, for example we noticed in practice when improving liquidations on the v3.3 release. Additionally, this adds overhead on the treasury management and peg control, as it creates the concepts of realised and non-realised GHO fees. - GHO on v3 Core is the only exception for an asset not using the virtual accounting introduced on v3.1, with no plans for this to change anytime in the future.
GHO post-v3.4
On this v3.4 proposal, GHO on v3 Core changes the following way:
- The minting model will follow that of the Aave v3 Prime Ethereum with the GhoDirectMinter: a smart contract that can supply liquidity of GHO to the pool by minting it, but technically outside of Aave v3.
- Different from currently on v3 Prime, and to not change the behaviour of GHO on Core, only the GhoDirectMinter will be able to supply liquidity of GHO on Aave.
This will be achieved by having a 0 supply cap on GHO, which is skipped by the GhoDirectMinter exclusively. It opens to aGHO transfer scenarios (e.g., if the Collector could decide to transfer aGHO), but that should not create any issue. - The interest rate remains as it is, totally static (not based on utilisation), and changeable via governance and stewards.
- aGHO yield accrual will work exactly as with any other aToken, continuously, and not only on repayment.
- The native discount model is removed, as it can be achieved in the same way by having GHO rewards to specific vGHO/stkAAVE holders on top, with only very small differences on debt accrual dynamics, which in practice make no difference historically on GHO borrowing positions on Core.
The most natural option is following the anti-GHO approach approved on the new Aavenomics, for stkAAVE holders to get anti-GHO, which can be used to repay the GHO debt, factually being a discount. Important to clarify that this will not be in sync with v3.4, with anti-gho potentially being released before or after. - No users’ migration of any type is required. On the v3.4 upgrade, the GHO model of v3 Core will change under the hood, but all user positions will not be affected.
The benefits of these changes on the technical/security side are very important:
- All reserves will have virtual accounting enabled, with no exception.
- Related to v3.3 deficit management, burning bad debt no longer relies on special logic for GHO that discounts the protocol fee in some special cases.
- GHO flashloans are enabled, eliminating the need for custom adapters, and in most cases making the flash-mint facilitator obsolete (but can be kept if the community wants).
- Even if only affecting the GhoDirectMinter and the Collector in the average case, now for vGHO balanceOf(user) (similar to aGHO) will act as any other reserve, high-level being scaledBalanceOf(user) / normalizedDebt.
- Umbrella will not need special handling of the GHO case.
- Further changes on v3 will become way simpler.
2. Multicall support on the Aave Pool
Multi-call support is the ability of a contract to allow native usage of multiple of its functions in one single transaction, one after the other. For example, being able to do supply + borrow, without requiring another contract on top doing under the hood the Pool.supply
and then the Pool.borrow
.
This has been a frequently requested feature on the Aave protocol; however, in previous iterations, we discarded it because we perceived extra complexity that we were not comfortable with.
With the type of non-invasive approach to multi-call we introduce on v3.4, we now think it really can bring all the value without compromising the security model of Aave v3 anyhow. Additionally, this will exponentially improve the experience post-v3.2 Liquid Emodes, opening to a myriad of new flows and use cases.
3. Aave Position Manager role
When analysing the needs of different integrators after the release of Aave v3.2, it became clear that it could be important to allow a user to authorise a third-party to the following actions on its behalf:
- Enable/disable an asset as collateral in the user position.
- Enter an eMode (or switching from one to another).
Aave v3.4 introduces the concept of Position Manager, which has the following characteristics:
- Any user can optionally configure one or multiple Position Manager addresses by calling the
approvePositionManager()
on the Aave v3 Pool. Same for removing authorisation. - These addresses will have the capability to do the previously commented actions on behalf of the user, with all protocol validations still applying as usual.
The Position Manager becomes especially interesting when combined with multi-call in the pool, opening ever more use-cases and flows.
4. Misc improvements and optimizations
4.1. Removal of the “unbacked” concept
Unbacked supply was part of a feature called Portals on the original Aave v3.0. Eventually, even if potentially useful, the feature was never used, but it occupies an important amount of contract size while also costing gas on most of the pool transactions.
Therefore, in this version of the protocol, we decided to drop all the related logic to make room for other improvements. The feature might be added back in a future iteration if a use-case emerges and/or code-size becomes less of a concern(e.g. via Fusaka/EOF).
4.2. Multiple configurations changed from variable to immutable
On the current v3.3 iteration of the protocol, there are multiple configurations defined as variables, but that historically have a “constant” nature.
To simplify the gas and reasoning models around them, v3.4 changes the following configurations from variable to immutable.
- Interest rate strategy address. While all reserves already share the same interest rate strategy smart contract, in Aave v3.3, the strategy is still defined per reserve, which leads to one unnecessary storage read per touched reserve on user actions.
Given that there is no realistic need any time soon to change the interest rate strategy for only one asset but not for others, in Aave v3.4, one immutable variableRESERVE_INTEREST_RATE_STRATEGY
was added to thePool
contract. - Incentives controller address on a/v Tokens. While all reserves already share the same Incentives Controlled address (the contract handling supply/borrow incentives), in Aave v3.3, the Incentives Controller was defined in state per token, which leads to unnecessary storage reads per touched reserve(s).
This had some historical reasoning of being able to customise rewards accounting contracts per asset, but it is a behaviour totally deprecated with no plans to appear any anytime soon.
Therefore, in Aave v3.4, one immutable variableREWARDS_CONTROLLER
was introduced on theIncentivizedERC20
base class. - Pool address on ProtocolDataProvider. Up until now, the ProtocolDataProvider was doing a call
AddressesProvider.getPool()
to fetch any data, but given that the addresses provider and pool proxy pair will never change, it is way more optimal to simply make the pool reference on the data provider immutable, and this way adding extra gas savings across the board. - Treasury address on aToken. Currently, the treasury on each aToken is stored in storage, although it is the same for each aToken, even across pools in the same networks. This causes unnecessary storage reads on liquidations and mintToTreasury, with the first having high gas sensitivity. Therefore, in Aave 3.4, the treasury was moved to an immutable.
4.3. Migration to Errors
Aave has historically used string error codes as opposed to Error
signatures, because there was a preference for require(cond, error)
, which did not support signatures in previous compiler versions.
As on Aave v3.4 the codebase was upgraded to solc 8.27, require(cond, Error())
is now supported. This greatly improves UX, as explorers and simulations now show helpful errors opposed to cryptic numeric Error codes. At the same time, the change results in minor gas & codesize savings across the board.
While this change could be breaking for anyone relying on exact error codes, it is important to note that:
- Every single Aave release since v3.0 has had breaking changes in regards to error emission (either due to new Errors or the order of Errors)
- We checked all the major integrations and did not find examples of people relying on exact error codes.
4.4. Refinement of rounding/precision on user actions
Similar to the majority of protocols dealing with any type of mathematical operations on-chain, Aave v3 has certain inherent imprecision, generally affecting the order of magnitude of +/- 1 wei (last decimal).
Considering the extensive list of security procedures applied to Aave v3 over the years, we are confident this imprecision doesn’t pose any important problem.
However, on v3.4 we are improving the rounding logic on user operations to follow the design approach of the protocol (and industry in general) of being more explicit rounding on the side of the protocol, as on the 4626 standard.
4.5. Flash loan fees model simplification
On the Aave Pool and, more specifically, the flash loan feature, there is a fee which currently has 2 components:
- The flash loan fee to the protocol
_flashLoanPremiumToProtocol
. The percentage of the total flash loan fee that goes as fees to the protocol Collector itself. - The flash loan fees to aToken holders (
_flashLoanPremiumTotal - _flashLoanPremiumToProtocol
). The percentage of the flash loan fee that gets automatically accrued into the liquidity index, so factually “spread” as yield amongst all suppliers.
From a technical and security perspective, the model is sound and functional. However, the component of the flash loan fee going to suppliers creates a highly important consequence on the Aave system when looking from a fundamental point of view.
In Aave, yield/debt growth happens in practise by the so-called “indexes” (liquidity index, variable borrow index) increasing. Being a yield-oriented architecture, what is expected is that as debt gets accrued on the borrow side, the debt index increases, and this reflects on the liquidity index also increasing, to “forward” the yield from borrowers to aToken holders.
High-level, this feels like it should be the only gateway for the liquidity index to increase, however, there is one single exception on Aave v3: the flash loan fee going to suppliers.
Aside from being the only exception, the mechanism of “injecting” the flash loan fee into the liquidity index of the aTokens is very different morphologically to yield accrual:
- It is “fast”. It happens immediately during a flash loan, and there can be multiple “injections” of flash loan fees during the same block. In comparison, the increase of the index based on yield is “slow”, as it depends on the time passed and the interest rate.
For example, if an asset has a yearly 5% supply rate, the rate per Ethereum block is approximately 0.001%. However, one single flash loan of the whole liquidity available would be 0.05%, 50 times more, and that could potentially be repeated multiple times in the same block. - It is quasi-unbounded. Meaning that the higher the flashed amount, the higher the flash loan fee, and is only bound to available liquidity on the pool, which technically can be increased by supplying just before the flash loan.
These characteristics of the flash loan fee, and even considering countless other protections like virtual accounting, make the security profile of the flash loan feature more dangerous by default than yield accrual limited by time.
Because of the previous, on v3.4 we remove completely the component of flash loan fee going directly to suppliers, with the total flash loan premium becoming equal to the flash loan premium to the protocol.
The consequences of this are:
- Yield accrual becomes the only “entry point” for liquidity index increase. This is extremely valuable from both a security and a technical perspective.
- High-level assumptions don’t change significantly. The DAO can still spread to suppliers flash loan fees if required, just via other methods like on-chain rewards, Merit, or anything else.
- The DAO can choose on top to spread rewards as incentives to the users that will carry the risk of the aTokens: Umbrella stakers.
- Alternatively, the DAO can simply choose to completely zero the fee, but this would just be a separate configuration, not having any consequence on the codebase.
4.6. Misc
- Solc version used on the Aave protocol upgraded to 0.8.27.
- Gas usage of
executeUseReserveAsCollateral
was greatly reduced by optimizing storage access. - Gas usage improved by shuffling
virtualUnderlyingBalance
into the position of pre-3.4unbacked
. - Minor code style improvements on
executeRepay
. - Usage of imported events from the interface contracts instead of re-declarations.
- Skip unnecessary calculations on a subset of transfers.
- Self-liquidation is now forbidden (BREAKING). While this is a breaking change, it’s unlikely to affect anyone, as there are essentially no on-chain traces of people relying on this functionality.
The rationale is that even if a user has monitoring capabilities/tooling to avoid liquidation, the optimal path is repaying with collateral (or just repaying), not self-liquidating. - SafeCast was upgraded from Open Zeppelin v4 to v5. The main difference is the usage of Error signatures, reducing the code size of various contracts.
- VersionedInitializable now bricks the initializer on the implementation, so implementations no longer have to be initialized in order to prevent malicious initialization.
- ScaledBalanceTokenBase changed the
.balance
storage from 128 to 120 bits. This was done to align storage across all tokens (currently on aAave on Ethereum mainnet uses 120 bits of storage), as 120 bits is more than enough. - Improved the accuracy and gas consumption of the
calculateCompoundedInterest
function without changing the formula. Credit to @Certora on this simplification.
FAQ
I’m borrowing GHO on Ethereum, do I need to do anything with v3.4?
No, the migration is totally non-invasive.
So I will not have a GHO discount on the protocol after v3.4?
Not natively, but in practice, you will with the new anti-GHO proposed on the aavenomics: once that mechanism is activated, by holding staked AAVE token, you will receive anti-GHO that can be used to “burn” GHO debt on Aave v3 Ethereum core.
The discount “accrued” up until the v3.4 upgrade proposal will be applied to your position.
Security procedures
Security procedures of Aave v3.4 will start in the following days, and as usual, we will update the forum post once they are finished pre-release, together with the final codebase.
Next steps
- Create an ARFC Snapshot for the upgrade of all Aave v3 instances to Aave v3.4, after some days of discussion in this forum.
- Advance on security procedures for v3.4.
- On-chain AIP creation, for the Aave governance to upgrade all pool instances across all networks.