From 5f0fb4bec923592e8d8b96cae2a8c7c28ff1fb07 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 29 Apr 2021 20:40:37 +0200 Subject: [PATCH 01/64] refactor: changed the behavior of mintToTreasury --- contracts/interfaces/ILendingPool.sol | 10 ++++++++++ .../protocol/lendingpool/LendingPool.sol | 20 +++++++++++++++++++ .../protocol/libraries/logic/ReserveLogic.sol | 2 +- .../protocol/libraries/types/DataTypes.sol | 2 ++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 64f726c0..1178d350 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -167,6 +167,16 @@ interface ILendingPool { uint256 variableBorrowIndex ); + /** + * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. + * @param reserve the address of the reserve + * @param amountMinted the amount minted to the treasury + **/ + event TreasuryUpdated( + address indexed reserve, + uint256 amountMinted + ); + /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. * - E.g. User deposits 100 USDC and gets in return 100 aUSDC diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 531f007b..1425b0ff 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -479,6 +479,26 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } } + /** + * @dev Mints the assets accrued through the reserve factor to the treasury in the form of aTokens + **/ + function mintToTreasury() public { + for (uint256 i = 0; i < _reservesCount; i++) { + address reserveAddress = _reservesList[i]; + DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; + uint256 accruedToTreasury = reserve.accruedToTreasury; + + if (accruedToTreasury != 0) { + uint256 normalizedIncome = reserve.getNormalizedIncome(); + uint256 amountToMint = accruedToTreasury.rayMul(normalizedIncome); + IAToken(reserve.aTokenAddress).mintToTreasury(amountToMint, normalizedIncome); + + reserve.accruedToTreasury = 0; + emit TreasuryUpdated(reserveAddress, amountToMint); + } + } + } + /** * @dev Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2b5b2cf4..485ad0d3 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -320,7 +320,7 @@ library ReserveLogic { vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { - IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex); + reserve.accruedToTreasury = reserve.accruedToTreasury.add(vars.amountToMint.rayDiv(newLiquidityIndex)); } } diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index a19e5efc..ffec1d20 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -25,6 +25,8 @@ library DataTypes { address interestRateStrategyAddress; //the id of the reserve. Represents the position in the list of the active reserves uint8 id; + //the current treasury balance, scaled + uint256 accruedToTreasury; } struct ReserveConfigurationMap { From c6dbc98926ea94230b84419df04b4c950586c9b2 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 6 May 2021 16:21:55 +0200 Subject: [PATCH 02/64] test: initial test for mintToTreasury() --- .../test-aave/mint-to-treasury.spec.ts | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test-suites/test-aave/mint-to-treasury.spec.ts diff --git a/test-suites/test-aave/mint-to-treasury.spec.ts b/test-suites/test-aave/mint-to-treasury.spec.ts new file mode 100644 index 00000000..29813520 --- /dev/null +++ b/test-suites/test-aave/mint-to-treasury.spec.ts @@ -0,0 +1,62 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther, ONE_YEAR } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; +import { advanceTimeAndBlock, waitForTx } from '../../helpers/misc-utils'; + +const { expect } = require('chai'); + +makeSuite('Mint to treasury', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + LP_IS_PAUSED, + INVALID_FROM_BALANCE_AFTER_TRANSFER, + INVALID_TO_BALANCE_AFTER_TRANSFER, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 0 deposits 1000 DAI. Borrower borrows 100 DAI. Clock moved forward one year. Calculates and verifies the amount earned by the treasury', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + const amountDAItoBorrow = await convertToCurrencyDecimals(dai.address, '100'); + + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + await pool + .connect(users[0].signer) + .borrow(dai.address, amountDAItoBorrow, RateMode.Variable, '0', users[0].address); + + + await advanceTimeAndBlock(parseInt(ONE_YEAR)); + + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + const reserveData = await pool.getReserveData(dai.address); + + console.log(reserveData.accruedToTreasury.toString()); + + }); + + +}); From ce052c669f73fd31039e6e789c10429275bc8a79 Mon Sep 17 00:00:00 2001 From: The3D Date: Fri, 7 May 2021 20:04:24 +0200 Subject: [PATCH 03/64] refactor: partially fixed mint to treasury tests --- .../test-aave/helpers/utils/calculations.ts | 2 +- .../test-aave/mint-to-treasury.spec.ts | 43 +++++++++++++------ .../test-amm/helpers/utils/calculations.ts | 2 +- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/test-suites/test-aave/helpers/utils/calculations.ts b/test-suites/test-aave/helpers/utils/calculations.ts index de3d31f9..db633d51 100644 --- a/test-suites/test-aave/helpers/utils/calculations.ts +++ b/test-suites/test-aave/helpers/utils/calculations.ts @@ -1196,7 +1196,7 @@ const calcLinearInterest = ( return cumulatedInterest; }; -const calcCompoundedInterest = ( +export const calcCompoundedInterest = ( rate: BigNumber, currentTimestamp: BigNumber, lastUpdateTimestamp: BigNumber diff --git a/test-suites/test-aave/mint-to-treasury.spec.ts b/test-suites/test-aave/mint-to-treasury.spec.ts index 29813520..44cf7e33 100644 --- a/test-suites/test-aave/mint-to-treasury.spec.ts +++ b/test-suites/test-aave/mint-to-treasury.spec.ts @@ -1,12 +1,15 @@ import { makeSuite, TestEnv } from './helpers/make-suite'; import { ProtocolErrors, RateMode } from '../../helpers/types'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneEther, ONE_YEAR } from '../../helpers/constants'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther, ONE_YEAR, RAY } from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { parseEther, parseUnits } from 'ethers/lib/utils'; import { BigNumber } from 'bignumber.js'; import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; -import { advanceTimeAndBlock, waitForTx } from '../../helpers/misc-utils'; +import { advanceTimeAndBlock, timeLatest, waitForTx } from '../../helpers/misc-utils'; +import './helpers/utils/math'; +import { calcCompoundedInterest } from './helpers/utils/calculations'; +import {getTxCostAndTimestamp } from './helpers/actions'; const { expect } = require('chai'); @@ -24,11 +27,10 @@ makeSuite('Mint to treasury', (testEnv: TestEnv) => { }); it('User 0 deposits 1000 DAI. Borrower borrows 100 DAI. Clock moved forward one year. Calculates and verifies the amount earned by the treasury', async () => { - const { users, pool, dai, aDai, configurator } = testEnv; + const { users, pool, dai, helpersContract } = testEnv; const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); const amountDAItoBorrow = await convertToCurrencyDecimals(dai.address, '100'); - await dai.connect(users[0].signer).mint(amountDAItoDeposit); @@ -38,25 +40,40 @@ makeSuite('Mint to treasury', (testEnv: TestEnv) => { .connect(users[0].signer) .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); - await pool + const borrowTx = await waitForTx(await pool .connect(users[0].signer) - .borrow(dai.address, amountDAItoBorrow, RateMode.Variable, '0', users[0].address); + .borrow(dai.address, amountDAItoBorrow, RateMode.Variable, '0', users[0].address)); + const {txTimestamp : lastUpdateTimestamp} = await getTxCostAndTimestamp(borrowTx); + + + const { + currentLiquidityRate: liquidityRate, + currentVariableBorrowRate: variableBorrowRate, + } = await pool.getReserveData(dai.address); + const { reserveFactor } = await helpersContract.getReserveConfigurationData(dai.address); await advanceTimeAndBlock(parseInt(ONE_YEAR)); - await dai.connect(users[0].signer).mint(amountDAItoDeposit); - await pool + const depositTx = await waitForTx(await pool .connect(users[0].signer) - .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0')); - const reserveData = await pool.getReserveData(dai.address); + + const {txTimestamp : currTimestamp} = await getTxCostAndTimestamp(depositTx); - console.log(reserveData.accruedToTreasury.toString()); + const interestNormalized = calcCompoundedInterest(new BigNumber(variableBorrowRate.toString()), currTimestamp, lastUpdateTimestamp).minus(RAY); + + const expectedReserveFactor = new BigNumber(amountDAItoBorrow.toString()) + .rayMul(interestNormalized) + .times(reserveFactor.toString()) + .div(10000); + + const reserveData = await pool.getReserveData(dai.address); + + console.log(reserveData.accruedToTreasury.toString(), expectedReserveFactor.toString()); }); - - }); diff --git a/test-suites/test-amm/helpers/utils/calculations.ts b/test-suites/test-amm/helpers/utils/calculations.ts index e1574656..310df15b 100644 --- a/test-suites/test-amm/helpers/utils/calculations.ts +++ b/test-suites/test-amm/helpers/utils/calculations.ts @@ -1195,7 +1195,7 @@ const calcLinearInterest = ( return cumulatedInterest; }; -const calcCompoundedInterest = ( +export const calcCompoundedInterest = ( rate: BigNumber, currentTimestamp: BigNumber, lastUpdateTimestamp: BigNumber From 42a07e75abaa5b15347071d0597fe50ccdcd3f72 Mon Sep 17 00:00:00 2001 From: The3D Date: Sat, 8 May 2021 18:53:56 +0200 Subject: [PATCH 04/64] test: fixed the accrued to treasury test --- .../test-aave/mint-to-treasury.spec.ts | 86 ++++++++----------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/test-suites/test-aave/mint-to-treasury.spec.ts b/test-suites/test-aave/mint-to-treasury.spec.ts index 44cf7e33..00e24e83 100644 --- a/test-suites/test-aave/mint-to-treasury.spec.ts +++ b/test-suites/test-aave/mint-to-treasury.spec.ts @@ -1,79 +1,69 @@ import { makeSuite, TestEnv } from './helpers/make-suite'; -import { ProtocolErrors, RateMode } from '../../helpers/types'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneEther, ONE_YEAR, RAY } from '../../helpers/constants'; +import { RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, ONE_YEAR } from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; -import { parseEther, parseUnits } from 'ethers/lib/utils'; import { BigNumber } from 'bignumber.js'; -import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; -import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; -import { advanceTimeAndBlock, timeLatest, waitForTx } from '../../helpers/misc-utils'; +import { advanceTimeAndBlock, waitForTx } from '../../helpers/misc-utils'; import './helpers/utils/math'; -import { calcCompoundedInterest } from './helpers/utils/calculations'; -import {getTxCostAndTimestamp } from './helpers/actions'; const { expect } = require('chai'); makeSuite('Mint to treasury', (testEnv: TestEnv) => { - let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; - - const { - LP_IS_PAUSED, - INVALID_FROM_BALANCE_AFTER_TRANSFER, - INVALID_TO_BALANCE_AFTER_TRANSFER, - } = ProtocolErrors; - - before(async () => { - _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); - }); - it('User 0 deposits 1000 DAI. Borrower borrows 100 DAI. Clock moved forward one year. Calculates and verifies the amount earned by the treasury', async () => { const { users, pool, dai, helpersContract } = testEnv; const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); const amountDAItoBorrow = await convertToCurrencyDecimals(dai.address, '100'); - await dai.connect(users[0].signer).mint(amountDAItoDeposit); + await waitForTx(await dai.connect(users[0].signer).mint(amountDAItoDeposit)); // user 0 deposits 1000 DAI - await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); - await pool - .connect(users[0].signer) - .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + await waitForTx( + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL) + ); + await waitForTx( + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0') + ); - const borrowTx = await waitForTx(await pool - .connect(users[0].signer) - .borrow(dai.address, amountDAItoBorrow, RateMode.Variable, '0', users[0].address)); + await waitForTx( + await pool + .connect(users[0].signer) + .borrow(dai.address, amountDAItoBorrow, RateMode.Variable, '0', users[0].address) + ); - const {txTimestamp : lastUpdateTimestamp} = await getTxCostAndTimestamp(borrowTx); - - - const { - currentLiquidityRate: liquidityRate, - currentVariableBorrowRate: variableBorrowRate, - } = await pool.getReserveData(dai.address); const { reserveFactor } = await helpersContract.getReserveConfigurationData(dai.address); await advanceTimeAndBlock(parseInt(ONE_YEAR)); - await dai.connect(users[0].signer).mint(amountDAItoDeposit); + await waitForTx(await dai.connect(users[0].signer).mint(amountDAItoDeposit)); - const depositTx = await waitForTx(await pool - .connect(users[0].signer) - .deposit(dai.address, amountDAItoDeposit, users[0].address, '0')); + await waitForTx( + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0') + ); - - const {txTimestamp : currTimestamp} = await getTxCostAndTimestamp(depositTx); + const { liquidityIndex, variableBorrowIndex } = await pool.getReserveData(dai.address); - const interestNormalized = calcCompoundedInterest(new BigNumber(variableBorrowRate.toString()), currTimestamp, lastUpdateTimestamp).minus(RAY); + const amountBorrowedBN = new BigNumber(amountDAItoBorrow.toString()); + const liquidityIndexBN = new BigNumber(liquidityIndex.toString()); + const variableBorrowIndexBN = new BigNumber(variableBorrowIndex.toString()); - - const expectedReserveFactor = new BigNumber(amountDAItoBorrow.toString()) - .rayMul(interestNormalized) + const expectedAccruedToTreasury = amountBorrowedBN + .rayMul(variableBorrowIndexBN) + .minus(amountBorrowedBN) .times(reserveFactor.toString()) - .div(10000); + .div(10000) + .rayDiv(liquidityIndexBN) + .toFixed(0); - const reserveData = await pool.getReserveData(dai.address); + const { accruedToTreasury } = await pool.getReserveData(dai.address); - console.log(reserveData.accruedToTreasury.toString(), expectedReserveFactor.toString()); + expect(accruedToTreasury.toString()).to.be.bignumber.almostEqual( + expectedAccruedToTreasury, + 'Invalid amount accrued to treasury' + ); }); }); From 2e1af3cadde70da03ed3bb12d20191db8ddae593 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 5 May 2021 10:16:09 +0200 Subject: [PATCH 05/64] feat: added bool isPaused in data reserve config --- .../interfaces/ILendingPoolConfigurator.sol | 12 ++++++ contracts/misc/AaveProtocolDataProvider.sol | 11 +++++- contracts/misc/UiPoolDataProvider.sol | 3 +- contracts/misc/WalletBalanceProvider.sol | 2 +- .../misc/interfaces/IUiPoolDataProvider.sol | 1 + .../lendingpool/LendingPoolConfigurator.sol | 39 +++++++++++++++++++ .../configuration/ReserveConfiguration.sol | 31 ++++++++++++++- .../protocol/libraries/helpers/Errors.sol | 2 + .../libraries/logic/ValidationLogic.sol | 16 +++++--- .../protocol/libraries/types/DataTypes.sol | 3 +- 10 files changed, 107 insertions(+), 13 deletions(-) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index fea5560c..fd3bcb19 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -120,6 +120,18 @@ interface ILendingPoolConfigurator { **/ event ReserveUnfrozen(address indexed asset); + /** + * @dev Emitted when a reserve is paused + * @param asset The address of the underlying asset of the reserve + **/ + event ReservePaused(address indexed asset); + + /** + * @dev Emitted when a reserve is unpaused + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveUnpaused(address indexed asset); + /** * @dev Emitted when a reserve factor is updated * @param asset The address of the underlying asset of the reserve diff --git a/contracts/misc/AaveProtocolDataProvider.sol b/contracts/misc/AaveProtocolDataProvider.sol index d07a5f1b..95deb6ea 100644 --- a/contracts/misc/AaveProtocolDataProvider.sol +++ b/contracts/misc/AaveProtocolDataProvider.sol @@ -64,7 +64,7 @@ contract AaveProtocolDataProvider { return aTokens; } - // not returning borrow and supply caps for compatibility + // not returning borrow and supply caps for compatibility, nor pause flag function getReserveConfigurationData(address asset) external view @@ -87,7 +87,7 @@ contract AaveProtocolDataProvider { (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration.getParamsMemory(); - (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled, ) = configuration.getFlagsMemory(); usageAsCollateralEnabled = liquidationThreshold > 0; @@ -103,6 +103,13 @@ contract AaveProtocolDataProvider { .getCapsMemory(); } + function getPaused(address asset) external view returns (bool isPaused) { + (, , , , isPaused) = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()) + .getConfiguration(asset) + .getFlagsMemory(); + } + function getReserveData(address asset) external view diff --git a/contracts/misc/UiPoolDataProvider.sol b/contracts/misc/UiPoolDataProvider.sol index 2a1b6bae..4e4641c3 100644 --- a/contracts/misc/UiPoolDataProvider.sol +++ b/contracts/misc/UiPoolDataProvider.sol @@ -122,7 +122,8 @@ contract UiPoolDataProvider is IUiPoolDataProvider { reserveData.isActive, reserveData.isFrozen, reserveData.borrowingEnabled, - reserveData.stableBorrowRateEnabled + reserveData.stableBorrowRateEnabled, + reserveData.isPaused ) = baseData.configuration.getFlagsMemory(); reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0; ( diff --git a/contracts/misc/WalletBalanceProvider.sol b/contracts/misc/WalletBalanceProvider.sol index 3d4a9288..22a6f55e 100644 --- a/contracts/misc/WalletBalanceProvider.sol +++ b/contracts/misc/WalletBalanceProvider.sol @@ -96,7 +96,7 @@ contract WalletBalanceProvider { DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(reservesWithEth[j]); - (bool isActive, , , ) = configuration.getFlagsMemory(); + (bool isActive, , , , ) = configuration.getFlagsMemory(); if (!isActive) { balances[j] = 0; diff --git a/contracts/misc/interfaces/IUiPoolDataProvider.sol b/contracts/misc/interfaces/IUiPoolDataProvider.sol index 5ea96464..ac7d344a 100644 --- a/contracts/misc/interfaces/IUiPoolDataProvider.sol +++ b/contracts/misc/interfaces/IUiPoolDataProvider.sol @@ -22,6 +22,7 @@ interface IUiPoolDataProvider { bool stableBorrowRateEnabled; bool isActive; bool isFrozen; + bool isPaused; // base data uint128 liquidityIndex; uint128 variableBorrowIndex; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 85cd32cf..f38432f8 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -46,6 +46,16 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur _; } + modifier onlyEmergencyOrPoolAdmin { + require( + addressesProvider.getEmergencyAdmin() == msg.sender + || addressesProvider.getPoolAdmin() == msg.sender, + Errors.LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN + ); + _; + } + + uint256 internal constant CONFIGURATOR_REVISION = 0x1; function getRevision() internal pure override returns (uint256) { @@ -128,6 +138,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur currentConfig.setDecimals(input.underlyingAssetDecimals); currentConfig.setActive(true); + currentConfig.setPaused(false); currentConfig.setFrozen(false); pool.setConfiguration(input.underlyingAsset, currentConfig.data); @@ -416,6 +427,34 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit ReserveUnfrozen(asset); } + /** + * @dev Pauses a reserve. A paused reserve allow now user moves such as deposit, borrow, repay, swap interestrate, liquidate + * @param asset The address of the underlying asset of the reserve + **/ + function pauseReserve(address asset) external onlyEmergencyOrPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setPaused(true); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReservePaused(asset); + } + + /** + * @dev Unpauses a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function unpauseReserve(address asset) external onlyEmergencyOrPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setPaused(false); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveUnpaused(asset); + } + /** * @dev Updates the reserve factor of a reserve * @param asset The address of the underlying asset of the reserve diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 77375778..59cb734d 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -18,6 +18,7 @@ library ReserveConfiguration { uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore + uint256 constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore @@ -30,6 +31,8 @@ library ReserveConfiguration { uint256 constant IS_FROZEN_START_BIT_POSITION = 57; uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; + uint256 constant IS_PAUSED_START_BIT_POSITION = 60; + // bits 61 62 63 unused yet uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 constant BORROW_CAP_START_BIT_POSITION = 80; uint256 constant SUPPLY_CAP_START_BIT_POSITION = 128; @@ -187,6 +190,26 @@ library ReserveConfiguration { return (self.data & ~FROZEN_MASK) != 0; } + /** + * @dev Sets the paused state of the reserve + * @param self The reserve configuration + * @param paused The paused state + **/ + function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure { + self.data = + (self.data & PAUSED_MASK) | + (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION); + } + + /** + * @dev Gets the paused state of the reserve + * @param self The reserve configuration + * @return The paused state + **/ + function getPaused(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~PAUSED_MASK) != 0; + } + /** * @dev Enables or disables borrowing on the reserve * @param self The reserve configuration @@ -340,6 +363,7 @@ library ReserveConfiguration { bool, bool, bool, + bool, bool ) { @@ -349,7 +373,8 @@ library ReserveConfiguration { (dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, - (dataLocal & ~STABLE_BORROWING_MASK) != 0 + (dataLocal & ~STABLE_BORROWING_MASK) != 0, + (dataLocal & ~PAUSED_MASK) != 0 ); } @@ -457,6 +482,7 @@ library ReserveConfiguration { bool, bool, bool, + bool, bool ) { @@ -464,7 +490,8 @@ library ReserveConfiguration { (self.data & ~ACTIVE_MASK) != 0, (self.data & ~FROZEN_MASK) != 0, (self.data & ~BORROWING_MASK) != 0, - (self.data & ~STABLE_BORROWING_MASK) != 0 + (self.data & ~STABLE_BORROWING_MASK) != 0, + (self.data & ~PAUSED_MASK) != 0 ); } } diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 34e3521f..9fa7e96e 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -106,6 +106,8 @@ library Errors { string public constant RC_INVALID_BORROW_CAP = '82'; string public constant VL_SUPPLY_CAP_EXCEEDED = '83'; string public constant RC_INVALID_SUPPLY_CAP = '84'; + string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; + string public constant VL_RESERVE_PAUSED = '86'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 6473ebfe..8038043e 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -40,8 +40,8 @@ library ValidationLogic { * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ - function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { - (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); + function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { + (bool isActive, bool isFrozen, , , ) = reserve.configuration.getFlags(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -70,7 +70,7 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , ) = reserve.configuration.getFlags(); + (bool isActive, , , , ) = reservesData[reserveAddress].configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); } @@ -86,6 +86,7 @@ library ValidationLogic { uint256 totalSupplyVariableDebt; bool isActive; bool isFrozen; + bool isPaused; bool borrowingEnabled; bool stableRateBorrowingEnabled; } @@ -121,11 +122,12 @@ library ValidationLogic { ) internal view { ValidateBorrowLocalVars memory vars; - (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve + (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled, vars.isPaused) = reserve .configuration .getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); require(amount != 0, Errors.VL_INVALID_AMOUNT); @@ -267,9 +269,10 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) external view { - (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags(); + (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isFrozen, Errors.VL_RESERVE_FROZEN); if (currentRateMode == DataTypes.InterestRateMode.STABLE) { @@ -311,9 +314,10 @@ library ValidationLogic { IERC20 variableDebtToken, address aTokenAddress ) external view { - (bool isActive, , , ) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); //if the usage ratio is below 95%, no rebalances are needed uint256 totalDebt = diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 419efe59..b56c8b47 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -36,7 +36,8 @@ library DataTypes { //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 59: stable rate borrowing enabled - //bit 60-63: reserved + //bit 60: asset is paused + //bit 61-63: reserved //bit 64-79: reserve factor //bit 80-127 borrow cap //bit 128-175 borrow cap From 87e69e84e18fa0e2b309722d64d0b9e65f5d0786 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 5 May 2021 14:06:27 +0200 Subject: [PATCH 06/64] test: tested configurator --- helpers/types.ts | 2 + test-suites/test-aave/configurator.spec.ts | 177 ++++++++++++++++++++- 2 files changed, 178 insertions(+), 1 deletion(-) diff --git a/helpers/types.ts b/helpers/types.ts index ad6dbced..45f7ddcf 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -181,6 +181,8 @@ export enum ProtocolErrors { RC_INVALID_BORROW_CAP = '82', VL_SUPPLY_CAP_EXCEEDED = '83', RC_INVALID_SUPPLY_CAP = '84', + LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', + VL_RESERVE_PAUSED = '86', // old diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index d4e3d0f1..f05d5a5b 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -1,5 +1,10 @@ import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, RAY, MAX_BORROW_CAP } from '../../helpers/constants'; +import { + APPROVAL_AMOUNT_LENDING_POOL, + MAX_UINT_AMOUNT, + RAY, + MAX_BORROW_CAP, +} from '../../helpers/constants'; import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; import { ProtocolErrors } from '../../helpers/types'; import { strategyWETH } from '../../markets/aave/reservesConfigs'; @@ -18,6 +23,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_RESERVE_FACTOR, RC_INVALID_BORROW_CAP, RC_INVALID_SUPPLY_CAP, + LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN, + VL_RESERVE_PAUSED, } = ProtocolErrors; it('Reverts trying to set an invalid reserve factor', async () => { @@ -60,6 +67,152 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); + it('Pauses the ETH reserve by pool admin', async () => { + const { configurator, weth, helpersContract, addressesProvider, users } = testEnv; + expect(await configurator.signer.getAddress()).to.be.equal( + await addressesProvider.getPoolAdmin() + ); + + await configurator.pauseReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Unpauses the ETH reserve by pool admin ', async () => { + const { configurator, helpersContract, weth } = testEnv; + await configurator.unpauseReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + it('Pauses the ETH reserve by emergency admin', async () => { + const { configurator, weth, helpersContract, addressesProvider, users } = testEnv; + expect(users[1].address).to.be.equal(await addressesProvider.getEmergencyAdmin()); + + await configurator.connect(users[1].signer).pauseReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(true); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Unpauses the ETH reserve by emergency admin ', async () => { + const { configurator, helpersContract, weth, users } = testEnv; + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Check the only admin or emergency admin can pauseReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).pauseReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); + }); + + it('Check the only admin or emergency admin can unpauseReserve ', async () => { + const { configurator, users, weth } = testEnv; + await expect( + configurator.connect(users[2].signer).unpauseReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); + }); it('Freezes the ETH reserve', async () => { const { configurator, weth, helpersContract } = testEnv; @@ -77,9 +230,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(true); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -107,9 +262,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -152,9 +309,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(false); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -183,9 +342,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -233,9 +394,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(18); expect(ltv).to.be.equal(0); @@ -263,9 +426,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -302,9 +467,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -331,9 +498,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -399,9 +568,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -443,9 +614,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); @@ -472,9 +645,11 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { isFrozen, } = await helpersContract.getReserveConfigurationData(weth.address); const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); expect(borrowingEnabled).to.be.equal(true); expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); expect(isFrozen).to.be.equal(false); expect(decimals).to.be.equal(strategyWETH.reserveDecimals); expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); From 788ab15665c4c84963a4977e3331594b89b6f746 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 10 May 2021 10:01:08 +0200 Subject: [PATCH 07/64] spec: added spec for reseve pause --- package.json | 2 + test-suites/test-aave/reserve-pause.spec.ts | 330 ++++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 test-suites/test-aave/reserve-pause.spec.ts diff --git a/package.json b/package.json index cf887ed4..575a1dde 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "test-scenarios": "npm run compile && npx hardhat test test-suites/test-aave/__setup.spec.ts test-suites/test-aave/scenario.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", + "test:aave": "hardhat test test-suites/test-aave/__setup.spec.ts", + "test:amm": "hardhat test test-suites/test-amm/__setup.spec.ts", "dev:coverage": "buidler compile --force && buidler coverage --network coverage", "aave:evm:dev:migration": "npm run compile && hardhat aave:dev", "aave:docker:full:migration": "npm run compile && npm run hardhat:docker -- aave:mainnet --skip-registry", diff --git a/test-suites/test-aave/reserve-pause.spec.ts b/test-suites/test-aave/reserve-pause.spec.ts new file mode 100644 index 00000000..f6a57d0c --- /dev/null +++ b/test-suites/test-aave/reserve-pause.spec.ts @@ -0,0 +1,330 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('Pausable Pool', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + VL_RESERVE_PAUSED, + INVALID_FROM_BALANCE_AFTER_TRANSFER, + INVALID_TO_BALANCE_AFTER_TRANSFER, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 0 deposits 1000 DAI. Configurator pauses pool. Transfers to user 1 reverts. Configurator unpauses the network and next transfer succees', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + const user0Balance = await aDai.balanceOf(users[0].address); + const user1Balance = await aDai.balanceOf(users[1].address); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // User 0 tries the transfer to User 1 + await expect( + aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit) + ).to.revertedWith(VL_RESERVE_PAUSED); + + const pausedFromBalance = await aDai.balanceOf(users[0].address); + const pausedToBalance = await aDai.balanceOf(users[1].address); + + expect(pausedFromBalance).to.be.equal( + user0Balance.toString(), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + expect(pausedToBalance.toString()).to.be.equal( + user1Balance.toString(), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + + // User 0 succeeds transfer to User 1 + await aDai.connect(users[0].signer).transfer(users[1].address, amountDAItoDeposit); + + const fromBalance = await aDai.balanceOf(users[0].address); + const toBalance = await aDai.balanceOf(users[1].address); + + expect(fromBalance.toString()).to.be.equal( + user0Balance.sub(amountDAItoDeposit), + INVALID_FROM_BALANCE_AFTER_TRANSFER + ); + expect(toBalance.toString()).to.be.equal( + user1Balance.add(amountDAItoDeposit), + INVALID_TO_BALANCE_AFTER_TRANSFER + ); + }); + + it('Deposit', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + await expect( + pool.connect(users[0].signer).deposit(dai.address, amountDAItoDeposit, users[0].address, '0') + ).to.revertedWith(VL_RESERVE_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Withdraw', async () => { + const { users, pool, dai, aDai, configurator } = testEnv; + + const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await dai.connect(users[0].signer).mint(amountDAItoDeposit); + + // user 0 deposits 1000 DAI + await dai.connect(users[0].signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool + .connect(users[0].signer) + .deposit(dai.address, amountDAItoDeposit, users[0].address, '0'); + + // Configurator pauses the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // user tries to burn + await expect( + pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit, users[0].address) + ).to.revertedWith(VL_RESERVE_PAUSED); + + // Configurator unpauses the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Borrow', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // Try to execute liquidation + await expect( + pool.connect(user.signer).borrow(dai.address, '1', '1', '0', user.address) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Repay', async () => { + const { pool, dai, users, configurator } = testEnv; + + const user = users[1]; + // Pause the pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + // Try to execute liquidation + await expect(pool.connect(user.signer).repay(dai.address, '1', '1', user.address)).revertedWith( + VL_RESERVE_PAUSED + ); + + // Unpause the pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('Flash loan', async () => { + const { dai, pool, weth, users, configurator } = testEnv; + + const caller = users[3]; + + const flashAmount = parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(weth.address); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + caller.address, + '0x10', + '0' + ) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + }); + + it('Liquidation call', async () => { + const { users, pool, usdc, oracle, weth, configurator, helpersContract } = testEnv; + const depositor = users[3]; + const borrower = users[4]; + + //mints USDC to depositor + await usdc + .connect(depositor.signer) + .mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + //approve protocol to access depositor wallet + await usdc.connect(depositor.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + //user 3 deposits 1000 USDC + const amountUSDCtoDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool + .connect(depositor.signer) + .deposit(usdc.address, amountUSDCtoDeposit, depositor.address, '0'); + + //user 4 deposits 1 ETH + const amountETHtoDeposit = await convertToCurrencyDecimals(weth.address, '1'); + + //mints WETH to borrower + await weth.connect(borrower.signer).mint(amountETHtoDeposit); + + //approve protocol to access 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 4 borrows + const userGlobalData = await pool.getUserAccountData(borrower.address); + + const usdcPrice = await oracle.getAssetPrice(usdc.address); + + const amountUSDCToBorrow = await convertToCurrencyDecimals( + usdc.address, + new BigNumber(userGlobalData.availableBorrowsETH.toString()) + .div(usdcPrice.toString()) + .multipliedBy(0.9502) + .toFixed(0) + ); + + await pool + .connect(borrower.signer) + .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address); + + // Drops HF below 1 + await oracle.setAssetPrice( + usdc.address, + new BigNumber(usdcPrice.toString()).multipliedBy(1.2).toFixed(0) + ); + + //mints dai to the liquidator + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const userReserveDataBefore = await helpersContract.getUserReserveData( + usdc.address, + borrower.address + ); + + const amountToLiquidate = new BigNumber(userReserveDataBefore.currentStableDebt.toString()) + .multipliedBy(0.5) + .toFixed(0); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(usdc.address); + + // Do liquidation + await expect( + pool.liquidationCall(weth.address, usdc.address, borrower.address, amountToLiquidate, true) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(usdc.address); + }); + + it('SwapBorrowRateMode', async () => { + const { pool, weth, dai, usdc, users, configurator } = testEnv; + const user = users[1]; + const amountWETHToDeposit = parseEther('10'); + const amountDAIToDeposit = parseEther('120'); + const amountToBorrow = parseUnits('65', 6); + + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + await dai.connect(user.signer).mint(amountDAIToDeposit); + await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0'); + + await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(usdc.address); + + // Try to repay + await expect( + pool.connect(user.signer).swapBorrowRateMode(usdc.address, RateMode.Stable) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(usdc.address); + }); + + it('RebalanceStableBorrowRate', async () => { + const { pool, dai, users, configurator } = testEnv; + const user = users[1]; + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(dai.address); + + await expect( + pool.connect(user.signer).rebalanceStableBorrowRate(dai.address, user.address) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(dai.address); + }); + + it('setUserUseReserveAsCollateral', async () => { + const { pool, weth, users, configurator } = testEnv; + const user = users[1]; + + const amountWETHToDeposit = parseEther('1'); + await weth.connect(user.signer).mint(amountWETHToDeposit); + await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0'); + + // Pause pool + await configurator.connect(users[1].signer).pauseReserve(weth.address); + + await expect( + pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false) + ).revertedWith(VL_RESERVE_PAUSED); + + // Unpause pool + await configurator.connect(users[1].signer).unpauseReserve(weth.address); + }); +}); From 05f66f0513fbc65ed153b0ddac377261b0a442a5 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 10 May 2021 10:01:50 +0200 Subject: [PATCH 08/64] feat: implemented pause validation for reserve assets --- .../protocol/lendingpool/LendingPool.sol | 3 +- .../protocol/libraries/helpers/Errors.sol | 3 +- .../libraries/logic/ValidationLogic.sol | 38 ++++++++++++++++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index f61826f0..ad7f8948 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -465,7 +465,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { FlashLoanLocalVars memory vars; - ValidationLogic.validateFlashloan(assets, amounts); + ValidationLogic.validateFlashloan(assets, amounts, _reserves); address[] memory aTokenAddresses = new address[](assets.length); uint256[] memory premiums = new uint256[](assets.length); @@ -728,6 +728,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (fromConfig.isUsingAsCollateral(reserveId)) { if (fromConfig.isBorrowingAny()) { ValidationLogic.validateHealthFactor( + asset, from, _reserves, _usersConfig[from], diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 9fa7e96e..2b6f015a 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -119,6 +119,7 @@ library Errors { NO_ACTIVE_RESERVE, HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, INVALID_EQUAL_ASSETS_TO_SWAP, - FROZEN_RESERVE + FROZEN_RESERVE, + PAUSED_RESERVE } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 8038043e..24915aa5 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -41,10 +41,11 @@ library ValidationLogic { * @param amount The amount to be deposited */ function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { - (bool isActive, bool isFrozen, , , ) = reserve.configuration.getFlags(); + (bool isActive, bool isFrozen, , , bool isPaused) = reserve.configuration.getFlags(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( IERC20(reserve.aTokenAddress) @@ -70,8 +71,9 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , ) = reservesData[reserveAddress].configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reservesData[reserveAddress].configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); } struct ValidateBorrowLocalVars { @@ -233,10 +235,10 @@ library ValidationLogic { address onBehalfOf, uint256 stableDebt, uint256 variableDebt - ) internal view { - bool isActive = reserve.configuration.getActive(); - + ) external view { + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(amountSent > 0, Errors.VL_INVALID_AMOUNT); @@ -348,6 +350,9 @@ library ValidationLogic { DataTypes.ReserveData storage reserve ) external view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); + bool isPaused = reserve.configuration.getPaused(); + + require(!isPaused, Errors.VL_RESERVE_PAUSED); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); } @@ -357,7 +362,17 @@ library ValidationLogic { * @param assets The assets being flashborrowed * @param amounts The amounts for each asset being borrowed **/ - function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure { + function validateFlashloan( + address[] memory assets, + uint256[] memory amounts, + mapping(address => DataTypes.ReserveData) storage reservesData + ) internal view { + for (uint i = 0; i < assets.length; i++) { + require( + !reservesData[assets[i]].configuration.getPaused(), + Errors.VL_RESERVE_PAUSED + ); + } require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } @@ -386,6 +401,14 @@ library ValidationLogic { Errors.VL_NO_ACTIVE_RESERVE ); } + if ( + collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused() + ) { + return ( + uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), + Errors.VL_RESERVE_PAUSED + ); + } if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { return ( @@ -426,6 +449,7 @@ library ValidationLogic { * @param oracle The price oracle */ function validateHealthFactor( + address reserveAddress, address from, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, @@ -433,6 +457,8 @@ library ValidationLogic { uint256 reservesCount, address oracle ) internal view { + bool isPaused = reservesData[reserveAddress].configuration.getPaused(); + require(!isPaused, Errors.VL_RESERVE_PAUSED); (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData( from, From 1c69499f4505a3107071405998a150087e4377ca Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 18 May 2021 09:40:08 +0200 Subject: [PATCH 09/64] fix: named properly pause test --- test-suites/test-aave/reserve-pause.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-suites/test-aave/reserve-pause.spec.ts b/test-suites/test-aave/reserve-pause.spec.ts index f6a57d0c..3464a20a 100644 --- a/test-suites/test-aave/reserve-pause.spec.ts +++ b/test-suites/test-aave/reserve-pause.spec.ts @@ -9,7 +9,7 @@ import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; const { expect } = require('chai'); -makeSuite('Pausable Pool', (testEnv: TestEnv) => { +makeSuite('Pause Reserve', (testEnv: TestEnv) => { let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; const { From 2f388c1b49cedcf0cccfbcfdcd851a1a5a583bc9 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 18 May 2021 09:40:39 +0200 Subject: [PATCH 10/64] fix: updated matic reserve config --- markets/matic/reservesConfigs.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts index 2fd924f1..babb5b17 100644 --- a/markets/matic/reservesConfigs.ts +++ b/markets/matic/reservesConfigs.ts @@ -91,6 +91,8 @@ export const strategyMATIC: IReserveParams = { reserveDecimals: '18', aTokenImpl: eContractid.AToken, reserveFactor: '2000', + borrowCap: MAX_BORROW_CAP, + supplyCap: MAX_SUPPLY_CAP, }; export const strategyAAVE: IReserveParams = { From e18bd375cca6b0e4f1bc8a115022c215cfa5a458 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 18 May 2021 09:51:38 +0200 Subject: [PATCH 11/64] refactor: refactor validation logic after merging validateHealthFactor --- .../protocol/lendingpool/LendingPool.sol | 3 +- .../libraries/logic/ValidationLogic.sol | 78 +++++++++---------- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index ad7f8948..d3ef2c26 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -720,6 +720,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); + ValidationLogic.validateTransfer(_reserves[asset]); + uint256 reserveId = _reserves[asset].id; if (from != to) { @@ -728,7 +730,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (fromConfig.isUsingAsCollateral(reserveId)) { if (fromConfig.isBorrowingAny()) { ValidationLogic.validateHealthFactor( - asset, from, _reserves, _usersConfig[from], diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 24915aa5..e356a0d4 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -48,15 +48,13 @@ library ValidationLogic { require(!isPaused, Errors.VL_RESERVE_PAUSED); require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( - IERC20(reserve.aTokenAddress) - .totalSupply() - .add(amount) - .div(10 ** reserve.configuration.getDecimals()) < reserve.configuration.getSupplyCap(), + IERC20(reserve.aTokenAddress).totalSupply().add(amount).div( + 10**reserve.configuration.getDecimals() + ) < reserve.configuration.getSupplyCap(), Errors.VL_SUPPLY_CAP_EXCEEDED ); } - /** * @dev Validates a withdraw action * @param reserve The reserve object @@ -71,7 +69,7 @@ library ValidationLogic { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , bool isPaused) = reservesData[reserveAddress].configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); } @@ -124,9 +122,13 @@ library ValidationLogic { ) internal view { ValidateBorrowLocalVars memory vars; - (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled, vars.isPaused) = reserve - .configuration - .getFlags(); + ( + vars.isActive, + vars.isFrozen, + vars.borrowingEnabled, + vars.stableRateBorrowingEnabled, + vars.isPaused + ) = reserve.configuration.getFlags(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); @@ -141,22 +143,20 @@ library ValidationLogic { uint256(DataTypes.InterestRateMode.STABLE) == interestRateMode, Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED ); - + (vars.totalSupplyStableDebt, ) = IStableDebtToken(reserve.stableDebtTokenAddress) .getTotalSupplyAndAvgRate(); vars.totalSupplyVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) - .scaledTotalSupply() - .rayMul(reserve.variableBorrowIndex); - - + .scaledTotalSupply() + .rayMul(reserve.variableBorrowIndex); + require( - vars.totalSupplyStableDebt - .add(vars.totalSupplyVariableDebt) - .add(amount) - .div(10 ** reserve.configuration.getDecimals()) - < reserve.configuration.getBorrowCap(), - Errors.VL_BORROW_CAP_EXCEEDED); + vars.totalSupplyStableDebt.add(vars.totalSupplyVariableDebt).add(amount).div( + 10**reserve.configuration.getDecimals() + ) < reserve.configuration.getBorrowCap(), + Errors.VL_BORROW_CAP_EXCEEDED + ); ( vars.userCollateralBalanceETH, @@ -271,7 +271,8 @@ library ValidationLogic { uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) external view { - (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = + reserve.configuration.getFlags(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -346,9 +347,7 @@ library ValidationLogic { * @dev Validates the action of setting an asset as collateral * @param reserve The state of the reserve that the user is enabling or disabling as collateral */ - function validateSetUseReserveAsCollateral( - DataTypes.ReserveData storage reserve - ) external view { + function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); bool isPaused = reserve.configuration.getPaused(); @@ -363,16 +362,13 @@ library ValidationLogic { * @param amounts The amounts for each asset being borrowed **/ function validateFlashloan( - address[] memory assets, + address[] memory assets, uint256[] memory amounts, mapping(address => DataTypes.ReserveData) storage reservesData ) internal view { - for (uint i = 0; i < assets.length; i++) { - require( - !reservesData[assets[i]].configuration.getPaused(), - Errors.VL_RESERVE_PAUSED - ); - } + for (uint256 i = 0; i < assets.length; i++) { + require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED); + } require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } @@ -401,13 +397,8 @@ library ValidationLogic { Errors.VL_NO_ACTIVE_RESERVE ); } - if ( - collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused() - ) { - return ( - uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), - Errors.VL_RESERVE_PAUSED - ); + if (collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused()) { + return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); } if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { @@ -449,7 +440,6 @@ library ValidationLogic { * @param oracle The price oracle */ function validateHealthFactor( - address reserveAddress, address from, mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, @@ -457,8 +447,6 @@ library ValidationLogic { uint256 reservesCount, address oracle ) internal view { - bool isPaused = reservesData[reserveAddress].configuration.getPaused(); - require(!isPaused, Errors.VL_RESERVE_PAUSED); (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData( from, @@ -474,4 +462,12 @@ library ValidationLogic { Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); } + + /** + * @dev Validates a transfer action + * @param reserve The reserve object + */ + function validateTransfer(DataTypes.ReserveData storage reserve) internal view { + require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); + } } From f3418670f833e78683579a775648e2bb008972f3 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 20 May 2021 12:17:47 +0200 Subject: [PATCH 12/64] fix: fixed test with wrong descimals in uniswap liquidity swap --- .../uniswapAdapters.liquiditySwap.spec.ts | 86 +++++++------------ 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts index 42224c52..e9883776 100644 --- a/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts +++ b/test-suites/test-aave/uniswapAdapters.liquiditySwap.spec.ts @@ -187,17 +187,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -240,7 +235,6 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { ], [false, false] ); - await pool .connect(user) .flashLoan( @@ -309,17 +303,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -862,17 +851,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmount = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmount = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); await mockUniswapRouter.connect(user).setAmountToReturn(usdc.address, expectedDaiAmount); @@ -1484,17 +1468,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); @@ -1592,17 +1571,12 @@ makeSuite('Uniswap adapters', (testEnv: TestEnv) => { const collateralDecimals = (await usdc.decimals()).toString(); const principalDecimals = (await dai.decimals()).toString(); - const expectedDaiAmountForUsdc = await convertToCurrencyDecimals( - dai.address, - new BigNumber(amountUSDCtoSwap.toString()) - .times( - new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) - ) - .div( - new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals)) - ) - .toFixed(0) - ); + const expectedDaiAmountForUsdc = new BigNumber(amountUSDCtoSwap.toString()) + .times( + new BigNumber(usdcPrice.toString()).times(new BigNumber(10).pow(principalDecimals)) + ) + .div(new BigNumber(daiPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))) + .toFixed(0); // Make a deposit for user await usdc.connect(user).mint(amountUSDCtoSwap); From 32944733bd9484bd25f14fa19d59a102a7ab7d13 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 01:08:02 +0200 Subject: [PATCH 13/64] feat: added risk admins --- .../interfaces/ILendingPoolConfigurator.sol | 10 +++ .../lendingpool/LendingPoolConfigurator.sol | 74 ++++++++++++------- .../protocol/libraries/helpers/Errors.sol | 1 + 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index fd3bcb19..8d01820c 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -202,4 +202,14 @@ interface ILendingPoolConfigurator { address indexed proxy, address indexed implementation ); + + event RiskAdminRegistered(address indexed admin); + + event RiskAdminUnregistered(address indexed admin); + + function registerRiskAdmin(address admin) external; + + function unregisterRiskAdmin(address admin) external; + + function isRiskAdmin(address admin) external view returns (bool); } diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index f38432f8..b67f966f 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -33,6 +33,8 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ILendingPoolAddressesProvider internal addressesProvider; ILendingPool internal pool; + mapping(address => bool) private _riskAdmins; + modifier onlyPoolAdmin { require(addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); _; @@ -48,13 +50,20 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur modifier onlyEmergencyOrPoolAdmin { require( - addressesProvider.getEmergencyAdmin() == msg.sender - || addressesProvider.getPoolAdmin() == msg.sender, + addressesProvider.getEmergencyAdmin() == msg.sender || + addressesProvider.getPoolAdmin() == msg.sender, Errors.LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN ); _; } + modifier onlyRiskOrPoolAdmins { + require( + _riskAdmins[msg.sender] || addressesProvider.getPoolAdmin() == msg.sender, + Errors.LPC_CALLER_NOT_RISK_OR_POOL_ADMIN + ); + _; + } uint256 internal constant CONFIGURATOR_REVISION = 0x1; @@ -162,7 +171,8 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableAToken.initialize.selector, cachedPool, input.treasury, @@ -174,11 +184,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur input.params ); - _upgradeTokenImplementation( - reserveData.aTokenAddress, - input.implementation, - encodedCall - ); + _upgradeTokenImplementation(reserveData.aTokenAddress, input.implementation, encodedCall); emit ATokenUpgraded(input.asset, reserveData.aTokenAddress, input.implementation); } @@ -190,10 +196,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ILendingPool cachedPool = pool; DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); - + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, cachedPool, input.asset, @@ -220,17 +227,15 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur /** * @dev Updates the variable debt token implementation for the asset **/ - function updateVariableDebtToken(UpdateDebtTokenInput calldata input) - external - onlyPoolAdmin - { + function updateVariableDebtToken(UpdateDebtTokenInput calldata input) external onlyPoolAdmin { ILendingPool cachedPool = pool; DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); - bytes memory encodedCall = abi.encodeWithSelector( + bytes memory encodedCall = + abi.encodeWithSelector( IInitializableDebtToken.initialize.selector, cachedPool, input.asset, @@ -259,10 +264,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @param asset The address of the underlying asset of the reserve * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve **/ - function enableBorrowingOnReserve(address asset, uint256 borrowCap, bool stableBorrowRateEnabled) - external - onlyPoolAdmin - { + function enableBorrowingOnReserve( + address asset, + uint256 borrowCap, + bool stableBorrowRateEnabled + ) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(true); @@ -278,7 +284,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @dev Disables borrowing on a reserve * @param asset The address of the underlying asset of the reserve **/ - function disableBorrowingOnReserve(address asset) external onlyPoolAdmin { + function disableBorrowingOnReserve(address asset) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(false); @@ -301,7 +307,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus - ) external onlyPoolAdmin { + ) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); //validation of the parameters: the LTV can @@ -344,7 +350,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @dev Enable stable rate borrowing on a reserve * @param asset The address of the underlying asset of the reserve **/ - function enableReserveStableRate(address asset) external onlyPoolAdmin { + function enableReserveStableRate(address asset) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setStableRateBorrowingEnabled(true); @@ -358,7 +364,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @dev Disable stable rate borrowing on a reserve * @param asset The address of the underlying asset of the reserve **/ - function disableReserveStableRate(address asset) external onlyPoolAdmin { + function disableReserveStableRate(address asset) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setStableRateBorrowingEnabled(false); @@ -460,7 +466,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @param asset The address of the underlying asset of the reserve * @param reserveFactor The new reserve factor of the reserve **/ - function setReserveFactor(address asset, uint256 reserveFactor) external onlyPoolAdmin { + function setReserveFactor(address asset, uint256 reserveFactor) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setReserveFactor(reserveFactor); @@ -475,7 +481,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @param asset The address of the underlying asset of the reserve * @param borrowCap The new borrow of the reserve **/ - function setBorrowCap(address asset, uint256 borrowCap) external onlyPoolAdmin { + function setBorrowCap(address asset, uint256 borrowCap) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setBorrowCap(borrowCap); @@ -490,7 +496,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur * @param asset The address of the underlying asset of the reserve * @param supplyCap The new supply of the reserve **/ - function setSupplyCap(address asset, uint256 supplyCap) external onlyPoolAdmin { + function setSupplyCap(address asset, uint256 supplyCap) external onlyRiskOrPoolAdmins { DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); currentConfig.setSupplyCap(supplyCap); @@ -507,7 +513,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur **/ function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) external - onlyPoolAdmin + onlyRiskOrPoolAdmins { pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress); emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress); @@ -521,6 +527,20 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur pool.setPause(val); } + function registerRiskAdmin(address admin) external override onlyPoolAdmin { + _riskAdmins[admin] = true; + emit RiskAdminRegistered(admin); + } + + function unregisterRiskAdmin(address admin) external override onlyPoolAdmin { + _riskAdmins[admin] = false; + emit RiskAdminUnregistered(admin); + } + + function isRiskAdmin(address admin) external view override onlyPoolAdmin returns (bool) { + return _riskAdmins[admin]; + } + function _initTokenWithProxy(address implementation, bytes memory initParams) internal returns (address) diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 2b6f015a..8a2e7653 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -108,6 +108,7 @@ library Errors { string public constant RC_INVALID_SUPPLY_CAP = '84'; string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; string public constant VL_RESERVE_PAUSED = '86'; + string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; enum CollateralManagerErrors { NO_ERROR, From c1d05a13f21b004e7d15666510edc8208076e442 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 02:19:30 +0200 Subject: [PATCH 14/64] test: added test for risk admin and updated configurator tests --- helpers/types.ts | 1 + test-suites/test-aave/__setup.spec.ts | 4 +- test-suites/test-aave/configurator.spec.ts | 411 +++++++++++++++++--- test-suites/test-aave/helpers/make-suite.ts | 9 + 4 files changed, 365 insertions(+), 60 deletions(-) diff --git a/helpers/types.ts b/helpers/types.ts index 45f7ddcf..0e0ac375 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -183,6 +183,7 @@ export enum ProtocolErrors { RC_INVALID_SUPPLY_CAP = '84', LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', VL_RESERVE_PAUSED = '86', + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87', // old diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index 54110d6e..f8ff2fbe 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -102,7 +102,8 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { await waitForTx(await addressesProvider.setPoolAdmin(aaveAdmin)); //setting users[1] as emergency admin, which is in position 2 in the DRE addresses list - const addressList = await Promise.all( + // .. users[2] as risk admin .. position 3 + const addressList: string[] = await Promise.all( (await DRE.ethers.getSigners()).map((signer) => signer.getAddress()) ); @@ -129,6 +130,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { const lendingPoolConfiguratorProxy = await getLendingPoolConfiguratorProxy( await addressesProvider.getLendingPoolConfigurator() ); + await waitForTx(await lendingPoolConfiguratorProxy.registerRiskAdmin(addressList[3])); await insertContractAddressInDb( eContractid.LendingPoolConfigurator, lendingPoolConfiguratorProxy.address diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index f05d5a5b..f24c2f95 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -24,6 +24,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { RC_INVALID_BORROW_CAP, RC_INVALID_SUPPLY_CAP, LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN, + LPC_CALLER_NOT_RISK_OR_POOL_ADMIN, VL_RESERVE_PAUSED, } = ProtocolErrors; @@ -134,10 +135,15 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Pauses the ETH reserve by emergency admin', async () => { - const { configurator, weth, helpersContract, addressesProvider, users } = testEnv; - expect(users[1].address).to.be.equal(await addressesProvider.getEmergencyAdmin()); - - await configurator.connect(users[1].signer).pauseReserve(weth.address); + const { + configurator, + weth, + helpersContract, + addressesProvider, + users, + emergencyAdmin, + } = testEnv; + await configurator.connect(emergencyAdmin.signer).pauseReserve(weth.address); const { decimals, ltv, @@ -167,8 +173,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { }); it('Unpauses the ETH reserve by emergency admin ', async () => { - const { configurator, helpersContract, weth, users } = testEnv; - await configurator.connect(users[1].signer).unpauseReserve(weth.address); + const { configurator, helpersContract, weth, users, emergencyAdmin } = testEnv; + await configurator.connect(emergencyAdmin.signer).unpauseReserve(weth.address); const { decimals, @@ -199,17 +205,17 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { }); it('Check the only admin or emergency admin can pauseReserve ', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).pauseReserve(weth.address), + configurator.connect(riskAdmin.signer).pauseReserve(weth.address), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); }); it('Check the only admin or emergency admin can unpauseReserve ', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).unpauseReserve(weth.address), + configurator.connect(riskAdmin.signer).unpauseReserve(weth.address), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); }); @@ -279,22 +285,22 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { }); it('Check the onlyAaveAdmin on freezeReserve ', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).freezeReserve(weth.address), + configurator.connect(riskAdmin.signer).freezeReserve(weth.address), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); it('Check the onlyAaveAdmin on unfreezeReserve ', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, riskAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).unfreezeReserve(weth.address), + configurator.connect(riskAdmin.signer).unfreezeReserve(weth.address), CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); - it('Deactivates the ETH reserve for borrowing', async () => { + it('Deactivates the ETH reserve for borrowing via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.disableBorrowingOnReserve(weth.address); const { @@ -325,7 +331,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Activates the ETH reserve for borrowing', async () => { + it('Activates the ETH reserve for borrowing via pool admin', async () => { const { configurator, weth, helpersContract } = testEnv; await configurator.enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true); const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); @@ -360,25 +366,94 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(variableBorrowIndex.toString()).to.be.equal(RAY); }); - it('Check the onlyAaveAdmin on disableBorrowingOnReserve ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator.connect(users[2].signer).disableBorrowingOnReserve(weth.address), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + it('Deactivates the ETH reserve for borrowing via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).disableBorrowingOnReserve(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(false); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Check the onlyAaveAdmin on enableBorrowingOnReserve ', async () => { - const { configurator, users, weth } = testEnv; + it('Activates the ETH reserve for borrowing via risk admin', async () => { + const { configurator, weth, helpersContract, riskAdmin } = testEnv; + await configurator + .connect(riskAdmin.signer) + .enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true); + const { variableBorrowIndex } = await helpersContract.getReserveData(weth.address); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + + expect(variableBorrowIndex.toString()).to.be.equal(RAY); + }); + + it('Check the onlyAaveAdmin or Risk admin on disableBorrowingOnReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + + await expect( + configurator.connect(emergencyAdmin.signer).disableBorrowingOnReserve(weth.address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Check the onlyAaveAdmin or Risk admin on enableBorrowingOnReserve ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( configurator - .connect(users[2].signer) + .connect(emergencyAdmin.signer) .enableBorrowingOnReserve(weth.address, MAX_BORROW_CAP, true), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Deactivates the ETH reserve as collateral', async () => { + it('Deactivates the ETH reserve as collateral via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.configureReserveAsCollateral(weth.address, 0, 0, 0); @@ -410,7 +485,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Activates the ETH reserve as collateral', async () => { + it('Activates the ETH reserve as collateral via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.configureReserveAsCollateral(weth.address, '8000', '8250', '10500'); @@ -441,18 +516,85 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal(strategyWETH.borrowCap); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Deactivates the ETH reserve as collateral via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator + .connect(riskAdmin.signer) + .configureReserveAsCollateral(weth.address, 0, 0, 0); - it('Check the onlyAaveAdmin on configureReserveAsCollateral ', async () => { - const { configurator, users, weth } = testEnv; - await expect( - configurator - .connect(users[2].signer) - .configureReserveAsCollateral(weth.address, '7500', '8000', '10500'), - CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(18); + expect(ltv).to.be.equal(0); + expect(liquidationThreshold).to.be.equal(0); + expect(liquidationBonus).to.be.equal(0); + expect(stableBorrowRateEnabled).to.be.equal(true); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Disable stable borrow rate on the ETH reserve', async () => { + it('Activates the ETH reserve as collateral via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator + .connect(riskAdmin.signer) + .configureReserveAsCollateral(weth.address, '8000', '8250', '10500'); + + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Check the onlyRiskOrPoolAdmin on configureReserveAsCollateral ', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; + await expect( + configurator + .connect(emergencyAdmin.signer) + .configureReserveAsCollateral(weth.address, '7500', '8000', '10500'), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); + }); + + it('Disable stable borrow rate on the ETH reserve via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.disableReserveStableRate(weth.address); const { @@ -483,7 +625,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Enables stable borrow rate on the ETH reserve', async () => { + it('Enables stable borrow rate on the ETH reserve via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.enableReserveStableRate(weth.address); const { @@ -513,47 +655,108 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal(strategyWETH.borrowCap); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Disable stable borrow rate on the ETH reserve risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).disableReserveStableRate(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(false); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Enables stable borrow rate on the ETH reserve risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).enableReserveStableRate(weth.address); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(true); + expect(reserveFactor).to.be.equal(strategyWETH.reserveFactor); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); it('Check the onlyAaveAdmin on disableReserveStableRate', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).disableReserveStableRate(weth.address), + configurator.connect(emergencyAdmin.signer).disableReserveStableRate(weth.address), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); it('Check the onlyAaveAdmin on enableReserveStableRate', async () => { - const { configurator, users, weth } = testEnv; + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).enableReserveStableRate(weth.address), + configurator.connect(emergencyAdmin.signer).enableReserveStableRate(weth.address), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Check the onlyAaveAdmin on setReserveFactor', async () => { - const { configurator, users, weth } = testEnv; + it('Check the onlyRiskOrPoolAdmin on setReserveFactor', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).setReserveFactor(weth.address, '1000'), + configurator.connect(emergencyAdmin.signer).setReserveFactor(weth.address, '1000'), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Check the onlyAaveAdmin on setBorrowCap', async () => { - const { configurator, users, weth } = testEnv; + it('Check the onlyRiskOrPoolAdmin on setBorrowCap', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).setBorrowCap(weth.address, '3000000000'), + configurator.connect(emergencyAdmin.signer).setBorrowCap(weth.address, '3000000000'), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Check the onlyAaveAdmin on setSupplyCap', async () => { - const { configurator, users, weth } = testEnv; + it('Check the onlyRiskOrPoolAdmin on setSupplyCap', async () => { + const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( - configurator.connect(users[2].signer).setSupplyCap(weth.address, '3000000000'), + configurator.connect(emergencyAdmin.signer).setSupplyCap(weth.address, '3000000000'), CALLER_NOT_POOL_ADMIN - ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Changes the reserve factor of WETH', async () => { + it('Changes the reserve factor of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setReserveFactor(weth.address, '1000'); const { @@ -583,6 +786,36 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); expect(reserveFactor).to.be.equal(1000); }); + it('Changes the reserve factor of WETH risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).setReserveFactor(weth.address, '1000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(borrowCap).to.be.equal(strategyWETH.borrowCap); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + expect(reserveFactor).to.be.equal(1000); + }); it('Fails to change to too high borrowCap', async () => { const { configurator, users, weth } = testEnv; @@ -599,7 +832,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(RC_INVALID_SUPPLY_CAP); }); - it('Changes the borrow Cap of WETH', async () => { + it('Changes the borrow Cap of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setBorrowCap(weth.address, '3000000'); const { @@ -629,8 +862,38 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal('3000000'); expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); + it('Changes the borrow Cap of WETH risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).setBorrowCap(weth.address, '3000000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); - it('Changes the borrow Cap of WETH', async () => { + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(1000); + expect(borrowCap).to.be.equal('3000000'); + expect(supplyCap).to.be.equal(strategyWETH.supplyCap); + }); + + it('Changes the supply Cap of WETH via pool admin', async () => { const { configurator, helpersContract, weth } = testEnv; await configurator.setSupplyCap(weth.address, '3000000'); const { @@ -660,6 +923,36 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(borrowCap).to.be.equal('3000000'); expect(supplyCap).to.be.equal('3000000'); }); + it('Changes the supply Cap of WETH via risk admin', async () => { + const { configurator, helpersContract, weth, riskAdmin } = testEnv; + await configurator.connect(riskAdmin.signer).setSupplyCap(weth.address, '3000000'); + const { + decimals, + ltv, + liquidationBonus, + liquidationThreshold, + reserveFactor, + stableBorrowRateEnabled, + borrowingEnabled, + isActive, + isFrozen, + } = await helpersContract.getReserveConfigurationData(weth.address); + const { borrowCap, supplyCap } = await helpersContract.getReserveCaps(weth.address); + const isPaused = await helpersContract.getPaused(weth.address); + + expect(borrowingEnabled).to.be.equal(true); + expect(isActive).to.be.equal(true); + expect(isPaused).to.be.equal(false); + expect(isFrozen).to.be.equal(false); + expect(decimals).to.be.equal(strategyWETH.reserveDecimals); + expect(ltv).to.be.equal(strategyWETH.baseLTVAsCollateral); + expect(liquidationThreshold).to.be.equal(strategyWETH.liquidationThreshold); + expect(liquidationBonus).to.be.equal(strategyWETH.liquidationBonus); + expect(stableBorrowRateEnabled).to.be.equal(strategyWETH.stableBorrowRateEnabled); + expect(reserveFactor).to.be.equal(1000); + expect(borrowCap).to.be.equal('3000000'); + expect(supplyCap).to.be.equal('3000000'); + }); it('Reverts when trying to disable the DAI reserve with liquidity on it', async () => { const { dai, pool, configurator } = testEnv; diff --git a/test-suites/test-aave/helpers/make-suite.ts b/test-suites/test-aave/helpers/make-suite.ts index e503fe7a..400cb6f6 100644 --- a/test-suites/test-aave/helpers/make-suite.ts +++ b/test-suites/test-aave/helpers/make-suite.ts @@ -51,6 +51,9 @@ export interface SignerWithAddress { } export interface TestEnv { deployer: SignerWithAddress; + poolAdmin: SignerWithAddress; + emergencyAdmin: SignerWithAddress; + riskAdmin: SignerWithAddress; users: SignerWithAddress[]; pool: LendingPool; configurator: LendingPoolConfigurator; @@ -77,6 +80,9 @@ const setBuidlerevmSnapshotId = (id: string) => { const testEnv: TestEnv = { deployer: {} as SignerWithAddress, + poolAdmin: {} as SignerWithAddress, + emergencyAdmin: {} as SignerWithAddress, + riskAdmin: {} as SignerWithAddress, users: [] as SignerWithAddress[], pool: {} as LendingPool, configurator: {} as LendingPoolConfigurator, @@ -110,6 +116,9 @@ export async function initializeMakeSuite() { }); } testEnv.deployer = deployer; + testEnv.poolAdmin = deployer; + testEnv.emergencyAdmin = testEnv.users[1]; + testEnv.riskAdmin = testEnv.users[2]; testEnv.pool = await getLendingPool(); testEnv.configurator = await getLendingPoolConfiguratorProxy(); From 2341eae52ab665fd6b838d801cc667dec5559d00 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 09:32:03 +0200 Subject: [PATCH 15/64] test: added test for registering/ unregistering riskAdmins --- test-suites/test-aave/configurator.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index f24c2f95..7862f530 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -971,4 +971,23 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { LPC_RESERVE_LIQUIDITY_NOT_0 ).to.be.revertedWith(LPC_RESERVE_LIQUIDITY_NOT_0); }); + it('Register a new risk Admin', async () => { + const { dai, pool, configurator, users, riskAdmin } = testEnv; + await configurator.registerRiskAdmin(users[3].address); + + const isRiskAdminRegistered = await configurator.isRiskAdmin(riskAdmin.address); + const isNewRegistered = await configurator.isRiskAdmin(users[3].address); + expect(isNewRegistered).to.be.true; + expect(isRiskAdminRegistered).to.be.true; + }); + it('Unregister a risk Admins', async () => { + const { dai, pool, configurator, users, riskAdmin } = testEnv; + await configurator.unregisterRiskAdmin(users[3].address); + await configurator.unregisterRiskAdmin(riskAdmin.address); + + const isRiskAdminRegistered = await configurator.isRiskAdmin(riskAdmin.address); + const isNewRegistered = await configurator.isRiskAdmin(users[3].address); + expect(isNewRegistered).to.be.false; + expect(isRiskAdminRegistered).to.be.false; + }); }); From ec77fc3db48f8361d27e3c093729d5c601c56ae9 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 09:38:55 +0200 Subject: [PATCH 16/64] doc: updated test naming --- test-suites/test-aave/configurator.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index 7862f530..b484a2e1 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -717,7 +717,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); - it('Check the onlyAaveAdmin on disableReserveStableRate', async () => { + it('Check the onlyRiskOrPoolAdmin on disableReserveStableRate', async () => { const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( configurator.connect(emergencyAdmin.signer).disableReserveStableRate(weth.address), @@ -725,7 +725,7 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { ).to.be.revertedWith(LPC_CALLER_NOT_RISK_OR_POOL_ADMIN); }); - it('Check the onlyAaveAdmin on enableReserveStableRate', async () => { + it('Check the onlyRiskOrPoolAdmin on enableReserveStableRate', async () => { const { configurator, users, weth, emergencyAdmin } = testEnv; await expect( configurator.connect(emergencyAdmin.signer).enableReserveStableRate(weth.address), From ccad06fc948dbf4129a7b67e01cf229793f8304a Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 13:08:18 +0200 Subject: [PATCH 17/64] feat: added authorized flashloaners --- contracts/interfaces/ILendingPool.sol | 6 ++++++ .../interfaces/ILendingPoolConfigurator.sol | 8 +++++++ .../protocol/lendingpool/LendingPool.sol | 21 +++++++++++++++++-- .../lendingpool/LendingPoolConfigurator.sol | 10 +++++++++ .../lendingpool/LendingPoolStorage.sol | 2 ++ 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index ab012fb8..8ecbd40a 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -458,4 +458,10 @@ interface ILendingPool { function setPause(bool val) external; function paused() external view returns (bool); + + function authorizeFlashloaner(address flashloaner) external; + + function unauthorizeFlashloaner(address flashloaner) external; + + function isFlashloanerAuthorized(address flashloaner) external view returns (bool); } diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 8d01820c..40f30aee 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -207,9 +207,17 @@ interface ILendingPoolConfigurator { event RiskAdminUnregistered(address indexed admin); + event FlashloanerAuthorized(address indexed flashloaner); + + event FlashloanerUnauthorized(address indexed flashloaner); + function registerRiskAdmin(address admin) external; function unregisterRiskAdmin(address admin) external; + function authorizeFlashloaner(address flashloaner) external; + + function unauthorizeFlashloaner(address flashloaner) external; + function isRiskAdmin(address admin) external view returns (bool); } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index d3ef2c26..3b28776d 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -435,6 +435,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint256 currentPremium; uint256 currentAmountPlusPremium; address debtToken; + uint256 flashloanPremiumTotal; } /** @@ -469,13 +470,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address[] memory aTokenAddresses = new address[](assets.length); uint256[] memory premiums = new uint256[](assets.length); - vars.receiver = IFlashLoanReceiver(receiverAddress); + vars.flashloanPremiumTotal = _authorizedFlashloaners[msg.sender] ? 0 : _flashLoanPremiumTotal; for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); + premiums[vars.i] = amounts[vars.i].mul(vars.flashloanPremiumTotal).div(10000); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } @@ -821,6 +822,22 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } } + function authorizeFlashloaner(address flashloaner) external override onlyLendingPoolConfigurator { + _authorizedFlashloaners[flashloaner] = true; + } + + function unauthorizeFlashloaner(address flashloaner) + external + override + onlyLendingPoolConfigurator + { + _authorizedFlashloaners[flashloaner] = false; + } + + function isFlashloanerAuthorized(address flashloaner) external view override returns (bool) { + return _authorizedFlashloaners[flashloaner]; + } + struct ExecuteBorrowParams { address asset; address user; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index b67f966f..6fe55fc8 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -537,6 +537,16 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit RiskAdminUnregistered(admin); } + function authorizeFlashloaner(address flashloaner) external override onlyPoolAdmin { + pool.authorizeFlashloaner(flashloaner); + emit FlashloanerAuthorized(flashloaner); + } + + function unauthorizeFlashloaner(address flashloaner) external override onlyPoolAdmin { + pool.unauthorizeFlashloaner(flashloaner); + emit FlashloanerUnauthorized(flashloaner); + } + function isRiskAdmin(address admin) external view override onlyPoolAdmin returns (bool) { return _riskAdmins[admin]; } diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index 198a3eea..8dcf1d9d 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -29,4 +29,6 @@ contract LendingPoolStorage { uint256 internal _flashLoanPremiumTotal; uint256 internal _maxNumberOfReserves; + + mapping(address => bool) _authorizedFlashloaners; } From 5a1e87ae0999bb7cd06103e488e75993f9766f32 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 12 May 2021 13:45:40 +0200 Subject: [PATCH 18/64] rename: renamed flashloaner => flashBorrower --- contracts/interfaces/ILendingPool.sol | 6 ++--- .../interfaces/ILendingPoolConfigurator.sol | 8 +++---- .../protocol/lendingpool/LendingPool.sol | 22 +++++++++++-------- .../lendingpool/LendingPoolConfigurator.sol | 12 +++++----- .../lendingpool/LendingPoolStorage.sol | 2 +- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 8ecbd40a..c5801065 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -459,9 +459,9 @@ interface ILendingPool { function paused() external view returns (bool); - function authorizeFlashloaner(address flashloaner) external; + function authorizeFlashBorrower(address flashBorrower) external; - function unauthorizeFlashloaner(address flashloaner) external; + function unauthorizeFlashBorrower(address flashBorrower) external; - function isFlashloanerAuthorized(address flashloaner) external view returns (bool); + function isFlashBorrowerAuthorized(address flashBorrower) external view returns (bool); } diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 40f30aee..26de950f 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -207,17 +207,17 @@ interface ILendingPoolConfigurator { event RiskAdminUnregistered(address indexed admin); - event FlashloanerAuthorized(address indexed flashloaner); + event FlashBorrowerAuthorized(address indexed flashBorrower); - event FlashloanerUnauthorized(address indexed flashloaner); + event FlashBorrowerUnauthorized(address indexed flashBorrower); function registerRiskAdmin(address admin) external; function unregisterRiskAdmin(address admin) external; - function authorizeFlashloaner(address flashloaner) external; + function authorizeFlashBorrower(address flashBorrower) external; - function unauthorizeFlashloaner(address flashloaner) external; + function unauthorizeFlashBorrower(address flashBorrower) external; function isRiskAdmin(address admin) external view returns (bool); } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 3b28776d..663c3efa 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -471,7 +471,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address[] memory aTokenAddresses = new address[](assets.length); uint256[] memory premiums = new uint256[](assets.length); vars.receiver = IFlashLoanReceiver(receiverAddress); - vars.flashloanPremiumTotal = _authorizedFlashloaners[msg.sender] ? 0 : _flashLoanPremiumTotal; + vars.flashloanPremiumTotal = _authorizedFlashBorrowers[msg.sender] ? 0 : _flashLoanPremiumTotal; for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; @@ -822,20 +822,24 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } } - function authorizeFlashloaner(address flashloaner) external override onlyLendingPoolConfigurator { - _authorizedFlashloaners[flashloaner] = true; - } - - function unauthorizeFlashloaner(address flashloaner) + function authorizeFlashBorrower(address flashBorrower) external override onlyLendingPoolConfigurator { - _authorizedFlashloaners[flashloaner] = false; + _authorizedFlashBorrowers[flashBorrower] = true; } - function isFlashloanerAuthorized(address flashloaner) external view override returns (bool) { - return _authorizedFlashloaners[flashloaner]; + function unauthorizeFlashBorrower(address flashBorrower) + external + override + onlyLendingPoolConfigurator + { + _authorizedFlashBorrowers[flashBorrower] = false; + } + + function isFlashBorrowerAuthorized(address flashBorrower) external view override returns (bool) { + return _authorizedFlashBorrowers[flashBorrower]; } struct ExecuteBorrowParams { diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 6fe55fc8..80ca2410 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -537,14 +537,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit RiskAdminUnregistered(admin); } - function authorizeFlashloaner(address flashloaner) external override onlyPoolAdmin { - pool.authorizeFlashloaner(flashloaner); - emit FlashloanerAuthorized(flashloaner); + function authorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { + pool.authorizeFlashBorrower(flashBorrower); + emit FlashBorrowerAuthorized(flashBorrower); } - function unauthorizeFlashloaner(address flashloaner) external override onlyPoolAdmin { - pool.unauthorizeFlashloaner(flashloaner); - emit FlashloanerUnauthorized(flashloaner); + function unauthorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { + pool.unauthorizeFlashBorrower(flashBorrower); + emit FlashBorrowerUnauthorized(flashBorrower); } function isRiskAdmin(address admin) external view override onlyPoolAdmin returns (bool) { diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index 8dcf1d9d..4f37e658 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -30,5 +30,5 @@ contract LendingPoolStorage { uint256 internal _maxNumberOfReserves; - mapping(address => bool) _authorizedFlashloaners; + mapping(address => bool) _authorizedFlashBorrowers; } From 90eef1ffc6f073aaa90c009b6daaab91f59a65c5 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 13 May 2021 21:04:12 +0200 Subject: [PATCH 19/64] test: added test for authorized flash borrowers --- .../test-aave/authorized-flashloan.spec.ts | 501 ++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 test-suites/test-aave/authorized-flashloan.spec.ts diff --git a/test-suites/test-aave/authorized-flashloan.spec.ts b/test-suites/test-aave/authorized-flashloan.spec.ts new file mode 100644 index 00000000..a66c6276 --- /dev/null +++ b/test-suites/test-aave/authorized-flashloan.spec.ts @@ -0,0 +1,501 @@ +import BigNumber from 'bignumber.js'; + +import { TestEnv, makeSuite } from './helpers/make-suite'; +import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../../helpers/constants'; +import { convertToCurrencyDecimals, getContract } from '../../helpers/contracts-helpers'; +import { ethers } from 'ethers'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { ProtocolErrors, eContractid } from '../../helpers/types'; +import { VariableDebtToken } from '../../types/VariableDebtToken'; +import { StableDebtToken } from '../../types/StableDebtToken'; +import { + getMockFlashLoanReceiver, + getStableDebtToken, + getVariableDebtToken, +} from '../../helpers/contracts-getters'; + +const { expect } = require('chai'); + +makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + const { + VL_COLLATERAL_BALANCE_IS_0, + TRANSFER_AMOUNT_EXCEEDS_BALANCE, + LP_INVALID_FLASHLOAN_MODE, + SAFEERC20_LOWLEVEL_CALL, + LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN, + LP_BORROW_ALLOWANCE_NOT_ENOUGH, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + it('Authorize a flash bororwer', async () => { + const { deployer, pool, weth, configurator } = testEnv; + await configurator.authorizeFlashBorrower(deployer.address); + }); + + it('Deposits WETH into the reserve', async () => { + const { pool, weth } = testEnv; + const userAddress = await pool.signer.getAddress(); + const amountToDeposit = ethers.utils.parseEther('1'); + + await weth.mint(amountToDeposit); + + await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool.deposit(weth.address, amountToDeposit, userAddress, '0'); + }); + + it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => { + const { pool, helpersContract, weth } = testEnv; + + await pool.flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + ethers.utils.parseUnits('10000'); + + const reserveData = await helpersContract.getReserveData(weth.address); + + const currentLiquidityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; + + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidity.toString()).to.be.equal('1000000000000000000'); + expect(currentLiquidityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal('1000000000000000000000000000'); + }); + + it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => { + const { pool, helpersContract, weth } = testEnv; + + const reserveDataBefore = await helpersContract.getReserveData(weth.address); + const txResult = await pool.flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + ['1000000000000000000'], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + const reserveData = await helpersContract.getReserveData(weth.address); + + const currentLiqudityRate = reserveData.liquidityRate; + const currentLiquidityIndex = reserveData.liquidityIndex; + + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidity.toString()).to.be.equal('1000000000000000000'); + expect(currentLiqudityRate.toString()).to.be.equal('0'); + expect(currentLiquidityIndex.toString()).to.be.equal('1000000000000000000000000000'); + }); + + it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + await _mockFlashLoanReceiver.setSimulateEOA(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN); + }); + + it('Takes a WETH flashloan with an invalid mode. (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + await _mockFlashLoanReceiver.setSimulateEOA(false); + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [4], + caller.address, + '0x10', + '0' + ) + ).to.be.reverted; + }); + + it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan with mode = 2, does not return the funds. A variable loan for caller is created', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[1]; + + await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [ethers.utils.parseEther('0.8')], + [2], + caller.address, + '0x10', + '0' + ); + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const callerDebt = await wethDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); + }); + + it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { + const { pool, weth, users } = testEnv; + const caller = users[1]; + + await expect( + pool.connect(caller.signer).flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + ['1000000000000000001'], //slightly higher than the available liquidity + [2], + caller.address, + '0x10', + '0' + ), + TRANSFER_AMOUNT_EXCEEDS_BALANCE + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => { + const { pool, deployer, weth, users } = testEnv; + const caller = users[1]; + + await expect( + pool.flashLoan( + deployer.address, + [weth.address], + ['1000000000000000000'], + [2], + caller.address, + '0x10', + '0' + ) + ).to.be.reverted; + }); + + it('Deposits USDC into the reserve', async () => { + const { usdc, pool } = testEnv; + const userAddress = await pool.signer.getAddress(); + + await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000')); + + await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000'); + + await pool.deposit(usdc.address, amountToDeposit, userAddress, '0'); + }); + + it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => { + const { usdc, pool, helpersContract, deployer: depositor } = testEnv; + + await _mockFlashLoanReceiver.setFailExecutionTransfer(false); + + const reserveDataBefore = await helpersContract.getReserveData(usdc.address); + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await pool.flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + const reserveDataAfter = helpersContract.getReserveData(usdc.address); + + const reserveData = await helpersContract.getReserveData(usdc.address); + const userData = await helpersContract.getUserReserveData(usdc.address, depositor.address); + + const totalLiquidity = reserveData.availableLiquidity + .add(reserveData.totalStableDebt) + .add(reserveData.totalVariableDebt) + .toString(); + const currentLiqudityRate = reserveData.liquidityRate.toString(); + const currentLiquidityIndex = reserveData.liquidityIndex.toString(); + const currentUserBalance = userData.currentATokenBalance.toString(); + + const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000'); + + expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity'); + expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate'); + expect(currentLiquidityIndex).to.be.equal( + new BigNumber('1.00000').multipliedBy(oneRay).toFixed(), + 'Invalid liquidity index' + ); + expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance'); + }); + + it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => { + const { usdc, pool, users } = testEnv; + const caller = users[2]; + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [2], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(VL_COLLATERAL_BALANCE_IS_0); + }); + + it('Caller deposits 5 WETH as collateral, Takes a USDC flashloan with mode = 2, does not return the funds. A loan for caller is created', async () => { + const { usdc, pool, weth, users, helpersContract } = testEnv; + + const caller = users[2]; + + await weth.connect(caller.signer).mint(await convertToCurrencyDecimals(weth.address, '5')); + + await weth.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(weth.address, '5'); + + await pool.connect(caller.signer).deposit(weth.address, amountToDeposit, caller.address, '0'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500'); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [usdc.address], + [flashloanAmount], + [2], + caller.address, + '0x10', + '0' + ); + const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + usdc.address + ); + + const usdcDebtToken = await getVariableDebtToken(variableDebtTokenAddress); + + const callerDebt = await usdcDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('500000000', 'Invalid user debt'); + }); + + it('Caller deposits 1000 DAI as collateral, Takes a WETH flashloan with mode = 0, does not approve the transfer of the funds', async () => { + const { dai, pool, weth, users } = testEnv; + const caller = users[3]; + + await dai.connect(caller.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool.connect(caller.signer).deposit(dai.address, amountToDeposit, caller.address, '0'); + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(false); + await _mockFlashLoanReceiver.setAmountToApprove(flashAmount.div(2)); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [0], + caller.address, + '0x10', + '0' + ) + ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); + }); + + it('Caller takes a WETH flashloan with mode = 1', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[3]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + caller.address, + '0x10', + '0' + ); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + const callerDebt = await wethDebtToken.balanceOf(caller.address); + + expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); + }); + + it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user without allowance', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + // Deposit 1000 dai for onBehalfOf user + await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000')); + + await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000'); + + await pool + .connect(onBehalfOf.signer) + .deposit(dai.address, amountToDeposit, onBehalfOf.address, '0'); + + const flashAmount = ethers.utils.parseEther('0.8'); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await expect( + pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + onBehalfOf.address, + '0x10', + '0' + ) + ).to.be.revertedWith(LP_BORROW_ALLOWANCE_NOT_ENOUGH); + }); + + it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user with allowance. A loan for onBehalfOf is creatd.', async () => { + const { dai, pool, weth, users, helpersContract } = testEnv; + + const caller = users[5]; + const onBehalfOf = users[4]; + + const flashAmount = ethers.utils.parseEther('0.8'); + + const reserveData = await pool.getReserveData(weth.address); + + const stableDebtToken = await getStableDebtToken(reserveData.stableDebtTokenAddress); + + // Deposited for onBehalfOf user already, delegate borrow allowance + await stableDebtToken.connect(onBehalfOf.signer).approveDelegation(caller.address, flashAmount); + + await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + + await pool + .connect(caller.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [weth.address], + [flashAmount], + [1], + onBehalfOf.address, + '0x10', + '0' + ); + + const { stableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( + weth.address + ); + + const wethDebtToken = await getStableDebtToken(stableDebtTokenAddress); + + const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address); + + expect(onBehalfOfDebt.toString()).to.be.equal( + '800000000000000000', + 'Invalid onBehalfOf user debt' + ); + }); +}); From a24a09a2bf94668be93cc9e9eab85e4004118f13 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 13 May 2021 21:13:21 +0200 Subject: [PATCH 20/64] test: configurator: added authorize/unauthorize test and check onlyAdmin missing on risk admin --- test-suites/test-aave/configurator.spec.ts | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index b484a2e1..4c002dfc 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -990,4 +990,62 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(isNewRegistered).to.be.false; expect(isRiskAdminRegistered).to.be.false; }); + it('Checks only pool admin can register/unregister a risk Admins', async () => { + const { dai, pool, configurator, users, riskAdmin, emergencyAdmin } = testEnv; + + await expect( + configurator.connect(riskAdmin.signer).registerRiskAdmin(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(riskAdmin.signer).unregisterRiskAdmin(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(emergencyAdmin.signer).registerRiskAdmin(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + await expect( + configurator.connect(emergencyAdmin.signer).unregisterRiskAdmin(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); + it('Authorized a new flashborrower', async () => { + const { dai, pool, configurator, users, riskAdmin } = testEnv; + await configurator.authorizeFlashBorrower(users[4].address); + + const isFlashBorrowerAuthorized = await pool.isFlashBorrowerAuthorized(users[4].address); + expect(isFlashBorrowerAuthorized).to.be.true; + }); + it('Unauthorized flashborrower', async () => { + const { dai, pool, configurator, users } = testEnv; + await configurator.unauthorizeFlashBorrower(users[4].address); + + const isFlashBorrowerAuthorized = await pool.isFlashBorrowerAuthorized(users[4].address); + expect(isFlashBorrowerAuthorized).to.be.false; + }); + it('Checks only pool admin can authorize/unauthorize a flash borrower', async () => { + const { dai, pool, configurator, users, riskAdmin, emergencyAdmin } = testEnv; + + await expect( + configurator.connect(riskAdmin.signer).authorizeFlashBorrower(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(riskAdmin.signer).authorizeFlashBorrower(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + + await expect( + configurator.connect(emergencyAdmin.signer).unauthorizeFlashBorrower(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + await expect( + configurator.connect(emergencyAdmin.signer).unauthorizeFlashBorrower(users[3].address), + CALLER_NOT_POOL_ADMIN + ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); + }); }); From 335b13e891aea37dc85d5748f5545d246ef4956e Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 13 May 2021 21:29:18 +0200 Subject: [PATCH 21/64] review-fix: mul => percentMul in premium --- contracts/protocol/lendingpool/LendingPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 663c3efa..777b47bf 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -476,7 +476,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].mul(vars.flashloanPremiumTotal).div(10000); + premiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } From 717db22980a118cdf918e6bd43cd450cd82f4c68 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Fri, 21 May 2021 10:33:14 +0200 Subject: [PATCH 22/64] fix: LendingPool/ValidationLogic: validation logic functions from internal to external for bytecodesize requirements --- contracts/protocol/libraries/logic/ValidationLogic.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index e356a0d4..ca22b7ff 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -65,7 +65,7 @@ library ValidationLogic { DataTypes.ReserveData storage reserve, uint256 amount, uint256 userBalance - ) internal view { + ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); @@ -365,7 +365,7 @@ library ValidationLogic { address[] memory assets, uint256[] memory amounts, mapping(address => DataTypes.ReserveData) storage reservesData - ) internal view { + ) external view { for (uint256 i = 0; i < assets.length; i++) { require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED); } From 26a09f2ba8bbf5f26e1aacbeea10b486002b1dc8 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 24 May 2021 19:39:13 +0200 Subject: [PATCH 23/64] refactor: changed _mintToTreasury() function name --- contracts/protocol/libraries/logic/ReserveLogic.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 485ad0d3..2e9850c9 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -123,7 +123,7 @@ library ReserveLogic { lastUpdatedTimestamp ); - _mintToTreasury( + _accrueToTreasury( reserve, scaledVariableDebt, previousVariableBorrowIndex, @@ -271,7 +271,7 @@ library ReserveLogic { * @param newLiquidityIndex The new liquidity index * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest **/ - function _mintToTreasury( + function _accrueToTreasury( DataTypes.ReserveData storage reserve, uint256 scaledVariableDebt, uint256 previousVariableBorrowIndex, From b591db752cdc01914017bda9117952db0143cc0a Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 1 Jun 2021 10:11:58 +0200 Subject: [PATCH 24/64] post-merge: cleaning after conflicts resolution --- .../lendingpool/LendingPoolConfigurator.sol | 25 +++---------------- markets/matic/reservesConfigs.ts | 2 -- test-suites/test-aave/configurator.spec.ts | 14 ++++------- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index dae274e8..5c8e8f46 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -35,8 +35,6 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur mapping(address => bool) private _riskAdmins; - mapping(address => bool) private _riskAdmins; - modifier onlyPoolAdmin { require(_addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); _; @@ -266,7 +264,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur uint256 borrowCap, bool stableBorrowRateEnabled ) external override onlyRiskOrPoolAdmins { - DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset);$ + DataTypes.ReserveConfigurationMap memory currentConfig = _pool.getConfiguration(asset); currentConfig.setBorrowingEnabled(true); currentConfig.setBorrowCap(borrowCap); @@ -487,32 +485,15 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur emit RiskAdminUnregistered(admin); } - /// @inheritdoc ILendingPoolConfigurator - function isRiskAdmin(address admin) external view override onlyPoolAdmin returns (bool) { - return _riskAdmins[admin]; - } - - /// @inheritdoc ILendingPoolConfigurator - function registerRiskAdmin(address admin) external override onlyPoolAdmin { - _riskAdmins[admin] = true; - emit RiskAdminRegistered(admin); - } - - /// @inheritdoc ILendingPoolConfigurator - function unregisterRiskAdmin(address admin) external override onlyPoolAdmin { - _riskAdmins[admin] = false; - emit RiskAdminUnregistered(admin); - } - /// @inheritdoc ILendingPoolConfigurator function authorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { - pool.authorizeFlashBorrower(flashBorrower); + _pool.authorizeFlashBorrower(flashBorrower); emit FlashBorrowerAuthorized(flashBorrower); } /// @inheritdoc ILendingPoolConfigurator function unauthorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { - pool.unauthorizeFlashBorrower(flashBorrower); + _pool.unauthorizeFlashBorrower(flashBorrower); emit FlashBorrowerUnauthorized(flashBorrower); } diff --git a/markets/matic/reservesConfigs.ts b/markets/matic/reservesConfigs.ts index 285a2713..cbeba1a6 100644 --- a/markets/matic/reservesConfigs.ts +++ b/markets/matic/reservesConfigs.ts @@ -92,8 +92,6 @@ export const strategyMATIC: IReserveParams = { borrowCap: '0', supplyCap: '0', reserveFactor: '2000', - borrowCap: MAX_BORROW_CAP, - supplyCap: MAX_SUPPLY_CAP, }; export const strategyAAVE: IReserveParams = { diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index a941e3ef..bff8b02a 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -134,14 +134,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { expect(supplyCap).to.be.equal(strategyWETH.supplyCap); }); it('Pauses the ETH reserve by emergency admin', async () => { - const { - configurator, - weth, - helpersContract, - addressesProvider, - users, - emergencyAdmin, - } = testEnv; + const { configurator, weth, helpersContract, addressesProvider, users, emergencyAdmin } = + testEnv; await configurator.connect(emergencyAdmin.signer).pauseReserve(weth.address); const { decimals, @@ -218,7 +212,9 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN); }); - + it('Pauses the ETH reserve by the pool admin', async () => { + const { configurator, weth, helpersContract, addressesProvider, users, emergencyAdmin } = + testEnv; await configurator.pauseReserve(weth.address); const { decimals, From 054dd5973ec44e3d2e0de188a5cc0527e13746cd Mon Sep 17 00:00:00 2001 From: The3D Date: Tue, 1 Jun 2021 20:24:51 +0200 Subject: [PATCH 25/64] fix: changed event name --- contracts/interfaces/ILendingPool.sol | 2 +- contracts/protocol/lendingpool/LendingPool.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 55cbbedd..98d66538 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -172,7 +172,7 @@ interface ILendingPool { * @param reserve the address of the reserve * @param amountMinted the amount minted to the treasury **/ - event TreasuryUpdated( + event MintedToTreasury( address indexed reserve, uint256 amountMinted ); diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index d49c98fb..130b62bf 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -552,7 +552,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IAToken(reserve.aTokenAddress).mintToTreasury(amountToMint, normalizedIncome); reserve.accruedToTreasury = 0; - emit TreasuryUpdated(reserveAddress, amountToMint); + emit MintedToTreasury(reserveAddress, amountToMint); } } } From 0326259c0361af86462b3850e086ccdab8f52980 Mon Sep 17 00:00:00 2001 From: The3D Date: Tue, 1 Jun 2021 20:41:35 +0200 Subject: [PATCH 26/64] fix: added check to avoid dropped reserves --- contracts/protocol/lendingpool/LendingPool.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 130b62bf..de5ea403 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -543,6 +543,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function mintToTreasury() public { for (uint256 i = 0; i < _reservesCount; i++) { address reserveAddress = _reservesList[i]; + + // if a reserve has been dropped this might happen + if(reserveAddress == address(0)){ + continue; + } + DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; uint256 accruedToTreasury = reserve.accruedToTreasury; From d013c6e9cee48a0676d0dffb3d6fc584e40e6494 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 3 Jun 2021 11:09:48 +0200 Subject: [PATCH 27/64] test: added mintToTreasury() test --- .../protocol/lendingpool/LendingPool.sol | 18 +++++++------ .../test-aave/mint-to-treasury.spec.ts | 25 +++++++++++++++++-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index de5ea403..0ea6f302 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -539,17 +539,19 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage /** * @dev Mints the assets accrued through the reserve factor to the treasury in the form of aTokens + * @param reserves The list of reserves for which the minting needs to be executed **/ - function mintToTreasury() public { - for (uint256 i = 0; i < _reservesCount; i++) { - address reserveAddress = _reservesList[i]; - - // if a reserve has been dropped this might happen - if(reserveAddress == address(0)){ - continue; - } + function mintToTreasury(address[] calldata reserves) public { + for (uint256 i = 0; i < reserves.length; i++) { + address reserveAddress = reserves[i]; DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; + + // this cover both inactive reserves and invalid reserves since the flag will be 0 for both + if(!reserve.configuration.getActive()){ + continue; + } + uint256 accruedToTreasury = reserve.accruedToTreasury; if (accruedToTreasury != 0) { diff --git a/test-suites/test-aave/mint-to-treasury.spec.ts b/test-suites/test-aave/mint-to-treasury.spec.ts index 00e24e83..1ed83274 100644 --- a/test-suites/test-aave/mint-to-treasury.spec.ts +++ b/test-suites/test-aave/mint-to-treasury.spec.ts @@ -9,7 +9,7 @@ import './helpers/utils/math'; const { expect } = require('chai'); makeSuite('Mint to treasury', (testEnv: TestEnv) => { - it('User 0 deposits 1000 DAI. Borrower borrows 100 DAI. Clock moved forward one year. Calculates and verifies the amount earned by the treasury', async () => { + it('User 0 deposits 1000 DAI. Borrower borrows 100 DAI. Clock moved forward one year. Calculates and verifies the amount accrued to the treasury', async () => { const { users, pool, dai, helpersContract } = testEnv; const amountDAItoDeposit = await convertToCurrencyDecimals(dai.address, '1000'); @@ -61,9 +61,30 @@ makeSuite('Mint to treasury', (testEnv: TestEnv) => { const { accruedToTreasury } = await pool.getReserveData(dai.address); + console.log("Accrued to treasury ", accruedToTreasury.toString()); + expect(accruedToTreasury.toString()).to.be.bignumber.almostEqual( expectedAccruedToTreasury, - 'Invalid amount accrued to treasury' + 'Invalid amount accrued to the treasury' ); }); + + it('Mints the accrued to the treasury', async () => { + const { users, pool, dai, aDai, helpersContract } = testEnv; + + const treasuryAddress = await aDai.RESERVE_TREASURY_ADDRESS(); + const { accruedToTreasury } = await pool.getReserveData(dai.address); + + await waitForTx(await pool.connect(users[0].signer).mintToTreasury([dai.address])); + const normalizedIncome = await pool.getReserveNormalizedIncome(dai.address); + + const treasuryBalance = await aDai.balanceOf(treasuryAddress); + + const expectedTreasuryBalance = new BigNumber(accruedToTreasury.toString()).rayMul( + new BigNumber(normalizedIncome.toString()) + ); + + expect(treasuryBalance.toString()).to.be.bignumber.almostEqual(expectedTreasuryBalance, "Invalid treasury balance after minting"); + + }); }); From 6cdfd8e31b6f14e721f87ccdf6e427f696b32725 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 3 Jun 2021 14:10:50 +0200 Subject: [PATCH 28/64] feat: initial cache layer implementation --- .../protocol/lendingpool/LendingPool.sol | 61 ++++++++++------- .../LendingPoolCollateralManager.sol | 9 ++- .../libraries/helpers/CachingHelper.sol | 59 ++++++++++++++++ .../protocol/libraries/logic/ReserveLogic.sol | 67 +++++++------------ 4 files changed, 127 insertions(+), 69 deletions(-) create mode 100644 contracts/protocol/libraries/helpers/CachingHelper.sol diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 0ea6f302..0c4ea068 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -26,6 +26,7 @@ import {ReserveConfiguration} from '../libraries/configuration/ReserveConfigurat import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; +import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPool contract @@ -270,6 +271,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); @@ -283,7 +285,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage interestRateMode ); - reserve.updateState(); + reserve.updateState(cachedData); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); @@ -323,6 +325,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); @@ -338,7 +341,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage aTokenAddress ); - reserve.updateState(); + reserve.updateState(cachedData); IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); IStableDebtToken(address(stableDebtToken)).mint( @@ -435,6 +438,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint256 currentPremium; uint256 currentAmountPlusPremium; address debtToken; + address[] aTokenAddresses; + uint256[] premiums; } /** @@ -465,40 +470,44 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) external override whenNotPaused { FlashLoanLocalVars memory vars; + vars.aTokenAddresses = new address[](assets.length); + vars.premiums = new uint256[](assets.length); + ValidationLogic.validateFlashloan(assets, amounts, _reserves); - address[] memory aTokenAddresses = new address[](assets.length); - uint256[] memory premiums = new uint256[](assets.length); vars.receiver = IFlashLoanReceiver(receiverAddress); for (vars.i = 0; vars.i < assets.length; vars.i++) { - aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; + vars.aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); + vars.premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); - IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); + IAToken(vars.aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } require( - vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params), + vars.receiver.executeOperation(assets, amounts, vars.premiums, msg.sender, params), Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN ); for (vars.i = 0; vars.i < assets.length; vars.i++) { vars.currentAsset = assets[vars.i]; vars.currentAmount = amounts[vars.i]; - vars.currentPremium = premiums[vars.i]; - vars.currentATokenAddress = aTokenAddresses[vars.i]; + vars.currentPremium = vars.premiums[vars.i]; + vars.currentATokenAddress = vars.aTokenAddresses[vars.i]; vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { - _reserves[vars.currentAsset].updateState(); - _reserves[vars.currentAsset].cumulateToLiquidityIndex( + DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + + reserve.updateState(cachedData); + reserve.cumulateToLiquidityIndex( IERC20(vars.currentATokenAddress).totalSupply(), vars.currentPremium ); - _reserves[vars.currentAsset].updateInterestRates( + reserve.updateInterestRates( vars.currentAsset, vars.currentATokenAddress, vars.currentAmountPlusPremium, @@ -863,6 +872,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function _executeBorrow(ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); address oracle = _addressesProvider.getPriceOracle(); @@ -886,7 +896,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage oracle ); - reserve.updateState(); + reserve.updateState(cachedData); uint256 currentStableRate = 0; @@ -944,17 +954,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); ValidationLogic.validateDeposit(reserve, amount); - address aToken = reserve.aTokenAddress; + reserve.updateState(cachedData); + reserve.updateInterestRates(asset, cachedData.aTokenAddress, amount, 0); - reserve.updateState(); - reserve.updateInterestRates(asset, aToken, amount, 0); + IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount); - IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); - - bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); + bool isFirstDeposit = IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); @@ -971,14 +980,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); address aToken = reserve.aTokenAddress; - reserve.updateState(); + reserve.updateState(cachedData); - uint256 liquidityIndex = reserve.liquidityIndex; - - uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(liquidityIndex); + uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(cachedData.newLiquidityIndex); uint256 amountToWithdraw = amount; @@ -990,7 +998,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); - IAToken(aToken).burn(msg.sender, to, amountToWithdraw, liquidityIndex); + IAToken(aToken).burn(msg.sender, to, amountToWithdraw, cachedData.newLiquidityIndex); if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isBorrowingAny()) { @@ -1022,6 +1030,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; + CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); @@ -1043,7 +1052,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage paybackAmount = amount; } - reserve.updateState(); + reserve.updateState(cachedData); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 80692726..5e6722b2 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -18,6 +18,7 @@ import {Errors} from '../libraries/helpers/Errors.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; +import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPoolCollateralManager contract @@ -160,7 +161,9 @@ contract LendingPoolCollateralManager is } } - debtReserve.updateState(); + CachingHelper.CachedData memory debtReserveCachedData = CachingHelper.fetchData(debtReserve); + + debtReserve.updateState(debtReserveCachedData); if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( @@ -200,7 +203,9 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { - collateralReserve.updateState(); + CachingHelper.CachedData memory collateralReserveCachedData = CachingHelper.fetchData(collateralReserve); + + collateralReserve.updateState(collateralReserveCachedData); collateralReserve.updateInterestRates( collateralAsset, address(vars.collateralAtoken), diff --git a/contracts/protocol/libraries/helpers/CachingHelper.sol b/contracts/protocol/libraries/helpers/CachingHelper.sol new file mode 100644 index 00000000..bbfd959c --- /dev/null +++ b/contracts/protocol/libraries/helpers/CachingHelper.sol @@ -0,0 +1,59 @@ +pragma solidity 0.6.12; + +pragma experimental ABIEncoderV2; + +import {DataTypes} from '../types/DataTypes.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; + +library CachingHelper { + struct CachedData { + uint256 oldScaledVariableDebt; + uint256 oldTotalVariableDebt; + uint256 newSscaledVariableDebt; + uint256 newTtotalVariableDebt; + uint256 oldPrincipalStableDebt; + uint256 oldAvgStableBorrowRate; + uint256 oldTotalStableDebt; + uint256 newPrincipalStableDebt; + uint256 newAvgStableBorrowRate; + uint256 newTotalStableDebt; + uint256 oldLiquidityIndex; + uint256 newLiquidityIndex; + uint256 oldVariableBorrowIndex; + uint256 newVariableBorrowIndex; + uint256 oldLiquidityRate; + uint256 oldVariableBorrowRate; + DataTypes.ReserveConfigurationMap reserveConfiguration; + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + uint40 reserveLastUpdateTimestamp; + } + + function fetchData(DataTypes.ReserveData storage reserveData) + internal + view + returns (CachingHelper.CachedData memory) + { + CachedData memory cachedData; + + cachedData.oldLiquidityIndex = reserveData.liquidityIndex; + cachedData.oldVariableBorrowIndex = reserveData.variableBorrowIndex; + + cachedData.aTokenAddress = reserveData.aTokenAddress; + cachedData.stableDebtTokenAddress = reserveData.stableDebtTokenAddress; + cachedData.variableDebtTokenAddress = reserveData.variableDebtTokenAddress; + + cachedData.reserveConfiguration = reserveData.configuration; + + cachedData.oldLiquidityRate = reserveData.currentLiquidityRate; + cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate; + + cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; + + cachedData.oldScaledVariableDebt = IVariableDebtToken(cachedData.variableDebtTokenAddress) + .scaledTotalSupply(); + + return cachedData; + } +} diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2e9850c9..33ba2aaf 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -14,6 +14,7 @@ import {WadRayMath} from '../math/WadRayMath.sol'; import {PercentageMath} from '../math/PercentageMath.sol'; import {Errors} from '../helpers/Errors.sol'; import {DataTypes} from '../types/DataTypes.sol'; +import {CachingHelper} from '../helpers/CachingHelper.sol'; /** * @title ReserveLogic library @@ -107,29 +108,22 @@ library ReserveLogic { * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ - function updateState(DataTypes.ReserveData storage reserve) internal { - uint256 scaledVariableDebt = - IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); - uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; - uint256 previousLiquidityIndex = reserve.liquidityIndex; - uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; + function updateState(DataTypes.ReserveData storage reserve, CachingHelper.CachedData memory cachedData) internal { - (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = _updateIndexes( reserve, - scaledVariableDebt, - previousLiquidityIndex, - previousVariableBorrowIndex, - lastUpdatedTimestamp + cachedData ); + + _accrueToTreasury( reserve, - scaledVariableDebt, - previousVariableBorrowIndex, - newLiquidityIndex, - newVariableBorrowIndex, - lastUpdatedTimestamp + cachedData.oldScaledVariableDebt, + cachedData.oldVariableBorrowIndex, + cachedData.newLiquidityIndex, + cachedData.newVariableBorrowIndex, + cachedData.reserveLastUpdateTimestamp ); } @@ -209,9 +203,7 @@ library ReserveLogic { (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) .getTotalSupplyAndAvgRate(); - //calculates the total variable debt locally using the scaled total supply instead - //of totalSupply(), as it's noticeably cheaper. Also, the index has been - //updated by the previous updateState() call + vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) .scaledTotalSupply() .rayMul(reserve.variableBorrowIndex); @@ -327,47 +319,40 @@ library ReserveLogic { /** * @dev Updates the reserve indexes and the timestamp of the update * @param reserve The reserve reserve to be updated - * @param scaledVariableDebt The scaled variable debt - * @param liquidityIndex The last stored liquidity index - * @param variableBorrowIndex The last stored variable borrow index + * @param cachedData The cache layer holding the cached protocol data **/ function _updateIndexes( DataTypes.ReserveData storage reserve, - uint256 scaledVariableDebt, - uint256 liquidityIndex, - uint256 variableBorrowIndex, - uint40 timestamp - ) internal returns (uint256, uint256) { - uint256 currentLiquidityRate = reserve.currentLiquidityRate; + CachingHelper.CachedData memory cachedData + ) internal { - uint256 newLiquidityIndex = liquidityIndex; - uint256 newVariableBorrowIndex = variableBorrowIndex; + cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex; + cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex; //only cumulating if there is any income being produced - if (currentLiquidityRate > 0) { + if (cachedData.oldLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = - MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); - newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); - require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + MathUtils.calculateLinearInterest(cachedData.oldLiquidityRate, cachedData.reserveLastUpdateTimestamp); + cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul(cachedData.oldLiquidityIndex); + require( cachedData.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); - reserve.liquidityIndex = uint128(newLiquidityIndex); + reserve.liquidityIndex = uint128(cachedData.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) { + if (cachedData.oldScaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = - MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); - newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); + MathUtils.calculateCompoundedInterest(cachedData.oldVariableBorrowRate, cachedData.reserveLastUpdateTimestamp); + cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(cachedData.oldVariableBorrowIndex); require( - newVariableBorrowIndex <= type(uint128).max, + cachedData.newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW ); - reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); + reserve.variableBorrowIndex = uint128(cachedData.newVariableBorrowIndex); } } //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); - return (newLiquidityIndex, newVariableBorrowIndex); } } From 360e83fd05598d4b3ed1683d6135b31e14b43c94 Mon Sep 17 00:00:00 2001 From: The3D Date: Thu, 3 Jun 2021 21:28:21 +0200 Subject: [PATCH 29/64] refactor: further refactored the cache layer and replaced reads/calls --- .../protocol/lendingpool/LendingPool.sol | 125 +++++++++++------- .../LendingPoolCollateralManager.sol | 26 +++- .../configuration/ReserveConfiguration.sol | 37 ++++++ .../libraries/helpers/CachingHelper.sol | 24 +++- .../protocol/libraries/logic/ReserveLogic.sol | 112 ++++++++-------- .../libraries/logic/ValidationLogic.sol | 76 ++++++----- 6 files changed, 250 insertions(+), 150 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 0c4ea068..06bfeb8e 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -288,28 +288,44 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateState(cachedData); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); - IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + IStableDebtToken(cachedData.stableDebtTokenAddress).burn(msg.sender, stableDebt); + + cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + .oldTotalStableDebt + .sub(stableDebt); + + IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, - reserve.variableBorrowIndex + cachedData.newVariableBorrowIndex + ); + cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.add( + stableDebt.rayDiv(cachedData.newVariableBorrowIndex) ); } else { - IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( msg.sender, variableDebt, - reserve.variableBorrowIndex + cachedData.newVariableBorrowIndex ); - IStableDebtToken(reserve.stableDebtTokenAddress).mint( + cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( + variableDebt.rayDiv(cachedData.newVariableBorrowIndex) + ); + + IStableDebtToken(cachedData.stableDebtTokenAddress).mint( msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate ); + + cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + .oldTotalStableDebt + .add(stableDebt); } - reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); + reserve.updateInterestRates(cachedData, asset, 0, 0); emit Swap(asset, msg.sender, rateMode); } @@ -327,10 +343,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.ReserveData storage reserve = _reserves[asset]; CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); - IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); - IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); - address aTokenAddress = reserve.aTokenAddress; - + IERC20 stableDebtToken = IERC20(cachedData.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(cachedData.variableDebtTokenAddress); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); ValidationLogic.validateRebalanceStableBorrowRate( @@ -338,7 +352,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage asset, stableDebtToken, variableDebtToken, - aTokenAddress + cachedData.aTokenAddress ); reserve.updateState(cachedData); @@ -351,7 +365,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.currentStableBorrowRate ); - reserve.updateInterestRates(asset, aTokenAddress, 0, 0); + reserve.updateInterestRates(cachedData, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); } @@ -475,7 +489,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateFlashloan(assets, amounts, _reserves); - vars.receiver = IFlashLoanReceiver(receiverAddress); for (vars.i = 0; vars.i < assets.length; vars.i++) { @@ -508,8 +521,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.currentPremium ); reserve.updateInterestRates( + cachedData, vars.currentAsset, - vars.currentATokenAddress, vars.currentAmountPlusPremium, 0 ); @@ -553,11 +566,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function mintToTreasury(address[] calldata reserves) public { for (uint256 i = 0; i < reserves.length; i++) { address reserveAddress = reserves[i]; - + DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; // this cover both inactive reserves and invalid reserves since the flag will be 0 for both - if(!reserve.configuration.getActive()){ + if (!reserve.configuration.getActive()) { continue; } @@ -874,48 +887,49 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); - address oracle = _addressesProvider.getPriceOracle(); - - uint256 amountInETH = - IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div( - 10**reserve.configuration.getDecimals() - ); + reserve.updateState(cachedData); ValidationLogic.validateBorrow( + cachedData, vars.asset, - reserve, vars.onBehalfOf, vars.amount, - amountInETH, vars.interestRateMode, _maxStableRateBorrowSizePercent, _reserves, userConfig, _reservesList, _reservesCount, - oracle + _addressesProvider.getPriceOracle() ); - reserve.updateState(cachedData); - uint256 currentStableRate = 0; bool isFirstBorrowing = false; if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; - isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint( + isFirstBorrowing = IStableDebtToken(cachedData.stableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, currentStableRate ); + + cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + .oldTotalStableDebt + .add(vars.amount); + } else { - isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + isFirstBorrowing = IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, - reserve.variableBorrowIndex + cachedData.newVariableBorrowIndex + ); + + cachedData.newScaledVariableDebt = cachedData.newScaledVariableDebt.add( + vars.amount.rayDiv(cachedData.newVariableBorrowIndex) ); } @@ -924,14 +938,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } reserve.updateInterestRates( + cachedData, vars.asset, - vars.aTokenAddress, 0, vars.releaseUnderlying ? vars.amount : 0 ); if (vars.releaseUnderlying) { - IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + IAToken(cachedData.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); } emit Borrow( @@ -956,14 +970,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.ReserveData storage reserve = _reserves[asset]; CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); - ValidationLogic.validateDeposit(reserve, amount); - reserve.updateState(cachedData); - reserve.updateInterestRates(asset, cachedData.aTokenAddress, amount, 0); + + ValidationLogic.validateDeposit(reserve, cachedData, amount); + + reserve.updateInterestRates(cachedData, asset, amount, 0); IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount); - bool isFirstDeposit = IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); + bool isFirstDeposit = + IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); @@ -982,11 +998,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); - address aToken = reserve.aTokenAddress; - reserve.updateState(cachedData); - uint256 userBalance = IAToken(aToken).scaledBalanceOf(msg.sender).rayMul(cachedData.newLiquidityIndex); + uint256 userBalance = + IAToken(cachedData.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( + cachedData.newLiquidityIndex + ); uint256 amountToWithdraw = amount; @@ -996,9 +1013,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); - reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); + reserve.updateInterestRates(cachedData, asset, 0, amountToWithdraw); - IAToken(aToken).burn(msg.sender, to, amountToWithdraw, cachedData.newLiquidityIndex); + IAToken(cachedData.aTokenAddress).burn( + msg.sender, + to, + amountToWithdraw, + cachedData.newLiquidityIndex + ); if (userConfig.isUsingAsCollateral(reserve.id)) { if (userConfig.isBorrowingAny()) { @@ -1055,25 +1077,30 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateState(cachedData); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + IStableDebtToken(cachedData.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + .oldTotalStableDebt + .sub(paybackAmount); } else { - IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, - reserve.variableBorrowIndex + cachedData.newVariableBorrowIndex + ); + cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( + paybackAmount.rayDiv(cachedData.newVariableBorrowIndex) ); } - address aToken = reserve.aTokenAddress; - reserve.updateInterestRates(asset, aToken, paybackAmount, 0); + reserve.updateInterestRates(cachedData, asset, paybackAmount, 0); if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } - IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount); + IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, paybackAmount); - IAToken(aToken).handleRepayment(msg.sender, paybackAmount); + IAToken(cachedData.aTokenAddress).handleRepayment(msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 5e6722b2..2f823d50 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -169,26 +169,37 @@ contract LendingPoolCollateralManager is IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate, - debtReserve.variableBorrowIndex + debtReserveCachedData.newVariableBorrowIndex + ); + debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData.oldScaledVariableDebt.sub( + vars.actualDebtToLiquidate.rayDiv(debtReserveCachedData.newVariableBorrowIndex) ); } else { // If the user doesn't have variable debt, no need to try to burn variable debt tokens if (vars.userVariableDebt > 0) { - IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( user, vars.userVariableDebt, - debtReserve.variableBorrowIndex + debtReserveCachedData.newVariableBorrowIndex ); + debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData + .oldScaledVariableDebt + .sub(vars.userVariableDebt.rayDiv(debtReserveCachedData.newVariableBorrowIndex)); } - IStableDebtToken(debtReserve.stableDebtTokenAddress).burn( + IStableDebtToken(debtReserveCachedData.stableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); + + debtReserveCachedData.newPrincipalStableDebt = debtReserveCachedData + .newTotalStableDebt = debtReserveCachedData.oldTotalStableDebt.sub( + vars.actualDebtToLiquidate.sub(vars.userVariableDebt) + ); } debtReserve.updateInterestRates( + debtReserveCachedData, debtAsset, - debtReserve.aTokenAddress, vars.actualDebtToLiquidate, 0 ); @@ -203,12 +214,13 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { - CachingHelper.CachedData memory collateralReserveCachedData = CachingHelper.fetchData(collateralReserve); + CachingHelper.CachedData memory collateralReserveCachedData = + CachingHelper.fetchData(collateralReserve); collateralReserve.updateState(collateralReserveCachedData); collateralReserve.updateInterestRates( + collateralReserveCachedData, collateralAsset, - address(vars.collateralAtoken), 0, vars.maxCollateralToLiquidate ); diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 91d8bc50..389f5091 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -65,6 +65,16 @@ library ReserveConfiguration { return self.data & ~LTV_MASK; } + /** + * @dev Gets the Loan to Value of the reserve + * @param self The reserve configuration + * @return The loan to value + **/ + function getLtvMemory(DataTypes.ReserveConfigurationMap memory self) internal view returns (uint256) { + return self.data & ~LTV_MASK; + } + + /** * @dev Sets the liquidation threshold of the reserve * @param self The reserve configuration @@ -150,6 +160,20 @@ library ReserveConfiguration { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; } + + /** + * @dev Gets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @return The decimals of the asset + **/ + function getDecimalsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + view + returns (uint256) + { + return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; + } + /** * @dev Sets the active state of the reserve * @param self The reserve configuration @@ -293,6 +317,19 @@ library ReserveConfiguration { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; } + /** + * @dev Gets the reserve factor of the reserve + * @param self The reserve configuration + * @return The reserve factor + **/ + function getReserveFactorMemory(DataTypes.ReserveConfigurationMap memory self) + internal + view + returns (uint256) + { + return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; + } + /** * @dev Sets the borrow cap of the reserve * @param self The reserve configuration diff --git a/contracts/protocol/libraries/helpers/CachingHelper.sol b/contracts/protocol/libraries/helpers/CachingHelper.sol index bbfd959c..8bb739ef 100644 --- a/contracts/protocol/libraries/helpers/CachingHelper.sol +++ b/contracts/protocol/libraries/helpers/CachingHelper.sol @@ -4,13 +4,14 @@ pragma experimental ABIEncoderV2; import {DataTypes} from '../types/DataTypes.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; library CachingHelper { struct CachedData { uint256 oldScaledVariableDebt; uint256 oldTotalVariableDebt; - uint256 newSscaledVariableDebt; - uint256 newTtotalVariableDebt; + uint256 newScaledVariableDebt; + uint256 newTotalVariableDebt; uint256 oldPrincipalStableDebt; uint256 oldAvgStableBorrowRate; uint256 oldTotalStableDebt; @@ -28,6 +29,7 @@ library CachingHelper { address stableDebtTokenAddress; address variableDebtTokenAddress; uint40 reserveLastUpdateTimestamp; + uint40 stableDebtLastUpdateTimestamp; } function fetchData(DataTypes.ReserveData storage reserveData) @@ -51,9 +53,23 @@ library CachingHelper { cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; - cachedData.oldScaledVariableDebt = IVariableDebtToken(cachedData.variableDebtTokenAddress) + cachedData.oldScaledVariableDebt = cachedData.newScaledVariableDebt = IVariableDebtToken( + cachedData + .variableDebtTokenAddress + ) .scaledTotalSupply(); - + + ( + cachedData.oldPrincipalStableDebt, + cachedData.oldTotalStableDebt, + cachedData.oldAvgStableBorrowRate, + cachedData.stableDebtLastUpdateTimestamp + ) = IStableDebtToken(cachedData.stableDebtTokenAddress).getSupplyData(); + + cachedData.newPrincipalStableDebt = cachedData.oldPrincipalStableDebt; + cachedData.newTotalStableDebt = cachedData.oldTotalStableDebt; + cachedData.newAvgStableBorrowRate = cachedData.oldAvgStableBorrowRate; + return cachedData; } } diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 33ba2aaf..53c56454 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -108,23 +108,13 @@ library ReserveLogic { * @dev Updates the liquidity cumulative index and the variable borrow index. * @param reserve the reserve object **/ - function updateState(DataTypes.ReserveData storage reserve, CachingHelper.CachedData memory cachedData) internal { + function updateState( + DataTypes.ReserveData storage reserve, + CachingHelper.CachedData memory cachedData + ) internal { + _updateIndexes(reserve, cachedData); - _updateIndexes( - reserve, - cachedData - ); - - - - _accrueToTreasury( - reserve, - cachedData.oldScaledVariableDebt, - cachedData.oldVariableBorrowIndex, - cachedData.newLiquidityIndex, - cachedData.newVariableBorrowIndex, - cachedData.reserveLastUpdateTimestamp - ); + _accrueToTreasury(reserve, cachedData); } /** @@ -191,22 +181,21 @@ library ReserveLogic { **/ function updateInterestRates( DataTypes.ReserveData storage reserve, + CachingHelper.CachedData memory cachedData, address reserveAddress, - address aTokenAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { UpdateInterestRatesLocalVars memory vars; - vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + if (cachedData.oldTotalStableDebt != cachedData.newTotalStableDebt) { + cachedData.newAvgStableBorrowRate = IStableDebtToken(cachedData.stableDebtTokenAddress) + .getAverageStableRate(); + } - (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) - .getTotalSupplyAndAvgRate(); - - - vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) - .scaledTotalSupply() - .rayMul(reserve.variableBorrowIndex); + cachedData.newTotalVariableDebt = cachedData.newScaledVariableDebt.rayMul( + cachedData.newVariableBorrowIndex + ); ( vars.newLiquidityRate, @@ -214,13 +203,13 @@ library ReserveLogic { vars.newVariableRate ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( reserveAddress, - aTokenAddress, + cachedData.aTokenAddress, liquidityAdded, liquidityTaken, - vars.totalStableDebt, - vars.totalVariableDebt, - vars.avgStableRate, - reserve.configuration.getReserveFactor() + cachedData.newTotalStableDebt, + cachedData.newTotalVariableDebt, + cachedData.newAvgStableBorrowRate, + cachedData.reserveConfiguration.getReserveFactorMemory() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); @@ -235,8 +224,8 @@ library ReserveLogic { vars.newLiquidityRate, vars.newStableRate, vars.newVariableRate, - reserve.liquidityIndex, - reserve.variableBorrowIndex + cachedData.newLiquidityIndex, + cachedData.newVariableBorrowIndex ); } @@ -258,46 +247,35 @@ library ReserveLogic { * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * specific asset. * @param reserve The reserve reserve to be updated - * @param scaledVariableDebt The current scaled total variable debt - * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest - * @param newLiquidityIndex The new liquidity index - * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest + * @param cachedData The caching layer for the reserve data **/ function _accrueToTreasury( DataTypes.ReserveData storage reserve, - uint256 scaledVariableDebt, - uint256 previousVariableBorrowIndex, - uint256 newLiquidityIndex, - uint256 newVariableBorrowIndex, - uint40 timestamp + CachingHelper.CachedData memory cachedData ) internal { MintToTreasuryLocalVars memory vars; - vars.reserveFactor = reserve.configuration.getReserveFactor(); + vars.reserveFactor = cachedData.reserveConfiguration.getReserveFactorMemory(); if (vars.reserveFactor == 0) { return; } - //fetching the principal, total stable debt and the avg stable rate - ( - vars.principalStableDebt, - vars.currentStableDebt, - vars.avgStableRate, - vars.stableSupplyUpdatedTimestamp - ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData(); - //calculate the last principal variable debt - vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); + vars.previousVariableDebt = cachedData.oldScaledVariableDebt.rayMul( + cachedData.oldVariableBorrowIndex + ); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); + vars.currentVariableDebt = cachedData.oldScaledVariableDebt.rayMul( + cachedData.newVariableBorrowIndex + ); //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( vars.avgStableRate, vars.stableSupplyUpdatedTimestamp, - timestamp + cachedData.reserveLastUpdateTimestamp ); vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); @@ -312,7 +290,9 @@ library ReserveLogic { vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { - reserve.accruedToTreasury = reserve.accruedToTreasury.add(vars.amountToMint.rayDiv(newLiquidityIndex)); + reserve.accruedToTreasury = reserve.accruedToTreasury.add( + vars.amountToMint.rayDiv(cachedData.newLiquidityIndex) + ); } } @@ -325,16 +305,23 @@ library ReserveLogic { DataTypes.ReserveData storage reserve, CachingHelper.CachedData memory cachedData ) internal { - cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex; cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex; //only cumulating if there is any income being produced if (cachedData.oldLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = - MathUtils.calculateLinearInterest(cachedData.oldLiquidityRate, cachedData.reserveLastUpdateTimestamp); - cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul(cachedData.oldLiquidityIndex); - require( cachedData.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + MathUtils.calculateLinearInterest( + cachedData.oldLiquidityRate, + cachedData.reserveLastUpdateTimestamp + ); + cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( + cachedData.oldLiquidityIndex + ); + require( + cachedData.newLiquidityIndex <= type(uint128).max, + Errors.RL_LIQUIDITY_INDEX_OVERFLOW + ); reserve.liquidityIndex = uint128(cachedData.newLiquidityIndex); @@ -342,8 +329,13 @@ library ReserveLogic { //that there is actual variable debt before accumulating if (cachedData.oldScaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = - MathUtils.calculateCompoundedInterest(cachedData.oldVariableBorrowRate, cachedData.reserveLastUpdateTimestamp); - cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(cachedData.oldVariableBorrowIndex); + MathUtils.calculateCompoundedInterest( + cachedData.oldVariableBorrowRate, + cachedData.reserveLastUpdateTimestamp + ); + cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( + cachedData.oldVariableBorrowIndex + ); require( cachedData.newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 40ef23be..94cc3248 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -12,11 +12,14 @@ import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20. import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {Errors} from '../helpers/Errors.sol'; +import {CachingHelper} from '../helpers/CachingHelper.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IAToken} from '../../../interfaces/IAToken.sol'; import {DataTypes} from '../types/DataTypes.sol'; +import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol'; /** * @title ReserveLogic library @@ -40,11 +43,15 @@ library ValidationLogic { * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ - function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { - DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; - (bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory(); - (, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory(); - uint256 supplyCap = reserveConfiguration.getSupplyCapMemory(); + function validateDeposit( + DataTypes.ReserveData storage reserve, + CachingHelper.CachedData memory cachedData, + uint256 amount + ) internal view { + (bool isActive, bool isFrozen, , , bool isPaused) = + cachedData.reserveConfiguration.getFlagsMemory(); + (, , , uint256 reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); + uint256 supplyCap = cachedData.reserveConfiguration.getSupplyCapMemory(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -52,7 +59,11 @@ library ValidationLogic { require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( supplyCap == 0 || - IERC20(reserve.aTokenAddress).totalSupply().add(amount).div(10**reserveDecimals) < + IAToken(cachedData.aTokenAddress) + .scaledTotalSupply() + .rayMul(cachedData.newLiquidityIndex) + .add(amount) + .div(10**reserveDecimals) < supplyCap, Errors.VL_SUPPLY_CAP_EXCEEDED ); @@ -85,10 +96,11 @@ library ValidationLogic { uint256 userBorrowBalanceETH; uint256 availableLiquidity; uint256 healthFactor; - uint256 totalSupplyStableDebt; + uint256 totalDebt; uint256 totalSupplyVariableDebt; uint256 reserveDecimals; uint256 borrowCap; + uint256 amountInETH; bool isActive; bool isFrozen; bool isPaused; @@ -99,10 +111,8 @@ library ValidationLogic { /** * @dev Validates a borrow action * @param asset The address of the asset to borrow - * @param reserve The reserve state from which the user is borrowing * @param userAddress The address of the user * @param amount The amount to be borrowed - * @param amountInETH The amount to be borrowed, in ETH * @param interestRateMode The interest rate mode at which the user is borrowing * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage * @param reservesData The state of all the reserves @@ -112,11 +122,10 @@ library ValidationLogic { */ function validateBorrow( + CachingHelper.CachedData memory cachedData, address asset, - DataTypes.ReserveData storage reserve, address userAddress, uint256 amount, - uint256 amountInETH, uint256 interestRateMode, uint256 maxStableLoanPercent, mapping(address => DataTypes.ReserveData) storage reservesData, @@ -127,8 +136,7 @@ library ValidationLogic { ) external view { ValidateBorrowLocalVars memory vars; - DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; - (, , , vars.reserveDecimals, ) = reserveConfiguration.getParamsMemory(); + (, , , vars.reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); ( vars.isActive, @@ -136,7 +144,7 @@ library ValidationLogic { vars.borrowingEnabled, vars.stableRateBorrowingEnabled, vars.isPaused - ) = reserveConfiguration.getFlagsMemory(); + ) = cachedData.reserveConfiguration.getFlagsMemory(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); @@ -152,18 +160,23 @@ library ValidationLogic { Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED ); - vars.totalSupplyStableDebt = IERC20(reserve.stableDebtTokenAddress).totalSupply(); - vars.borrowCap = reserveConfiguration.getBorrowCapMemory(); - vars.totalSupplyVariableDebt = IERC20(reserve.variableDebtTokenAddress).totalSupply(); + vars.borrowCap = cachedData.reserveConfiguration.getBorrowCapMemory(); - require( - vars.borrowCap == 0 || - vars.totalSupplyStableDebt.add(vars.totalSupplyVariableDebt).add(amount).div( - 10**vars.reserveDecimals - ) < - vars.borrowCap, - Errors.VL_BORROW_CAP_EXCEEDED - ); + if (vars.borrowCap > 0) { + { + vars.totalSupplyVariableDebt = cachedData.oldScaledVariableDebt.rayMul( + cachedData.newVariableBorrowIndex + ); + + vars.totalDebt = cachedData.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( + amount + ); + require( + vars.totalDebt.div(10**vars.reserveDecimals) < vars.borrowCap, + Errors.VL_BORROW_CAP_EXCEEDED + ); + } + } ( vars.userCollateralBalanceETH, @@ -187,8 +200,11 @@ library ValidationLogic { Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD ); + vars.amountInETH = IPriceOracleGetter(oracle).getAssetPrice(asset); + vars.amountInETH = vars.amountInETH.mul(amount).div(10**vars.reserveDecimals); + //add the current already borrowed amount to the amount requested to calculate the total collateral needed. - vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv( + vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(vars.amountInETH).percentDiv( vars.currentLtv ); //LTV is calculated in percentage @@ -211,13 +227,13 @@ library ValidationLogic { require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); require( - !userConfig.isUsingAsCollateral(reserve.id) || - reserve.configuration.getLtv() == 0 || - amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), + !userConfig.isUsingAsCollateral(reservesData[asset].id) || + cachedData.reserveConfiguration.getLtvMemory() == 0 || + amount > IERC20(cachedData.aTokenAddress).balanceOf(userAddress), Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY ); - vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress); + vars.availableLiquidity = IERC20(asset).balanceOf(cachedData.aTokenAddress); //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity From 3d3bdc20961e2bf732f7a702c5aabd20063ed7f8 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Fri, 4 Jun 2021 09:43:43 +0200 Subject: [PATCH 30/64] fix-grammar: fixed typos --- .../test-aave/authorized-flashloan.spec.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test-suites/test-aave/authorized-flashloan.spec.ts b/test-suites/test-aave/authorized-flashloan.spec.ts index a66c6276..06977e36 100644 --- a/test-suites/test-aave/authorized-flashloan.spec.ts +++ b/test-suites/test-aave/authorized-flashloan.spec.ts @@ -30,7 +30,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { before(async () => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); }); - it('Authorize a flash bororwer', async () => { + it('Authorize a flash borrower', async () => { const { deployer, pool, weth, configurator } = testEnv; await configurator.authorizeFlashBorrower(deployer.address); }); @@ -47,7 +47,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await pool.deposit(weth.address, amountToDeposit, userAddress, '0'); }); - it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => { + it('Takes WETH flash loan with mode = 0, returns the funds correctly', async () => { const { pool, helpersContract, weth } = testEnv; await pool.flashLoan( @@ -76,7 +76,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { expect(currentLiquidityIndex.toString()).to.be.equal('1000000000000000000000000000'); }); - it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => { + it('Takes an ETH flash loan with mode = 0 as big as the available liquidity', async () => { const { pool, helpersContract, weth } = testEnv; const reserveDataBefore = await helpersContract.getReserveData(weth.address); @@ -92,7 +92,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const reserveData = await helpersContract.getReserveData(weth.address); - const currentLiqudityRate = reserveData.liquidityRate; + const currentLiquidityRate = reserveData.liquidityRate; const currentLiquidityIndex = reserveData.liquidityIndex; const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) @@ -100,7 +100,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .plus(reserveData.totalVariableDebt.toString()); expect(totalLiquidity.toString()).to.be.equal('1000000000000000000'); - expect(currentLiqudityRate.toString()).to.be.equal('0'); + expect(currentLiquidityRate.toString()).to.be.equal('0'); expect(currentLiquidityIndex.toString()).to.be.equal('1000000000000000000000000000'); }); @@ -124,7 +124,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); }); - it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => { + it('Takes WETH flash loan, simulating a receiver as EOA (revert expected)', async () => { const { pool, weth, users } = testEnv; const caller = users[1]; await _mockFlashLoanReceiver.setFailExecutionTransfer(true); @@ -279,14 +279,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { .add(reserveData.totalStableDebt) .add(reserveData.totalVariableDebt) .toString(); - const currentLiqudityRate = reserveData.liquidityRate.toString(); + const currentLiquidityRate = reserveData.liquidityRate.toString(); const currentLiquidityIndex = reserveData.liquidityIndex.toString(); const currentUserBalance = userData.currentATokenBalance.toString(); const expectedLiquidity = await convertToCurrencyDecimals(usdc.address, '1000'); expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity'); - expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate'); + expect(currentLiquidityRate).to.be.equal('0', 'Invalid liquidity rate'); expect(currentLiquidityIndex).to.be.equal( new BigNumber('1.00000').multipliedBy(oneRay).toFixed(), 'Invalid liquidity index' From a0c92d47d052f0fd6bf1a05a99de336c69fb85a7 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Fri, 4 Jun 2021 09:53:48 +0200 Subject: [PATCH 31/64] fix-grammar: fixed typos --- test-suites/test-aave/configurator.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-suites/test-aave/configurator.spec.ts b/test-suites/test-aave/configurator.spec.ts index bff8b02a..7a49e625 100644 --- a/test-suites/test-aave/configurator.spec.ts +++ b/test-suites/test-aave/configurator.spec.ts @@ -1267,14 +1267,14 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => { CALLER_NOT_POOL_ADMIN ).to.be.revertedWith(CALLER_NOT_POOL_ADMIN); }); - it('Authorized a new flashborrower', async () => { + it('Authorized a new flash borrower', async () => { const { dai, pool, configurator, users, riskAdmin } = testEnv; await configurator.authorizeFlashBorrower(users[4].address); const isFlashBorrowerAuthorized = await pool.isFlashBorrowerAuthorized(users[4].address); expect(isFlashBorrowerAuthorized).to.be.true; }); - it('Unauthorized flashborrower', async () => { + it('Unauthorized flash borrower', async () => { const { dai, pool, configurator, users } = testEnv; await configurator.unauthorizeFlashBorrower(users[4].address); From b03710b9dc93ac07e361c8628ae0b520ae140c2b Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Fri, 4 Jun 2021 09:55:36 +0200 Subject: [PATCH 32/64] refactor: LendingPool: flash loan borrowers authorization functions merged into one --- contracts/interfaces/ILendingPool.sol | 4 +--- contracts/protocol/lendingpool/LendingPool.sol | 12 ++---------- .../protocol/lendingpool/LendingPoolConfigurator.sol | 4 ++-- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index c5801065..4f3d172e 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -459,9 +459,7 @@ interface ILendingPool { function paused() external view returns (bool); - function authorizeFlashBorrower(address flashBorrower) external; - - function unauthorizeFlashBorrower(address flashBorrower) external; + function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external; function isFlashBorrowerAuthorized(address flashBorrower) external view returns (bool); } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 777b47bf..83497d58 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -822,20 +822,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } } - function authorizeFlashBorrower(address flashBorrower) + function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external override onlyLendingPoolConfigurator { - _authorizedFlashBorrowers[flashBorrower] = true; - } - - function unauthorizeFlashBorrower(address flashBorrower) - external - override - onlyLendingPoolConfigurator - { - _authorizedFlashBorrowers[flashBorrower] = false; + _authorizedFlashBorrowers[flashBorrower] = authorized; } function isFlashBorrowerAuthorized(address flashBorrower) external view override returns (bool) { diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 5c8e8f46..2730937f 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -487,13 +487,13 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur /// @inheritdoc ILendingPoolConfigurator function authorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { - _pool.authorizeFlashBorrower(flashBorrower); + _pool.updateFlashBorrowerAuthorization(flashBorrower, true); emit FlashBorrowerAuthorized(flashBorrower); } /// @inheritdoc ILendingPoolConfigurator function unauthorizeFlashBorrower(address flashBorrower) external override onlyPoolAdmin { - _pool.unauthorizeFlashBorrower(flashBorrower); + _pool.updateFlashBorrowerAuthorization(flashBorrower, false); emit FlashBorrowerUnauthorized(flashBorrower); } From 044b492f7fbb853d5ad7c93dabe883a27c9226e9 Mon Sep 17 00:00:00 2001 From: The3D Date: Fri, 4 Jun 2021 14:47:02 +0200 Subject: [PATCH 33/64] refactor: further refactored the CachingHelper --- contracts/protocol/lendingpool/LendingPool.sol | 3 --- .../protocol/lendingpool/LendingPoolCollateralManager.sol | 2 +- contracts/protocol/libraries/helpers/CachingHelper.sol | 8 +++----- contracts/protocol/libraries/logic/ReserveLogic.sol | 8 ++++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 06bfeb8e..3165a0e2 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -198,7 +198,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage onBehalfOf, amount, interestRateMode, - reserve.aTokenAddress, referralCode, true ) @@ -542,7 +541,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage onBehalfOf, vars.currentAmount, modes[vars.i], - vars.currentATokenAddress, referralCode, false ) @@ -877,7 +875,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address onBehalfOf; uint256 amount; uint256 interestRateMode; - address aTokenAddress; uint16 referralCode; bool releaseUnderlying; } diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 2f823d50..a92045c3 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -166,7 +166,7 @@ contract LendingPoolCollateralManager is debtReserve.updateState(debtReserveCachedData); if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { - IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate, debtReserveCachedData.newVariableBorrowIndex diff --git a/contracts/protocol/libraries/helpers/CachingHelper.sol b/contracts/protocol/libraries/helpers/CachingHelper.sol index 8bb739ef..06908ee9 100644 --- a/contracts/protocol/libraries/helpers/CachingHelper.sol +++ b/contracts/protocol/libraries/helpers/CachingHelper.sol @@ -39,18 +39,16 @@ library CachingHelper { { CachedData memory cachedData; + cachedData.reserveConfiguration = reserveData.configuration; cachedData.oldLiquidityIndex = reserveData.liquidityIndex; cachedData.oldVariableBorrowIndex = reserveData.variableBorrowIndex; + cachedData.oldLiquidityRate = reserveData.currentLiquidityRate; + cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate; cachedData.aTokenAddress = reserveData.aTokenAddress; cachedData.stableDebtTokenAddress = reserveData.stableDebtTokenAddress; cachedData.variableDebtTokenAddress = reserveData.variableDebtTokenAddress; - cachedData.reserveConfiguration = reserveData.configuration; - - cachedData.oldLiquidityRate = reserveData.currentLiquidityRate; - cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate; - cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; cachedData.oldScaledVariableDebt = cachedData.newScaledVariableDebt = IVariableDebtToken( diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 53c56454..ac5ff510 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -273,17 +273,17 @@ library ReserveLogic { //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( - vars.avgStableRate, - vars.stableSupplyUpdatedTimestamp, + cachedData.oldAvgStableBorrowRate, + cachedData.stableDebtLastUpdateTimestamp, cachedData.reserveLastUpdateTimestamp ); - vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); + vars.previousStableDebt = cachedData.oldPrincipalStableDebt.rayMul(vars.cumulatedStableInterest); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars .currentVariableDebt - .add(vars.currentStableDebt) + .add(cachedData.oldTotalStableDebt) .sub(vars.previousVariableDebt) .sub(vars.previousStableDebt); From 86686ef3be93bde85a9d6269569ebbc6ec3a5a30 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 7 Jun 2021 18:02:13 +0200 Subject: [PATCH 34/64] refactor: further refactored the cache helper functions --- .../protocol/lendingpool/LendingPool.sol | 127 ++++++++-------- .../LendingPoolCollateralManager.sol | 41 +++--- .../configuration/ReserveConfiguration.sol | 12 ++ .../libraries/helpers/CachingHelper.sol | 73 ---------- .../protocol/libraries/logic/ReserveLogic.sol | 136 +++++++++++------- .../libraries/logic/ValidationLogic.sol | 77 +++++----- .../protocol/libraries/types/DataTypes.sol | 25 ++++ package-lock.json | 14 +- 8 files changed, 260 insertions(+), 245 deletions(-) delete mode 100644 contracts/protocol/libraries/helpers/CachingHelper.sol diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index e99428cc..1c5f4208 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -26,7 +26,6 @@ import {ReserveConfiguration} from '../libraries/configuration/ReserveConfigurat import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; -import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPool contract @@ -270,7 +269,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); @@ -278,53 +277,54 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateSwapRateMode( reserve, + reserveCache, _usersConfig[msg.sender], stableDebt, variableDebt, interestRateMode ); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(cachedData.stableDebtTokenAddress).burn(msg.sender, stableDebt); + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .sub(stableDebt); - IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.add( - stableDebt.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.add( + stableDebt.rayDiv(reserveCache.newVariableBorrowIndex) ); } else { - IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( msg.sender, variableDebt, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( - variableDebt.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( + variableDebt.rayDiv(reserveCache.newVariableBorrowIndex) ); - IStableDebtToken(cachedData.stableDebtTokenAddress).mint( + IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate ); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .add(stableDebt); } - reserve.updateInterestRates(cachedData, asset, 0, 0); + reserve.updateInterestRates(reserveCache, asset, 0, 0); emit Swap(asset, msg.sender, rateMode); } @@ -340,21 +340,22 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage **/ function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - IERC20 stableDebtToken = IERC20(cachedData.stableDebtTokenAddress); - IERC20 variableDebtToken = IERC20(cachedData.variableDebtTokenAddress); + IERC20 stableDebtToken = IERC20(reserveCache.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserveCache.variableDebtTokenAddress); uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); ValidationLogic.validateRebalanceStableBorrowRate( reserve, + reserveCache, asset, stableDebtToken, variableDebtToken, - cachedData.aTokenAddress + reserveCache.aTokenAddress ); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); IStableDebtToken(address(stableDebtToken)).mint( @@ -364,7 +365,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.currentStableBorrowRate ); - reserve.updateInterestRates(cachedData, asset, 0, 0); + reserve.updateInterestRates(reserveCache, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); } @@ -380,8 +381,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage whenNotPaused { DataTypes.ReserveData storage reserve = _reserves[asset]; + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - ValidationLogic.validateSetUseReserveAsCollateral(reserve); + ValidationLogic.validateSetUseReserveAsCollateral(reserve, reserveCache); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); @@ -512,15 +514,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { DataTypes.ReserveData storage reserve = _reserves[vars.currentAsset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); reserve.cumulateToLiquidityIndex( IERC20(vars.currentATokenAddress).totalSupply(), vars.currentPremium ); reserve.updateInterestRates( - cachedData, + reserveCache, vars.currentAsset, vars.currentAmountPlusPremium, 0 @@ -894,12 +896,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function _executeBorrow(ExecuteBorrowParams memory vars) internal { DataTypes.ReserveData storage reserve = _reserves[vars.asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); ValidationLogic.validateBorrow( - cachedData, + reserveCache, vars.asset, vars.onBehalfOf, vars.amount, @@ -918,27 +920,27 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; - isFirstBorrowing = IStableDebtToken(cachedData.stableDebtTokenAddress).mint( + isFirstBorrowing = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, currentStableRate ); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .add(vars.amount); } else { - isFirstBorrowing = IVariableDebtToken(cachedData.variableDebtTokenAddress).mint( + isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( vars.user, vars.onBehalfOf, vars.amount, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.newScaledVariableDebt.add( - vars.amount.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.newScaledVariableDebt.add( + vars.amount.rayDiv(reserveCache.newVariableBorrowIndex) ); } @@ -947,14 +949,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } reserve.updateInterestRates( - cachedData, + reserveCache, vars.asset, 0, vars.releaseUnderlying ? vars.amount : 0 ); if (vars.releaseUnderlying) { - IAToken(cachedData.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); } emit Borrow( @@ -977,18 +979,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint16 referralCode ) internal { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); - ValidationLogic.validateDeposit(reserve, cachedData, amount); + ValidationLogic.validateDeposit(reserve, reserveCache, amount); - reserve.updateInterestRates(cachedData, asset, amount, 0); + reserve.updateInterestRates(reserveCache, asset, amount, 0); - IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, amount); + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); bool isFirstDeposit = - IAToken(cachedData.aTokenAddress).mint(onBehalfOf, amount, cachedData.newLiquidityIndex); + IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.newLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); @@ -1005,13 +1007,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.UserConfigurationMap storage userConfig = _usersConfig[msg.sender]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); - reserve.updateState(cachedData); + reserve.updateState(reserveCache); uint256 userBalance = - IAToken(cachedData.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( - cachedData.newLiquidityIndex + IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( + reserveCache.newLiquidityIndex ); uint256 amountToWithdraw = amount; @@ -1020,15 +1022,15 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage amountToWithdraw = userBalance; } - ValidationLogic.validateWithdraw(reserve, amountToWithdraw, userBalance); + ValidationLogic.validateWithdraw(reserve, reserveCache, amountToWithdraw, userBalance); - reserve.updateInterestRates(cachedData, asset, 0, amountToWithdraw); + reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); - IAToken(cachedData.aTokenAddress).burn( + IAToken(reserveCache.aTokenAddress).burn( msg.sender, to, amountToWithdraw, - cachedData.newLiquidityIndex + reserveCache.newLiquidityIndex ); if (userConfig.isUsingAsCollateral(reserve.id)) { @@ -1061,7 +1063,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address onBehalfOf ) internal returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; - CachingHelper.CachedData memory cachedData = CachingHelper.fetchData(reserve); + DataTypes.ReserveCache memory reserveCache = reserve.cache(); (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); @@ -1069,6 +1071,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateRepay( reserve, + reserveCache, amount, interestRateMode, onBehalfOf, @@ -1083,33 +1086,33 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage paybackAmount = amount; } - reserve.updateState(cachedData); + reserve.updateState(reserveCache); if (interestRateMode == DataTypes.InterestRateMode.STABLE) { - IStableDebtToken(cachedData.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); - cachedData.newPrincipalStableDebt = cachedData.newTotalStableDebt = cachedData + IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache .oldTotalStableDebt .sub(paybackAmount); } else { - IVariableDebtToken(cachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, - cachedData.newVariableBorrowIndex + reserveCache.newVariableBorrowIndex ); - cachedData.newScaledVariableDebt = cachedData.oldScaledVariableDebt.sub( - paybackAmount.rayDiv(cachedData.newVariableBorrowIndex) + reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( + paybackAmount.rayDiv(reserveCache.newVariableBorrowIndex) ); } - reserve.updateInterestRates(cachedData, asset, paybackAmount, 0); + reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); } - IERC20(asset).safeTransferFrom(msg.sender, cachedData.aTokenAddress, paybackAmount); + IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount); - IAToken(cachedData.aTokenAddress).handleRepayment(msg.sender, paybackAmount); + IAToken(reserveCache.aTokenAddress).handleRepayment(msg.sender, paybackAmount); emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index a92045c3..2f6b0bcb 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -18,7 +18,6 @@ import {Errors} from '../libraries/helpers/Errors.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; import {LendingPoolStorage} from './LendingPoolStorage.sol'; -import {CachingHelper} from '../libraries/helpers/CachingHelper.sol'; /** * @title LendingPoolCollateralManager contract @@ -101,11 +100,16 @@ contract LendingPoolCollateralManager is _addressesProvider.getPriceOracle() ); + DataTypes.ReserveCache memory debtReserveCache = debtReserve.cache(); + DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache(); + (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve); (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall( collateralReserve, debtReserve, + debtReserveCache, + collateralReserveCache, userConfig, vars.healthFactor, vars.userStableDebt, @@ -161,44 +165,42 @@ contract LendingPoolCollateralManager is } } - CachingHelper.CachedData memory debtReserveCachedData = CachingHelper.fetchData(debtReserve); - - debtReserve.updateState(debtReserveCachedData); + debtReserve.updateState(debtReserveCache); if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { - IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate, - debtReserveCachedData.newVariableBorrowIndex + debtReserveCache.newVariableBorrowIndex ); - debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData.oldScaledVariableDebt.sub( - vars.actualDebtToLiquidate.rayDiv(debtReserveCachedData.newVariableBorrowIndex) + debtReserveCache.newScaledVariableDebt = debtReserveCache.oldScaledVariableDebt.sub( + vars.actualDebtToLiquidate.rayDiv(debtReserveCache.newVariableBorrowIndex) ); } else { // If the user doesn't have variable debt, no need to try to burn variable debt tokens if (vars.userVariableDebt > 0) { - IVariableDebtToken(debtReserveCachedData.variableDebtTokenAddress).burn( + IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.userVariableDebt, - debtReserveCachedData.newVariableBorrowIndex + debtReserveCache.newVariableBorrowIndex ); - debtReserveCachedData.newScaledVariableDebt = debtReserveCachedData + debtReserveCache.newScaledVariableDebt = debtReserveCache .oldScaledVariableDebt - .sub(vars.userVariableDebt.rayDiv(debtReserveCachedData.newVariableBorrowIndex)); + .sub(vars.userVariableDebt.rayDiv(debtReserveCache.newVariableBorrowIndex)); } - IStableDebtToken(debtReserveCachedData.stableDebtTokenAddress).burn( + IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); - debtReserveCachedData.newPrincipalStableDebt = debtReserveCachedData - .newTotalStableDebt = debtReserveCachedData.oldTotalStableDebt.sub( + debtReserveCache.newPrincipalStableDebt = debtReserveCache + .newTotalStableDebt = debtReserveCache.oldTotalStableDebt.sub( vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); } debtReserve.updateInterestRates( - debtReserveCachedData, + debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0 @@ -214,12 +216,9 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { - CachingHelper.CachedData memory collateralReserveCachedData = - CachingHelper.fetchData(collateralReserve); - - collateralReserve.updateState(collateralReserveCachedData); + collateralReserve.updateState(collateralReserveCache); collateralReserve.updateInterestRates( - collateralReserveCachedData, + collateralReserveCache, collateralAsset, 0, vars.maxCollateralToLiquidate diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 389f5091..012e4cbd 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -104,6 +104,18 @@ library ReserveConfiguration { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } + /** + * @dev Gets the liquidation threshold of the reserve + * @param self The reserve configuration + * @return The liquidation threshold + **/ + function getLiquidationThresholdMemory(DataTypes.ReserveConfigurationMap memory self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; + } /** * @dev Sets the liquidation bonus of the reserve * @param self The reserve configuration diff --git a/contracts/protocol/libraries/helpers/CachingHelper.sol b/contracts/protocol/libraries/helpers/CachingHelper.sol deleted file mode 100644 index 06908ee9..00000000 --- a/contracts/protocol/libraries/helpers/CachingHelper.sol +++ /dev/null @@ -1,73 +0,0 @@ -pragma solidity 0.6.12; - -pragma experimental ABIEncoderV2; - -import {DataTypes} from '../types/DataTypes.sol'; -import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; -import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; - -library CachingHelper { - struct CachedData { - uint256 oldScaledVariableDebt; - uint256 oldTotalVariableDebt; - uint256 newScaledVariableDebt; - uint256 newTotalVariableDebt; - uint256 oldPrincipalStableDebt; - uint256 oldAvgStableBorrowRate; - uint256 oldTotalStableDebt; - uint256 newPrincipalStableDebt; - uint256 newAvgStableBorrowRate; - uint256 newTotalStableDebt; - uint256 oldLiquidityIndex; - uint256 newLiquidityIndex; - uint256 oldVariableBorrowIndex; - uint256 newVariableBorrowIndex; - uint256 oldLiquidityRate; - uint256 oldVariableBorrowRate; - DataTypes.ReserveConfigurationMap reserveConfiguration; - address aTokenAddress; - address stableDebtTokenAddress; - address variableDebtTokenAddress; - uint40 reserveLastUpdateTimestamp; - uint40 stableDebtLastUpdateTimestamp; - } - - function fetchData(DataTypes.ReserveData storage reserveData) - internal - view - returns (CachingHelper.CachedData memory) - { - CachedData memory cachedData; - - cachedData.reserveConfiguration = reserveData.configuration; - cachedData.oldLiquidityIndex = reserveData.liquidityIndex; - cachedData.oldVariableBorrowIndex = reserveData.variableBorrowIndex; - cachedData.oldLiquidityRate = reserveData.currentLiquidityRate; - cachedData.oldVariableBorrowRate = reserveData.currentVariableBorrowRate; - - cachedData.aTokenAddress = reserveData.aTokenAddress; - cachedData.stableDebtTokenAddress = reserveData.stableDebtTokenAddress; - cachedData.variableDebtTokenAddress = reserveData.variableDebtTokenAddress; - - cachedData.reserveLastUpdateTimestamp = reserveData.lastUpdateTimestamp; - - cachedData.oldScaledVariableDebt = cachedData.newScaledVariableDebt = IVariableDebtToken( - cachedData - .variableDebtTokenAddress - ) - .scaledTotalSupply(); - - ( - cachedData.oldPrincipalStableDebt, - cachedData.oldTotalStableDebt, - cachedData.oldAvgStableBorrowRate, - cachedData.stableDebtLastUpdateTimestamp - ) = IStableDebtToken(cachedData.stableDebtTokenAddress).getSupplyData(); - - cachedData.newPrincipalStableDebt = cachedData.oldPrincipalStableDebt; - cachedData.newTotalStableDebt = cachedData.oldTotalStableDebt; - cachedData.newAvgStableBorrowRate = cachedData.oldAvgStableBorrowRate; - - return cachedData; - } -} diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index ac5ff510..2db7fe34 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -14,7 +14,6 @@ import {WadRayMath} from '../math/WadRayMath.sol'; import {PercentageMath} from '../math/PercentageMath.sol'; import {Errors} from '../helpers/Errors.sol'; import {DataTypes} from '../types/DataTypes.sol'; -import {CachingHelper} from '../helpers/CachingHelper.sol'; /** * @title ReserveLogic library @@ -110,11 +109,10 @@ library ReserveLogic { **/ function updateState( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { - _updateIndexes(reserve, cachedData); - - _accrueToTreasury(reserve, cachedData); + _updateIndexes(reserve, reserveCache); + _accrueToTreasury(reserve, reserveCache); } /** @@ -181,20 +179,20 @@ library ReserveLogic { **/ function updateInterestRates( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, address reserveAddress, uint256 liquidityAdded, uint256 liquidityTaken ) internal { UpdateInterestRatesLocalVars memory vars; - if (cachedData.oldTotalStableDebt != cachedData.newTotalStableDebt) { - cachedData.newAvgStableBorrowRate = IStableDebtToken(cachedData.stableDebtTokenAddress) + if (reserveCache.oldTotalStableDebt != reserveCache.newTotalStableDebt) { + reserveCache.newAvgStableBorrowRate = IStableDebtToken(reserveCache.stableDebtTokenAddress) .getAverageStableRate(); } - cachedData.newTotalVariableDebt = cachedData.newScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + reserveCache.newTotalVariableDebt = reserveCache.newScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); ( @@ -203,13 +201,13 @@ library ReserveLogic { vars.newVariableRate ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( reserveAddress, - cachedData.aTokenAddress, + reserveCache.aTokenAddress, liquidityAdded, liquidityTaken, - cachedData.newTotalStableDebt, - cachedData.newTotalVariableDebt, - cachedData.newAvgStableBorrowRate, - cachedData.reserveConfiguration.getReserveFactorMemory() + reserveCache.newTotalStableDebt, + reserveCache.newTotalVariableDebt, + reserveCache.newAvgStableBorrowRate, + reserveCache.reserveConfiguration.getReserveFactorMemory() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); @@ -224,8 +222,8 @@ library ReserveLogic { vars.newLiquidityRate, vars.newStableRate, vars.newVariableRate, - cachedData.newLiquidityIndex, - cachedData.newVariableBorrowIndex + reserveCache.newLiquidityIndex, + reserveCache.newVariableBorrowIndex ); } @@ -247,43 +245,45 @@ library ReserveLogic { * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the * specific asset. * @param reserve The reserve reserve to be updated - * @param cachedData The caching layer for the reserve data + * @param reserveCache The caching layer for the reserve data **/ function _accrueToTreasury( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { MintToTreasuryLocalVars memory vars; - vars.reserveFactor = cachedData.reserveConfiguration.getReserveFactorMemory(); + vars.reserveFactor = reserveCache.reserveConfiguration.getReserveFactorMemory(); if (vars.reserveFactor == 0) { return; } //calculate the last principal variable debt - vars.previousVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.oldVariableBorrowIndex + vars.previousVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.oldVariableBorrowIndex ); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + vars.currentVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( - cachedData.oldAvgStableBorrowRate, - cachedData.stableDebtLastUpdateTimestamp, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldAvgStableBorrowRate, + reserveCache.stableDebtLastUpdateTimestamp, + reserveCache.reserveLastUpdateTimestamp ); - vars.previousStableDebt = cachedData.oldPrincipalStableDebt.rayMul(vars.cumulatedStableInterest); + vars.previousStableDebt = reserveCache.oldPrincipalStableDebt.rayMul( + vars.cumulatedStableInterest + ); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars .currentVariableDebt - .add(cachedData.oldTotalStableDebt) + .add(reserveCache.oldTotalStableDebt) .sub(vars.previousVariableDebt) .sub(vars.previousStableDebt); @@ -291,7 +291,7 @@ library ReserveLogic { if (vars.amountToMint != 0) { reserve.accruedToTreasury = reserve.accruedToTreasury.add( - vars.amountToMint.rayDiv(cachedData.newLiquidityIndex) + vars.amountToMint.rayDiv(reserveCache.newLiquidityIndex) ); } } @@ -299,52 +299,90 @@ library ReserveLogic { /** * @dev Updates the reserve indexes and the timestamp of the update * @param reserve The reserve reserve to be updated - * @param cachedData The cache layer holding the cached protocol data + * @param reserveCache The cache layer holding the cached protocol data **/ function _updateIndexes( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData + DataTypes.ReserveCache memory reserveCache ) internal { - cachedData.newLiquidityIndex = cachedData.oldLiquidityIndex; - cachedData.newVariableBorrowIndex = cachedData.oldVariableBorrowIndex; + reserveCache.newLiquidityIndex = reserveCache.oldLiquidityIndex; + reserveCache.newVariableBorrowIndex = reserveCache.oldVariableBorrowIndex; //only cumulating if there is any income being produced - if (cachedData.oldLiquidityRate > 0) { + if (reserveCache.oldLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest( - cachedData.oldLiquidityRate, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldLiquidityRate, + reserveCache.reserveLastUpdateTimestamp ); - cachedData.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( - cachedData.oldLiquidityIndex + reserveCache.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( + reserveCache.oldLiquidityIndex ); require( - cachedData.newLiquidityIndex <= type(uint128).max, + reserveCache.newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW ); - - reserve.liquidityIndex = uint128(cachedData.newLiquidityIndex); + reserve.liquidityIndex = uint128(reserveCache.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 (cachedData.oldScaledVariableDebt != 0) { + if (reserveCache.oldScaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( - cachedData.oldVariableBorrowRate, - cachedData.reserveLastUpdateTimestamp + reserveCache.oldVariableBorrowRate, + reserveCache.reserveLastUpdateTimestamp ); - cachedData.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( - cachedData.oldVariableBorrowIndex + reserveCache.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( + reserveCache.oldVariableBorrowIndex ); require( - cachedData.newVariableBorrowIndex <= type(uint128).max, + reserveCache.newVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW ); - reserve.variableBorrowIndex = uint128(cachedData.newVariableBorrowIndex); + reserve.variableBorrowIndex = uint128(reserveCache.newVariableBorrowIndex); } } //solium-disable-next-line reserve.lastUpdateTimestamp = uint40(block.timestamp); } + + function cache(DataTypes.ReserveData storage reserve) + internal + view + returns (DataTypes.ReserveCache memory) + { + DataTypes.ReserveCache memory reserveCache; + + reserveCache.reserveConfiguration = reserve.configuration; + reserveCache.oldLiquidityIndex = reserve.liquidityIndex; + reserveCache.oldVariableBorrowIndex = reserve.variableBorrowIndex; + reserveCache.oldLiquidityRate = reserve.currentLiquidityRate; + reserveCache.oldVariableBorrowRate = reserve.currentVariableBorrowRate; + + reserveCache.aTokenAddress = reserve.aTokenAddress; + reserveCache.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + reserveCache.variableDebtTokenAddress = reserve.variableDebtTokenAddress; + + reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp; + + reserveCache.oldScaledVariableDebt = reserveCache.newScaledVariableDebt = IVariableDebtToken( + reserveCache + .variableDebtTokenAddress + ) + .scaledTotalSupply(); + + ( + reserveCache.oldPrincipalStableDebt, + reserveCache.oldTotalStableDebt, + reserveCache.oldAvgStableBorrowRate, + reserveCache.stableDebtLastUpdateTimestamp + ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData(); + + reserveCache.newPrincipalStableDebt = reserveCache.oldPrincipalStableDebt; + reserveCache.newTotalStableDebt = reserveCache.oldTotalStableDebt; + reserveCache.newAvgStableBorrowRate = reserveCache.oldAvgStableBorrowRate; + + return reserveCache; + } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 94cc3248..43629f1f 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -12,7 +12,6 @@ import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20. import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../configuration/UserConfiguration.sol'; import {Errors} from '../helpers/Errors.sol'; -import {CachingHelper} from '../helpers/CachingHelper.sol'; import {Helpers} from '../helpers/Helpers.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; @@ -45,13 +44,13 @@ library ValidationLogic { */ function validateDeposit( DataTypes.ReserveData storage reserve, - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, uint256 amount ) internal view { (bool isActive, bool isFrozen, , , bool isPaused) = - cachedData.reserveConfiguration.getFlagsMemory(); - (, , , uint256 reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); - uint256 supplyCap = cachedData.reserveConfiguration.getSupplyCapMemory(); + reserveCache.reserveConfiguration.getFlagsMemory(); + (, , , uint256 reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory(); + uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCapMemory(); require(amount != 0, Errors.VL_INVALID_AMOUNT); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); @@ -59,9 +58,9 @@ library ValidationLogic { require(!isFrozen, Errors.VL_RESERVE_FROZEN); require( supplyCap == 0 || - IAToken(cachedData.aTokenAddress) + IAToken(reserveCache.aTokenAddress) .scaledTotalSupply() - .rayMul(cachedData.newLiquidityIndex) + .rayMul(reserveCache.newLiquidityIndex) .add(amount) .div(10**reserveDecimals) < supplyCap, @@ -77,13 +76,14 @@ library ValidationLogic { */ function validateWithdraw( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, uint256 amount, uint256 userBalance ) external view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); } @@ -122,7 +122,7 @@ library ValidationLogic { */ function validateBorrow( - CachingHelper.CachedData memory cachedData, + DataTypes.ReserveCache memory reserveCache, address asset, address userAddress, uint256 amount, @@ -136,7 +136,7 @@ library ValidationLogic { ) external view { ValidateBorrowLocalVars memory vars; - (, , , vars.reserveDecimals, ) = cachedData.reserveConfiguration.getParamsMemory(); + (, , , vars.reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory(); ( vars.isActive, @@ -144,7 +144,7 @@ library ValidationLogic { vars.borrowingEnabled, vars.stableRateBorrowingEnabled, vars.isPaused - ) = cachedData.reserveConfiguration.getFlagsMemory(); + ) = reserveCache.reserveConfiguration.getFlagsMemory(); require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!vars.isPaused, Errors.VL_RESERVE_PAUSED); @@ -160,15 +160,15 @@ library ValidationLogic { Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED ); - vars.borrowCap = cachedData.reserveConfiguration.getBorrowCapMemory(); + vars.borrowCap = reserveCache.reserveConfiguration.getBorrowCapMemory(); if (vars.borrowCap > 0) { { - vars.totalSupplyVariableDebt = cachedData.oldScaledVariableDebt.rayMul( - cachedData.newVariableBorrowIndex + vars.totalSupplyVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( + reserveCache.newVariableBorrowIndex ); - vars.totalDebt = cachedData.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( + vars.totalDebt = reserveCache.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( amount ); require( @@ -228,12 +228,12 @@ library ValidationLogic { require( !userConfig.isUsingAsCollateral(reservesData[asset].id) || - cachedData.reserveConfiguration.getLtvMemory() == 0 || - amount > IERC20(cachedData.aTokenAddress).balanceOf(userAddress), + reserveCache.reserveConfiguration.getLtvMemory() == 0 || + amount > IERC20(reserveCache.aTokenAddress).balanceOf(userAddress), Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY ); - vars.availableLiquidity = IERC20(asset).balanceOf(cachedData.aTokenAddress); + vars.availableLiquidity = IERC20(asset).balanceOf(reserveCache.aTokenAddress); //calculate the max available loan size in stable rate mode as a percentage of the //available liquidity @@ -253,13 +253,14 @@ library ValidationLogic { */ function validateRepay( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, uint256 amountSent, DataTypes.InterestRateMode rateMode, address onBehalfOf, uint256 stableDebt, uint256 variableDebt ) external view { - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -289,13 +290,14 @@ library ValidationLogic { */ function validateSwapRateMode( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, DataTypes.UserConfigurationMap storage userConfig, uint256 stableDebt, uint256 variableDebt, DataTypes.InterestRateMode currentRateMode ) external view { (bool isActive, bool isFrozen, , bool stableRateEnabled, bool isPaused) = - reserve.configuration.getFlags(); + reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -316,8 +318,8 @@ library ValidationLogic { require( !userConfig.isUsingAsCollateral(reserve.id) || - reserve.configuration.getLtv() == 0 || - stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), + reserveCache.reserveConfiguration.getLtvMemory() == 0 || + stableDebt.add(variableDebt) > IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender), Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY ); } else { @@ -335,12 +337,13 @@ library ValidationLogic { */ function validateRebalanceStableBorrowRate( DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache, address reserveAddress, IERC20 stableDebtToken, IERC20 variableDebtToken, address aTokenAddress ) external view { - (bool isActive, , , , bool isPaused) = reserve.configuration.getFlags(); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -354,7 +357,7 @@ library ValidationLogic { //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, //then we allow rebalancing of the stable rate positions. - uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 currentLiquidityRate = reserveCache.oldLiquidityRate; uint256 maxVariableBorrowRate = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); @@ -370,10 +373,14 @@ library ValidationLogic { * @dev Validates the action of setting an asset as collateral * @param reserve The state of the reserve that the user is enabling or disabling as collateral */ - function validateSetUseReserveAsCollateral(DataTypes.ReserveData storage reserve) external view { - uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); - bool isPaused = reserve.configuration.getPaused(); + function validateSetUseReserveAsCollateral( + DataTypes.ReserveData storage reserve, + DataTypes.ReserveCache memory reserveCache + ) external view { + uint256 underlyingBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); + (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); @@ -407,20 +414,26 @@ library ValidationLogic { function validateLiquidationCall( DataTypes.ReserveData storage collateralReserve, DataTypes.ReserveData storage principalReserve, + DataTypes.ReserveCache memory collateralReserveCache, + DataTypes.ReserveCache memory principalReserveCache, DataTypes.UserConfigurationMap storage userConfig, uint256 userHealthFactor, uint256 userStableDebt, uint256 userVariableDebt ) internal view returns (uint256, string memory) { - if ( - !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive() - ) { + (bool collateralReserveActive, , , , bool collateralReservePaused) = + collateralReserveCache.reserveConfiguration.getFlagsMemory(); + + (bool principalReserveActive, , , , bool principalReservePaused) = + collateralReserveCache.reserveConfiguration.getFlagsMemory(); + + if (!collateralReserveActive || !principalReserveActive) { return ( uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.VL_NO_ACTIVE_RESERVE ); } - if (collateralReserve.configuration.getPaused() || principalReserve.configuration.getPaused()) { + if (collateralReservePaused || principalReservePaused) { return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); } @@ -432,7 +445,7 @@ library ValidationLogic { } bool isCollateralEnabled = - collateralReserve.configuration.getLiquidationThreshold() > 0 && + collateralReserveCache.reserveConfiguration.getLiquidationThresholdMemory() > 0 && userConfig.isUsingAsCollateral(collateralReserve.id); //if collateral isn't enabled as collateral by user, it cannot be liquidated diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 5daa68b1..fd05fa89 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -51,4 +51,29 @@ library DataTypes { } enum InterestRateMode {NONE, STABLE, VARIABLE} + + struct ReserveCache { + uint256 oldScaledVariableDebt; + uint256 oldTotalVariableDebt; + uint256 newScaledVariableDebt; + uint256 newTotalVariableDebt; + uint256 oldPrincipalStableDebt; + uint256 oldAvgStableBorrowRate; + uint256 oldTotalStableDebt; + uint256 newPrincipalStableDebt; + uint256 newAvgStableBorrowRate; + uint256 newTotalStableDebt; + uint256 oldLiquidityIndex; + uint256 newLiquidityIndex; + uint256 oldVariableBorrowIndex; + uint256 newVariableBorrowIndex; + uint256 oldLiquidityRate; + uint256 oldVariableBorrowRate; + DataTypes.ReserveConfigurationMap reserveConfiguration; + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + uint40 reserveLastUpdateTimestamp; + uint40 stableDebtLastUpdateTimestamp; + } } diff --git a/package-lock.json b/package-lock.json index ad482fe2..7f3c76fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11142,14 +11142,6 @@ "requires": { "min-document": "^2.19.0", "process": "^0.11.10" - }, - "dependencies": { - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true - } } }, "got": { @@ -12794,6 +12786,12 @@ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", From b8fb9e592fcfaee4f38aead0135f46df10658e02 Mon Sep 17 00:00:00 2001 From: emilio Date: Tue, 8 Jun 2021 10:44:16 +0200 Subject: [PATCH 35/64] refactor: updated cache field names --- .../protocol/lendingpool/LendingPool.sol | 46 +++++----- .../LendingPoolCollateralManager.sol | 18 ++-- .../protocol/libraries/logic/ReserveLogic.sol | 84 +++++++++---------- .../libraries/logic/ValidationLogic.sol | 14 ++-- .../protocol/libraries/types/DataTypes.sol | 31 ++++--- 5 files changed, 96 insertions(+), 97 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 1c5f4208..24a9ae4b 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -289,27 +289,27 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); - reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache - .oldTotalStableDebt + reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache + .currTotalStableDebt .sub(stableDebt); IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, - reserveCache.newVariableBorrowIndex + reserveCache.nextVariableBorrowIndex ); - reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.add( - stableDebt.rayDiv(reserveCache.newVariableBorrowIndex) + reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.add( + stableDebt.rayDiv(reserveCache.nextVariableBorrowIndex) ); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( msg.sender, variableDebt, - reserveCache.newVariableBorrowIndex + reserveCache.nextVariableBorrowIndex ); - reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( - variableDebt.rayDiv(reserveCache.newVariableBorrowIndex) + reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.sub( + variableDebt.rayDiv(reserveCache.nextVariableBorrowIndex) ); IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( @@ -319,8 +319,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.currentStableBorrowRate ); - reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache - .oldTotalStableDebt + reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache + .currTotalStableDebt .add(stableDebt); } @@ -927,8 +927,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage currentStableRate ); - reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache - .oldTotalStableDebt + reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache + .currTotalStableDebt .add(vars.amount); } else { @@ -936,11 +936,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.user, vars.onBehalfOf, vars.amount, - reserveCache.newVariableBorrowIndex + reserveCache.nextVariableBorrowIndex ); - reserveCache.newScaledVariableDebt = reserveCache.newScaledVariableDebt.add( - vars.amount.rayDiv(reserveCache.newVariableBorrowIndex) + reserveCache.nextScaledVariableDebt = reserveCache.nextScaledVariableDebt.add( + vars.amount.rayDiv(reserveCache.nextVariableBorrowIndex) ); } @@ -990,7 +990,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IERC20(asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, amount); bool isFirstDeposit = - IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.newLiquidityIndex); + IAToken(reserveCache.aTokenAddress).mint(onBehalfOf, amount, reserveCache.nextLiquidityIndex); if (isFirstDeposit) { _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); @@ -1013,7 +1013,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint256 userBalance = IAToken(reserveCache.aTokenAddress).scaledBalanceOf(msg.sender).rayMul( - reserveCache.newLiquidityIndex + reserveCache.nextLiquidityIndex ); uint256 amountToWithdraw = amount; @@ -1030,7 +1030,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage msg.sender, to, amountToWithdraw, - reserveCache.newLiquidityIndex + reserveCache.nextLiquidityIndex ); if (userConfig.isUsingAsCollateral(reserve.id)) { @@ -1090,17 +1090,17 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); - reserveCache.newPrincipalStableDebt = reserveCache.newTotalStableDebt = reserveCache - .oldTotalStableDebt + reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache + .currTotalStableDebt .sub(paybackAmount); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, - reserveCache.newVariableBorrowIndex + reserveCache.nextVariableBorrowIndex ); - reserveCache.newScaledVariableDebt = reserveCache.oldScaledVariableDebt.sub( - paybackAmount.rayDiv(reserveCache.newVariableBorrowIndex) + reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.sub( + paybackAmount.rayDiv(reserveCache.nextVariableBorrowIndex) ); } diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 2f6b0bcb..19908705 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -171,10 +171,10 @@ contract LendingPoolCollateralManager is IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate, - debtReserveCache.newVariableBorrowIndex + debtReserveCache.nextVariableBorrowIndex ); - debtReserveCache.newScaledVariableDebt = debtReserveCache.oldScaledVariableDebt.sub( - vars.actualDebtToLiquidate.rayDiv(debtReserveCache.newVariableBorrowIndex) + debtReserveCache.nextScaledVariableDebt = debtReserveCache.currScaledVariableDebt.sub( + vars.actualDebtToLiquidate.rayDiv(debtReserveCache.nextVariableBorrowIndex) ); } else { // If the user doesn't have variable debt, no need to try to burn variable debt tokens @@ -182,19 +182,19 @@ contract LendingPoolCollateralManager is IVariableDebtToken(debtReserveCache.variableDebtTokenAddress).burn( user, vars.userVariableDebt, - debtReserveCache.newVariableBorrowIndex + debtReserveCache.nextVariableBorrowIndex ); - debtReserveCache.newScaledVariableDebt = debtReserveCache - .oldScaledVariableDebt - .sub(vars.userVariableDebt.rayDiv(debtReserveCache.newVariableBorrowIndex)); + debtReserveCache.nextScaledVariableDebt = debtReserveCache + .currScaledVariableDebt + .sub(vars.userVariableDebt.rayDiv(debtReserveCache.nextVariableBorrowIndex)); } IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); - debtReserveCache.newPrincipalStableDebt = debtReserveCache - .newTotalStableDebt = debtReserveCache.oldTotalStableDebt.sub( + debtReserveCache.nextPrincipalStableDebt = debtReserveCache + .nextTotalStableDebt = debtReserveCache.currTotalStableDebt.sub( vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); } diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2db7fe34..1db225ac 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -186,13 +186,13 @@ library ReserveLogic { ) internal { UpdateInterestRatesLocalVars memory vars; - if (reserveCache.oldTotalStableDebt != reserveCache.newTotalStableDebt) { - reserveCache.newAvgStableBorrowRate = IStableDebtToken(reserveCache.stableDebtTokenAddress) + if (reserveCache.currTotalStableDebt != reserveCache.nextTotalStableDebt) { + reserveCache.nextAvgStableBorrowRate = IStableDebtToken(reserveCache.stableDebtTokenAddress) .getAverageStableRate(); } - reserveCache.newTotalVariableDebt = reserveCache.newScaledVariableDebt.rayMul( - reserveCache.newVariableBorrowIndex + reserveCache.nextTotalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( + reserveCache.nextVariableBorrowIndex ); ( @@ -204,9 +204,9 @@ library ReserveLogic { reserveCache.aTokenAddress, liquidityAdded, liquidityTaken, - reserveCache.newTotalStableDebt, - reserveCache.newTotalVariableDebt, - reserveCache.newAvgStableBorrowRate, + reserveCache.nextTotalStableDebt, + reserveCache.nextTotalVariableDebt, + reserveCache.nextAvgStableBorrowRate, reserveCache.reserveConfiguration.getReserveFactorMemory() ); require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); @@ -222,8 +222,8 @@ library ReserveLogic { vars.newLiquidityRate, vars.newStableRate, vars.newVariableRate, - reserveCache.newLiquidityIndex, - reserveCache.newVariableBorrowIndex + reserveCache.nextLiquidityIndex, + reserveCache.nextVariableBorrowIndex ); } @@ -260,30 +260,30 @@ library ReserveLogic { } //calculate the last principal variable debt - vars.previousVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( - reserveCache.oldVariableBorrowIndex + vars.previousVariableDebt = reserveCache.currScaledVariableDebt.rayMul( + reserveCache.currVariableBorrowIndex ); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( - reserveCache.newVariableBorrowIndex + vars.currentVariableDebt = reserveCache.currScaledVariableDebt.rayMul( + reserveCache.nextVariableBorrowIndex ); //calculate the stable debt until the last timestamp update vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( - reserveCache.oldAvgStableBorrowRate, + reserveCache.currAvgStableBorrowRate, reserveCache.stableDebtLastUpdateTimestamp, reserveCache.reserveLastUpdateTimestamp ); - vars.previousStableDebt = reserveCache.oldPrincipalStableDebt.rayMul( + vars.previousStableDebt = reserveCache.currPrincipalStableDebt.rayMul( vars.cumulatedStableInterest ); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars .currentVariableDebt - .add(reserveCache.oldTotalStableDebt) + .add(reserveCache.currTotalStableDebt) .sub(vars.previousVariableDebt) .sub(vars.previousStableDebt); @@ -291,7 +291,7 @@ library ReserveLogic { if (vars.amountToMint != 0) { reserve.accruedToTreasury = reserve.accruedToTreasury.add( - vars.amountToMint.rayDiv(reserveCache.newLiquidityIndex) + vars.amountToMint.rayDiv(reserveCache.nextLiquidityIndex) ); } } @@ -305,41 +305,41 @@ library ReserveLogic { DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache ) internal { - reserveCache.newLiquidityIndex = reserveCache.oldLiquidityIndex; - reserveCache.newVariableBorrowIndex = reserveCache.oldVariableBorrowIndex; + reserveCache.nextLiquidityIndex = reserveCache.currLiquidityIndex; + reserveCache.nextVariableBorrowIndex = reserveCache.currVariableBorrowIndex; //only cumulating if there is any income being produced - if (reserveCache.oldLiquidityRate > 0) { + if (reserveCache.currLiquidityRate > 0) { uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest( - reserveCache.oldLiquidityRate, + reserveCache.currLiquidityRate, reserveCache.reserveLastUpdateTimestamp ); - reserveCache.newLiquidityIndex = cumulatedLiquidityInterest.rayMul( - reserveCache.oldLiquidityIndex + reserveCache.nextLiquidityIndex = cumulatedLiquidityInterest.rayMul( + reserveCache.currLiquidityIndex ); require( - reserveCache.newLiquidityIndex <= type(uint128).max, + reserveCache.nextLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW ); - reserve.liquidityIndex = uint128(reserveCache.newLiquidityIndex); + reserve.liquidityIndex = uint128(reserveCache.nextLiquidityIndex); //as the liquidity rate might come only from stable rate loans, we need to ensure //that there is actual variable debt before accumulating - if (reserveCache.oldScaledVariableDebt != 0) { + if (reserveCache.currScaledVariableDebt != 0) { uint256 cumulatedVariableBorrowInterest = MathUtils.calculateCompoundedInterest( - reserveCache.oldVariableBorrowRate, + reserveCache.currVariableBorrowRate, reserveCache.reserveLastUpdateTimestamp ); - reserveCache.newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( - reserveCache.oldVariableBorrowIndex + reserveCache.nextVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul( + reserveCache.currVariableBorrowIndex ); require( - reserveCache.newVariableBorrowIndex <= type(uint128).max, + reserveCache.nextVariableBorrowIndex <= type(uint128).max, Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW ); - reserve.variableBorrowIndex = uint128(reserveCache.newVariableBorrowIndex); + reserve.variableBorrowIndex = uint128(reserveCache.nextVariableBorrowIndex); } } @@ -355,10 +355,10 @@ library ReserveLogic { DataTypes.ReserveCache memory reserveCache; reserveCache.reserveConfiguration = reserve.configuration; - reserveCache.oldLiquidityIndex = reserve.liquidityIndex; - reserveCache.oldVariableBorrowIndex = reserve.variableBorrowIndex; - reserveCache.oldLiquidityRate = reserve.currentLiquidityRate; - reserveCache.oldVariableBorrowRate = reserve.currentVariableBorrowRate; + reserveCache.currLiquidityIndex = reserve.liquidityIndex; + reserveCache.currVariableBorrowIndex = reserve.variableBorrowIndex; + reserveCache.currLiquidityRate = reserve.currentLiquidityRate; + reserveCache.currVariableBorrowRate = reserve.currentVariableBorrowRate; reserveCache.aTokenAddress = reserve.aTokenAddress; reserveCache.stableDebtTokenAddress = reserve.stableDebtTokenAddress; @@ -366,22 +366,22 @@ library ReserveLogic { reserveCache.reserveLastUpdateTimestamp = reserve.lastUpdateTimestamp; - reserveCache.oldScaledVariableDebt = reserveCache.newScaledVariableDebt = IVariableDebtToken( + reserveCache.currScaledVariableDebt = reserveCache.nextScaledVariableDebt = IVariableDebtToken( reserveCache .variableDebtTokenAddress ) .scaledTotalSupply(); ( - reserveCache.oldPrincipalStableDebt, - reserveCache.oldTotalStableDebt, - reserveCache.oldAvgStableBorrowRate, + reserveCache.currPrincipalStableDebt, + reserveCache.currTotalStableDebt, + reserveCache.currAvgStableBorrowRate, reserveCache.stableDebtLastUpdateTimestamp ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData(); - reserveCache.newPrincipalStableDebt = reserveCache.oldPrincipalStableDebt; - reserveCache.newTotalStableDebt = reserveCache.oldTotalStableDebt; - reserveCache.newAvgStableBorrowRate = reserveCache.oldAvgStableBorrowRate; + reserveCache.nextPrincipalStableDebt = reserveCache.currPrincipalStableDebt; + reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt; + reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate; return reserveCache; } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 43629f1f..37f0c6a8 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -60,7 +60,7 @@ library ValidationLogic { supplyCap == 0 || IAToken(reserveCache.aTokenAddress) .scaledTotalSupply() - .rayMul(reserveCache.newLiquidityIndex) + .rayMul(reserveCache.nextLiquidityIndex) .add(amount) .div(10**reserveDecimals) < supplyCap, @@ -79,7 +79,7 @@ library ValidationLogic { DataTypes.ReserveCache memory reserveCache, uint256 amount, uint256 userBalance - ) external view { + ) internal view { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); @@ -162,13 +162,13 @@ library ValidationLogic { vars.borrowCap = reserveCache.reserveConfiguration.getBorrowCapMemory(); - if (vars.borrowCap > 0) { + if (vars.borrowCap != 0) { { - vars.totalSupplyVariableDebt = reserveCache.oldScaledVariableDebt.rayMul( - reserveCache.newVariableBorrowIndex + vars.totalSupplyVariableDebt = reserveCache.currScaledVariableDebt.rayMul( + reserveCache.nextVariableBorrowIndex ); - vars.totalDebt = reserveCache.oldTotalStableDebt.add(vars.totalSupplyVariableDebt).add( + vars.totalDebt = reserveCache.currTotalStableDebt.add(vars.totalSupplyVariableDebt).add( amount ); require( @@ -357,7 +357,7 @@ library ValidationLogic { //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, //then we allow rebalancing of the stable rate positions. - uint256 currentLiquidityRate = reserveCache.oldLiquidityRate; + uint256 currentLiquidityRate = reserveCache.currLiquidityRate; uint256 maxVariableBorrowRate = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index fd05fa89..4a2ce981 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -53,22 +53,21 @@ library DataTypes { enum InterestRateMode {NONE, STABLE, VARIABLE} struct ReserveCache { - uint256 oldScaledVariableDebt; - uint256 oldTotalVariableDebt; - uint256 newScaledVariableDebt; - uint256 newTotalVariableDebt; - uint256 oldPrincipalStableDebt; - uint256 oldAvgStableBorrowRate; - uint256 oldTotalStableDebt; - uint256 newPrincipalStableDebt; - uint256 newAvgStableBorrowRate; - uint256 newTotalStableDebt; - uint256 oldLiquidityIndex; - uint256 newLiquidityIndex; - uint256 oldVariableBorrowIndex; - uint256 newVariableBorrowIndex; - uint256 oldLiquidityRate; - uint256 oldVariableBorrowRate; + uint256 currScaledVariableDebt; + uint256 nextScaledVariableDebt; + uint256 nextTotalVariableDebt; + uint256 currPrincipalStableDebt; + uint256 currAvgStableBorrowRate; + uint256 currTotalStableDebt; + uint256 nextPrincipalStableDebt; + uint256 nextAvgStableBorrowRate; + uint256 nextTotalStableDebt; + uint256 currLiquidityIndex; + uint256 nextLiquidityIndex; + uint256 currVariableBorrowIndex; + uint256 nextVariableBorrowIndex; + uint256 currLiquidityRate; + uint256 currVariableBorrowRate; DataTypes.ReserveConfigurationMap reserveConfiguration; address aTokenAddress; address stableDebtTokenAddress; From e2edf016eb8d8f7d360bb5b38d7c058a71dd9ec6 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 26 May 2021 20:11:48 +0200 Subject: [PATCH 36/64] feat: added drop reserve main capability --- contracts/interfaces/ILendingPool.sol | 15 +++-- .../protocol/lendingpool/LendingPool.sol | 56 +++++++++++++++---- .../lendingpool/LendingPoolConfigurator.sol | 5 ++ .../protocol/libraries/helpers/Errors.sol | 3 + .../protocol/libraries/logic/ReserveLogic.sol | 21 ++++++- test-suites/test-aave/__setup.spec.ts | 1 + 6 files changed, 80 insertions(+), 21 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index 4441d4d4..bcebbde8 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -168,14 +168,11 @@ interface ILendingPool { ); /** - * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. - * @param reserve the address of the reserve - * @param amountMinted the amount minted to the treasury - **/ - event MintedToTreasury( - address indexed reserve, - uint256 amountMinted - ); + * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. + * @param reserve the address of the reserve + * @param amountMinted the amount minted to the treasury + **/ + event MintedToTreasury(address indexed reserve, uint256 amountMinted); /** * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. @@ -406,6 +403,8 @@ interface ILendingPool { address interestRateStrategyAddress ) external; + function dropReserve(address reserve) external; + function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) external; diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 479c9298..9fb2bcfb 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -545,11 +545,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function mintToTreasury(address[] calldata reserves) public { for (uint256 i = 0; i < reserves.length; i++) { address reserveAddress = reserves[i]; - + DataTypes.ReserveData storage reserve = _reserves[reserveAddress]; // this cover both inactive reserves and invalid reserves since the flag will be 0 for both - if(!reserve.configuration.getActive()){ + if (!reserve.configuration.getActive()) { continue; } @@ -690,15 +690,29 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } /** - * @dev Returns the list of the initialized reserves + * @dev Returns the list of the initialized reserves, does not contain dropped reserves **/ function getReservesList() external view override returns (address[] memory) { - address[] memory _activeReserves = new address[](_reservesCount); + uint256 reserveListCount = _reservesCount; + uint256 droppedReservesCount = 0; + address[] memory reserves = new address[](reserveListCount); - for (uint256 i = 0; i < _reservesCount; i++) { - _activeReserves[i] = _reservesList[i]; + for (uint256 i = 0; i < reserveListCount; i++) { + if (_reservesList[i] != address(0)) { + reserves[i - droppedReservesCount] = _reservesList[i]; + } else { + droppedReservesCount++; + } } - return _activeReserves; + + if (droppedReservesCount == 0) return reserves; + + address[] memory undroppedReserves = new address[](reserveListCount - droppedReservesCount); + for (uint256 i = 0; i < reserveListCount - droppedReservesCount; i++) { + undroppedReserves[i] = reserves[i]; + } + + return undroppedReserves; } /** @@ -808,6 +822,16 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage _addReserveToList(asset); } + /** + * @dev Drop a reserve + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + **/ + function dropReserve(address asset) external override onlyLendingPoolConfigurator { + _reserves[asset].dropReserve(); + _removeReserveFromList(asset); + } + /** * @dev Updates the address of the interest rate strategy contract * - Only callable by the LendingPoolConfigurator contract @@ -1084,7 +1108,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage return paybackAmount; } - function _addReserveToList(address asset) internal { + function _addReserveToList(address asset) internal returns (uint8) { uint256 reservesCount = _reservesCount; require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); @@ -1092,10 +1116,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; if (!reserveAlreadyAdded) { - _reserves[asset].id = uint8(reservesCount); - _reservesList[reservesCount] = asset; - - _reservesCount = reservesCount + 1; + for (uint8 i = 0; i <= reservesCount; i++) { + if (_reservesList[i] == address(0)) { + _reserves[asset].id = i; + _reservesList[i] = asset; + _reservesCount = reservesCount + 1; + return i; + } + } } } + + function _removeReserveFromList(address asset) internal { + _reservesList[_reserves[asset].id] = address(0); + } } diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 2730937f..c3c8e2e9 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -159,6 +159,11 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur ); } + /// @inheritdoc ILendingPoolConfigurator + function dropReserve(address asset) external onlyPoolAdmin { + _pool.dropReserve(asset); + } + /// @inheritdoc ILendingPoolConfigurator function updateAToken(UpdateATokenInput calldata input) external override onlyPoolAdmin { ILendingPool cachedPool = _pool; diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 8a2e7653..1b984270 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -109,6 +109,9 @@ library Errors { string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; string public constant VL_RESERVE_PAUSED = '86'; string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; + string public constant RL_ATOKEN_SUPPLY_NOT_NULL = '88'; + string public constant RL_STABLE_DEBT_NOT_NULL = '89'; + string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_NULL = '90'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 2e9850c9..81fe56fc 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -178,6 +178,23 @@ library ReserveLogic { reserve.interestRateStrategyAddress = interestRateStrategyAddress; } + /** + * @dev drop a reserve + * @param reserve The reserve object + **/ + function dropReserve(DataTypes.ReserveData storage reserve) external { + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require( + IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, + Errors.RL_STABLE_DEBT_NOT_NULL + ); + require( + IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + ); + reserve.id = type(uint8).max; + } + struct UpdateInterestRatesLocalVars { address stableDebtTokenAddress; uint256 availableLiquidity; @@ -320,7 +337,9 @@ library ReserveLogic { vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); if (vars.amountToMint != 0) { - reserve.accruedToTreasury = reserve.accruedToTreasury.add(vars.amountToMint.rayDiv(newLiquidityIndex)); + reserve.accruedToTreasury = reserve.accruedToTreasury.add( + vars.amountToMint.rayDiv(newLiquidityIndex) + ); } } diff --git a/test-suites/test-aave/__setup.spec.ts b/test-suites/test-aave/__setup.spec.ts index dd8ed168..2ef2f3a0 100644 --- a/test-suites/test-aave/__setup.spec.ts +++ b/test-suites/test-aave/__setup.spec.ts @@ -265,6 +265,7 @@ const buildTestEnv = async (deployer: Signer, secondaryWallet: Signer) => { ); await configureReservesByHelper(reservesParams, allReservesAddresses, testHelpers, admin); + lendingPoolConfiguratorProxy.dropReserve(mockTokens.KNC.address); const collateralManager = await deployLendingPoolCollateralManager(); await waitForTx( From 776220d705a63c630392f82f2ffb5201c5cfa017 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 27 May 2021 08:02:41 +0200 Subject: [PATCH 37/64] feat: removed user updates on droped reserves --- contracts/protocol/libraries/logic/GenericLogic.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 381fe804..549328a8 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -93,6 +93,9 @@ library GenericLogic { } vars.currentReserveAddress = reserves[vars.i]; + + if (vars.currentReserveAddress == address(0)) continue; + DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve @@ -132,7 +135,7 @@ library GenericLogic { IERC20(currentReserve.stableDebtTokenAddress).balanceOf(user) ); vars.userDebtETH = vars.assetPrice.mul(vars.userDebt).div(vars.assetUnit); - vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); + vars.totalDebtInETH = vars.totalDebtInETH.add(vars.userDebtETH); } } From 52b903cd205914bbb35195c1bde9f8324a256a56 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 27 May 2021 08:12:27 +0200 Subject: [PATCH 38/64] feat: configurator: added reserveDropped event --- .../interfaces/ILendingPoolConfigurator.sol | 16 +++++++++++----- .../lendingpool/LendingPoolConfigurator.sol | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index ee5cdf2f..75b8c47e 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -132,6 +132,12 @@ interface ILendingPoolConfigurator { **/ event ReserveUnpaused(address indexed asset); + /** + * @dev Emitted when a reserve is dropped + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveDropped(address indexed asset); + /** * @dev Emitted when a reserve factor is updated * @param asset The address of the underlying asset of the reserve @@ -203,13 +209,13 @@ interface ILendingPoolConfigurator { address indexed implementation ); - /** + /** * @dev Emitted when a new borrower is authorized (fees = 0) * @param flashBorrower The address of the authorized borrower **/ event FlashBorrowerAuthorized(address indexed flashBorrower); - /** + /** * @dev Emitted when a borrower is unauthorized * @param flashBorrower The address of the unauthorized borrower **/ @@ -386,14 +392,14 @@ interface ILendingPoolConfigurator { * @param admin The address of the potential admin **/ function isRiskAdmin(address admin) external view returns (bool); - - /** + + /** * @dev Authorize a new borrower (fees are 0 for the authorized borrower) * @param flashBorrower The address of the authorized borrower **/ function authorizeFlashBorrower(address flashBorrower) external; - /** + /** * @dev Unauthorize a borrower * @param flashBorrower The address of the unauthorized borrower **/ diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index c3c8e2e9..a7cc2f77 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -162,6 +162,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur /// @inheritdoc ILendingPoolConfigurator function dropReserve(address asset) external onlyPoolAdmin { _pool.dropReserve(asset); + emit ReserveDropped(asset); } /// @inheritdoc ILendingPoolConfigurator From d0841097e6d0f8a9594189755afe275fcf6a970a Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Sun, 6 Jun 2021 09:35:32 +0200 Subject: [PATCH 39/64] fix: drop reserve logic updated --- contracts/protocol/lendingpool/LendingPool.sol | 3 ++- .../protocol/libraries/logic/ReserveLogic.sol | 17 ----------------- .../libraries/logic/ValidationLogic.sol | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 9fb2bcfb..e49a53f1 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -828,8 +828,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage * @param asset The address of the underlying asset of the reserve **/ function dropReserve(address asset) external override onlyLendingPoolConfigurator { - _reserves[asset].dropReserve(); + ValidationLogic.validateDropReserve(_reserves[asset]); _removeReserveFromList(asset); + delete _reserves[asset]; } /** diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 81fe56fc..28e96647 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -178,23 +178,6 @@ library ReserveLogic { reserve.interestRateStrategyAddress = interestRateStrategyAddress; } - /** - * @dev drop a reserve - * @param reserve The reserve object - **/ - function dropReserve(DataTypes.ReserveData storage reserve) external { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); - require( - IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, - Errors.RL_STABLE_DEBT_NOT_NULL - ); - require( - IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, - Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL - ); - reserve.id = type(uint8).max; - } - struct UpdateInterestRatesLocalVars { address stableDebtTokenAddress; uint256 availableLiquidity; diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 40ef23be..2f887d7e 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -477,4 +477,20 @@ library ValidationLogic { function validateTransfer(DataTypes.ReserveData storage reserve) internal view { require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); } + + /** + * @dev Validates a drop reserve action + * @param reserve The reserve object + **/ + function validateDropReserve(DataTypes.ReserveData storage reserve) external view { + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require( + IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, + Errors.RL_STABLE_DEBT_NOT_NULL + ); + require( + IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + ); + } } From 68b7384146e8c258fa34a699b37574e3eddd4c1b Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 09:24:52 +0200 Subject: [PATCH 40/64] rename: renamed NOT_NULL Errors to NOT_ZERO --- contracts/protocol/libraries/helpers/Errors.sol | 6 +++--- contracts/protocol/libraries/logic/ValidationLogic.sol | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/libraries/helpers/Errors.sol b/contracts/protocol/libraries/helpers/Errors.sol index 1b984270..4b0644f5 100644 --- a/contracts/protocol/libraries/helpers/Errors.sol +++ b/contracts/protocol/libraries/helpers/Errors.sol @@ -109,9 +109,9 @@ library Errors { string public constant LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85'; string public constant VL_RESERVE_PAUSED = '86'; string public constant LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87'; - string public constant RL_ATOKEN_SUPPLY_NOT_NULL = '88'; - string public constant RL_STABLE_DEBT_NOT_NULL = '89'; - string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_NULL = '90'; + string public constant RL_ATOKEN_SUPPLY_NOT_ZERO = '88'; + string public constant RL_STABLE_DEBT_NOT_ZERO = '89'; + string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90'; enum CollateralManagerErrors { NO_ERROR, diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 2f887d7e..3e4419dc 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -483,14 +483,14 @@ library ValidationLogic { * @param reserve The reserve object **/ function validateDropReserve(DataTypes.ReserveData storage reserve) external view { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_NULL); + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); require( IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, - Errors.RL_STABLE_DEBT_NOT_NULL + Errors.RL_STABLE_DEBT_NOT_ZERO ); require( IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, - Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_NULL + Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO ); } } From 46d14e912434e9b6bc6cbda75cc34c22d698e779 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 09:49:51 +0200 Subject: [PATCH 41/64] refactor: changed the order of requirements in validateDropReserve for easier testing --- contracts/protocol/libraries/logic/ValidationLogic.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 3e4419dc..e57f55fa 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -483,7 +483,6 @@ library ValidationLogic { * @param reserve The reserve object **/ function validateDropReserve(DataTypes.ReserveData storage reserve) external view { - require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); require( IERC20(reserve.stableDebtTokenAddress).totalSupply() == 0, Errors.RL_STABLE_DEBT_NOT_ZERO @@ -492,5 +491,6 @@ library ValidationLogic { IERC20(reserve.variableDebtTokenAddress).totalSupply() == 0, Errors.RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO ); + require(IERC20(reserve.aTokenAddress).totalSupply() == 0, Errors.RL_ATOKEN_SUPPLY_NOT_ZERO); } } From 648604930e27e86ebe458ea33f3b12b47eab4bd2 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 10:18:37 +0200 Subject: [PATCH 42/64] test: tested drop reserve --- helpers/types.ts | 3 + test-suites/test-aave/drop-reserve.spec.ts | 104 +++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test-suites/test-aave/drop-reserve.spec.ts diff --git a/helpers/types.ts b/helpers/types.ts index 0e0ac375..157f34ba 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -184,6 +184,9 @@ export enum ProtocolErrors { LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN = '85', VL_RESERVE_PAUSED = '86', LPC_CALLER_NOT_RISK_OR_POOL_ADMIN = '87', + RL_ATOKEN_SUPPLY_NOT_ZERO = '88', + RL_STABLE_DEBT_NOT_ZERO = '89', + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90', // old diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts new file mode 100644 index 00000000..9c98686a --- /dev/null +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -0,0 +1,104 @@ +import { makeSuite, TestEnv } from './helpers/make-suite'; +import { ProtocolErrors, RateMode } from '../../helpers/types'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneEther } from '../../helpers/constants'; +import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers'; +import { parseEther, parseUnits } from 'ethers/lib/utils'; +import { BigNumber } from 'bignumber.js'; +import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; +import { getMockFlashLoanReceiver } from '../../helpers/contracts-getters'; +import { domainToUnicode } from 'url'; + +const { expect } = require('chai'); + +makeSuite('Pause Reserve', (testEnv: TestEnv) => { + let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; + + const { + RL_ATOKEN_SUPPLY_NOT_ZERO, + RL_STABLE_DEBT_NOT_ZERO, + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO, + } = ProtocolErrors; + + before(async () => { + _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); + }); + + it('User 1 deposits Dai, User 2 borrow Dai stable and variable, should fail to drop Dai reserve', async () => { + const { + deployer, + users: [user1], + pool, + dai, + aDai, + weth, + configurator, + } = testEnv; + + const depositedAmount = parseEther('1000'); + const borrowedAmount = parseEther('100'); + console.log((await aDai.totalSupply()).toString()); + // setting reserve factor to 0 to ease tests, no aToken accrued in reserve + await configurator.setReserveFactor(dai.address, 0); + + await dai.mint(depositedAmount); + await dai.approve(pool.address, depositedAmount); + await dai.connect(user1.signer).mint(depositedAmount); + await dai.connect(user1.signer).approve(pool.address, depositedAmount); + + await weth.connect(user1.signer).mint(depositedAmount); + await weth.connect(user1.signer).approve(pool.address, depositedAmount); + + await pool.deposit(dai.address, depositedAmount, deployer.address, 0); + + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_ATOKEN_SUPPLY_NOT_ZERO + ); + + await pool.connect(user1.signer).deposit(weth.address, depositedAmount, user1.address, 0); + + await pool.connect(user1.signer).borrow(dai.address, borrowedAmount, 2, 0, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO + ); + await pool.connect(user1.signer).borrow(dai.address, borrowedAmount, 1, 0, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith(RL_STABLE_DEBT_NOT_ZERO); + }); + it('User 2 repays debts, drop Dai reserve should fail', async () => { + const { + deployer, + users: [user1], + pool, + dai, + weth, + configurator, + } = testEnv; + await pool.connect(user1.signer).repay(dai.address, MAX_UINT_AMOUNT, 1, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO + ); + + await pool.connect(user1.signer).repay(dai.address, MAX_UINT_AMOUNT, 2, user1.address); + await expect(configurator.dropReserve(dai.address)).to.be.revertedWith( + RL_ATOKEN_SUPPLY_NOT_ZERO + ); + }); + it('User 1 withdraw Dai, drop Dai reserve should succeed', async () => { + const { + deployer, + users: [user1], + pool, + dai, + aDai, + weth, + configurator, + } = testEnv; + + await pool.withdraw(dai.address, MAX_UINT_AMOUNT, deployer.address); + console.log((await aDai.totalSupply()).toString()); + await configurator.dropReserve(dai.address); + + const tokens = await pool.getReservesList(); + + expect(tokens.includes(dai.address)).to.be.false; + }); +}); From a0f6199ea36a3e9d4240f0cbc1a9b8d85731c2d5 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 10:26:38 +0200 Subject: [PATCH 43/64] doc: added doc to drop reserve --- contracts/interfaces/ILendingPoolConfigurator.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 75b8c47e..312aebed 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -404,4 +404,10 @@ interface ILendingPoolConfigurator { * @param flashBorrower The address of the unauthorized borrower **/ function unauthorizeFlashBorrower(address flashBorrower) external; + + /** + * @dev Drops a reserve entirely + * @param asset the address of the reserve to drop + **/ + function dropReserve(address asset) external; } From 58ab73272ea0c6f0957969a4c72c1326e0234cc7 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 7 Jun 2021 13:43:04 +0200 Subject: [PATCH 44/64] test: added testing droped reserve is inactive --- test-suites/test-aave/drop-reserve.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts index 9c98686a..f4e9b68a 100644 --- a/test-suites/test-aave/drop-reserve.spec.ts +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -36,7 +36,6 @@ makeSuite('Pause Reserve', (testEnv: TestEnv) => { const depositedAmount = parseEther('1000'); const borrowedAmount = parseEther('100'); - console.log((await aDai.totalSupply()).toString()); // setting reserve factor to 0 to ease tests, no aToken accrued in reserve await configurator.setReserveFactor(dai.address, 0); @@ -91,14 +90,18 @@ makeSuite('Pause Reserve', (testEnv: TestEnv) => { aDai, weth, configurator, + helpersContract, } = testEnv; await pool.withdraw(dai.address, MAX_UINT_AMOUNT, deployer.address); - console.log((await aDai.totalSupply()).toString()); await configurator.dropReserve(dai.address); const tokens = await pool.getReservesList(); expect(tokens.includes(dai.address)).to.be.false; + + const { isActive } = await helpersContract.getReserveConfigurationData(dai.address); + + expect(isActive).to.be.false; }); }); From 5bbbc78d0b3e6e7e281dafb7dd7da8013a381edd Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 8 Jun 2021 18:04:33 +0200 Subject: [PATCH 45/64] fix: test naming --- test-suites/test-aave/drop-reserve.spec.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test-suites/test-aave/drop-reserve.spec.ts b/test-suites/test-aave/drop-reserve.spec.ts index f4e9b68a..d2257f79 100644 --- a/test-suites/test-aave/drop-reserve.spec.ts +++ b/test-suites/test-aave/drop-reserve.spec.ts @@ -10,14 +10,11 @@ import { domainToUnicode } from 'url'; const { expect } = require('chai'); -makeSuite('Pause Reserve', (testEnv: TestEnv) => { +makeSuite('Drop Reserve', (testEnv: TestEnv) => { let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver; - const { - RL_ATOKEN_SUPPLY_NOT_ZERO, - RL_STABLE_DEBT_NOT_ZERO, - RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO, - } = ProtocolErrors; + const { RL_ATOKEN_SUPPLY_NOT_ZERO, RL_STABLE_DEBT_NOT_ZERO, RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO } = + ProtocolErrors; before(async () => { _mockFlashLoanReceiver = await getMockFlashLoanReceiver(); From d708ff4971375fd5f606b07ed698fb394985dc83 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Tue, 8 Jun 2021 18:05:26 +0200 Subject: [PATCH 46/64] fix: added override to drop reserve function --- contracts/protocol/lendingpool/LendingPoolConfigurator.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index a7cc2f77..29c725cf 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -160,7 +160,7 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur } /// @inheritdoc ILendingPoolConfigurator - function dropReserve(address asset) external onlyPoolAdmin { + function dropReserve(address asset) external override onlyPoolAdmin { _pool.dropReserve(asset); emit ReserveDropped(asset); } From 9cbda77b2708b88d19de0c3d77a6d9235a7ece25 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 9 Jun 2021 18:12:33 +0200 Subject: [PATCH 47/64] feat: split flashoan fees between fees to protocol and fees to LPs --- .../protocol/lendingpool/LendingPool.sol | 37 +++++++++++++------ .../lendingpool/LendingPoolStorage.sol | 4 +- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index e49a53f1..0ddce0fe 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -87,8 +87,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function initialize(ILendingPoolAddressesProvider provider) public initializer { _addressesProvider = provider; _maxStableRateBorrowSizePercent = 2500; - _flashLoanPremiumTotal = 9; + _flashLoanPremiumToLP = 9; _maxNumberOfReserves = 128; + _flashLoanPremiumToProtocol = 0; } /** @@ -432,10 +433,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address currentAsset; address currentATokenAddress; uint256 currentAmount; - uint256 currentPremium; + uint256 currentPremiumToLP; + uint256 currentPremiumToProtocol; uint256 currentAmountPlusPremium; + uint256 currentAmountPlusPremiumToProtocol; address debtToken; - uint256 flashloanPremiumTotal; + uint256 flashloanPremiumToLP; + uint256 flashloanPremiumToProtocol; } /** @@ -469,36 +473,45 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ValidationLogic.validateFlashloan(assets, amounts, _reserves); address[] memory aTokenAddresses = new address[](assets.length); - uint256[] memory premiums = new uint256[](assets.length); + uint256[] memory totalPremiums = new uint256[](assets.length); vars.receiver = IFlashLoanReceiver(receiverAddress); - vars.flashloanPremiumTotal = _authorizedFlashBorrowers[msg.sender] ? 0 : _flashLoanPremiumTotal; + (vars.flashloanPremiumToLP, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[ + msg.sender + ] + ? (0, 0) + : (_flashLoanPremiumToLP, _flashLoanPremiumToProtocol); for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal); + vars.currentPremiumToLP = amounts[vars.i].percentMul(vars.flashloanPremiumToLP); + vars.currentPremiumToProtocol = amounts[vars.i].percentMul(vars.flashloanPremiumToProtocol); + + totalPremiums[vars.i] = vars.currentPremiumToLP.add(vars.currentPremiumToProtocol); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } require( - vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params), + vars.receiver.executeOperation(assets, amounts, totalPremiums, msg.sender, params), Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN ); for (vars.i = 0; vars.i < assets.length; vars.i++) { vars.currentAsset = assets[vars.i]; vars.currentAmount = amounts[vars.i]; - vars.currentPremium = premiums[vars.i]; vars.currentATokenAddress = aTokenAddresses[vars.i]; - vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); + vars.currentAmountPlusPremium = vars.currentAmount.add(totalPremiums[vars.i]); if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { _reserves[vars.currentAsset].updateState(); _reserves[vars.currentAsset].cumulateToLiquidityIndex( IERC20(vars.currentATokenAddress).totalSupply(), - vars.currentPremium + vars.currentPremiumToLP ); + _reserves[vars.currentAsset].accruedToTreasury = _reserves[vars.currentAsset] + .accruedToTreasury + .add(vars.currentPremiumToProtocol.rayDiv(_reserves[vars.currentAsset].liquidityIndex)); _reserves[vars.currentAsset].updateInterestRates( vars.currentAsset, vars.currentATokenAddress, @@ -532,7 +545,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage msg.sender, vars.currentAsset, vars.currentAmount, - vars.currentPremium, + vars.currentPremiumToLP, referralCode ); } @@ -733,7 +746,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage * @dev Returns the fee on flash loans */ function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { - return _flashLoanPremiumTotal; + return _flashLoanPremiumToLP; } /** diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index 4f37e658..dacec177 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -26,9 +26,11 @@ contract LendingPoolStorage { uint256 internal _maxStableRateBorrowSizePercent; - uint256 internal _flashLoanPremiumTotal; + uint256 internal _flashLoanPremiumToLP; uint256 internal _maxNumberOfReserves; mapping(address => bool) _authorizedFlashBorrowers; + + uint256 internal _flashLoanPremiumToProtocol; } From b33fd05b14063f41556a0527c9f72bd70fe789d8 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Wed, 9 Jun 2021 18:15:43 +0200 Subject: [PATCH 48/64] fix: removed useless caching variable --- contracts/protocol/lendingpool/LendingPool.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 0ddce0fe..eadcecb9 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -436,7 +436,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint256 currentPremiumToLP; uint256 currentPremiumToProtocol; uint256 currentAmountPlusPremium; - uint256 currentAmountPlusPremiumToProtocol; address debtToken; uint256 flashloanPremiumToLP; uint256 flashloanPremiumToProtocol; From 85c477f4229645f7d9b6ccc9ba1161488f706cce Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 10 Jun 2021 12:29:50 +0200 Subject: [PATCH 49/64] test: commited first tests --- .../protocol/lendingpool/LendingPool.sol | 4 +- test-suites/test-aave/flashloan.spec.ts | 115 ++++++++++++++++-- 2 files changed, 104 insertions(+), 15 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index eadcecb9..c8aadf43 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -87,9 +87,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function initialize(ILendingPoolAddressesProvider provider) public initializer { _addressesProvider = provider; _maxStableRateBorrowSizePercent = 2500; - _flashLoanPremiumToLP = 9; + _flashLoanPremiumToLP = 7; _maxNumberOfReserves = 128; - _flashLoanPremiumToProtocol = 0; + _flashLoanPremiumToProtocol = 2; } /** diff --git a/test-suites/test-aave/flashloan.spec.ts b/test-suites/test-aave/flashloan.spec.ts index 911c4adc..d45d0d87 100644 --- a/test-suites/test-aave/flashloan.spec.ts +++ b/test-suites/test-aave/flashloan.spec.ts @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js'; import { TestEnv, makeSuite } from './helpers/make-suite'; -import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../../helpers/constants'; +import { APPROVAL_AMOUNT_LENDING_POOL, MAX_UINT_AMOUNT, oneRay } from '../../helpers/constants'; import { convertToCurrencyDecimals, getContract } from '../../helpers/contracts-helpers'; import { ethers } from 'ethers'; import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver'; @@ -32,7 +32,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { }); it('Deposits WETH into the reserve', async () => { - const { pool, weth } = testEnv; + const { pool, weth, aave } = testEnv; const userAddress = await pool.signer.getAddress(); const amountToDeposit = ethers.utils.parseEther('1'); @@ -41,52 +41,124 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); await pool.deposit(weth.address, amountToDeposit, userAddress, '0'); + + await aave.mint(amountToDeposit); + + await aave.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + + await pool.deposit(aave.address, amountToDeposit, userAddress, '0'); }); - it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => { + it('Takes WETH flash loan with mode = 0, returns the funds correctly', async () => { const { pool, helpersContract, weth } = testEnv; + const flashBorrowedAmount = ethers.utils.parseEther('0.8'); + const fees = new BigNumber(flashBorrowedAmount.mul(9).div(10000).toString()); + + let reserveData = await helpersContract.getReserveData(weth.address); + + const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + await pool.flashLoan( _mockFlashLoanReceiver.address, [weth.address], - [ethers.utils.parseEther('0.8')], + [flashBorrowedAmount], [0], _mockFlashLoanReceiver.address, '0x10', '0' ); - ethers.utils.parseUnits('10000'); + await pool.mintToTreasury([weth.address]); - const reserveData = await helpersContract.getReserveData(weth.address); + reserveData = await helpersContract.getReserveData(weth.address); const currentLiquidityRate = reserveData.liquidityRate; const currentLiquidityIndex = reserveData.liquidityIndex; - const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) .plus(reserveData.totalStableDebt.toString()) .plus(reserveData.totalVariableDebt.toString()); - expect(totalLiquidity.toString()).to.be.equal('1000720000000000000'); + expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString()); expect(currentLiquidityRate.toString()).to.be.equal('0'); expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000'); }); + it('Takes an authorized AAVE flash loan with mode = 0, returns the funds correctly', async () => { + const { + pool, + helpersContract, + aave, + configurator, + users: [, , , authorizedUser], + } = testEnv; + await configurator.authorizeFlashBorrower(authorizedUser.address); + const flashBorrowedAmount = ethers.utils.parseEther('0.8'); + const fees = new BigNumber(0); + + let reserveData = await helpersContract.getReserveData(aave.address); + + const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + await pool + .connect(authorizedUser.signer) + .flashLoan( + _mockFlashLoanReceiver.address, + [aave.address], + [flashBorrowedAmount], + [0], + _mockFlashLoanReceiver.address, + '0x10', + '0' + ); + + await pool.mintToTreasury([weth.address]); + + ethers.utils.parseUnits('10000'); + + reserveData = await helpersContract.getReserveData(aave.address); + + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString()); + }); it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => { const { pool, helpersContract, weth } = testEnv; - const reserveDataBefore = await helpersContract.getReserveData(weth.address); + let reserveData = await helpersContract.getReserveData(weth.address); + + const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + const flashBorrowedAmount = totalLiquidityBefore.toString(); + + const fees = new BigNumber(flashBorrowedAmount).multipliedBy(9).dividedBy(10000).toString(); + const txResult = await pool.flashLoan( _mockFlashLoanReceiver.address, [weth.address], - ['1000720000000000000'], + [totalLiquidityBefore.toString()], [0], _mockFlashLoanReceiver.address, '0x10', '0' ); - const reserveData = await helpersContract.getReserveData(weth.address); + await pool.mintToTreasury([weth.address]); + + reserveData = await helpersContract.getReserveData(weth.address); + + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); const currentLiqudityRate = reserveData.liquidityRate; const currentLiquidityIndex = reserveData.liquidityIndex; @@ -177,6 +249,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { await _mockFlashLoanReceiver.setFailExecutionTransfer(true); + let reserveData = await helpersContract.getReserveData(weth.address); + + let totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + await pool .connect(caller.signer) .flashLoan( @@ -191,14 +269,25 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses( weth.address ); + reserveData = await helpersContract.getReserveData(weth.address); + + const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); + + expect(totalLiquidityAfter.toString()).to.be.equal( + ethers.BigNumber.from(totalLiquidityBefore.toString()) + ); const wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress); - const callerDebt = await wethDebtToken.balanceOf(caller.address); expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); + // repays debt for later, so no interest accrue + await weth.connect(caller.signer).mint(await convertToCurrencyDecimals(weth.address, '1000')); + await weth.connect(caller.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL); + await pool.connect(caller.signer).repay(weth.address, MAX_UINT_AMOUNT, 2, caller.address); }); - it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { const { pool, weth, users } = testEnv; const caller = users[1]; From 61217d1ee5ef81e1cf2bb496b37005725ff700a2 Mon Sep 17 00:00:00 2001 From: emilio Date: Thu, 10 Jun 2021 18:18:01 +0200 Subject: [PATCH 50/64] refactor: further refactored the caching logic --- .../protocol/lendingpool/LendingPool.sol | 42 ++++----------- .../LendingPoolCollateralManager.sol | 30 +++++------ .../protocol/libraries/logic/ReserveLogic.sol | 52 +++++++++++++------ package-lock.json | 2 +- 4 files changed, 60 insertions(+), 66 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 24a9ae4b..f349d412 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -49,6 +49,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage using WadRayMath for uint256; using PercentageMath for uint256; using SafeERC20 for IERC20; + using ReserveLogic for DataTypes.ReserveCache; uint256 public constant LENDINGPOOL_REVISION = 0x2; @@ -288,40 +289,26 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(msg.sender, stableDebt); - - reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache - .currTotalStableDebt - .sub(stableDebt); - IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( msg.sender, msg.sender, stableDebt, reserveCache.nextVariableBorrowIndex ); - reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.add( - stableDebt.rayDiv(reserveCache.nextVariableBorrowIndex) - ); + reserveCache.refreshDebt(0, stableDebt, stableDebt, 0); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( msg.sender, variableDebt, reserveCache.nextVariableBorrowIndex ); - reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.sub( - variableDebt.rayDiv(reserveCache.nextVariableBorrowIndex) - ); - IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( msg.sender, msg.sender, variableDebt, reserve.currentStableBorrowRate ); - - reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache - .currTotalStableDebt - .add(stableDebt); + reserveCache.refreshDebt(variableDebt, 0, 0, variableDebt); } reserve.updateInterestRates(reserveCache, asset, 0, 0); @@ -365,6 +352,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.currentStableBorrowRate ); + reserveCache.refreshDebt(stableDebt, stableDebt, 0, 0); + reserve.updateInterestRates(reserveCache, asset, 0, 0); emit RebalanceStableBorrowRate(asset, user); @@ -915,8 +904,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ); uint256 currentStableRate = 0; - bool isFirstBorrowing = false; + if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { currentStableRate = reserve.currentStableBorrowRate; @@ -926,11 +915,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.amount, currentStableRate ); - - reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache - .currTotalStableDebt - .add(vars.amount); - + reserveCache.refreshDebt(vars.amount, 0, 0, 0); } else { isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( vars.user, @@ -938,10 +923,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.amount, reserveCache.nextVariableBorrowIndex ); - - reserveCache.nextScaledVariableDebt = reserveCache.nextScaledVariableDebt.add( - vars.amount.rayDiv(reserveCache.nextVariableBorrowIndex) - ); + reserveCache.refreshDebt(0, 0, vars.amount, 0); } if (isFirstBorrowing) { @@ -1090,18 +1072,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage if (interestRateMode == DataTypes.InterestRateMode.STABLE) { IStableDebtToken(reserveCache.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); - reserveCache.nextPrincipalStableDebt = reserveCache.nextTotalStableDebt = reserveCache - .currTotalStableDebt - .sub(paybackAmount); + reserveCache.refreshDebt(0, paybackAmount, 0, 0); } else { IVariableDebtToken(reserveCache.variableDebtTokenAddress).burn( onBehalfOf, paybackAmount, reserveCache.nextVariableBorrowIndex ); - reserveCache.nextScaledVariableDebt = reserveCache.currScaledVariableDebt.sub( - paybackAmount.rayDiv(reserveCache.nextVariableBorrowIndex) - ); + reserveCache.refreshDebt(0, 0, 0, paybackAmount); } reserve.updateInterestRates(reserveCache, asset, paybackAmount, 0); diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index 19908705..b757f9de 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -10,6 +10,7 @@ import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; import {Helpers} from '../libraries/helpers/Helpers.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; @@ -35,6 +36,7 @@ contract LendingPoolCollateralManager is using SafeMath for uint256; using WadRayMath for uint256; using PercentageMath for uint256; + using ReserveLogic for DataTypes.ReserveCache; uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000; @@ -173,9 +175,8 @@ contract LendingPoolCollateralManager is vars.actualDebtToLiquidate, debtReserveCache.nextVariableBorrowIndex ); - debtReserveCache.nextScaledVariableDebt = debtReserveCache.currScaledVariableDebt.sub( - vars.actualDebtToLiquidate.rayDiv(debtReserveCache.nextVariableBorrowIndex) - ); + debtReserveCache.refreshDebt(0, 0, 0, vars.actualDebtToLiquidate); + debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0); } else { // If the user doesn't have variable debt, no need to try to burn variable debt tokens if (vars.userVariableDebt > 0) { @@ -184,27 +185,20 @@ contract LendingPoolCollateralManager is vars.userVariableDebt, debtReserveCache.nextVariableBorrowIndex ); - debtReserveCache.nextScaledVariableDebt = debtReserveCache - .currScaledVariableDebt - .sub(vars.userVariableDebt.rayDiv(debtReserveCache.nextVariableBorrowIndex)); } IStableDebtToken(debtReserveCache.stableDebtTokenAddress).burn( user, vars.actualDebtToLiquidate.sub(vars.userVariableDebt) ); - - debtReserveCache.nextPrincipalStableDebt = debtReserveCache - .nextTotalStableDebt = debtReserveCache.currTotalStableDebt.sub( - vars.actualDebtToLiquidate.sub(vars.userVariableDebt) + debtReserveCache.refreshDebt( + 0, + vars.actualDebtToLiquidate.sub(vars.userVariableDebt), + 0, + vars.userVariableDebt ); - } - debtReserve.updateInterestRates( - debtReserveCache, - debtAsset, - vars.actualDebtToLiquidate, - 0 - ); + debtReserve.updateInterestRates(debtReserveCache, debtAsset, vars.actualDebtToLiquidate, 0); + } if (receiveAToken) { vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender); @@ -216,7 +210,7 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { - collateralReserve.updateState(collateralReserveCache); + collateralReserve.updateState(collateralReserveCache); collateralReserve.updateInterestRates( collateralReserveCache, collateralAsset, diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 1db225ac..305559e2 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -186,10 +186,6 @@ library ReserveLogic { ) internal { UpdateInterestRatesLocalVars memory vars; - if (reserveCache.currTotalStableDebt != reserveCache.nextTotalStableDebt) { - reserveCache.nextAvgStableBorrowRate = IStableDebtToken(reserveCache.stableDebtTokenAddress) - .getAverageStableRate(); - } reserveCache.nextTotalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex @@ -228,11 +224,9 @@ library ReserveLogic { } struct MintToTreasuryLocalVars { - uint256 currentStableDebt; - uint256 principalStableDebt; - uint256 previousStableDebt; - uint256 currentVariableDebt; - uint256 previousVariableDebt; + uint256 prevTotalStableDebt; + uint256 prevTotalVariableDebt; + uint256 currTotalVariableDebt; uint256 avgStableRate; uint256 cumulatedStableInterest; uint256 totalDebtAccrued; @@ -260,12 +254,12 @@ library ReserveLogic { } //calculate the last principal variable debt - vars.previousVariableDebt = reserveCache.currScaledVariableDebt.rayMul( + vars.prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul( reserveCache.currVariableBorrowIndex ); //calculate the new total supply after accumulation of the index - vars.currentVariableDebt = reserveCache.currScaledVariableDebt.rayMul( + vars.currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex ); @@ -276,16 +270,16 @@ library ReserveLogic { reserveCache.reserveLastUpdateTimestamp ); - vars.previousStableDebt = reserveCache.currPrincipalStableDebt.rayMul( + vars.prevTotalStableDebt = reserveCache.currPrincipalStableDebt.rayMul( vars.cumulatedStableInterest ); //debt accrued is the sum of the current debt minus the sum of the debt at the last update vars.totalDebtAccrued = vars - .currentVariableDebt + .currTotalVariableDebt .add(reserveCache.currTotalStableDebt) - .sub(vars.previousVariableDebt) - .sub(vars.previousStableDebt); + .sub(vars.prevTotalVariableDebt) + .sub(vars.prevTotalStableDebt); vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); @@ -385,4 +379,32 @@ library ReserveLogic { return reserveCache; } + + function refreshDebt( + DataTypes.ReserveCache memory cache, + uint256 stableDebtMinted, + uint256 stableDebtBurned, + uint256 variableDebtMinted, + uint256 variableDebtBurned + ) internal { + uint256 scaledVariableDebtMinted = variableDebtMinted.rayDiv(cache.nextVariableBorrowIndex); + uint256 scaledVariableDebtBurned = variableDebtBurned.rayDiv(cache.nextVariableBorrowIndex); + + if (cache.currTotalStableDebt.add(stableDebtMinted) > stableDebtBurned) { + cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache + .currTotalStableDebt + .add(stableDebtMinted) + .sub(stableDebtBurned); + if (stableDebtMinted != 0 || stableDebtBurned != 0) { + cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) + .getAverageStableRate(); + } + } else { + cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache.nextAvgStableBorrowRate = 0; + } + + cache.nextScaledVariableDebt = cache.currScaledVariableDebt.add(scaledVariableDebtMinted).sub( + scaledVariableDebtBurned + ); + } } diff --git a/package-lock.json b/package-lock.json index 7f3c76fd..e5198127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14793,7 +14793,7 @@ } }, "ethereumjs-abi": { - "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e", + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", "dev": true, "requires": { From 39301ebee1696edad584c138f4fd1ed12cc1fec4 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 10 Jun 2021 18:32:24 +0200 Subject: [PATCH 51/64] feat: passed some internal validation functions to external for LendingPool codesize --- contracts/protocol/libraries/logic/ValidationLogic.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index e57f55fa..d71cd15c 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -40,7 +40,7 @@ library ValidationLogic { * @param reserve The reserve object on which the user is depositing * @param amount The amount to be deposited */ - function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) internal view { + function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration; (bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory(); (, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory(); @@ -453,7 +453,7 @@ library ValidationLogic { mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle - ) internal view { + ) external view { (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData( from, @@ -474,7 +474,7 @@ library ValidationLogic { * @dev Validates a transfer action * @param reserve The reserve object */ - function validateTransfer(DataTypes.ReserveData storage reserve) internal view { + function validateTransfer(DataTypes.ReserveData storage reserve) external view { require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED); } From a6b71313e102f911596e62bf0934481f0e39899d Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 10 Jun 2021 19:05:50 +0200 Subject: [PATCH 52/64] feat: Added configurator managing flashloan premiums --- contracts/interfaces/ILendingPool.sol | 13 ++++++ .../interfaces/ILendingPoolConfigurator.sol | 16 ++++++++ .../protocol/lendingpool/LendingPool.sol | 40 +++++++++++++------ .../lendingpool/LendingPoolConfigurator.sol | 20 ++++++++++ .../lendingpool/LendingPoolStorage.sol | 2 +- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/contracts/interfaces/ILendingPool.sol b/contracts/interfaces/ILendingPool.sol index bcebbde8..86e58f61 100644 --- a/contracts/interfaces/ILendingPool.sol +++ b/contracts/interfaces/ILendingPool.sol @@ -471,4 +471,17 @@ interface ILendingPool { function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external; function isFlashBorrowerAuthorized(address flashBorrower) external view returns (bool); + + function updateFlashloanPremiums( + uint256 flashLoanPremiumTotal, + uint256 flashLoanPremiumToProtocol + ) external; + + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256); + + function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint256); + + function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint256); + + function MAX_NUMBER_RESERVES() external view returns (uint256); } diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 312aebed..055325b0 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -233,6 +233,18 @@ interface ILendingPoolConfigurator { **/ event RiskAdminUnregistered(address indexed admin); + /** + * @dev Emitted when a the total premium on flashloans is updated + * @param flashloanPremiumTotal the new premium + **/ + event FlashloanPremiumTotalUpdated(uint256 flashloanPremiumTotal); + + /** + * @dev Emitted when a the part of the premium that goes to protoco lis updated + * @param flashloanPremiumToProtocol the new premium + **/ + event FlashloanPremiumToProcolUpdated(uint256 flashloanPremiumToProtocol); + /** * @dev Initializes reserves in batch * @param input The array of reserves initialization parameters @@ -410,4 +422,8 @@ interface ILendingPoolConfigurator { * @param asset the address of the reserve to drop **/ function dropReserve(address asset) external; + + function updateFlashloanPremiumTotal(uint256 flashloanPremiumTotal) external; + + function updateFlashloanPremiumToProtocol(uint256 flashloanPremiumToProtocol) external; } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index c8aadf43..848fe112 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -87,9 +87,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function initialize(ILendingPoolAddressesProvider provider) public initializer { _addressesProvider = provider; _maxStableRateBorrowSizePercent = 2500; - _flashLoanPremiumToLP = 7; + _flashLoanPremiumTotal = 9; _maxNumberOfReserves = 128; - _flashLoanPremiumToProtocol = 2; + _flashLoanPremiumToProtocol = 0; } /** @@ -437,7 +437,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint256 currentPremiumToProtocol; uint256 currentAmountPlusPremium; address debtToken; - uint256 flashloanPremiumToLP; + uint256 flashloanPremiumTotal; uint256 flashloanPremiumToProtocol; } @@ -474,19 +474,18 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address[] memory aTokenAddresses = new address[](assets.length); uint256[] memory totalPremiums = new uint256[](assets.length); vars.receiver = IFlashLoanReceiver(receiverAddress); - (vars.flashloanPremiumToLP, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[ + (vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[ msg.sender ] ? (0, 0) - : (_flashLoanPremiumToLP, _flashLoanPremiumToProtocol); + : (_flashLoanPremiumTotal, _flashLoanPremiumToProtocol); for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - vars.currentPremiumToLP = amounts[vars.i].percentMul(vars.flashloanPremiumToLP); + totalPremiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal); vars.currentPremiumToProtocol = amounts[vars.i].percentMul(vars.flashloanPremiumToProtocol); - - totalPremiums[vars.i] = vars.currentPremiumToLP.add(vars.currentPremiumToProtocol); + vars.currentPremiumToLP = totalPremiums[vars.i].sub(vars.currentPremiumToProtocol); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } @@ -737,21 +736,28 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage /** * @dev Returns the percentage of available liquidity that can be borrowed at once at stable rate */ - function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view returns (uint256) { + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view override returns (uint256) { return _maxStableRateBorrowSizePercent; } /** - * @dev Returns the fee on flash loans + * @dev Returns the total fee on flash loans */ - function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { - return _flashLoanPremiumToLP; + function FLASHLOAN_PREMIUM_TOTAL() public view override returns (uint256) { + return _flashLoanPremiumTotal; + } + + /** + * @dev Returns the part of the flashloan fees sent to protocol + */ + function FLASHLOAN_PREMIUM_TO_PROTOCOL() public view override returns (uint256) { + return _flashLoanPremiumToProtocol; } /** * @dev Returns the maximum number of reserves supported to be listed in this LendingPool */ - function MAX_NUMBER_RESERVES() public view returns (uint256) { + function MAX_NUMBER_RESERVES() public view override returns (uint256) { return _maxNumberOfReserves; } @@ -899,6 +905,14 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage return _authorizedFlashBorrowers[flashBorrower]; } + function updateFlashloanPremiums( + uint256 flashLoanPremiumTotal, + uint256 flashLoanPremiumToProtocol + ) external override onlyLendingPoolConfigurator { + _flashLoanPremiumTotal = flashLoanPremiumTotal; + _flashLoanPremiumToProtocol = flashLoanPremiumToProtocol; + } + struct ExecuteBorrowParams { address asset; address user; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index 29c725cf..3c699179 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -508,6 +508,26 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur return _riskAdmins[admin]; } + /// @inheritdoc ILendingPoolConfigurator + function updateFlashloanPremiumTotal(uint256 flashloanPremiumTotal) + external + override + onlyPoolAdmin + { + _pool.updateFlashloanPremiums(flashloanPremiumTotal, _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()); + emit FlashloanPremiumTotalUpdated(flashloanPremiumTotal); + } + + /// @inheritdoc ILendingPoolConfigurator + function updateFlashloanPremiumToProtocol(uint256 flashloanPremiumToProtocol) + external + override + onlyPoolAdmin + { + _pool.updateFlashloanPremiums(_pool.FLASHLOAN_PREMIUM_TOTAL(), flashloanPremiumToProtocol); + emit FlashloanPremiumToProcolUpdated(flashloanPremiumToProtocol); + } + function _initTokenWithProxy(address implementation, bytes memory initParams) internal returns (address) diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index dacec177..b8516bbf 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -26,7 +26,7 @@ contract LendingPoolStorage { uint256 internal _maxStableRateBorrowSizePercent; - uint256 internal _flashLoanPremiumToLP; + uint256 internal _flashLoanPremiumTotal; uint256 internal _maxNumberOfReserves; From ce6a07be629683c0c9cfaef851adf9abd9c1e09e Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 10 Jun 2021 19:06:02 +0200 Subject: [PATCH 53/64] fix: fixed test --- test-suites/test-aave/flashloan.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-suites/test-aave/flashloan.spec.ts b/test-suites/test-aave/flashloan.spec.ts index d45d0d87..91e162a5 100644 --- a/test-suites/test-aave/flashloan.spec.ts +++ b/test-suites/test-aave/flashloan.spec.ts @@ -50,7 +50,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { }); it('Takes WETH flash loan with mode = 0, returns the funds correctly', async () => { - const { pool, helpersContract, weth } = testEnv; + const { pool, helpersContract, weth, aWETH } = testEnv; const flashBorrowedAmount = ethers.utils.parseEther('0.8'); const fees = new BigNumber(flashBorrowedAmount.mul(9).div(10000).toString()); @@ -117,7 +117,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { '0' ); - await pool.mintToTreasury([weth.address]); + await pool.mintToTreasury([aave.address]); ethers.utils.parseUnits('10000'); From e416c10c6721c1606d1c2afedb04f46b62e4b3a2 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Thu, 10 Jun 2021 19:09:47 +0200 Subject: [PATCH 54/64] fix: event now emit total flashloa premium --- contracts/protocol/lendingpool/LendingPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 848fe112..75daa782 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -543,7 +543,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage msg.sender, vars.currentAsset, vars.currentAmount, - vars.currentPremiumToLP, + totalPremiums[vars.i], referralCode ); } From 760be1cb423889e867ff674b188384dec876186a Mon Sep 17 00:00:00 2001 From: emilio Date: Fri, 11 Jun 2021 11:27:19 +0200 Subject: [PATCH 55/64] refactor: further refactored cache, changed linkage behavior of GenericLogic --- .../protocol/lendingpool/LendingPool.sol | 12 +-- .../LendingPoolCollateralManager.sol | 52 +++++----- .../configuration/ReserveConfiguration.sol | 19 +--- .../protocol/libraries/logic/GenericLogic.sol | 32 ++++++ .../protocol/libraries/logic/ReserveLogic.sol | 39 ++++---- .../libraries/logic/ValidationLogic.sol | 98 +++++++++++-------- helpers/contracts-deployments.ts | 3 +- 7 files changed, 145 insertions(+), 110 deletions(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index e2f1509f..3f87e74e 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -20,8 +20,8 @@ import {Errors} from '../libraries/helpers/Errors.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; -import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; +import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; @@ -190,7 +190,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage uint16 referralCode, address onBehalfOf ) external override whenNotPaused { - DataTypes.ReserveData storage reserve = _reserves[asset]; _executeBorrow( ExecuteBorrowParams( asset, @@ -372,7 +371,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.ReserveData storage reserve = _reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); - ValidationLogic.validateSetUseReserveAsCollateral(reserve, reserveCache); + ValidationLogic.validateSetUseReserveAsCollateral(reserveCache); _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); @@ -619,7 +618,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage ltv, currentLiquidationThreshold, healthFactor - ) = GenericLogic.calculateUserAccountData( + ) = GenericLogic.getUserAccountData( user, _reserves, _usersConfig[user], @@ -990,7 +989,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage reserve.updateState(reserveCache); - ValidationLogic.validateDeposit(reserve, reserveCache, amount); + ValidationLogic.validateDeposit(reserveCache, amount); reserve.updateInterestRates(reserveCache, asset, amount, 0); @@ -1029,7 +1028,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage amountToWithdraw = userBalance; } - ValidationLogic.validateWithdraw(reserve, reserveCache, amountToWithdraw, userBalance); + ValidationLogic.validateWithdraw(reserveCache, amountToWithdraw, userBalance); reserve.updateInterestRates(reserveCache, asset, 0, amountToWithdraw); @@ -1077,7 +1076,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); ValidationLogic.validateRepay( - reserve, reserveCache, amount, interestRateMode, diff --git a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol index b757f9de..e530a9a4 100644 --- a/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol +++ b/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol'; import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol'; import {IAToken} from '../../interfaces/IAToken.sol'; @@ -9,7 +11,6 @@ import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; -import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; import {Helpers} from '../libraries/helpers/Helpers.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; @@ -54,6 +55,7 @@ contract LendingPoolCollateralManager is uint256 healthFactor; uint256 liquidatorPreviousATokenBalance; IAToken collateralAtoken; + IPriceOracleGetter oracle; bool isCollateralEnabled; DataTypes.InterestRateMode borrowRateMode; uint256 errorCode; @@ -89,33 +91,25 @@ contract LendingPoolCollateralManager is ) external override returns (uint256, string memory) { DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset]; DataTypes.ReserveData storage debtReserve = _reserves[debtAsset]; + DataTypes.ReserveCache memory debtReserveCache = debtReserve.cache(); DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user]; LiquidationCallLocalVars memory vars; - (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData( + + (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve); + vars.oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle()); + + (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall( + collateralReserve, + debtReserveCache, + vars.userStableDebt.add(vars.userVariableDebt), user, _reserves, userConfig, _reservesList, _reservesCount, - _addressesProvider.getPriceOracle() - ); - - DataTypes.ReserveCache memory debtReserveCache = debtReserve.cache(); - DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache(); - - (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve); - - (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall( - collateralReserve, - debtReserve, - debtReserveCache, - collateralReserveCache, - userConfig, - vars.healthFactor, - vars.userStableDebt, - vars.userVariableDebt + address(vars.oracle) ); if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) { @@ -139,11 +133,12 @@ contract LendingPoolCollateralManager is vars.debtAmountNeeded ) = _calculateAvailableCollateralToLiquidate( collateralReserve, - debtReserve, + debtReserveCache, collateralAsset, debtAsset, vars.actualDebtToLiquidate, - vars.userCollateralBalance + vars.userCollateralBalance, + vars.oracle ); // If debtAmountNeeded < actualDebtToLiquidate, there isn't enough @@ -210,6 +205,7 @@ contract LendingPoolCollateralManager is emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); } } else { + DataTypes.ReserveCache memory collateralReserveCache = collateralReserve.cache(); collateralReserve.updateState(collateralReserveCache); collateralReserve.updateInterestRates( collateralReserveCache, @@ -223,7 +219,7 @@ contract LendingPoolCollateralManager is user, msg.sender, vars.maxCollateralToLiquidate, - collateralReserve.liquidityIndex + collateralReserveCache.nextLiquidityIndex ); } @@ -237,7 +233,7 @@ contract LendingPoolCollateralManager is // Transfers the debt asset being repaid to the aToken, where the liquidity is kept IERC20(debtAsset).safeTransferFrom( msg.sender, - debtReserve.aTokenAddress, + debtReserveCache.aTokenAddress, vars.actualDebtToLiquidate ); @@ -270,7 +266,7 @@ contract LendingPoolCollateralManager is * - This function needs to be called after all the checks to validate the liquidation have been performed, * otherwise it might fail. * @param collateralReserve The data of the collateral reserve - * @param debtReserve The data of the debt reserve + * @param debtReserveCache The cached data of the debt reserve * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover @@ -281,15 +277,15 @@ contract LendingPoolCollateralManager is **/ function _calculateAvailableCollateralToLiquidate( DataTypes.ReserveData storage collateralReserve, - DataTypes.ReserveData storage debtReserve, + DataTypes.ReserveCache memory debtReserveCache, address collateralAsset, address debtAsset, uint256 debtToCover, - uint256 userCollateralBalance + uint256 userCollateralBalance, + IPriceOracleGetter oracle ) internal view returns (uint256, uint256) { uint256 collateralAmount = 0; uint256 debtAmountNeeded = 0; - IPriceOracleGetter oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle()); AvailableCollateralToLiquidateLocalVars memory vars; @@ -299,7 +295,7 @@ contract LendingPoolCollateralManager is (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve .configuration .getParams(); - vars.debtAssetDecimals = debtReserve.configuration.getDecimals(); + vars.debtAssetDecimals = debtReserveCache.reserveConfiguration.getDecimalsMemory(); // This is the maximum possible amount of the selected collateral that can be liquidated, given the // max amount of liquidatable debt diff --git a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol index 012e4cbd..b369aaee 100644 --- a/contracts/protocol/libraries/configuration/ReserveConfiguration.sol +++ b/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -70,7 +70,7 @@ library ReserveConfiguration { * @param self The reserve configuration * @return The loan to value **/ - function getLtvMemory(DataTypes.ReserveConfigurationMap memory self) internal view returns (uint256) { + function getLtvMemory(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) { return self.data & ~LTV_MASK; } @@ -104,18 +104,6 @@ library ReserveConfiguration { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } - /** - * @dev Gets the liquidation threshold of the reserve - * @param self The reserve configuration - * @return The liquidation threshold - **/ - function getLiquidationThresholdMemory(DataTypes.ReserveConfigurationMap memory self) - internal - view - returns (uint256) - { - return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; - } /** * @dev Sets the liquidation bonus of the reserve * @param self The reserve configuration @@ -172,7 +160,6 @@ library ReserveConfiguration { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; } - /** * @dev Gets the decimals of the underlying asset of the reserve * @param self The reserve configuration @@ -180,7 +167,7 @@ library ReserveConfiguration { **/ function getDecimalsMemory(DataTypes.ReserveConfigurationMap memory self) internal - view + pure returns (uint256) { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; @@ -336,7 +323,7 @@ library ReserveConfiguration { **/ function getReserveFactorMemory(DataTypes.ReserveConfigurationMap memory self) internal - view + pure returns (uint256) { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; diff --git a/contracts/protocol/libraries/logic/GenericLogic.sol b/contracts/protocol/libraries/logic/GenericLogic.sol index 549328a8..fb867ce2 100644 --- a/contracts/protocol/libraries/logic/GenericLogic.sol +++ b/contracts/protocol/libraries/logic/GenericLogic.sol @@ -198,4 +198,36 @@ library GenericLogic { availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); return availableBorrowsETH; } + + /** + * @dev proxy call for calculateUserAccountData as external function. + * Used in LendingPool to work around contract size limit issues + * @param user The address of the user + * @param reservesData Data of all the reserves + * @param userConfig The configuration of the user + * @param reserves The list of the available reserves + * @param oracle The price oracle address + * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF + **/ + function getUserAccountData( + address user, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap memory userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return + calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle); + } } diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 305559e2..86679f9a 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -186,7 +186,6 @@ library ReserveLogic { ) internal { UpdateInterestRatesLocalVars memory vars; - reserveCache.nextTotalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex ); @@ -386,25 +385,29 @@ library ReserveLogic { uint256 stableDebtBurned, uint256 variableDebtMinted, uint256 variableDebtBurned - ) internal { - uint256 scaledVariableDebtMinted = variableDebtMinted.rayDiv(cache.nextVariableBorrowIndex); - uint256 scaledVariableDebtBurned = variableDebtBurned.rayDiv(cache.nextVariableBorrowIndex); - - if (cache.currTotalStableDebt.add(stableDebtMinted) > stableDebtBurned) { - cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache - .currTotalStableDebt - .add(stableDebtMinted) - .sub(stableDebtBurned); - if (stableDebtMinted != 0 || stableDebtBurned != 0) { - cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) - .getAverageStableRate(); + ) internal view { + if (stableDebtMinted != 0 || stableDebtBurned != 0) { + if (cache.currTotalStableDebt.add(stableDebtMinted) > stableDebtBurned) { + cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache + .currTotalStableDebt + .add(stableDebtMinted) + .sub(stableDebtBurned); + if (stableDebtMinted != 0 || stableDebtBurned != 0) { + cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) + .getAverageStableRate(); + } + } else { + cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache + .nextAvgStableBorrowRate = 0; } - } else { - cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache.nextAvgStableBorrowRate = 0; } - cache.nextScaledVariableDebt = cache.currScaledVariableDebt.add(scaledVariableDebtMinted).sub( - scaledVariableDebtBurned - ); + if (variableDebtMinted != 0 || variableDebtBurned != 0) { + uint256 scaledVariableDebtMinted = variableDebtMinted.rayDiv(cache.nextVariableBorrowIndex); + uint256 scaledVariableDebtBurned = variableDebtBurned.rayDiv(cache.nextVariableBorrowIndex); + cache.nextScaledVariableDebt = cache.currScaledVariableDebt.add(scaledVariableDebtMinted).sub( + scaledVariableDebtBurned + ); + } } } diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index dc141a6c..21a6324d 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -16,6 +16,7 @@ import {Helpers} from '../helpers/Helpers.sol'; import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IScaledBalanceToken} from '../../../interfaces/IScaledBalanceToken.sol'; import {IAToken} from '../../../interfaces/IAToken.sol'; import {DataTypes} from '../types/DataTypes.sol'; import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol'; @@ -39,14 +40,13 @@ library ValidationLogic { /** * @dev Validates a deposit action - * @param reserve The reserve object on which the user is depositing + * @param reserveCache The cached data of the reserve * @param amount The amount to be deposited */ - function validateDeposit( - DataTypes.ReserveData storage reserve, - DataTypes.ReserveCache memory reserveCache, - uint256 amount - ) internal view { + function validateDeposit(DataTypes.ReserveCache memory reserveCache, uint256 amount) + internal + view + { (bool isActive, bool isFrozen, , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); (, , , uint256 reserveDecimals, ) = reserveCache.reserveConfiguration.getParamsMemory(); @@ -70,16 +70,15 @@ library ValidationLogic { /** * @dev Validates a withdraw action - * @param reserve The reserve object + * @param reserveCache The cached data of the reserve * @param amount The amount to be withdrawn * @param userBalance The balance of the user */ function validateWithdraw( - DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache, uint256 amount, uint256 userBalance - ) internal view { + ) internal pure { require(amount != 0, Errors.VL_INVALID_AMOUNT); require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); @@ -245,21 +244,20 @@ library ValidationLogic { /** * @dev Validates a repay action - * @param reserve The reserve state from which the user is repaying + * @param reserveCache The cached data of the reserve * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) * @param onBehalfOf The address of the user msg.sender is repaying for * @param stableDebt The borrow balance of the user * @param variableDebt The borrow balance of the user */ function validateRepay( - DataTypes.ReserveData storage reserve, DataTypes.ReserveCache memory reserveCache, uint256 amountSent, DataTypes.InterestRateMode rateMode, address onBehalfOf, uint256 stableDebt, uint256 variableDebt - ) external view { + ) internal view { (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(!isPaused, Errors.VL_RESERVE_PAUSED); @@ -371,12 +369,12 @@ library ValidationLogic { /** * @dev Validates the action of setting an asset as collateral - * @param reserve The state of the reserve that the user is enabling or disabling as collateral + * @param reserveCache The cached data of the reserve */ - function validateSetUseReserveAsCollateral( - DataTypes.ReserveData storage reserve, - DataTypes.ReserveCache memory reserveCache - ) external view { + function validateSetUseReserveAsCollateral(DataTypes.ReserveCache memory reserveCache) + external + view + { uint256 underlyingBalance = IERC20(reserveCache.aTokenAddress).balanceOf(msg.sender); (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); @@ -395,68 +393,88 @@ library ValidationLogic { address[] memory assets, uint256[] memory amounts, mapping(address => DataTypes.ReserveData) storage reservesData - ) external view { + ) internal view { for (uint256 i = 0; i < assets.length; i++) { require(!reservesData[assets[i]].configuration.getPaused(), Errors.VL_RESERVE_PAUSED); } require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); } + struct ValidateLiquidationCallLocalVars { + uint256 healthFactor; + bool collateralReserveActive; + bool collateralReservePaused; + bool principalReserveActive; + bool principalReservePaused; + bool isCollateralEnabled; + } + /** * @dev Validates the liquidation action * @param collateralReserve The reserve data of the collateral - * @param principalReserve The reserve data of the principal * @param userConfig The user configuration - * @param userHealthFactor The user's health factor - * @param userStableDebt Total stable debt balance of the user - * @param userVariableDebt Total variable debt balance of the user + * @param totalDebt Total debt balance of the user **/ function validateLiquidationCall( DataTypes.ReserveData storage collateralReserve, - DataTypes.ReserveData storage principalReserve, - DataTypes.ReserveCache memory collateralReserveCache, DataTypes.ReserveCache memory principalReserveCache, + uint256 totalDebt, + address user, + mapping(address => DataTypes.ReserveData) storage reservesData, DataTypes.UserConfigurationMap storage userConfig, - uint256 userHealthFactor, - uint256 userStableDebt, - uint256 userVariableDebt + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle ) internal view returns (uint256, string memory) { - (bool collateralReserveActive, , , , bool collateralReservePaused) = - collateralReserveCache.reserveConfiguration.getFlagsMemory(); + ValidateLiquidationCallLocalVars memory vars; - (bool principalReserveActive, , , , bool principalReservePaused) = - collateralReserveCache.reserveConfiguration.getFlagsMemory(); + (vars.collateralReserveActive, , , , vars.collateralReservePaused) = collateralReserve + .configuration + .getFlagsMemory(); - if (!collateralReserveActive || !principalReserveActive) { + (vars.principalReserveActive, , , , vars.principalReservePaused) = principalReserveCache + .reserveConfiguration + .getFlagsMemory(); + + if (!vars.collateralReserveActive || !vars.principalReserveActive) { return ( uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.VL_NO_ACTIVE_RESERVE ); } - if (collateralReservePaused || principalReservePaused) { + if (vars.collateralReservePaused || vars.principalReservePaused) { return (uint256(Errors.CollateralManagerErrors.PAUSED_RESERVE), Errors.VL_RESERVE_PAUSED); } - if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { + (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData( + user, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); + + if (vars.healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { return ( uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD), Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD ); } - bool isCollateralEnabled = - collateralReserveCache.reserveConfiguration.getLiquidationThresholdMemory() > 0 && - userConfig.isUsingAsCollateral(collateralReserve.id); + vars.isCollateralEnabled = + collateralReserve.configuration.getLiquidationThreshold() > 0 && + userConfig.isUsingAsCollateral(collateralReserve.id); //if collateral isn't enabled as collateral by user, it cannot be liquidated - if (!isCollateralEnabled) { + if (!vars.isCollateralEnabled) { return ( uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED), Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED ); } - if (userStableDebt == 0 && userVariableDebt == 0) { + if (totalDebt == 0) { return ( uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED), Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER @@ -482,7 +500,7 @@ library ValidationLogic { mapping(uint256 => address) storage reserves, uint256 reservesCount, address oracle - ) internal view { + ) external view { (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData( from, diff --git a/helpers/contracts-deployments.ts b/helpers/contracts-deployments.ts index 2d764885..05f79206 100644 --- a/helpers/contracts-deployments.ts +++ b/helpers/contracts-deployments.ts @@ -188,7 +188,8 @@ export const deployAaveLibraries = async ( return { ['__$de8c0cf1a7d7c36c802af9a64fb9d86036$__']: validationLogic.address, ['__$22cd43a9dda9ce44e9b92ba393b88fb9ac$__']: reserveLogic.address, - }; + ["__$52a8a86ab43135662ff256bbc95497e8e3$__"]: genericLogic.address, + } }; export const deployLendingPool = async (verify?: boolean) => { From 8451c88174f14d4ad07334c0e075490b28cb0151 Mon Sep 17 00:00:00 2001 From: emilio Date: Fri, 11 Jun 2021 11:53:43 +0200 Subject: [PATCH 56/64] refactor:removed unneeded diff --- contracts/protocol/lendingpool/LendingPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 3f87e74e..a5283d11 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -20,8 +20,8 @@ import {Errors} from '../libraries/helpers/Errors.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; -import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; +import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; From 065799eb29d8a2c67dfc92f855085dbb1cc98f7f Mon Sep 17 00:00:00 2001 From: emilio Date: Fri, 11 Jun 2021 17:03:52 +0200 Subject: [PATCH 57/64] comments: added comments to cache() and refreshDebt() --- .../protocol/libraries/logic/ReserveLogic.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 86679f9a..ca6521c6 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -340,6 +340,12 @@ library ReserveLogic { reserve.lastUpdateTimestamp = uint40(block.timestamp); } + /** + * @dev Creates a cache object to avoid repeated storage reads and external contract calls + * when updating state and interest rates. + * @param reserve The reserve object for which the cache will be filled + * @return The cache object + */ function cache(DataTypes.ReserveData storage reserve) internal view @@ -372,6 +378,8 @@ library ReserveLogic { reserveCache.stableDebtLastUpdateTimestamp ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData(); + // setting by default the debt data after the action with the same values as before + // if the action involves mint/burn of debt, the cache is updated through refreshDebt() reserveCache.nextPrincipalStableDebt = reserveCache.currPrincipalStableDebt; reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt; reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate; @@ -379,6 +387,15 @@ library ReserveLogic { return reserveCache; } + /** + * @dev Updates the debt data in the cache object. Invoked after an interaction caused minting/burning of the debt. + * MUST be invoked before updateInterestRates(). + * @param cache The cache object + * @param stableDebtMinted The stable debt minted as a consequence of the interaction + * @param stableDebtBurned The stable debt burned as a consequence of the interaction + * @param variableDebtMinted The variable debt minted as a consequence of the interaction + * @param variableDebtBurned The variable debt burned as a consequence of the interaction + */ function refreshDebt( DataTypes.ReserveCache memory cache, uint256 stableDebtMinted, From dd07e934c212563b3fa87d7f2d3797166d792615 Mon Sep 17 00:00:00 2001 From: emilio Date: Fri, 11 Jun 2021 19:45:31 +0200 Subject: [PATCH 58/64] comments: updated comments --- contracts/protocol/libraries/logic/ReserveLogic.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index ca6521c6..b44c3853 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -341,8 +341,7 @@ library ReserveLogic { } /** - * @dev Creates a cache object to avoid repeated storage reads and external contract calls - * when updating state and interest rates. + * @dev Creates a cache object to avoid repeated storage reads and external contract calls when updating state and interest rates. * @param reserve The reserve object for which the cache will be filled * @return The cache object */ @@ -378,8 +377,8 @@ library ReserveLogic { reserveCache.stableDebtLastUpdateTimestamp ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).getSupplyData(); - // setting by default the debt data after the action with the same values as before - // if the action involves mint/burn of debt, the cache is updated through refreshDebt() + // by default the actions are considered as not affecting the debt balances. + // if the action involves mint/burn of debt, the cache needs to be updated through refreshDebt() reserveCache.nextPrincipalStableDebt = reserveCache.currPrincipalStableDebt; reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt; reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate; @@ -388,8 +387,8 @@ library ReserveLogic { } /** - * @dev Updates the debt data in the cache object. Invoked after an interaction caused minting/burning of the debt. - * MUST be invoked before updateInterestRates(). + * @dev Updates the debt data in the cache object. MUST be invoked before updateInterestRates() when a protocol interaction + * causes minting or burning of debt. * @param cache The cache object * @param stableDebtMinted The stable debt minted as a consequence of the interaction * @param stableDebtBurned The stable debt burned as a consequence of the interaction From 0965a99d883f631e3cebb4fa083beb8facaa98f6 Mon Sep 17 00:00:00 2001 From: emilio Date: Sun, 13 Jun 2021 21:15:06 +0200 Subject: [PATCH 59/64] refactor: fixed natspec comments and removed redunant check in refreshDebt() --- .../protocol/libraries/logic/ReserveLogic.sol | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index b44c3853..91104182 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -341,10 +341,10 @@ library ReserveLogic { } /** - * @dev Creates a cache object to avoid repeated storage reads and external contract calls when updating state and interest rates. - * @param reserve The reserve object for which the cache will be filled - * @return The cache object - */ + * @dev Creates a cache object to avoid repeated storage reads and external contract calls when updating state and interest rates. + * @param reserve The reserve object for which the cache will be filled + * @return The cache object + */ function cache(DataTypes.ReserveData storage reserve) internal view @@ -387,14 +387,14 @@ library ReserveLogic { } /** - * @dev Updates the debt data in the cache object. MUST be invoked before updateInterestRates() when a protocol interaction - * causes minting or burning of debt. - * @param cache The cache object - * @param stableDebtMinted The stable debt minted as a consequence of the interaction - * @param stableDebtBurned The stable debt burned as a consequence of the interaction - * @param variableDebtMinted The variable debt minted as a consequence of the interaction - * @param variableDebtBurned The variable debt burned as a consequence of the interaction - */ + * @dev Updates the debt data in the cache object. MUST be invoked before updateInterestRates() when a protocol interaction + * causes minting or burning of debt. + * @param cache The cache object + * @param stableDebtMinted The stable debt minted as a consequence of the interaction + * @param stableDebtBurned The stable debt burned as a consequence of the interaction + * @param variableDebtMinted The variable debt minted as a consequence of the interaction + * @param variableDebtBurned The variable debt burned as a consequence of the interaction + */ function refreshDebt( DataTypes.ReserveCache memory cache, uint256 stableDebtMinted, @@ -408,10 +408,8 @@ library ReserveLogic { .currTotalStableDebt .add(stableDebtMinted) .sub(stableDebtBurned); - if (stableDebtMinted != 0 || stableDebtBurned != 0) { - cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) - .getAverageStableRate(); - } + cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) + .getAverageStableRate(); } else { cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache .nextAvgStableBorrowRate = 0; From 9f71c7da8eb4d08a709e9e66f6dce40839bf6003 Mon Sep 17 00:00:00 2001 From: emilio Date: Sun, 13 Jun 2021 21:54:23 +0200 Subject: [PATCH 60/64] comments: fixed natspec comments on validation --- .../protocol/libraries/logic/ValidationLogic.sol | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/libraries/logic/ValidationLogic.sol b/contracts/protocol/libraries/logic/ValidationLogic.sol index 21a6324d..26388f46 100644 --- a/contracts/protocol/libraries/logic/ValidationLogic.sol +++ b/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -109,6 +109,7 @@ library ValidationLogic { /** * @dev Validates a borrow action + * @param reserveCache the cached data of the reserve * @param asset The address of the asset to borrow * @param userAddress The address of the user * @param amount The amount to be borrowed @@ -246,6 +247,7 @@ library ValidationLogic { * @dev Validates a repay action * @param reserveCache The cached data of the reserve * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) + * @param rateMode the interest rate mode of the debt being repaid * @param onBehalfOf The address of the user msg.sender is repaying for * @param stableDebt The borrow balance of the user * @param variableDebt The borrow balance of the user @@ -281,10 +283,11 @@ library ValidationLogic { /** * @dev Validates a swap of borrow rate mode. * @param reserve The reserve state on which the user is swapping the rate + * @param reserveCache The cached data of the reserve * @param userConfig The user reserves configuration * @param stableDebt The stable debt of the user * @param variableDebt The variable debt of the user - * @param currentRateMode The rate mode of the borrow + * @param currentRateMode The rate mode of the debt being swapped */ function validateSwapRateMode( DataTypes.ReserveData storage reserve, @@ -328,6 +331,7 @@ library ValidationLogic { /** * @dev Validates a stable borrow rate rebalance action * @param reserve The reserve state on which the user is getting rebalanced + * @param reserveCache The cached state of the reserve * @param reserveAddress The address of the reserve * @param stableDebtToken The stable debt token instance * @param variableDebtToken The variable debt token instance @@ -412,8 +416,15 @@ library ValidationLogic { /** * @dev Validates the liquidation action * @param collateralReserve The reserve data of the collateral + * @param principalReserveCache The cached reserve data of the principal * @param userConfig The user configuration * @param totalDebt Total debt balance of the user + * @param user The address of the user being liquidated + * @param reservesData The mapping of the reserves data + * @param userConfig The user configuration mapping + * @param reserves The list of the reserves + * @param reservesCount The number of reserves in the list + * @param oracle The address of the price oracle **/ function validateLiquidationCall( DataTypes.ReserveData storage collateralReserve, From 978f6c93854c35a30d78bbdff458d915c18e7d07 Mon Sep 17 00:00:00 2001 From: emilio Date: Mon, 14 Jun 2021 15:47:13 +0200 Subject: [PATCH 61/64] refactor:removed unused/unneeded fields in the cache object --- .../protocol/libraries/logic/ReserveLogic.sol | 23 +++++++------------ .../protocol/libraries/types/DataTypes.sol | 2 -- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/contracts/protocol/libraries/logic/ReserveLogic.sol b/contracts/protocol/libraries/logic/ReserveLogic.sol index 91104182..bbcc6fb8 100644 --- a/contracts/protocol/libraries/logic/ReserveLogic.sol +++ b/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -161,9 +161,6 @@ library ReserveLogic { } struct UpdateInterestRatesLocalVars { - address stableDebtTokenAddress; - uint256 availableLiquidity; - uint256 totalStableDebt; uint256 newLiquidityRate; uint256 newStableRate; uint256 newVariableRate; @@ -186,10 +183,9 @@ library ReserveLogic { ) internal { UpdateInterestRatesLocalVars memory vars; - reserveCache.nextTotalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( + vars.totalVariableDebt = reserveCache.nextScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex ); - ( vars.newLiquidityRate, vars.newStableRate, @@ -200,7 +196,7 @@ library ReserveLogic { liquidityAdded, liquidityTaken, reserveCache.nextTotalStableDebt, - reserveCache.nextTotalVariableDebt, + vars.totalVariableDebt, reserveCache.nextAvgStableBorrowRate, reserveCache.reserveConfiguration.getReserveFactorMemory() ); @@ -252,12 +248,12 @@ library ReserveLogic { return; } - //calculate the last principal variable debt + //calculate the total variable debt at moment of the last interaction vars.prevTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul( reserveCache.currVariableBorrowIndex ); - //calculate the new total supply after accumulation of the index + //calculate the new total variable debt after accumulation of the interest on the index vars.currTotalVariableDebt = reserveCache.currScaledVariableDebt.rayMul( reserveCache.nextVariableBorrowIndex ); @@ -379,7 +375,6 @@ library ReserveLogic { // by default the actions are considered as not affecting the debt balances. // if the action involves mint/burn of debt, the cache needs to be updated through refreshDebt() - reserveCache.nextPrincipalStableDebt = reserveCache.currPrincipalStableDebt; reserveCache.nextTotalStableDebt = reserveCache.currTotalStableDebt; reserveCache.nextAvgStableBorrowRate = reserveCache.currAvgStableBorrowRate; @@ -404,15 +399,13 @@ library ReserveLogic { ) internal view { if (stableDebtMinted != 0 || stableDebtBurned != 0) { if (cache.currTotalStableDebt.add(stableDebtMinted) > stableDebtBurned) { - cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache - .currTotalStableDebt - .add(stableDebtMinted) - .sub(stableDebtBurned); + cache.nextTotalStableDebt = cache.currTotalStableDebt.add(stableDebtMinted).sub( + stableDebtBurned + ); cache.nextAvgStableBorrowRate = IStableDebtToken(cache.stableDebtTokenAddress) .getAverageStableRate(); } else { - cache.nextPrincipalStableDebt = cache.nextTotalStableDebt = cache - .nextAvgStableBorrowRate = 0; + cache.nextTotalStableDebt = cache.nextAvgStableBorrowRate = 0; } } diff --git a/contracts/protocol/libraries/types/DataTypes.sol b/contracts/protocol/libraries/types/DataTypes.sol index 4a2ce981..62cd4de5 100644 --- a/contracts/protocol/libraries/types/DataTypes.sol +++ b/contracts/protocol/libraries/types/DataTypes.sol @@ -55,11 +55,9 @@ library DataTypes { struct ReserveCache { uint256 currScaledVariableDebt; uint256 nextScaledVariableDebt; - uint256 nextTotalVariableDebt; uint256 currPrincipalStableDebt; uint256 currAvgStableBorrowRate; uint256 currTotalStableDebt; - uint256 nextPrincipalStableDebt; uint256 nextAvgStableBorrowRate; uint256 nextTotalStableDebt; uint256 currLiquidityIndex; From a579ca15d88e47ff5426c0be9fc9db724a597723 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 14 Jun 2021 17:02:36 +0200 Subject: [PATCH 62/64] doc: added natspec doc to `updateFlashloanPremiums` and `isFlashBorrowerAuthorized` --- contracts/protocol/lendingpool/LendingPool.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 75daa782..4fc706da 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -893,6 +893,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage } } + /** + * @dev Authorizes/Unauthorizes a flash borrower. Authorized borrowers pay no flash loan premium + * @param flashBorrower address of the flash borrower + * @param authorized `true` to authorize, `false` to unauthorize + */ function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external override @@ -901,10 +906,23 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage _authorizedFlashBorrowers[flashBorrower] = authorized; } + /** + * @dev Returns whether a flashborrower is authorized (pays no premium) + * @param flashBorrower address of the flash borrower + * @return `true` if authorized, `false` if not + */ function isFlashBorrowerAuthorized(address flashBorrower) external view override returns (bool) { return _authorizedFlashBorrowers[flashBorrower]; } + /** + * @dev Updates flash loan premiums + * flash loan premium consist in 2 parts + * - A part is sent to aToken holders as extra balance + * - A part is collecte by the protocol reserves + * @param flashLoanPremiumTotal total premium in bps + * @param flashLoanPremiumToProtocol part of the premium sent to protocol + */ function updateFlashloanPremiums( uint256 flashLoanPremiumTotal, uint256 flashLoanPremiumToProtocol From cdbfeef1dcde00ec480911c77fc815dcd8d4e1b4 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 14 Jun 2021 17:18:32 +0200 Subject: [PATCH 63/64] doc: added natspec doc --- contracts/interfaces/ILendingPoolConfigurator.sol | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index 055325b0..eb39b196 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -423,7 +423,18 @@ interface ILendingPoolConfigurator { **/ function dropReserve(address asset) external; + /** + * @dev Updates the total flash loan premium + * flash loan premium consist in 2 parts + * - A part is sent to aToken holders as extra balance + * - A part is collecte by the protocol reserves + * @param flashloanPremiumTotal total premium in bps + */ function updateFlashloanPremiumTotal(uint256 flashloanPremiumTotal) external; + /** + * @dev Updates the flash loan premium collected by protocol reserves + * @param flashloanPremiumToProtocol part of the premium sent to protocol + */ function updateFlashloanPremiumToProtocol(uint256 flashloanPremiumToProtocol) external; } From 1abfdda4af82ad98993c73391d4e963313ac25e4 Mon Sep 17 00:00:00 2001 From: Hadrien Charlanes Date: Mon, 14 Jun 2021 17:21:00 +0200 Subject: [PATCH 64/64] fix: typo --- contracts/interfaces/ILendingPoolConfigurator.sol | 2 +- contracts/protocol/lendingpool/LendingPool.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol index eb39b196..634bde26 100644 --- a/contracts/interfaces/ILendingPoolConfigurator.sol +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -427,7 +427,7 @@ interface ILendingPoolConfigurator { * @dev Updates the total flash loan premium * flash loan premium consist in 2 parts * - A part is sent to aToken holders as extra balance - * - A part is collecte by the protocol reserves + * - A part is collected by the protocol reserves * @param flashloanPremiumTotal total premium in bps */ function updateFlashloanPremiumTotal(uint256 flashloanPremiumTotal) external; diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 4fc706da..08cbd170 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -919,7 +919,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage * @dev Updates flash loan premiums * flash loan premium consist in 2 parts * - A part is sent to aToken holders as extra balance - * - A part is collecte by the protocol reserves + * - A part is collected by the protocol reserves * @param flashLoanPremiumTotal total premium in bps * @param flashLoanPremiumToProtocol part of the premium sent to protocol */