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

Fixes #730. accruedToTreasury not being properly considered together with aToken supply #733

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
5 changes: 3 additions & 2 deletions contracts/protocol/libraries/logic/BridgeLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ library BridgeLogic {

reserve.updateState(reserveCache);

ValidationLogic.validateSupply(reserveCache, amount);
ValidationLogic.validateSupply(reserveCache, reserve, amount);

uint256 unbackedMintCap = reserveCache.reserveConfiguration.getUnbackedMintCap();
uint256 reserveDecimals = reserveCache.reserveConfiguration.getDecimals();
Expand Down Expand Up @@ -125,7 +125,8 @@ library BridgeLogic {
uint256 added = backingAmount + fee;

reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
IERC20(reserveCache.aTokenAddress).totalSupply(),
IERC20(reserveCache.aTokenAddress).totalSupply() +
miguelmtzinf marked this conversation as resolved.
Show resolved Hide resolved
uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
feeToLP
);

Expand Down
3 changes: 2 additions & 1 deletion contracts/protocol/libraries/logic/FlashLoanLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ library FlashLoanLogic {
DataTypes.ReserveCache memory reserveCache = reserve.cache();
reserve.updateState(reserveCache);
reserveCache.nextLiquidityIndex = reserve.cumulateToLiquidityIndex(
IERC20(reserveCache.aTokenAddress).totalSupply(),
IERC20(reserveCache.aTokenAddress).totalSupply() +
uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex),
premiumToLP
);

Expand Down
9 changes: 7 additions & 2 deletions contracts/protocol/libraries/logic/SupplyLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ library SupplyLogic {

reserve.updateState(reserveCache);

ValidationLogic.validateSupply(reserveCache, params.amount);
ValidationLogic.validateSupply(reserveCache, reserve, params.amount);

reserve.updateInterestRates(reserveCache, params.asset, params.amount, 0);

Expand Down Expand Up @@ -264,7 +264,12 @@ library SupplyLogic {

if (useAsCollateral) {
require(
ValidationLogic.validateUseAsCollateral(reservesData, reservesList, userConfig, reserveCache.reserveConfiguration),
ValidationLogic.validateUseAsCollateral(
reservesData,
reservesList,
userConfig,
reserveCache.reserveConfiguration
),
Errors.USER_IN_ISOLATION_MODE
);

Expand Down
18 changes: 12 additions & 6 deletions contracts/protocol/libraries/logic/ValidationLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ library ValidationLogic {
* @param reserveCache The cached data of the reserve
* @param amount The amount to be supplied
*/
function validateSupply(DataTypes.ReserveCache memory reserveCache, uint256 amount)
internal
view
{
function validateSupply(
DataTypes.ReserveCache memory reserveCache,
DataTypes.ReserveData storage reserve,
uint256 amount
) internal view {
require(amount != 0, Errors.INVALID_AMOUNT);

(bool isActive, bool isFrozen, , , bool isPaused) = reserveCache
Expand All @@ -72,7 +73,9 @@ library ValidationLogic {
supplyCap == 0 ||
(IAToken(reserveCache.aTokenAddress).scaledTotalSupply().rayMul(
reserveCache.nextLiquidityIndex
) + amount) <=
) +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess adding up the scaled amounts and multiplying will be the same and save 1 operation. Is there a reason you didn't do it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, makes more sense. Will change

uint256(reserve.accruedToTreasury).rayMul(reserveCache.nextLiquidityIndex) +
amount) <=
supplyCap * (10**reserveCache.reserveConfiguration.getDecimals()),
Errors.SUPPLY_CAP_EXCEEDED
);
Expand Down Expand Up @@ -650,7 +653,10 @@ library ValidationLogic {
IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0,
Errors.VARIABLE_DEBT_SUPPLY_NOT_ZERO
);
require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.ATOKEN_SUPPLY_NOT_ZERO);
require(
IERC20(reserve.aTokenAddress).totalSupply() == 0 && reserve.accruedToTreasury == 0,
miguelmtzinf marked this conversation as resolved.
Show resolved Hide resolved
Errors.ATOKEN_SUPPLY_NOT_ZERO
);
}

/**
Expand Down
8 changes: 5 additions & 3 deletions contracts/protocol/pool/PoolConfigurator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,11 @@ contract PoolConfigurator is VersionedInitializable, IPoolConfigurator {
}

function _checkNoSuppliers(address asset) internal view {
uint256 totalATokens = IPoolDataProvider(_addressesProvider.getPoolDataProvider())
.getATokenTotalSupply(asset);
require(totalATokens == 0, Errors.RESERVE_LIQUIDITY_NOT_ZERO);
(, uint256 accruedToTreasury, uint256 totalATokens, , , , , , , , , ) = IPoolDataProvider(
_addressesProvider.getPoolDataProvider()
).getReserveData(asset);

require(totalATokens == 0 && accruedToTreasury == 0, Errors.RESERVE_LIQUIDITY_NOT_ZERO);
}

function _checkNoBorrowers(address asset) internal view {
Expand Down
4 changes: 3 additions & 1 deletion test-suites/helpers/utils/calculations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,9 @@ export const calcExpectedReserveDataAfterBackUnbacked = (

expectedReserveData.liquidityIndex = cumulateToLiquidityIndex(
expectedReserveData.liquidityIndex,
totalSupply,
totalSupply.add(
expectedReserveData.accruedToTreasuryScaled.rayMul(expectedReserveData.liquidityIndex)
),
premiumToLP
);

Expand Down
123 changes: 123 additions & 0 deletions test-suites/liquidity-indexes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {expect} from 'chai';
import {BigNumber, ethers} from 'ethers';
import {MAX_UINT_AMOUNT} from '../helpers/constants';
import {makeSuite, TestEnv} from './helpers/make-suite';
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {
evmSnapshot,
evmRevert,
MockFlashLoanReceiver,
getMockFlashLoanReceiver,
} from '@aave/deploy-v3';
import './helpers/utils/wadraymath';

declare var hre: HardhatRuntimeEnvironment;

makeSuite('Pool: liquidity indexes misc tests', (testEnv: TestEnv) => {
const TOTAL_PREMIUM = 9;
const PREMIUM_TO_PROTOCOL = 3000;

let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;

let snap: string;

const setupForFlashloan = async (testEnv: TestEnv) => {
const {
configurator,
pool,
weth,
aave,
dai,
users: [user0],
} = testEnv;

_mockFlashLoanReceiver = await getMockFlashLoanReceiver();

await configurator.updateFlashloanPremiumTotal(TOTAL_PREMIUM);
await configurator.updateFlashloanPremiumToProtocol(PREMIUM_TO_PROTOCOL);

const userAddress = user0.address;
const amountToDeposit = ethers.utils.parseEther('1');

await weth['mint(uint256)'](amountToDeposit);

await weth.approve(pool.address, MAX_UINT_AMOUNT);

await pool.deposit(weth.address, amountToDeposit, userAddress, '0');

await aave['mint(uint256)'](amountToDeposit);

await aave.approve(pool.address, MAX_UINT_AMOUNT);

await pool.deposit(aave.address, amountToDeposit, userAddress, '0');
await dai['mint(uint256)'](amountToDeposit);

await dai.approve(pool.address, MAX_UINT_AMOUNT);

await pool.deposit(dai.address, amountToDeposit, userAddress, '0');
};

before(async () => {
await setupForFlashloan(testEnv);
});

beforeEach(async () => {
snap = await evmSnapshot();
});

afterEach(async () => {
await evmRevert(snap);
});

it('Validates that the flash loan fee properly takes into account both aToken supply and accruedToTreasury', async () => {
const {
pool,
helpersContract,
weth,
aWETH,
users: [depositorWeth],
} = testEnv;

/**
* 1. Flashes 0.8 WETH
* 2. Flashes again 0.8 ETH (to have accruedToTreasury)
* 3. Validates that liquidity index took into account both aToken supply and accruedToTreasury
*/

const wethFlashBorrowedAmount = ethers.utils.parseEther('0.8');

await pool.flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[wethFlashBorrowedAmount],
[0],
_mockFlashLoanReceiver.address,
'0x10',
'0'
);

await pool.flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[wethFlashBorrowedAmount],
[0],
_mockFlashLoanReceiver.address,
'0x10',
'0'
);

const wethReserveDataAfterSecondFlash = await helpersContract.getReserveData(weth.address);

const totalScaledWithTreasuryAfterSecondFlash = (
await aWETH.scaledBalanceOf(depositorWeth.address)
).add(wethReserveDataAfterSecondFlash.accruedToTreasuryScaled.toString());

expect(await weth.balanceOf(aWETH.address)).to.be.closeTo(
BigNumber.from(totalScaledWithTreasuryAfterSecondFlash.toString()).rayMul(
wethReserveDataAfterSecondFlash.liquidityIndex
),
1,
'Scaled total supply not (+/- 1) equal to WETH balance of aWETH'
);
});
});