mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	Merge branch 'master' into 100/flash-loan-bath-modes
This commit is contained in:
		
						commit
						1954f609c2
					
				| 
						 | 
				
			
			@ -99,17 +99,18 @@ interface ILendingPool {
 | 
			
		|||
  /**
 | 
			
		||||
   * @dev emitted when a flashloan is executed
 | 
			
		||||
   * @param target the address of the flashLoanReceiver
 | 
			
		||||
   * @param assets the address of the assets being flashborrowed
 | 
			
		||||
   * @param amounts the amount requested
 | 
			
		||||
   * @param premiums the total fee on the amount
 | 
			
		||||
   * @param asset the address of the assets being flashborrowed
 | 
			
		||||
   * @param amount the amount requested
 | 
			
		||||
   * @param premium the total fee on the amount
 | 
			
		||||
   * @param referralCode the referral code of the caller
 | 
			
		||||
   **/
 | 
			
		||||
  event FlashLoan(
 | 
			
		||||
    address indexed target,
 | 
			
		||||
    uint256[] modes,
 | 
			
		||||
    address[] assets,
 | 
			
		||||
    uint256[] amounts,
 | 
			
		||||
    uint256[] premiums,
 | 
			
		||||
    // uint256[] modes,
 | 
			
		||||
    // address indexed onBehalfOf,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 premium,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -286,7 +287,7 @@ interface ILendingPool {
 | 
			
		|||
   * @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
 | 
			
		||||
   * @param assets the address of the principal reserve
 | 
			
		||||
   * @param amounts the amount requested for this flashloan
 | 
			
		||||
   * @param modes the flashloan mode
 | 
			
		||||
   * @param modes the flashloan borrow modes
 | 
			
		||||
   * @param params a bytes array to be sent to the flashloan executor
 | 
			
		||||
   * @param referralCode the referral code of the caller
 | 
			
		||||
   **/
 | 
			
		||||
| 
						 | 
				
			
			@ -295,6 +296,7 @@ interface ILendingPool {
 | 
			
		|||
    address[] calldata assets,
 | 
			
		||||
    uint256[] calldata amounts,
 | 
			
		||||
    uint256[] calldata modes,
 | 
			
		||||
    address onBehalfOf,
 | 
			
		||||
    bytes calldata params,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  ) external;
 | 
			
		||||
| 
						 | 
				
			
			@ -349,11 +351,14 @@ interface ILendingPool {
 | 
			
		|||
 | 
			
		||||
  function getReserveData(address asset) external view returns (ReserveLogic.ReserveData memory);
 | 
			
		||||
 | 
			
		||||
  function balanceDecreaseAllowed(
 | 
			
		||||
    address reserve,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external view returns (bool);
 | 
			
		||||
  function finalizeTransfer(
 | 
			
		||||
    address asset,
 | 
			
		||||
    address from,
 | 
			
		||||
    address to,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 balanceFromAfter,
 | 
			
		||||
    uint256 balanceToBefore
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  function getReservesList() external view returns (address[] memory);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -493,6 +493,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
    uint256 currentAmount;
 | 
			
		||||
    uint256 currentPremium;
 | 
			
		||||
    uint256 currentAmountPlusPremium;
 | 
			
		||||
    address debtToken;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -503,6 +504,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
   * @param assets The addresss of the assets being flashborrowed
 | 
			
		||||
   * @param amounts The amounts requested for this flashloan for each asset
 | 
			
		||||
   * @param modes Types of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
 | 
			
		||||
   * @param onBehalfOf If mode is not 0, then the address to take the debt onBehalfOf. The onBehalfOf address must already have approved `msg.sender` to incur the debt on their behalf.
 | 
			
		||||
   * @param params Variadic packed params to pass to the receiver as extra information
 | 
			
		||||
   * @param referralCode Referral code of the flash loan
 | 
			
		||||
   **/
 | 
			
		||||
| 
						 | 
				
			
			@ -511,6 +513,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
    address[] calldata assets,
 | 
			
		||||
    uint256[] calldata amounts,
 | 
			
		||||
    uint256[] calldata modes,
 | 
			
		||||
    address onBehalfOf,
 | 
			
		||||
    bytes calldata params,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
  ) external override {
 | 
			
		||||
| 
						 | 
				
			
			@ -567,13 +570,21 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
          vars.currentAmountPlusPremium
 | 
			
		||||
        );
 | 
			
		||||
      } else {
 | 
			
		||||
        if (msg.sender != onBehalfOf) {
 | 
			
		||||
          vars.debtToken = _reserves[vars.currentAsset].getDebtTokenAddress(modes[vars.i]);
 | 
			
		||||
 | 
			
		||||
          _borrowAllowance[vars.debtToken][onBehalfOf][msg.sender] = _borrowAllowance[vars
 | 
			
		||||
            .debtToken][onBehalfOf][msg.sender]
 | 
			
		||||
            .sub(vars.currentAmount, Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //if the user didn't choose to return the funds, the system checks if there
 | 
			
		||||
        //is enough collateral and eventually open a position
 | 
			
		||||
        _executeBorrow(
 | 
			
		||||
          ExecuteBorrowParams(
 | 
			
		||||
            vars.currentAsset,
 | 
			
		||||
            msg.sender,
 | 
			
		||||
            msg.sender,
 | 
			
		||||
            onBehalfOf,
 | 
			
		||||
            vars.currentAmount,
 | 
			
		||||
            modes[vars.i],
 | 
			
		||||
            vars.currentATokenAddress,
 | 
			
		||||
| 
						 | 
				
			
			@ -582,7 +593,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
          )
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      emit FlashLoan(receiverAddress, modes, assets, amounts, premiums, referralCode);
 | 
			
		||||
      emit FlashLoan(
 | 
			
		||||
        receiverAddress,
 | 
			
		||||
        vars.currentAsset,
 | 
			
		||||
        vars.currentAmount,
 | 
			
		||||
        vars.currentPremium,
 | 
			
		||||
        referralCode
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -723,29 +740,48 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev validate if a balance decrease for an asset is allowed
 | 
			
		||||
   * @dev validates and finalizes an aToken transfer
 | 
			
		||||
   * @param asset the address of the reserve
 | 
			
		||||
   * @param user the user related to the balance decrease
 | 
			
		||||
   * @param from the user from which the aTokens are transferred
 | 
			
		||||
   * @param to the user receiving the aTokens
 | 
			
		||||
   * @param amount the amount being transferred/redeemed
 | 
			
		||||
   * @return true if the balance decrease can be allowed, false otherwise
 | 
			
		||||
   * @param balanceFromBefore the balance of the from user before the transfer
 | 
			
		||||
   * @param balanceToBefore the balance of the to user before the transfer
 | 
			
		||||
   */
 | 
			
		||||
  function balanceDecreaseAllowed(
 | 
			
		||||
  function finalizeTransfer(
 | 
			
		||||
    address asset,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external override view returns (bool) {
 | 
			
		||||
    address from,
 | 
			
		||||
    address to,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 balanceFromBefore,
 | 
			
		||||
    uint256 balanceToBefore
 | 
			
		||||
  ) external override {
 | 
			
		||||
    _whenNotPaused();
 | 
			
		||||
    return
 | 
			
		||||
      GenericLogic.balanceDecreaseAllowed(
 | 
			
		||||
        asset,
 | 
			
		||||
        user,
 | 
			
		||||
        amount,
 | 
			
		||||
        _reserves,
 | 
			
		||||
        _usersConfig[user],
 | 
			
		||||
        _reservesList,
 | 
			
		||||
        _reservesCount,
 | 
			
		||||
        _addressesProvider.getPriceOracle()
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    require(msg.sender == _reserves[asset].aTokenAddress, Errors.CALLER_MUST_BE_AN_ATOKEN);
 | 
			
		||||
 | 
			
		||||
    ValidationLogic.validateTransfer(
 | 
			
		||||
      from,
 | 
			
		||||
      _reserves,
 | 
			
		||||
      _usersConfig[from],
 | 
			
		||||
      _reservesList,
 | 
			
		||||
      _reservesCount,
 | 
			
		||||
      _addressesProvider.getPriceOracle()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    uint256 reserveId = _reserves[asset].id;
 | 
			
		||||
 | 
			
		||||
    if (from != to) {
 | 
			
		||||
      if (balanceFromBefore.sub(amount) == 0) {
 | 
			
		||||
        UserConfiguration.Map storage fromConfig = _usersConfig[from];
 | 
			
		||||
        fromConfig.setUsingAsCollateral(reserveId, false);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (balanceToBefore == 0 && amount != 0) {
 | 
			
		||||
        UserConfiguration.Map storage toConfig = _usersConfig[to];
 | 
			
		||||
        toConfig.setUsingAsCollateral(reserveId, true);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ library Errors {
 | 
			
		|||
  string public constant NO_MORE_RESERVES_ALLOWED = '59';
 | 
			
		||||
  string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60';
 | 
			
		||||
  string public constant INCONSISTENT_FLASHLOAN_PARAMS = '69';
 | 
			
		||||
  string public constant CALLER_MUST_BE_AN_ATOKEN = '76';
 | 
			
		||||
 | 
			
		||||
  // require error messages - aToken - DebtTokens
 | 
			
		||||
  string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,6 @@ library Errors {
 | 
			
		|||
  string public constant INVALID_DECIMALS = '73';
 | 
			
		||||
  string public constant INVALID_RESERVE_FACTOR = '74';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  enum CollateralManagerErrors {
 | 
			
		||||
    NO_ERROR,
 | 
			
		||||
    NO_COLLATERAL_AVAILABLE,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,11 @@ library ValidationLogic {
 | 
			
		|||
   * @param reserveAddress the address of the reserve
 | 
			
		||||
   * @param amount the amount to be withdrawn
 | 
			
		||||
   * @param userBalance the balance of the user
 | 
			
		||||
   * @param reservesData the reserves state
 | 
			
		||||
   * @param userConfig the user configuration
 | 
			
		||||
   * @param reserves the addresses of the reserves
 | 
			
		||||
   * @param reservesCount the number of reserves
 | 
			
		||||
   * @param oracle the price oracle
 | 
			
		||||
   */
 | 
			
		||||
  function validateWithdraw(
 | 
			
		||||
    address reserveAddress,
 | 
			
		||||
| 
						 | 
				
			
			@ -331,10 +336,10 @@ library ValidationLogic {
 | 
			
		|||
   * @param amounts the amounts for each asset being borrowed
 | 
			
		||||
   **/
 | 
			
		||||
  function validateFlashloan(
 | 
			
		||||
    address[] memory assets,
 | 
			
		||||
    uint256[] memory amounts,
 | 
			
		||||
    uint256[] memory modes
 | 
			
		||||
  ) internal pure {
 | 
			
		||||
    address[] calldata assets,
 | 
			
		||||
    uint256[] calldata amounts,
 | 
			
		||||
    uint256[] calldata modes
 | 
			
		||||
  ) external pure {
 | 
			
		||||
    require(
 | 
			
		||||
      assets.length == amounts.length && assets.length == modes.length,
 | 
			
		||||
      Errors.INCONSISTENT_FLASHLOAN_PARAMS
 | 
			
		||||
| 
						 | 
				
			
			@ -398,4 +403,35 @@ library ValidationLogic {
 | 
			
		|||
 | 
			
		||||
    return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev validates an aToken transfer.
 | 
			
		||||
   * @param from the user from which the aTokens are being transferred
 | 
			
		||||
   * @param reservesData the state of all the reserves
 | 
			
		||||
   * @param userConfig the state of the user for the specific reserve
 | 
			
		||||
   * @param reserves the addresses of all the active reserves
 | 
			
		||||
   * @param oracle the price oracle
 | 
			
		||||
   */
 | 
			
		||||
  function validateTransfer(
 | 
			
		||||
    address from,
 | 
			
		||||
    mapping(address => ReserveLogic.ReserveData) storage reservesData,
 | 
			
		||||
    UserConfiguration.Map storage userConfig,
 | 
			
		||||
    mapping(uint256 => address) storage reserves,
 | 
			
		||||
    uint256 reservesCount,
 | 
			
		||||
    address oracle
 | 
			
		||||
  ) internal view {
 | 
			
		||||
    (, , , , uint256 healthFactor) = GenericLogic.calculateUserAccountData(
 | 
			
		||||
      from,
 | 
			
		||||
      reservesData,
 | 
			
		||||
      userConfig,
 | 
			
		||||
      reserves,
 | 
			
		||||
      reservesCount,
 | 
			
		||||
      oracle
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    require(
 | 
			
		||||
      healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
 | 
			
		||||
      Errors.TRANSFER_NOT_ALLOWED
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -241,16 +241,6 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
 | 
			
		|||
    return super.totalSupply();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Used to validate transfers before actually executing them.
 | 
			
		||||
   * @param user address of the user to check
 | 
			
		||||
   * @param amount the amount to check
 | 
			
		||||
   * @return true if the user can transfer amount, false otherwise
 | 
			
		||||
   **/
 | 
			
		||||
  function isTransferAllowed(address user, uint256 amount) public override view returns (bool) {
 | 
			
		||||
    return POOL.balanceDecreaseAllowed(UNDERLYING_ASSET_ADDRESS, user, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev transfers the underlying asset to the target. Used by the lendingpool to transfer
 | 
			
		||||
   * assets in borrow(), redeem() and flashLoan()
 | 
			
		||||
| 
						 | 
				
			
			@ -317,14 +307,24 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
 | 
			
		|||
    uint256 amount,
 | 
			
		||||
    bool validate
 | 
			
		||||
  ) internal {
 | 
			
		||||
    if (validate) {
 | 
			
		||||
      require(isTransferAllowed(from, amount), Errors.TRANSFER_NOT_ALLOWED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS);
 | 
			
		||||
 | 
			
		||||
    uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
 | 
			
		||||
    uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
 | 
			
		||||
 | 
			
		||||
    super._transfer(from, to, amount.rayDiv(index));
 | 
			
		||||
 | 
			
		||||
    if (validate) {
 | 
			
		||||
      POOL.finalizeTransfer(
 | 
			
		||||
        UNDERLYING_ASSET_ADDRESS,
 | 
			
		||||
        from,
 | 
			
		||||
        to,
 | 
			
		||||
        amount,
 | 
			
		||||
        fromBalanceBefore,
 | 
			
		||||
        toBalanceBefore
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit BalanceTransfer(from, to, amount, index);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,8 +54,6 @@ interface IAToken is IERC20, IScaledBalanceToken {
 | 
			
		|||
    uint256 value
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  function isTransferAllowed(address user, uint256 amount) external view returns (bool);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev transfer the amount of the underlying asset to the user
 | 
			
		||||
   * @param user address of the user
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -91,6 +91,7 @@ export enum ProtocolErrors {
 | 
			
		|||
  REQUESTED_AMOUNT_TOO_SMALL = '25', // 'The requested amount is too small for a FlashLoan.'
 | 
			
		||||
  INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26', // 'The actual balance of the protocol is inconsistent'
 | 
			
		||||
  CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27', // 'The actual balance of the protocol is inconsistent'
 | 
			
		||||
  BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '54', // User borrows on behalf, but allowance are too small
 | 
			
		||||
  INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60', // The flash loan received returned 0 (EOA)
 | 
			
		||||
 | 
			
		||||
  // require error messages - aToken
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,8 +50,8 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
 | 
			
		|||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 0 deposits 1 WETH and user 1 tries to borrow, but the aTokens received as a transfer are not available as collateral (revert expected)', async () => {
 | 
			
		||||
    const {users, pool, weth} = testEnv;
 | 
			
		||||
  it('User 0 deposits 1 WETH and user 1 tries to borrow the WETH with the received DAI as collateral', async () => {
 | 
			
		||||
    const {users, pool, weth, helpersContract} = testEnv;
 | 
			
		||||
    const userAddress = await pool.signer.getAddress();
 | 
			
		||||
 | 
			
		||||
    await weth.connect(users[0].signer).mint(await convertToCurrencyDecimals(weth.address, '1'));
 | 
			
		||||
| 
						 | 
				
			
			@ -61,26 +61,6 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
 | 
			
		|||
    await pool
 | 
			
		||||
      .connect(users[0].signer)
 | 
			
		||||
      .deposit(weth.address, ethers.utils.parseEther('1.0'), userAddress, '0');
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(users[1].signer)
 | 
			
		||||
        .borrow(
 | 
			
		||||
          weth.address,
 | 
			
		||||
          ethers.utils.parseEther('0.1'),
 | 
			
		||||
          RateMode.Stable,
 | 
			
		||||
          AAVE_REFERRAL,
 | 
			
		||||
          users[1].address
 | 
			
		||||
        ),
 | 
			
		||||
      COLLATERAL_BALANCE_IS_0
 | 
			
		||||
    ).to.be.revertedWith(COLLATERAL_BALANCE_IS_0);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 1 sets the DAI as collateral and borrows, tries to transfer everything back to user 0 (revert expected)', async () => {
 | 
			
		||||
    const {users, pool, aDai, dai, weth} = testEnv;
 | 
			
		||||
    await pool.connect(users[1].signer).setUserUseReserveAsCollateral(dai.address, true);
 | 
			
		||||
 | 
			
		||||
    const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '1000');
 | 
			
		||||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(users[1].signer)
 | 
			
		||||
      .borrow(
 | 
			
		||||
| 
						 | 
				
			
			@ -91,9 +71,34 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
 | 
			
		|||
        users[1].address
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    const userReserveData = await helpersContract.getUserReserveData(
 | 
			
		||||
      weth.address,
 | 
			
		||||
      users[1].address
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    expect(userReserveData.currentStableDebt.toString()).to.be.eq(ethers.utils.parseEther('0.1'));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 1 tries to transfer all the DAI used as collateral back to user 0 (revert expected)', async () => {
 | 
			
		||||
    const {users, pool, aDai, dai, weth} = testEnv;
 | 
			
		||||
 | 
			
		||||
    const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '1000');
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
 | 
			
		||||
      TRANSFER_NOT_ALLOWED
 | 
			
		||||
    ).to.be.revertedWith(TRANSFER_NOT_ALLOWED);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => {
 | 
			
		||||
    const {users, pool, aDai, dai, weth} = testEnv;
 | 
			
		||||
 | 
			
		||||
    const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '100');
 | 
			
		||||
 | 
			
		||||
    await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer);
 | 
			
		||||
 | 
			
		||||
    const user0Balance = await aDai.balanceOf(users[0].address);
 | 
			
		||||
 | 
			
		||||
    expect(user0Balance.toString()).to.be.eq(aDAItoTransfer.toString());
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
    SAFEERC20_LOWLEVEL_CALL,
 | 
			
		||||
    IS_PAUSED,
 | 
			
		||||
    INVALID_FLASH_LOAN_EXECUTOR_RETURN,
 | 
			
		||||
    BORROW_ALLOWANCE_ARE_NOT_ENOUGH,
 | 
			
		||||
  } = ProtocolErrors;
 | 
			
		||||
 | 
			
		||||
  before(async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +49,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
      [weth.address],
 | 
			
		||||
      [ethers.utils.parseEther('0.8')],
 | 
			
		||||
      [0],
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +79,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
      [weth.address],
 | 
			
		||||
      ['1000720000000000000'],
 | 
			
		||||
      [0],
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +111,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
          [weth.address],
 | 
			
		||||
          [ethers.utils.parseEther('0.8')],
 | 
			
		||||
          [0],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +132,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
          [weth.address],
 | 
			
		||||
          [ethers.utils.parseEther('0.8')],
 | 
			
		||||
          [0],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +153,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
          [weth.address],
 | 
			
		||||
          [ethers.utils.parseEther('0.8')],
 | 
			
		||||
          [4],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +182,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
        [weth.address],
 | 
			
		||||
        [ethers.utils.parseEther('0.8')],
 | 
			
		||||
        [2],
 | 
			
		||||
        caller.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
| 
						 | 
				
			
			@ -194,14 +201,16 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
 | 
			
		||||
    const {pool, weth} = testEnv;
 | 
			
		||||
    const {pool, weth, users} = testEnv;
 | 
			
		||||
    const caller = users[1];
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.flashLoan(
 | 
			
		||||
      pool.connect(caller.signer).flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        [weth.address],
 | 
			
		||||
        ['1004415000000000000'], //slightly higher than the available liquidity
 | 
			
		||||
        [2],
 | 
			
		||||
        caller.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      ),
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +219,19 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  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, users} = testEnv;
 | 
			
		||||
    const caller = users[1];
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], [2], '0x10', '0')
 | 
			
		||||
      pool.flashLoan(
 | 
			
		||||
        deployer.address,
 | 
			
		||||
        [weth.address],
 | 
			
		||||
        ['1000000000000000000'],
 | 
			
		||||
        [2],
 | 
			
		||||
        caller.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      )
 | 
			
		||||
    ).to.be.reverted;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -242,6 +260,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
      [usdc.address],
 | 
			
		||||
      [flashloanAmount],
 | 
			
		||||
      [0],
 | 
			
		||||
      _mockFlashLoanReceiver.address,
 | 
			
		||||
      '0x10',
 | 
			
		||||
      '0'
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -284,6 +303,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
          [usdc.address],
 | 
			
		||||
          [flashloanAmount],
 | 
			
		||||
          [2],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +329,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(caller.signer)
 | 
			
		||||
      .flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], [2], '0x10', '0');
 | 
			
		||||
      .flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        [usdc.address],
 | 
			
		||||
        [flashloanAmount],
 | 
			
		||||
        [2],
 | 
			
		||||
        caller.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
    const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(
 | 
			
		||||
      usdc.address
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -344,7 +372,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
    await expect(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(caller.signer)
 | 
			
		||||
        .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], [0], '0x10', '0')
 | 
			
		||||
        .flashLoan(
 | 
			
		||||
          _mockFlashLoanReceiver.address,
 | 
			
		||||
          [weth.address],
 | 
			
		||||
          [flashAmount],
 | 
			
		||||
          [0],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
    ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +395,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(caller.signer)
 | 
			
		||||
      .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], [1], '0x10', '0');
 | 
			
		||||
      .flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        [weth.address],
 | 
			
		||||
        [flashAmount],
 | 
			
		||||
        [1],
 | 
			
		||||
        caller.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -372,4 +416,82 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user without allowance', async () => {
 | 
			
		||||
    const {dai, pool, weth, users, helpersContract} = testEnv;
 | 
			
		||||
 | 
			
		||||
    const caller = users[5];
 | 
			
		||||
    const onBehalfOf = users[4];
 | 
			
		||||
 | 
			
		||||
    // Deposit 1000 dai for onBehalfOf user
 | 
			
		||||
    await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
 | 
			
		||||
 | 
			
		||||
    await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
 | 
			
		||||
 | 
			
		||||
    const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
 | 
			
		||||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(onBehalfOf.signer)
 | 
			
		||||
      .deposit(dai.address, amountToDeposit, onBehalfOf.address, '0');
 | 
			
		||||
 | 
			
		||||
    const flashAmount = ethers.utils.parseEther('0.8');
 | 
			
		||||
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(caller.signer)
 | 
			
		||||
        .flashLoan(
 | 
			
		||||
          _mockFlashLoanReceiver.address,
 | 
			
		||||
          [weth.address],
 | 
			
		||||
          [flashAmount],
 | 
			
		||||
          [1],
 | 
			
		||||
          onBehalfOf.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
    ).to.be.revertedWith(BORROW_ALLOWANCE_ARE_NOT_ENOUGH);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user with allowance. A loan for onBehalfOf is creatd.', async () => {
 | 
			
		||||
    const {dai, pool, weth, users, helpersContract} = testEnv;
 | 
			
		||||
 | 
			
		||||
    const caller = users[5];
 | 
			
		||||
    const onBehalfOf = users[4];
 | 
			
		||||
 | 
			
		||||
    const flashAmount = ethers.utils.parseEther('0.8');
 | 
			
		||||
 | 
			
		||||
    // Deposited for onBehalfOf user already, delegate borrow allowance
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(onBehalfOf.signer)
 | 
			
		||||
      .delegateBorrowAllowance(weth.address, caller.address, 1, flashAmount);
 | 
			
		||||
 | 
			
		||||
    await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
 | 
			
		||||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(caller.signer)
 | 
			
		||||
      .flashLoan(
 | 
			
		||||
        _mockFlashLoanReceiver.address,
 | 
			
		||||
        [weth.address],
 | 
			
		||||
        [flashAmount],
 | 
			
		||||
        [1],
 | 
			
		||||
        onBehalfOf.address,
 | 
			
		||||
        '0x10',
 | 
			
		||||
        '0'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
 | 
			
		||||
 | 
			
		||||
    const wethDebtToken = await getContract<StableDebtToken>(
 | 
			
		||||
      eContractid.VariableDebtToken,
 | 
			
		||||
      stableDebtTokenAddress
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address);
 | 
			
		||||
 | 
			
		||||
    expect(onBehalfOfDebt.toString()).to.be.equal(
 | 
			
		||||
      '800000000000000000',
 | 
			
		||||
      'Invalid onBehalfOf user debt'
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -187,7 +187,15 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
 | 
			
		|||
    await expect(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(caller.signer)
 | 
			
		||||
        .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], [1], '0x10', '0')
 | 
			
		||||
        .flashLoan(
 | 
			
		||||
          _mockFlashLoanReceiver.address,
 | 
			
		||||
          [weth.address],
 | 
			
		||||
          [flashAmount],
 | 
			
		||||
          [1],
 | 
			
		||||
          caller.address,
 | 
			
		||||
          '0x10',
 | 
			
		||||
          '0'
 | 
			
		||||
        )
 | 
			
		||||
    ).revertedWith(IS_PAUSED);
 | 
			
		||||
 | 
			
		||||
    // Unpause pool
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user