mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	Updated flashloans V2
This commit is contained in:
		
							parent
							
								
									e04d1881a1
								
							
						
					
					
						commit
						928770d9d5
					
				| 
						 | 
				
			
			@ -8,7 +8,7 @@ usePlugin('buidler-typechain');
 | 
			
		|||
usePlugin('solidity-coverage');
 | 
			
		||||
usePlugin('@nomiclabs/buidler-waffle');
 | 
			
		||||
usePlugin('@nomiclabs/buidler-etherscan');
 | 
			
		||||
usePlugin('buidler-gas-reporter');
 | 
			
		||||
//usePlugin('buidler-gas-reporter');
 | 
			
		||||
 | 
			
		||||
const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
 | 
			
		||||
const DEFAULT_GAS_PRICE = 10;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ interface ILendingPool {
 | 
			
		|||
   * @param reserve the address of the reserve
 | 
			
		||||
   * @param user the address of the user executing the swap
 | 
			
		||||
   **/
 | 
			
		||||
  event Swap(address indexed reserve, address indexed user, uint256 timestamp);
 | 
			
		||||
  event Swap(address indexed reserve, address indexed user);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev emitted when a user enables a reserve as collateral
 | 
			
		||||
| 
						 | 
				
			
			@ -91,12 +91,14 @@ interface ILendingPool {
 | 
			
		|||
   * @param reserve the address of the reserve
 | 
			
		||||
   * @param amount the amount requested
 | 
			
		||||
   * @param totalFee the total fee on the amount
 | 
			
		||||
   * @param referralCode the referral code of the caller
 | 
			
		||||
   **/
 | 
			
		||||
  event FlashLoan(
 | 
			
		||||
    address indexed target,
 | 
			
		||||
    address indexed reserve,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 totalFee
 | 
			
		||||
    uint256 totalFee,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  );
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev these events are not emitted directly by the LendingPool
 | 
			
		||||
| 
						 | 
				
			
			@ -105,21 +107,6 @@ interface ILendingPool {
 | 
			
		|||
   * This allows to have the events in the generated ABI for LendingPool.
 | 
			
		||||
   **/
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev emitted when a borrow fee is liquidated
 | 
			
		||||
   * @param collateral the address of the collateral being liquidated
 | 
			
		||||
   * @param reserve the address of the reserve
 | 
			
		||||
   * @param user the address of the user being liquidated
 | 
			
		||||
   * @param feeLiquidated the total fee liquidated
 | 
			
		||||
   * @param liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee
 | 
			
		||||
   **/
 | 
			
		||||
  event OriginationFeeLiquidated(
 | 
			
		||||
    address indexed collateral,
 | 
			
		||||
    address indexed reserve,
 | 
			
		||||
    address indexed user,
 | 
			
		||||
    uint256 feeLiquidated,
 | 
			
		||||
    uint256 liquidatedCollateralForFee
 | 
			
		||||
  );
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev emitted when a borrower is liquidated
 | 
			
		||||
   * @param collateral the address of the collateral being liquidated
 | 
			
		||||
| 
						 | 
				
			
			@ -238,12 +225,15 @@ interface ILendingPool {
 | 
			
		|||
   * @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
 | 
			
		||||
   * @param reserve the address of the principal reserve
 | 
			
		||||
   * @param amount the amount requested for this flashloan
 | 
			
		||||
   * @param params a bytes array to be sent to the flashloan executor
 | 
			
		||||
   * @param referralCode the referral code of the caller
 | 
			
		||||
   **/
 | 
			
		||||
  function flashLoan(
 | 
			
		||||
    address receiver,
 | 
			
		||||
    address reserve,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    bytes calldata params
 | 
			
		||||
    bytes calldata params,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,7 +108,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    //transfer to the aToken contract
 | 
			
		||||
    IERC20(asset).safeTransferFrom(msg.sender, address(aToken), amount);
 | 
			
		||||
 | 
			
		||||
    //solium-disable-next-line
 | 
			
		||||
    emit Deposit(asset, msg.sender, amount, referralCode);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +151,6 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
    aToken.burn(msg.sender, msg.sender, amountToWithdraw);
 | 
			
		||||
 | 
			
		||||
    //solium-disable-next-line
 | 
			
		||||
    emit Withdraw(asset, msg.sender, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -320,9 +318,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
    emit Swap(
 | 
			
		||||
      asset,
 | 
			
		||||
      msg.sender,
 | 
			
		||||
      //solium-disable-next-line
 | 
			
		||||
      block.timestamp
 | 
			
		||||
      msg.sender
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -441,6 +437,15 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct FlashLoanLocalVars{
 | 
			
		||||
    uint256 amountFee;
 | 
			
		||||
    uint256 amountPlusFee;
 | 
			
		||||
    uint256 amountPlusFeeInETH;
 | 
			
		||||
    IFlashLoanReceiver receiver;
 | 
			
		||||
    address aTokenAddress;
 | 
			
		||||
    address oracle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev allows smartcontracts to access the liquidity of the pool within one transaction,
 | 
			
		||||
   * as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
 | 
			
		||||
| 
						 | 
				
			
			@ -453,40 +458,77 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    address receiverAddress,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    bytes calldata params
 | 
			
		||||
    bytes calldata params,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  ) external override {
 | 
			
		||||
    ReserveLogic.ReserveData storage reserve = _reserves[asset];
 | 
			
		||||
    FlashLoanLocalVars memory vars;
 | 
			
		||||
 | 
			
		||||
    address aTokenAddress = reserve.aTokenAddress;
 | 
			
		||||
    vars.aTokenAddress = reserve.aTokenAddress;
 | 
			
		||||
 | 
			
		||||
    //calculate amount fee
 | 
			
		||||
    uint256 amountFee = amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
 | 
			
		||||
    vars.amountFee = amount.mul(FLASHLOAN_FEE_TOTAL).div(10000);
 | 
			
		||||
 | 
			
		||||
    require(amountFee > 0, 'The requested amount is too small for a FlashLoan.');
 | 
			
		||||
    require(vars.amountFee > 0, 'The requested amount is too small for a FlashLoan.');
 | 
			
		||||
 | 
			
		||||
    //get the FlashLoanReceiver instance
 | 
			
		||||
    IFlashLoanReceiver receiver = IFlashLoanReceiver(receiverAddress);
 | 
			
		||||
    vars.receiver = IFlashLoanReceiver(receiverAddress);
 | 
			
		||||
 | 
			
		||||
    //transfer funds to the receiver
 | 
			
		||||
    IAToken(aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
 | 
			
		||||
    IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
 | 
			
		||||
 | 
			
		||||
    //execute action of the receiver
 | 
			
		||||
    receiver.executeOperation(asset, amount, amountFee, params);
 | 
			
		||||
    vars.receiver.executeOperation(asset, amount, vars.amountFee, params);
 | 
			
		||||
 | 
			
		||||
    //transfer from the receiver the amount plus the fee
 | 
			
		||||
    IERC20(asset).safeTransferFrom(receiverAddress, aTokenAddress, amount.add(amountFee));
 | 
			
		||||
 | 
			
		||||
       //compounding the cumulated interest
 | 
			
		||||
    //compounding the cumulated interest
 | 
			
		||||
    reserve.updateCumulativeIndexesAndTimestamp();
 | 
			
		||||
 | 
			
		||||
    //compounding the received fee into the reserve
 | 
			
		||||
    reserve.cumulateToLiquidityIndex(IERC20(aTokenAddress).totalSupply(), amountFee);
 | 
			
		||||
    vars.amountPlusFee = amount.add(vars.amountFee);
 | 
			
		||||
 | 
			
		||||
    //refresh interest rates
 | 
			
		||||
    reserve.updateInterestRates(asset, amountFee, 0);
 | 
			
		||||
    //transfer from the receiver the amount plus the fee
 | 
			
		||||
    try IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusFee) {
 | 
			
		||||
      //if the transfer succeeded, the executor has repaid the flashloans.
 | 
			
		||||
      //the fee is compounded into the reserve
 | 
			
		||||
      reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.amountFee);
 | 
			
		||||
      //refresh interest rates
 | 
			
		||||
      reserve.updateInterestRates(asset, vars.amountFee, 0);
 | 
			
		||||
      emit FlashLoan(receiverAddress, asset, amount, vars.amountFee, referralCode);
 | 
			
		||||
    }
 | 
			
		||||
    catch(bytes memory reason){
 | 
			
		||||
 | 
			
		||||
    //solium-disable-next-line
 | 
			
		||||
    emit FlashLoan(receiverAddress, asset, amount, amountFee);
 | 
			
		||||
      //if the transfer didn't succeed, the executor either didn't return the funds, or didn't approve the transfer.
 | 
			
		||||
      //we check if the caller has enough collateral to open a variable rate loan. If it does, then debt is mint to msg.sender
 | 
			
		||||
      vars.oracle = addressesProvider.getPriceOracle();
 | 
			
		||||
      vars.amountPlusFeeInETH = IPriceOracleGetter(vars.oracle)
 | 
			
		||||
      .getAssetPrice(asset)
 | 
			
		||||
      .mul(vars.amountPlusFee)
 | 
			
		||||
      .div(10**reserve.configuration.getDecimals()); //price is in ether
 | 
			
		||||
 | 
			
		||||
      ValidationLogic.validateBorrow(
 | 
			
		||||
      reserve,
 | 
			
		||||
      asset,
 | 
			
		||||
      vars.amountPlusFee,
 | 
			
		||||
      vars.amountPlusFeeInETH,
 | 
			
		||||
      uint256(ReserveLogic.InterestRateMode.VARIABLE),
 | 
			
		||||
      MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
 | 
			
		||||
      _reserves,
 | 
			
		||||
      _usersConfig[msg.sender],
 | 
			
		||||
      reservesList,
 | 
			
		||||
      vars.oracle
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      IVariableDebtToken(reserve.variableDebtTokenAddress).mint(msg.sender, vars.amountPlusFee);
 | 
			
		||||
      //refresh interest rates
 | 
			
		||||
      reserve.updateInterestRates(asset, vars.amountFee, 0);
 | 
			
		||||
      emit Borrow(
 | 
			
		||||
        asset,
 | 
			
		||||
        msg.sender,
 | 
			
		||||
        vars.amountPlusFee,
 | 
			
		||||
        uint256(ReserveLogic.InterestRateMode.VARIABLE),
 | 
			
		||||
        reserve.currentVariableBorrowRate,
 | 
			
		||||
        referralCode
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,10 +62,6 @@ library ValidationLogic {
 | 
			
		|||
  ) external view {
 | 
			
		||||
    require(amount > 0, 'Amount must be greater than 0');
 | 
			
		||||
 | 
			
		||||
    uint256 currentAvailableLiquidity = IERC20(reserveAddress).balanceOf(address(aTokenAddress));
 | 
			
		||||
 | 
			
		||||
    require(currentAvailableLiquidity >= amount, '4');
 | 
			
		||||
 | 
			
		||||
    require(amount <= userBalance, 'User cannot withdraw more than the available balance');
 | 
			
		||||
 | 
			
		||||
    require(
 | 
			
		||||
| 
						 | 
				
			
			@ -150,11 +146,6 @@ library ValidationLogic {
 | 
			
		|||
      'Invalid interest rate mode selected'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    //check that the amount is available in the reserve
 | 
			
		||||
    vars.availableLiquidity = IERC20(reserveAddress).balanceOf(address(reserve.aTokenAddress));
 | 
			
		||||
 | 
			
		||||
    require(vars.availableLiquidity >= amount, '7');
 | 
			
		||||
 | 
			
		||||
    (
 | 
			
		||||
      vars.userCollateralBalanceETH,
 | 
			
		||||
      vars.userBorrowBalanceETH,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,6 +63,7 @@ export enum ProtocolErrors {
 | 
			
		|||
  INVALID_HF = 'Invalid health factor',
 | 
			
		||||
  USER_DID_NOT_BORROW_SPECIFIED = 'User did not borrow the specified currency',
 | 
			
		||||
  THE_COLLATERAL_CHOSEN_CANNOT_BE_LIQUIDATED = 'The collateral chosen cannot be liquidated',
 | 
			
		||||
  COLLATERAL_BALANCE_IS_0 = 'The collateral balance is 0'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type tEthereumAddress = string;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,20 @@
 | 
			
		|||
import {TestEnv, makeSuite} from './helpers/make-suite';
 | 
			
		||||
import {APPROVAL_AMOUNT_LENDING_POOL, oneRay} from '../helpers/constants';
 | 
			
		||||
import {convertToCurrencyDecimals, getMockFlashLoanReceiver} from '../helpers/contracts-helpers';
 | 
			
		||||
import {ethers} from 'ethers';
 | 
			
		||||
import {MockFlashLoanReceiver} from '../types/MockFlashLoanReceiver';
 | 
			
		||||
import {ProtocolErrors} from '../helpers/types';
 | 
			
		||||
import { TestEnv, makeSuite } from './helpers/make-suite';
 | 
			
		||||
import { APPROVAL_AMOUNT_LENDING_POOL, oneRay } from '../helpers/constants';
 | 
			
		||||
import { convertToCurrencyDecimals, getMockFlashLoanReceiver, getContract } from '../helpers/contracts-helpers';
 | 
			
		||||
import { ethers } from 'ethers';
 | 
			
		||||
import { MockFlashLoanReceiver } from '../types/MockFlashLoanReceiver';
 | 
			
		||||
import { ProtocolErrors, eContractid } from '../helpers/types';
 | 
			
		||||
import BigNumber from 'bignumber.js';
 | 
			
		||||
import { VariableDebtToken } from '../types/VariableDebtToken';
 | 
			
		||||
 | 
			
		||||
const {expect} = require('chai');
 | 
			
		||||
const { expect } = require('chai');
 | 
			
		||||
 | 
			
		||||
makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		||||
  let _mockFlashLoanReceiver = {} as MockFlashLoanReceiver;
 | 
			
		||||
  const {
 | 
			
		||||
    TRANSFER_AMOUNT_EXCEEDS_BALANCE,
 | 
			
		||||
    TOO_SMALL_FLASH_LOAN,
 | 
			
		||||
    COLLATERAL_BALANCE_IS_0,
 | 
			
		||||
  } = ProtocolErrors;
 | 
			
		||||
 | 
			
		||||
  before(async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +22,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  it('Deposits ETH into the reserve', async () => {
 | 
			
		||||
    const {pool, weth} = testEnv;
 | 
			
		||||
    const { pool, weth } = testEnv;
 | 
			
		||||
    const amountToDeposit = ethers.utils.parseEther('1');
 | 
			
		||||
 | 
			
		||||
    await weth.mint(amountToDeposit);
 | 
			
		||||
| 
						 | 
				
			
			@ -31,13 +33,14 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  it('Takes ETH flashloan, returns the funds correctly', async () => {
 | 
			
		||||
    const {pool, deployer, weth} = testEnv;
 | 
			
		||||
    const { pool, deployer, weth } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await pool.flashLoan(
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      weth.address,
 | 
			
		||||
      ethers.utils.parseEther('0.8'),
 | 
			
		||||
      '0x10'
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    ethers.utils.parseUnits('10000');
 | 
			
		||||
| 
						 | 
				
			
			@ -57,17 +60,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  it('Takes an ETH flashloan as big as the available liquidity', async () => {
 | 
			
		||||
    const {pool, weth} = testEnv;
 | 
			
		||||
    const { pool, weth } = testEnv;
 | 
			
		||||
 | 
			
		||||
    const reserveDataBefore = await pool.getReserveData(weth.address);
 | 
			
		||||
 | 
			
		||||
    console.log("Total liquidity is ", reserveDataBefore.availableLiquidity.toString());
 | 
			
		||||
 | 
			
		||||
    const txResult = await pool.flashLoan(
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      weth.address,
 | 
			
		||||
      '1000720000000000000',
 | 
			
		||||
      '0x10'
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const reserveData = await pool.getReserveData(weth.address);
 | 
			
		||||
| 
						 | 
				
			
			@ -84,83 +85,122 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
    expect(currentLiquidityIndex.toString()).to.be.equal('1001620648000000000000000000');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Takes WETH flashloan, does not return the funds (revert expected)', async () => {
 | 
			
		||||
    const {pool, deployer, weth} = testEnv;
 | 
			
		||||
 | 
			
		||||
    // move funds to the MockFlashLoanReceiver contract to pay the fee
 | 
			
		||||
 | 
			
		||||
  it('Takes WETH flashloan, does not return the funds. Caller does not have any collateral (revert expected)', async () => {
 | 
			
		||||
    const { pool, weth, users } = testEnv;
 | 
			
		||||
    const caller = users[1];
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.flashLoan(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(caller.signer)
 | 
			
		||||
        .flashLoan(
 | 
			
		||||
          _mockFlashLoanReceiver.address,
 | 
			
		||||
          weth.address,
 | 
			
		||||
          ethers.utils.parseEther('0.8'),
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
    ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan, does not return the funds. A loan for caller is created', async () => {
 | 
			
		||||
    const { dai, pool, weth, users } = 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, '0');
 | 
			
		||||
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
 | 
			
		||||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(caller.signer)
 | 
			
		||||
      .flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        weth.address,
 | 
			
		||||
        ethers.utils.parseEther('0.8'),
 | 
			
		||||
        '0x10'
 | 
			
		||||
      )
 | 
			
		||||
    ).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
    const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(weth.address);
 | 
			
		||||
 | 
			
		||||
    const wethDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress);
 | 
			
		||||
 | 
			
		||||
    const callerDebt = await wethDebtToken.balanceOf(caller.address);
 | 
			
		||||
 | 
			
		||||
    expect(callerDebt.toString()).to.be.equal('800720000000000000', 'Invalid user debt');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('tries to take a very small flashloan, which would result in 0 fees (revert expected)', async () => {
 | 
			
		||||
    const {pool, weth} = testEnv;
 | 
			
		||||
    const { pool, weth } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        weth.address,
 | 
			
		||||
        '1', //1 wei loan
 | 
			
		||||
        '0x10'
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      )
 | 
			
		||||
    ).to.be.revertedWith(TOO_SMALL_FLASH_LOAN);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
 | 
			
		||||
    const {pool, weth} = testEnv;
 | 
			
		||||
    const { pool, weth } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        weth.address,
 | 
			
		||||
        '1004415000000000000', //slightly higher than the available liquidity
 | 
			
		||||
        '0x10'
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      ),
 | 
			
		||||
      TRANSFER_AMOUNT_EXCEEDS_BALANCE
 | 
			
		||||
    ).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => {
 | 
			
		||||
    const {pool, deployer, weth} = testEnv;
 | 
			
		||||
    const { pool, deployer, weth } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await expect(pool.flashLoan(deployer.address, weth.address, '1000000000000000000', '0x10')).to
 | 
			
		||||
      .be.reverted;
 | 
			
		||||
    await expect(pool.flashLoan(deployer.address, weth.address, '1000000000000000000', '0x10', '0'))
 | 
			
		||||
      .to.be.reverted;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Deposits DAI into the reserve', async () => {
 | 
			
		||||
    const {dai, pool} = testEnv;
 | 
			
		||||
  it('Deposits USDC into the reserve', async () => {
 | 
			
		||||
    const { usdc, pool } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await dai.mint(await convertToCurrencyDecimals(dai.address, '1000'));
 | 
			
		||||
    await usdc.mint(await convertToCurrencyDecimals(usdc.address, '1000'));
 | 
			
		||||
 | 
			
		||||
    await dai.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
 | 
			
		||||
    await usdc.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
 | 
			
		||||
 | 
			
		||||
    const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
 | 
			
		||||
    const amountToDeposit = await convertToCurrencyDecimals(usdc.address, '1000');
 | 
			
		||||
 | 
			
		||||
    await pool.deposit(dai.address, amountToDeposit, '0');
 | 
			
		||||
    await pool.deposit(usdc.address, amountToDeposit, '0');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Takes out a 500 DAI flashloan, returns the funds correctly', async () => {
 | 
			
		||||
    const {dai, pool, deployer: depositor} = testEnv;
 | 
			
		||||
  it('Takes out a 500 USDC flashloan, returns the funds correctly', async () => {
 | 
			
		||||
    const { usdc, pool, deployer: depositor } = testEnv;
 | 
			
		||||
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(false);
 | 
			
		||||
 | 
			
		||||
    const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
 | 
			
		||||
 | 
			
		||||
    await pool.flashLoan(
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      dai.address,
 | 
			
		||||
      ethers.utils.parseEther('500'),
 | 
			
		||||
      '0x10'
 | 
			
		||||
      usdc.address,
 | 
			
		||||
      flashloanAmount,
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const reserveData = await pool.getReserveData(dai.address);
 | 
			
		||||
    const userData = await pool.getUserReserveData(dai.address, depositor.address);
 | 
			
		||||
    const reserveData = await pool.getReserveData(usdc.address);
 | 
			
		||||
    const userData = await pool.getUserReserveData(usdc.address, depositor.address);
 | 
			
		||||
 | 
			
		||||
    const totalLiquidity = reserveData.availableLiquidity
 | 
			
		||||
      .add(reserveData.totalBorrowsStable)
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +210,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
    const currentLiquidityIndex = reserveData.liquidityIndex.toString();
 | 
			
		||||
    const currentUserBalance = userData.currentATokenBalance.toString();
 | 
			
		||||
 | 
			
		||||
    const expectedLiquidity = ethers.utils.parseEther('1000.450');
 | 
			
		||||
    const expectedLiquidity = await convertToCurrencyDecimals(usdc.address,'1000.450');
 | 
			
		||||
 | 
			
		||||
    expect(totalLiquidity).to.be.equal(expectedLiquidity, 'Invalid total liquidity');
 | 
			
		||||
    expect(currentLiqudityRate).to.be.equal('0', 'Invalid liquidity rate');
 | 
			
		||||
| 
						 | 
				
			
			@ -181,19 +221,59 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
    expect(currentUserBalance.toString()).to.be.equal(expectedLiquidity, 'Invalid user balance');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Takes out a 500 DAI flashloan, does not return the funds (revert expected)', async () => {
 | 
			
		||||
    const {dai, pool} = testEnv;
 | 
			
		||||
  it('Takes out a 500 USDC flashloan, does not return the funds. Caller does not have any collateral (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.flashLoan(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(caller.signer)
 | 
			
		||||
        .flashLoan(
 | 
			
		||||
          _mockFlashLoanReceiver.address,
 | 
			
		||||
          usdc.address,
 | 
			
		||||
          flashloanAmount,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
    ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Caller deposits 5 ETH as collateral, Takes a USDC flashloan, does not return the funds. A loan for caller is created', async () => {
 | 
			
		||||
    const { usdc, pool, weth, users } = 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, '0');
 | 
			
		||||
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
 | 
			
		||||
    
 | 
			
		||||
    const flashloanAmount = await convertToCurrencyDecimals(usdc.address, '500');
 | 
			
		||||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(caller.signer)
 | 
			
		||||
      .flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        dai.address,
 | 
			
		||||
        ethers.utils.parseEther('500'),
 | 
			
		||||
        '0x10'
 | 
			
		||||
      ),
 | 
			
		||||
      TRANSFER_AMOUNT_EXCEEDS_BALANCE
 | 
			
		||||
    ).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
 | 
			
		||||
        usdc.address,
 | 
			
		||||
        flashloanAmount,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
    const {variableDebtTokenAddress} = await pool.getReserveTokensAddresses(usdc.address);
 | 
			
		||||
 | 
			
		||||
    const usdcDebtToken = await getContract<VariableDebtToken>(eContractid.VariableDebtToken, variableDebtTokenAddress);
 | 
			
		||||
 | 
			
		||||
    const callerDebt = await usdcDebtToken.balanceOf(caller.address);
 | 
			
		||||
 | 
			
		||||
    expect(callerDebt.toString()).to.be.equal('500450000', 'Invalid user debt');
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user