creation_sourcecode: // SPDX-License-Identifier: BUSL-1.1
  pragma solidity 0.8.20;
  
  /// @notice Interface of the AmmTreasury contract.
  interface IAmmTreasuryBaseV1 {
      /// @notice Gets router address.
      function router() external view returns (address);
  
      /// @notice Retrieves the version number of the contract.
      /// @return The version number of the contract.
      /// @dev This function provides a way to access the version information of the contract.
      /// Currently, the version is set to 1.
      function getVersion() external pure returns (uint256);
  
      /// @notice Gets the balance of the liquidity pool.
      /// @dev Liquidity Pool balance not take into account following balances: collateral, ipor publication fee, treasury
      function getLiquidityPoolBalance() external view returns (uint256);
  
      /// @notice Pauses the contract and revokes the approval of stEth tokens for the router.
      /// @dev This function can only be called by the pause guardian.
      /// It revokes the approval of stEth tokens for the router and then pauses the contract.
      /// require Caller must be the pause guardian.
      function pause() external;
  
      /// @notice Unpauses the contract and forcefully approves the router to transfer an unlimited amount of stEth tokens.
      /// @dev This function can only be called by the contract owner.
      /// It unpauses the contract and then forcefully sets the approval of stEth tokens for the router to the maximum possible value.
      /// require Caller must be the contract owner.
      function unpause() external;
  
      /// @notice Checks if the given account is a pause guardian.
      /// @param account Address to be checked.
      /// @return A boolean indicating whether the provided account is a pause guardian.
      /// @dev This function queries the PauseManager to determine if the provided account is a pause guardian.
      function isPauseGuardian(address account) external view returns (bool);
  
      /// @notice Adds a new pause guardian to the contract.
      /// @param guardians List Addresses of the accounts to be added as a pause guardian.
      /// @dev This function can only be called by the contract owner.
      /// It delegates the addition of a new pause guardian to the PauseManager.
      /// require Caller must be the contract owner.
      function addPauseGuardians(address[] calldata guardians) external;
  
      /// @notice Removes an existing pause guardian from the contract.
      /// @param guardians List addresses of the accounts to be removed as a pause guardian.
      /// @dev This function can only be called by the contract owner.
      /// It delegates the removal of a pause guardian to the PauseManager.
      /// require Caller must be the contract owner.
      function removePauseGuardians(address[] calldata guardians) external;
  }
  
  /// @title Technical interface for reading data related to the UUPS proxy pattern in Ipor Protocol.
  interface IProxyImplementation {
      /// @notice Retrieves the address of the implementation contract for UUPS proxy.
      /// @return The address of the implementation contract.
      /// @dev The function returns the value stored in the implementation storage slot.
      function getImplementation() external view returns (address);
  }
  
  /// @title Types used in AmmStorage smart contract
  library AmmStorageTypes {
      /// @notice struct representing swap's ID and direction
      /// @dev direction = 0 - Pay Fixed - Receive Floating, direction = 1 - Receive Fixed - Pay Floating
      struct IporSwapId {
          /// @notice Swap's ID
          uint256 id;
          /// @notice Swap's direction, 0 - Pay Fixed Receive Floating, 1 - Receive Fixed Pay Floating
          uint8 direction;
      }
  
      /// @notice Struct containing extended balance information.
      /// @dev extended information includes: opening fee balance, liquidation deposit balance,
      /// IPOR publication fee balance, treasury balance, all values are with 18 decimals
      struct ExtendedBalancesMemory {
          /// @notice Swap's balance for Pay Fixed leg
          uint256 totalCollateralPayFixed;
          /// @notice Swap's balance for Receive Fixed leg
          uint256 totalCollateralReceiveFixed;
          /// @notice Liquidity Pool's Balance
          uint256 liquidityPool;
          /// @notice AssetManagement's (Asset Management) balance
          uint256 vault;
          /// @notice IPOR publication fee balance. This balance is used to subsidise the oracle operations
          uint256 iporPublicationFee;
          /// @notice Balance of the DAO's treasury. Fed by portion of the opening fee set by the DAO
          uint256 treasury;
      }
  
      /// @notice A struct with parameters required to calculate SOAP for pay fixed and receive fixed legs.
      /// @dev Committed to the memory.
      struct SoapIndicators {
          /// @notice Value of interest accrued on a fixed leg of all derivatives for this particular type of swap.
          /// @dev Represented in 18 decimals.
          uint256 hypotheticalInterestCumulative;
          /// @notice Sum of all swaps' notional amounts for a given leg.
          /// @dev Represented in 18 decimals.
          uint256 totalNotional;
          /// @notice Sum of all IBTs on a given leg.
          /// @dev Represented in 18 decimals.
          uint256 totalIbtQuantity;
          /// @notice The notional-weighted average interest rate of all swaps on a given leg combined.
          /// @dev Represented in 18 decimals.
          uint256 averageInterestRate;
          /// @notice EPOCH timestamp of when the most recent rebalancing took place
          uint256 rebalanceTimestamp;
      }
  }
  
  /// @title Struct used across various interfaces in IPOR Protocol.
  library IporTypes {
      /// @notice enum describing Swap's state, ACTIVE - when the swap is opened, INACTIVE when it's closed
      enum SwapState {
          INACTIVE,
          ACTIVE
      }
  
      /// @notice enum describing Swap's duration, 28 days, 60 days or 90 days
      enum SwapTenor {
          DAYS_28,
          DAYS_60,
          DAYS_90
      }
  
      /// @notice The struct describing the IPOR and its params calculated for the time when it was most recently updated and the change that took place since the update.
      /// Namely, the interest that would be computed into IBT should the rebalance occur.
      struct  AccruedIpor {
          /// @notice IPOR Index Value
          /// @dev value represented in 18 decimals
          uint256 indexValue;
          /// @notice IBT Price (IBT - Interest Bearing Token). For more information refer to the documentation:
          /// https://ipor-labs.gitbook.io/ipor-labs/interest-rate-derivatives/ibt
          /// @dev value represented in 18 decimals
          uint256 ibtPrice;
      }
  
      /// @notice Struct representing balances used internally for asset calculations
      /// @dev all balances in 18 decimals
      struct AmmBalancesMemory {
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Fixed & Receive Floating leg.
          uint256 totalCollateralPayFixed;
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Floating & Receive Fixed leg.
          uint256 totalCollateralReceiveFixed;
          /// @notice Liquidity Pool Balance. This balance is where the liquidity from liquidity providers and the opening fee are accounted for,
          /// @dev Amount of opening fee accounted in this balance is defined by _OPENING_FEE_FOR_TREASURY_PORTION_RATE param.
          uint256 liquidityPool;
          /// @notice Vault's balance, describes how much asset has been transferred to Asset Management Vault (AssetManagement)
          uint256 vault;
      }
  
      struct AmmBalancesForOpenSwapMemory {
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Fixed & Receive Floating leg.
          uint256 totalCollateralPayFixed;
          /// @notice Total notional amount of all swaps on  Pay Fixed leg (denominated in 18 decimals).
          uint256 totalNotionalPayFixed;
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Floating & Receive Fixed leg.
          uint256 totalCollateralReceiveFixed;
          /// @notice Total notional amount of all swaps on  Receive Fixed leg (denominated in 18 decimals).
          uint256 totalNotionalReceiveFixed;
          /// @notice Liquidity Pool Balance.
          uint256 liquidityPool;
      }
  
      struct SpreadInputs {
          //// @notice Swap's assets DAI/USDC/USDT
          address asset;
          /// @notice Swap's notional value
          uint256 swapNotional;
          /// @notice demand spread factor used in demand spread calculation
          uint256 demandSpreadFactor;
          /// @notice Base spread
          int256 baseSpreadPerLeg;
          /// @notice Swap's balance for Pay Fixed leg
          uint256 totalCollateralPayFixed;
          /// @notice Swap's balance for Receive Fixed leg
          uint256 totalCollateralReceiveFixed;
          /// @notice Liquidity Pool's Balance
          uint256 liquidityPoolBalance;
          /// @notice Ipor index value at the time of swap creation
          uint256 iporIndexValue;
          // @notice fixed rate cap for given leg for offered rate without demandSpread in 18 decimals
          uint256 fixedRateCapPerLeg;
      }
  }
  
  library Constants {
      uint256 public constant MAX_VALUE = type(uint256).max;
      uint256 public constant WAD_LEVERAGE_1000 = 1_000e18;
      uint256 public constant YEAR_IN_SECONDS = 365 days;
      uint256 public constant MAX_CHUNK_SIZE = 50;
  }
  
  /// @title Storage ID's associated with the IPOR Protocol Router.
  library StorageLib {
      uint256 constant STORAGE_SLOT_BASE = 1_000_000;
  
      // append only
      enum StorageId {
          /// @dev The address of the contract owner.
          Owner,
          AppointedOwner,
          Paused,
          PauseGuardian,
          ReentrancyStatus,
          RouterFunctionPaused,
          AmmSwapsLiquidators,
          AmmPoolsAppointedToRebalance,
          AmmPoolsParams
      }
  
      /// @notice Struct which contains owner address of IPOR Protocol Router.
      struct OwnerStorage {
          address owner;
      }
  
      /// @notice Struct which contains appointed owner address of IPOR Protocol Router.
      struct AppointedOwnerStorage {
          address appointedOwner;
      }
  
      /// @notice Struct which contains reentrancy status of IPOR Protocol Router.
      struct ReentrancyStatusStorage {
          uint256 value;
      }
  
      /// @notice Struct which contains information about swap liquidators.
      /// @dev First key is an asset (pool), second key is an liquidator address in the asset pool,
      /// value is a flag to indicate whether account is a liquidator.
      /// True - account is a liquidator, False - account is not a liquidator.
      struct AmmSwapsLiquidatorsStorage {
          mapping(address => mapping(address => bool)) value;
      }
  
      /// @notice Struct which contains information about accounts appointed to rebalance.
      /// @dev first key - asset address, second key - account address which is allowed to rebalance in the asset pool,
      /// value - flag to indicate whether account is allowed to rebalance. True - allowed, False - not allowed.
      struct AmmPoolsAppointedToRebalanceStorage {
          mapping(address => mapping(address => bool)) value;
      }
  
      struct AmmPoolsParamsValue {
          /// @dev max liquidity pool balance in the asset pool, represented without 18 decimals
          uint32 maxLiquidityPoolBalance;
          /// @dev The threshold for auto-rebalancing the pool. Value represented without 18 decimals.
          /// Value represents multiplication of 1000.
          uint32 autoRebalanceThresholdInThousands;
          /// @dev asset management ratio, represented without 18 decimals, value represents percentage with 2 decimals
          /// 65% = 6500, 99,99% = 9999, this is a percentage which stay in Amm Treasury in opposite to Asset Management
          /// based on AMM Treasury balance (100%).
          uint16 ammTreasuryAndAssetManagementRatio;
      }
  
      /// @dev key - asset address, value - struct AmmOpenSwapParamsValue
      struct AmmPoolsParamsStorage {
          mapping(address => AmmPoolsParamsValue) value;
      }
  
      /// @dev key - function sig, value - 1 if function is paused, 0 if not
      struct RouterFunctionPausedStorage {
          mapping(bytes4 => uint256) value;
      }
  
      /// @notice Gets Ipor Protocol Router owner address.
      function getOwner() internal pure returns (OwnerStorage storage owner) {
          uint256 slot = _getStorageSlot(StorageId.Owner);
          assembly {
              owner.slot := slot
          }
      }
  
      /// @notice Gets Ipor Protocol Router appointed owner address.
      function getAppointedOwner() internal pure returns (AppointedOwnerStorage storage appointedOwner) {
          uint256 slot = _getStorageSlot(StorageId.AppointedOwner);
          assembly {
              appointedOwner.slot := slot
          }
      }
  
      /// @notice Gets Ipor Protocol Router reentrancy status.
      function getReentrancyStatus() internal pure returns (ReentrancyStatusStorage storage reentrancyStatus) {
          uint256 slot = _getStorageSlot(StorageId.ReentrancyStatus);
          assembly {
              reentrancyStatus.slot := slot
          }
      }
  
      /// @notice Gets information if function is paused in Ipor Protocol Router.
      function getRouterFunctionPaused() internal pure returns (RouterFunctionPausedStorage storage paused) {
          uint256 slot = _getStorageSlot(StorageId.RouterFunctionPaused);
          assembly {
              paused.slot := slot
          }
      }
  
      /// @notice Gets point to pause guardian storage.
      function getPauseGuardianStorage() internal pure returns (mapping(address => bool) storage store) {
          uint256 slot = _getStorageSlot(StorageId.PauseGuardian);
          assembly {
              store.slot := slot
          }
      }
  
      /// @notice Gets point to liquidators storage.
      /// @return store - point to liquidators storage.
      function getAmmSwapsLiquidatorsStorage() internal pure returns (AmmSwapsLiquidatorsStorage storage store) {
          uint256 slot = _getStorageSlot(StorageId.AmmSwapsLiquidators);
          assembly {
              store.slot := slot
          }
      }
  
      /// @notice Gets point to accounts appointed to rebalance storage.
      /// @return store - point to accounts appointed to rebalance storage.
      function getAmmPoolsAppointedToRebalanceStorage()
          internal
          pure
          returns (AmmPoolsAppointedToRebalanceStorage storage store)
      {
          uint256 slot = _getStorageSlot(StorageId.AmmPoolsAppointedToRebalance);
          assembly {
              store.slot := slot
          }
      }
  
      /// @notice Gets point to amm pools params storage.
      /// @return store - point to amm pools params storage.
      function getAmmPoolsParamsStorage() internal pure returns (AmmPoolsParamsStorage storage store) {
          uint256 slot = _getStorageSlot(StorageId.AmmPoolsParams);
          assembly {
              store.slot := slot
          }
      }
  
      function _getStorageSlot(StorageId storageId) private pure returns (uint256 slot) {
          return uint256(storageId) + STORAGE_SLOT_BASE;
      }
  }
  
  library IporErrors {
      /// @notice Error when address is wrong
      error WrongAddress(string errorCode, address wrongAddress, string message);
  
      /// @notice Error when amount is wrong
      error WrongAmount(string errorCode, uint256 value);
  
      /// @notice Error when caller is not an ipor protocol router
      error CallerNotIporProtocolRouter(string errorCode, address caller);
  
      /// @notice Error when caller is not a pause guardian
      error CallerNotPauseGuardian(string errorCode, address caller);
  
      /// @notice Error when caller is not a AmmTreasury contract
      error CallerNotAmmTreasury(string errorCode, address caller);
  
      /// @notice Error when given direction is not supported
      error UnsupportedDirection(string errorCode, uint256 direction);
  
      /// @notice Error when given asset is not supported
      error UnsupportedAsset(string errorCode, address asset);
  
      /// @notice Error when given asset is not supported
      error UnsupportedAssetPair(string errorCode, address poolAsset, address inputAsset);
  
      /// @notice Error when given module is not supported
      error UnsupportedModule(string errorCode, address asset);
  
      /// @notice Error when given tenor is not supported
      error UnsupportedTenor(string errorCode, uint256 tenor);
  
      /// @notice Error when Input Asset total amount is too low
      error InputAssetTotalAmountTooLow(string errorCode, uint256 value);
  
      /// @dev Error appears if user/account doesn't have enough balance to open a swap with a specific totalAmount
      error InputAssetBalanceTooLow(string errorCode, address inputAsset, uint256 inputAssetBalance, uint256 totalAmount);
  
      // 000-199 - general codes
  
      /// @notice General problem, address is wrong
      string public constant WRONG_ADDRESS = "IPOR_000";
  
      /// @notice General problem. Wrong decimals
      string public constant WRONG_DECIMALS = "IPOR_001";
  
      /// @notice General problem, addresses mismatch
      string public constant ADDRESSES_MISMATCH = "IPOR_002";
  
      /// @notice Sender's asset balance is too low to transfer and to open a swap
      string public constant SENDER_ASSET_BALANCE_TOO_LOW = "IPOR_003";
  
      /// @notice Value is not greater than zero
      string public constant VALUE_NOT_GREATER_THAN_ZERO = "IPOR_004";
  
      /// @notice Input arrays length mismatch
      string public constant INPUT_ARRAYS_LENGTH_MISMATCH = "IPOR_005";
  
      /// @notice Amount is too low to transfer
      string public constant NOT_ENOUGH_AMOUNT_TO_TRANSFER = "IPOR_006";
  
      /// @notice msg.sender is not an appointed owner, so cannot confirm his appointment to be an owner of a specific smart contract
      string public constant SENDER_NOT_APPOINTED_OWNER = "IPOR_007";
  
      /// @notice only Router can have access to function
      string public constant CALLER_NOT_IPOR_PROTOCOL_ROUTER = "IPOR_008";
  
      /// @notice Chunk size is equal to zero
      string public constant CHUNK_SIZE_EQUAL_ZERO = "IPOR_009";
  
      /// @notice Chunk size is too big
      string public constant CHUNK_SIZE_TOO_BIG = "IPOR_010";
  
      /// @notice Caller is not a pause guardian
      string public constant CALLER_NOT_PAUSE_GUARDIAN = "IPOR_011";
  
      /// @notice Request contains invalid method signature, which is not supported by the Ipor Protocol Router
      string public constant ROUTER_INVALID_SIGNATURE = "IPOR_012";
  
      /// @notice Only AMM Treasury can have access to function
      string public constant CALLER_NOT_AMM_TREASURY = "IPOR_013";
  
      /// @notice Caller is not an owner
      string public constant CALLER_NOT_OWNER = "IPOR_014";
  
      /// @notice Method is paused
      string public constant METHOD_PAUSED = "IPOR_015";
  
      /// @notice Reentrancy appears
      string public constant REENTRANCY = "IPOR_016";
  
      /// @notice Asset is not supported
      string public constant ASSET_NOT_SUPPORTED = "IPOR_017";
  
      /// @notice Return back ETH failed in Ipor Protocol Router
      string public constant ROUTER_RETURN_BACK_ETH_FAILED = "IPOR_018";
  
      /// @notice Risk indicators are expired
      string public constant RISK_INDICATORS_EXPIRED = "IPOR_019";
  
      /// @notice Signature is invalid for risk indicators
      string public constant RISK_INDICATORS_SIGNATURE_INVALID = "IPOR_020";
  
      /// @notice Input Asset used by user is not supported
      string public constant INPUT_ASSET_NOT_SUPPORTED = "IPOR_021";
  
      /// @notice Module Asset Management is not supported
      string public constant UNSUPPORTED_MODULE_ASSET_MANAGEMENT = "IPOR_022";
  }
  
  // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
  // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
  
  /**
   * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
   * checks.
   *
   * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
   * easily result in undesired exploitation or bugs, since developers usually
   * assume that overflows raise errors. `SafeCast` restores this intuition by
   * reverting the transaction when such an operation overflows.
   *
   * Using this library instead of the unchecked operations eliminates an entire
   * class of bugs, so it's recommended to use it always.
   *
   * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
   * all math on `uint256` and `int256` and then downcasting.
   */
  library SafeCast {
      /**
       * @dev Returns the downcasted uint248 from uint256, reverting on
       * overflow (when the input is greater than largest uint248).
       *
       * Counterpart to Solidity's `uint248` operator.
       *
       * Requirements:
       *
       * - input must fit into 248 bits
       *
       * _Available since v4.7._
       */
      function toUint248(uint256 value) internal pure returns (uint248) {
          require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
          return uint248(value);
      }
  
      /**
       * @dev Returns the downcasted uint240 from uint256, reverting on
       * overflow (when the input is greater than largest uint240).
       *
       * Counterpart to Solidity's `uint240` operator.
       *
       * Requirements:
       *
       * - input must fit into 240 bits
       *
       * _Available since v4.7._
       */
      function toUint240(uint256 value) internal pure returns (uint240) {
          require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
          return uint240(value);
      }
  
      /**
       * @dev Returns the downcasted uint232 from uint256, reverting on
       * overflow (when the input is greater than largest uint232).
       *
       * Counterpart to Solidity's `uint232` operator.
       *
       * Requirements:
       *
       * - input must fit into 232 bits
       *
       * _Available since v4.7._
       */
      function toUint232(uint256 value) internal pure returns (uint232) {
          require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
          return uint232(value);
      }
  
      /**
       * @dev Returns the downcasted uint224 from uint256, reverting on
       * overflow (when the input is greater than largest uint224).
       *
       * Counterpart to Solidity's `uint224` operator.
       *
       * Requirements:
       *
       * - input must fit into 224 bits
       *
       * _Available since v4.2._
       */
      function toUint224(uint256 value) internal pure returns (uint224) {
          require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
          return uint224(value);
      }
  
      /**
       * @dev Returns the downcasted uint216 from uint256, reverting on
       * overflow (when the input is greater than largest uint216).
       *
       * Counterpart to Solidity's `uint216` operator.
       *
       * Requirements:
       *
       * - input must fit into 216 bits
       *
       * _Available since v4.7._
       */
      function toUint216(uint256 value) internal pure returns (uint216) {
          require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
          return uint216(value);
      }
  
      /**
       * @dev Returns the downcasted uint208 from uint256, reverting on
       * overflow (when the input is greater than largest uint208).
       *
       * Counterpart to Solidity's `uint208` operator.
       *
       * Requirements:
       *
       * - input must fit into 208 bits
       *
       * _Available since v4.7._
       */
      function toUint208(uint256 value) internal pure returns (uint208) {
          require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
          return uint208(value);
      }
  
      /**
       * @dev Returns the downcasted uint200 from uint256, reverting on
       * overflow (when the input is greater than largest uint200).
       *
       * Counterpart to Solidity's `uint200` operator.
       *
       * Requirements:
       *
       * - input must fit into 200 bits
       *
       * _Available since v4.7._
       */
      function toUint200(uint256 value) internal pure returns (uint200) {
          require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
          return uint200(value);
      }
  
      /**
       * @dev Returns the downcasted uint192 from uint256, reverting on
       * overflow (when the input is greater than largest uint192).
       *
       * Counterpart to Solidity's `uint192` operator.
       *
       * Requirements:
       *
       * - input must fit into 192 bits
       *
       * _Available since v4.7._
       */
      function toUint192(uint256 value) internal pure returns (uint192) {
          require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
          return uint192(value);
      }
  
      /**
       * @dev Returns the downcasted uint184 from uint256, reverting on
       * overflow (when the input is greater than largest uint184).
       *
       * Counterpart to Solidity's `uint184` operator.
       *
       * Requirements:
       *
       * - input must fit into 184 bits
       *
       * _Available since v4.7._
       */
      function toUint184(uint256 value) internal pure returns (uint184) {
          require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
          return uint184(value);
      }
  
      /**
       * @dev Returns the downcasted uint176 from uint256, reverting on
       * overflow (when the input is greater than largest uint176).
       *
       * Counterpart to Solidity's `uint176` operator.
       *
       * Requirements:
       *
       * - input must fit into 176 bits
       *
       * _Available since v4.7._
       */
      function toUint176(uint256 value) internal pure returns (uint176) {
          require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
          return uint176(value);
      }
  
      /**
       * @dev Returns the downcasted uint168 from uint256, reverting on
       * overflow (when the input is greater than largest uint168).
       *
       * Counterpart to Solidity's `uint168` operator.
       *
       * Requirements:
       *
       * - input must fit into 168 bits
       *
       * _Available since v4.7._
       */
      function toUint168(uint256 value) internal pure returns (uint168) {
          require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
          return uint168(value);
      }
  
      /**
       * @dev Returns the downcasted uint160 from uint256, reverting on
       * overflow (when the input is greater than largest uint160).
       *
       * Counterpart to Solidity's `uint160` operator.
       *
       * Requirements:
       *
       * - input must fit into 160 bits
       *
       * _Available since v4.7._
       */
      function toUint160(uint256 value) internal pure returns (uint160) {
          require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
          return uint160(value);
      }
  
      /**
       * @dev Returns the downcasted uint152 from uint256, reverting on
       * overflow (when the input is greater than largest uint152).
       *
       * Counterpart to Solidity's `uint152` operator.
       *
       * Requirements:
       *
       * - input must fit into 152 bits
       *
       * _Available since v4.7._
       */
      function toUint152(uint256 value) internal pure returns (uint152) {
          require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
          return uint152(value);
      }
  
      /**
       * @dev Returns the downcasted uint144 from uint256, reverting on
       * overflow (when the input is greater than largest uint144).
       *
       * Counterpart to Solidity's `uint144` operator.
       *
       * Requirements:
       *
       * - input must fit into 144 bits
       *
       * _Available since v4.7._
       */
      function toUint144(uint256 value) internal pure returns (uint144) {
          require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
          return uint144(value);
      }
  
      /**
       * @dev Returns the downcasted uint136 from uint256, reverting on
       * overflow (when the input is greater than largest uint136).
       *
       * Counterpart to Solidity's `uint136` operator.
       *
       * Requirements:
       *
       * - input must fit into 136 bits
       *
       * _Available since v4.7._
       */
      function toUint136(uint256 value) internal pure returns (uint136) {
          require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
          return uint136(value);
      }
  
      /**
       * @dev Returns the downcasted uint128 from uint256, reverting on
       * overflow (when the input is greater than largest uint128).
       *
       * Counterpart to Solidity's `uint128` operator.
       *
       * Requirements:
       *
       * - input must fit into 128 bits
       *
       * _Available since v2.5._
       */
      function toUint128(uint256 value) internal pure returns (uint128) {
          require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
          return uint128(value);
      }
  
      /**
       * @dev Returns the downcasted uint120 from uint256, reverting on
       * overflow (when the input is greater than largest uint120).
       *
       * Counterpart to Solidity's `uint120` operator.
       *
       * Requirements:
       *
       * - input must fit into 120 bits
       *
       * _Available since v4.7._
       */
      function toUint120(uint256 value) internal pure returns (uint120) {
          require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
          return uint120(value);
      }
  
      /**
       * @dev Returns the downcasted uint112 from uint256, reverting on
       * overflow (when the input is greater than largest uint112).
       *
       * Counterpart to Solidity's `uint112` operator.
       *
       * Requirements:
       *
       * - input must fit into 112 bits
       *
       * _Available since v4.7._
       */
      function toUint112(uint256 value) internal pure returns (uint112) {
          require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
          return uint112(value);
      }
  
      /**
       * @dev Returns the downcasted uint104 from uint256, reverting on
       * overflow (when the input is greater than largest uint104).
       *
       * Counterpart to Solidity's `uint104` operator.
       *
       * Requirements:
       *
       * - input must fit into 104 bits
       *
       * _Available since v4.7._
       */
      function toUint104(uint256 value) internal pure returns (uint104) {
          require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
          return uint104(value);
      }
  
      /**
       * @dev Returns the downcasted uint96 from uint256, reverting on
       * overflow (when the input is greater than largest uint96).
       *
       * Counterpart to Solidity's `uint96` operator.
       *
       * Requirements:
       *
       * - input must fit into 96 bits
       *
       * _Available since v4.2._
       */
      function toUint96(uint256 value) internal pure returns (uint96) {
          require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
          return uint96(value);
      }
  
      /**
       * @dev Returns the downcasted uint88 from uint256, reverting on
       * overflow (when the input is greater than largest uint88).
       *
       * Counterpart to Solidity's `uint88` operator.
       *
       * Requirements:
       *
       * - input must fit into 88 bits
       *
       * _Available since v4.7._
       */
      function toUint88(uint256 value) internal pure returns (uint88) {
          require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
          return uint88(value);
      }
  
      /**
       * @dev Returns the downcasted uint80 from uint256, reverting on
       * overflow (when the input is greater than largest uint80).
       *
       * Counterpart to Solidity's `uint80` operator.
       *
       * Requirements:
       *
       * - input must fit into 80 bits
       *
       * _Available since v4.7._
       */
      function toUint80(uint256 value) internal pure returns (uint80) {
          require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
          return uint80(value);
      }
  
      /**
       * @dev Returns the downcasted uint72 from uint256, reverting on
       * overflow (when the input is greater than largest uint72).
       *
       * Counterpart to Solidity's `uint72` operator.
       *
       * Requirements:
       *
       * - input must fit into 72 bits
       *
       * _Available since v4.7._
       */
      function toUint72(uint256 value) internal pure returns (uint72) {
          require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
          return uint72(value);
      }
  
      /**
       * @dev Returns the downcasted uint64 from uint256, reverting on
       * overflow (when the input is greater than largest uint64).
       *
       * Counterpart to Solidity's `uint64` operator.
       *
       * Requirements:
       *
       * - input must fit into 64 bits
       *
       * _Available since v2.5._
       */
      function toUint64(uint256 value) internal pure returns (uint64) {
          require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
          return uint64(value);
      }
  
      /**
       * @dev Returns the downcasted uint56 from uint256, reverting on
       * overflow (when the input is greater than largest uint56).
       *
       * Counterpart to Solidity's `uint56` operator.
       *
       * Requirements:
       *
       * - input must fit into 56 bits
       *
       * _Available since v4.7._
       */
      function toUint56(uint256 value) internal pure returns (uint56) {
          require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
          return uint56(value);
      }
  
      /**
       * @dev Returns the downcasted uint48 from uint256, reverting on
       * overflow (when the input is greater than largest uint48).
       *
       * Counterpart to Solidity's `uint48` operator.
       *
       * Requirements:
       *
       * - input must fit into 48 bits
       *
       * _Available since v4.7._
       */
      function toUint48(uint256 value) internal pure returns (uint48) {
          require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
          return uint48(value);
      }
  
      /**
       * @dev Returns the downcasted uint40 from uint256, reverting on
       * overflow (when the input is greater than largest uint40).
       *
       * Counterpart to Solidity's `uint40` operator.
       *
       * Requirements:
       *
       * - input must fit into 40 bits
       *
       * _Available since v4.7._
       */
      function toUint40(uint256 value) internal pure returns (uint40) {
          require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
          return uint40(value);
      }
  
      /**
       * @dev Returns the downcasted uint32 from uint256, reverting on
       * overflow (when the input is greater than largest uint32).
       *
       * Counterpart to Solidity's `uint32` operator.
       *
       * Requirements:
       *
       * - input must fit into 32 bits
       *
       * _Available since v2.5._
       */
      function toUint32(uint256 value) internal pure returns (uint32) {
          require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
          return uint32(value);
      }
  
      /**
       * @dev Returns the downcasted uint24 from uint256, reverting on
       * overflow (when the input is greater than largest uint24).
       *
       * Counterpart to Solidity's `uint24` operator.
       *
       * Requirements:
       *
       * - input must fit into 24 bits
       *
       * _Available since v4.7._
       */
      function toUint24(uint256 value) internal pure returns (uint24) {
          require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
          return uint24(value);
      }
  
      /**
       * @dev Returns the downcasted uint16 from uint256, reverting on
       * overflow (when the input is greater than largest uint16).
       *
       * Counterpart to Solidity's `uint16` operator.
       *
       * Requirements:
       *
       * - input must fit into 16 bits
       *
       * _Available since v2.5._
       */
      function toUint16(uint256 value) internal pure returns (uint16) {
          require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
          return uint16(value);
      }
  
      /**
       * @dev Returns the downcasted uint8 from uint256, reverting on
       * overflow (when the input is greater than largest uint8).
       *
       * Counterpart to Solidity's `uint8` operator.
       *
       * Requirements:
       *
       * - input must fit into 8 bits
       *
       * _Available since v2.5._
       */
      function toUint8(uint256 value) internal pure returns (uint8) {
          require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
          return uint8(value);
      }
  
      /**
       * @dev Converts a signed int256 into an unsigned uint256.
       *
       * Requirements:
       *
       * - input must be greater than or equal to 0.
       *
       * _Available since v3.0._
       */
      function toUint256(int256 value) internal pure returns (uint256) {
          require(value >= 0, "SafeCast: value must be positive");
          return uint256(value);
      }
  
      /**
       * @dev Returns the downcasted int248 from int256, reverting on
       * overflow (when the input is less than smallest int248 or
       * greater than largest int248).
       *
       * Counterpart to Solidity's `int248` operator.
       *
       * Requirements:
       *
       * - input must fit into 248 bits
       *
       * _Available since v4.7._
       */
      function toInt248(int256 value) internal pure returns (int248 downcasted) {
          downcasted = int248(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
      }
  
      /**
       * @dev Returns the downcasted int240 from int256, reverting on
       * overflow (when the input is less than smallest int240 or
       * greater than largest int240).
       *
       * Counterpart to Solidity's `int240` operator.
       *
       * Requirements:
       *
       * - input must fit into 240 bits
       *
       * _Available since v4.7._
       */
      function toInt240(int256 value) internal pure returns (int240 downcasted) {
          downcasted = int240(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
      }
  
      /**
       * @dev Returns the downcasted int232 from int256, reverting on
       * overflow (when the input is less than smallest int232 or
       * greater than largest int232).
       *
       * Counterpart to Solidity's `int232` operator.
       *
       * Requirements:
       *
       * - input must fit into 232 bits
       *
       * _Available since v4.7._
       */
      function toInt232(int256 value) internal pure returns (int232 downcasted) {
          downcasted = int232(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
      }
  
      /**
       * @dev Returns the downcasted int224 from int256, reverting on
       * overflow (when the input is less than smallest int224 or
       * greater than largest int224).
       *
       * Counterpart to Solidity's `int224` operator.
       *
       * Requirements:
       *
       * - input must fit into 224 bits
       *
       * _Available since v4.7._
       */
      function toInt224(int256 value) internal pure returns (int224 downcasted) {
          downcasted = int224(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
      }
  
      /**
       * @dev Returns the downcasted int216 from int256, reverting on
       * overflow (when the input is less than smallest int216 or
       * greater than largest int216).
       *
       * Counterpart to Solidity's `int216` operator.
       *
       * Requirements:
       *
       * - input must fit into 216 bits
       *
       * _Available since v4.7._
       */
      function toInt216(int256 value) internal pure returns (int216 downcasted) {
          downcasted = int216(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
      }
  
      /**
       * @dev Returns the downcasted int208 from int256, reverting on
       * overflow (when the input is less than smallest int208 or
       * greater than largest int208).
       *
       * Counterpart to Solidity's `int208` operator.
       *
       * Requirements:
       *
       * - input must fit into 208 bits
       *
       * _Available since v4.7._
       */
      function toInt208(int256 value) internal pure returns (int208 downcasted) {
          downcasted = int208(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
      }
  
      /**
       * @dev Returns the downcasted int200 from int256, reverting on
       * overflow (when the input is less than smallest int200 or
       * greater than largest int200).
       *
       * Counterpart to Solidity's `int200` operator.
       *
       * Requirements:
       *
       * - input must fit into 200 bits
       *
       * _Available since v4.7._
       */
      function toInt200(int256 value) internal pure returns (int200 downcasted) {
          downcasted = int200(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
      }
  
      /**
       * @dev Returns the downcasted int192 from int256, reverting on
       * overflow (when the input is less than smallest int192 or
       * greater than largest int192).
       *
       * Counterpart to Solidity's `int192` operator.
       *
       * Requirements:
       *
       * - input must fit into 192 bits
       *
       * _Available since v4.7._
       */
      function toInt192(int256 value) internal pure returns (int192 downcasted) {
          downcasted = int192(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
      }
  
      /**
       * @dev Returns the downcasted int184 from int256, reverting on
       * overflow (when the input is less than smallest int184 or
       * greater than largest int184).
       *
       * Counterpart to Solidity's `int184` operator.
       *
       * Requirements:
       *
       * - input must fit into 184 bits
       *
       * _Available since v4.7._
       */
      function toInt184(int256 value) internal pure returns (int184 downcasted) {
          downcasted = int184(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
      }
  
      /**
       * @dev Returns the downcasted int176 from int256, reverting on
       * overflow (when the input is less than smallest int176 or
       * greater than largest int176).
       *
       * Counterpart to Solidity's `int176` operator.
       *
       * Requirements:
       *
       * - input must fit into 176 bits
       *
       * _Available since v4.7._
       */
      function toInt176(int256 value) internal pure returns (int176 downcasted) {
          downcasted = int176(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
      }
  
      /**
       * @dev Returns the downcasted int168 from int256, reverting on
       * overflow (when the input is less than smallest int168 or
       * greater than largest int168).
       *
       * Counterpart to Solidity's `int168` operator.
       *
       * Requirements:
       *
       * - input must fit into 168 bits
       *
       * _Available since v4.7._
       */
      function toInt168(int256 value) internal pure returns (int168 downcasted) {
          downcasted = int168(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
      }
  
      /**
       * @dev Returns the downcasted int160 from int256, reverting on
       * overflow (when the input is less than smallest int160 or
       * greater than largest int160).
       *
       * Counterpart to Solidity's `int160` operator.
       *
       * Requirements:
       *
       * - input must fit into 160 bits
       *
       * _Available since v4.7._
       */
      function toInt160(int256 value) internal pure returns (int160 downcasted) {
          downcasted = int160(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
      }
  
      /**
       * @dev Returns the downcasted int152 from int256, reverting on
       * overflow (when the input is less than smallest int152 or
       * greater than largest int152).
       *
       * Counterpart to Solidity's `int152` operator.
       *
       * Requirements:
       *
       * - input must fit into 152 bits
       *
       * _Available since v4.7._
       */
      function toInt152(int256 value) internal pure returns (int152 downcasted) {
          downcasted = int152(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
      }
  
      /**
       * @dev Returns the downcasted int144 from int256, reverting on
       * overflow (when the input is less than smallest int144 or
       * greater than largest int144).
       *
       * Counterpart to Solidity's `int144` operator.
       *
       * Requirements:
       *
       * - input must fit into 144 bits
       *
       * _Available since v4.7._
       */
      function toInt144(int256 value) internal pure returns (int144 downcasted) {
          downcasted = int144(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
      }
  
      /**
       * @dev Returns the downcasted int136 from int256, reverting on
       * overflow (when the input is less than smallest int136 or
       * greater than largest int136).
       *
       * Counterpart to Solidity's `int136` operator.
       *
       * Requirements:
       *
       * - input must fit into 136 bits
       *
       * _Available since v4.7._
       */
      function toInt136(int256 value) internal pure returns (int136 downcasted) {
          downcasted = int136(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
      }
  
      /**
       * @dev Returns the downcasted int128 from int256, reverting on
       * overflow (when the input is less than smallest int128 or
       * greater than largest int128).
       *
       * Counterpart to Solidity's `int128` operator.
       *
       * Requirements:
       *
       * - input must fit into 128 bits
       *
       * _Available since v3.1._
       */
      function toInt128(int256 value) internal pure returns (int128 downcasted) {
          downcasted = int128(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
      }
  
      /**
       * @dev Returns the downcasted int120 from int256, reverting on
       * overflow (when the input is less than smallest int120 or
       * greater than largest int120).
       *
       * Counterpart to Solidity's `int120` operator.
       *
       * Requirements:
       *
       * - input must fit into 120 bits
       *
       * _Available since v4.7._
       */
      function toInt120(int256 value) internal pure returns (int120 downcasted) {
          downcasted = int120(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
      }
  
      /**
       * @dev Returns the downcasted int112 from int256, reverting on
       * overflow (when the input is less than smallest int112 or
       * greater than largest int112).
       *
       * Counterpart to Solidity's `int112` operator.
       *
       * Requirements:
       *
       * - input must fit into 112 bits
       *
       * _Available since v4.7._
       */
      function toInt112(int256 value) internal pure returns (int112 downcasted) {
          downcasted = int112(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
      }
  
      /**
       * @dev Returns the downcasted int104 from int256, reverting on
       * overflow (when the input is less than smallest int104 or
       * greater than largest int104).
       *
       * Counterpart to Solidity's `int104` operator.
       *
       * Requirements:
       *
       * - input must fit into 104 bits
       *
       * _Available since v4.7._
       */
      function toInt104(int256 value) internal pure returns (int104 downcasted) {
          downcasted = int104(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
      }
  
      /**
       * @dev Returns the downcasted int96 from int256, reverting on
       * overflow (when the input is less than smallest int96 or
       * greater than largest int96).
       *
       * Counterpart to Solidity's `int96` operator.
       *
       * Requirements:
       *
       * - input must fit into 96 bits
       *
       * _Available since v4.7._
       */
      function toInt96(int256 value) internal pure returns (int96 downcasted) {
          downcasted = int96(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
      }
  
      /**
       * @dev Returns the downcasted int88 from int256, reverting on
       * overflow (when the input is less than smallest int88 or
       * greater than largest int88).
       *
       * Counterpart to Solidity's `int88` operator.
       *
       * Requirements:
       *
       * - input must fit into 88 bits
       *
       * _Available since v4.7._
       */
      function toInt88(int256 value) internal pure returns (int88 downcasted) {
          downcasted = int88(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
      }
  
      /**
       * @dev Returns the downcasted int80 from int256, reverting on
       * overflow (when the input is less than smallest int80 or
       * greater than largest int80).
       *
       * Counterpart to Solidity's `int80` operator.
       *
       * Requirements:
       *
       * - input must fit into 80 bits
       *
       * _Available since v4.7._
       */
      function toInt80(int256 value) internal pure returns (int80 downcasted) {
          downcasted = int80(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
      }
  
      /**
       * @dev Returns the downcasted int72 from int256, reverting on
       * overflow (when the input is less than smallest int72 or
       * greater than largest int72).
       *
       * Counterpart to Solidity's `int72` operator.
       *
       * Requirements:
       *
       * - input must fit into 72 bits
       *
       * _Available since v4.7._
       */
      function toInt72(int256 value) internal pure returns (int72 downcasted) {
          downcasted = int72(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
      }
  
      /**
       * @dev Returns the downcasted int64 from int256, reverting on
       * overflow (when the input is less than smallest int64 or
       * greater than largest int64).
       *
       * Counterpart to Solidity's `int64` operator.
       *
       * Requirements:
       *
       * - input must fit into 64 bits
       *
       * _Available since v3.1._
       */
      function toInt64(int256 value) internal pure returns (int64 downcasted) {
          downcasted = int64(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
      }
  
      /**
       * @dev Returns the downcasted int56 from int256, reverting on
       * overflow (when the input is less than smallest int56 or
       * greater than largest int56).
       *
       * Counterpart to Solidity's `int56` operator.
       *
       * Requirements:
       *
       * - input must fit into 56 bits
       *
       * _Available since v4.7._
       */
      function toInt56(int256 value) internal pure returns (int56 downcasted) {
          downcasted = int56(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
      }
  
      /**
       * @dev Returns the downcasted int48 from int256, reverting on
       * overflow (when the input is less than smallest int48 or
       * greater than largest int48).
       *
       * Counterpart to Solidity's `int48` operator.
       *
       * Requirements:
       *
       * - input must fit into 48 bits
       *
       * _Available since v4.7._
       */
      function toInt48(int256 value) internal pure returns (int48 downcasted) {
          downcasted = int48(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
      }
  
      /**
       * @dev Returns the downcasted int40 from int256, reverting on
       * overflow (when the input is less than smallest int40 or
       * greater than largest int40).
       *
       * Counterpart to Solidity's `int40` operator.
       *
       * Requirements:
       *
       * - input must fit into 40 bits
       *
       * _Available since v4.7._
       */
      function toInt40(int256 value) internal pure returns (int40 downcasted) {
          downcasted = int40(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
      }
  
      /**
       * @dev Returns the downcasted int32 from int256, reverting on
       * overflow (when the input is less than smallest int32 or
       * greater than largest int32).
       *
       * Counterpart to Solidity's `int32` operator.
       *
       * Requirements:
       *
       * - input must fit into 32 bits
       *
       * _Available since v3.1._
       */
      function toInt32(int256 value) internal pure returns (int32 downcasted) {
          downcasted = int32(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
      }
  
      /**
       * @dev Returns the downcasted int24 from int256, reverting on
       * overflow (when the input is less than smallest int24 or
       * greater than largest int24).
       *
       * Counterpart to Solidity's `int24` operator.
       *
       * Requirements:
       *
       * - input must fit into 24 bits
       *
       * _Available since v4.7._
       */
      function toInt24(int256 value) internal pure returns (int24 downcasted) {
          downcasted = int24(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
      }
  
      /**
       * @dev Returns the downcasted int16 from int256, reverting on
       * overflow (when the input is less than smallest int16 or
       * greater than largest int16).
       *
       * Counterpart to Solidity's `int16` operator.
       *
       * Requirements:
       *
       * - input must fit into 16 bits
       *
       * _Available since v3.1._
       */
      function toInt16(int256 value) internal pure returns (int16 downcasted) {
          downcasted = int16(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
      }
  
      /**
       * @dev Returns the downcasted int8 from int256, reverting on
       * overflow (when the input is less than smallest int8 or
       * greater than largest int8).
       *
       * Counterpart to Solidity's `int8` operator.
       *
       * Requirements:
       *
       * - input must fit into 8 bits
       *
       * _Available since v3.1._
       */
      function toInt8(int256 value) internal pure returns (int8 downcasted) {
          downcasted = int8(value);
          require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
      }
  
      /**
       * @dev Converts an unsigned uint256 into a signed int256.
       *
       * Requirements:
       *
       * - input must be less than or equal to maxInt256.
       *
       * _Available since v3.0._
       */
      function toInt256(uint256 value) internal pure returns (int256) {
          // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
          require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
          return int256(value);
      }
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
  
  /**
   * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
   *
   * _Available since v4.8.3._
   */
  interface IERC1967Upgradeable {
      /**
       * @dev Emitted when the implementation is upgraded.
       */
      event Upgraded(address indexed implementation);
  
      /**
       * @dev Emitted when the admin account has changed.
       */
      event AdminChanged(address previousAdmin, address newAdmin);
  
      /**
       * @dev Emitted when the beacon is changed.
       */
      event BeaconUpgraded(address indexed beacon);
  }
  
  // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
  
  /**
   * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
   * proxy whose upgrades are fully controlled by the current implementation.
   */
  interface IERC1822ProxiableUpgradeable {
      /**
       * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
       * address.
       *
       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
       * function revert if invoked through a proxy.
       */
      function proxiableUUID() external view returns (bytes32);
  }
  
  // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
  
  /**
   * @dev This is the interface that {BeaconProxy} expects of its beacon.
   */
  interface IBeaconUpgradeable {
      /**
       * @dev Must return an address that can be used as a delegate call target.
       *
       * {BeaconProxy} will check that this address is a contract.
       */
      function implementation() external view returns (address);
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
  
  /**
   * @dev Interface of the ERC20 standard as defined in the EIP.
   */
  interface IERC20Upgradeable {
      /**
       * @dev Emitted when `value` tokens are moved from one account (`from`) to
       * another (`to`).
       *
       * Note that `value` may be zero.
       */
      event Transfer(address indexed from, address indexed to, uint256 value);
  
      /**
       * @dev Emitted when the allowance of a `spender` for an `owner` is set by
       * a call to {approve}. `value` is the new allowance.
       */
      event Approval(address indexed owner, address indexed spender, uint256 value);
  
      /**
       * @dev Returns the amount of tokens in existence.
       */
      function totalSupply() external view returns (uint256);
  
      /**
       * @dev Returns the amount of tokens owned by `account`.
       */
      function balanceOf(address account) external view returns (uint256);
  
      /**
       * @dev Moves `amount` tokens from the caller's account to `to`.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transfer(address to, uint256 amount) external returns (bool);
  
      /**
       * @dev Returns the remaining number of tokens that `spender` will be
       * allowed to spend on behalf of `owner` through {transferFrom}. This is
       * zero by default.
       *
       * This value changes when {approve} or {transferFrom} are called.
       */
      function allowance(address owner, address spender) external view returns (uint256);
  
      /**
       * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * IMPORTANT: Beware that changing an allowance with this method brings the risk
       * that someone may use both the old and the new allowance by unfortunate
       * transaction ordering. One possible solution to mitigate this race
       * condition is to first reduce the spender's allowance to 0 and set the
       * desired value afterwards:
       * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
       *
       * Emits an {Approval} event.
       */
      function approve(address spender, uint256 amount) external returns (bool);
  
      /**
       * @dev Moves `amount` tokens from `from` to `to` using the
       * allowance mechanism. `amount` is then deducted from the caller's
       * allowance.
       *
       * Returns a boolean value indicating whether the operation succeeded.
       *
       * Emits a {Transfer} event.
       */
      function transferFrom(address from, address to, uint256 amount) external returns (bool);
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
  
  /**
   * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
   * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
   *
   * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
   * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
   * need to send a transaction, and thus is not required to hold Ether at all.
   */
  interface IERC20PermitUpgradeable {
      /**
       * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
       * given ``owner``'s signed approval.
       *
       * IMPORTANT: The same issues {IERC20-approve} has related to transaction
       * ordering also apply here.
       *
       * Emits an {Approval} event.
       *
       * Requirements:
       *
       * - `spender` cannot be the zero address.
       * - `deadline` must be a timestamp in the future.
       * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
       * over the EIP712-formatted function arguments.
       * - the signature must use ``owner``'s current nonce (see {nonces}).
       *
       * For more information on the signature format, see the
       * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
       * section].
       */
      function permit(
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) external;
  
      /**
       * @dev Returns the current nonce for `owner`. This value must be
       * included whenever a signature is generated for {permit}.
       *
       * Every successful call to {permit} increases ``owner``'s nonce by one. This
       * prevents a signature from being used multiple times.
       */
      function nonces(address owner) external view returns (uint256);
  
      /**
       * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
       */
      // solhint-disable-next-line func-name-mixedcase
      function DOMAIN_SEPARATOR() external view returns (bytes32);
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
  
  /**
   * @dev Collection of functions related to the address type
   */
  library AddressUpgradeable {
      /**
       * @dev Returns true if `account` is a contract.
       *
       * [IMPORTANT]
       * ====
       * It is unsafe to assume that an address for which this function returns
       * false is an externally-owned account (EOA) and not a contract.
       *
       * Among others, `isContract` will return false for the following
       * types of addresses:
       *
       *  - an externally-owned account
       *  - a contract in construction
       *  - an address where a contract will be created
       *  - an address where a contract lived, but was destroyed
       *
       * Furthermore, `isContract` will also return true if the target contract within
       * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
       * which only has an effect at the end of a transaction.
       * ====
       *
       * [IMPORTANT]
       * ====
       * You shouldn't rely on `isContract` to protect against flash loan attacks!
       *
       * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
       * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
       * constructor.
       * ====
       */
      function isContract(address account) internal view returns (bool) {
          // This method relies on extcodesize/address.code.length, which returns 0
          // for contracts in construction, since the code is only stored at the end
          // of the constructor execution.
  
          return account.code.length > 0;
      }
  
      /**
       * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
       * `recipient`, forwarding all available gas and reverting on errors.
       *
       * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
       * of certain opcodes, possibly making contracts go over the 2300 gas limit
       * imposed by `transfer`, making them unable to receive funds via
       * `transfer`. {sendValue} removes this limitation.
       *
       * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
       *
       * IMPORTANT: because control is transferred to `recipient`, care must be
       * taken to not create reentrancy vulnerabilities. Consider using
       * {ReentrancyGuard} or the
       * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
       */
      function sendValue(address payable recipient, uint256 amount) internal {
          require(address(this).balance >= amount, "Address: insufficient balance");
  
          (bool success, ) = recipient.call{value: amount}("");
          require(success, "Address: unable to send value, recipient may have reverted");
      }
  
      /**
       * @dev Performs a Solidity function call using a low level `call`. A
       * plain `call` is an unsafe replacement for a function call: use this
       * function instead.
       *
       * If `target` reverts with a revert reason, it is bubbled up by this
       * function (like regular Solidity function calls).
       *
       * Returns the raw returned data. To convert to the expected return value,
       * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
       *
       * Requirements:
       *
       * - `target` must be a contract.
       * - calling `target` with `data` must not revert.
       *
       * _Available since v3.1._
       */
      function functionCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, "Address: low-level call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
       * `errorMessage` as a fallback revert reason when `target` reverts.
       *
       * _Available since v3.1._
       */
      function functionCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          return functionCallWithValue(target, data, 0, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but also transferring `value` wei to `target`.
       *
       * Requirements:
       *
       * - the calling contract must have an ETH balance of at least `value`.
       * - the called Solidity function must be `payable`.
       *
       * _Available since v3.1._
       */
      function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
          return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
       * with `errorMessage` as a fallback revert reason when `target` reverts.
       *
       * _Available since v3.1._
       */
      function functionCallWithValue(
          address target,
          bytes memory data,
          uint256 value,
          string memory errorMessage
      ) internal returns (bytes memory) {
          require(address(this).balance >= value, "Address: insufficient balance for call");
          (bool success, bytes memory returndata) = target.call{value: value}(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but performing a static call.
       *
       * _Available since v3.3._
       */
      function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
          return functionStaticCall(target, data, "Address: low-level static call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
       * but performing a static call.
       *
       * _Available since v3.3._
       */
      function functionStaticCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          (bool success, bytes memory returndata) = target.staticcall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
       * but performing a delegate call.
       *
       * _Available since v3.4._
       */
      function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
          return functionDelegateCall(target, data, "Address: low-level delegate call failed");
      }
  
      /**
       * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
       * but performing a delegate call.
       *
       * _Available since v3.4._
       */
      function functionDelegateCall(
          address target,
          bytes memory data,
          string memory errorMessage
      ) internal returns (bytes memory) {
          (bool success, bytes memory returndata) = target.delegatecall(data);
          return verifyCallResultFromTarget(target, success, returndata, errorMessage);
      }
  
      /**
       * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
       * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
       *
       * _Available since v4.8._
       */
      function verifyCallResultFromTarget(
          address target,
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal view returns (bytes memory) {
          if (success) {
              if (returndata.length == 0) {
                  // only check isContract if the call was successful and the return data is empty
                  // otherwise we already know that it was a contract
                  require(isContract(target), "Address: call to non-contract");
              }
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
  
      /**
       * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
       * revert reason or using the provided one.
       *
       * _Available since v4.3._
       */
      function verifyCallResult(
          bool success,
          bytes memory returndata,
          string memory errorMessage
      ) internal pure returns (bytes memory) {
          if (success) {
              return returndata;
          } else {
              _revert(returndata, errorMessage);
          }
      }
  
      function _revert(bytes memory returndata, string memory errorMessage) private pure {
          // Look for revert reason and bubble it up if present
          if (returndata.length > 0) {
              // The easiest way to bubble the revert reason is using memory via assembly
              /// @solidity memory-safe-assembly
              assembly {
                  let returndata_size := mload(returndata)
                  revert(add(32, returndata), returndata_size)
              }
          } else {
              revert(errorMessage);
          }
      }
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
  // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
  
  /**
   * @dev Library for reading and writing primitive types to specific storage slots.
   *
   * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
   * This library helps with reading and writing to such slots without the need for inline assembly.
   *
   * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
   *
   * Example usage to set ERC1967 implementation slot:
   * ```solidity
   * contract ERC1967 {
   *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
   *
   *     function _getImplementation() internal view returns (address) {
   *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
   *     }
   *
   *     function _setImplementation(address newImplementation) internal {
   *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
   *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
   *     }
   * }
   * ```
   *
   * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
   * _Available since v4.9 for `string`, `bytes`._
   */
  library StorageSlotUpgradeable {
      struct AddressSlot {
          address value;
      }
  
      struct BooleanSlot {
          bool value;
      }
  
      struct Bytes32Slot {
          bytes32 value;
      }
  
      struct Uint256Slot {
          uint256 value;
      }
  
      struct StringSlot {
          string value;
      }
  
      struct BytesSlot {
          bytes value;
      }
  
      /**
       * @dev Returns an `AddressSlot` with member `value` located at `slot`.
       */
      function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
       */
      function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
       */
      function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
       */
      function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `StringSlot` with member `value` located at `slot`.
       */
      function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
       */
      function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := store.slot
          }
      }
  
      /**
       * @dev Returns an `BytesSlot` with member `value` located at `slot`.
       */
      function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := slot
          }
      }
  
      /**
       * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
       */
      function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
          /// @solidity memory-safe-assembly
          assembly {
              r.slot := store.slot
          }
      }
  }
  
  library IporContractValidator {
      function checkAddress(address addr) internal pure returns (address) {
          require(addr != address(0), IporErrors.WRONG_ADDRESS);
          return addr;
      }
  }
  
  /// @title Ipor Protocol Router Pause Manager library
  library PauseManager {
      /// @notice Emitted when new pause guardian is added
      /// @param guardians List of addresses of guardian
      event PauseGuardiansAdded(address[] indexed guardians);
  
      /// @notice Emitted when pause guardian is removed
      /// @param guardians List of addresses of guardian
      event PauseGuardiansRemoved(address[] indexed guardians);
  
      /// @notice Checks if account is Ipor Protocol Router pause guardian
      /// @param account Address of guardian
      /// @return true if account is Ipor Protocol Router pause guardian
      function isPauseGuardian(address account) internal view returns (bool) {
          mapping(address => bool) storage pauseGuardians = StorageLib.getPauseGuardianStorage();
          return pauseGuardians[account];
      }
  
      /// @notice Adds Ipor Protocol Router pause guardian
      /// @param newGuardians Addresses of guardians
      function addPauseGuardians(address[] calldata newGuardians) internal {
          uint256 length = newGuardians.length;
          if (length == 0) {
              return;
          }
  
          mapping(address => bool) storage pauseGuardians = StorageLib.getPauseGuardianStorage();
  
          for (uint256 i; i < length; ) {
              pauseGuardians[newGuardians[i]] = true;
              unchecked {
                  i++;
              }
          }
          emit PauseGuardiansAdded(newGuardians);
      }
  
      /// @notice Removes Ipor Protocol Router pause guardian
      /// @param guardians Addresses of guardians
      function removePauseGuardians(address[] calldata guardians) internal {
          uint256 length = guardians.length;
  
          if (length == 0) {
              return;
          }
  
          mapping(address => bool) storage pauseGuardians = StorageLib.getPauseGuardianStorage();
  
          for (uint256 i; i < length; ) {
              pauseGuardians[guardians[i]] = false;
              unchecked {
                  i++;
              }
          }
          emit PauseGuardiansRemoved(guardians);
      }
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
  
  /**
   * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
   * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
   * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
   * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
   *
   * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
   * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
   * case an upgrade adds a module that needs to be initialized.
   *
   * For example:
   *
   * [.hljs-theme-light.nopadding]
   * ```solidity
   * contract MyToken is ERC20Upgradeable {
   *     function initialize() initializer public {
   *         __ERC20_init("MyToken", "MTK");
   *     }
   * }
   *
   * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
   *     function initializeV2() reinitializer(2) public {
   *         __ERC20Permit_init("MyToken");
   *     }
   * }
   * ```
   *
   * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
   * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
   *
   * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
   * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
   *
   * [CAUTION]
   * ====
   * Avoid leaving a contract uninitialized.
   *
   * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
   * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
   * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
   *
   * [.hljs-theme-light.nopadding]
   * ```
   * /// @custom:oz-upgrades-unsafe-allow constructor
   * constructor() {
   *     _disableInitializers();
   * }
   * ```
   * ====
   */
  abstract contract Initializable {
      /**
       * @dev Indicates that the contract has been initialized.
       * @custom:oz-retyped-from bool
       */
      uint8 private _initialized;
  
      /**
       * @dev Indicates that the contract is in the process of being initialized.
       */
      bool private _initializing;
  
      /**
       * @dev Triggered when the contract has been initialized or reinitialized.
       */
      event Initialized(uint8 version);
  
      /**
       * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
       * `onlyInitializing` functions can be used to initialize parent contracts.
       *
       * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
       * constructor.
       *
       * Emits an {Initialized} event.
       */
      modifier initializer() {
          bool isTopLevelCall = !_initializing;
          require(
              (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
              "Initializable: contract is already initialized"
          );
          _initialized = 1;
          if (isTopLevelCall) {
              _initializing = true;
          }
          _;
          if (isTopLevelCall) {
              _initializing = false;
              emit Initialized(1);
          }
      }
  
      /**
       * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
       * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
       * used to initialize parent contracts.
       *
       * A reinitializer may be used after the original initialization step. This is essential to configure modules that
       * are added through upgrades and that require initialization.
       *
       * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
       * cannot be nested. If one is invoked in the context of another, execution will revert.
       *
       * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
       * a contract, executing them in the right order is up to the developer or operator.
       *
       * WARNING: setting the version to 255 will prevent any future reinitialization.
       *
       * Emits an {Initialized} event.
       */
      modifier reinitializer(uint8 version) {
          require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
          _initialized = version;
          _initializing = true;
          _;
          _initializing = false;
          emit Initialized(version);
      }
  
      /**
       * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
       * {initializer} and {reinitializer} modifiers, directly or indirectly.
       */
      modifier onlyInitializing() {
          require(_initializing, "Initializable: contract is not initializing");
          _;
      }
  
      /**
       * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
       * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
       * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
       * through proxies.
       *
       * Emits an {Initialized} event the first time it is successfully executed.
       */
      function _disableInitializers() internal virtual {
          require(!_initializing, "Initializable: contract is initializing");
          if (_initialized != type(uint8).max) {
              _initialized = type(uint8).max;
              emit Initialized(type(uint8).max);
          }
      }
  
      /**
       * @dev Returns the highest version that has been initialized. See {reinitializer}.
       */
      function _getInitializedVersion() internal view returns (uint8) {
          return _initialized;
      }
  
      /**
       * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
       */
      function _isInitializing() internal view returns (bool) {
          return _initializing;
      }
  }
  
  /// @title Interface of the CloseSwap Lens.
  interface IAmmCloseSwapLens {
      /// @notice Structure representing the configuration of the AmmCloseSwapService for a given pool (asset).
      struct AmmCloseSwapServicePoolConfiguration {
          /// @notice asset address
          address asset;
          /// @notice asset decimals
          uint256 decimals;
          /// @notice Amm Storage contract address
          address ammStorage;
          /// @notice Amm Treasury contract address
          address ammTreasury;
          /// @notice Asset Management contract address, for stETH is empty, because stETH doesn't have asset management module
          address assetManagement;
          /// @notice Spread address, for USDT, USDC, DAI is a spread router address, for stETH is a spread address
          address spread;
          /// @notice Unwinding Fee Rate for unwinding the swap, represented in 18 decimals, 1e18 = 100%
          uint256 unwindingFeeRate;
          /// @notice Unwinding Fee Rate for unwinding the swap, part earmarked for the treasury, represented in 18 decimals, 1e18 = 100%
          uint256 unwindingFeeTreasuryPortionRate;
          /// @notice Max number of swaps (per leg) that can be liquidated in one call, represented without decimals
          uint256 maxLengthOfLiquidatedSwapsPerLeg;
          /// @notice Time before maturity when the community is allowed to close the swap, represented in seconds
          uint256 timeBeforeMaturityAllowedToCloseSwapByCommunity;
          /// @notice Time before maturity then the swap owner can close it, for tenor 28 days, represented in seconds
          uint256 timeBeforeMaturityAllowedToCloseSwapByBuyerTenor28days;
          /// @notice Time before maturity then the swap owner can close it, for tenor 60 days, represented in seconds
          uint256 timeBeforeMaturityAllowedToCloseSwapByBuyerTenor60days;
          /// @notice Time before maturity then the swap owner can close it, for tenor 90 days, represented in seconds
          uint256 timeBeforeMaturityAllowedToCloseSwapByBuyerTenor90days;
          /// @notice Min liquidation threshold allowing community to close the swap ahead of maturity, represented in 18 decimals
          uint256 minLiquidationThresholdToCloseBeforeMaturityByCommunity;
          /// @notice Min liquidation threshold allowing the owner to close the swap ahead of maturity, represented in 18 decimals
          uint256 minLiquidationThresholdToCloseBeforeMaturityByBuyer;
          /// @notice Min leverage of the virtual swap used in unwinding, represented in 18 decimals
          uint256 minLeverage;
          /// @notice Time after open swap when it is allowed to close swap with unwinding, for tenor 28 days, represented in seconds
          uint256 timeAfterOpenAllowedToCloseSwapWithUnwindingTenor28days;
          /// @notice Time after open swap when it is allowed to close swap with unwinding, for tenor 60 days, represented in seconds
          uint256 timeAfterOpenAllowedToCloseSwapWithUnwindingTenor60days;
          /// @notice Time after open swap when it is allowed to close swap with unwinding, for tenor 90 days, represented in seconds
          uint256 timeAfterOpenAllowedToCloseSwapWithUnwindingTenor90days;
      }
  
      /// @notice Returns the configuration of the AmmCloseSwapService for a given pool (asset).
      /// @param asset asset address
      /// @return AmmCloseSwapServicePoolConfiguration struct representing the configuration of the AmmCloseSwapService for a given pool (asset).
      function getAmmCloseSwapServicePoolConfiguration(
          address asset
      ) external view returns (AmmCloseSwapServicePoolConfiguration memory);
  
      /// @notice Returns the closing swap details for a given swap and closing timestamp.
      /// @param asset asset address
      /// @param account account address for which are returned closing swap details, for example closableStatus depends on the account
      /// @param direction swap direction
      /// @param swapId swap id
      /// @param closeTimestamp closing timestamp
      /// @param riskIndicatorsInput risk indicators input
      /// @return closingSwapDetails struct representing the closing swap details for a given swap and closing timestamp.
      function getClosingSwapDetails(
          address asset,
          address account,
          AmmTypes.SwapDirection direction,
          uint256 swapId,
          uint256 closeTimestamp,
          AmmTypes.CloseSwapRiskIndicatorsInput calldata riskIndicatorsInput
      ) external view returns (AmmTypes.ClosingSwapDetails memory closingSwapDetails);
  }
  
  /// @title Types used in interfaces strictly related to AMM (Automated Market Maker).
  /// @dev Used by IAmmTreasury and IAmmStorage interfaces.
  library AmmTypes {
      /// @notice Struct describing AMM Pool's core addresses.
      struct AmmPoolCoreModel {
          /// @notice asset address
          address asset;
          /// @notice asset decimals
          uint256 assetDecimals;
          /// @notice ipToken address associated to the asset
          address ipToken;
          /// @notice AMM Storage address
          address ammStorage;
          /// @notice AMM Treasury address
          address ammTreasury;
          /// @notice Asset Management address
          address assetManagement;
          /// @notice IPOR Oracle address
          address iporOracle;
      }
  
      /// @notice Structure which represents Swap's data that will be saved in the storage.
      /// Refer to the documentation https://ipor-labs.gitbook.io/ipor-labs/automated-market-maker/ipor-swaps for more information.
      struct NewSwap {
          /// @notice Account / trader who opens the Swap
          address buyer;
          /// @notice Epoch timestamp of when position was opened by the trader.
          uint256 openTimestamp;
          /// @notice Swap's collateral amount.
          /// @dev value represented in 18 decimals
          uint256 collateral;
          /// @notice Swap's notional amount.
          /// @dev value represented in 18 decimals
          uint256 notional;
          /// @notice Quantity of Interest Bearing Token (IBT) at moment when position was opened.
          /// @dev value represented in 18 decimals
          uint256 ibtQuantity;
          /// @notice Fixed interest rate at which the position has been opened.
          /// @dev value represented in 18 decimals
          uint256 fixedInterestRate;
          /// @notice Liquidation deposit is retained when the swap is opened. It is then paid back to agent who closes the derivative at maturity.
          /// It can be both trader or community member. Trader receives the deposit back when he chooses to close the derivative before maturity.
          /// @dev value represented WITHOUT 18 decimals for USDT, USDC, DAI pool. Notice! Value represented in 6 decimals for stETH pool.
          /// @dev Example value in 6 decimals: 25000000 (in 6 decimals) = 25 stETH = 25.000000
          uint256 liquidationDepositAmount;
          /// @notice Opening fee amount part which is allocated in Liquidity Pool Balance. This fee is calculated as a rate of the swap's collateral.
          /// @dev value represented in 18 decimals
          uint256 openingFeeLPAmount;
          /// @notice Opening fee amount part which is allocated in Treasury Balance. This fee is calculated as a rate of the swap's collateral.
          /// @dev value represented in 18 decimals
          uint256 openingFeeTreasuryAmount;
          /// @notice Swap's tenor, 0 - 28 days, 1 - 60 days or 2 - 90 days
          IporTypes.SwapTenor tenor;
      }
  
      /// @notice Struct representing swap item, used for listing and in internal calculations
      struct Swap {
          /// @notice Swap's unique ID
          uint256 id;
          /// @notice Swap's buyer
          address buyer;
          /// @notice Swap opening epoch timestamp
          uint256 openTimestamp;
          /// @notice Swap's tenor
          IporTypes.SwapTenor tenor;
          /// @notice Index position of this Swap in an array of swaps' identification associated to swap buyer
          /// @dev Field used for gas optimization purposes, it allows for quick removal by id in the array.
          /// During removal the last item in the array is switched with the one that just has been removed.
          uint256 idsIndex;
          /// @notice Swap's collateral
          /// @dev value represented in 18 decimals
          uint256 collateral;
          /// @notice Swap's notional amount
          /// @dev value represented in 18 decimals
          uint256 notional;
          /// @notice Swap's notional amount denominated in the Interest Bearing Token (IBT)
          /// @dev value represented in 18 decimals
          uint256 ibtQuantity;
          /// @notice Fixed interest rate at which the position has been opened
          /// @dev value represented in 18 decimals
          uint256 fixedInterestRate;
          /// @notice Liquidation deposit amount
          /// @dev value represented in 18 decimals
          uint256 liquidationDepositAmount;
          /// @notice State of the swap
          /// @dev 0 - INACTIVE, 1 - ACTIVE
          IporTypes.SwapState state;
      }
  
      /// @notice Struct representing amounts related to Swap that is presently being opened.
      /// @dev all values represented in 18 decimals
      struct OpenSwapAmount {
          /// @notice Total Amount of asset that is sent from buyer to AmmTreasury when opening swap.
          uint256 totalAmount;
          /// @notice Swap's collateral
          uint256 collateral;
          /// @notice Swap's notional
          uint256 notional;
          /// @notice Opening Fee - part allocated as a profit of the Liquidity Pool
          uint256 openingFeeLPAmount;
          /// @notice  Part of the fee set aside for subsidizing the oracle that publishes IPOR rate. Flat fee set by the DAO.
          /// @notice Opening Fee - part allocated in Treasury balance. Part of the fee set asside for subsidising the oracle that publishes IPOR rate. Flat fee set by the DAO.
          uint256 openingFeeTreasuryAmount;
          /// @notice Fee set aside for subsidizing the oracle that publishes IPOR rate. Flat fee set by the DAO.
          uint256 iporPublicationFee;
          /// @notice Liquidation deposit is retained when the swap is opened. Value represented in 18 decimals.
          uint256 liquidationDepositAmount;
      }
  
      /// @notice Structure describes one swap processed by closeSwaps method, information about swap ID and flag if this swap was closed during execution closeSwaps method.
      struct IporSwapClosingResult {
          /// @notice Swap ID
          uint256 swapId;
          /// @notice Flag describe if swap was closed during this execution
          bool closed;
      }
  
      /// @notice Technical structure used for storing information about amounts used during redeeming assets from liquidity pool.
      struct RedeemAmount {
          /// @notice Asset amount represented in 18 decimals
          /// @dev Asset amount is a sum of wadRedeemFee and wadRedeemAmount
          uint256 wadAssetAmount;
          /// @notice Redeemed amount represented in decimals of asset
          uint256 redeemAmount;
          /// @notice Redeem fee value represented in 18 decimals
          uint256 wadRedeemFee;
          /// @notice Redeem amount represented in 18 decimals
          uint256 wadRedeemAmount;
      }
  
      struct UnwindParams {
          /// @notice Risk Indicators Inputs signer
          address messageSigner;
          /// @notice Spread Router contract address
          address spreadRouter;
          address ammStorage;
          address ammTreasury;
          SwapDirection direction;
          uint256 closeTimestamp;
          int256 swapPnlValueToDate;
          uint256 indexValue;
          Swap swap;
          IAmmCloseSwapLens.AmmCloseSwapServicePoolConfiguration poolCfg;
          CloseSwapRiskIndicatorsInput riskIndicatorsInputs;
      }
  
      /// @notice Swap direction (long = Pay Fixed and Receive a Floating or short = receive fixed and pay a floating)
      enum SwapDirection {
          /// @notice When taking the "long" position the trader will pay a fixed rate and receive a floating rate.
          /// for more information refer to the documentation https://ipor-labs.gitbook.io/ipor-labs/automated-market-maker/ipor-swaps
          PAY_FIXED_RECEIVE_FLOATING,
          /// @notice When taking the "short" position the trader will pay a floating rate and receive a fixed rate.
          PAY_FLOATING_RECEIVE_FIXED
      }
      /// @notice List of closable statuses for a given swap
      /// @dev Closable status is a one of the following values:
      /// 0 - Swap is closable
      /// 1 - Swap is already closed
      /// 2 - Swap state required Buyer or Liquidator to close. Sender is not Buyer nor Liquidator.
      /// 3 - Cannot close swap, closing is too early for Community
      /// 4 - Cannot close swap with unwind because action is too early from the moment when swap was opened, validation based on Close Service configuration
      enum SwapClosableStatus {
          SWAP_IS_CLOSABLE,
          SWAP_ALREADY_CLOSED,
          SWAP_REQUIRED_BUYER_OR_LIQUIDATOR_TO_CLOSE,
          SWAP_CANNOT_CLOSE_CLOSING_TOO_EARLY_FOR_COMMUNITY,
          SWAP_CANNOT_CLOSE_WITH_UNWIND_ACTION_IS_TOO_EARLY
      }
  
      /// @notice Collection of swap attributes connected with IPOR Index and swap itself.
      /// @dev all values are in 18 decimals
      struct IporSwapIndicator {
          /// @notice IPOR Index value at the time of swap opening
          uint256 iporIndexValue;
          /// @notice IPOR Interest Bearing Token (IBT) price at the time of swap opening
          uint256 ibtPrice;
          /// @notice Swap's notional denominated in IBT
          uint256 ibtQuantity;
          /// @notice Fixed interest rate at which the position has been opened,
          /// it is quote from spread documentation
          uint256 fixedInterestRate;
      }
  
      /// @notice Risk indicators calculated for swap opening
      struct OpenSwapRiskIndicators {
          /// @notice Maximum collateral ratio in general
          uint256 maxCollateralRatio;
          /// @notice Maximum collateral ratio for a given leg
          uint256 maxCollateralRatioPerLeg;
          /// @notice Maximum leverage for a given leg
          uint256 maxLeveragePerLeg;
          /// @notice Base Spread for a given leg (without demand part)
          int256 baseSpreadPerLeg;
          /// @notice Fixed rate cap
          uint256 fixedRateCapPerLeg;
          /// @notice Demand spread factor used to calculate demand spread
          uint256 demandSpreadFactor;
      }
  
      /// @notice Risk indicators calculated for swap opening
      struct RiskIndicatorsInputs {
          /// @notice Maximum collateral ratio in general
          uint256 maxCollateralRatio;
          /// @notice Maximum collateral ratio for a given leg
          uint256 maxCollateralRatioPerLeg;
          /// @notice Maximum leverage for a given leg
          uint256 maxLeveragePerLeg;
          /// @notice Base Spread for a given leg (without demand part)
          int256 baseSpreadPerLeg;
          /// @notice Fixed rate cap
          uint256 fixedRateCapPerLeg;
          /// @notice Demand spread factor used to calculate demand spread
          uint256 demandSpreadFactor;
          /// @notice expiration date in seconds
          uint256 expiration;
          /// @notice signature of data (maxCollateralRatio, maxCollateralRatioPerLeg,maxLeveragePerLeg,baseSpreadPerLeg,fixedRateCapPerLeg,demandSpreadFactor,expiration,asset,tenor,direction)
          /// asset - address
          /// tenor - uint256
          /// direction - uint256
          bytes signature;
      }
  
      struct CloseSwapRiskIndicatorsInput {
          RiskIndicatorsInputs payFixed;
          RiskIndicatorsInputs receiveFixed;
      }
  
      /// @notice Structure containing information about swap's closing status, unwind values and PnL for a given swap and time.
      struct ClosingSwapDetails {
          /// @notice Swap's closing status
          AmmTypes.SwapClosableStatus closableStatus;
          /// @notice Flag indicating if swap unwind is required
          bool swapUnwindRequired;
          /// @notice Swap's unwind PnL Value, part of PnL corresponded to virtual swap (unwinded swap), represented in 18 decimals
          int256 swapUnwindPnlValue;
          /// @notice Unwind opening fee amount it is a sum of `swapUnwindFeeLPAmount` and `swapUnwindFeeTreasuryAmount`
          uint256 swapUnwindOpeningFeeAmount;
          /// @notice Part of unwind opening fee allocated as a profit of the Liquidity Pool
          uint256 swapUnwindFeeLPAmount;
          /// @notice Part of unwind opening fee allocated in Treasury Balance
          uint256 swapUnwindFeeTreasuryAmount;
          /// @notice Final Profit and Loss which takes into account the swap unwind and limits the PnL to the collateral amount. Represented in 18 decimals.
          int256 pnlValue;
      }
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
  
  /**
   * @dev Contract module that helps prevent reentrant calls to a function.
   *
   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
   * available, which can be applied to functions to make sure there are no nested
   * (reentrant) calls to them.
   *
   * Note that because there is a single `nonReentrant` guard, functions marked as
   * `nonReentrant` may not call one another. This can be worked around by making
   * those functions `private`, and then adding `external` `nonReentrant` entry
   * points to them.
   *
   * TIP: If you would like to learn more about reentrancy and alternative ways
   * to protect against it, check out our blog post
   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
   */
  abstract contract ReentrancyGuardUpgradeable is Initializable {
      // Booleans are more expensive than uint256 or any type that takes up a full
      // word because each write operation emits an extra SLOAD to first read the
      // slot's contents, replace the bits taken up by the boolean, and then write
      // back. This is the compiler's defense against contract upgrades and
      // pointer aliasing, and it cannot be disabled.
  
      // The values being non-zero value makes deployment a bit more expensive,
      // but in exchange the refund on every call to nonReentrant will be lower in
      // amount. Since refunds are capped to a percentage of the total
      // transaction's gas, it is best to keep them low in cases like this one, to
      // increase the likelihood of the full refund coming into effect.
      uint256 private constant _NOT_ENTERED = 1;
      uint256 private constant _ENTERED = 2;
  
      uint256 private _status;
  
      function __ReentrancyGuard_init() internal onlyInitializing {
          __ReentrancyGuard_init_unchained();
      }
  
      function __ReentrancyGuard_init_unchained() internal onlyInitializing {
          _status = _NOT_ENTERED;
      }
  
      /**
       * @dev Prevents a contract from calling itself, directly or indirectly.
       * Calling a `nonReentrant` function from another `nonReentrant`
       * function is not supported. It is possible to prevent this from happening
       * by making the `nonReentrant` function external, and making it call a
       * `private` function that does the actual work.
       */
      modifier nonReentrant() {
          _nonReentrantBefore();
          _;
          _nonReentrantAfter();
      }
  
      function _nonReentrantBefore() private {
          // On the first call to nonReentrant, _status will be _NOT_ENTERED
          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
  
          // Any calls to nonReentrant after this point will fail
          _status = _ENTERED;
      }
  
      function _nonReentrantAfter() private {
          // By storing the original value once again, a refund is triggered (see
          // https://eips.ethereum.org/EIPS/eip-2200)
          _status = _NOT_ENTERED;
      }
  
      /**
       * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
       * `nonReentrant` function in the call stack.
       */
      function _reentrancyGuardEntered() internal view returns (bool) {
          return _status == _ENTERED;
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[49] private __gap;
  }
  
  // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
  
  /**
   * @dev Provides information about the current execution context, including the
   * sender of the transaction and its data. While these are generally available
   * via msg.sender and msg.data, they should not be accessed in such a direct
   * manner, since when dealing with meta-transactions the account sending and
   * paying for execution may not be the actual sender (as far as an application
   * is concerned).
   *
   * This contract is only required for intermediate, library-like contracts.
   */
  abstract contract ContextUpgradeable is Initializable {
      function __Context_init() internal onlyInitializing {
      }
  
      function __Context_init_unchained() internal onlyInitializing {
      }
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  /// @notice The types used in the AmmTreasury's interface.
  /// @dev All values, where applicable, are represented in 18 decimals.
  library AmmInternalTypes {
      struct PnlValueStruct {
          /// @notice PnL Value of the swap.
          int256 pnlValue;
          /// @notice flag indicating if unwind is required when closing swap.
          bool swapUnwindRequired;
          /// @notice Unwind amount of the swap.
          int256 swapUnwindAmount;
          /// @notice Unwind fee of the swap that will be added to the AMM liquidity pool balance.
          uint256 swapUnwindFeeLPAmount;
          /// @notice Unwind fee of the swap that will be added to the AMM treasury balance.
          uint256 swapUnwindFeeTreasuryAmount;
      }
  
      struct BeforeOpenSwapStruct {
          /// @notice Sum of all asset transferred when opening swap. It includes the collateral, fees and deposits.
          /// @dev The amount is represented in 18 decimals regardless of the decimals of the asset.
          uint256 wadTotalAmount;
          /// @notice Swap's collateral.
          uint256 collateral;
          /// @notice Swap's notional amount.
          uint256 notional;
          /// @notice The part of the opening fee that will be added to the liquidity pool balance.
          uint256 openingFeeLPAmount;
          /// @notice Part of the opening fee that will be added to the treasury balance.
          uint256 openingFeeTreasuryAmount;
          /// @notice Amount of asset set aside for the oracle subsidization.
          uint256 iporPublicationFeeAmount;
          /// @notice Refundable deposit blocked for the entity that will close the swap.
          /// For more information on how the liquidations work refer to the documentation.
          /// https://ipor-labs.gitbook.io/ipor-labs/automated-market-maker/liquidations
          /// @dev value represented without decimals for USDT, USDC, DAI, with 6 decimals for stETH, as an integer.
          uint256 liquidationDepositAmount;
          /// @notice The struct describing the IPOR and its params calculated for the time when it was most recently updated and the change that took place since the update.
          /// Namely, the interest that would be computed into IBT should the rebalance occur.
          IporTypes.AccruedIpor accruedIpor;
      }
  
      /// @notice Risk indicators context data
      struct RiskIndicatorsContext {
          /// @notice Asset address for which the risk indicators are calculated.
          address asset;
          /// @notice Tenor of the swap.
          IporTypes.SwapTenor tenor;
          /// @notice AMM Liquidity Pool balance.
          uint256 liquidityPoolBalance;
          /// @notice AMM Min Leverage allowed for a swap.
          uint256 minLeverage;
      }
  
      /// @notice Spread context data
      struct SpreadContext {
          /// @notice Asset address for which the spread is calculated.
          address asset;
          /// @notice Signature of spread method used to calculate spread.
          bytes4 spreadFunctionSig;
          /// @notice Tenor of the swap.
          IporTypes.SwapTenor tenor;
          /// @notice Swap's notional
          uint256 notional;
          /// @notice Minimum leverage allowed for a swap.
          uint256 minLeverage;
          /// @notice Ipor Index Value
          uint256 indexValue;
          /// @notice Risk Indicators data for a opened swap used to calculate spread.
          AmmTypes.OpenSwapRiskIndicators riskIndicators;
          /// @notice AMM Balance for a opened swap used to calculate spread.
          IporTypes.AmmBalancesForOpenSwapMemory balance;
      }
  
      /// @notice Open swap item - element of linked list of swaps
      struct OpenSwapItem {
          /// @notice Swap ID
          uint32 swapId;
          /// @notcie Next swap ID in linked list
          uint32 nextSwapId;
          /// @notice Previous swap ID in linked list
          uint32 previousSwapId;
          /// @notice Timestamp of the swap opening
          uint32 openSwapTimestamp;
      }
  
      /// @notice Open swap list structure
      struct OpenSwapList {
          /// @notice Head swap ID
          uint32 headSwapId;
          /// @notice Swaps mapping, where key is swap ID
          mapping(uint32 => OpenSwapItem) swaps;
      }
  }
  
  /// @title Types used in interfaces strictly related to AMM (Automated Market Maker).
  /// @dev Used by IAmmTreasury and IAmmStorage interfaces.
  library AmmTypesBaseV1 {
      /// @notice Struct representing swap item, used for listing and in internal calculations
      struct Swap {
          /// @notice Swap's unique ID
          uint256 id;
          /// @notice Swap's buyer
          address buyer;
          /// @notice Swap opening epoch timestamp
          uint256 openTimestamp;
          /// @notice Swap's tenor
          IporTypes.SwapTenor tenor;
          /// @notice Swap's direction
          AmmTypes.SwapDirection direction;
          /// @notice Index position of this Swap in an array of swaps' identification associated to swap buyer
          /// @dev Field used for gas optimization purposes, it allows for quick removal by id in the array.
          /// During removal the last item in the array is switched with the one that just has been removed.
          uint256 idsIndex;
          /// @notice Swap's collateral
          /// @dev value represented in 18 decimals
          uint256 collateral;
          /// @notice Swap's notional amount
          /// @dev value represented in 18 decimals
          uint256 notional;
          /// @notice Swap's notional amount denominated in the Interest Bearing Token (IBT)
          /// @dev value represented in 18 decimals
          uint256 ibtQuantity;
          /// @notice Fixed interest rate at which the position has been opened
          /// @dev value represented in 18 decimals
          uint256 fixedInterestRate;
          /// @notice Liquidation deposit amount
          /// @dev value represented in 18 decimals
          uint256 wadLiquidationDepositAmount;
          /// @notice State of the swap
          /// @dev 0 - INACTIVE, 1 - ACTIVE
          IporTypes.SwapState state;
      }
  
      /// @notice Structure representing configuration of the AmmOpenSwapServicePool for specific asset (pool).
      struct AmmOpenSwapServicePoolConfiguration {
          /// @notice address of the asset
          address asset;
          /// @notice asset decimals
          uint256 decimals;
          /// @notice address of the AMM Storage
          address ammStorage;
          /// @notice address of the AMM Treasury
          address ammTreasury;
          /// @notice spread contract address
          address spread;
          /// @notice ipor publication fee, fee used when opening swap, represented in 18 decimals.
          uint256 iporPublicationFee;
          /// @notice maximum swap collateral amount, represented in 18 decimals.
          uint256 maxSwapCollateralAmount;
          /// @notice liquidation deposit amount, represented with 6 decimals. Example 25000000 = 25 units = 25.000000, 1000 = 0.001
          uint256 liquidationDepositAmount;
          /// @notice minimum leverage, represented in 18 decimals.
          uint256 minLeverage;
          /// @notice swap's opening fee rate, represented in 18 decimals. 1e18 = 100%
          uint256 openingFeeRate;
          /// @notice swap's opening fee rate, portion of the rate which is allocated to "treasury" balance
          /// @dev Value describes what percentage of opening fee amount is allocated to "treasury" balance. Value represented in 18 decimals. 1e18 = 100%
          uint256 openingFeeTreasuryPortionRate;
      }
  
      /// @notice Technical structure with unwinding parameters.
      struct UnwindParams {
          address asset;
          /// @notice Risk Indicators Inputs signer
          address messageSigner;
          address spread;
          address ammStorage;
          address ammTreasury;
          /// @notice Moment when the swap is closing
          uint256 closeTimestamp;
          /// @notice Swap's PnL value to moment when the swap is closing
          int256 swapPnlValueToDate;
          /// @notice Actual IPOR index value
          uint256 indexValue;
          /// @notice Swap data
          AmmTypesBaseV1.Swap swap;
          uint256 unwindingFeeRate;
          uint256 unwindingFeeTreasuryPortionRate;
          /// @notice Risk indicators for both legs pay fixed and receive fixed
          AmmTypes.CloseSwapRiskIndicatorsInput riskIndicatorsInputs;
      }
  
      struct BeforeOpenSwapStruct {
          /// @notice Amount of entered asset that is sent from buyer to AmmTreasury when opening swap.
          /// @dev Notice! Input Asset can be different than the asset that is used as a collateral. Value represented in decimals of input asset.
          uint256 inputAssetTotalAmount;
          /// @notice Amount of entered asset that is sent from buyer to AmmTreasury when opening swap.
          /// @dev Notice! Input Asset can be different than the asset that is used as a collateral. Value represented in 18 decimals.
          uint256 wadInputAssetTotalAmount;
          /// @notice Amount of underlying asset that is used as a collateral and other costs related to swap opening.
          /// @dev The amount is represented in decimals of the asset.
          uint256 assetTotalAmount;
          /// @notice Amount of underlying asset that is used as a collateral and other costs related to swap opening.
          /// @dev The amount is represented in 18 decimals regardless of the decimals of the asset.
          uint256 wadAssetTotalAmount;
          /// @notice Swap's collateral.
          uint256 collateral;
          /// @notice Swap's notional amount.
          uint256 notional;
          /// @notice The part of the opening fee that will be added to the liquidity pool balance.
          uint256 openingFeeLPAmount;
          /// @notice Part of the opening fee that will be added to the treasury balance.
          uint256 openingFeeTreasuryAmount;
          /// @notice Amount of asset set aside for the oracle subsidization.
          uint256 iporPublicationFeeAmount;
          /// @notice Refundable deposit blocked for the entity that will close the swap.
          /// For more information on how the liquidations work refer to the documentation.
          /// https://ipor-labs.gitbook.io/ipor-labs/automated-market-maker/liquidations
          /// @dev value represented without decimals for USDT, USDC, DAI, with 6 decimals for stETH, as an integer.
          uint256 liquidationDepositAmount;
          /// @notice The struct describing the IPOR and its params calculated for the time when it was most recently updated and the change that took place since the update.
          /// Namely, the interest that would be computed into IBT should the rebalance occur.
          IporTypes.AccruedIpor accruedIpor;
      }
  
      struct ClosableSwapInput {
          address account;
          address asset;
          uint256 closeTimestamp;
          address swapBuyer;
          uint256 swapOpenTimestamp;
          uint256 swapCollateral;
          IporTypes.SwapTenor swapTenor;
          IporTypes.SwapState swapState;
          int256 swapPnlValueToDate;
          uint256 minLiquidationThresholdToCloseBeforeMaturityByCommunity;
          uint256 minLiquidationThresholdToCloseBeforeMaturityByBuyer;
          uint256 timeBeforeMaturityAllowedToCloseSwapByCommunity;
          uint256 timeBeforeMaturityAllowedToCloseSwapByBuyer;
          uint256 timeAfterOpenAllowedToCloseSwapWithUnwinding;
      }
  
      /// @notice Struct representing amounts related to Swap that is presently being opened.
      /// @dev all values represented in 18 decimals
      struct OpenSwapAmount {
          /// @notice Amount of entered asset that is sent from buyer to AmmTreasury when opening swap.
          /// @dev Notice. Input Asset can be different than the asset that is used as a collateral. Represented in 18 decimals.
          uint256 inputAssetTotalAmount;
          /// @notice Total Amount of underlying asset that is used as a collateral.
          uint256 assetTotalAmount;
          /// @notice Swap's collateral, represented in underlying asset, represented in 18 decimals.
          uint256 collateral;
          /// @notice Swap's notional, represented in underlying asset, represented in 18 decimals.
          uint256 notional;
          /// @notice Opening Fee - part allocated as a profit of the Liquidity Pool, represented in underlying asset, represented in 18 decimals.
          uint256 openingFeeLPAmount;
          /// @notice  Part of the fee set aside for subsidizing the oracle that publishes IPOR rate. Flat fee set by the DAO. Represented in underlying asset, represented in 18 decimals.
          /// @notice Opening Fee - part allocated in Treasury balance. Part of the fee set asside for subsidising the oracle that publishes IPOR rate. Flat fee set by the DAO.
          uint256 openingFeeTreasuryAmount;
          /// @notice Fee set aside for subsidizing the oracle that publishes IPOR rate. Flat fee set by the DAO. Represented in underlying asset, represented in 18 decimals.
          uint256 iporPublicationFee;
          /// @notice Liquidation deposit is retained when the swap is opened. Notice! Value represented in 18 decimals. Represents in underlying asset, represented in 18 decimals.
          uint256 liquidationDepositAmount;
      }
  
      struct AmmBalanceForOpenSwap {
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Fixed & Receive Floating leg.
          uint256 totalCollateralPayFixed;
          /// @notice Total notional amount of all swaps on  Pay Fixed leg (denominated in 18 decimals).
          uint256 totalNotionalPayFixed;
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Floating & Receive Fixed leg.
          uint256 totalCollateralReceiveFixed;
          /// @notice Total notional amount of all swaps on  Receive Fixed leg (denominated in 18 decimals).
          uint256 totalNotionalReceiveFixed;
      }
  
      struct Balance {
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Fixed & Receive Floating leg.
          uint256 totalCollateralPayFixed;
          /// @notice Sum of all collateral put forward by the derivative buyer's on  Pay Floating & Receive Fixed leg.
          uint256 totalCollateralReceiveFixed;
          /// @notice This balance is used to track the funds accounted for IporOracle subsidization.
          uint256 iporPublicationFee;
          /// @notice Treasury is the balance that belongs to IPOR DAO and funds up to this amount can be transferred to the DAO-appointed multi-sig wallet.
          /// this ballance is fed by part of the opening fee appointed by the DAO. For more information refer to the documentation:
          /// https://ipor-labs.gitbook.io/ipor-labs/automated-market-maker/ipor-swaps#fees
          uint256 treasury;
          /// @notice Sum of all liquidation deposits for all opened swaps.
          /// @dev Value represented in 18 decimals.
          uint256 totalLiquidationDepositBalance;
      }
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
  
  /**
   * @dev Contract module which provides a basic access control mechanism, where
   * there is an account (an owner) that can be granted exclusive access to
   * specific functions.
   *
   * By default, the owner account will be the one that deploys the contract. This
   * can later be changed with {transferOwnership}.
   *
   * This module is used through inheritance. It will make available the modifier
   * `onlyOwner`, which can be applied to your functions to restrict their use to
   * the owner.
   */
  abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
      address private _owner;
  
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  
      /**
       * @dev Initializes the contract setting the deployer as the initial owner.
       */
      function __Ownable_init() internal onlyInitializing {
          __Ownable_init_unchained();
      }
  
      function __Ownable_init_unchained() internal onlyInitializing {
          _transferOwnership(_msgSender());
      }
  
      /**
       * @dev Throws if called by any account other than the owner.
       */
      modifier onlyOwner() {
          _checkOwner();
          _;
      }
  
      /**
       * @dev Returns the address of the current owner.
       */
      function owner() public view virtual returns (address) {
          return _owner;
      }
  
      /**
       * @dev Throws if the sender is not the owner.
       */
      function _checkOwner() internal view virtual {
          require(owner() == _msgSender(), "Ownable: caller is not the owner");
      }
  
      /**
       * @dev Leaves the contract without owner. It will not be possible to call
       * `onlyOwner` functions. Can only be called by the current owner.
       *
       * NOTE: Renouncing ownership will leave the contract without an owner,
       * thereby disabling any functionality that is only available to the owner.
       */
      function renounceOwnership() public virtual onlyOwner {
          _transferOwnership(address(0));
      }
  
      /**
       * @dev Transfers ownership of the contract to a new account (`newOwner`).
       * Can only be called by the current owner.
       */
      function transferOwnership(address newOwner) public virtual onlyOwner {
          require(newOwner != address(0), "Ownable: new owner is the zero address");
          _transferOwnership(newOwner);
      }
  
      /**
       * @dev Transfers ownership of the contract to a new account (`newOwner`).
       * Internal function without access restriction.
       */
      function _transferOwnership(address newOwner) internal virtual {
          address oldOwner = _owner;
          _owner = newOwner;
          emit OwnershipTransferred(oldOwner, newOwner);
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[49] private __gap;
  }
  
  // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
  
  /**
   * @dev Contract module which allows children to implement an emergency stop
   * mechanism that can be triggered by an authorized account.
   *
   * This module is used through inheritance. It will make available the
   * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
   * the functions of your contract. Note that they will not be pausable by
   * simply including this module, only once the modifiers are put in place.
   */
  abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
      /**
       * @dev Emitted when the pause is triggered by `account`.
       */
      event Paused(address account);
  
      /**
       * @dev Emitted when the pause is lifted by `account`.
       */
      event Unpaused(address account);
  
      bool private _paused;
  
      /**
       * @dev Initializes the contract in unpaused state.
       */
      function __Pausable_init() internal onlyInitializing {
          __Pausable_init_unchained();
      }
  
      function __Pausable_init_unchained() internal onlyInitializing {
          _paused = false;
      }
  
      /**
       * @dev Modifier to make a function callable only when the contract is not paused.
       *
       * Requirements:
       *
       * - The contract must not be paused.
       */
      modifier whenNotPaused() {
          _requireNotPaused();
          _;
      }
  
      /**
       * @dev Modifier to make a function callable only when the contract is paused.
       *
       * Requirements:
       *
       * - The contract must be paused.
       */
      modifier whenPaused() {
          _requirePaused();
          _;
      }
  
      /**
       * @dev Returns true if the contract is paused, and false otherwise.
       */
      function paused() public view virtual returns (bool) {
          return _paused;
      }
  
      /**
       * @dev Throws if the contract is paused.
       */
      function _requireNotPaused() internal view virtual {
          require(!paused(), "Pausable: paused");
      }
  
      /**
       * @dev Throws if the contract is not paused.
       */
      function _requirePaused() internal view virtual {
          require(paused(), "Pausable: not paused");
      }
  
      /**
       * @dev Triggers stopped state.
       *
       * Requirements:
       *
       * - The contract must not be paused.
       */
      function _pause() internal virtual whenNotPaused {
          _paused = true;
          emit Paused(_msgSender());
      }
  
      /**
       * @dev Returns to normal state.
       *
       * Requirements:
       *
       * - The contract must be paused.
       */
      function _unpause() internal virtual whenPaused {
          _paused = false;
          emit Unpaused(_msgSender());
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[49] private __gap;
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
  
  /**
   * @title SafeERC20
   * @dev Wrappers around ERC20 operations that throw on failure (when the token
   * contract returns false). Tokens that return no value (and instead revert or
   * throw on failure) are also supported, non-reverting calls are assumed to be
   * successful.
   * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
   * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
   */
  library SafeERC20Upgradeable {
      using AddressUpgradeable for address;
  
      /**
       * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
       * non-reverting calls are assumed to be successful.
       */
      function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
          _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
      }
  
      /**
       * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
       * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
       */
      function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
          _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
      }
  
      /**
       * @dev Deprecated. This function has issues similar to the ones found in
       * {IERC20-approve}, and its usage is discouraged.
       *
       * Whenever possible, use {safeIncreaseAllowance} and
       * {safeDecreaseAllowance} instead.
       */
      function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
          // safeApprove should only be called when setting an initial allowance,
          // or when resetting it to zero. To increase and decrease it, use
          // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
          require(
              (value == 0) || (token.allowance(address(this), spender) == 0),
              "SafeERC20: approve from non-zero to non-zero allowance"
          );
          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
      }
  
      /**
       * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
       * non-reverting calls are assumed to be successful.
       */
      function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
          uint256 oldAllowance = token.allowance(address(this), spender);
          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
      }
  
      /**
       * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
       * non-reverting calls are assumed to be successful.
       */
      function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
          unchecked {
              uint256 oldAllowance = token.allowance(address(this), spender);
              require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
          }
      }
  
      /**
       * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
       * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
       * 0 before setting it to a non-zero value.
       */
      function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
          bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
  
          if (!_callOptionalReturnBool(token, approvalCall)) {
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
              _callOptionalReturn(token, approvalCall);
          }
      }
  
      /**
       * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
       * Revert on invalid signature.
       */
      function safePermit(
          IERC20PermitUpgradeable token,
          address owner,
          address spender,
          uint256 value,
          uint256 deadline,
          uint8 v,
          bytes32 r,
          bytes32 s
      ) internal {
          uint256 nonceBefore = token.nonces(owner);
          token.permit(owner, spender, value, deadline, v, r, s);
          uint256 nonceAfter = token.nonces(owner);
          require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
      }
  
      /**
       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
       * on the return value: the return value is optional (but if data is returned, it must not be false).
       * @param token The token targeted by the call.
       * @param data The call data (encoded using abi.encode or one of its variants).
       */
      function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
          // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
          // the target address contains contract code and also asserts for success in the low-level call.
  
          bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
          require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
      }
  
      /**
       * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
       * on the return value: the return value is optional (but if data is returned, it must not be false).
       * @param token The token targeted by the call.
       * @param data The call data (encoded using abi.encode or one of its variants).
       *
       * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
       */
      function _callOptionalReturnBool(IERC20Upgradeable token, bytes memory data) private returns (bool) {
          // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
          // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
          // and not revert is the subcall reverts.
  
          (bool success, bytes memory returndata) = address(token).call(data);
          return
              success && (returndata.length == 0 || abi.decode(returndata, (bool))) && AddressUpgradeable.isContract(address(token));
      }
  }
  
  /// @title Extended version of OpenZeppelin OwnableUpgradeable contract with appointed owner
  abstract contract IporOwnableUpgradeable is OwnableUpgradeable {
      address private _appointedOwner;
  
      /// @notice Emitted when account is appointed to transfer ownership
      /// @param appointedOwner Address of appointed owner
      event AppointedToTransferOwnership(address indexed appointedOwner);
  
      modifier onlyAppointedOwner() {
          require(_appointedOwner == msg.sender, IporErrors.SENDER_NOT_APPOINTED_OWNER);
          _;
      }
  
      /// @notice Oppoint account to transfer ownership
      /// @param appointedOwner Address of appointed owner
      function transferOwnership(address appointedOwner) public override onlyOwner {
          require(appointedOwner != address(0), IporErrors.WRONG_ADDRESS);
          _appointedOwner = appointedOwner;
          emit AppointedToTransferOwnership(appointedOwner);
      }
  
      /// @notice Confirm transfer ownership
      /// @dev This is real transfer ownership in second step by appointed account
      function confirmTransferOwnership() external onlyAppointedOwner {
          _appointedOwner = address(0);
          _transferOwnership(msg.sender);
      }
  
      /// @notice Renounce ownership
      function renounceOwnership() public virtual override onlyOwner {
          _transferOwnership(address(0));
          _appointedOwner = address(0);
      }
  }
  
  /// @title Interface for interaction with the IPOR AMM Storage, contract responsible for managing AMM storage.
  interface IAmmStorageBaseV1 {
      /// @notice Returns the current version of AmmTreasury Storage
      /// @dev Increase number when the implementation inside source code is different that the implementation deployed on the Mainnet
      /// @return current AmmTreasury Storage version, integer
      function getVersion() external pure returns (uint256);
  
      /// @notice Gets last swap ID.
      /// @dev swap ID is incremented when new position is opened, last swap ID is used in Pay Fixed and Receive Fixed swaps.
      /// @dev ID is global for all swaps, regardless if they are Pay Fixed or Receive Fixed in tenor 28, 60 or 90 days.
      /// @return last swap ID, integer
      function getLastSwapId() external view returns (uint256);
  
      /// @notice Gets the last opened swap for a given tenor and direction.
      /// @param tenor tenor of the swap
      /// @param direction direction of the swap: 0 for Pay Fixed, 1 for Receive Fixed
      /// @return last opened swap {AmmInternalTypes.OpenSwapItem}
      function getLastOpenedSwap(
          IporTypes.SwapTenor tenor,
          uint256 direction
      ) external view returns (AmmInternalTypes.OpenSwapItem memory);
  
      /// @notice Gets the AMM balance struct
      /// @dev Balance contains:
      /// # Pay Fixed Total Collateral
      /// # Receive Fixed Total Collateral
      /// # Liquidity Pool and Vault balances.
      /// All balances are represented in 18 decimals.
      /// @return balance structure {AmmTypesBaseV1.Balance}
      function getBalance() external view returns (AmmTypesBaseV1.Balance memory);
  
      /// @notice Gets the balance for open swap
      /// @dev Balance contains:
      /// # Pay Fixed Total Collateral
      /// # Receive Fixed Total Collateral
      /// # Liquidity Pool balance
      /// # Total Notional Pay Fixed
      /// # Total Notional Receive Fixed
      /// @return balance structure {AmmTypesBaseV1.AmmBalanceForOpenSwap}
      function getBalancesForOpenSwap() external view returns (AmmTypesBaseV1.AmmBalanceForOpenSwap memory);
  
      /// @notice gets the SOAP indicators.
      /// @dev SOAP is a Sum Of All Payouts, aka undealised PnL.
      /// @return indicatorsPayFixed structure {AmmStorageTypes.SoapIndicators} indicators for Pay Fixed swaps
      /// @return indicatorsReceiveFixed structure {AmmStorageTypes.SoapIndicators} indicators for Receive Fixed swaps
      function getSoapIndicators()
          external
          view
          returns (
              AmmStorageTypes.SoapIndicators memory indicatorsPayFixed,
              AmmStorageTypes.SoapIndicators memory indicatorsReceiveFixed
          );
  
      /// @notice Gets swap based on the direction and swap ID.
      /// @param direction direction of the swap: 0 for Pay Fixed, 1 for Receive Fixed
      /// @param swapId swap ID
      /// @return swap structure {AmmTypesBaseV1.sol.Swap}
      function getSwap(
          AmmTypes.SwapDirection direction,
          uint256 swapId
      ) external view returns (AmmTypesBaseV1.Swap memory);
  
      /// @notice Gets the active Pay-Fixed swaps for a given account address.
      /// @param account account address
      /// @param offset offset for paging
      /// @param chunkSize page size for paging
      /// @return totalCount total number of active Pay-Fixed swaps
      /// @return swaps array where each element has structure {AmmTypesBaseV1.sol.Swap}
      function getSwapsPayFixed(
          address account,
          uint256 offset,
          uint256 chunkSize
      ) external view returns (uint256 totalCount, AmmTypesBaseV1.Swap[] memory swaps);
  
      /// @notice Gets the active Receive-Fixed swaps for a given account address.
      /// @param account account address
      /// @param offset offset for paging
      /// @param chunkSize page size for paging
      /// @return totalCount total number of active Receive Fixed swaps
      /// @return swaps array where each element has structure {AmmTypesBaseV1.sol.Swap}
      function getSwapsReceiveFixed(
          address account,
          uint256 offset,
          uint256 chunkSize
      ) external view returns (uint256 totalCount, AmmTypesBaseV1.Swap[] memory swaps);
  
      /// @notice Gets the active Pay-Fixed and Receive-Fixed swaps IDs for a given account address.
      /// @param account account address
      /// @param offset offset for paging
      /// @param chunkSize page size for paging
      /// @return totalCount total number of active Pay-Fixed and Receive-Fixed IDs.
      /// @return ids array where each element has structure {AmmStorageTypes.IporSwapId}
      function getSwapIds(
          address account,
          uint256 offset,
          uint256 chunkSize
      ) external view returns (uint256 totalCount, AmmStorageTypes.IporSwapId[] memory ids);
  
      /// @notice Updates structures in storage: balance, swaps, SOAP indicators when new Pay-Fixed swap is opened.
      /// @dev Function is only available to AmmOpenSwapService, it can be executed only by IPOR Protocol Router as internal interaction.
      /// @param newSwap new swap structure {AmmTypesBaseV1.sol.NewSwap}
      /// @param cfgIporPublicationFee publication fee amount taken from AmmTreasury configuration, represented in 18 decimals.
      /// @return new swap ID
      function updateStorageWhenOpenSwapPayFixedInternal(
          AmmTypes.NewSwap memory newSwap,
          uint256 cfgIporPublicationFee
      ) external returns (uint256);
  
      /// @notice Updates structures in the storage: balance, swaps, SOAP indicators when new Receive-Fixed swap is opened.
      /// @dev Function is only available to AmmOpenSwapService, it can be executed only by IPOR Protocol Router as internal interaction.
      /// @param newSwap new swap structure {AmmTypesBaseV1.sol.NewSwap}
      /// @param cfgIporPublicationFee publication fee amount taken from AmmTreasury configuration, represented in 18 decimals.
      /// @return new swap ID
      function updateStorageWhenOpenSwapReceiveFixedInternal(
          AmmTypes.NewSwap memory newSwap,
          uint256 cfgIporPublicationFee
      ) external returns (uint256);
  
      /// @notice Updates structures in the storage: balance, swaps, SOAP indicators when closing Pay-Fixed swap.
      /// @dev Function is only available to AmmCloseSwapService, it can be executed only by IPOR Protocol Router as internal interaction.
      /// @param swap The swap structure containing IPOR swap information.
      /// @param pnlValue The amount that the trader has earned or lost on the swap, represented in 18 decimals.
      /// pnValue can be negative, pnlValue NOT INCLUDE potential unwind fee.
      /// @param swapUnwindFeeLPAmount unwind fee which is accounted on AMM Liquidity Pool balance.
      /// @param swapUnwindFeeTreasuryAmount unwind fee which is accounted on AMM Treasury balance.
      /// @param closingTimestamp The moment when the swap was closed.
      /// @return closedSwap A memory struct representing the closed swap.
      function updateStorageWhenCloseSwapPayFixedInternal(
          AmmTypesBaseV1.Swap memory swap,
          int256 pnlValue,
          uint256 swapUnwindFeeLPAmount,
          uint256 swapUnwindFeeTreasuryAmount,
          uint256 closingTimestamp
      ) external returns (AmmInternalTypes.OpenSwapItem memory closedSwap);
  
      /// @notice Updates structures in the storage: swaps, balances, SOAP indicators when closing Receive-Fixed swap.
      /// @dev Function is only available to AmmCloseSwapService, it can be executed only by IPOR Protocol Router as internal interaction.
      /// @param swap The swap structure containing IPOR swap information.
      /// @param pnlValue The amount that the trader has earned or lost on the swap, represented in 18 decimals.
      /// pnValue can be negative, pnlValue NOT INCLUDE potential unwind fee.
      /// @param swapUnwindFeeLPAmount unwind fee which is accounted on AMM Liquidity Pool balance.
      /// @param swapUnwindFeeTreasuryAmount unwind fee which is accounted on AMM Treasury balance.
      /// @param closingTimestamp The moment when the swap was closed.
      /// @return closedSwap A memory struct representing the closed swap.
      function updateStorageWhenCloseSwapReceiveFixedInternal(
          AmmTypesBaseV1.Swap memory swap,
          int256 pnlValue,
          uint256 swapUnwindFeeLPAmount,
          uint256 swapUnwindFeeTreasuryAmount,
          uint256 closingTimestamp
      ) external returns (AmmInternalTypes.OpenSwapItem memory closedSwap);
  
      /// @notice Updates the balance when AmmPoolsService transfers AmmTreasury's assets to Oracle Treasury's multisig wallet.
      /// @dev Function is only available to the AmmGovernanceService, can be executed only by IPOR Protocol Router as internal interaction.
      /// @param transferredAmount asset amount transferred to Charlie Treasury multisig wallet.
      function updateStorageWhenTransferToCharlieTreasuryInternal(uint256 transferredAmount) external;
  
      /// @notice Updates the balance when AmmPoolsService transfers AmmTreasury's assets to Treasury's multisig wallet.
      /// @dev Function is only available to the AmmGovernanceService, can be executed only by IPOR Protocol Router as internal interaction.
      /// @param transferredAmount asset amount transferred to Treasury's multisig wallet.
      function updateStorageWhenTransferToTreasuryInternal(uint256 transferredAmount) external;
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
  
  /**
   * @dev This abstract contract provides getters and event emitting update functions for
   * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
   *
   * _Available since v4.1._
   */
  abstract contract ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
      function __ERC1967Upgrade_init() internal onlyInitializing {
      }
  
      function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
      }
      // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
      bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
  
      /**
       * @dev Storage slot with the address of the current implementation.
       * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
       * validated in the constructor.
       */
      bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
  
      /**
       * @dev Returns the current implementation address.
       */
      function _getImplementation() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
      }
  
      /**
       * @dev Stores a new address in the EIP1967 implementation slot.
       */
      function _setImplementation(address newImplementation) private {
          require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
          StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
      }
  
      /**
       * @dev Perform implementation upgrade
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeTo(address newImplementation) internal {
          _setImplementation(newImplementation);
          emit Upgraded(newImplementation);
      }
  
      /**
       * @dev Perform implementation upgrade with additional setup call.
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
          _upgradeTo(newImplementation);
          if (data.length > 0 || forceCall) {
              AddressUpgradeable.functionDelegateCall(newImplementation, data);
          }
      }
  
      /**
       * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
       *
       * Emits an {Upgraded} event.
       */
      function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
          // Upgrades from old implementations will perform a rollback test. This test requires the new
          // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
          // this special case will break upgrade paths from old UUPS implementation to new ones.
          if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
              _setImplementation(newImplementation);
          } else {
              try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                  require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
              } catch {
                  revert("ERC1967Upgrade: new implementation is not UUPS");
              }
              _upgradeToAndCall(newImplementation, data, forceCall);
          }
      }
  
      /**
       * @dev Storage slot with the admin of the contract.
       * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
       * validated in the constructor.
       */
      bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
  
      /**
       * @dev Returns the current admin.
       */
      function _getAdmin() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
      }
  
      /**
       * @dev Stores a new address in the EIP1967 admin slot.
       */
      function _setAdmin(address newAdmin) private {
          require(newAdmin != address(0), "ERC1967: new admin is the zero address");
          StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
      }
  
      /**
       * @dev Changes the admin of the proxy.
       *
       * Emits an {AdminChanged} event.
       */
      function _changeAdmin(address newAdmin) internal {
          emit AdminChanged(_getAdmin(), newAdmin);
          _setAdmin(newAdmin);
      }
  
      /**
       * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
       * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
       */
      bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
  
      /**
       * @dev Returns the current beacon.
       */
      function _getBeacon() internal view returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
      }
  
      /**
       * @dev Stores a new beacon in the EIP1967 beacon slot.
       */
      function _setBeacon(address newBeacon) private {
          require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
          require(
              AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
              "ERC1967: beacon implementation is not a contract"
          );
          StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
      }
  
      /**
       * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
       * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
       *
       * Emits a {BeaconUpgraded} event.
       */
      function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
          _setBeacon(newBeacon);
          emit BeaconUpgraded(newBeacon);
          if (data.length > 0 || forceCall) {
              AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
          }
      }
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
  
  /**
   * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
   * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
   *
   * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
   * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
   * `UUPSUpgradeable` with a custom implementation of upgrades.
   *
   * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
   *
   * _Available since v4.1._
   */
  abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
      function __UUPSUpgradeable_init() internal onlyInitializing {
      }
  
      function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
      }
      /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
      address private immutable __self = address(this);
  
      /**
       * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
       * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
       * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
       * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
       * fail.
       */
      modifier onlyProxy() {
          require(address(this) != __self, "Function must be called through delegatecall");
          require(_getImplementation() == __self, "Function must be called through active proxy");
          _;
      }
  
      /**
       * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
       * callable on the implementing contract but not through proxies.
       */
      modifier notDelegated() {
          require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
          _;
      }
  
      /**
       * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
       * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
       *
       * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
       * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
       * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
       */
      function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
          return _IMPLEMENTATION_SLOT;
      }
  
      /**
       * @dev Upgrade the implementation of the proxy to `newImplementation`.
       *
       * Calls {_authorizeUpgrade}.
       *
       * Emits an {Upgraded} event.
       *
       * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
       */
      function upgradeTo(address newImplementation) public virtual onlyProxy {
          _authorizeUpgrade(newImplementation);
          _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
      }
  
      /**
       * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
       * encoded in `data`.
       *
       * Calls {_authorizeUpgrade}.
       *
       * Emits an {Upgraded} event.
       *
       * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
       */
      function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
          _authorizeUpgrade(newImplementation);
          _upgradeToAndCallUUPS(newImplementation, data, true);
      }
  
      /**
       * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
       * {upgradeTo} and {upgradeToAndCall}.
       *
       * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
       *
       * ```solidity
       * function _authorizeUpgrade(address) internal override onlyOwner {}
       * ```
       */
      function _authorizeUpgrade(address newImplementation) internal virtual;
  
      /**
       * @dev This empty reserved space is put in place to allow future versions to add new
       * variables without shifting down storage in the inheritance chain.
       * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
       */
      uint256[50] private __gap;
  }
  
  contract AmmTreasuryBaseV1 is
      Initializable,
      PausableUpgradeable,
      ReentrancyGuardUpgradeable,
      UUPSUpgradeable,
      IporOwnableUpgradeable,
      IAmmTreasuryBaseV1,
      IProxyImplementation
  {
      using SafeCast for uint256;
      using SafeCast for int256;
      using IporContractValidator for address;
      using SafeERC20Upgradeable for IERC20Upgradeable;
  
      address public immutable asset;
      address public immutable router;
      address public immutable ammStorage;
  
      modifier onlyPauseGuardian() {
          if (!PauseManager.isPauseGuardian(msg.sender)) {
              revert IporErrors.CallerNotPauseGuardian(IporErrors.CALLER_NOT_PAUSE_GUARDIAN, msg.sender);
          }
          _;
      }
  
      constructor(address assetInput, address routerInput, address ammStorageInput) {
          asset = assetInput.checkAddress();
          router = routerInput.checkAddress();
          ammStorage = ammStorageInput.checkAddress();
  
          _disableInitializers();
      }
  
      function initialize(bool paused) public initializer {
          __Pausable_init();
          __Ownable_init();
          __UUPSUpgradeable_init();
  
          if (paused) {
              _pause();
          } else {
              IERC20Upgradeable(asset).forceApprove(router, Constants.MAX_VALUE);
          }
      }
  
      function getLiquidityPoolBalance() external view override returns (uint256) {
          AmmTypesBaseV1.Balance memory balance = IAmmStorageBaseV1(ammStorage).getBalance();
  
          uint256 liquidityPool = (IERC20Upgradeable(asset).balanceOf(address(this)).toInt256() -
              balance.totalCollateralPayFixed.toInt256() -
              balance.totalCollateralReceiveFixed.toInt256() -
              balance.iporPublicationFee.toInt256() -
              balance.treasury.toInt256() -
              balance.totalLiquidationDepositBalance.toInt256()).toUint256();
  
          return liquidityPool;
      }
  
      function getVersion() external pure returns (uint256) {
          return 2_001;
      }
  
      function pause() external override onlyPauseGuardian {
          IERC20Upgradeable(asset).safeApprove(router, 0);
          _pause();
      }
  
      function unpause() external override onlyOwner {
          _unpause();
          IERC20Upgradeable(asset).forceApprove(router, Constants.MAX_VALUE);
      }
  
      function isPauseGuardian(address account) external view override returns (bool) {
          return PauseManager.isPauseGuardian(account);
      }
  
      function addPauseGuardians(address[] calldata guardians) external override onlyOwner {
          PauseManager.addPauseGuardians(guardians);
      }
  
      function removePauseGuardians(address[] calldata guardians) external override onlyOwner {
          PauseManager.removePauseGuardians(guardians);
      }
  
      function getImplementation() external view override returns (address) {
          return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
      }
  
      /**
       * @notice Function run at the time of the contract upgrade via proxy. Available only to the contract's owner.
       **/
      //solhint-disable no-empty-blocks
      function _authorizeUpgrade(address) internal override onlyOwner {}
  }
  