// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

/**
 * @title CalldataLogic library
 * @author Aave
 * @notice Library to decode calldata, used to optimize calldata size in L2Pool for transaction cost reduction
 */
library CalldataLogic {
  /**
   * @notice Decodes compressed supply params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed supply params
   * @return The address of the underlying reserve
   * @return The amount to supply
   * @return The referralCode
   */
  function decodeSupplyParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256, uint16) {
    uint16 assetId;
    uint256 amount;
    uint16 referralCode;

    assembly {
      assetId := and(args, 0xFFFF)
      amount := and(shr(16, args), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
      referralCode := and(shr(144, args), 0xFFFF)
    }
    return (reservesList[assetId], amount, referralCode);
  }

  /**
   * @notice Decodes compressed supply params to standard params along with permit params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed supply with permit params
   * @return The address of the underlying reserve
   * @return The amount to supply
   * @return The referralCode
   * @return The deadline of the permit
   * @return The V value of the permit signature
   */
  function decodeSupplyWithPermitParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256, uint16, uint256, uint8) {
    uint256 deadline;
    uint8 permitV;

    assembly {
      deadline := and(shr(160, args), 0xFFFFFFFF)
      permitV := and(shr(192, args), 0xFF)
    }
    (address asset, uint256 amount, uint16 referralCode) = decodeSupplyParams(reservesList, args);

    return (asset, amount, referralCode, deadline, permitV);
  }

  /**
   * @notice Decodes compressed withdraw params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed withdraw params
   * @return The address of the underlying reserve
   * @return The amount to withdraw
   */
  function decodeWithdrawParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256) {
    uint16 assetId;
    uint256 amount;
    assembly {
      assetId := and(args, 0xFFFF)
      amount := and(shr(16, args), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
    }
    if (amount == type(uint128).max) {
      amount = type(uint256).max;
    }
    return (reservesList[assetId], amount);
  }

  /**
   * @notice Decodes compressed borrow params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed borrow params
   * @return The address of the underlying reserve
   * @return The amount to borrow
   * @return The interestRateMode, 2 for variable debt, 1 is deprecated (changed on v3.2.0)
   * @return The referralCode
   */
  function decodeBorrowParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256, uint256, uint16) {
    uint16 assetId;
    uint256 amount;
    uint256 interestRateMode;
    uint16 referralCode;

    assembly {
      assetId := and(args, 0xFFFF)
      amount := and(shr(16, args), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
      interestRateMode := and(shr(144, args), 0xFF)
      referralCode := and(shr(152, args), 0xFFFF)
    }

    return (reservesList[assetId], amount, interestRateMode, referralCode);
  }

  /**
   * @notice Decodes compressed repay params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed repay params
   * @return The address of the underlying reserve
   * @return The amount to repay
   * @return The interestRateMode, 2 for variable debt, 1 is deprecated (changed on v3.2.0)
   */
  function decodeRepayParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256, uint256) {
    uint16 assetId;
    uint256 amount;
    uint256 interestRateMode;

    assembly {
      assetId := and(args, 0xFFFF)
      amount := and(shr(16, args), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
      interestRateMode := and(shr(144, args), 0xFF)
    }

    if (amount == type(uint128).max) {
      amount = type(uint256).max;
    }

    return (reservesList[assetId], amount, interestRateMode);
  }

  /**
   * @notice Decodes compressed repay params to standard params along with permit params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed repay with permit params
   * @return The address of the underlying reserve
   * @return The amount to repay
   * @return The interestRateMode, 2 for variable debt, 1 is deprecated (changed on v3.2.0)
   * @return The deadline of the permit
   * @return The V value of the permit signature
   */
  function decodeRepayWithPermitParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, uint256, uint256, uint256, uint8) {
    uint256 deadline;
    uint8 permitV;

    (address asset, uint256 amount, uint256 interestRateMode) = decodeRepayParams(
      reservesList,
      args
    );

    assembly {
      deadline := and(shr(152, args), 0xFFFFFFFF)
      permitV := and(shr(184, args), 0xFF)
    }

    return (asset, amount, interestRateMode, deadline, permitV);
  }

  /**
   * @notice Decodes compressed set user use reserve as collateral params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args The packed set user use reserve as collateral params
   * @return The address of the underlying reserve
   * @return True if to set using as collateral, false otherwise
   */
  function decodeSetUserUseReserveAsCollateralParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args
  ) internal view returns (address, bool) {
    uint16 assetId;
    bool useAsCollateral;
    assembly {
      assetId := and(args, 0xFFFF)
      useAsCollateral := and(shr(16, args), 0x1)
    }
    return (reservesList[assetId], useAsCollateral);
  }

  /**
   * @notice Decodes compressed liquidation call params to standard params
   * @param reservesList The addresses of all the active reserves
   * @param args1 The first half of packed liquidation call params
   * @param args2 The second half of the packed liquidation call params
   * @return The address of the underlying collateral asset
   * @return The address of the underlying debt asset
   * @return The address of the user to liquidate
   * @return The amount of debt to cover
   * @return True if receiving aTokens, false otherwise
   */
  function decodeLiquidationCallParams(
    mapping(uint256 => address) storage reservesList,
    bytes32 args1,
    bytes32 args2
  ) internal view returns (address, address, address, uint256, bool) {
    uint16 collateralAssetId;
    uint16 debtAssetId;
    address user;
    uint256 debtToCover;
    bool receiveAToken;

    assembly {
      collateralAssetId := and(args1, 0xFFFF)
      debtAssetId := and(shr(16, args1), 0xFFFF)
      user := and(shr(32, args1), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)

      debtToCover := and(args2, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
      receiveAToken := and(shr(128, args2), 0x1)
    }

    if (debtToCover == type(uint128).max) {
      debtToCover = type(uint256).max;
    }

    return (
      reservesList[collateralAssetId],
      reservesList[debtAssetId],
      user,
      debtToCover,
      receiveAToken
    );
  }
}
