mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	initial implementation of the credit delegation + basic tests
This commit is contained in:
		
							parent
							
								
									23f99d30f0
								
							
						
					
					
						commit
						3173bee782
					
				| 
						 | 
				
			
			@ -28,6 +28,12 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  event Withdraw(address indexed reserve, address indexed user, uint256 amount);
 | 
			
		||||
 | 
			
		||||
  event BorrowAllowanceDelegated(
 | 
			
		||||
    address indexed fromUser,
 | 
			
		||||
    address indexed toUser,
 | 
			
		||||
    address indexed asset,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  );
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev emitted on borrow
 | 
			
		||||
   * @param reserve the address of the reserve
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +45,8 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  event Borrow(
 | 
			
		||||
    address indexed reserve,
 | 
			
		||||
    address indexed user,
 | 
			
		||||
    address user,
 | 
			
		||||
    address indexed onBehalfOf,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 borrowRateMode,
 | 
			
		||||
    uint256 borrowRate,
 | 
			
		||||
| 
						 | 
				
			
			@ -149,6 +156,18 @@ interface ILendingPool {
 | 
			
		|||
   **/
 | 
			
		||||
  function withdraw(address reserve, uint256 amount) external;
 | 
			
		||||
 | 
			
		||||
  function delegateBorrowAllowance(
 | 
			
		||||
    address user,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  function getBorrowAllowance(
 | 
			
		||||
    address fromUser,
 | 
			
		||||
    address toUser,
 | 
			
		||||
    address asset
 | 
			
		||||
  ) external view returns (uint256);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
 | 
			
		||||
   * already deposited enough collateral.
 | 
			
		||||
| 
						 | 
				
			
			@ -160,7 +179,8 @@ interface ILendingPool {
 | 
			
		|||
    address reserve,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
    uint16 referralCode,
 | 
			
		||||
    address onBehalfOf
 | 
			
		||||
  ) external;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
  mapping(address => ReserveLogic.ReserveData) internal _reserves;
 | 
			
		||||
  mapping(address => UserConfiguration.Map) internal _usersConfig;
 | 
			
		||||
 | 
			
		||||
  mapping(address => mapping(address => mapping(address => uint256))) internal _borrowAllowance;
 | 
			
		||||
  address[] internal _reservesList;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +157,23 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    emit Withdraw(asset, msg.sender, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function getBorrowAllowance(
 | 
			
		||||
    address fromUser,
 | 
			
		||||
    address toUser,
 | 
			
		||||
    address asset
 | 
			
		||||
  ) external override view returns (uint256) {
 | 
			
		||||
    return _borrowAllowance[fromUser][asset][toUser];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function delegateBorrowAllowance(
 | 
			
		||||
    address user,
 | 
			
		||||
    address asset,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
  ) external override {
 | 
			
		||||
    _borrowAllowance[msg.sender][asset][user] = amount;
 | 
			
		||||
    emit BorrowAllowanceDelegated(msg.sender, user, asset, amount);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Allows users to borrow a specific amount of the reserve currency, provided that the borrower
 | 
			
		||||
   * already deposited enough collateral.
 | 
			
		||||
| 
						 | 
				
			
			@ -169,12 +186,19 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    address asset,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 interestRateMode,
 | 
			
		||||
    uint16 referralCode
 | 
			
		||||
    uint16 referralCode,
 | 
			
		||||
    address onBehalfOf
 | 
			
		||||
  ) external override {
 | 
			
		||||
    if (onBehalfOf != msg.sender) {
 | 
			
		||||
      _borrowAllowance[onBehalfOf][asset][msg.sender] = _borrowAllowance[onBehalfOf][asset][msg
 | 
			
		||||
        .sender]
 | 
			
		||||
        .sub(amount, Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH);
 | 
			
		||||
    }
 | 
			
		||||
    _executeBorrow(
 | 
			
		||||
      ExecuteBorrowParams(
 | 
			
		||||
        asset,
 | 
			
		||||
        msg.sender,
 | 
			
		||||
        onBehalfOf,
 | 
			
		||||
        amount,
 | 
			
		||||
        interestRateMode,
 | 
			
		||||
        _reserves[asset].aTokenAddress,
 | 
			
		||||
| 
						 | 
				
			
			@ -450,21 +474,20 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
    vars.amountPlusPremium = amount.add(vars.premium);
 | 
			
		||||
 | 
			
		||||
    if (debtMode == ReserveLogic.InterestRateMode.NONE) {
 | 
			
		||||
      
 | 
			
		||||
      IERC20(asset).transferFrom(receiverAddress, vars.aTokenAddress, vars.amountPlusPremium);
 | 
			
		||||
      
 | 
			
		||||
 | 
			
		||||
      reserve.updateCumulativeIndexesAndTimestamp();
 | 
			
		||||
      reserve.cumulateToLiquidityIndex(IERC20(vars.aTokenAddress).totalSupply(), vars.premium);
 | 
			
		||||
      reserve.updateInterestRates(asset, vars.aTokenAddress, vars.premium, 0);
 | 
			
		||||
      
 | 
			
		||||
      emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode);
 | 
			
		||||
 | 
			
		||||
      emit FlashLoan(receiverAddress, asset, amount, vars.premium, referralCode);
 | 
			
		||||
    } else {
 | 
			
		||||
      // If the transfer didn't succeed, the receiver either didn't return the funds, or didn't approve the transfer.
 | 
			
		||||
      _executeBorrow(
 | 
			
		||||
        ExecuteBorrowParams(
 | 
			
		||||
          asset,
 | 
			
		||||
          msg.sender,
 | 
			
		||||
          msg.sender,
 | 
			
		||||
          vars.amountPlusPremium.sub(vars.availableBalance),
 | 
			
		||||
          mode,
 | 
			
		||||
          vars.aTokenAddress,
 | 
			
		||||
| 
						 | 
				
			
			@ -694,6 +717,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
  struct ExecuteBorrowParams {
 | 
			
		||||
    address asset;
 | 
			
		||||
    address user;
 | 
			
		||||
    address onBehalfOf;
 | 
			
		||||
    uint256 amount;
 | 
			
		||||
    uint256 interestRateMode;
 | 
			
		||||
    address aTokenAddress;
 | 
			
		||||
| 
						 | 
				
			
			@ -707,7 +731,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();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -717,6 +741,7 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
 | 
			
		||||
    ValidationLogic.validateBorrow(
 | 
			
		||||
      reserve,
 | 
			
		||||
      vars.onBehalfOf,
 | 
			
		||||
      vars.asset,
 | 
			
		||||
      vars.amount,
 | 
			
		||||
      amountInETH,
 | 
			
		||||
| 
						 | 
				
			
			@ -728,13 +753,11 @@ contract LendingPool is VersionedInitializable, ILendingPool {
 | 
			
		|||
      oracle
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    uint256 reserveIndex = reserve.index;
 | 
			
		||||
    if (!userConfig.isBorrowing(reserveIndex)) {
 | 
			
		||||
      userConfig.setBorrowing(reserveIndex, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    reserve.updateCumulativeIndexesAndTimestamp();
 | 
			
		||||
 | 
			
		||||
    //caching the current stable borrow rate
 | 
			
		||||
| 
						 | 
				
			
			@ -746,24 +769,29 @@ 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(vars.asset, vars.aTokenAddress, 0, vars.releaseUnderlying ?  vars.amount : 0);
 | 
			
		||||
  
 | 
			
		||||
    if(vars.releaseUnderlying){
 | 
			
		||||
        IAToken(vars.aTokenAddress).transferUnderlyingTo(msg.sender, vars.amount);
 | 
			
		||||
    reserve.updateInterestRates(
 | 
			
		||||
      vars.asset,
 | 
			
		||||
      vars.aTokenAddress,
 | 
			
		||||
      0,
 | 
			
		||||
      vars.releaseUnderlying ? vars.amount : 0
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (vars.releaseUnderlying) {
 | 
			
		||||
      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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = '52'; // User borrows on behalf, but allowance are too small
 | 
			
		||||
 | 
			
		||||
  // require error messages - aToken
 | 
			
		||||
  string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +71,7 @@ library Errors {
 | 
			
		|||
  string public constant NO_ERRORS = '42'; // 'No errors'
 | 
			
		||||
 | 
			
		||||
  //require error messages - Math libraries
 | 
			
		||||
  string public constant MULTIPLICATION_OVERFLOW = '44'; 
 | 
			
		||||
  string public constant ADDITION_OVERFLOW = '45'; 
 | 
			
		||||
  string public constant MULTIPLICATION_OVERFLOW = '44';
 | 
			
		||||
  string public constant ADDITION_OVERFLOW = '45';
 | 
			
		||||
  string public constant DIVISION_BY_ZERO = '46';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,7 @@ library ValidationLogic {
 | 
			
		|||
  /**
 | 
			
		||||
   * @dev validates a borrow.
 | 
			
		||||
   * @param reserve the reserve state from which the user is borrowing
 | 
			
		||||
   * @param userAddress the address of the user
 | 
			
		||||
   * @param reserveAddress the address of the reserve
 | 
			
		||||
   * @param amount the amount to be borrowed
 | 
			
		||||
   * @param amountInETH the amount to be borrowed, in ETH
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +114,7 @@ library ValidationLogic {
 | 
			
		|||
 | 
			
		||||
  function validateBorrow(
 | 
			
		||||
    ReserveLogic.ReserveData storage reserve,
 | 
			
		||||
    address userAddress,
 | 
			
		||||
    address reserveAddress,
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    uint256 amountInETH,
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +153,7 @@ library ValidationLogic {
 | 
			
		|||
      vars.currentLiquidationThreshold,
 | 
			
		||||
      vars.healthFactor
 | 
			
		||||
    ) = GenericLogic.calculateUserAccountData(
 | 
			
		||||
      msg.sender,
 | 
			
		||||
      userAddress,
 | 
			
		||||
      reservesData,
 | 
			
		||||
      userConfig,
 | 
			
		||||
      reserves,
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +194,7 @@ library ValidationLogic {
 | 
			
		|||
      require(
 | 
			
		||||
        !userConfig.isUsingAsCollateral(reserve.index) ||
 | 
			
		||||
          reserve.configuration.getLtv() == 0 ||
 | 
			
		||||
          amount > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
 | 
			
		||||
          amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
 | 
			
		||||
        Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,10 +323,10 @@ library ValidationLogic {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
  * @dev validates a flashloan action
 | 
			
		||||
  * @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan)
 | 
			
		||||
  * @param premium the premium paid on the flashloan
 | 
			
		||||
  **/
 | 
			
		||||
   * @dev validates a flashloan action
 | 
			
		||||
   * @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan)
 | 
			
		||||
   * @param premium the premium paid on the flashloan
 | 
			
		||||
   **/
 | 
			
		||||
  function validateFlashloan(uint256 mode, uint256 premium) internal pure {
 | 
			
		||||
    require(premium > 0, Errors.REQUESTED_AMOUNT_TOO_SMALL);
 | 
			
		||||
    require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,7 +111,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "DefaultReserveInterestRateStrategy": {
 | 
			
		||||
    "buidlerevm": {
 | 
			
		||||
      "address": "0x626FdE749F9d499d3777320CAf29484B624ab84a",
 | 
			
		||||
      "address": "0x2530ce07D254eA185E8e0bCC37a39e2FbA3bE548",
 | 
			
		||||
      "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
 | 
			
		||||
    },
 | 
			
		||||
    "localhost": {
 | 
			
		||||
| 
						 | 
				
			
			@ -422,7 +422,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "StableDebtToken": {
 | 
			
		||||
    "buidlerevm": {
 | 
			
		||||
      "address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
 | 
			
		||||
      "address": "0x0Cf45557d25a4e4c0F1aC65EF6c48ae67c61a0E6",
 | 
			
		||||
      "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
 | 
			
		||||
    },
 | 
			
		||||
    "localhost": {
 | 
			
		||||
| 
						 | 
				
			
			@ -432,7 +432,7 @@
 | 
			
		|||
  },
 | 
			
		||||
  "VariableDebtToken": {
 | 
			
		||||
    "buidlerevm": {
 | 
			
		||||
      "address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
 | 
			
		||||
      "address": "0x7fAeC7791277Ff512c41CA903c177B2Ed952dDAc",
 | 
			
		||||
      "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
 | 
			
		||||
    },
 | 
			
		||||
    "localhost": {
 | 
			
		||||
| 
						 | 
				
			
			@ -446,7 +446,7 @@
 | 
			
		|||
      "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
 | 
			
		||||
    },
 | 
			
		||||
    "buidlerevm": {
 | 
			
		||||
      "address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
 | 
			
		||||
      "address": "0x33958cC3535Fc328369EAC2B2Bebd120D67C7fa1",
 | 
			
		||||
      "deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,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);
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +122,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),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -278,11 +278,34 @@ export const withdraw = async (
 | 
			
		|||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const delegateBorrowAllowance = async (
 | 
			
		||||
  reserveSymbol: string,
 | 
			
		||||
  amount: string,
 | 
			
		||||
  user: SignerWithAddress,
 | 
			
		||||
  receiver: tEthereumAddress,
 | 
			
		||||
  testEnv: TestEnv
 | 
			
		||||
) => {
 | 
			
		||||
  const {pool} = testEnv;
 | 
			
		||||
 | 
			
		||||
  const reserve = await getReserveAddressFromSymbol(reserveSymbol);
 | 
			
		||||
  const amountToDelegate = await convertToCurrencyDecimals(reserve, amount);
 | 
			
		||||
 | 
			
		||||
  await pool
 | 
			
		||||
    .connect(user.signer)
 | 
			
		||||
    .delegateBorrowAllowance(receiver, reserve, amountToDelegate.toString());
 | 
			
		||||
  expect(
 | 
			
		||||
    (
 | 
			
		||||
      await pool['getBorrowAllowance(address,address,address)'](user.address, receiver, reserve)
 | 
			
		||||
    ).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,
 | 
			
		||||
| 
						 | 
				
			
			@ -294,15 +317,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -317,7 +343,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(),
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +390,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;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -845,10 +871,15 @@ const getTxCostAndTimestamp = async (tx: ContractReceipt) => {
 | 
			
		|||
  return {txCost, txTimestamp};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getContractsData = async (reserve: string, user: string, testEnv: TestEnv) => {
 | 
			
		||||
const getContractsData = async (
 | 
			
		||||
  reserve: string,
 | 
			
		||||
  user: string,
 | 
			
		||||
  testEnv: TestEnv,
 | 
			
		||||
  sender?: string
 | 
			
		||||
) => {
 | 
			
		||||
  const {pool} = testEnv;
 | 
			
		||||
  const reserveData = await getReserveData(pool, reserve);
 | 
			
		||||
  const userData = await getUserData(pool, reserve, user);
 | 
			
		||||
  const userData = await getUserData(pool, reserve, user, sender || user);
 | 
			
		||||
  const timestamp = await timeLatest();
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import {
 | 
			
		|||
  redirectInterestStream,
 | 
			
		||||
  redirectInterestStreamOf,
 | 
			
		||||
  allowInterestRedirectionTo,
 | 
			
		||||
  delegateBorrowAllowance,
 | 
			
		||||
} from './actions';
 | 
			
		||||
import {RateMode} from '../../helpers/types';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +103,18 @@ 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, user, toUser, testEnv);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'withdraw':
 | 
			
		||||
      {
 | 
			
		||||
        const {amount} = action.args;
 | 
			
		||||
| 
						 | 
				
			
			@ -115,13 +128,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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -955,6 +955,85 @@
 | 
			
		|||
          "expected": "success"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 deposits 1000 DAI, user 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 1 WETH on behalf of user 0",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "mint",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "DAI",
 | 
			
		||||
            "amount": "1000",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "approve",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "DAI",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "deposit",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "DAI",
 | 
			
		||||
            "amount": "1000",
 | 
			
		||||
            "user": "0"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "toUser": "4"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "4",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "variable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "description": "User 0 delegates borrowing of 1 WETH to user 4, user 4 borrows 2 WETH on behalf of user 0, revert expected",
 | 
			
		||||
      "actions": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "delegateBorrowAllowance",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "1",
 | 
			
		||||
            "user": "0",
 | 
			
		||||
            "toUser": "4"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "success"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "borrow",
 | 
			
		||||
          "args": {
 | 
			
		||||
            "reserve": "WETH",
 | 
			
		||||
            "amount": "2",
 | 
			
		||||
            "user": "4",
 | 
			
		||||
            "onBehalfOf": "0",
 | 
			
		||||
            "borrowRateMode": "variable"
 | 
			
		||||
          },
 | 
			
		||||
          "expected": "revert",
 | 
			
		||||
          "revertMessage": "52"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,7 +61,8 @@ export const getReserveData = async (
 | 
			
		|||
export const getUserData = async (
 | 
			
		||||
  pool: LendingPool,
 | 
			
		||||
  reserve: string,
 | 
			
		||||
  user: string
 | 
			
		||||
  user: tEthereumAddress,
 | 
			
		||||
  sender?: tEthereumAddress
 | 
			
		||||
): Promise<UserReserveData> => {
 | 
			
		||||
  const [userData, aTokenData] = await Promise.all([
 | 
			
		||||
    pool.getUserReserveData(reserve, user),
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +78,7 @@ export const getUserData = async (
 | 
			
		|||
  ] = aTokenData;
 | 
			
		||||
 | 
			
		||||
  const token = await getMintableErc20(reserve);
 | 
			
		||||
  const walletBalance = new BigNumber((await token.balanceOf(user)).toString());
 | 
			
		||||
  const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString());
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    principalATokenBalance: new BigNumber(principalATokenBalance),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +252,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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +222,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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user