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 2 commits
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
2 changes: 1 addition & 1 deletion contracts/protocol/libraries/helpers/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ library Errors {
string public constant SUPPLY_CAP_EXCEEDED = '51'; // 'Supply cap is exceeded'
string public constant UNBACKED_MINT_CAP_EXCEEDED = '52'; // 'Unbacked mint cap is exceeded'
string public constant DEBT_CEILING_EXCEEDED = '53'; // 'Debt ceiling is exceeded'
string public constant ATOKEN_SUPPLY_NOT_ZERO = '54'; // 'AToken supply is not zero'
string public constant UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54'; // 'Claimable rights over underlying not zero (aToken supply or accruedToTreasury)'
string public constant STABLE_DEBT_NOT_ZERO = '55'; // 'Stable debt supply is not zero'
string public constant VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56'; // 'Variable debt supply is not zero'
string public constant LTV_VALIDATION_FAILED = '57'; // 'Ltv validation failed'
Expand Down
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
19 changes: 11 additions & 8 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 @@ -70,9 +71,8 @@ library ValidationLogic {
uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap();
require(
supplyCap == 0 ||
(IAToken(reserveCache.aTokenAddress).scaledTotalSupply().rayMul(
reserveCache.nextLiquidityIndex
) + amount) <=
((IAToken(reserveCache.aTokenAddress).scaledTotalSupply() +
uint256(reserve.accruedToTreasury)).rayMul(reserveCache.nextLiquidityIndex) + amount) <=
supplyCap * (10**reserveCache.reserveConfiguration.getDecimals()),
Errors.SUPPLY_CAP_EXCEEDED
);
Expand Down Expand Up @@ -650,7 +650,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.UNDERLYING_CLAIMABLE_RIGHTS_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
2 changes: 1 addition & 1 deletion helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export enum ProtocolErrors {
SUPPLY_CAP_EXCEEDED = '51', // 'Supply cap is exceeded'
UNBACKED_MINT_CAP_EXCEEDED = '52', // 'Unbacked mint cap is exceeded'
DEBT_CEILING_EXCEEDED = '53', // 'Debt ceiling is exceeded'
ATOKEN_SUPPLY_NOT_ZERO = '54', // 'AToken supply is not zero'
UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO = '54', // 'AToken supply is not zero'
eboadom marked this conversation as resolved.
Show resolved Hide resolved
STABLE_DEBT_NOT_ZERO = '55', // 'Stable debt supply is not zero'
VARIABLE_DEBT_SUPPLY_NOT_ZERO = '56', // 'Variable debt supply is not zero'
LTV_VALIDATION_FAILED = '57', // 'Ltv validation failed'
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'
);
});
});
6 changes: 3 additions & 3 deletions test-suites/pool-drop-reserve.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ makeSuite('Pool: Drop Reserve', (testEnv: TestEnv) => {
let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;

const {
ATOKEN_SUPPLY_NOT_ZERO,
UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO,
STABLE_DEBT_NOT_ZERO,
VARIABLE_DEBT_SUPPLY_NOT_ZERO,
ASSET_NOT_LISTED,
Expand Down Expand Up @@ -46,7 +46,7 @@ makeSuite('Pool: Drop Reserve', (testEnv: TestEnv) => {

await pool.deposit(dai.address, depositedAmount, deployer.address, 0);

await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(ATOKEN_SUPPLY_NOT_ZERO);
await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO);

await pool.connect(user1.signer).deposit(weth.address, depositedAmount, user1.address, 0);

Expand All @@ -71,7 +71,7 @@ makeSuite('Pool: Drop Reserve', (testEnv: TestEnv) => {
);

expect(await pool.connect(user1.signer).repay(dai.address, MAX_UINT_AMOUNT, 2, user1.address));
await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(ATOKEN_SUPPLY_NOT_ZERO);
await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(UNDERLYING_CLAIMABLE_RIGHTS_NOT_ZERO);
});

it('User 1 withdraw DAI, drop DAI reserve should succeed', async () => {
Expand Down
Loading