Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V3.3.0 #87

Open
wants to merge 85 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 76 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
bff7ce3
fix: apply gas & codesize savings
sakulstra Sep 26, 2024
969ee04
refactor: aggregator interface cleanup
sakulstra Sep 26, 2024
1b56764
perf: optimize bitmap for access instead of writes
sakulstra Sep 26, 2024
9302038
feat: deficit tracking and excess debt removal (#61)
ianflexa Sep 26, 2024
2ea3e77
fix: patch imports
sakulstra Sep 26, 2024
7e582b0
feat: getReserveAToken getter (#64)
sakulstra Sep 28, 2024
802924b
docs: refine docs (#67)
sakulstra Oct 1, 2024
f5389a4
fix: access cache not storage (#68)
sakulstra Oct 7, 2024
c4c438e
chore: add comment in `_burnBadDebt` + lint
ianflexa Oct 7, 2024
26b632d
feat: use addresses provider instead of acl (#66)
sakulstra Oct 7, 2024
da89365
fix: use cache.lastUpdatedTimestamp (#70)
sakulstra Oct 10, 2024
95bf29b
fix: debt cleanup review & liquidation improvements (#69)
sakulstra Oct 16, 2024
6e4a93b
refactor: improvements (#82)
sakulstra Oct 16, 2024
ae74fad
fix: add note about breaking change
sakulstra Oct 16, 2024
07c1da7
refactor: reuse _burnDebt (#83)
kyzia551 Oct 17, 2024
f910b01
feat: make weth and pool public for easier validation (#85)
sakulstra Oct 21, 2024
dd00c6a
docs: 3.3 docs & GHO improvements (#84)
sakulstra Oct 22, 2024
0c6fac8
fix: remove incomplete properties
sakulstra Oct 22, 2024
0e195bb
finding: remove unused error
sakulstra Oct 23, 2024
95fc118
finding: Validationlogic was imported but never used
sakulstra Oct 23, 2024
a473403
finding: unnecessary calculations in LiquiationLogic
sakulstra Oct 23, 2024
69baa1d
fix: allow 100% vf liquidations if collateral is below threshold
sakulstra Oct 23, 2024
677173c
fix: add test for 100% close factor
sakulstra Oct 23, 2024
027aaed
fix: resolve conflicts
sakulstra Oct 24, 2024
21f2616
Merge branch 'stermi/unused-import' into v3.3.3-certora
sakulstra Oct 24, 2024
86d07df
Merge branch 'stermi/unused-error' into v3.3.3-certora
sakulstra Oct 24, 2024
a223bed
finding: not considering fee when validating min_leftover
sakulstra Oct 23, 2024
57411c9
fix: conflicts
sakulstra Oct 24, 2024
b1f7f58
finding: unnecessary IAccessControl import
sakulstra Oct 29, 2024
17444f6
finding: misc natspec issues
sakulstra Oct 29, 2024
8fe1aa6
fix: apply stermies suggestions to eliminateReserveDeficit
sakulstra Oct 29, 2024
c21a2d2
fix: improve tests
sakulstra Oct 29, 2024
5e489ba
fix: remove obsolete comment
sakulstra Oct 29, 2024
cd4d59d
fix: move is collateral logic
sakulstra Oct 29, 2024
d4ae764
finding: cache lastUpdated
sakulstra Oct 29, 2024
ab8066b
fix: dont disable collateral
sakulstra Oct 29, 2024
a2180ee
fix: call handleRepayment when clearing deficit
sakulstra Oct 31, 2024
181577b
fix: gho accounting
sakulstra Oct 31, 2024
3aaee60
fix: docs
sakulstra Oct 31, 2024
dbfd840
fix: logic patch
sakulstra Oct 31, 2024
3fba8d0
fix: comment out test as doesn't make sense with the mock
sakulstra Oct 31, 2024
4f101ba
test: add tests
sakulstra Oct 31, 2024
05a2b55
fix: add comment
sakulstra Oct 31, 2024
2d5ec46
Update src/contracts/protocol/libraries/logic/LiquidationLogic.sol
sakulstra Nov 4, 2024
73a23b9
fix: suppl 1 wei surplus
sakulstra Nov 4, 2024
ffe466d
fix: remove non functional test
sakulstra Nov 5, 2024
104a288
Update src/contracts/protocol/libraries/logic/LiquidationLogic.sol
sakulstra Nov 6, 2024
b516363
Update src/contracts/protocol/libraries/logic/LiquidationLogic.sol
sakulstra Nov 6, 2024
5ca633b
docs: add explanatory comment
sakulstra Nov 6, 2024
0b21084
refactor: better remove test
sakulstra Nov 6, 2024
707d652
fix: clearify it's the interest on the bad debt only
sakulstra Nov 6, 2024
1eb834b
docs: add a sentence to docs
sakulstra Nov 6, 2024
d45702a
docs: improve docs
sakulstra Nov 6, 2024
bacdbf0
fix: dont disable collateral alternative
sakulstra Nov 13, 2024
905ade8
Merge branch 'stermi/IAccessControl' into v3.3.0
sakulstra Nov 18, 2024
9cfb2fc
Merge branch 'stermi/natspec' into v3.3.0
sakulstra Nov 18, 2024
e2315b3
Merge branch 'fix/cache-lastUpdated' into v3.3.0
sakulstra Nov 18, 2024
d9450ad
Merge branch 'fix/dont-disable-collateral-alternative' into v3.3.0
sakulstra Nov 18, 2024
38e0b15
docs: clearify docs
sakulstra Nov 18, 2024
2a59531
fix: address comments by stermi
sakulstra Nov 18, 2024
91ed89b
fix: resolve conflicts
sakulstra Nov 18, 2024
9d0ee8d
Merge branch 'fix/call-handleRepayment-when-clearing-deficit' into v3…
sakulstra Nov 18, 2024
ddb3ad0
fix: resolve conflicts
sakulstra Nov 18, 2024
e741c4d
lint: lint markdown
sakulstra Nov 18, 2024
d3bf92e
fix: resolve conflicts
sakulstra Nov 20, 2024
af4bfb0
fix: properly resolve conflicts
sakulstra Nov 20, 2024
3008691
fix: adjust to new rescuable
sakulstra Nov 20, 2024
8b94bae
refactor: move logic to liquidationlogic (#122)
sakulstra Nov 25, 2024
b21b164
docs: add properties (#94)
sakulstra Nov 26, 2024
38b0cdd
fix: func visibility (#123)
brotherlymite Nov 26, 2024
12e529b
Fix: validationLogic and BridgeLogic titles (#127)
ianflexa Nov 27, 2024
0160ab5
fix: filtering reserves aTokens (#128)
ianflexa Nov 27, 2024
784d7f3
Merge branch 'main' into v3.3.0
sakulstra Nov 27, 2024
7aabb1b
docs: update example to not overlap with other mechanics
sakulstra Dec 9, 2024
80d3454
fix: rsolve conflicts
sakulstra Dec 10, 2024
1fa3d57
fix: consider deficit as part of unbacked
sakulstra Dec 10, 2024
002a2b1
Update src/contracts/interfaces/IPool.sol
sakulstra Dec 11, 2024
487f19a
chore: bump revisions fix typos (#41)
sakulstra Dec 11, 2024
385287d
Apply suggestions from code review
sakulstra Dec 11, 2024
69b23b8
Merge branch 'main' into v3.3.0
sakulstra Dec 13, 2024
006ca27
gas: update gas snapshots
sakulstra Dec 13, 2024
ec4c11c
Merge branch 'main' into v3.3.0
sakulstra Dec 16, 2024
367d732
tests: update gas snapshots
sakulstra Dec 16, 2024
1f4df52
Feat: Added LiquidationDataProvider contract (#148)
TepNik Dec 17, 2024
094e6ec
Added more gas snapshot tests for the Pool contract (#168)
TepNik Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test-contract :; forge test --match-contract ${filter} -vvv
test-watch :; forge test --watch -vvv --no-match-contract DeploymentsGasLimits

# Coverage
coverage-base :; forge coverage --report lcov --no-match-coverage "(scripts|tests|deployments|mocks)"
coverage-base :; forge coverage --fuzz-runs 50 --report lcov --no-match-coverage "(scripts|tests|deployments|mocks)"
coverage-clean :; lcov --rc derive_function_end_line=0 --remove ./lcov.info -o ./lcov.info.p \
'src/contracts/extensions/v3-config-engine/*' \
'src/contracts/treasury/*' \
Expand All @@ -37,7 +37,7 @@ coverage :
download :; cast etherscan-source --chain ${chain} -d src/etherscan/${chain}_${address} ${address}
git-diff :
@mkdir -p diffs
@npx prettier ${before} ${after} --write
# @npx prettier ${before} ${after} --write
@printf '%s\n%s\n%s\n' "\`\`\`diff" "$$(git diff --no-index --ignore-space-at-eol ${before} ${after})" "\`\`\`" > diffs/${out}.md

# Deploy
Expand All @@ -51,3 +51,5 @@ deploy-libs :
npx catapulta-verify -b broadcast/LibraryPreCompileOne.sol/${chainId}/run-latest.json
make deploy-libs-two chain=${chain}
npx catapulta-verify -b broadcast/LibraryPreCompileTwo.sol/${chainId}/run-latest.json

gas-report :; forge test --fuzz-runs 50 --gas-report
6 changes: 0 additions & 6 deletions certora/stata/harness/pool/SymbolicLendingPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ contract SymbolicLendingPool {
return res;
}

function getReserveDataExtended(
address asset
) external view returns (DataTypes.ReserveData memory) {
return reserve;
}

function getConfiguration(
address asset
) external view virtual returns (DataTypes.ReserveConfigurationMap memory) {
Expand Down
166 changes: 166 additions & 0 deletions docs/3.3/Aave-v3.3-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
## Aave v3.3 features

Aave v3.3 is an upgrade on top of Aave 3.2

## Features

### 1. Bad Debt Management

On Aave v3, some liquidation scenarios can result in a permanent "bad debt" on the protocol.
This occurs when the total collateral liquidated is insufficient to cover the repayment of all debt, leaving the account with zero collateral and some remaining debt.
We understand that such debt is unlikely to be repaid and adversely affects the protocol by continuing to accrue interest.

The bad debt feature introduces a new verification step during liquidation to mitigate the creation of new bad debt accounts post-liquidation and halt further interest accrual on such liabilities.
This step checks the total collateral and total debt values of the account post-liquidation and repayment:
If an account ends up with zero collateral and non-zero debt, any remaining debt in the account is burned and the new deficit created is accounted to the reserve.

In terms of implementation, the feature checks whether the liquidation will result in a bad debt situation by comparing whether the total borrower’s collateral equals the collateral liquidated in the base currency.
If the total borrower’s debt exceeds the debt repaid in base currency, the variable debt tokens of the borrower are burned, and it is accounted to the respective reserve as a deficit.

Conceptually the bad debt cleanup is seen as step **after** the actual liquidation.
In the special case of vGHO, the liquidation process is split into two steps:

1. vGHO.burn, burning the variable debt token.
2. `aGHO.handleRepayment(address user, address onBehalfOf, uint256 amount)` which will first discount the fee from the amount as this is the part that belongs to the treasury and then burn the remaining GHO.

When a deficit is created in GHO, there is the possibility that the no fee or only part of the fee is repaid to the treasury, but in any case, all corresponding vGHO is burned.
This would leave the protocol in an inconsistent state as the user would have stale accrued fee storage, but no more debt so the accrued fee will likely never be redirected to the treasury.
Therefore in order to maintain proper accounting and not leave users with stale fee storage on the vGHO token, the protocol will reset the accrued fee storage on the vGHO token when burning bad debt and discount the created deficit accordingly.
In practice, this means the protocol will burn the claims / accept the loss on the accrued fee when burning bad debt.
It is important to note, that this only applies to the **bad debt** part of the liquidation.
If GHO is the liquidated asset, it is possible that part of the fee or even the full fee is repaid to the treasury.

The new `deficit` data is introduced to the `ReserveData` struct by re-utilizing the deprecated stableBorrowRate (`__deprecatedStableBorrowRate`) storage, and can be fetched via the new `getReserveDeficit` function in the Pool contract.

The deficit reduction of a reserve is introduced via the `eliminateReserveDeficit` function in the Pool contract, where a permissioned entity (the registered `Umbrella` on the PoolAddressesProvider) can burn aTokens to decrease the deficit of the respective reserve.
This function only allows burning aTokens(and in the case of GHO underlying) up to the currently existing deficit and validates that the caller has no open borrow positions.

**Misc considerations**

- For positions already in bad debt, this upgrade does not offer any solution, but recommends the DAO to clean up these positions via a `repayOnBehalf`.
- `eliminateReserveDeficit` assumes for umbrella to have the tokens to be burned. In case of assets having virtual accounting enabled, aTokens will be burned. In case of virtual accounting being disabled, the underlying will be disposed of.
Depending on the coverage asset and reserve configuration(e.g. if coverage is done via underlying, and caps don't allow depositing) it might be that it us not possible to receive the aToken.
This is expected and considered a non-issue as the elimination of the deficit has no strict time constraint.

**Acknowledged limitations**

- For the scope of this feature we define a bad debt situation as an account that has zero collateral, in base currency, but retains some level of debt, in base currency.
Accounts with any remaining collateral potentially can be overcollateralized again.

### 2. Liquidation logic changes

#### 2.1 Liquidation: 50% close factor re-design

The Aave protocol currently implements a so-called "Close Factor" which determines how much of a debt position can be repaid in a single liquidation.
While in Aave v2, this parameter was a constant 50%, in Aave v3 there is an alteration between the default 50% and a max close factor of 100%, currently applied when a user health-factor deteriorates below a certain threshold.

The 50% close factor has always been applied on a per reserve basis, so if a user has e.g. the following positions:

- 3k $ GHO DEBT
- 3k $ USDC DEBT
- 3k $ DAI DEBT
- 9k $ ETH Collateral
A liquidation would only be able to liquidate 1.5k $ GHO/USDC or DAI per liquidation.

While being by design, it's also rather unintuitive and can even be problematic in smaller positions.
If the overall position value falls below a certain threshold it might no longer be economically sound to liquidate that position - by applying the close factor to each specific reserve, this problem scope is increased unnecessarily.

Therefore in Aave v3.3 the Close Factor is altered to apply for the whole position, which in the above example would allow to liquidate the whole 3k $ GHO/USDC or DAI in a single liquidation.

#### 2.2 Liquidation: Position size dependent 100% close factor

For the aave protocol it is problematic to have dust debt positions, as there is no incentive to liquidate them, while on the other hand they create an ever increasing liability to the protocol.
Most of these dust debt positions are caused by the 50% close factor being applied to already small positions.
In this case, liquidators can only liquidate 50% of a position which will decrease the overall position value to a point where the gas cost no longer outweighs the liquidation bonus.

Therefore in order to reduce the accumulation of minor debt positions, a new mechanism is introduced:
Liquidations up to a 100% close factor are now allowed whenever the total principal or the total debt of the user on the specific reserve being liquidated is below a `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD`

**Example**:
Assuming a `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD` of 1_000e8 and a position composed as:

- 1200 $ collateral A
- 900 $ debt B
- a health-factor at 0.96

In the previous system, a liquidation could have liquidated up to 50% of `debt B`.
With the new system the debt position is below the `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD`.
Therefore a liquidation could liquidate 100% of `debt B`.

**Acknowledged limitations**
Liquidations are still highly influenced by gas prices, liquidation-bonus and secondary market liquidity.
`MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD` has to be chosen in a "best effort" way to allow for liquidations on "average" network conditions.
A threshold of 2000$ for example would mean that, at a 1% bonus a liquidation cannot cost more then 20$ before no longer being liquidated by a economically reasonable liquidator.

#### 2.3 Liquidation: Forced position cleanup

A problem that exists with current liquidations is that for one or another reason, liquidators sometimes chose to not clean up a full positions but leave some minor dust.
As elaborated before, small debt positions can be a burden already, but they are especially problematic with the newly introduced bad debt cleanup:

- when leaving dust, the bad debt cleanup will **not** clean up debt, as it only triggers when the collateral is zero
- as the bad debt cleanup slightly increases gas, for liquidators there is an incentive to leave dust

To counter these problems a new mechanism is introduced to now allow any debt or collateral below `MIN_LEFTOVER_BASE` to remain after a liquidation.
If debt or collateral after the liquidation would be below `MIN_LEFTOVER_BASE`, but non of the two is exactly zero, the transaction reverts.
To achieve that, `MIN_LEFTOVER_BASE` is defined as `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD/2`.
This way it is ensured that in the range of `[0, MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD]` you can perform a full liquidation (via close Factor 100%).
On the other hand a 50% liquidation in the range of `[MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD, Infinity]` will always leave at least `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD/2`.

**Example**
Assuming a `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD` of 1_000e8, `MIN_LEFTOVER_BASE` of 500e8 and a position composed as:

- 400e8 collateral A
- 1000e8 collateral B
- 900e8 debt A
- 400e8 debt B
- a healthFactor at 0.94

In the previous system it would have been possible to liquidate any amount of debt for any respective amount of collateral.
With the new system you have to either:

- liquidate 100% of `debt B`
- liquidate 100% of `collateral A`
- liquidate up to 400e8 of `debt A` or liquidate 100% of `debt A`
- liquidate up to 500e8 of `collateral B` or liquidate 100% of `collateral B`

**Acknowledged limitations**
This feature is highly dependent on `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD` and therefore relies on choosing a reasonably high `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD`.

### 3. Bitmap access optimization

The current bitmasks on `ReserveConfiguration` have been optimized for `writes`.
This is unintuitive, as the most common protocol interactions `read` from the configuration.
By flipping the masks:

```diff
- uint256 internal constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore
+ uint256 internal constant LTV_MASK = 0x000000000000000000000000000000000000000000000000000000000000FFFF; // prettier-ignore
```

The access can be simplified:

```
function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) {
- return self.data & ~LTV_MASK;
+ return self.data & LTV_MASK;
}
```

Which slightly reduces gas & code-size. The effect is getting more meaningful for accounts holding multiple collateral & borrow positions.

### 4. Additional getters

When analyzing ecosystem contracts we noticed that a lot of contracts have to pay excess gas due to the lack of fine grained getters on the protocol.
If an external integration e.g. wants to query the aToken balance of an address, it currently has to fetch `Pool.getReserveData().aTokenAddress` which will read 9 storage slots.
This is suboptimal, as the consumer is only interested in a single slot - the one containing the `aTokenAddress`.
Therefore we added a `getReserveAToken()` and `getReserveVariableDebtToken()` getters reducing gas cost by up to ~16k gas dependent on the usecase.
We plan on adding more dedicated getters in the future as we see fit.

## Breaking changes

The previously deprecated `pool.getReserveDataExtended()` was removed.
You can fetch the data via `pool.getReserveData()`, `pool.getVirtualUnderlyingBalance()` & `pool.getVirtualUnderlyingBalance()`.

While the interface of `calculateInterestRates` did not change, the usage assumptions changed.
Instead of passing `reserve.unbacked` you now have to pass `reserve.deficit + reserve.unbacked`.
The rational being that both values represent unbacked tokens on the pool.
39 changes: 39 additions & 0 deletions docs/3.3/Aave-v3.3-properties.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Aave v3.3 features properties

Formal properties in natural language of the 3.3 features.

## Properties

### 1. Deficit Management

- The deficit of all reserves should initially be zero, even if bad debt was created before the protocol upgrade.
- Deficits are tracked within the `reserve.deficit` and accumulate over the curse of multiple deficit creations.
- Deficits can only be reduced by burning a claim, and thus reducing the protocols obligations in `reserve.deficit`.
- The burning of claims can be performed via `pool.eliminateReserveDeficit()`. In case of assets having virtual accounting enabled, aTokens will be burned. In case of virtual accounting being disabled, the underlying will be disposed of.
- The burning of claims can only be performed by a permissioned `UMBRELLA` entity registered on the `PoolAddressesProvider`.
- The `pool.eliminateReserveDeficit()` requires for the `UMBRELLA` entity to never have any debt.
- claims can only be burned trough `pool.eliminateReserveDeficit()` up to the current obligations stored in `reserve.deficit`.
- A deficit should be created as the result of a liquidation. A liquidation only creates a deficit if the users total collateral across all reserves being zero in the base currency, while the total debt across all reserves remains non-zero in the base currency as the result of the liquidation.
- Edge case: when liquidating yourself with `receiveAToken=true`, it is possible that bad debt is created although after the liquidation the user will end up with the liquidated, non-zero collateral.
- Whenever a deficit is created as a result of a liquidation, the user's excess debt should be burned and accounted for as deficit.
- Deficit added during the liquidation can't be more than the user's debt
- Deficit can only be created and eliminated for an `active` reserve.
- Edge case: deficit can be created and eliminated even is a reserve is `paused` in case it is not the main liquidated asset. Both actions don't affect a user negatively, and preventing the burning of bad debt on paused reserves could create overhead for the protocol.
- For the interest rate calculation, deficit is treated equally as the unbacked parameter, given that it should be reducing utilisation.

### 2. Liquidation mechanics

- A liquidation can only be performed once a users health-factor drops below 1.
- A maximum of `totalUserDebt * CLOSE_FACTOR` can be liquidated in a single liquidation, while the upper bound is further restrained by the available collateral, liquidationBonus and protocolFee.
- The **default** `CLOSE_FACTOR` is defined as `DEFAULT_LIQUIDATION_CLOSE_FACTOR` and limits the liquidation to 50% of the users todal debt.
- When liquidating a position, the liquidation must either:
- liquidate a full debt position **or**
- liquidate a full collateral position **or**
- leave at least a value of `MIN_LEFTOVER_BASE` on both collateral and debt side
- Edge case: when liquidating yourself with `receiveAToken=true`, it is possible that a position <`MIN_LEFTOVER_BASE` is created as a result of a liquidation.

There are certain mutually inclusive conditions which increases the `CLOSE_FACTOR` to 100%:

- when a users health-factor drops `<=0.95` **or**
- if the users total value of the debt position to be liquidated is below `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD`
- if the users total value of the collateral position to be liquidated is below `MIN_BASE_MAX_CLOSE_FACTOR_THRESHOLD`
Loading
Loading