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

Fix for #315 (v2 Polygon) #316

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
34 changes: 20 additions & 14 deletions contracts/protocol/libraries/logic/ReserveLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ library ReserveLogic {
* @param reserve the reserve object
**/
function updateState(DataTypes.ReserveData storage reserve) internal {
// If time didn't pass since last stored timestamp, skip state update
//solium-disable-next-line
if (reserve.lastUpdateTimestamp == uint40(block.timestamp)) {
return;
}

uint256 scaledVariableDebt =
IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply();
uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
Expand Down Expand Up @@ -343,27 +349,27 @@ library ReserveLogic {
uint256 newLiquidityIndex = liquidityIndex;
uint256 newVariableBorrowIndex = variableBorrowIndex;

//only cumulating if there is any income being produced
// Only cumulating on the supply side if there is any income being produced
// The case of Reserve Factor 100% is not a problem (currentLiquidityRate == 0),
// as liquidity index should not be updated
if (currentLiquidityRate > 0) {
uint256 cumulatedLiquidityInterest =
MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp);
newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex);
require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);

reserve.liquidityIndex = uint128(newLiquidityIndex);
}

//as the liquidity rate might come only from stable rate loans, we need to ensure
//that there is actual variable debt before accumulating
if (scaledVariableDebt != 0) {
uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
require(
newVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
);
reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
}
// Variable borrow side only gets updated if there is any accrual of variable debt
if (scaledVariableDebt > 0) {
uint256 cumulatedVariableBorrowInterest =
MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
require(
newVariableBorrowIndex <= type(uint128).max,
Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
);
reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
}

//solium-disable-next-line
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"test-amm": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/*.spec.ts",
"test-amm-scenarios": "npm run compile && TS_NODE_TRANSPILE_ONLY=1 hardhat test ./test-suites/test-amm/__setup.spec.ts test-suites/test-amm/scenario.spec.ts",
"test-scenarios": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.spec.ts",
"test-reserve-factor": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/reserve-factor.spec.ts",
"test-subgraph:scenarios": "npm run compile && hardhat --network hardhatevm_docker test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/subgraph-scenarios.spec.ts",
"test:main:check-list": "npm run compile && FORK=main TS_NODE_TRANSPILE_ONLY=1 hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/mainnet/check-list.spec.ts",
"dev:coverage": "buidler compile --force && buidler coverage --network coverage",
Expand Down
192 changes: 192 additions & 0 deletions test-suites/test-aave/reserve-factor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import BigNumber from 'bignumber.js';

import { DRE, increaseTime } from '../../helpers/misc-utils';
import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants';
import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers';
import { makeSuite, TestEnv } from './helpers/make-suite';
import { RateMode } from '../../helpers/types';
import { ConfigNames, getTreasuryAddress, loadPoolConfig } from '../../helpers/configuration';

const chai = require('chai');

const { expect } = chai;

// Setup function to have 1 user with DAI deposits, and another user with WETH collateral
// and DAI borrowings at an indicated borrowing mode
const setupPositions = async (testEnv: TestEnv, borrowingMode: RateMode) => {
const { dai, weth, users, pool, oracle } = testEnv;
const depositor = users[0];
const borrower = users[1];

// mints DAI to depositor
await dai.connect(depositor.signer).mint(await convertToCurrencyDecimals(dai.address, '2000'));

// approve protocol to access depositor wallet
await dai.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);

// user 1 deposits 1000 DAI
const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000');

await pool
.connect(depositor.signer)
.deposit(dai.address, amountDAItoDeposit, depositor.address, '0');
// user 2 deposits 1 ETH
const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1');

// mints WETH to borrower
await weth.connect(borrower.signer).mint(await convertToCurrencyDecimals(weth.address, '1000'));

// approve protocol to access the borrower wallet
await weth.connect(borrower.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);

await pool
.connect(borrower.signer)
.deposit(weth.address, amountETHtoDeposit, borrower.address, '0');

//user 2 borrows

const userGlobalData = await pool.getUserAccountData(borrower.address);
const daiPrice = await oracle.getAssetPrice(dai.address);

const amountDAIToBorrow = await convertToCurrencyDecimals(
dai.address,
new BigNumber(userGlobalData.availableBorrowsETH.toString())
.div(daiPrice.toString())
.multipliedBy(0.95)
.toFixed(0)
);

await pool
.connect(borrower.signer)
.borrow(dai.address, amountDAIToBorrow, borrowingMode, '0', borrower.address);
};

makeSuite('LendingPool Reserve Factor 100%. Only variable borrowings', (testEnv) => {
before('Before LendingPool Reserve Factor accrual: set config', () => {
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
});

after('After LendingPool Reserve Factor accrual: reset config', () => {
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
});

it('Validates that variable borrow index accrue, liquidity index not, and the Collector receives aTokens after interest accrues', async () => {
const { configurator, dai, users, pool, aDai } = testEnv;

await setupPositions(testEnv, RateMode.Variable);

// Set the RF to 100%
await configurator.setReserveFactor(dai.address, '10000');

const depositor = users[0];

const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave));

const collectorADAIBalanceBefore = await aDai.scaledBalanceOf(collectorAddress);

const reserveDataBefore = await pool.getReserveData(dai.address);

await increaseTime(10000);

// Deposit to "settle" the liquidity index accrual from pre-RF increase to 100%
await pool
.connect(depositor.signer)
.deposit(
dai.address,
await convertToCurrencyDecimals(dai.address, '1'),
depositor.address,
'0'
);

const reserveDataAfter1 = await pool.getReserveData(dai.address);
const collectorADAIBalanceAfter1 = await aDai.balanceOf(collectorAddress);

expect(reserveDataAfter1.variableBorrowIndex).to.be.gt(reserveDataBefore.variableBorrowIndex);
expect(collectorADAIBalanceAfter1).to.be.gt(collectorADAIBalanceBefore);
expect(reserveDataAfter1.liquidityIndex).to.be.gt(reserveDataBefore.liquidityIndex);

await increaseTime(10000);

// "Clean" update, that should not increase the liquidity index, only variable borrow
await pool
.connect(depositor.signer)
.deposit(
dai.address,
await convertToCurrencyDecimals(dai.address, '1'),
depositor.address,
'0'
);

const reserveDataAfter2 = await pool.getReserveData(dai.address);
const collectorADAIBalanceAfter2 = await aDai.balanceOf(collectorAddress);

expect(reserveDataAfter2.variableBorrowIndex).to.be.gt(reserveDataAfter1.variableBorrowIndex);
expect(collectorADAIBalanceAfter2).to.be.gt(collectorADAIBalanceAfter1);
expect(reserveDataAfter2.liquidityIndex).to.be.eq(reserveDataAfter1.liquidityIndex);
});
});

makeSuite('LendingPool Reserve Factor 100%. Only stable borrowings', (testEnv) => {
before('Before LendingPool Reserve Factor accrual: set config', () => {
BigNumber.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumber.ROUND_DOWN });
});

after('After LendingPool Reserve Factor accrual: reset config', () => {
BigNumber.config({ DECIMAL_PLACES: 20, ROUNDING_MODE: BigNumber.ROUND_HALF_UP });
});

it('Validates that neither variable borrow index nor liquidity index increase, but the Collector receives aTokens after interest accrues', async () => {
const { configurator, dai, users, pool, aDai } = testEnv;

await setupPositions(testEnv, RateMode.Stable);

// Set the RF to 100%
await configurator.setReserveFactor(dai.address, '10000');

const depositor = users[0];

const collectorAddress = await getTreasuryAddress(loadPoolConfig(ConfigNames.Aave));

const collectorADAIBalanceBefore = await aDai.scaledBalanceOf(collectorAddress);

const reserveDataBefore = await pool.getReserveData(dai.address);

await increaseTime(10000);

// Deposit to "settle" the liquidity index accrual from pre-RF increase to 100%
await pool
.connect(depositor.signer)
.deposit(
dai.address,
await convertToCurrencyDecimals(dai.address, '1'),
depositor.address,
'0'
);

const reserveDataAfter1 = await pool.getReserveData(dai.address);
const collectorADAIBalanceAfter1 = await aDai.balanceOf(collectorAddress);

expect(reserveDataAfter1.variableBorrowIndex).to.be.eq(reserveDataBefore.variableBorrowIndex);
expect(collectorADAIBalanceAfter1).to.be.gt(collectorADAIBalanceBefore);
expect(reserveDataAfter1.liquidityIndex).to.be.gt(reserveDataBefore.liquidityIndex);

await increaseTime(10000);

// "Clean" update, that should not increase the liquidity index, only variable borrow
await pool
.connect(depositor.signer)
.deposit(
dai.address,
await convertToCurrencyDecimals(dai.address, '1'),
depositor.address,
'0'
);

const reserveDataAfter2 = await pool.getReserveData(dai.address);
const collectorADAIBalanceAfter2 = await aDai.balanceOf(collectorAddress);

expect(reserveDataAfter2.variableBorrowIndex).to.be.eq(reserveDataAfter1.variableBorrowIndex);
expect(collectorADAIBalanceAfter2).to.be.gt(collectorADAIBalanceAfter1);
expect(reserveDataAfter2.liquidityIndex).to.be.eq(reserveDataAfter1.liquidityIndex);
});
});