mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge pull request #145 from aave/feat/2.5-split-flashloan-fees
feat: split flashoan fees between fees to protocol and fees to LPs
This commit is contained in:
commit
b3b8d11a8c
|
@ -471,4 +471,17 @@ interface ILendingPool {
|
||||||
function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external;
|
function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized) external;
|
||||||
|
|
||||||
function isFlashBorrowerAuthorized(address flashBorrower) external view returns (bool);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,18 @@ interface ILendingPoolConfigurator {
|
||||||
**/
|
**/
|
||||||
event RiskAdminUnregistered(address indexed admin);
|
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
|
* @dev Initializes reserves in batch
|
||||||
* @param input The array of reserves initialization parameters
|
* @param input The array of reserves initialization parameters
|
||||||
|
@ -410,4 +422,19 @@ interface ILendingPoolConfigurator {
|
||||||
* @param asset the address of the reserve to drop
|
* @param asset the address of the reserve to drop
|
||||||
**/
|
**/
|
||||||
function dropReserve(address asset) external;
|
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 collected 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
_maxStableRateBorrowSizePercent = 2500;
|
_maxStableRateBorrowSizePercent = 2500;
|
||||||
_flashLoanPremiumTotal = 9;
|
_flashLoanPremiumTotal = 9;
|
||||||
_maxNumberOfReserves = 128;
|
_maxNumberOfReserves = 128;
|
||||||
|
_flashLoanPremiumToProtocol = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -432,10 +433,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
address currentAsset;
|
address currentAsset;
|
||||||
address currentATokenAddress;
|
address currentATokenAddress;
|
||||||
uint256 currentAmount;
|
uint256 currentAmount;
|
||||||
uint256 currentPremium;
|
uint256 currentPremiumToLP;
|
||||||
|
uint256 currentPremiumToProtocol;
|
||||||
uint256 currentAmountPlusPremium;
|
uint256 currentAmountPlusPremium;
|
||||||
address debtToken;
|
address debtToken;
|
||||||
uint256 flashloanPremiumTotal;
|
uint256 flashloanPremiumTotal;
|
||||||
|
uint256 flashloanPremiumToProtocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -469,36 +472,44 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
ValidationLogic.validateFlashloan(assets, amounts, _reserves);
|
ValidationLogic.validateFlashloan(assets, amounts, _reserves);
|
||||||
|
|
||||||
address[] memory aTokenAddresses = new address[](assets.length);
|
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.receiver = IFlashLoanReceiver(receiverAddress);
|
||||||
vars.flashloanPremiumTotal = _authorizedFlashBorrowers[msg.sender] ? 0 : _flashLoanPremiumTotal;
|
(vars.flashloanPremiumTotal, vars.flashloanPremiumToProtocol) = _authorizedFlashBorrowers[
|
||||||
|
msg.sender
|
||||||
|
]
|
||||||
|
? (0, 0)
|
||||||
|
: (_flashLoanPremiumTotal, _flashLoanPremiumToProtocol);
|
||||||
|
|
||||||
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
||||||
aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
|
aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
|
||||||
|
|
||||||
premiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal);
|
totalPremiums[vars.i] = amounts[vars.i].percentMul(vars.flashloanPremiumTotal);
|
||||||
|
vars.currentPremiumToProtocol = amounts[vars.i].percentMul(vars.flashloanPremiumToProtocol);
|
||||||
|
vars.currentPremiumToLP = totalPremiums[vars.i].sub(vars.currentPremiumToProtocol);
|
||||||
|
|
||||||
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
|
IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
require(
|
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
|
Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
|
||||||
);
|
);
|
||||||
|
|
||||||
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
||||||
vars.currentAsset = assets[vars.i];
|
vars.currentAsset = assets[vars.i];
|
||||||
vars.currentAmount = amounts[vars.i];
|
vars.currentAmount = amounts[vars.i];
|
||||||
vars.currentPremium = premiums[vars.i];
|
|
||||||
vars.currentATokenAddress = aTokenAddresses[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) {
|
if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) {
|
||||||
_reserves[vars.currentAsset].updateState();
|
_reserves[vars.currentAsset].updateState();
|
||||||
_reserves[vars.currentAsset].cumulateToLiquidityIndex(
|
_reserves[vars.currentAsset].cumulateToLiquidityIndex(
|
||||||
IERC20(vars.currentATokenAddress).totalSupply(),
|
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(
|
_reserves[vars.currentAsset].updateInterestRates(
|
||||||
vars.currentAsset,
|
vars.currentAsset,
|
||||||
vars.currentATokenAddress,
|
vars.currentATokenAddress,
|
||||||
|
@ -532,7 +543,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
msg.sender,
|
msg.sender,
|
||||||
vars.currentAsset,
|
vars.currentAsset,
|
||||||
vars.currentAmount,
|
vars.currentAmount,
|
||||||
vars.currentPremium,
|
totalPremiums[vars.i],
|
||||||
referralCode
|
referralCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -725,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
|
* @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;
|
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) {
|
function FLASHLOAN_PREMIUM_TOTAL() public view override returns (uint256) {
|
||||||
return _flashLoanPremiumTotal;
|
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
|
* @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;
|
return _maxNumberOfReserves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,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)
|
function updateFlashBorrowerAuthorization(address flashBorrower, bool authorized)
|
||||||
external
|
external
|
||||||
override
|
override
|
||||||
|
@ -883,10 +906,31 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
_authorizedFlashBorrowers[flashBorrower] = authorized;
|
_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) {
|
function isFlashBorrowerAuthorized(address flashBorrower) external view override returns (bool) {
|
||||||
return _authorizedFlashBorrowers[flashBorrower];
|
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 collected 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
|
||||||
|
) external override onlyLendingPoolConfigurator {
|
||||||
|
_flashLoanPremiumTotal = flashLoanPremiumTotal;
|
||||||
|
_flashLoanPremiumToProtocol = flashLoanPremiumToProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
struct ExecuteBorrowParams {
|
struct ExecuteBorrowParams {
|
||||||
address asset;
|
address asset;
|
||||||
address user;
|
address user;
|
||||||
|
|
|
@ -508,6 +508,26 @@ contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigur
|
||||||
return _riskAdmins[admin];
|
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)
|
function _initTokenWithProxy(address implementation, bytes memory initParams)
|
||||||
internal
|
internal
|
||||||
returns (address)
|
returns (address)
|
||||||
|
|
|
@ -31,4 +31,6 @@ contract LendingPoolStorage {
|
||||||
uint256 internal _maxNumberOfReserves;
|
uint256 internal _maxNumberOfReserves;
|
||||||
|
|
||||||
mapping(address => bool) _authorizedFlashBorrowers;
|
mapping(address => bool) _authorizedFlashBorrowers;
|
||||||
|
|
||||||
|
uint256 internal _flashLoanPremiumToProtocol;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ library ValidationLogic {
|
||||||
* @param reserve The reserve object on which the user is depositing
|
* @param reserve The reserve object on which the user is depositing
|
||||||
* @param amount The amount to be deposited
|
* @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;
|
DataTypes.ReserveConfigurationMap memory reserveConfiguration = reserve.configuration;
|
||||||
(bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory();
|
(bool isActive, bool isFrozen, , , bool isPaused) = reserveConfiguration.getFlagsMemory();
|
||||||
(, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory();
|
(, , , uint256 reserveDecimals, ) = reserveConfiguration.getParamsMemory();
|
||||||
|
@ -453,7 +453,7 @@ library ValidationLogic {
|
||||||
mapping(uint256 => address) storage reserves,
|
mapping(uint256 => address) storage reserves,
|
||||||
uint256 reservesCount,
|
uint256 reservesCount,
|
||||||
address oracle
|
address oracle
|
||||||
) internal view {
|
) external view {
|
||||||
(, , , , uint256 healthFactor) =
|
(, , , , uint256 healthFactor) =
|
||||||
GenericLogic.calculateUserAccountData(
|
GenericLogic.calculateUserAccountData(
|
||||||
from,
|
from,
|
||||||
|
@ -474,7 +474,7 @@ library ValidationLogic {
|
||||||
* @dev Validates a transfer action
|
* @dev Validates a transfer action
|
||||||
* @param reserve The reserve object
|
* @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);
|
require(!reserve.configuration.getPaused(), Errors.VL_RESERVE_PAUSED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
import { TestEnv, makeSuite } from './helpers/make-suite';
|
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 { convertToCurrencyDecimals, getContract } from '../../helpers/contracts-helpers';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver';
|
import { MockFlashLoanReceiver } from '../../types/MockFlashLoanReceiver';
|
||||||
|
@ -32,7 +32,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deposits WETH into the reserve', async () => {
|
it('Deposits WETH into the reserve', async () => {
|
||||||
const { pool, weth } = testEnv;
|
const { pool, weth, aave } = 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');
|
||||||
|
|
||||||
|
@ -41,52 +41,124 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
await weth.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||||
|
|
||||||
await pool.deposit(weth.address, amountToDeposit, userAddress, '0');
|
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 { pool, helpersContract, weth, aWETH } = 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(
|
await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
[ethers.utils.parseEther('0.8')],
|
[flashBorrowedAmount],
|
||||||
[0],
|
[0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
'0'
|
'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 currentLiquidityRate = reserveData.liquidityRate;
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
||||||
|
|
||||||
const totalLiquidity = 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(totalLiquidity.toString()).to.be.equal('1000720000000000000');
|
expect(totalLiquidityBefore.plus(fees).toString()).to.be.equal(totalLiquidityAfter.toString());
|
||||||
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
expect(currentLiquidityRate.toString()).to.be.equal('0');
|
||||||
expect(currentLiquidityIndex.toString()).to.be.equal('1000720000000000000000000000');
|
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([aave.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 () => {
|
it('Takes an ETH flashloan with mode = 0 as big as the available liquidity', async () => {
|
||||||
const { pool, helpersContract, weth } = testEnv;
|
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(
|
const txResult = await pool.flashLoan(
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
[weth.address],
|
[weth.address],
|
||||||
['1000720000000000000'],
|
[totalLiquidityBefore.toString()],
|
||||||
[0],
|
[0],
|
||||||
_mockFlashLoanReceiver.address,
|
_mockFlashLoanReceiver.address,
|
||||||
'0x10',
|
'0x10',
|
||||||
'0'
|
'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 currentLiqudityRate = reserveData.liquidityRate;
|
||||||
const currentLiquidityIndex = reserveData.liquidityIndex;
|
const currentLiquidityIndex = reserveData.liquidityIndex;
|
||||||
|
@ -177,6 +249,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
|
|
||||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
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
|
await pool
|
||||||
.connect(caller.signer)
|
.connect(caller.signer)
|
||||||
.flashLoan(
|
.flashLoan(
|
||||||
|
@ -191,14 +269,25 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
||||||
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
const { variableDebtTokenAddress } = await helpersContract.getReserveTokensAddresses(
|
||||||
weth.address
|
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 wethDebtToken = await getVariableDebtToken(variableDebtTokenAddress);
|
||||||
|
|
||||||
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
const callerDebt = await wethDebtToken.balanceOf(caller.address);
|
||||||
|
|
||||||
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
|
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 () => {
|
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
|
||||||
const { pool, weth, users } = testEnv;
|
const { pool, weth, users } = testEnv;
|
||||||
const caller = users[1];
|
const caller = users[1];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user