mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'protocol-2.5' into feat/2.5-exposure-ceiling
This commit is contained in:
commit
715635def4
51
contracts/mocks/tests/BorrowRepayTestMock.sol
Normal file
51
contracts/mocks/tests/BorrowRepayTestMock.sol
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
pragma solidity 0.6.12;
|
||||||
|
|
||||||
|
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||||
|
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||||
|
|
||||||
|
contract BorrowRepayTestMock {
|
||||||
|
ILendingPool _pool;
|
||||||
|
address _weth;
|
||||||
|
address _dai;
|
||||||
|
|
||||||
|
constructor(ILendingPool pool, address weth, address dai) public {
|
||||||
|
_pool = pool;
|
||||||
|
_weth = weth;
|
||||||
|
_dai = dai;
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeBorrowRepayVariable() external {
|
||||||
|
//mints 1 eth
|
||||||
|
MintableERC20(_weth).mint(1e18);
|
||||||
|
//deposits weth in the protocol
|
||||||
|
MintableERC20(_weth).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_weth, 1e18, address(this),0);
|
||||||
|
//borrow 1 wei of weth at variable
|
||||||
|
_pool.borrow(_weth, 1, 2, 0, address(this));
|
||||||
|
//repay 1 wei of weth (expected to fail)
|
||||||
|
_pool.repay(_weth, 1, 2, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeBorrowRepayStable() external {
|
||||||
|
//mints 1 eth
|
||||||
|
MintableERC20(_weth).mint(1e18);
|
||||||
|
//mints 1 dai
|
||||||
|
MintableERC20(_dai).mint(1e18);
|
||||||
|
//deposits weth in the protocol
|
||||||
|
MintableERC20(_weth).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_weth, 1e18, address(this),0);
|
||||||
|
|
||||||
|
//deposits dai in the protocol
|
||||||
|
MintableERC20(_dai).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_dai, 1e18, address(this),0);
|
||||||
|
|
||||||
|
//disabling dai as collateral so it can be borrowed at stable
|
||||||
|
_pool.setUserUseReserveAsCollateral(_dai, false);
|
||||||
|
//borrow 1 wei of dai at stable
|
||||||
|
_pool.borrow(_dai, 1, 1, 0, address(this));
|
||||||
|
|
||||||
|
//repay 1 wei of dai (expected to fail)
|
||||||
|
_pool.repay(_dai, 1, 1, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -240,7 +240,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
|
|
||||||
///@inheritdoc ILendingPool
|
///@inheritdoc ILendingPool
|
||||||
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
|
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
|
||||||
|
|
||||||
DataTypes.ReserveData storage reserve = _reserves[asset];
|
DataTypes.ReserveData storage reserve = _reserves[asset];
|
||||||
DataTypes.ReserveCache memory reserveCache = reserve.cache();
|
DataTypes.ReserveCache memory reserveCache = reserve.cache();
|
||||||
|
|
||||||
|
@ -795,6 +794,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
vars.releaseUnderlying ? vars.amount : 0
|
vars.releaseUnderlying ? vars.amount : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_lastBorrower = vars.user;
|
||||||
|
_lastBorrowTimestamp = uint40(block.timestamp);
|
||||||
|
|
||||||
if (vars.releaseUnderlying) {
|
if (vars.releaseUnderlying) {
|
||||||
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
||||||
}
|
}
|
||||||
|
@ -911,6 +913,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
||||||
|
|
||||||
ValidationLogic.validateRepay(
|
ValidationLogic.validateRepay(
|
||||||
|
_lastBorrower,
|
||||||
|
_lastBorrowTimestamp,
|
||||||
reserveCache,
|
reserveCache,
|
||||||
amount,
|
amount,
|
||||||
interestRateMode,
|
interestRateMode,
|
||||||
|
|
|
@ -530,6 +530,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur
|
||||||
override
|
override
|
||||||
onlyPoolAdmin
|
onlyPoolAdmin
|
||||||
{
|
{
|
||||||
|
require(
|
||||||
|
flashloanPremiumTotal < PercentageMath.PERCENTAGE_FACTOR,
|
||||||
|
Errors.LPC_FLASHLOAN_PREMIUM_INVALID
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
flashloanPremiumTotal >= _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL(),
|
||||||
|
Errors.LPC_FLASHLOAN_PREMIUMS_MISMATCH
|
||||||
|
);
|
||||||
_pool.updateFlashloanPremiums(flashloanPremiumTotal, _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL());
|
_pool.updateFlashloanPremiums(flashloanPremiumTotal, _pool.FLASHLOAN_PREMIUM_TO_PROTOCOL());
|
||||||
emit FlashloanPremiumTotalUpdated(flashloanPremiumTotal);
|
emit FlashloanPremiumTotalUpdated(flashloanPremiumTotal);
|
||||||
}
|
}
|
||||||
|
@ -540,6 +548,14 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur
|
||||||
override
|
override
|
||||||
onlyPoolAdmin
|
onlyPoolAdmin
|
||||||
{
|
{
|
||||||
|
require(
|
||||||
|
flashloanPremiumToProtocol < PercentageMath.PERCENTAGE_FACTOR,
|
||||||
|
Errors.LPC_FLASHLOAN_PREMIUM_INVALID
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
flashloanPremiumToProtocol <= _pool.FLASHLOAN_PREMIUM_TOTAL(),
|
||||||
|
Errors.LPC_FLASHLOAN_PREMIUMS_MISMATCH
|
||||||
|
);
|
||||||
_pool.updateFlashloanPremiums(_pool.FLASHLOAN_PREMIUM_TOTAL(), flashloanPremiumToProtocol);
|
_pool.updateFlashloanPremiums(_pool.FLASHLOAN_PREMIUM_TOTAL(), flashloanPremiumToProtocol);
|
||||||
emit FlashloanPremiumToProcolUpdated(flashloanPremiumToProtocol);
|
emit FlashloanPremiumToProcolUpdated(flashloanPremiumToProtocol);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,4 +33,8 @@ contract LendingPoolStorage {
|
||||||
mapping(address => bool) _authorizedFlashBorrowers;
|
mapping(address => bool) _authorizedFlashBorrowers;
|
||||||
|
|
||||||
uint256 internal _flashLoanPremiumToProtocol;
|
uint256 internal _flashLoanPremiumToProtocol;
|
||||||
|
|
||||||
|
address internal _lastBorrower;
|
||||||
|
|
||||||
|
uint40 internal _lastBorrowTimestamp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,9 @@ library Errors {
|
||||||
string public constant LP_CALLER_NOT_EOA = '91';
|
string public constant LP_CALLER_NOT_EOA = '91';
|
||||||
string public constant RC_INVALID_EXPOSURE_CAP = '92';
|
string public constant RC_INVALID_EXPOSURE_CAP = '92';
|
||||||
string public constant VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED = '93';
|
string public constant VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED = '93';
|
||||||
|
string public constant VL_SAME_BLOCK_BORROW_REPAY = '94';
|
||||||
|
string public constant LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95';
|
||||||
|
string public constant LPC_FLASHLOAN_PREMIUM_INVALID = '96';
|
||||||
|
|
||||||
enum CollateralManagerErrors {
|
enum CollateralManagerErrors {
|
||||||
NO_ERROR,
|
NO_ERROR,
|
||||||
|
|
|
@ -255,6 +255,8 @@ library ValidationLogic {
|
||||||
* @param variableDebt The borrow balance of the user
|
* @param variableDebt The borrow balance of the user
|
||||||
*/
|
*/
|
||||||
function validateRepay(
|
function validateRepay(
|
||||||
|
address lastBorrower,
|
||||||
|
uint40 lastBorrowTimestamp,
|
||||||
DataTypes.ReserveCache memory reserveCache,
|
DataTypes.ReserveCache memory reserveCache,
|
||||||
uint256 amountSent,
|
uint256 amountSent,
|
||||||
DataTypes.InterestRateMode rateMode,
|
DataTypes.InterestRateMode rateMode,
|
||||||
|
@ -268,6 +270,11 @@ library ValidationLogic {
|
||||||
|
|
||||||
require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
|
require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
|
||||||
|
|
||||||
|
require(
|
||||||
|
lastBorrower != onBehalfOf || lastBorrowTimestamp != uint40(block.timestamp),
|
||||||
|
Errors.VL_SAME_BLOCK_BORROW_REPAY
|
||||||
|
);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
(stableDebt > 0 &&
|
(stableDebt > 0 &&
|
||||||
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
|
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
|
||||||
|
@ -347,7 +354,6 @@ library ValidationLogic {
|
||||||
IERC20 variableDebtToken,
|
IERC20 variableDebtToken,
|
||||||
address aTokenAddress
|
address aTokenAddress
|
||||||
) external view {
|
) external view {
|
||||||
|
|
||||||
// to avoid potential abuses using flashloans, the rebalance stable rate must happen through an EOA
|
// to avoid potential abuses using flashloans, the rebalance stable rate must happen through an EOA
|
||||||
require(!address(msg.sender).isContract(), Errors.LP_CALLER_NOT_EOA);
|
require(!address(msg.sender).isContract(), Errors.LP_CALLER_NOT_EOA);
|
||||||
|
|
||||||
|
|
|
@ -190,6 +190,9 @@ export enum ProtocolErrors {
|
||||||
LP_CALLER_NOT_EOA = '91',
|
LP_CALLER_NOT_EOA = '91',
|
||||||
RC_INVALID_EXPOSURE_CAP = '92',
|
RC_INVALID_EXPOSURE_CAP = '92',
|
||||||
VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED = '93',
|
VL_COLLATERAL_EXPOSURE_CAP_EXCEEDED = '93',
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY = '94',
|
||||||
|
LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95',
|
||||||
|
LPC_FLASHLOAN_PREMIUM_INVALID = '96',
|
||||||
|
|
||||||
// old
|
// old
|
||||||
|
|
||||||
|
|
52
test-suites/test-aave/borrow-repay-same-tx.ts
Normal file
52
test-suites/test-aave/borrow-repay-same-tx.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { TestEnv, makeSuite } from './helpers/make-suite';
|
||||||
|
import {
|
||||||
|
APPROVAL_AMOUNT_LENDING_POOL,
|
||||||
|
MAX_UINT_AMOUNT,
|
||||||
|
RAY,
|
||||||
|
MAX_BORROW_CAP,
|
||||||
|
MAX_SUPPLY_CAP,
|
||||||
|
} from '../../helpers/constants';
|
||||||
|
import { ProtocolErrors } from '../../helpers/types';
|
||||||
|
import {
|
||||||
|
BorrowRepayTestMock,
|
||||||
|
BorrowRepayTestMockFactory,
|
||||||
|
MintableERC20,
|
||||||
|
WETH9,
|
||||||
|
WETH9Mocked,
|
||||||
|
} from '../../types';
|
||||||
|
import { parseEther } from '@ethersproject/units';
|
||||||
|
import { BigNumber } from '@ethersproject/bignumber';
|
||||||
|
import { waitForTx } from '../../helpers/misc-utils';
|
||||||
|
import { getFirstSigner } from '../../helpers/contracts-getters';
|
||||||
|
|
||||||
|
const { expect } = require('chai');
|
||||||
|
|
||||||
|
makeSuite('Borrow/repay in the same tx', (testEnv: TestEnv) => {
|
||||||
|
const { VL_SAME_BLOCK_BORROW_REPAY } = ProtocolErrors;
|
||||||
|
const unitParse = async (token: WETH9Mocked | MintableERC20, nb: string) =>
|
||||||
|
BigNumber.from(nb).mul(BigNumber.from('10').pow((await token.decimals()) - 3));
|
||||||
|
|
||||||
|
let testContract: BorrowRepayTestMock;
|
||||||
|
|
||||||
|
it('Deploys the test contract', async () => {
|
||||||
|
const { weth, dai, pool } = testEnv;
|
||||||
|
|
||||||
|
testContract = await (
|
||||||
|
await new BorrowRepayTestMockFactory(await getFirstSigner())
|
||||||
|
).deploy(pool.address, weth.address, dai.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Executes a test borrow/repay in the same transaction at variable (revert expected)', async () => {
|
||||||
|
await expect(testContract.executeBorrowRepayVariable()).to.be.revertedWith(
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY,
|
||||||
|
'Borrow/repay in the same transaction did not revert as expected'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Executes a test borrow/repay in the same transaction at stabke (revert expected)', async () => {
|
||||||
|
await expect(testContract.executeBorrowRepayStable()).to.be.revertedWith(
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY,
|
||||||
|
'Borrow/repay in the same transaction did not revert as expected'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -26,6 +26,8 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => {
|
||||||
LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN,
|
LPC_CALLER_NOT_EMERGENCY_OR_POOL_ADMIN,
|
||||||
LPC_CALLER_NOT_RISK_OR_POOL_ADMIN,
|
LPC_CALLER_NOT_RISK_OR_POOL_ADMIN,
|
||||||
VL_RESERVE_PAUSED,
|
VL_RESERVE_PAUSED,
|
||||||
|
LPC_FLASHLOAN_PREMIUMS_MISMATCH,
|
||||||
|
LPC_FLASHLOAN_PREMIUM_INVALID,
|
||||||
} = ProtocolErrors;
|
} = ProtocolErrors;
|
||||||
|
|
||||||
it('Reverts trying to set an invalid reserve factor', async () => {
|
it('Reverts trying to set an invalid reserve factor', async () => {
|
||||||
|
@ -1454,4 +1456,61 @@ makeSuite('LendingPoolConfigurator', (testEnv: TestEnv) => {
|
||||||
CALLER_NOT_POOL_ADMIN
|
CALLER_NOT_POOL_ADMIN
|
||||||
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
||||||
});
|
});
|
||||||
|
it('Update flash loan premiums: 10 toProtocol, 40 total', async () => {
|
||||||
|
const { dai, pool, configurator, users } = testEnv;
|
||||||
|
const newPremiumTotal = 40;
|
||||||
|
const newPremiumToProtocol = 10;
|
||||||
|
|
||||||
|
await configurator.updateFlashloanPremiumTotal(newPremiumTotal);
|
||||||
|
await configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol);
|
||||||
|
|
||||||
|
expect(await pool.FLASHLOAN_PREMIUM_TOTAL()).to.be.eq(newPremiumTotal);
|
||||||
|
expect(await pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()).to.be.eq(newPremiumToProtocol);
|
||||||
|
});
|
||||||
|
it('Fails to update flahloan premiums with toProtocol > total', async () => {
|
||||||
|
const { dai, pool, configurator, users } = testEnv;
|
||||||
|
const newPremiumTotal = 9;
|
||||||
|
const newPremiumToProtocol = 41;
|
||||||
|
|
||||||
|
await expect(configurator.updateFlashloanPremiumTotal(newPremiumTotal)).to.be.revertedWith(
|
||||||
|
LPC_FLASHLOAN_PREMIUMS_MISMATCH
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol)
|
||||||
|
).to.be.revertedWith(LPC_FLASHLOAN_PREMIUMS_MISMATCH);
|
||||||
|
});
|
||||||
|
it('Fails to update flahloan premiums > 100%', async () => {
|
||||||
|
const { dai, pool, configurator, users } = testEnv;
|
||||||
|
const newPremiumTotal = 10100;
|
||||||
|
const newPremiumToProtocol = 10100;
|
||||||
|
|
||||||
|
await expect(configurator.updateFlashloanPremiumTotal(newPremiumTotal)).to.be.revertedWith(
|
||||||
|
LPC_FLASHLOAN_PREMIUM_INVALID
|
||||||
|
);
|
||||||
|
await expect(
|
||||||
|
configurator.updateFlashloanPremiumToProtocol(newPremiumToProtocol)
|
||||||
|
).to.be.revertedWith(LPC_FLASHLOAN_PREMIUM_INVALID);
|
||||||
|
});
|
||||||
|
it('Checks only pool admin can update flashloan premiums', async () => {
|
||||||
|
const { dai, pool, configurator, users, riskAdmin, emergencyAdmin } = testEnv;
|
||||||
|
await expect(
|
||||||
|
configurator.connect(riskAdmin.signer).updateFlashloanPremiumToProtocol(50),
|
||||||
|
CALLER_NOT_POOL_ADMIN
|
||||||
|
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
configurator.connect(riskAdmin.signer).updateFlashloanPremiumTotal(50),
|
||||||
|
CALLER_NOT_POOL_ADMIN
|
||||||
|
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
configurator.connect(emergencyAdmin.signer).updateFlashloanPremiumToProtocol(50),
|
||||||
|
CALLER_NOT_POOL_ADMIN
|
||||||
|
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
configurator.connect(emergencyAdmin.signer).updateFlashloanPremiumTotal(50),
|
||||||
|
CALLER_NOT_POOL_ADMIN
|
||||||
|
).to.be.revertedWith(CALLER_NOT_POOL_ADMIN);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,12 +27,24 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
LP_BORROW_ALLOWANCE_NOT_ENOUGH,
|
LP_BORROW_ALLOWANCE_NOT_ENOUGH,
|
||||||
} = ProtocolErrors;
|
} = ProtocolErrors;
|
||||||
|
|
||||||
|
const TOTAL_PREMIUM = 9;
|
||||||
|
const PREMIUM_TO_PROTOCOL = 3;
|
||||||
|
const PREMIUM_TO_LP = TOTAL_PREMIUM - PREMIUM_TO_PROTOCOL;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
|
_mockFlashLoanReceiver = await getMockFlashLoanReceiver();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Configurator sets total premium = 9 bps, premium to protocol = 3 bps', async () => {
|
||||||
|
const { configurator, pool } = testEnv;
|
||||||
|
await configurator.updateFlashloanPremiumTotal(TOTAL_PREMIUM);
|
||||||
|
await configurator.updateFlashloanPremiumToProtocol(PREMIUM_TO_PROTOCOL);
|
||||||
|
|
||||||
|
expect(await pool.FLASHLOAN_PREMIUM_TOTAL()).to.be.equal(TOTAL_PREMIUM);
|
||||||
|
expect(await pool.FLASHLOAN_PREMIUM_TO_PROTOCOL()).to.be.equal(PREMIUM_TO_PROTOCOL);
|
||||||
|
});
|
||||||
it('Deposits WETH into the reserve', async () => {
|
it('Deposits WETH into the reserve', async () => {
|
||||||
const { pool, weth, aave } = testEnv;
|
const { pool, weth, aave, dai } = testEnv;
|
||||||
const userAddress = await pool.signer.getAddress();
|
const userAddress = await pool.signer.getAddress();
|
||||||
const amountToDeposit = ethers.utils.parseEther('1');
|
const amountToDeposit = ethers.utils.parseEther('1');
|
||||||
|
|
||||||
|
@ -47,44 +59,102 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
await aave.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await aave.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
await pool.deposit(aave.address, amountToDeposit, userAddress, '0');
|
await pool.deposit(aave.address, amountToDeposit, userAddress, '0');
|
||||||
|
await dai.mint(amountToDeposit);
|
||||||
|
|
||||||
|
await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
|
await pool.deposit(dai.address, amountToDeposit, userAddress, '0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Takes WETH flash loan with mode = 0, returns the funds correctly', async () => {
|
it('Takes WETH + Dai flash loan with mode = 0, returns the funds correctly', async () => {
|
||||||
const { pool, helpersContract, weth, aWETH } = testEnv;
|
const { pool, helpersContract, weth, aWETH, dai, aDai } = testEnv;
|
||||||
|
|
||||||
const flashBorrowedAmount = ethers.utils.parseEther('0.8');
|
const wethFlashBorrowedAmount = ethers.utils.parseEther('0.8');
|
||||||
const fees = new BigNumber(flashBorrowedAmount.mul(9).div(10000).toString());
|
const daiFlashBorrowedAmount = ethers.utils.parseEther('0.3');
|
||||||
|
const wethTotalFees = new BigNumber(
|
||||||
|
wethFlashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString()
|
||||||
|
);
|
||||||
|
const wethFeesToProtocol = wethFlashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000);
|
||||||
|
const wethFeesToLp = wethFlashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000);
|
||||||
|
const daiTotalFees = new BigNumber(
|
||||||
|
daiFlashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString()
|
||||||
|
);
|
||||||
|
const daiFeesToProtocol = daiFlashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000);
|
||||||
|
const daiFeesToLp = daiFlashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000);
|
||||||
|
|
||||||
let reserveData = await helpersContract.getReserveData(weth.address);
|
const wethLiquidityIndexAdded = wethFeesToLp
|
||||||
|
.mul(ethers.BigNumber.from(10).pow(27))
|
||||||
|
.div((await aWETH.totalSupply()).toString());
|
||||||
|
|
||||||
const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString())
|
const daiLiquidityIndexAdded = daiFeesToLp
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
.mul(ethers.BigNumber.from(10).pow(27))
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
.div((await aDai.totalSupply()).toString());
|
||||||
|
|
||||||
|
let wethReserveData = await helpersContract.getReserveData(weth.address);
|
||||||
|
let daiReserveData = await helpersContract.getReserveData(dai.address);
|
||||||
|
|
||||||
|
const wethLiquidityIndexBefore = wethReserveData.liquidityIndex;
|
||||||
|
const daiLiquidityIndexBefore = daiReserveData.liquidityIndex;
|
||||||
|
|
||||||
|
const wethTotalLiquidityBefore = new BigNumber(wethReserveData.availableLiquidity.toString())
|
||||||
|
.plus(wethReserveData.totalStableDebt.toString())
|
||||||
|
.plus(wethReserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
|
const daiTotalLiquidityBefore = new BigNumber(daiReserveData.availableLiquidity.toString())
|
||||||
|
.plus(daiReserveData.totalStableDebt.toString())
|
||||||
|
.plus(daiReserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
|
const wethReservesBefore = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS());
|
||||||
|
const daiReservesBefore = await aDai.balanceOf(await aDai.RESERVE_TREASURY_ADDRESS());
|
||||||
|
|
||||||
await pool.flashLoan(
|
await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[weth.address],
|
[weth.address, dai.address],
|
||||||
[flashBorrowedAmount],
|
[wethFlashBorrowedAmount, daiFlashBorrowedAmount],
|
||||||
[0],
|
[0, 0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
'0'
|
'0'
|
||||||
);
|
);
|
||||||
|
|
||||||
await pool.mintToTreasury([weth.address]);
|
await pool.mintToTreasury([weth.address, dai.address]);
|
||||||
|
|
||||||
reserveData = await helpersContract.getReserveData(weth.address);
|
wethReserveData = await helpersContract.getReserveData(weth.address);
|
||||||
|
daiReserveData = await helpersContract.getReserveData(dai.address);
|
||||||
|
|
||||||
const currentLiquidityRate = reserveData.liquidityRate;
|
const wethCurrentLiquidityRate = wethReserveData.liquidityRate;
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
const wethCurrentLiquidityIndex = wethReserveData.liquidityIndex;
|
||||||
|
const daiCurrentLiquidityRate = daiReserveData.liquidityRate;
|
||||||
|
const daiCurrentLiquidityIndex = daiReserveData.liquidityIndex;
|
||||||
|
|
||||||
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
const wethTotalLiquidityAfter = new BigNumber(wethReserveData.availableLiquidity.toString())
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
.plus(wethReserveData.totalStableDebt.toString())
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
.plus(wethReserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString());
|
const daiTotalLiquidityAfter = new BigNumber(daiReserveData.availableLiquidity.toString())
|
||||||
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
.plus(daiReserveData.totalStableDebt.toString())
|
||||||
expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000');
|
.plus(daiReserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
|
const wethReservesAfter = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS());
|
||||||
|
const daiReservesAfter = await aDai.balanceOf(await aDai.RESERVE_TREASURY_ADDRESS());
|
||||||
|
|
||||||
|
expect(wethTotalLiquidityBefore.plus(wethTotalFees).toString()).to.be.equal(
|
||||||
|
wethTotalLiquidityAfter.toString()
|
||||||
|
);
|
||||||
|
expect(wethCurrentLiquidityRate.toString()).to.be.equal('0');
|
||||||
|
expect(wethCurrentLiquidityIndex.toString()).to.be.equal(
|
||||||
|
wethLiquidityIndexBefore.add(wethLiquidityIndexAdded.toString()).toString()
|
||||||
|
);
|
||||||
|
expect(wethReservesAfter).to.be.equal(wethReservesBefore.add(wethFeesToProtocol));
|
||||||
|
|
||||||
|
expect(daiTotalLiquidityBefore.plus(daiTotalFees).toString()).to.be.equal(
|
||||||
|
daiTotalLiquidityAfter.toString()
|
||||||
|
);
|
||||||
|
expect(daiCurrentLiquidityRate.toString()).to.be.equal('0');
|
||||||
|
expect(daiCurrentLiquidityIndex.toString()).to.be.equal(
|
||||||
|
daiLiquidityIndexBefore.add(daiLiquidityIndexAdded.toString()).toString()
|
||||||
|
);
|
||||||
|
expect(daiReservesAfter).to.be.equal(daiReservesBefore.add(daiFeesToProtocol));
|
||||||
});
|
});
|
||||||
it('Takes an authorized AAVE flash loan with mode = 0, returns the funds correctly', async () => {
|
it('Takes an authorized AAVE flash loan with mode = 0, returns the funds correctly', async () => {
|
||||||
const {
|
const {
|
||||||
|
@ -97,7 +167,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
await configurator.authorizeFlashBorrower(authorizedUser.address);
|
await configurator.authorizeFlashBorrower(authorizedUser.address);
|
||||||
|
|
||||||
const flashBorrowedAmount = ethers.utils.parseEther('0.8');
|
const flashBorrowedAmount = ethers.utils.parseEther('0.8');
|
||||||
const fees = new BigNumber(0);
|
const totalFees = new BigNumber(0);
|
||||||
|
|
||||||
let reserveData = await helpersContract.getReserveData(aave.address);
|
let reserveData = await helpersContract.getReserveData(aave.address);
|
||||||
|
|
||||||
|
@ -119,33 +189,43 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await pool.mintToTreasury([aave.address]);
|
await pool.mintToTreasury([aave.address]);
|
||||||
|
|
||||||
ethers.utils.parseUnits('10000');
|
|
||||||
|
|
||||||
reserveData = await helpersContract.getReserveData(aave.address);
|
reserveData = await helpersContract.getReserveData(aave.address);
|
||||||
|
|
||||||
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
.plus(reserveData.totalStableDebt.toString())
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
.plus(reserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString());
|
expect(totalLiquidityBefore.plus(totalFees).toString()).to.be.equal(
|
||||||
|
totalLiquidityAfter.toString()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => {
|
it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => {
|
||||||
const { pool, helpersContract, weth } = testEnv;
|
const { pool, helpersContract, weth, aWETH } = testEnv;
|
||||||
|
|
||||||
let reserveData = await helpersContract.getReserveData(weth.address);
|
let reserveData = await helpersContract.getReserveData(weth.address);
|
||||||
|
|
||||||
const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString())
|
const totalLiquidityBefore = reserveData.availableLiquidity
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
.add(reserveData.totalStableDebt)
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
.add(reserveData.totalVariableDebt);
|
||||||
|
|
||||||
const flashBorrowedAmount = totalLiquidityBefore.toString();
|
const flashBorrowedAmount = totalLiquidityBefore;
|
||||||
|
|
||||||
const fees = new BigNumber(flashBorrowedAmount).multipliedBy(9).dividedBy(10000).toString();
|
const totalFees = new BigNumber(flashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString());
|
||||||
|
const feesToProtocol = flashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000);
|
||||||
|
const feesToLp = flashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000);
|
||||||
|
const liquidityIndexBefore = reserveData.liquidityIndex;
|
||||||
|
const liquidityIndexAdded = feesToLp
|
||||||
|
.mul(ethers.BigNumber.from(10).pow(27))
|
||||||
|
.div((await aWETH.totalSupply()).toString())
|
||||||
|
.mul(liquidityIndexBefore)
|
||||||
|
.div(ethers.BigNumber.from(10).pow(27));
|
||||||
|
|
||||||
|
const reservesBefore = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS());
|
||||||
|
|
||||||
const txResult = await pool.flashLoan(
|
const txResult = await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[totalLiquidityBefore.toString()],
|
[flashBorrowedAmount],
|
||||||
[0],
|
[0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
|
@ -156,22 +236,25 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
reserveData = await helpersContract.getReserveData(weth.address);
|
reserveData = await helpersContract.getReserveData(weth.address);
|
||||||
|
|
||||||
|
const currentLiquidityRate = reserveData.liquidityRate;
|
||||||
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
||||||
|
|
||||||
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
.plus(reserveData.totalStableDebt.toString())
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
.plus(reserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
const currentLiqudityRate = reserveData.liquidityRate;
|
const reservesAfter = await aWETH.balanceOf(await aWETH.RESERVE_TREASURY_ADDRESS());
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
expect(new BigNumber(totalLiquidityBefore.toString()).plus(totalFees).toString()).to.be.equal(
|
||||||
|
totalLiquidityAfter.toString()
|
||||||
const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString())
|
);
|
||||||
.plus(reserveData.totalStableDebt.toString())
|
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
||||||
.plus(reserveData.totalVariableDebt.toString());
|
expect(currentLiquidityIndex.toString()).to.be.equal(
|
||||||
|
liquidityIndexBefore.add(liquidityIndexAdded.toString()).toString()
|
||||||
expect(totalLiquidity.toString()).to.be.equal('1001620648000000000');
|
);
|
||||||
expect(currentLiqudityRate.toString()).to.be.equal('0');
|
expect(
|
||||||
expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000');
|
reservesAfter.sub(feesToProtocol).mul(liquidityIndexBefore).div(currentLiquidityIndex)
|
||||||
|
).to.be.equal(reservesBefore);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => {
|
it('Takes WETH flashloan, does not return the funds with mode = 0. (revert expected)', async () => {
|
||||||
const { pool, weth, users } = testEnv;
|
const { pool, weth, users } = testEnv;
|
||||||
const caller = users[1];
|
const caller = users[1];
|
||||||
|
@ -337,46 +420,59 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
|
it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
|
||||||
const { usdc, pool, helpersContract, deployer: depositor } = testEnv;
|
const { usdc, aUsdc, pool, helpersContract, deployer: depositor } = testEnv;
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
|
||||||
|
|
||||||
const reserveDataBefore = await helpersContract.getReserveData(usdc.address);
|
const flashBorrowedAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
||||||
|
const totalFees = new BigNumber(flashBorrowedAmount.mul(TOTAL_PREMIUM).div(10000).toString());
|
||||||
|
const feesToProtocol = flashBorrowedAmount.mul(PREMIUM_TO_PROTOCOL).div(10000);
|
||||||
|
const feesToLp = flashBorrowedAmount.mul(PREMIUM_TO_LP).div(10000);
|
||||||
|
const liquidityIndexAdded = feesToLp
|
||||||
|
.mul(ethers.BigNumber.from(10).pow(27))
|
||||||
|
.div((await aUsdc.totalSupply()).toString());
|
||||||
|
|
||||||
const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
|
let reserveData = await helpersContract.getReserveData(usdc.address);
|
||||||
|
|
||||||
|
const liquidityIndexBefore = reserveData.liquidityIndex;
|
||||||
|
|
||||||
|
const totalLiquidityBefore = new BigNumber(reserveData.availableLiquidity.toString())
|
||||||
|
.plus(reserveData.totalStableDebt.toString())
|
||||||
|
.plus(reserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
|
const reservesBefore = await aUsdc.balanceOf(await aUsdc.RESERVE_TREASURY_ADDRESS());
|
||||||
|
|
||||||
await pool.flashLoan(
|
await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[usdc.address],
|
[usdc.address],
|
||||||
[flashloanAmount],
|
[flashBorrowedAmount],
|
||||||
[0],
|
[0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
'0'
|
'0'
|
||||||
);
|
);
|
||||||
|
|
||||||
const reserveDataAfter = helpersContract.getReserveData(usdc.address);
|
await pool.mintToTreasury([usdc.address]);
|
||||||
|
|
||||||
const reserveData = await helpersContract.getReserveData(usdc.address);
|
reserveData = await helpersContract.getReserveData(usdc.address);
|
||||||
const userData = await helpersContract.getUserReserveData(usdc.address, depositor.address);
|
|
||||||
|
|
||||||
const totalLiquidity = reserveData.availableLiquidity
|
const currentLiquidityRate = reserveData.liquidityRate;
|
||||||
.add(reserveData.totalStableDebt)
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
||||||
.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.450');
|
const totalLiquidityAfter = new BigNumber(reserveData.availableLiquidity.toString())
|
||||||
|
.plus(reserveData.totalStableDebt.toString())
|
||||||
|
.plus(reserveData.totalVariableDebt.toString());
|
||||||
|
|
||||||
expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity');
|
const reservesAfter = await aUsdc.balanceOf(await aUsdc.RESERVE_TREASURY_ADDRESS());
|
||||||
expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate');
|
|
||||||
expect(currentLiquidityIndex).to.be.equal(
|
expect(totalLiquidityBefore.plus(totalFees).toString()).to.be.equal(
|
||||||
new BigNumber('1.00045').multipliedBy(oneRay).toFixed(),
|
totalLiquidityAfter.toString()
|
||||||
'Invalid liquidity index'
|
|
||||||
);
|
);
|
||||||
expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
|
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
||||||
|
expect(currentLiquidityIndex.toString()).to.be.equal(
|
||||||
|
liquidityIndexBefore.add(liquidityIndexAdded.toString()).toString()
|
||||||
|
);
|
||||||
|
expect(reservesAfter).to.be.equal(reservesBefore.add(feesToProtocol));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => {
|
it('Takes out a 500 USDC flashloan with mode = 0, does not return the funds. (revert expected)', async () => {
|
||||||
|
|
|
@ -47,10 +47,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => {
|
it('Takes WETH flashloan with mode = 0, returns the funds correctly', async () => {
|
||||||
const { pool, helpersContract, weth } = testEnv;
|
const { pool, helpersContract, weth } = testEnv;
|
||||||
|
|
||||||
|
const borrowedAmount = ethers.utils.parseEther('0.8');
|
||||||
|
|
||||||
await pool.flashLoan(
|
await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[ethers.utils.parseEther('0.8')],
|
[borrowedAmount],
|
||||||
[0],
|
[0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user