mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	Add permit support in swap adapters
This commit is contained in:
		
							parent
							
								
									e7183536b3
								
							
						
					
					
						commit
						fa7fa9f948
					
				| 
						 | 
				
			
			@ -12,6 +12,7 @@ import {ILendingPool} from '../interfaces/ILendingPool.sol';
 | 
			
		|||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
 | 
			
		||||
import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol';
 | 
			
		||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
 | 
			
		||||
import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @title BaseUniswapAdapter
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +26,20 @@ contract BaseUniswapAdapter {
 | 
			
		|||
 | 
			
		||||
  enum LeftoverAction {DEPOSIT, TRANSFER}
 | 
			
		||||
 | 
			
		||||
  struct PermitParams {
 | 
			
		||||
    uint256[] deadline;
 | 
			
		||||
    uint8[] v;
 | 
			
		||||
    bytes32[] r;
 | 
			
		||||
    bytes32[] s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct PermitSignature {
 | 
			
		||||
    uint256 deadline;
 | 
			
		||||
    uint8 v;
 | 
			
		||||
    bytes32 r;
 | 
			
		||||
    bytes32 s;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Max slippage percent allow by param
 | 
			
		||||
  uint256 public constant MAX_SLIPPAGE_PERCENT = 3000; // 30%
 | 
			
		||||
  // Min slippage percent allow by param
 | 
			
		||||
| 
						 | 
				
			
			@ -221,14 +236,28 @@ contract BaseUniswapAdapter {
 | 
			
		|||
   * @param reserve address of the asset
 | 
			
		||||
   * @param user address
 | 
			
		||||
   * @param amount of tokens to be transferred to the contract
 | 
			
		||||
   * @param permitSignature struct containing the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function pullAToken(
 | 
			
		||||
    address reserve,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 amount
 | 
			
		||||
    uint256 amount,
 | 
			
		||||
    PermitSignature memory permitSignature
 | 
			
		||||
  ) internal {
 | 
			
		||||
    address reserveAToken = getAToken(reserve);
 | 
			
		||||
 | 
			
		||||
    if (_usePermit(permitSignature)) {
 | 
			
		||||
      IERC20WithPermit(reserveAToken).permit(
 | 
			
		||||
        user,
 | 
			
		||||
        address(this),
 | 
			
		||||
        amount,
 | 
			
		||||
        permitSignature.deadline,
 | 
			
		||||
        permitSignature.v,
 | 
			
		||||
        permitSignature.r,
 | 
			
		||||
        permitSignature.s
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // transfer from user to adapter
 | 
			
		||||
    IERC20(reserveAToken).safeTransferFrom(user, address(this), amount);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -241,15 +270,28 @@ contract BaseUniswapAdapter {
 | 
			
		|||
   * @param reserve address of the asset
 | 
			
		||||
   * @param user address
 | 
			
		||||
   * @param flashLoanDebt need to be repaid
 | 
			
		||||
   * @param permitSignature struct containing the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function pullATokenAndRepayFlashLoan(
 | 
			
		||||
    address reserve,
 | 
			
		||||
    address user,
 | 
			
		||||
    uint256 flashLoanDebt
 | 
			
		||||
    uint256 flashLoanDebt,
 | 
			
		||||
    PermitSignature memory permitSignature
 | 
			
		||||
  ) internal {
 | 
			
		||||
    pullAToken(reserve, user, flashLoanDebt);
 | 
			
		||||
    pullAToken(reserve, user, flashLoanDebt, permitSignature);
 | 
			
		||||
 | 
			
		||||
    // Repay flashloan
 | 
			
		||||
    IERC20(reserve).approve(address(POOL), flashLoanDebt);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @dev Tells if the permit method should be called by inspecting if there is a valid signature.
 | 
			
		||||
   * If signature params are set to 0, then permit won't be called.
 | 
			
		||||
   * @param signature struct containing the permit signature
 | 
			
		||||
   * @return whether or not permit should be called
 | 
			
		||||
   */
 | 
			
		||||
  function _usePermit(PermitSignature memory signature) internal pure returns (bool) {
 | 
			
		||||
    return !(uint256(signature.deadline) == uint256(signature.v) &&
 | 
			
		||||
      uint256(signature.deadline) == 0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,12 @@ import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
 | 
			
		|||
 **/
 | 
			
		||||
contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		||||
 | 
			
		||||
  struct SwapParams {
 | 
			
		||||
    address[] assetToSwapToList;
 | 
			
		||||
    uint256 slippage;
 | 
			
		||||
    PermitParams permitParams;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    ILendingPoolAddressesProvider addressesProvider,
 | 
			
		||||
    IUniswapV2Router02 uniswapRouter
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +41,10 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
   * @param params Additional variadic field to include extra params. Expected parameters:
 | 
			
		||||
   *   address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited
 | 
			
		||||
   *   uint256 slippage The max slippage percentage allowed for the swap
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint8[] v List of v param for the permit signature
 | 
			
		||||
   *   bytes32[] r List of r param for the permit signature
 | 
			
		||||
   *   bytes32[] s List of s param for the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function executeOperation(
 | 
			
		||||
    address[] calldata assets,
 | 
			
		||||
| 
						 | 
				
			
			@ -45,19 +55,46 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
  ) external override returns (bool) {
 | 
			
		||||
    require(msg.sender == address(POOL), "CALLER_MUST_BE_LENDING_POOL");
 | 
			
		||||
 | 
			
		||||
    (address[] memory assetToSwapToList, uint256 slippage) = abi.decode(params, (address[], uint256));
 | 
			
		||||
    require(slippage < MAX_SLIPPAGE_PERCENT && slippage >= MIN_SLIPPAGE_PERCENT, 'SLIPPAGE_OUT_OF_RANGE');
 | 
			
		||||
    require(assetToSwapToList.length == assets.length, 'INCONSISTENT_PARAMS');
 | 
			
		||||
    SwapParams memory decodedParams = _decodeParams(params);
 | 
			
		||||
 | 
			
		||||
    require(
 | 
			
		||||
      decodedParams.slippage < MAX_SLIPPAGE_PERCENT && decodedParams.slippage >= MIN_SLIPPAGE_PERCENT,
 | 
			
		||||
      'SLIPPAGE_OUT_OF_RANGE'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    require(
 | 
			
		||||
      decodedParams.assetToSwapToList.length == assets.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.deadline.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.v.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.r.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.s.length,
 | 
			
		||||
      'INCONSISTENT_PARAMS'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    for (uint256 i = 0; i < assets.length; i++) {
 | 
			
		||||
      uint256 receivedAmount = swapExactTokensForTokens(assets[i], assetToSwapToList[i], amounts[i], slippage);
 | 
			
		||||
      uint256 receivedAmount = swapExactTokensForTokens(
 | 
			
		||||
        assets[i],
 | 
			
		||||
        decodedParams.assetToSwapToList[i],
 | 
			
		||||
        amounts[i],
 | 
			
		||||
        decodedParams.slippage
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // Deposit new reserve
 | 
			
		||||
      IERC20(assetToSwapToList[i]).approve(address(POOL), receivedAmount);
 | 
			
		||||
      POOL.deposit(assetToSwapToList[i], receivedAmount, initiator, 0);
 | 
			
		||||
      IERC20(decodedParams.assetToSwapToList[i]).approve(address(POOL), receivedAmount);
 | 
			
		||||
      POOL.deposit(decodedParams.assetToSwapToList[i], receivedAmount, initiator, 0);
 | 
			
		||||
 | 
			
		||||
      uint256 flashLoanDebt = amounts[i].add(premiums[i]);
 | 
			
		||||
      pullATokenAndRepayFlashLoan(assets[i], initiator, flashLoanDebt);
 | 
			
		||||
      pullATokenAndRepayFlashLoan(
 | 
			
		||||
        assets[i],
 | 
			
		||||
        initiator,
 | 
			
		||||
        flashLoanDebt,
 | 
			
		||||
        PermitSignature(
 | 
			
		||||
          decodedParams.permitParams.deadline[i],
 | 
			
		||||
          decodedParams.permitParams.v[i],
 | 
			
		||||
          decodedParams.permitParams.r[i],
 | 
			
		||||
          decodedParams.permitParams.s[i]
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,20 +109,32 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
   * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited
 | 
			
		||||
   * @param amountToSwapList List of amounts to be swapped
 | 
			
		||||
   * @param slippage The max slippage percentage allowed for the swap
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint8[] v List of v param for the permit signature
 | 
			
		||||
   *   bytes32[] r List of r param for the permit signature
 | 
			
		||||
   *   bytes32[] s List of s param for the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function swapAndDeposit(
 | 
			
		||||
    address[] calldata assetToSwapFromList,
 | 
			
		||||
    address[] calldata assetToSwapToList,
 | 
			
		||||
    uint256[] calldata amountToSwapList,
 | 
			
		||||
    uint256 slippage
 | 
			
		||||
    uint256 slippage,
 | 
			
		||||
    PermitSignature[] calldata permitParams
 | 
			
		||||
  ) external {
 | 
			
		||||
    require(
 | 
			
		||||
      assetToSwapFromList.length == assetToSwapToList.length && assetToSwapFromList.length == amountToSwapList.length,
 | 
			
		||||
      assetToSwapFromList.length == assetToSwapToList.length
 | 
			
		||||
      && assetToSwapFromList.length == amountToSwapList.length
 | 
			
		||||
      && assetToSwapFromList.length == permitParams.length,
 | 
			
		||||
      'INCONSISTENT_PARAMS'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    for (uint256 i = 0; i < assetToSwapFromList.length; i++) {
 | 
			
		||||
      pullAToken(assetToSwapFromList[i], msg.sender, amountToSwapList[i]);
 | 
			
		||||
      pullAToken(
 | 
			
		||||
        assetToSwapFromList[i],
 | 
			
		||||
        msg.sender,
 | 
			
		||||
        amountToSwapList[i],
 | 
			
		||||
        permitParams[i]
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      uint256 receivedAmount = swapExactTokensForTokens(
 | 
			
		||||
        assetToSwapFromList[i],
 | 
			
		||||
| 
						 | 
				
			
			@ -99,4 +148,29 @@ contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
      POOL.deposit(assetToSwapToList[i], receivedAmount, msg.sender, 0);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 * @dev Decodes debt information encoded in flashloan params
 | 
			
		||||
 * @param params Additional variadic field to include extra params. Expected parameters:
 | 
			
		||||
   *   address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited
 | 
			
		||||
   *   uint256 slippage The max slippage percentage allowed for the swap
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint8[] v List of v param for the permit signature
 | 
			
		||||
   *   bytes32[] r List of r param for the permit signature
 | 
			
		||||
   *   bytes32[] s List of s param for the permit signature
 | 
			
		||||
 * @return SwapParams struct containing decoded params
 | 
			
		||||
 */
 | 
			
		||||
  function _decodeParams(bytes memory params) internal returns (SwapParams memory) {
 | 
			
		||||
    (
 | 
			
		||||
      address[] memory assetToSwapToList,
 | 
			
		||||
      uint256 slippage,
 | 
			
		||||
      uint256[] memory deadline,
 | 
			
		||||
      uint8[] memory v,
 | 
			
		||||
      bytes32[] memory r,
 | 
			
		||||
      bytes32[] memory s
 | 
			
		||||
    ) = abi.decode(params, (address[], uint256, uint256[], uint8[], bytes32[], bytes32[]));
 | 
			
		||||
 | 
			
		||||
    return SwapParams(assetToSwapToList, slippage, PermitParams(deadline, v, r, s));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
    LeftoverAction leftOverAction;
 | 
			
		||||
    uint256[] repayAmounts;
 | 
			
		||||
    uint256[] rateModes;
 | 
			
		||||
    PermitParams permitParams;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +47,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
   *     (1) Direct transfer to user
 | 
			
		||||
   *   uint256[] repayAmounts List of amounts of debt to be repaid
 | 
			
		||||
   *   uint256[] rateModes List of the rate modes of the debt to be repaid
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint8[] v List of v param for the permit signature
 | 
			
		||||
   *   bytes32[] r List of r param for the permit signature
 | 
			
		||||
   *   bytes32[] s List of s param for the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function executeOperation(
 | 
			
		||||
    address[] calldata assets,
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +66,11 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
    require(
 | 
			
		||||
      assets.length == decodedParams.assetToSwapToList.length
 | 
			
		||||
      && assets.length == decodedParams.repayAmounts.length
 | 
			
		||||
      && assets.length == decodedParams.rateModes.length,
 | 
			
		||||
      && assets.length == decodedParams.rateModes.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.deadline.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.v.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.r.length
 | 
			
		||||
      && assets.length == decodedParams.permitParams.s.length,
 | 
			
		||||
      'INCONSISTENT_PARAMS');
 | 
			
		||||
 | 
			
		||||
    for (uint256 i = 0; i < assets.length; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +82,13 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
        decodedParams.rateModes[i],
 | 
			
		||||
        initiator,
 | 
			
		||||
        decodedParams.leftOverAction,
 | 
			
		||||
        premiums[i]
 | 
			
		||||
        premiums[i],
 | 
			
		||||
        PermitSignature(
 | 
			
		||||
          decodedParams.permitParams.deadline[i],
 | 
			
		||||
          decodedParams.permitParams.v[i],
 | 
			
		||||
          decodedParams.permitParams.r[i],
 | 
			
		||||
          decodedParams.permitParams.s[i]
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +106,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
   * @param initiator Address of the user
 | 
			
		||||
   * @param leftOverAction enum indicating what to do with the left over balance from the swap
 | 
			
		||||
   * @param premium Fee of the flash loan
 | 
			
		||||
   * @param permitSignature struct containing the permit signature
 | 
			
		||||
   */
 | 
			
		||||
  function _swapAndRepay(
 | 
			
		||||
    address assetFrom,
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +116,8 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
    uint256 rateMode,
 | 
			
		||||
    address initiator,
 | 
			
		||||
    LeftoverAction leftOverAction,
 | 
			
		||||
    uint256 premium
 | 
			
		||||
    uint256 premium,
 | 
			
		||||
    PermitSignature memory permitSignature
 | 
			
		||||
  ) internal {
 | 
			
		||||
    swapTokensForExactTokens(assetFrom, assetTo, amount, repayAmount);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +126,7 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
    POOL.repay(assetTo, repayAmount, rateMode, initiator);
 | 
			
		||||
 | 
			
		||||
    uint256 flashLoanDebt = amount.add(premium);
 | 
			
		||||
    pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt);
 | 
			
		||||
    pullATokenAndRepayFlashLoan(assetFrom, initiator, flashLoanDebt, permitSignature);
 | 
			
		||||
 | 
			
		||||
    // Take care of reserve leftover from the swap
 | 
			
		||||
    sendLeftovers(assetFrom, flashLoanDebt, leftOverAction, initiator);
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +141,10 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
   *     (1) Direct transfer to user
 | 
			
		||||
   *   uint256[] repayAmounts List of amounts of debt to be repaid
 | 
			
		||||
   *   uint256[] rateModes List of the rate modes of the debt to be repaid
 | 
			
		||||
   *   uint256[] deadline List of deadlines for the permit signature
 | 
			
		||||
   *   uint8[] v List of v param for the permit signature
 | 
			
		||||
   *   bytes32[] r List of r param for the permit signature
 | 
			
		||||
   *   bytes32[] s List of s param for the permit signature
 | 
			
		||||
   * @return RepayParams struct containing decoded params
 | 
			
		||||
   */
 | 
			
		||||
  function _decodeParams(bytes memory params) internal returns (RepayParams memory) {
 | 
			
		||||
| 
						 | 
				
			
			@ -131,9 +152,24 @@ contract UniswapRepayAdapter is BaseUniswapAdapter, IFlashLoanReceiver {
 | 
			
		|||
      address[] memory assetToSwapToList,
 | 
			
		||||
      LeftoverAction leftOverAction,
 | 
			
		||||
      uint256[] memory repayAmounts,
 | 
			
		||||
      uint256[] memory rateModes
 | 
			
		||||
    ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[]));
 | 
			
		||||
      uint256[] memory rateModes,
 | 
			
		||||
      uint256[] memory deadline,
 | 
			
		||||
      uint8[] memory v,
 | 
			
		||||
      bytes32[] memory r,
 | 
			
		||||
      bytes32[] memory s
 | 
			
		||||
    ) = abi.decode(params, (address[], LeftoverAction, uint256[], uint256[], uint256[], uint8[], bytes32[], bytes32[]));
 | 
			
		||||
 | 
			
		||||
    return RepayParams(assetToSwapToList, leftOverAction, repayAmounts, rateModes);
 | 
			
		||||
    return RepayParams(
 | 
			
		||||
      assetToSwapToList,
 | 
			
		||||
        leftOverAction,
 | 
			
		||||
        repayAmounts,
 | 
			
		||||
        rateModes,
 | 
			
		||||
        PermitParams(
 | 
			
		||||
          deadline,
 | 
			
		||||
          v,
 | 
			
		||||
          r,
 | 
			
		||||
          s
 | 
			
		||||
        )
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								contracts/interfaces/IERC20WithPermit.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								contracts/interfaces/IERC20WithPermit.sol
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
// SPDX-License-Identifier: agpl-3.0
 | 
			
		||||
pragma solidity ^0.6.8;
 | 
			
		||||
 | 
			
		||||
import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
 | 
			
		||||
 | 
			
		||||
interface IERC20WithPermit is IERC20 {
 | 
			
		||||
  function permit(
 | 
			
		||||
    address owner,
 | 
			
		||||
    address spender,
 | 
			
		||||
    uint256 value,
 | 
			
		||||
    uint256 deadline,
 | 
			
		||||
    uint8 v,
 | 
			
		||||
    bytes32 r,
 | 
			
		||||
    bytes32 s
 | 
			
		||||
  ) external;
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user