mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	Merge branch 'master' into feat/36-permit-atoken
This commit is contained in:
		
						commit
						0d36eab807
					
				| 
						 | 
				
			
			@ -29,6 +29,13 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  event Withdraw(address indexed reserve, address indexed user, uint256 amount);
 | 
			
		||||
 | 
			
		||||
  event BorrowAllowanceDelegated(
 | 
			
		||||
    address indexed asset,
 | 
			
		||||
    address indexed fromUser,
 | 
			
		||||
    address indexed toUser,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  );
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev emitted on borrow
 | 
			
		||||
   * @param reserve the address of the reserve
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +47,8 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  event Borrow(
 | 
			
		||||
    address indexed reserve,
 | 
			
		||||
    address indexed user,
 | 
			
		||||
    address user,
 | 
			
		||||
    address indexed onBehalfOf,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 borrowRateMode,
 | 
			
		||||
    uint256 borrowRate,
 | 
			
		||||
| 
						 | 
				
			
			@ -151,6 +159,27 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  function withdraw(address reserve, uint256 amount) external;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
 | 
			
		||||
   * @param asset The underlying asset of the debt token
 | 
			
		||||
   * @param user The user to give allowance to
 | 
			
		||||
   * @param interestRateMode Type of debt: 1 for stable, 2 for variable
 | 
			
		||||
   * @param amount Allowance amount to borrow
 | 
			
		||||
   **/
 | 
			
		||||
  function delegateBorrowAllowance(
 | 
			
		||||
    address asset,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  function getBorrowAllowance(
 | 
			
		||||
    address fromUser,
 | 
			
		||||
    address toUser,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 interestRateMode
 | 
			
		||||
  ) external view returns (uint256);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
 | 
			
		||||
   * already deposited enough collateral.
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +191,8 @@ interface ILendingPool {
 | 
			
		|||
    address reserve,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
    uint16 referralCode,
 | 
			
		||||
    address onBehalfOf
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,8 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
  mapping(address => ReserveLogic.ReserveData) internal _reserves;
 | 
			
		||||
  mapping(address => UserConfiguration.Map) internal _usersConfig;
 | 
			
		||||
  // debt token address => user who gives allowance => user who receives allowance => amount
 | 
			
		||||
  mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
 | 
			
		||||
 | 
			
		||||
  address[] internal _reservesList;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +161,35 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    emit Withdraw(asset, msg.sender, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getBorrowAllowance(
 | 
			
		||||
    address fromUser,
 | 
			
		||||
    address toUser,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 interestRateMode
 | 
			
		||||
  ) external override view returns (uint256) {
 | 
			
		||||
    return
 | 
			
		||||
      _borrowAllowance[_reserves[asset].getDebtTokenAddress(interestRateMode)][fromUser][toUser];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
 | 
			
		||||
   * @param asset The underlying asset of the debt token
 | 
			
		||||
   * @param user The user to give allowance to
 | 
			
		||||
   * @param interestRateMode Type of debt: 1 for stable, 2 for variable
 | 
			
		||||
   * @param amount Allowance amount to borrow
 | 
			
		||||
   **/
 | 
			
		||||
  function delegateBorrowAllowance(
 | 
			
		||||
    address asset,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external override {
 | 
			
		||||
    address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
 | 
			
		||||
 | 
			
		||||
    _borrowAllowance[debtToken][msg.sender][user] = amount;
 | 
			
		||||
    emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
 | 
			
		||||
   * already deposited enough collateral.
 | 
			
		||||
| 
						 | 
				
			
			@ -166,20 +197,34 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
   * @param amount the amount to be borrowed
 | 
			
		||||
   * @param interestRateMode the interest rate mode at which the user wants to borrow. Can be 0 (STABLE) or 1 (VARIABLE)
 | 
			
		||||
   * @param referralCode a referral code for integrators
 | 
			
		||||
   * @param onBehalfOf address of the user who will receive the debt
 | 
			
		||||
   **/
 | 
			
		||||
  function borrow(
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
    uint16 referralCode,
 | 
			
		||||
    address onBehalfOf
 | 
			
		||||
  ) external override {
 | 
			
		||||
    ReserveLogic.ReserveData storage reserve = _reserves[asset];
 | 
			
		||||
 | 
			
		||||
    if (onBehalfOf != msg.sender) {
 | 
			
		||||
      address debtToken = reserve.getDebtTokenAddress(interestRateMode);
 | 
			
		||||
 | 
			
		||||
      _borrowAllowance[debtToken][onBehalfOf][msg
 | 
			
		||||
        .sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub(
 | 
			
		||||
        amount,
 | 
			
		||||
        Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    _executeBorrow(
 | 
			
		||||
      ExecuteBorrowParams(
 | 
			
		||||
        asset,
 | 
			
		||||
        msg.sender,
 | 
			
		||||
        onBehalfOf,
 | 
			
		||||
        amount,
 | 
			
		||||
        interestRateMode,
 | 
			
		||||
        _reserves[asset].aTokenAddress,
 | 
			
		||||
        reserve.aTokenAddress,
 | 
			
		||||
        referralCode,
 | 
			
		||||
        true
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			@ -523,6 +568,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
        ExecuteBorrowParams(
 | 
			
		||||
          asset,
 | 
			
		||||
          msg.sender,
 | 
			
		||||
          msg.sender,
 | 
			
		||||
          vars.amountPlusPremium.sub(vars.availableBalance),
 | 
			
		||||
          mode,
 | 
			
		||||
          vars.aTokenAddress,
 | 
			
		||||
| 
						 | 
				
			
			@ -752,6 +798,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
  struct ExecuteBorrowParams {
 | 
			
		||||
    address asset;
 | 
			
		||||
    address user;
 | 
			
		||||
    address onBehalfOf;
 | 
			
		||||
    uint256 amount;
 | 
			
		||||
    uint256 interestRateMode;
 | 
			
		||||
    address aTokenAddress;
 | 
			
		||||
| 
						 | 
				
			
			@ -765,7 +812,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  function _executeBorrow(ExecuteBorrowParams memory vars) internal {
 | 
			
		||||
    ReserveLogic.ReserveData storage reserve = _reserves[vars.asset];
 | 
			
		||||
    UserConfiguration.Map storage userConfig = _usersConfig[msg.sender];
 | 
			
		||||
    UserConfiguration.Map storage userConfig = _usersConfig[vars.onBehalfOf];
 | 
			
		||||
 | 
			
		||||
    address oracle = _addressesProvider.getPriceOracle();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -775,7 +822,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
    ValidationLogic.validateBorrow(
 | 
			
		||||
      reserve,
 | 
			
		||||
      vars.asset,
 | 
			
		||||
      vars.onBehalfOf,
 | 
			
		||||
      vars.amount,
 | 
			
		||||
      amountInETH,
 | 
			
		||||
      vars.interestRateMode,
 | 
			
		||||
| 
						 | 
				
			
			@ -802,12 +849,12 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
      currentStableRate = reserve.currentStableBorrowRate;
 | 
			
		||||
 | 
			
		||||
      IStableDebtToken(reserve.stableDebtTokenAddress).mint(
 | 
			
		||||
        vars.user,
 | 
			
		||||
        vars.onBehalfOf,
 | 
			
		||||
        vars.amount,
 | 
			
		||||
        currentStableRate
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.user, vars.amount);
 | 
			
		||||
      IVariableDebtToken(reserve.variableDebtTokenAddress).mint(vars.onBehalfOf, vars.amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    reserve.updateInterestRates(
 | 
			
		||||
| 
						 | 
				
			
			@ -818,12 +865,13 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    );
 | 
			
		||||
 | 
			
		||||
    if (vars.releaseUnderlying) {
 | 
			
		||||
      IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount);
 | 
			
		||||
      IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    emit Borrow(
 | 
			
		||||
      vars.asset,
 | 
			
		||||
      msg.sender,
 | 
			
		||||
      vars.user,
 | 
			
		||||
      vars.onBehalfOf,
 | 
			
		||||
      vars.amount,
 | 
			
		||||
      vars.interestRateMode,
 | 
			
		||||
      ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ contract LendingPoolLiquidationManager is VersionedInitializable {
 | 
			
		|||
 | 
			
		||||
  mapping(address => ReserveLogic.ReserveData) internal reserves;
 | 
			
		||||
  mapping(address => UserConfiguration.Map) internal usersConfig;
 | 
			
		||||
  mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
 | 
			
		||||
 | 
			
		||||
  address[] internal reservesList;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ library Errors {
 | 
			
		|||
  string public constant INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
 | 
			
		||||
  string public constant CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The actual balance of the protocol is inconsistent'
 | 
			
		||||
  string public constant INVALID_FLASHLOAN_MODE = '43'; //Invalid flashloan mode selected
 | 
			
		||||
  string public constant BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '54'; // User borrows on behalf, but allowance are too small
 | 
			
		||||
  string public constant REENTRANCY_NOT_ALLOWED = '52';
 | 
			
		||||
  string public constant FAILED_REPAY_WITH_COLLATERAL = '53';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,6 +120,28 @@ library ReserveLogic {
 | 
			
		|||
    return cumulated;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev returns an address of the debt token used for particular interest rate mode on asset.
 | 
			
		||||
   * @param reserve the reserve object
 | 
			
		||||
   * @param interestRateMode - STABLE or VARIABLE from ReserveLogic.InterestRateMode enum
 | 
			
		||||
   * @return an address of the corresponding debt token from reserve configuration
 | 
			
		||||
   **/
 | 
			
		||||
  function getDebtTokenAddress(ReserveLogic.ReserveData storage reserve, uint256 interestRateMode)
 | 
			
		||||
    internal
 | 
			
		||||
    view
 | 
			
		||||
    returns (address)
 | 
			
		||||
  {
 | 
			
		||||
    require(
 | 
			
		||||
      ReserveLogic.InterestRateMode.STABLE == ReserveLogic.InterestRateMode(interestRateMode) ||
 | 
			
		||||
        ReserveLogic.InterestRateMode.VARIABLE == ReserveLogic.InterestRateMode(interestRateMode),
 | 
			
		||||
      Errors.INVALID_INTEREST_RATE_MODE_SELECTED
 | 
			
		||||
    );
 | 
			
		||||
    return
 | 
			
		||||
      ReserveLogic.InterestRateMode.STABLE == ReserveLogic.InterestRateMode(interestRateMode)
 | 
			
		||||
        ? reserve.stableDebtTokenAddress
 | 
			
		||||
        : reserve.variableDebtTokenAddress;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
 | 
			
		||||
   * a formal specification.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ library ValidationLogic {
 | 
			
		|||
  /**
 | 
			
		||||
   * @dev validates a borrow.
 | 
			
		||||
   * @param reserve the reserve state from which the user is borrowing
 | 
			
		||||
   * @param reserveAddress the address of the reserve
 | 
			
		||||
   * @param userAddress the address of the user
 | 
			
		||||
   * @param amount the amount to be borrowed
 | 
			
		||||
   * @param amountInETH the amount to be borrowed, in ETH
 | 
			
		||||
   * @param interestRateMode the interest rate mode at which the user is borrowing
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +113,7 @@ library ValidationLogic {
 | 
			
		|||
 | 
			
		||||
  function validateBorrow(
 | 
			
		||||
    ReserveLogic.ReserveData storage reserve,
 | 
			
		||||
    address reserveAddress,
 | 
			
		||||
    address userAddress,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 amountInETH,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +151,7 @@ library ValidationLogic {
 | 
			
		|||
      vars.currentLiquidationThreshold,
 | 
			
		||||
      vars.healthFactor
 | 
			
		||||
    ) = GenericLogic.calculateUserAccountData(
 | 
			
		||||
      msg.sender,
 | 
			
		||||
      userAddress,
 | 
			
		||||
      reservesData,
 | 
			
		||||
      userConfig,
 | 
			
		||||
      reserves,
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +192,7 @@ library ValidationLogic {
 | 
			
		|||
      require(
 | 
			
		||||
        !userConfig.isUsingAsCollateral(reserve.id) ||
 | 
			
		||||
          reserve.configuration.getLtv() == 0 ||
 | 
			
		||||
          amount > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
 | 
			
		||||
          amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
 | 
			
		||||
        Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
 | 
			
		|||
    await expect(
 | 
			
		||||
      pool
 | 
			
		||||
        .connect(users[1].signer)
 | 
			
		||||
        .borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL),
 | 
			
		||||
        .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);
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +79,13 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(users[1].signer)
 | 
			
		||||
      .borrow(weth.address, ethers.utils.parseEther('0.1'), RateMode.Stable, AAVE_REFERRAL);
 | 
			
		||||
      .borrow(
 | 
			
		||||
        weth.address,
 | 
			
		||||
        ethers.utils.parseEther('0.1'),
 | 
			
		||||
        RateMode.Stable,
 | 
			
		||||
        AAVE_REFERRAL,
 | 
			
		||||
        users[1].address
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,9 +47,9 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
 | 
			
		|||
 | 
			
		||||
    const usdcPrice = await oracle.getAssetPrice(usdc.address);
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    const {userData: wethUserDataBefore} = await getContractsData(
 | 
			
		||||
      weth.address,
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +203,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
 | 
			
		|||
        .toFixed(0)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -464,7 +464,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
 | 
			
		|||
        .toFixed(0)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -736,7 +736,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
 | 
			
		|||
    await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
 | 
			
		||||
    await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    const amountToRepay = amountToBorrowVariable;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -844,7 +844,7 @@ makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEn
 | 
			
		|||
    await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
 | 
			
		||||
    await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('Liquidator tries to liquidates User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled', async () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -283,11 +283,41 @@ export const withdraw = async (
 | 
			
		|||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const delegateBorrowAllowance = async (
 | 
			
		||||
  reserveSymbol: string,
 | 
			
		||||
  amount: string,
 | 
			
		||||
  interestRateMode: string,
 | 
			
		||||
  user: SignerWithAddress,
 | 
			
		||||
  receiver: tEthereumAddress,
 | 
			
		||||
  expectedResult: string,
 | 
			
		||||
  testEnv: TestEnv,
 | 
			
		||||
  revertMessage?: string
 | 
			
		||||
) => {
 | 
			
		||||
  const {pool} = testEnv;
 | 
			
		||||
 | 
			
		||||
  const reserve = await getReserveAddressFromSymbol(reserveSymbol);
 | 
			
		||||
  const amountToDelegate = await convertToCurrencyDecimals(reserve, amount);
 | 
			
		||||
 | 
			
		||||
  const delegateAllowancePromise = pool
 | 
			
		||||
    .connect(user.signer)
 | 
			
		||||
    .delegateBorrowAllowance(reserve, receiver, interestRateMode, amountToDelegate.toString());
 | 
			
		||||
  if (expectedResult === 'revert') {
 | 
			
		||||
    await expect(delegateAllowancePromise, revertMessage).to.be.reverted;
 | 
			
		||||
    return;
 | 
			
		||||
  } else {
 | 
			
		||||
    await delegateAllowancePromise;
 | 
			
		||||
    expect(
 | 
			
		||||
      (await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateMode)).toString()
 | 
			
		||||
    ).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly');
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const borrow = async (
 | 
			
		||||
  reserveSymbol: string,
 | 
			
		||||
  amount: string,
 | 
			
		||||
  interestRateMode: string,
 | 
			
		||||
  user: SignerWithAddress,
 | 
			
		||||
  onBehalfOf: tEthereumAddress,
 | 
			
		||||
  timeTravel: string,
 | 
			
		||||
  expectedResult: string,
 | 
			
		||||
  testEnv: TestEnv,
 | 
			
		||||
| 
						 | 
				
			
			@ -299,15 +329,18 @@ export const borrow = async (
 | 
			
		|||
 | 
			
		||||
  const {reserveData: reserveDataBefore, userData: userDataBefore} = await getContractsData(
 | 
			
		||||
    reserve,
 | 
			
		||||
    user.address,
 | 
			
		||||
    testEnv
 | 
			
		||||
    onBehalfOf,
 | 
			
		||||
    testEnv,
 | 
			
		||||
    user.address
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const amountToBorrow = await convertToCurrencyDecimals(reserve, amount);
 | 
			
		||||
 | 
			
		||||
  if (expectedResult === 'success') {
 | 
			
		||||
    const txResult = await waitForTx(
 | 
			
		||||
      await pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0')
 | 
			
		||||
      await pool
 | 
			
		||||
        .connect(user.signer)
 | 
			
		||||
        .borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const {txCost, txTimestamp} = await getTxCostAndTimestamp(txResult);
 | 
			
		||||
| 
						 | 
				
			
			@ -322,7 +355,7 @@ export const borrow = async (
 | 
			
		|||
      reserveData: reserveDataAfter,
 | 
			
		||||
      userData: userDataAfter,
 | 
			
		||||
      timestamp,
 | 
			
		||||
    } = await getContractsData(reserve, user.address, testEnv);
 | 
			
		||||
    } = await getContractsData(reserve, onBehalfOf, testEnv, user.address);
 | 
			
		||||
 | 
			
		||||
    const expectedReserveData = calcExpectedReserveDataAfterBorrow(
 | 
			
		||||
      amountToBorrow.toString(),
 | 
			
		||||
| 
						 | 
				
			
			@ -369,7 +402,7 @@ export const borrow = async (
 | 
			
		|||
    // });
 | 
			
		||||
  } else if (expectedResult === 'revert') {
 | 
			
		||||
    await expect(
 | 
			
		||||
      pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0'),
 | 
			
		||||
      pool.connect(user.signer).borrow(reserve, amountToBorrow, interestRateMode, '0', onBehalfOf),
 | 
			
		||||
      revertMessage
 | 
			
		||||
    ).to.be.reverted;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,8 @@ import {
 | 
			
		|||
  repay,
 | 
			
		||||
  setUseAsCollateral,
 | 
			
		||||
  swapBorrowRateMode,
 | 
			
		||||
  rebalanceStableBorrowRate
 | 
			
		||||
  rebalanceStableBorrowRate,
 | 
			
		||||
  delegateBorrowAllowance,
 | 
			
		||||
} from './actions';
 | 
			
		||||
import {RateMode} from '../../helpers/types';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +60,7 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
 | 
			
		|||
 | 
			
		||||
  if (borrowRateMode) {
 | 
			
		||||
    if (borrowRateMode === 'none') {
 | 
			
		||||
      RateMode.None;
 | 
			
		||||
      rateMode = RateMode.None;
 | 
			
		||||
    } else if (borrowRateMode === 'stable') {
 | 
			
		||||
      rateMode = RateMode.Stable;
 | 
			
		||||
    } else if (borrowRateMode === 'variable') {
 | 
			
		||||
| 
						 | 
				
			
			@ -111,6 +112,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
 | 
			
		|||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'delegateBorrowAllowance':
 | 
			
		||||
      {
 | 
			
		||||
        const {amount, toUser: toUserIndex} = action.args;
 | 
			
		||||
        const toUser = users[parseInt(toUserIndex, 10)].address;
 | 
			
		||||
        if (!amount || amount === '') {
 | 
			
		||||
          throw `Invalid amount to deposit into the ${reserve} reserve`;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await delegateBorrowAllowance(
 | 
			
		||||
          reserve,
 | 
			
		||||
          amount,
 | 
			
		||||
          rateMode,
 | 
			
		||||
          user,
 | 
			
		||||
          toUser,
 | 
			
		||||
          expected,
 | 
			
		||||
          testEnv,
 | 
			
		||||
          revertMessage
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'withdraw':
 | 
			
		||||
      {
 | 
			
		||||
        const {amount} = action.args;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,13 +146,27 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
 | 
			
		|||
      break;
 | 
			
		||||
    case 'borrow':
 | 
			
		||||
      {
 | 
			
		||||
        const {amount, timeTravel} = action.args;
 | 
			
		||||
        const {amount, timeTravel, onBehalfOf: onBehalfOfIndex} = action.args;
 | 
			
		||||
 | 
			
		||||
        const onBehalfOf = onBehalfOfIndex
 | 
			
		||||
          ? users[parseInt(onBehalfOfIndex)].address
 | 
			
		||||
          : user.address;
 | 
			
		||||
 | 
			
		||||
        if (!amount || amount === '') {
 | 
			
		||||
          throw `Invalid amount to borrow from the ${reserve} reserve`;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await borrow(reserve, amount, rateMode, user, timeTravel, expected, testEnv, revertMessage);
 | 
			
		||||
        await borrow(
 | 
			
		||||
          reserve,
 | 
			
		||||
          amount,
 | 
			
		||||
          rateMode,
 | 
			
		||||
          user,
 | 
			
		||||
          onBehalfOf,
 | 
			
		||||
          timeTravel,
 | 
			
		||||
          expected,
 | 
			
		||||
          testEnv,
 | 
			
		||||
          revertMessage
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										148
									
								
								test/helpers/scenarios/credit-delegation.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								test/helpers/scenarios/credit-delegation.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
{
 | 
			
		||||
  "title": "LendingPool: credit delegation",
 | 
			
		||||
  "description": "Test cases for the credit delegation related functions.",
 | 
			
		||||
  "stories": [
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH on variable to user 4, user 4 borrows 1 WETH variable on behalf of user 0",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "mint",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1000",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "approve",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "deposit",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1000",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "2",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "borrowRateMode": "variable",
 | 
			
		||||
            "toUser": "4"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "4",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "variable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 4 trying to borrow 1 WETH stable on behalf of user 0, revert expected",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "4",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "stable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "revert",
 | 
			
		||||
          "revertMessage": "54"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 3 WETH variable on behalf of user 0, revert expected",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "borrowRateMode": "variable",
 | 
			
		||||
            "toUser": "4"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "3",
 | 
			
		||||
            "user": "4",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "variable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "revert",
 | 
			
		||||
          "revertMessage": "54"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 delegates borrowing of 1 WETH on stable to user 2, user 2 borrows 1 WETH stable on behalf of user 0",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "borrowRateMode": "stable",
 | 
			
		||||
            "toUser": "2"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "2",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "stable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 delegates borrowing of 1 WETH to user 2 with wrong borrowRateMode, revert expected",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "borrowRateMode": "random",
 | 
			
		||||
            "toUser": "2"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "revert",
 | 
			
		||||
          "revertMessage": "8"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(borrower.signer)
 | 
			
		||||
      .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0');
 | 
			
		||||
      .borrow(dai.address, amountDAIToBorrow, RateMode.Variable, '0', borrower.address);
 | 
			
		||||
 | 
			
		||||
    const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +261,7 @@ makeSuite('LendingPool liquidation - liquidator receiving aToken', (testEnv) =>
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(borrower.signer)
 | 
			
		||||
      .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
 | 
			
		||||
      .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
 | 
			
		||||
 | 
			
		||||
    //drops HF below 1
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,7 +68,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(borrower.signer)
 | 
			
		||||
      .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0');
 | 
			
		||||
      .borrow(dai.address, amountDAIToBorrow, RateMode.Stable, '0', borrower.address);
 | 
			
		||||
 | 
			
		||||
    const userGlobalDataAfter = await pool.getUserAccountData(borrower.address);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +240,7 @@ makeSuite('LendingPool liquidation - liquidator receiving the underlying asset',
 | 
			
		|||
 | 
			
		||||
    await pool
 | 
			
		||||
      .connect(borrower.signer)
 | 
			
		||||
      .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0');
 | 
			
		||||
      .borrow(usdc.address, amountUSDCToBorrow, RateMode.Stable, '0', borrower.address);
 | 
			
		||||
 | 
			
		||||
    //drops HF below 1
 | 
			
		||||
    await oracle.setAssetPrice(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +187,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -309,9 +309,9 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
 | 
			
		|||
 | 
			
		||||
    await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    const amountToRepay = parseUnits('80', 6);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -450,7 +450,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
 | 
			
		|||
    await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
 | 
			
		||||
    await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0, user.address);
 | 
			
		||||
 | 
			
		||||
    const amountToRepay = parseEther('80');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -542,7 +542,7 @@ makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
 | 
			
		|||
    await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
 | 
			
		||||
    await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
 | 
			
		||||
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0);
 | 
			
		||||
    await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('User 5 tries to repay his USDC loan by swapping his WETH collateral, should not revert even with WETH collateral disabled', async () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user