HYPE Price: $37.63 (-2.50%)

Contract

0x97509c65Ff29C268F0D283A41201Be6b4090354c

Overview

HYPE Balance

HyperEvm LogoHyperEvm LogoHyperEvm Logo0 HYPE

HYPE Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 22 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
74878762025-07-04 6:49:003 days ago1751611740
0x97509c65...b4090354c
 Contract Creation0 HYPE
73190282025-07-02 8:41:005 days ago1751445660
0x97509c65...b4090354c
 Contract Creation0 HYPE
71465832025-06-30 9:34:007 days ago1751276040
0x97509c65...b4090354c
 Contract Creation0 HYPE
65267542025-06-23 8:03:0014 days ago1750665780
0x97509c65...b4090354c
 Contract Creation0 HYPE
59302302025-06-16 13:00:0021 days ago1750078800
0x97509c65...b4090354c
 Contract Creation0 HYPE
52961712025-06-09 7:43:0028 days ago1749454980
0x97509c65...b4090354c
 Contract Creation0 HYPE
42069722025-05-23 9:54:0045 days ago1747994040
0x97509c65...b4090354c
 Contract Creation0 HYPE
42067242025-05-23 9:46:0045 days ago1747993560
0x97509c65...b4090354c
 Contract Creation0 HYPE
40312022025-05-19 11:24:0049 days ago1747653840
0x97509c65...b4090354c
 Contract Creation0 HYPE
37944872025-05-14 4:06:0054 days ago1747195560
0x97509c65...b4090354c
 Contract Creation0 HYPE
33946262025-05-05 5:05:0063 days ago1746421500
0x97509c65...b4090354c
 Contract Creation0 HYPE
32730172025-05-02 11:40:0066 days ago1746186000
0x97509c65...b4090354c
 Contract Creation0 HYPE
25186052025-04-15 13:58:0083 days ago1744725480
0x97509c65...b4090354c
 Contract Creation0 HYPE
17035922025-03-28 7:42:00101 days ago1743147720
0x97509c65...b4090354c
 Contract Creation0 HYPE
11647362025-03-16 5:53:00113 days ago1742104380
0x97509c65...b4090354c
 Contract Creation0 HYPE
10284912025-03-13 4:38:00116 days ago1741840680
0x97509c65...b4090354c
 Contract Creation0 HYPE
3729412025-02-26 12:08:00131 days ago1740571680
0x97509c65...b4090354c
 Contract Creation0 HYPE
1145062025-02-20 17:10:00137 days ago1740071400
0x97509c65...b4090354c
 Contract Creation0 HYPE
1018582025-02-20 10:22:00137 days ago1740046920
0x97509c65...b4090354c
 Contract Creation0 HYPE
953792025-02-20 6:53:00137 days ago1740034380
0x97509c65...b4090354c
 Contract Creation0 HYPE
659292025-02-19 15:03:00138 days ago1739977380
0x97509c65...b4090354c
 Contract Creation0 HYPE
545832025-02-19 8:57:00138 days ago1739955420  Contract Creation0 HYPE
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TimeswapV2PoolFactory

Compiler Version
v0.8.8+commit.dddeac2f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Ownership} from "@timeswap-labs/v2-library/contracts/Ownership.sol";

import {ITimeswapV2OptionFactory} from "@timeswap-labs/v2-option/contracts/interfaces/ITimeswapV2OptionFactory.sol";

import {OptionPairLibrary} from "@timeswap-labs/v2-option/contracts/libraries/OptionPair.sol";

import {ITimeswapV2PoolFactory} from "./interfaces/ITimeswapV2PoolFactory.sol";

import {TimeswapV2PoolDeployer} from "./TimeswapV2PoolDeployer.sol";

import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";

import {OwnableTwoSteps} from "./base/OwnableTwoSteps.sol";

/// @title Factory contract for TimeswapV2Pool
/// @author Timeswap Labs
contract TimeswapV2PoolFactory is ITimeswapV2PoolFactory, TimeswapV2PoolDeployer, OwnableTwoSteps {
  using OptionPairLibrary for address;
  using Ownership for address;

  /// @dev Revert when fee initialization is chosen to be larger than uint16.
  /// @param fee The chosen fee.
  error IncorrectFeeInitialization(uint256 fee);

  /* ===== MODEL ===== */

  /// @inheritdoc ITimeswapV2PoolFactory
  address public immutable override optionFactory;
  /// @inheritdoc ITimeswapV2PoolFactory
  uint256 public immutable override transactionFee;
  /// @inheritdoc ITimeswapV2PoolFactory
  uint256 public immutable override protocolFee;

  mapping(address => address) private pairs;

  address[] public override getByIndex;

  /* ===== INIT ===== */

  constructor(
    address chosenOwner,
    address chosenOptionFactory,
    uint256 chosenTransactionFee,
    uint256 chosenProtocolFee
  ) OwnableTwoSteps(chosenOwner) {
    if (chosenTransactionFee > type(uint16).max) revert IncorrectFeeInitialization(chosenTransactionFee);
    if (chosenProtocolFee > type(uint16).max) revert IncorrectFeeInitialization(chosenProtocolFee);

    optionFactory = chosenOptionFactory;
    transactionFee = chosenTransactionFee;
    protocolFee = chosenProtocolFee;
  }

  /* ===== VIEW ===== */

  /// @inheritdoc ITimeswapV2PoolFactory
  function get(address optionPair) external view override returns (address pair) {
    pair = pairs[optionPair];
  }

  /// @inheritdoc ITimeswapV2PoolFactory
  function get(address token0, address token1) external view override returns (address pair) {
    address optionPair = ITimeswapV2OptionFactory(optionFactory).get(token0, token1);
    pair = pairs[optionPair];
  }

  function numberOfPairs() external view override returns (uint256) {
    return getByIndex.length;
  }

  /* ===== UPDATE ===== */

  /// @inheritdoc ITimeswapV2PoolFactory
  function create(address token0, address token1) external override returns (address pair) {
    address optionPair = ITimeswapV2OptionFactory(optionFactory).get(token0, token1);
    if (optionPair == address(0)) Error.zeroAddress();

    pair = pairs[optionPair];
    if (pair != address(0)) Error.zeroAddress();

    pair = deploy(address(this), optionPair, transactionFee, protocolFee);

    pairs[optionPair] = pair;
    getByIndex.push(pair);

    emit Create(msg.sender, optionPair, pair);
  }
}

File 2 of 39 : CallbackParam.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev The parameters for the add fees callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Fees The amount of long0 position required by the pool from msg.sender.
/// @param long1Fees The amount of long1 position required by the pool from msg.sender.
/// @param shortFees The amount of short position required by the pool from msg.sender.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolAddFeesCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Fees;
  uint256 long1Fees;
  uint256 shortFees;
  bytes data;
}

/// @dev The parameters for the mint choice callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param longAmount The amount of long position in base denomination required by the pool from msg.sender.
/// @param shortAmount The amount of short position required by the pool from msg.sender.
/// @param liquidityAmount The amount of liquidity position minted.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolMintChoiceCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 longAmount;
  uint256 shortAmount;
  uint160 liquidityAmount;
  bytes data;
}

/// @dev The parameters for the mint callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Amount The amount of long0 position required by the pool from msg.sender.
/// @param long1Amount The amount of long1 position required by the pool from msg.sender.
/// @param shortAmount The amount of short position required by the pool from msg.sender.
/// @param liquidityAmount The amount of liquidity position minted.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolMintCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Amount;
  uint256 long1Amount;
  uint256 shortAmount;
  uint160 liquidityAmount;
  bytes data;
}

/// @dev The parameters for the burn choice callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Balance The amount of long0 position that can be withdrawn from the pool.
/// @param long1Balance The amount of long1 position that can be withdrawn from the pool.
/// @param longAmount The amount of long position in base denomination that will be withdrawn.
/// @param shortAmount The amount of short position that will be withdrawn.
/// @param liquidityAmount The amount of liquidity position burnt.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolBurnChoiceCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Balance;
  uint256 long1Balance;
  uint256 longAmount;
  uint256 shortAmount;
  uint160 liquidityAmount;
  bytes data;
}

/// @dev The parameters for the burn callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Amount The amount of long0 position that will be withdrawn.
/// @param long1Amount The amount of long1 position that will be withdrawn.
/// @param shortAmount The amount of short position that will be withdrawn.
/// @param liquidityAmount The amount of liquidity position burnt.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolBurnCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Amount;
  uint256 long1Amount;
  uint256 shortAmount;
  uint160 liquidityAmount;
  bytes data;
}

/// @dev The parameters for the deleverage choice callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Amount The amount of long0 position required by the pool from msg.sender.
/// @param long1Amount The amount of long1 position required by the pool from msg.sender.
/// @param shortAmount The amount of short position that will be withdrawn.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolDeleverageChoiceCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 longAmount;
  uint256 shortAmount;
  bytes data;
}

/// @dev The parameters for the deleverage callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param longAmount The amount of long position in base denomination required by the pool from msg.sender.
/// @param shortAmount The amount of short position that will be withdrawn.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolDeleverageCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Amount;
  uint256 long1Amount;
  uint256 shortAmount;
  bytes data;
}

/// @dev The parameters for the leverage choice callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Balance The amount of long0 position that can be withdrawn from the pool.
/// @param long1Balance The amount of long1 position that can be withdrawn from the pool.
/// @param longAmount The amount of long position in base denomination that will be withdrawn.
/// @param shortAmount The amount of short position required by the pool from msg.sender.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolLeverageChoiceCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Balance;
  uint256 long1Balance;
  uint256 longAmount;
  uint256 shortAmount;
  bytes data;
}

/// @dev The parameters for the leverage choice callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0Amount The amount of long0 position that can be withdrawn.
/// @param long1Amount The amount of long1 position that can be withdrawn.
/// @param shortAmount The amount of short position required by the pool from msg.sender.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolLeverageCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 long0Amount;
  uint256 long1Amount;
  uint256 shortAmount;
  bytes data;
}

/// @dev The parameters for the rebalance callback.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param isLong0ToLong1 Long0ToLong1 when true. Long1ToLong0 when false.
/// @param long0Amount When Long0ToLong1, the amount of long0 position required by the pool from msg.sender.
/// When Long1ToLong0, the amount of long0 position that can be withdrawn.
/// @param long1Amount When Long0ToLong1, the amount of long1 position that can be withdrawn.
/// When Long1ToLong0, the amount of long1 position required by the pool from msg.sender.
/// @param data The bytes of data to be sent to msg.sender.
struct TimeswapV2PoolRebalanceCallbackParam {
  uint256 strike;
  uint256 maturity;
  bool isLong0ToLong1;
  uint256 long0Amount;
  uint256 long1Amount;
  bytes data;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title A library for The Timeswap V2 Option contract Duration type
library DurationLibrary {
  error DurationOverflow(uint256 duration);

  /// @dev initialize the duration type
  /// @dev Reverts when the duration is too large.
  /// @param duration The duration in seconds which is needed to be converted to the Duration type.
  function init(uint256 duration) internal pure returns (uint96) {
    if (duration > type(uint96).max) revert DurationOverflow(duration);
    return uint96(duration);
  }
}

File 4 of 39 : DurationWeight.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {FeeCalculation} from "./FeeCalculation.sol";
import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";

/// @title library for calculating duration weight
/// @author Timeswap Labs
library DurationWeight {
  using Math for uint256;

  /// @dev update the short returned growth given the short returned growth and the short token amount.
  /// @param liquidity The liquidity of the pool.
  /// @param shortReturnedGrowth The current amount of short returned growth.
  /// @param shortAmount The amount of short withdrawn.
  /// @param newShortReturnedGrowth The newly updated short returned growth.
  function update(
    uint160 liquidity,
    uint256 shortReturnedGrowth,
    uint256 shortAmount
  ) internal pure returns (uint256 newShortReturnedGrowth) {
    newShortReturnedGrowth = shortReturnedGrowth.unsafeAdd(FeeCalculation.getFeeGrowth(shortAmount, liquidity));
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";
import {FullMath} from "@timeswap-labs/v2-library/contracts/FullMath.sol";

import {SafeCast} from "@timeswap-labs/v2-library/contracts/SafeCast.sol";

import {FeeCalculation} from "./FeeCalculation.sol";

/// @title Constant Product Library that returns the Constant Product given certain parameters
library ConstantProduct {
  using Math for uint256;
  using SafeCast for uint256;

  /// @dev Reverts when calculation overflows or underflows.
  error CalculationOverload();

  /// @dev Reverts when there is not enough time value liqudity to receive when lending.
  error NotEnoughLiquidityToLend();

  /// @dev Reverts when there is not enough principal liquidity to borrow from.
  error NotEnoughLiquidityToBorrow();

  /// @dev Returns the Long position given liquidity.
  /// @param liquidity The liquidity given.
  /// @param rate The pool's squared root Interest Rate.
  /// @param roundUp Rounds up the result when true. Rounds down the result when false.
  function getLong(uint160 liquidity, uint160 rate, bool roundUp) internal pure returns (uint256) {
    return (uint256(liquidity) << 96).div(rate, roundUp);
  }

  /// @dev Returns the Short position given liquidity.
  /// @param liquidity The liquidity given.
  /// @param rate The pool's squared root Interest Rate.
  /// @param duration The time duration in seconds.
  /// @param roundUp Rounds up the result when true. Rounds down the result when false.
  function getShort(uint160 liquidity, uint160 rate, uint96 duration, bool roundUp) internal pure returns (uint256) {
    return FullMath.mulDiv(uint256(liquidity).unsafeMul(duration), uint256(rate), uint256(1) << 192, roundUp);
  }

  /// @dev Calculate the amount of long positions in base denomination and short positions from change of liquidity.
  /// @param rate The pool's squared root Interest Rate.
  /// @param deltaLiquidity The change in liquidity amount.
  /// @param duration The time duration in seconds.
  /// @param isAdd Increase liquidity amount if true. Decrease liquidity amount if false.
  /// @return longAmount The amount of long positions in base denomination to deposit when increasing liquidity.
  /// The amount of long positions in base denomination to withdraw when decreasing liquidity.
  /// @return shortAmount The amount of short positions to deposit when increasing liquidity.
  /// The amount of short positions to withdraw when decreasing liquidity.
  function calculateGivenLiquidityDelta(
    uint160 rate,
    uint160 deltaLiquidity,
    uint96 duration,
    bool isAdd
  ) internal pure returns (uint256 longAmount, uint256 shortAmount) {
    longAmount = getLong(deltaLiquidity, rate, isAdd);

    shortAmount = getShort(deltaLiquidity, rate, duration, isAdd);
  }

  /// @dev Calculate the amount of liquidity positions and amount of short positions from given long positions in base denomination.
  /// @param rate The pool's squared root Interest Rate.
  /// @param longAmount The amount of long positions.
  /// @param duration The time duration in seconds.
  /// @param isAdd Deposit long amount in base denomination if true. Withdraw long amount in base denomination if false.
  /// @return liquidityAmount The amount of liquidity positions minted when depositing long positions.
  /// The amount of liquidity positions burnt when withdrawing long positions.
  /// @return shortAmount The amount of short positions to deposit when depositing long positions.
  /// The amount of short positions to withdraw when withdrawing long positions.
  function calculateGivenLiquidityLong(
    uint160 rate,
    uint256 longAmount,
    uint96 duration,
    bool isAdd
  ) internal pure returns (uint160 liquidityAmount, uint256 shortAmount) {
    liquidityAmount = getLiquidityGivenLong(rate, longAmount, !isAdd);

    shortAmount = getShort(liquidityAmount, rate, duration, isAdd);
  }

  /// @dev Calculate the amount of liquidity positions and amount of long positions in base denomination from given short positions.
  /// @param rate The pool's squared root Interest Rate.
  /// @param shortAmount The amount of short positions.
  /// @param duration The time duration in seconds.
  /// @param isAdd Deposit short amount if true. Withdraw short amount if false.
  /// @return liquidityAmount The amount of liquidity positions minted when depositing short positions.
  /// The amount of liquidity positions burnt when withdrawing short positions.
  /// @return longAmount The amount of long positions in base denomination to deposit when depositing short positions.
  /// The amount of long positions in base denomination to withdraw when withdrawing short positions.
  function calculateGivenLiquidityShort(
    uint160 rate,
    uint256 shortAmount,
    uint96 duration,
    bool isAdd
  ) internal pure returns (uint160 liquidityAmount, uint256 longAmount) {
    liquidityAmount = getLiquidityGivenShort(rate, shortAmount, duration, !isAdd);

    longAmount = getLong(liquidityAmount, rate, isAdd);
  }

  /// @dev Calculate the amount of liquidity positions and amount of long positions in base denomination or short position whichever is larger or smaller.
  /// @param rate The pool's squared root Interest Rate.
  /// @param amount The amount of long positions in base denomination or short positions whichever is larger.
  /// @param duration The time duration in seconds.
  /// @param isAdd Deposit short amount if true. Withdraw short amount if false.
  /// @return liquidityAmount The amount of liquidity positions minted when depositing short positions.
  /// The amount of liquidity positions burnt when withdrawing short positions.
  /// @return longAmount The amount of long positions in base denomination to deposit when depositing short positions.
  /// The amount of long positions in base denomination to withdraw when withdrawing short positions.
  /// @return shortAmount The amount of short positions to deposit when depositing long positions.
  /// The amount of short positions to withdraw when withdrawing long positions.
  function calculateGivenLiquidityLargerOrSmaller(
    uint160 rate,
    uint256 amount,
    uint96 duration,
    bool isAdd
  ) internal pure returns (uint160 liquidityAmount, uint256 longAmount, uint256 shortAmount) {
    liquidityAmount = getLiquidityGivenLong(rate, amount, !isAdd);

    shortAmount = getShort(liquidityAmount, rate, duration, isAdd);

    if (isAdd ? amount >= shortAmount : amount <= shortAmount) longAmount = amount;
    else {
      liquidityAmount = getLiquidityGivenShort(rate, amount, duration, !isAdd);

      longAmount = getLong(liquidityAmount, rate, isAdd);

      shortAmount = amount;

      if (isAdd ? amount < longAmount : amount > longAmount) longAmount = amount;
    }
  }

  /// @dev Update the new square root interest rate given change in square root change.
  /// @param liquidity The amount of liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param deltaRate The change in the squared root Interest Rate.
  /// @param duration The time duration in seconds.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @param isAdd Increase square root interest rate if true. Decrease square root interest rate if false.
  /// @return newRate The new squared root Interest Rate.
  /// @return longAmount The amount of long positions in base denomination to withdraw when increasing square root interest rate.
  /// The amount of long positions in base denomination to deposit when decreasing square root interest rate.
  /// @return shortAmount The amount of short positions to deposit when increasing square root interest rate.
  /// The amount of short positions to withdraw when decreasing square root interest rate.
  /// @return fees The amount of long positions fee in base denominations when increasing square root interest rate.
  /// The amount of short positions fee when decreasing square root interest rate.
  function updateGivenSqrtInterestRateDelta(
    uint160 liquidity,
    uint160 rate,
    uint160 deltaRate,
    uint96 duration,
    uint256 transactionFee,
    bool isAdd
  ) internal pure returns (uint160 newRate, uint256 longAmount, uint256 shortAmount, uint256 fees) {
    newRate = isAdd ? rate + deltaRate : rate - deltaRate;

    longAmount = getLongFromSqrtInterestRate(liquidity, rate, deltaRate, newRate, !isAdd);

    shortAmount = getShortFromSqrtInterestRate(liquidity, deltaRate, duration, isAdd);

    fees = FeeCalculation.getFeesRemoval(isAdd ? longAmount : shortAmount, transactionFee);
    if (isAdd) longAmount -= fees;
    else shortAmount -= fees;
  }

  /// @dev Update the new square root interest rate given change in long positions in base denomination.
  /// @param liquidity The amount of liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param longAmount The amount of long positions.
  /// @param duration The time duration in seconds.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @return newRate The new squared root Interest Rate.
  /// @return shortAmount The amount of short positions to withdraw when depositing long positions in base denomination.
  /// The amount of short positions to deposit when withdrawing long positions in base denomination.
  /// @return fees The amount of short positions fee when depositing long positions in base denomination.
  /// The amount of long positions fee in base denominations fee when withdrawing long positions in base denomination.
  function updateGivenLong(
    uint160 liquidity,
    uint160 rate,
    uint256 longAmount,
    uint96 duration,
    uint256 transactionFee,
    bool isAdd
  ) internal pure returns (uint160 newRate, uint256 shortAmount, uint256 fees) {
    if (!isAdd) fees = FeeCalculation.getFeesAdditional(longAmount, transactionFee);

    newRate = getNewSqrtInterestRateGivenLong(liquidity, rate, longAmount + fees, isAdd);

    shortAmount = getShortFromSqrtInterestRate(liquidity, isAdd ? rate - newRate : newRate - rate, duration, !isAdd);

    if (isAdd) {
      fees = FeeCalculation.getFeesRemoval(shortAmount, transactionFee);
      shortAmount -= fees;
    }
  }

  /// @dev Update the new square root interest rate given change in short positions.
  /// @param liquidity The amount of liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param shortAmount The amount of short positions.
  /// @param duration The time duration in seconds.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @return newRate The new squared root Interest Rate.
  /// @return longAmount The amount of long positions in base denomination to withdraw when depositing short positions.
  /// The amount of long positions in base denomination to deposit when withdrawing short positions.
  /// @return fees The amount of long positions fee in base denominations when depositing short positions.
  /// The amount of short positions fee when withdrawing short positions.
  function updateGivenShort(
    uint160 liquidity,
    uint160 rate,
    uint256 shortAmount,
    uint96 duration,
    uint256 transactionFee,
    bool isAdd
  ) internal pure returns (uint160 newRate, uint256 longAmount, uint256 fees) {
    if (!isAdd) fees = FeeCalculation.getFeesAdditional(shortAmount, transactionFee);

    uint160 deltaRate;
    (newRate, deltaRate) = getNewSqrtInterestRateGivenShort(liquidity, rate, shortAmount + fees, duration, isAdd);

    longAmount = getLongFromSqrtInterestRate(liquidity, rate, deltaRate, newRate, !isAdd);

    if (isAdd) {
      fees = FeeCalculation.getFeesRemoval(longAmount, transactionFee);
      longAmount -= fees;
    }
  }

  /// @dev Update the new square root interest rate given sum of long positions in base denomination change and short position change.
  /// @param liquidity The amount of liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param sumAmount The sum amount of long positions in base denomination change and short position change.
  /// @param duration The time duration in seconds.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @param isAdd Increase square root interest rate if true. Decrease square root interest rate if false.
  /// @return newRate The new squared root Interest Rate.
  /// @return longAmount The amount of long positions in base denomination to withdraw when increasing square root interest rate.
  /// The amount of long positions in base denomination to deposit when decreasing square root interest rate.
  /// @return shortAmount The amount of short positions to deposit when increasing square root interest rate.
  /// The amount of short positions to withdraw when decreasing square root interest rate.
  /// @return fees The amount of long positions fee in base denominations when increasing square root interest rate.
  /// The amount of short positions fee when decreasing square root interest rate.
  function updateGivenSumLong(
    uint160 liquidity,
    uint160 rate,
    uint256 sumAmount,
    uint96 duration,
    uint256 transactionFee,
    bool isAdd
  ) internal pure returns (uint160 newRate, uint256 longAmount, uint256 shortAmount, uint256 fees) {
    uint256 amount = getShortOrLongFromGivenSum(liquidity, rate, sumAmount, duration, transactionFee, isAdd);

    if (isAdd) (newRate, ) = getNewSqrtInterestRateGivenShort(liquidity, rate, amount, duration, false);
    else newRate = getNewSqrtInterestRateGivenLong(liquidity, rate, amount, false);

    fees = FeeCalculation.getFeesRemoval(amount, transactionFee);
    amount -= fees;

    if (isAdd) {
      shortAmount = amount;
      longAmount = sumAmount - shortAmount;
    } else {
      longAmount = amount;
      shortAmount = sumAmount - longAmount;
    }
  }

  /// @dev Returns liquidity for a given long.
  /// @param rate The pool's squared root Interest Rate.
  /// @param longAmount The amount of long in base denomination change..
  /// @param roundUp Round up the result when true. Round down the result when false.
  function getLiquidityGivenLong(uint160 rate, uint256 longAmount, bool roundUp) private pure returns (uint160) {
    return FullMath.mulDiv(uint256(rate), longAmount, uint256(1) << 96, roundUp).toUint160();
  }

  /// @dev Returns liquidity for a given short.
  /// @param rate The pool's squared root Interest Rate.
  /// @param shortAmount The amount of short change.
  /// @param duration The time duration in seconds.
  /// @param roundUp Round up the result when true. Round down the result when false.
  function getLiquidityGivenShort(
    uint160 rate,
    uint256 shortAmount,
    uint96 duration,
    bool roundUp
  ) private pure returns (uint160) {
    return FullMath.mulDiv(shortAmount, uint256(1) << 192, uint256(rate).unsafeMul(duration), roundUp).toUint160();
  }

  /// @dev Returns the new squared root interest rate given long positions in base denomination change.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param longAmount The amount long positions in base denomination change.
  /// @param isAdd Long positions increase when true. Long positions decrease when false.
  function getNewSqrtInterestRateGivenLong(
    uint160 liquidity,
    uint160 rate,
    uint256 longAmount,
    bool isAdd
  ) private pure returns (uint160) {
    uint256 numerator;
    unchecked {
      numerator = uint256(liquidity) << 96;
    }

    uint256 product = longAmount.unsafeMul(rate);

    if (isAdd) {
      if (product.div(longAmount, false) == rate) {
        uint256 denominator = numerator.unsafeAdd(product);
        if (denominator >= numerator) {
          return FullMath.mulDiv(numerator, rate, denominator, true).toUint160();
        }
      }

      uint256 denominator2 = numerator.div(rate, false);

      denominator2 += longAmount;
      return numerator.div(denominator2, true).toUint160();
    } else {
      if (product.div(longAmount, false) != rate || product >= numerator) revert NotEnoughLiquidityToBorrow();

      uint256 denominator = numerator.unsafeSub(product);
      return (FullMath.mulDiv(numerator, rate, denominator, true)).toUint160();
    }
  }

  /// @dev Returns the new squared root interest rate given short position change.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param shortAmount The amount short positions change.
  /// @param duration The time duration in seconds.
  /// @param isAdd Short positions increase when true. Short positions decrease when false.
  /// @return newRate The updated squared interest rate
  /// @return deltaRate The difference between the new and old squared interest rate
  function getNewSqrtInterestRateGivenShort(
    uint160 liquidity,
    uint160 rate,
    uint256 shortAmount,
    uint96 duration,
    bool isAdd
  ) private pure returns (uint160 newRate, uint160 deltaRate) {
    uint256 denominator = uint256(liquidity).unsafeMul(duration);

    deltaRate = FullMath.mulDiv(shortAmount, uint256(1) << 192, denominator, !isAdd).toUint160();

    if (isAdd) newRate = rate + deltaRate;
    else if (rate > deltaRate) newRate = rate - deltaRate;
    else revert NotEnoughLiquidityToLend();
  }

  /// @dev Returns the long positions for a given interest rate.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param deltaRate The pool's delta rate in square root interest rate.
  /// @param newRate The new interest rate of the pool
  /// @param roundUp Increases in square root interest rate when true. Decrease in square root interest rate when false.
  function getLongFromSqrtInterestRate(
    uint160 liquidity,
    uint160 rate,
    uint160 deltaRate,
    uint160 newRate,
    bool roundUp
  ) private pure returns (uint256) {
    uint256 numerator;
    unchecked {
      numerator = uint256(liquidity) << 96;
    }
    return
      roundUp
        ? FullMath.mulDiv(numerator, deltaRate, uint256(rate), true).div(newRate, true)
        : FullMath.mulDiv(numerator, deltaRate, uint256(newRate), false).div(rate, false);
  }

  /// @dev Returns the short positions for a given interest rate.
  /// @param liquidity The liquidity of the pool.
  /// @param deltaRate The pool's delta rate in square root interest rate.
  /// @param duration The time duration in seconds.
  /// @param roundUp Increases in square root interest rate when true. Decrease in square root interest rate when false.
  function getShortFromSqrtInterestRate(
    uint160 liquidity,
    uint160 deltaRate,
    uint96 duration,
    bool roundUp
  ) private pure returns (uint256) {
    uint256 numerator = uint256(liquidity).unsafeMul(duration);
    return FullMath.mulDiv(uint256(numerator), uint256(deltaRate), uint256(1) << 192, roundUp);
  }

  /// @dev Get the short amount or long amount for given sum type transactions.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param sumAmount The given sum amount.
  /// @param duration The duration of the pool.
  /// @param transactionFee The transaction fee of the pool.
  /// @param isShort True if to calculate for short amount.
  /// False if to calculate for long amount.
  /// @return amount The short amount or long amount calculated.
  function getShortOrLongFromGivenSum(
    uint160 liquidity,
    uint160 rate,
    uint256 sumAmount,
    uint96 duration,
    uint256 transactionFee,
    bool isShort
  ) private pure returns (uint256 amount) {
    uint256 negativeB = calculateNegativeB(liquidity, rate, sumAmount, duration, transactionFee, isShort);

    uint256 sqrtDiscriminant = calculateSqrtDiscriminant(
      liquidity,
      rate,
      sumAmount,
      duration,
      transactionFee,
      negativeB,
      isShort
    );

    amount = (negativeB - sqrtDiscriminant).shr(1, false);
  }

  /// @dev Calculate the negativeB.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param sumAmount The given sum amount.
  /// @param duration The duration of the pool.
  /// @param transactionFee The transaction fee of the pool.
  /// @param isShort True if to calculate for short amount.
  /// False if to calculate for long amount.
  /// @return negativeB The negative B calculated.
  function calculateNegativeB(
    uint160 liquidity,
    uint160 rate,
    uint256 sumAmount,
    uint96 duration,
    uint256 transactionFee,
    bool isShort
  ) private pure returns (uint256 negativeB) {
    uint256 adjustment = (uint256(1) << 16).unsafeSub(transactionFee);

    uint256 negativeB0 = isShort ? getShort(liquidity, rate, duration, false) : getLong(liquidity, rate, false);
    uint256 negativeB1 = isShort
      ? FullMath.mulDiv(liquidity, uint256(1) << 112, uint256(rate).unsafeMul(adjustment), false)
      : FullMath.mulDiv(uint256(liquidity).unsafeMul(duration), rate, (uint256(1) << 176).unsafeMul(adjustment), false);
    uint256 negativeB2 = FullMath.mulDiv(sumAmount, uint256(1) << 16, adjustment, false);

    negativeB = negativeB0 + negativeB1 + negativeB2;
  }

  /// Dev Calculate the square root discriminant.
  /// @param liquidity The liquidity of the pool.
  /// @param rate The pool's squared root Interest Rate.
  /// @param sumAmount The given sum amount.
  /// @param duration The duration of the pool.
  /// @param transactionFee The transaction fee of the pool.
  /// @param negativeB The negative B calculated.
  /// @param isShort True if to calculate for short amount.
  /// False if to calculate for long amount.
  /// @return sqrtDiscriminant The square root disriminant calculated.
  function calculateSqrtDiscriminant(
    uint160 liquidity,
    uint160 rate,
    uint256 sumAmount,
    uint96 duration,
    uint256 transactionFee,
    uint256 negativeB,
    bool isShort
  ) private pure returns (uint256 sqrtDiscriminant) {
    uint256 denominator = isShort
      ? (uint256(1) << 174).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee))
      : uint256(rate).unsafeMul((uint256(1) << 16).unsafeSub(transactionFee));

    (uint256 a0, uint256 a1) = isShort
      ? FullMath.mul512(uint256(liquidity).unsafeMul(duration), rate)
      : FullMath.mul512(liquidity, uint256(1) << 114);

    (uint256 a00, uint256 a01) = FullMath.mul512(a0, sumAmount);
    (uint256 a10, uint256 a11) = FullMath.mul512(a1, sumAmount);

    if (a11 == 0 && a01.unsafeAdd(a10) >= a01) {
      a0 = a00;
      a1 = a01.unsafeAdd(a10);
      (a0, a1) = FullMath.div512(a0, a1, denominator, false);
    } else {
      (a0, a1) = FullMath.div512(a0, a1, denominator, false);

      (a00, a01) = FullMath.mul512(a0, sumAmount);
      (a10, a11) = FullMath.mul512(a1, sumAmount);

      if (a11 != 0 || a01.unsafeAdd(a10) < a01) revert CalculationOverload();
      a0 = a00;
      a1 = a01.unsafeAdd(a10);
    }

    (uint256 b0, uint256 b1) = FullMath.mul512(negativeB, negativeB);

    (b0, b1) = FullMath.sub512(b0, b1, a0, a1);

    sqrtDiscriminant = FullMath.sqrt512(b0, b1, true);
  }
}

File 6 of 39 : TimeswapV2PoolDeployer.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2Pool} from "./TimeswapV2Pool.sol";

import {ITimeswapV2PoolDeployer} from "./interfaces/ITimeswapV2PoolDeployer.sol";

/// @title Capable of deploying Timeswap V2 Pool
/// @author Timeswap Labs
contract TimeswapV2PoolDeployer is ITimeswapV2PoolDeployer {
  struct Parameter {
    address poolFactory;
    address optionPair;
    uint256 transactionFee;
    uint256 protocolFee;
  }

  /* ===== MODEL ===== */

  /// @inheritdoc ITimeswapV2PoolDeployer
  Parameter public override parameter;

  /* ===== UPDATE ===== */
  /// @dev deploy the pool contract
  /// @param poolFactory address of the pool factory
  /// @param optionPair address of the option pair contract
  /// @param transactionFee transaction fee to be used in the pool contract
  /// @param protocolFee protocol fee to be used in the pool contract
  function deploy(
    address poolFactory,
    address optionPair,
    uint256 transactionFee,
    uint256 protocolFee
  ) internal returns (address poolPair) {
    parameter = Parameter({
      poolFactory: poolFactory,
      optionPair: optionPair,
      transactionFee: transactionFee,
      protocolFee: protocolFee
    });

    poolPair = address(new TimeswapV2Pool{salt: keccak256(abi.encode(optionPair))}());

    delete parameter;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Ownership} from "@timeswap-labs/v2-library/contracts/Ownership.sol";

import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";

import {ITimeswapV2Option} from "@timeswap-labs/v2-option/contracts/interfaces/ITimeswapV2Option.sol";

import {TimeswapV2OptionPosition} from "@timeswap-labs/v2-option/contracts/enums/Position.sol";

import {StrikeAndMaturity} from "@timeswap-labs/v2-option/contracts/structs/StrikeAndMaturity.sol";

import {NoDelegateCall} from "./NoDelegateCall.sol";

import {ITimeswapV2Pool} from "./interfaces/ITimeswapV2Pool.sol";
import {ITimeswapV2PoolFactory} from "./interfaces/ITimeswapV2PoolFactory.sol";
import {ITimeswapV2PoolDeployer} from "./interfaces/ITimeswapV2PoolDeployer.sol";

import {ITimeswapV2PoolMintCallback} from "./interfaces/callbacks/ITimeswapV2PoolMintCallback.sol";
import {ITimeswapV2PoolBurnCallback} from "./interfaces/callbacks/ITimeswapV2PoolBurnCallback.sol";
import {ITimeswapV2PoolDeleverageCallback} from "./interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol";
import {ITimeswapV2PoolLeverageCallback} from "./interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol";
import {ITimeswapV2PoolRebalanceCallback} from "./interfaces/callbacks/ITimeswapV2PoolRebalanceCallback.sol";

import {ReentrancyGuard} from "./libraries/ReentrancyGuard.sol";

import {LiquidityPosition, LiquidityPositionLibrary} from "./structs/LiquidityPosition.sol";

import {Pool, PoolLibrary} from "./structs/Pool.sol";
import {TimeswapV2PoolCollectProtocolFeesParam, TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam, ParamLibrary} from "./structs/Param.sol";
import {TimeswapV2PoolMintChoiceCallbackParam, TimeswapV2PoolMintCallbackParam, TimeswapV2PoolBurnChoiceCallbackParam, TimeswapV2PoolBurnCallbackParam, TimeswapV2PoolDeleverageChoiceCallbackParam, TimeswapV2PoolDeleverageCallbackParam, TimeswapV2PoolLeverageCallbackParam, TimeswapV2PoolLeverageChoiceCallbackParam, TimeswapV2PoolRebalanceCallbackParam} from "./structs/CallbackParam.sol";

import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "./enums/Transaction.sol";

/// @title Contract for TimeswapV2Pool
/// @author Timeswap Labs
contract TimeswapV2Pool is ITimeswapV2Pool, NoDelegateCall {
  using PoolLibrary for Pool;
  using Ownership for address;
  using LiquidityPositionLibrary for LiquidityPosition;

  /* ===== MODEL ===== */

  /// @inheritdoc ITimeswapV2Pool
  address public immutable override poolFactory;
  /// @inheritdoc ITimeswapV2Pool
  address public immutable override optionPair;
  /// @inheritdoc ITimeswapV2Pool
  uint256 public immutable override transactionFee;
  /// @inheritdoc ITimeswapV2Pool
  uint256 public immutable override protocolFee;

  mapping(uint256 => mapping(uint256 => uint96)) private reentrancyGuards;
  mapping(uint256 => mapping(uint256 => Pool)) private pools;

  StrikeAndMaturity[] private listOfPools;

  function addPoolEnumerationIfNecessary(uint256 strike, uint256 maturity) private {
    if (reentrancyGuards[strike][maturity] == ReentrancyGuard.NOT_INTERACTED) {
      reentrancyGuards[strike][maturity] = ReentrancyGuard.NOT_ENTERED;
      listOfPools.push(StrikeAndMaturity({strike: strike, maturity: maturity}));
    }
  }

  /* ===== MODIFIER ===== */
  /// @dev function to raise the reentrancy guard
  /// @param strike the strike amount
  /// @param maturity the maturity timestamp
  function raiseGuard(uint256 strike, uint256 maturity) private {
    ReentrancyGuard.check(reentrancyGuards[strike][maturity]);
    reentrancyGuards[strike][maturity] = ReentrancyGuard.ENTERED;
  }

  /// @dev function to lower the reentrancy guard
  /// @param strike the strike amount
  /// @param maturity the maturity timestamp
  function lowerGuard(uint256 strike, uint256 maturity) private {
    reentrancyGuards[strike][maturity] = ReentrancyGuard.NOT_ENTERED;
  }

  /* ===== INIT ===== */
  /// @dev constructor for the contract
  constructor() NoDelegateCall() {
    (poolFactory, optionPair, transactionFee, protocolFee) = ITimeswapV2PoolDeployer(msg.sender).parameter();
  }

  // Can be overidden for testing purposes.
  /// @dev for advancing the duration
  /// @param durationForward the durationForward seconds
  function blockTimestamp(uint96 durationForward) internal view virtual returns (uint96) {
    return uint96(block.timestamp + durationForward); // truncation is desired
  }

  function hasLiquidity(uint256 strike, uint256 maturity) private view {
    if (pools[strike][maturity].liquidity == 0) Error.requireLiquidity();
  }

  /* ===== VIEW ===== */

  /// @inheritdoc ITimeswapV2Pool
  function getByIndex(uint256 id) external view override returns (StrikeAndMaturity memory) {
    return listOfPools[id];
  }

  /// @inheritdoc ITimeswapV2Pool
  function numberOfPools() external view override returns (uint256) {
    return listOfPools.length;
  }

  /// @inheritdoc ITimeswapV2Pool
  function totalLiquidity(uint256 strike, uint256 maturity) external view override returns (uint160) {
    return pools[strike][maturity].liquidity;
  }

  /// @inheritdoc ITimeswapV2Pool
  function sqrtInterestRate(uint256 strike, uint256 maturity) external view override returns (uint160) {
    return pools[strike][maturity].sqrtInterestRate;
  }

  /// @inheritdoc ITimeswapV2Pool
  function liquidityOf(uint256 strike, uint256 maturity, address owner) external view override returns (uint160) {
    return pools[strike][maturity].liquidityPositions[owner].liquidity;
  }

  /// @inheritdoc ITimeswapV2Pool
  function feesEarnedAndShortReturnedGrowth(
    uint256 strike,
    uint256 maturity
  )
    external
    view
    returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth)
  {
    return pools[strike][maturity].feesEarnedAndShortReturnedGrowth(maturity, blockTimestamp(0));
  }

  /// @inheritdoc ITimeswapV2Pool
  function feesEarnedAndShortReturnedGrowth(
    uint256 strike,
    uint256 maturity,
    uint96 durationForward
  )
    external
    view
    returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth)
  {
    return pools[strike][maturity].feesEarnedAndShortReturnedGrowth(maturity, blockTimestamp(durationForward));
  }

  /// @inheritdoc ITimeswapV2Pool
  function feesEarnedAndShortReturnedOf(
    uint256 strike,
    uint256 maturity,
    address owner
  ) external view override returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    return pools[strike][maturity].feesEarnedAndShortReturnedOf(maturity, owner, blockTimestamp(0));
  }

  /// @inheritdoc ITimeswapV2Pool
  function feesEarnedAndShortReturnedOf(
    uint256 strike,
    uint256 maturity,
    address owner,
    uint96 durationForward
  ) external view override returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    return pools[strike][maturity].feesEarnedAndShortReturnedOf(maturity, owner, blockTimestamp(durationForward));
  }

  /// @inheritdoc ITimeswapV2Pool
  function protocolFeesEarned(
    uint256 strike,
    uint256 maturity
  ) external view override returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees) {
    return pools[strike][maturity].protocolFeesEarned();
  }

  /// @inheritdoc ITimeswapV2Pool
  function totalLongBalance(
    uint256 strike,
    uint256 maturity
  ) external view override returns (uint256 long0Amount, uint256 long1Amount) {
    Pool storage pool = pools[strike][maturity];
    long0Amount = pool.long0Balance;
    long1Amount = pool.long1Balance;
  }

  /// @inheritdoc ITimeswapV2Pool
  function totalLongBalanceAdjustFees(
    uint256 strike,
    uint256 maturity
  ) external view override returns (uint256 long0Amount, uint256 long1Amount) {
    (long0Amount, long1Amount) = pools[strike][maturity].totalLongBalanceAdjustFees(transactionFee);
  }

  /// @inheritdoc ITimeswapV2Pool
  function totalPositions(
    uint256 strike,
    uint256 maturity
  ) external view override returns (uint256 longAmount, uint256 shortAmount) {
    (longAmount, shortAmount) = pools[strike][maturity].totalPositions(maturity, blockTimestamp(0));
  }

  /* ===== UPDATE ===== */

  /// @inheritdoc ITimeswapV2Pool
  function transferLiquidity(uint256 strike, uint256 maturity, address to, uint160 liquidityAmount) external override {
    hasLiquidity(strike, maturity);

    if (blockTimestamp(0) > maturity) Error.alreadyMatured(maturity, blockTimestamp(0));
    if (to == address(0)) Error.zeroAddress();
    if (liquidityAmount == 0) Error.zeroInput();

    pools[strike][maturity].transferLiquidity(maturity, to, liquidityAmount, blockTimestamp(0));

    emit TransferLiquidity(strike, maturity, msg.sender, to, liquidityAmount);
  }

  /// @inheritdoc ITimeswapV2Pool
  function initialize(uint256 strike, uint256 maturity, uint160 rate) external override noDelegateCall {
    if (strike == 0) Error.cannotBeZero();
    if (maturity < blockTimestamp(0)) Error.alreadyMatured(maturity, blockTimestamp(0));
    if (rate == 0) Error.cannotBeZero();
    addPoolEnumerationIfNecessary(strike, maturity);

    pools[strike][maturity].initialize(rate);
  }

  /// @inheritdoc ITimeswapV2Pool
  function collectProtocolFees(
    TimeswapV2PoolCollectProtocolFeesParam calldata param
  ) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) {
    ParamLibrary.check(param);
    raiseGuard(param.strike, param.maturity);

    // Can only be called by the TimeswapV2Pool factory owner.
    ITimeswapV2PoolFactory(poolFactory).owner().checkIfOwner();

    // Calculate the main logic of protocol fee.
    (long0Amount, long1Amount, shortAmount) = pools[param.strike][param.maturity].collectProtocolFees(
      param.long0Requested,
      param.long1Requested,
      param.shortRequested
    );

    collect(
      param.strike,
      param.maturity,
      param.long0To,
      param.long1To,
      param.shortTo,
      long0Amount,
      long1Amount,
      shortAmount
    );

    lowerGuard(param.strike, param.maturity);

    emit CollectProtocolFees(
      param.strike,
      param.maturity,
      msg.sender,
      param.long0To,
      param.long1To,
      param.shortTo,
      long0Amount,
      long1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Pool
  function collectTransactionFeesAndShortReturned(
    TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam calldata param
  )
    external
    override
    noDelegateCall
    returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned)
  {
    ParamLibrary.check(param);
    raiseGuard(param.strike, param.maturity);

    // Calculate the main logic of transaction fee.
    (long0Fees, long1Fees, shortFees, shortReturned) = pools[param.strike][param.maturity]
      .collectTransactionFeesAndShortReturned(
        param.maturity,
        param.long0FeesRequested,
        param.long1FeesRequested,
        param.shortFeesRequested,
        param.shortReturnedRequested,
        blockTimestamp(0)
      );

    collect(
      param.strike,
      param.maturity,
      param.long0FeesTo,
      param.long1FeesTo,
      param.shortFeesTo,
      long0Fees,
      long1Fees,
      shortFees
    );

    if (shortReturned != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        param.strike,
        param.maturity,
        param.shortReturnedTo,
        TimeswapV2OptionPosition.Short,
        shortReturned
      );

    lowerGuard(param.strike, param.maturity);

    emit CollectTransactionFeesAndShortReturned(
      param.strike,
      param.maturity,
      msg.sender,
      param.long0FeesTo,
      param.long1FeesTo,
      param.shortFeesTo,
      param.shortReturnedTo,
      long0Fees,
      long1Fees,
      shortFees,
      shortReturned
    );
  }

  /// @dev Transfer long0 positions, long1 positions, and/or short positions to the recipients.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param long0To The recipient of long0 positions.
  /// @param long1To The recipient of long1 positions.
  /// @param shortTo The recipient of short positions.
  /// @param long0Amount The amount of long0 positions wanted.
  /// @param long1Amount The amount of long1 positions wanted.
  /// @param shortAmount The amount of short positions wanted.
  function collect(
    uint256 strike,
    uint256 maturity,
    address long0To,
    address long1To,
    address shortTo,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  ) private {
    if (long0Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        strike,
        maturity,
        long0To,
        TimeswapV2OptionPosition.Long0,
        long0Amount
      );

    if (long1Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        strike,
        maturity,
        long1To,
        TimeswapV2OptionPosition.Long1,
        long1Amount
      );

    if (shortAmount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        strike,
        maturity,
        shortTo,
        TimeswapV2OptionPosition.Short,
        shortAmount
      );
  }

  /// @inheritdoc ITimeswapV2Pool
  function mint(
    TimeswapV2PoolMintParam calldata param
  )
    external
    override
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    return mint(param, false, 0);
  }

  /// @inheritdoc ITimeswapV2Pool
  function mint(
    TimeswapV2PoolMintParam calldata param,
    uint96 durationForward
  )
    external
    override
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    return mint(param, true, durationForward);
  }

  /// @dev deposit Short and Long tokens and mints Liquidity
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the mint function.
  /// @param durationForward The duration of time moved forward.
  /// @param isQuote Whether used for quoting purposes
  /// @return liquidityAmount The amount of liquidity minted.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function mint(
    TimeswapV2PoolMintParam calldata param,
    bool isQuote,
    uint96 durationForward
  )
    private
    noDelegateCall
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    ParamLibrary.check(param, blockTimestamp(durationForward));
    raiseGuard(param.strike, param.maturity);

    // Calculate the main logic of mint function.
    (liquidityAmount, long0Amount, long1Amount, shortAmount, data) = pools[param.strike][param.maturity].mint(
      param,
      blockTimestamp(durationForward)
    );

    // Calculate the amount of long0 position, long1 position, and short position required by the pool.

    // long0Amount chosen could be zero. Skip the calculation for gas efficiency.
    uint256 long0BalanceTarget;
    if (long0Amount != 0)
      long0BalanceTarget =
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long0
        ) +
        long0Amount;

    // long1Amount chosen could be zero. Skip the calculation for gas efficiency.
    uint256 long1BalanceTarget;
    if (long1Amount != 0)
      long1BalanceTarget =
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long1
        ) +
        long1Amount;

    // shortAmount cannot be zero.
    uint256 shortBalanceTarget = ITimeswapV2Option(optionPair).positionOf(
      param.strike,
      param.maturity,
      address(this),
      TimeswapV2OptionPosition.Short
    ) + shortAmount;

    // Ask the msg.sender to transfer the positions into this address.
    data = ITimeswapV2PoolMintCallback(msg.sender).timeswapV2PoolMintCallback(
      TimeswapV2PoolMintCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        long0Amount: long0Amount,
        long1Amount: long1Amount,
        shortAmount: shortAmount,
        liquidityAmount: liquidityAmount,
        data: data
      })
    );

    if (isQuote) revert Quote();

    // Check when the position balance targets are reached.

    if (long0Amount != 0)
      Error.checkEnough(
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long0
        ),
        long0BalanceTarget
      );

    if (long1Amount != 0)
      Error.checkEnough(
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long1
        ),
        long1BalanceTarget
      );

    Error.checkEnough(
      ITimeswapV2Option(optionPair).positionOf(
        param.strike,
        param.maturity,
        address(this),
        TimeswapV2OptionPosition.Short
      ),
      shortBalanceTarget
    );

    lowerGuard(param.strike, param.maturity);

    emit Mint(
      param.strike,
      param.maturity,
      msg.sender,
      param.to,
      liquidityAmount,
      long0Amount,
      long1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Pool
  function burn(
    TimeswapV2PoolBurnParam calldata param
  )
    external
    override
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    return burn(param, false, 0);
  }

  /// @inheritdoc ITimeswapV2Pool
  function burn(
    TimeswapV2PoolBurnParam calldata param,
    uint96 durationForward
  )
    external
    override
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    return burn(param, true, durationForward);
  }

  /// @dev burn Liquidity and receive Short and Long tokens
  /// @dev can be only called before the maturity.
  /// @dev after the maturity of the pool, the long0 and long1 tokens are zero. And the short tokens are added into the transaction fees.
  /// @dev if the user wants to burn the liquidity after the maturity, they should call the collectTransactionFee function.
  /// @param param it is a struct that contains the parameters of the burn function
  /// @param durationForward The duration of time moved forward.
  /// @param isQuote Whether is used for quoting purposes.
  /// @return liquidityAmount The amount of liquidity burned.
  /// @return long0Amount The amount of long0 withdrawn.
  /// @return long1Amount The amount of long1 withdrawn.
  /// @return shortAmount The amount of short withdrawn.
  /// @return data the data used for the callbacks.
  function burn(
    TimeswapV2PoolBurnParam calldata param,
    bool isQuote,
    uint96 durationForward
  )
    private
    noDelegateCall
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    hasLiquidity(param.strike, param.maturity);

    ParamLibrary.check(param, blockTimestamp(durationForward));
    raiseGuard(param.strike, param.maturity);

    Pool storage pool = pools[param.strike][param.maturity];

    // Calculate the main logic of burn function.
    (liquidityAmount, long0Amount, long1Amount, shortAmount, data) = pool.burn(param, blockTimestamp(durationForward));

    // Transfer the positions to the recipients.

    // Long0 amount can be zero.
    if (long0Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        param.strike,
        param.maturity,
        param.long0To,
        TimeswapV2OptionPosition.Long0,
        long0Amount
      );

    // Long1 amount can be zero.
    if (long1Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        param.strike,
        param.maturity,
        param.long1To,
        TimeswapV2OptionPosition.Long1,
        long1Amount
      );

    // Short amount cannot be zero.
    ITimeswapV2Option(optionPair).transferPosition(
      param.strike,
      param.maturity,
      param.shortTo,
      TimeswapV2OptionPosition.Short,
      shortAmount
    );

    data = ITimeswapV2PoolBurnCallback(msg.sender).timeswapV2PoolBurnCallback(
      TimeswapV2PoolBurnCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        long0Amount: long0Amount,
        long1Amount: long1Amount,
        shortAmount: shortAmount,
        liquidityAmount: liquidityAmount,
        data: data
      })
    );

    if (isQuote) revert Quote();

    pool.liquidityPositions[msg.sender].burn(liquidityAmount);

    lowerGuard(param.strike, param.maturity);

    emit Burn(
      param.strike,
      param.maturity,
      msg.sender,
      param.long0To,
      param.long1To,
      param.shortTo,
      liquidityAmount,
      long0Amount,
      long1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Pool
  function deleverage(
    TimeswapV2PoolDeleverageParam calldata param
  ) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    return deleverage(param, false, 0);
  }

  /// @inheritdoc ITimeswapV2Pool
  function deleverage(
    TimeswapV2PoolDeleverageParam calldata param,
    uint96 durationForward
  ) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    return deleverage(param, true, durationForward);
  }

  /// @dev deposit Long tokens and receive Short tokens
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the deleverage function.
  /// @param durationForward The duration of time moved forward.
  /// @param isQuote Whether is used for quoting purposes.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short received.
  /// @return data the data used for the callbacks.
  function deleverage(
    TimeswapV2PoolDeleverageParam calldata param,
    bool isQuote,
    uint96 durationForward
  ) private noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    hasLiquidity(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp(durationForward));
    raiseGuard(param.strike, param.maturity);

    // Calculate the main logic of deleverage function.
    (long0Amount, long1Amount, shortAmount, data) = pools[param.strike][param.maturity].deleverage(
      param,
      transactionFee,
      protocolFee,
      blockTimestamp(durationForward)
    );

    // Calculate the amount of long0 position and long1 position required by the pool.

    // long0Amount chosen could be zero. Skip the calculation for gas efficiency.
    uint256 long0BalanceTarget;
    if (long0Amount != 0)
      long0BalanceTarget =
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long0
        ) +
        long0Amount;

    // long1Amount chosen could be zero. Skip the calculation for gas efficiency.
    uint256 long1BalanceTarget;
    if (long1Amount != 0)
      long1BalanceTarget =
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long1
        ) +
        long1Amount;

    // Transfer short positions to the recipient.
    ITimeswapV2Option(optionPair).transferPosition(
      param.strike,
      param.maturity,
      param.to,
      TimeswapV2OptionPosition.Short,
      shortAmount
    );

    // Ask the msg.sender to transfer the positions into this address.
    data = ITimeswapV2PoolDeleverageCallback(msg.sender).timeswapV2PoolDeleverageCallback(
      TimeswapV2PoolDeleverageCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        long0Amount: long0Amount,
        long1Amount: long1Amount,
        shortAmount: shortAmount,
        data: data
      })
    );

    if (isQuote) revert Quote();

    // Check when the position balance targets are reached.

    if (long0Amount != 0)
      Error.checkEnough(
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long0
        ),
        long0BalanceTarget
      );

    if (long1Amount != 0)
      Error.checkEnough(
        ITimeswapV2Option(optionPair).positionOf(
          param.strike,
          param.maturity,
          address(this),
          TimeswapV2OptionPosition.Long1
        ),
        long1BalanceTarget
      );

    lowerGuard(param.strike, param.maturity);

    emit Deleverage(param.strike, param.maturity, msg.sender, param.to, long0Amount, long1Amount, shortAmount);
  }

  /// @inheritdoc ITimeswapV2Pool
  function leverage(
    TimeswapV2PoolLeverageParam calldata param
  ) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    return leverage(param, false, 0);
  }

  /// @inheritdoc ITimeswapV2Pool
  function leverage(
    TimeswapV2PoolLeverageParam calldata param,
    uint96 durationForward
  ) external override returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    return leverage(param, true, durationForward);
  }

  /// @dev deposit Short tokens and receive Long tokens
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the leverage function.
  /// @param durationForward The duration of time moved forward.
  /// @param isQuote Whether is used for quoting purposes.
  /// @return long0Amount The amount of long0 received.
  /// @return long1Amount The amount of long1 received.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function leverage(
    TimeswapV2PoolLeverageParam calldata param,
    bool isQuote,
    uint96 durationForward
  ) private noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    hasLiquidity(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp(durationForward));
    raiseGuard(param.strike, param.maturity);

    // Calculate the main logic of leverage function.
    (long0Amount, long1Amount, shortAmount, data) = pools[param.strike][param.maturity].leverage(
      param,
      transactionFee,
      protocolFee,
      blockTimestamp(durationForward)
    );

    // Calculate the amount of short position required by the pool.

    uint256 balanceTarget = ITimeswapV2Option(optionPair).positionOf(
      param.strike,
      param.maturity,
      address(this),
      TimeswapV2OptionPosition.Short
    ) + shortAmount;

    // Transfer the positions to the recipients.

    if (long0Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        param.strike,
        param.maturity,
        param.long0To,
        TimeswapV2OptionPosition.Long0,
        long0Amount
      );

    if (long1Amount != 0)
      ITimeswapV2Option(optionPair).transferPosition(
        param.strike,
        param.maturity,
        param.long1To,
        TimeswapV2OptionPosition.Long1,
        long1Amount
      );

    // Ask the msg.sender to transfer the positions into this address.
    data = ITimeswapV2PoolLeverageCallback(msg.sender).timeswapV2PoolLeverageCallback(
      TimeswapV2PoolLeverageCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        long0Amount: long0Amount,
        long1Amount: long1Amount,
        shortAmount: shortAmount,
        data: data
      })
    );

    if (isQuote) revert Quote();

    // Check when the position balance targets are reached.

    Error.checkEnough(
      ITimeswapV2Option(optionPair).positionOf(
        param.strike,
        param.maturity,
        address(this),
        TimeswapV2OptionPosition.Short
      ),
      balanceTarget
    );

    lowerGuard(param.strike, param.maturity);

    emit Leverage(
      param.strike,
      param.maturity,
      msg.sender,
      param.long0To,
      param.long1To,
      long0Amount,
      long1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Pool
  function rebalance(
    TimeswapV2PoolRebalanceParam calldata param
  ) external override noDelegateCall returns (uint256 long0Amount, uint256 long1Amount, bytes memory data) {
    hasLiquidity(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp(0));
    raiseGuard(param.strike, param.maturity);

    // Calculate the main logic of rebalance function.
    (long0Amount, long1Amount) = pools[param.strike][param.maturity].rebalance(param, transactionFee, protocolFee);

    // Calculate the amount of long position required by the pool.

    uint256 balanceTarget = ITimeswapV2Option(optionPair).positionOf(
      param.strike,
      param.maturity,
      address(this),
      param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long0 : TimeswapV2OptionPosition.Long1
    ) + (param.isLong0ToLong1 ? long0Amount : long1Amount);

    // Transfer the positions to the recipients.

    ITimeswapV2Option(optionPair).transferPosition(
      param.strike,
      param.maturity,
      param.to,
      param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long1 : TimeswapV2OptionPosition.Long0,
      param.isLong0ToLong1 ? long1Amount : long0Amount
    );

    // Ask the msg.sender to transfer the positions into this address.
    data = ITimeswapV2PoolRebalanceCallback(msg.sender).timeswapV2PoolRebalanceCallback(
      TimeswapV2PoolRebalanceCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        isLong0ToLong1: param.isLong0ToLong1,
        long0Amount: long0Amount,
        long1Amount: long1Amount,
        data: param.data
      })
    );

    // Check when the position balance targets are reached.

    Error.checkEnough(
      ITimeswapV2Option(optionPair).positionOf(
        param.strike,
        param.maturity,
        address(this),
        param.isLong0ToLong1 ? TimeswapV2OptionPosition.Long0 : TimeswapV2OptionPosition.Long1
      ),
      balanceTarget
    );

    lowerGuard(param.strike, param.maturity);

    emit Rebalance(param.strike, param.maturity, msg.sender, param.to, param.isLong0ToLong1, long0Amount, long1Amount);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";
import {SafeCast} from "@timeswap-labs/v2-library/contracts/SafeCast.sol";

import {DurationLibrary} from "./Duration.sol";

/// @title calculation of duration
library DurationCalculation {
  using Math for uint256;
  using SafeCast for uint256;

  /// @dev gives the duration between the current block timestamp and the last timestamp
  /// @param lastTimestamp The last timestamp
  /// @param blockTimestamp The current block timestamp
  /// @return duration The duration between the current block timestamp and the last timestamp
  function unsafeDurationFromLastTimestampToNow(
    uint96 lastTimestamp,
    uint96 blockTimestamp
  ) internal pure returns (uint96 duration) {
    duration = uint256(blockTimestamp).unsafeSub(lastTimestamp).toUint96();
  }

  /// @dev gives the duration between the maturity and the current block timestamp
  /// @param maturity The maturity of the pool
  /// @param blockTimestamp The current block timestamp
  /// @return duration The duration between the maturity and the current block timestamp
  function unsafeDurationFromNowToMaturity(
    uint256 maturity,
    uint96 blockTimestamp
  ) internal pure returns (uint96 duration) {
    duration = maturity.unsafeSub(uint256(blockTimestamp)).toUint96();
  }

  /// @dev gives the duration between the maturity and the last timestamp
  /// @param lastTimestamp The last timestamp
  /// @param maturity The maturity of the pool
  /// @return duration The duration between the maturity and the last timestamp
  function unsafeDurationFromLastTimestampToMaturity(
    uint96 lastTimestamp,
    uint256 maturity
  ) internal pure returns (uint96 duration) {
    duration = maturity.unsafeSub(lastTimestamp).toUint96();
  }

  /// @dev gives the duration between the maturity and the minimum of the last timestamp and the current block timestamp
  /// @param lastTimestamp The last timestamp
  /// @param maturity The maturity of the pool
  /// @param blockTimestamp The current block timestamp
  /// @return duration The duration between the maturity and the minimum of the last timestamp and the current block timestamp
  function unsafeDurationFromLastTimestampToNowOrMaturity(
    uint96 lastTimestamp,
    uint256 maturity,
    uint96 blockTimestamp
  ) internal pure returns (uint96 duration) {
    duration = maturity.min(blockTimestamp).unsafeSub(lastTimestamp).toUint96();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2PoolRebalanceCallbackParam} from "../../structs/CallbackParam.sol";

/// @dev The interface that needs to be implemented by a contract calling the rebalance function.
interface ITimeswapV2PoolRebalanceCallback {
  /// @dev When Long0ToLong1, require the transfer of long0 position into the pool.
  /// @dev When Long1ToLong0, require the transfer of long1 position into the pool.
  /// @dev The long0 positions or long1 positions will already be minted to the recipient.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolRebalanceCallback(
    TimeswapV2PoolRebalanceCallbackParam calldata param
  ) external returns (bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2OptionPosition} from "../enums/Position.sol";
import {TimeswapV2OptionMintParam, TimeswapV2OptionBurnParam, TimeswapV2OptionSwapParam, TimeswapV2OptionCollectParam} from "../structs/Param.sol";
import {StrikeAndMaturity} from "../structs/StrikeAndMaturity.sol";

/// @title An interface for a contract that deploys Timeswap V2 Option pair contracts
/// @notice A Timeswap V2 Option pair facilitates option mechanics between any two assets that strictly conform
/// to the ERC20 specification.
interface ITimeswapV2Option {
  /* ===== EVENT ===== */

  /// @dev Emits when a position is transferred.
  /// @param strike The strike ratio of token1 per token0 of the position.
  /// @param maturity The maturity of the position.
  /// @param from The address of the caller of the transferPosition function.
  /// @param to The address of the recipient of the position.
  /// @param position The type of position transferred. More information in the Position module.
  /// @param amount The amount of balance transferred.
  event TransferPosition(
    uint256 indexed strike,
    uint256 indexed maturity,
    address from,
    address to,
    TimeswapV2OptionPosition position,
    uint256 amount
  );

  /// @dev Emits when a mint transaction is called.
  /// @param strike The strike ratio of token1 per token0 of the option.
  /// @param maturity The maturity of the option.
  /// @param caller The address of the caller of the mint function.
  /// @param long0To The address of the recipient of long token0 position.
  /// @param long1To The address of the recipient of long token1 position.
  /// @param shortTo The address of the recipient of short position.
  /// @param token0AndLong0Amount The amount of token0 deposited and long0 minted.
  /// @param token1AndLong1Amount The amount of token1 deposited and long1 minted.
  /// @param shortAmount The amount of short minted.
  event Mint(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address long0To,
    address long1To,
    address shortTo,
    uint256 token0AndLong0Amount,
    uint256 token1AndLong1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when a burn transaction is called.
  /// @param strike The strike ratio of token1 per token0 of the option.
  /// @param maturity The maturity of the option.
  /// @param caller The address of the caller of the mint function.
  /// @param token0To The address of the recipient of token0.
  /// @param token1To The address of the recipient of token1.
  /// @param token0AndLong0Amount The amount of token0 withdrawn and long0 burnt.
  /// @param token1AndLong1Amount The amount of token1 withdrawn and long1 burnt.
  /// @param shortAmount The amount of short burnt.
  event Burn(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address token0To,
    address token1To,
    uint256 token0AndLong0Amount,
    uint256 token1AndLong1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when a swap transaction is called.
  /// @param strike The strike ratio of token1 per token0 of the option.
  /// @param maturity The maturity of the option.
  /// @param caller The address of the caller of the mint function.
  /// @param tokenTo The address of the recipient of token0 or token1.
  /// @param longTo The address of the recipient of long token0 or long token1.
  /// @param isLong0toLong1 The direction of the swap. More information in the Transaction module.
  /// @param token0AndLong0Amount If the direction is from long0 to long1, the amount of token0 withdrawn and long0 burnt.
  /// If the direction is from long1 to long0, the amount of token0 deposited and long0 minted.
  /// @param token1AndLong1Amount If the direction is from long0 to long1, the amount of token1 deposited and long1 minted.
  /// If the direction is from long1 to long0, the amount of token1 withdrawn and long1 burnt.
  event Swap(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address tokenTo,
    address longTo,
    bool isLong0toLong1,
    uint256 token0AndLong0Amount,
    uint256 token1AndLong1Amount
  );

  /// @dev Emits when a collect transaction is called.
  /// @param strike The strike ratio of token1 per token0 of the option.
  /// @param maturity The maturity of the option.
  /// @param caller The address of the caller of the mint function.
  /// @param token0To The address of the recipient of token0.
  /// @param token1To The address of the recipient of token1.
  /// @param long0AndToken0Amount The amount of token0 withdrawn.
  /// @param long1AndToken1Amount The amount of token1 withdrawn.
  /// @param shortAmount The amount of short burnt.
  event Collect(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address token0To,
    address token1To,
    uint256 long0AndToken0Amount,
    uint256 long1AndToken1Amount,
    uint256 shortAmount
  );

  /* ===== VIEW ===== */

  /// @dev Returns the factory address that deployed this contract.
  function optionFactory() external view returns (address);

  /// @dev Returns the first ERC20 token address of the pair.
  function token0() external view returns (address);

  /// @dev Returns the second ERC20 token address of the pair.
  function token1() external view returns (address);

  /// @dev Get the strike and maturity of the option in the option enumeration list.
  /// @param id The chosen index.
  function getByIndex(uint256 id) external view returns (StrikeAndMaturity memory);

  /// @dev Number of options being interacted.
  function numberOfOptions() external view returns (uint256);

  /// @dev Returns the total position of the option.
  /// @param strike The strike ratio of token1 per token0 of the position.
  /// @param maturity The maturity of the position.
  /// @param position The type of position inquired. More information in the Position module.
  /// @return balance The total position.
  function totalPosition(
    uint256 strike,
    uint256 maturity,
    TimeswapV2OptionPosition position
  ) external view returns (uint256 balance);

  /// @dev Returns the position of an owner of the option.
  /// @param strike The strike ratio of token1 per token0 of the position.
  /// @param maturity The maturity of the position.
  /// @param owner The address of the owner of the position.
  /// @param position The type of position inquired. More information in the Position module.
  /// @return balance The user position.
  function positionOf(
    uint256 strike,
    uint256 maturity,
    address owner,
    TimeswapV2OptionPosition position
  ) external view returns (uint256 balance);

  /* ===== UPDATE ===== */

  /// @dev Transfer position to another address.
  /// @param strike The strike ratio of token1 per token0 of the position.
  /// @param maturity The maturity of the position.
  /// @param to The address of the recipient of the position.
  /// @param position The type of position transferred. More information in the Position module.
  /// @param amount The amount of balance transferred.
  function transferPosition(
    uint256 strike,
    uint256 maturity,
    address to,
    TimeswapV2OptionPosition position,
    uint256 amount
  ) external;

  /// @dev Mint position.
  /// Mint long token0 position when token0 is deposited.
  /// Mint long token1 position when token1 is deposited.
  /// @dev Can only be called before the maturity of the pool.
  /// @param param The parameters for the mint function.
  /// @return token0AndLong0Amount The amount of token0 deposited and long0 minted.
  /// @return token1AndLong1Amount The amount of token1 deposited and long1 minted.
  /// @return shortAmount The amount of short minted.
  /// @return data The additional data return.
  function mint(
    TimeswapV2OptionMintParam calldata param
  )
    external
    returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data);

  /// @dev Burn short position.
  /// Withdraw token0, when long token0 is burnt.
  /// Withdraw token1, when long token1 is burnt.
  /// @dev Can only be called before the maturity of the pool.
  /// @param param The parameters for the burn function.
  /// @return token0AndLong0Amount The amount of token0 withdrawn and long0 burnt.
  /// @return token1AndLong1Amount The amount of token1 withdrawn and long1 burnt.
  /// @return shortAmount The amount of short burnt.
  function burn(
    TimeswapV2OptionBurnParam calldata param
  )
    external
    returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data);

  /// @dev If the direction is from long token0 to long token1, burn long token0 and mint equivalent long token1,
  /// also deposit token1 and withdraw token0.
  /// If the direction is from long token1 to long token0, burn long token1 and mint equivalent long token0,
  /// also deposit token0 and withdraw token1.
  /// @dev Can only be called before the maturity of the pool.
  /// @param param The parameters for the swap function.
  /// @return token0AndLong0Amount If direction is Long0ToLong1, the amount of token0 withdrawn and long0 burnt.
  /// If direction is Long1ToLong0, the amount of token0 deposited and long0 minted.
  /// @return token1AndLong1Amount If direction is Long0ToLong1, the amount of token1 deposited and long1 minted.
  /// If direction is Long1ToLong0, the amount of token1 withdrawn and long1 burnt.
  /// @return data The additional data return.
  function swap(
    TimeswapV2OptionSwapParam calldata param
  ) external returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, bytes memory data);

  /// @dev Burn short position, withdraw token0 and token1.
  /// @dev Can only be called after the maturity of the pool.
  /// @param param The parameters for the collect function.
  /// @return token0Amount The amount of token0 withdrawn.
  /// @return token1Amount The amount of token1 withdrawn.
  /// @return shortAmount The amount of short burnt.
  function collect(
    TimeswapV2OptionCollectParam calldata param
  ) external returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount, bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {IOwnableTwoSteps} from "./IOwnableTwoSteps.sol";

/// @title The interface for the contract that deploys Timeswap V2 Pool pair contracts
/// @notice The Timeswap V2 Pool Factory facilitates creation of Timeswap V2 Pool pair.
interface ITimeswapV2PoolFactory is IOwnableTwoSteps {
  /* ===== EVENT ===== */

  /// @dev Emits when a new Timeswap V2 Pool contract is created.
  /// @param caller The address of the caller of create function.
  /// @param option The address of the option contract used by the pool.
  /// @param poolPair The address of the Timeswap V2 Pool contract created.
  event Create(address indexed caller, address indexed option, address indexed poolPair);

  /* ===== VIEW ===== */

  /// @dev Returns the address of the Timeswap V2 Option factory contract utilized by Timeswap V2 Pool factory contract.
  function optionFactory() external view returns (address);

  /// @dev Returns the fixed transaction fee used by all created Timeswap V2 Pool contract.
  function transactionFee() external view returns (uint256);

  /// @dev Returns the fixed protocol fee used by all created Timeswap V2 Pool contract.
  function protocolFee() external view returns (uint256);

  /// @dev Returns the address of a Timeswap V2 Pool.
  /// @dev Returns a zero address if the Timeswap V2 Pool does not exist.
  /// @param option The address of the option contract used by the pool.
  /// @return poolPair The address of the Timeswap V2 Pool contract or a zero address.
  function get(address option) external view returns (address poolPair);

  /// @dev Returns the address of a Timeswap V2 Pool.
  /// @dev Returns a zero address if the Timeswap V2 Pool does not exist.
  /// @param token0 The address of the smaller sized address of ERC20.
  /// @param token1 The address of the larger sized address of ERC20.
  /// @return poolPair The address of the Timeswap V2 Pool contract or a zero address.
  function get(address token0, address token1) external view returns (address poolPair);

  function getByIndex(uint256 id) external view returns (address optionPair);

  function numberOfPairs() external view returns (uint256);

  /* ===== UPDATE ===== */

  /// @dev Creates a Timeswap V2 Pool based on option parameter.
  /// @dev Cannot create a duplicate Timeswap V2 Pool with the same option parameter.
  /// @param token0 The address of the smaller sized address of ERC20.
  /// @param token1 The address of the larger sized address of ERC20.
  /// @param poolPair The address of the Timeswap V2 Pool contract created.
  function create(address token0, address token1) external returns (address poolPair);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title library for optionPair utils
/// @author Timeswap Labs
library OptionPairLibrary {
  /// @dev Reverts when option address is zero.
  error ZeroOptionAddress();

  /// @dev Reverts when the pair has incorrect format.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  error InvalidOptionPair(address token0, address token1);

  /// @dev Reverts when the Timeswap V2 Option already exist.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  /// @param optionPair The address of the existed Pair contract.
  error OptionPairAlreadyExisted(address token0, address token1, address optionPair);

  /// @dev Checks if option address is not zero.
  /// @param optionPair The option pair address being inquired.
  function checkNotZeroAddress(address optionPair) internal pure {
    if (optionPair == address(0)) revert ZeroOptionAddress();
  }

  /// @dev Check if the pair tokens is in correct format.
  /// @notice Reverts if token0 is greater than or equal token1.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  function checkCorrectFormat(address token0, address token1) internal pure {
    if (token0 >= token1) revert InvalidOptionPair(token0, token1);
  }

  /// @dev Check if the pair already existed.
  /// @notice Reverts if the pair is not a zero address.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  /// @param optionPair The address of the existed Pair contract.
  function checkDoesNotExist(address token0, address token1, address optionPair) internal pure {
    if (optionPair != address(0)) revert OptionPairAlreadyExisted(token0, token1, optionPair);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title Library for errors
/// @author Timeswap Labs
/// @dev Common error messages
library Error {
  /// @dev Reverts when input is zero.
  error ZeroInput();

  /// @dev Reverts when output is zero.
  error ZeroOutput();

  /// @dev Reverts when a value cannot be zero.
  error CannotBeZero();

  /// @dev Reverts when a pool already have liquidity.
  /// @param liquidity The liquidity amount that already existed in the pool.
  error AlreadyHaveLiquidity(uint160 liquidity);

  /// @dev Reverts when a pool requires liquidity.
  error RequireLiquidity();

  /// @dev Reverts when a given address is the zero address.
  error ZeroAddress();

  /// @dev Reverts when the maturity given is not withing uint96.
  /// @param maturity The maturity being inquired.
  error IncorrectMaturity(uint256 maturity);

  /// @dev Reverts when an option of given strike and maturity is still inactive.
  /// @param strike The chosen strike.
  /// @param maturity The chosen maturity.
  error InactiveOption(uint256 strike, uint256 maturity);

  /// @dev Reverts when a pool of given strike and maturity is still inactive.
  /// @param strike The chosen strike.
  /// @param maturity The chosen maturity.
  error InactivePool(uint256 strike, uint256 maturity);

  /// @dev Reverts when a liquidity token is inactive.
  error InactiveLiquidityTokenChoice();

  /// @dev Reverts when the square root interest rate is zero.
  /// @param strike The chosen strike.
  /// @param maturity The chosen maturity.
  error ZeroSqrtInterestRate(uint256 strike, uint256 maturity);

  /// @dev Reverts when the maturity is already matured.
  /// @param maturity The maturity.
  /// @param blockTimestamp The current block timestamp.
  error AlreadyMatured(uint256 maturity, uint96 blockTimestamp);

  /// @dev Reverts when the maturity is still active.
  /// @param maturity The maturity.
  /// @param blockTimestamp The current block timestamp.
  error StillActive(uint256 maturity, uint96 blockTimestamp);

  /// @dev Token amount not received.
  /// @param minuend The amount being subtracted.
  /// @param subtrahend The amount subtracting.
  error NotEnoughReceived(uint256 minuend, uint256 subtrahend);

  /// @dev The deadline of a transaction has been reached.
  /// @param deadline The deadline set.
  error DeadlineReached(uint256 deadline);

  /// @dev Reverts when input is zero.
  function zeroInput() internal pure {
    revert ZeroInput();
  }

  /// @dev Reverts when output is zero.
  function zeroOutput() internal pure {
    revert ZeroOutput();
  }

  /// @dev Reverts when a value cannot be zero.
  function cannotBeZero() internal pure {
    revert CannotBeZero();
  }

  /// @dev Reverts when a pool already have liquidity.
  /// @param liquidity The liquidity amount that already existed in the pool.
  function alreadyHaveLiquidity(uint160 liquidity) internal pure {
    revert AlreadyHaveLiquidity(liquidity);
  }

  /// @dev Reverts when a pool requires liquidity.
  function requireLiquidity() internal pure {
    revert RequireLiquidity();
  }

  /// @dev Reverts when a given address is the zero address.
  function zeroAddress() internal pure {
    revert ZeroAddress();
  }

  /// @dev Reverts when the maturity given is not withing uint96.
  /// @param maturity The maturity being inquired.
  function incorrectMaturity(uint256 maturity) internal pure {
    revert IncorrectMaturity(maturity);
  }

  /// @dev Reverts when the maturity is already matured.
  /// @param maturity The maturity.
  /// @param blockTimestamp The current block timestamp.
  function alreadyMatured(uint256 maturity, uint96 blockTimestamp) internal pure {
    revert AlreadyMatured(maturity, blockTimestamp);
  }

  /// @dev Reverts when the maturity is still active.
  /// @param maturity The maturity.
  /// @param blockTimestamp The current block timestamp.
  function stillActive(uint256 maturity, uint96 blockTimestamp) internal pure {
    revert StillActive(maturity, blockTimestamp);
  }

  /// @dev The deadline of a transaction has been reached.
  /// @param deadline The deadline set.
  function deadlineReached(uint256 deadline) internal pure {
    revert DeadlineReached(deadline);
  }

  /// @dev Reverts when an option of given strike and maturity is still inactive.
  /// @param strike The chosen strike.
  function inactiveOptionChoice(uint256 strike, uint256 maturity) internal pure {
    revert InactiveOption(strike, maturity);
  }

  /// @dev Reverts when a pool of given strike and maturity is still inactive.
  /// @param strike The chosen strike.
  /// @param maturity The chosen maturity.
  function inactivePoolChoice(uint256 strike, uint256 maturity) internal pure {
    revert InactivePool(strike, maturity);
  }

  /// @dev Reverts when the square root interest rate is zero.
  /// @param strike The chosen strike.
  /// @param maturity The chosen maturity.
  function zeroSqrtInterestRate(uint256 strike, uint256 maturity) internal pure {
    revert ZeroSqrtInterestRate(strike, maturity);
  }

  /// @dev Reverts when a liquidity token is inactive.
  function inactiveLiquidityTokenChoice() internal pure {
    revert InactiveLiquidityTokenChoice();
  }

  /// @dev Reverts when token amount not received.
  /// @param balance The balance amount being subtracted.
  /// @param balanceTarget The amount target.
  function checkEnough(uint256 balance, uint256 balanceTarget) internal pure {
    if (balance < balanceTarget) revert NotEnoughReceived(balance, balanceTarget);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev The different input for the mint transaction.
enum TimeswapV2OptionMint {
  GivenTokensAndLongs,
  GivenShorts
}

/// @dev The different input for the burn transaction.
enum TimeswapV2OptionBurn {
  GivenTokensAndLongs,
  GivenShorts
}

/// @dev The different input for the swap transaction.
enum TimeswapV2OptionSwap {
  GivenToken0AndLong0,
  GivenToken1AndLong1
}

/// @dev The different input for the collect transaction.
enum TimeswapV2OptionCollect {
  GivenShort,
  GivenToken0,
  GivenToken1
}

/// @title library for transaction checks
/// @author Timeswap Labs
/// @dev Helper functions for the all enums in this module.
library TransactionLibrary {
  /// @dev Reverts when the given type of transaction is invalid.
  error InvalidTransaction();

  /// @dev checks that the given input is correct.
  /// @param transaction the mint transaction input.
  function check(TimeswapV2OptionMint transaction) internal pure {
    if (uint256(transaction) >= 2) revert InvalidTransaction();
  }

  /// @dev checks that the given input is correct.
  /// @param transaction the burn transaction input.
  function check(TimeswapV2OptionBurn transaction) internal pure {
    if (uint256(transaction) >= 2) revert InvalidTransaction();
  }

  /// @dev checks that the given input is correct.
  /// @param transaction the swap transaction input.
  function check(TimeswapV2OptionSwap transaction) internal pure {
    if (uint256(transaction) >= 2) revert InvalidTransaction();
  }

  /// @dev checks that the given input is correct.
  /// @param transaction the collect transaction input.
  function check(TimeswapV2OptionCollect transaction) internal pure {
    if (uint256(transaction) >= 3) revert InvalidTransaction();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";
import {Ownership} from "@timeswap-labs/v2-library/contracts/Ownership.sol";

import {IOwnableTwoSteps} from "../interfaces/IOwnableTwoSteps.sol";

/// @dev contract for ownable implementation with a safety two step owner transfership.
contract OwnableTwoSteps is IOwnableTwoSteps {
  using Ownership for address;

  /// @dev The current owner of the contract.
  address public override owner;
  /// @dev The pending owner of the contract. Is zero when none is pending.
  address public override pendingOwner;

  constructor(address chosenOwner) {
    owner = chosenOwner;
  }

  /// @inheritdoc IOwnableTwoSteps
  function setPendingOwner(address chosenPendingOwner) external override {
    Ownership.checkIfOwner(owner);

    if (chosenPendingOwner == address(0)) Error.zeroAddress();
    chosenPendingOwner.checkIfAlreadyOwner(owner);

    pendingOwner = chosenPendingOwner;

    emit SetOwner(pendingOwner);
  }

  /// @inheritdoc IOwnableTwoSteps
  function acceptOwner() external override {
    msg.sender.checkIfPendingOwner(pendingOwner);

    owner = msg.sender;
    delete pendingOwner;

    emit AcceptOwner(msg.sender);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {StrikeConversion} from "@timeswap-labs/v2-library/contracts/StrikeConversion.sol";
import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";

import {FeeCalculation} from "./FeeCalculation.sol";
import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";

/// @title Constant Sum Library that returns the Constant Sum given certain parameters
library ConstantSum {
  using Math for uint256;

  /// @dev Calculate long amount out.
  /// @param strike The strike of the pool.
  /// @param longAmountIn The long amount to be deposited.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false.
  function calculateGivenLongIn(
    uint256 strike,
    uint256 longAmountIn,
    uint256 transactionFee,
    bool isLong0ToLong1
  ) internal pure returns (uint256 longAmountOut, uint256 longFees) {
    longAmountOut = StrikeConversion.convert(longAmountIn, strike, isLong0ToLong1, false);
    longFees = FeeCalculation.getFeesRemoval(longAmountOut, transactionFee);
    longAmountOut = longAmountOut.unsafeSub(longFees);
  }

  /// @dev Calculate long amount in.
  /// @param strike The strike of the pool.
  /// @param longAmountOut The long amount to be withdrawn.
  /// @param transactionFee The fee that will be adjusted in the transaction.
  /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false.
  function calculateGivenLongOut(
    uint256 strike,
    uint256 longAmountOut,
    uint256 transactionFee,
    bool isLong0ToLong1
  ) internal pure returns (uint256 longAmountIn, uint256 longFees) {
    longFees = FeeCalculation.getFeesAdditional(longAmountOut, transactionFee);
    longAmountIn = StrikeConversion.convert(longAmountOut + longFees, strike, !isLong0ToLong1, true);
  }

  /// @dev Calculate long amount in without adjusting for fees.
  /// @param strike The strike of the pool.
  /// @param longAmountOut The long amount to be withdrawn.
  /// @param isLong0ToLong1 Deposit long0 positions when true. Deposit long1 positions when false.
  function calculateGivenLongOutAlreadyAdjustFees(
    uint256 strike,
    uint256 longAmountOut,
    bool isLong0ToLong1
  ) internal pure returns (uint256 longAmountIn) {
    longAmountIn = StrikeConversion.convert(longAmountOut, strike, !isLong0ToLong1, true);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev The different kind of mint transactions.
enum TimeswapV2PoolMint {
  GivenLiquidity,
  GivenLong,
  GivenShort,
  GivenLarger
}

/// @dev The different kind of burn transactions.
enum TimeswapV2PoolBurn {
  GivenLiquidity,
  GivenLong,
  GivenShort,
  GivenSmaller
}

/// @dev The different kind of deleverage transactions.
enum TimeswapV2PoolDeleverage {
  GivenDeltaSqrtInterestRate,
  GivenLong,
  GivenShort,
  GivenSum
}

/// @dev The different kind of leverage transactions.
enum TimeswapV2PoolLeverage {
  GivenDeltaSqrtInterestRate,
  GivenLong,
  GivenShort,
  GivenSum
}

/// @dev The different kind of rebalance transactions.
enum TimeswapV2PoolRebalance {
  GivenLong0,
  GivenLong1
}

library TransactionLibrary {
  /// @dev Reverts when the given type of transaction is invalid.
  error InvalidTransaction();

  /// @dev Function to revert with the error InvalidTransaction.
  function invalidTransaction() internal pure {
    revert InvalidTransaction();
  }

  /// @dev Sanity checks for the mint parameters.
  function check(TimeswapV2PoolMint transaction) internal pure {
    if (uint256(transaction) >= 4) revert InvalidTransaction();
  }

  /// @dev Sanity checks for the burn parameters.
  function check(TimeswapV2PoolBurn transaction) internal pure {
    if (uint256(transaction) >= 4) revert InvalidTransaction();
  }

  /// @dev Sanity checks for the deleverage parameters.
  function check(TimeswapV2PoolDeleverage transaction) internal pure {
    if (uint256(transaction) >= 4) revert InvalidTransaction();
  }

  /// @dev Sanity checks for the leverage parameters.
  function check(TimeswapV2PoolLeverage transaction) internal pure {
    if (uint256(transaction) >= 4) revert InvalidTransaction();
  }

  /// @dev Sanity checks for the rebalance parameters.
  function check(TimeswapV2PoolRebalance transaction) internal pure {
    if (uint256(transaction) >= 2) revert InvalidTransaction();
  }
}

File 18 of 39 : StrikeAndMaturity.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev A data with strike and maturity data.
/// @param strike The strike.
/// @param maturity The maturity.
struct StrikeAndMaturity {
  uint256 strike;
  uint256 maturity;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title Prevents delegatecall to a contract
/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract.
contract NoDelegateCall {
  /* ===== ERROR ===== */

  /// @dev Reverts when called using delegatecall.
  error CannotBeDelegateCalled();

  /* ===== MODEL ===== */

  /// @dev The original address of this contract.
  address private immutable original;

  /* ===== INIT ===== */

  constructor() {
    // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode.
    // In other words, this variable won't change when it's checked at runtime.
    original = address(this);
  }

  /* ===== MODIFIER ===== */

  /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method,
  /// and the use of immutable means the address bytes are copied in every place the modifier is used.
  function checkNotDelegateCall() private view {
    if (address(this) != original) revert CannotBeDelegateCalled();
  }

  /// @notice Prevents delegatecall into the modified method
  modifier noDelegateCall() {
    checkNotDelegateCall();
    _;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2PoolDeleverageChoiceCallbackParam, TimeswapV2PoolDeleverageCallbackParam} from "../../structs/CallbackParam.sol";

/// @dev The interface that needs to be implemented by a contract calling the deleverage function.
interface ITimeswapV2PoolDeleverageCallback {
  /// @dev Returns the amount of long0 position and long1 positions chosen to be deposited to the pool.
  /// @notice The StrikeConversion.combine of long0 position and long1 position must be greater than or equal to long amount.
  /// @dev The short positions will already be minted to the recipient.
  /// @return long0Amount Amount of long0 position to be deposited.
  /// @return long1Amount Amount of long1 position to be deposited.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolDeleverageChoiceCallback(
    TimeswapV2PoolDeleverageChoiceCallbackParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);

  /// @dev Require the transfer of long0 position and long1 position into the pool.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolDeleverageCallback(
    TimeswapV2PoolDeleverageCallbackParam calldata param
  ) external returns (bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";

import {FeeCalculation} from "../libraries/FeeCalculation.sol";

/// @param liquidity The amount of liquidity owned.
/// @param long0FeeGrowth The long0 position fee growth stored when the user entered the positions.
/// @param long1FeeGrowth The long1 position fee growth stored when the user entered the positions.
/// @param shortFeeGrowth The short position fee growth stored when the user entered the positions.
/// @param long0Fees The stored amount of long0 position fees owned.
/// @param long1Fees The stored amount of long1 position fees owned.
/// @param shortFees The stored amount of short position fees owned.
struct LiquidityPosition {
  uint160 liquidity;
  uint256 long0FeeGrowth;
  uint256 long1FeeGrowth;
  uint256 shortFeeGrowth;
  uint256 shortReturnedGrowth;
  uint256 long0Fees;
  uint256 long1Fees;
  uint256 shortFees;
  uint256 shortReturned;
}

/// @title library for liquidity position utils
/// @author Timeswap Labs
library LiquidityPositionLibrary {
  using Math for uint256;

  /// @dev Get the total fees earned and short returned by the owner.
  /// @param liquidityPosition The liquidity position of the owner.
  /// @param long0FeeGrowth The current global long0 position fee growth to be compared.
  /// @param long1FeeGrowth The current global long1 position fee growth to be compared.
  /// @param shortFeeGrowth The current global short position fee growth to be compared.
  /// @param shortReturnedGrowth The current glocal short position returned growth to be compared
  /// @return long0Fees The long0 fees owned.
  /// @return long1Fees The long1 fees owned.
  /// @return shortFees The short fees owned.
  /// @return shortReturned The short returned owned.
  function feesEarnedAndShortReturnedOf(
    LiquidityPosition memory liquidityPosition,
    uint256 long0FeeGrowth,
    uint256 long1FeeGrowth,
    uint256 shortFeeGrowth,
    uint256 shortReturnedGrowth
  ) internal pure returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    uint160 liquidity = liquidityPosition.liquidity;

    long0Fees = liquidityPosition.long0Fees.unsafeAdd(
      FeeCalculation.getFees(liquidity, liquidityPosition.long0FeeGrowth, long0FeeGrowth)
    );
    long1Fees = liquidityPosition.long1Fees.unsafeAdd(
      FeeCalculation.getFees(liquidity, liquidityPosition.long1FeeGrowth, long1FeeGrowth)
    );
    shortFees = liquidityPosition.shortFees.unsafeAdd(
      FeeCalculation.getFees(liquidity, liquidityPosition.shortFeeGrowth, shortFeeGrowth)
    );
    shortReturned = liquidityPosition.shortReturned.unsafeAdd(
      FeeCalculation.getFees(liquidityPosition.liquidity, liquidityPosition.shortReturnedGrowth, shortReturnedGrowth)
    );
  }

  /// @dev Update the liquidity position after collectTransactionFees, mint and/or burn functions.
  /// @param liquidityPosition The liquidity position of the owner.
  /// @param long0FeeGrowth The current global long0 position fee growth to be compared.
  /// @param long1FeeGrowth The current global long1 position fee growth to be compared.
  /// @param shortFeeGrowth The current global short position fee growth to be compared.
  /// @param shortReturnedGrowth The current global short position returned growth to be compared.
  function update(
    LiquidityPosition storage liquidityPosition,
    uint256 long0FeeGrowth,
    uint256 long1FeeGrowth,
    uint256 shortFeeGrowth,
    uint256 shortReturnedGrowth
  ) internal {
    uint160 liquidity = liquidityPosition.liquidity;

    if (liquidity != 0) {
      liquidityPosition.long0Fees += FeeCalculation.getFees(
        liquidity,
        liquidityPosition.long0FeeGrowth,
        long0FeeGrowth
      );
      liquidityPosition.long1Fees += FeeCalculation.getFees(
        liquidity,
        liquidityPosition.long1FeeGrowth,
        long1FeeGrowth
      );
      liquidityPosition.shortFees += FeeCalculation.getFees(
        liquidity,
        liquidityPosition.shortFeeGrowth,
        shortFeeGrowth
      );
      liquidityPosition.shortReturned += FeeCalculation.getFees(
        liquidity,
        liquidityPosition.shortReturnedGrowth,
        shortReturnedGrowth
      );
    }

    liquidityPosition.long0FeeGrowth = long0FeeGrowth;
    liquidityPosition.long1FeeGrowth = long1FeeGrowth;
    liquidityPosition.shortFeeGrowth = shortFeeGrowth;
    liquidityPosition.shortReturnedGrowth = shortReturnedGrowth;
  }

  /// @dev updates the liquidity position by the given amount
  /// @param liquidityPosition the position that is to be updated
  /// @param liquidityAmount the amount that is to be incremented in the position
  function mint(LiquidityPosition storage liquidityPosition, uint160 liquidityAmount) internal {
    liquidityPosition.liquidity += liquidityAmount;
  }

  /// @dev updates the liquidity position by the given amount
  /// @param liquidityPosition the position that is to be updated
  /// @param liquidityAmount the amount that is to be decremented in the position
  function burn(LiquidityPosition storage liquidityPosition, uint160 liquidityAmount) internal {
    liquidityPosition.liquidity -= liquidityAmount;
  }

  /// @dev function to collect the transaction fees accrued for a given liquidity position
  /// @param liquidityPosition the liquidity position that whose fees is collected
  /// @param long0FeesRequested the long0 fees requested
  /// @param long1FeesRequested the long1 fees requested
  /// @param shortFeesRequested the short fees requested
  /// @param shortReturnedRequested the short returned requested
  /// @return long0Fees the long0 fees collected
  /// @return long1Fees the long1 fees collected
  /// @return shortFees the short fees collected
  /// @return shortReturned the short returned collected
  function collectTransactionFeesAndShortReturned(
    LiquidityPosition storage liquidityPosition,
    uint256 long0FeesRequested,
    uint256 long1FeesRequested,
    uint256 shortFeesRequested,
    uint256 shortReturnedRequested
  ) internal returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    if (long0FeesRequested >= liquidityPosition.long0Fees) {
      long0Fees = liquidityPosition.long0Fees;
      liquidityPosition.long0Fees = 0;
    } else {
      long0Fees = long0FeesRequested;
      liquidityPosition.long0Fees = liquidityPosition.long0Fees.unsafeSub(long0FeesRequested);
    }

    if (long1FeesRequested >= liquidityPosition.long1Fees) {
      long1Fees = liquidityPosition.long1Fees;
      liquidityPosition.long1Fees = 0;
    } else {
      long1Fees = long1FeesRequested;
      liquidityPosition.long1Fees = liquidityPosition.long1Fees.unsafeSub(long1FeesRequested);
    }

    if (shortFeesRequested >= liquidityPosition.shortFees) {
      shortFees = liquidityPosition.shortFees;
      liquidityPosition.shortFees = 0;
    } else {
      shortFees = shortFeesRequested;
      liquidityPosition.shortFees = liquidityPosition.shortFees.unsafeSub(shortFeesRequested);
    }

    if (shortReturnedRequested >= liquidityPosition.shortReturned) {
      shortReturned = liquidityPosition.shortReturned;
      liquidityPosition.shortReturned = 0;
    } else {
      shortReturned = shortReturnedRequested;
      liquidityPosition.shortReturned = liquidityPosition.shortReturned.unsafeSub(shortReturnedRequested);
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title Libary for ownership
/// @author Timeswap Labs
library Ownership {
  /// @dev Reverts when the caller is not the owner.
  /// @param caller The caller of the function that is not the owner.
  /// @param owner The actual owner.
  error NotTheOwner(address caller, address owner);

  /// @dev reverts when the caller is already the owner.
  /// @param owner The owner.
  error AlreadyTheOwner(address owner);

  /// @dev revert when the caller is not the pending owner.
  /// @param caller The caller of the function that is not the pending owner.
  /// @param pendingOwner The actual pending owner.
  error NotThePendingOwner(address caller, address pendingOwner);

  /// @dev checks if the caller is the owner.
  /// @notice Reverts when the msg.sender is not the owner.
  /// @param owner The owner address.
  function checkIfOwner(address owner) internal view {
    if (msg.sender != owner) revert NotTheOwner(msg.sender, owner);
  }

  /// @dev checks if the caller is already the owner.
  /// @notice Reverts when the chosen pending owner is already the owner.
  /// @param chosenPendingOwner The chosen pending owner.
  /// @param owner The current actual owner.
  function checkIfAlreadyOwner(address chosenPendingOwner, address owner) internal pure {
    if (chosenPendingOwner == owner) revert AlreadyTheOwner(owner);
  }

  /// @dev checks if the caller is the pending owner.
  /// @notice Reverts when the caller is not the pending owner.
  /// @param caller The address of the caller.
  /// @param pendingOwner The current pending owner.
  function checkIfPendingOwner(address caller, address pendingOwner) internal pure {
    if (caller != pendingOwner) revert NotThePendingOwner(caller, pendingOwner);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title Library for safecasting
/// @author Timeswap Labs
library SafeCast {
  /// @dev Reverts when overflows over uint16.
  error Uint16Overflow();

  /// @dev Reverts when overflows over uint96.
  error Uint96Overflow();

  /// @dev Reverts when overflows over uint160.
  error Uint160Overflow();

  /// @dev Safely cast a uint256 number to uint16.
  /// @dev Reverts when number is greater than uint16.
  /// @param value The uint256 number to be safecasted.
  /// @param result The uint16 result.
  function toUint16(uint256 value) internal pure returns (uint16 result) {
    if (value > type(uint16).max) revert Uint16Overflow();
    result = uint16(value);
  }

  /// @dev Safely cast a uint256 number to uint96.
  /// @dev Reverts when number is greater than uint96.
  /// @param value The uint256 number to be safecasted.
  /// @param result The uint96 result.
  function toUint96(uint256 value) internal pure returns (uint96 result) {
    if (value > type(uint96).max) revert Uint96Overflow();
    result = uint96(value);
  }

  /// @dev Safely cast a uint256 number to uint160.
  /// @dev Reverts when number is greater than uint160.
  /// @param value The uint256 number to be safecasted.
  /// @param result The uint160 result.
  function toUint160(uint256 value) internal pure returns (uint160 result) {
    if (value > type(uint160).max) revert Uint160Overflow();
    result = uint160(value);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {FullMath} from "./FullMath.sol";

/// @title library for converting strike prices.
/// @dev When strike is greater than uint128, the base token is denominated as token0 (which is the smaller address token).
/// @dev When strike is uint128, the base token is denominated as token1 (which is the larger address).
library StrikeConversion {
  /// @dev When zeroToOne, converts a number in multiple of strike.
  /// @dev When oneToZero, converts a number in multiple of 1 / strike.
  /// @param amount The amount to be converted.
  /// @param strike The strike multiple conversion.
  /// @param zeroToOne ZeroToOne if it is true. OneToZero if it is false.
  /// @param roundUp Round up the result when true. Round down if false.
  function convert(uint256 amount, uint256 strike, bool zeroToOne, bool roundUp) internal pure returns (uint256) {
    return
      zeroToOne
        ? FullMath.mulDiv(amount, strike, uint256(1) << 128, roundUp)
        : FullMath.mulDiv(amount, uint256(1) << 128, strike, roundUp);
  }

  /// @dev When toOne, converts a base denomination to token1 denomination.
  /// @dev When toZero, converts a base denomination to token0 denomination.
  /// @param amount The amount ot be converted. Token0 amount when zeroToOne. Token1 amount when oneToZero.
  /// @param strike The strike multiple conversion.
  /// @param toOne ToOne if it is true, ToZero if it is false.
  /// @param roundUp Round up the result when true. Round down if false.
  function turn(uint256 amount, uint256 strike, bool toOne, bool roundUp) internal pure returns (uint256) {
    return
      strike > type(uint128).max
        ? (toOne ? convert(amount, strike, true, roundUp) : amount)
        : (toOne ? amount : convert(amount, strike, false, roundUp));
  }

  /// @dev Combine and add token0Amount and token1Amount into base token amount.
  /// @param amount0 The token0 amount to be combined.
  /// @param amount1 The token1 amount to be combined.
  /// @param strike The strike multiple conversion.
  /// @param roundUp Round up the result when true. Round down if false.
  function combine(uint256 amount0, uint256 amount1, uint256 strike, bool roundUp) internal pure returns (uint256) {
    return
      strike > type(uint128).max
        ? amount0 + convert(amount1, strike, false, roundUp)
        : amount1 + convert(amount0, strike, true, roundUp);
  }

  /// @dev When zeroToOne, given a larger base amount, and token0 amount, get the difference token1 amount.
  /// @dev When oneToZero, given a larger base amount, and toekn1 amount, get the difference token0 amount.
  /// @param base The larger base amount.
  /// @param amount The token0 amount when zeroToOne, the token1 amount when oneToZero.
  /// @param strike The strike multiple conversion.
  /// @param zeroToOne ZeroToOne if it is true. OneToZero if it is false.
  /// @param roundUp Round up the result when true. Round down if false.
  function dif(
    uint256 base,
    uint256 amount,
    uint256 strike,
    bool zeroToOne,
    bool roundUp
  ) internal pure returns (uint256) {
    return
      strike > type(uint128).max
        ? (zeroToOne ? convert(base - amount, strike, true, roundUp) : base - convert(amount, strike, false, !roundUp))
        : (zeroToOne ? base - convert(amount, strike, true, !roundUp) : convert(base - amount, strike, false, roundUp));
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2PoolLeverageChoiceCallbackParam, TimeswapV2PoolLeverageCallbackParam} from "../../structs/CallbackParam.sol";

/// @dev The interface that needs to be implemented by a contract calling the leverage function.
interface ITimeswapV2PoolLeverageCallback {
  /// @dev Returns the amount of long0 position and long1 positions chosen to be withdrawn.
  /// @notice The StrikeConversion.combine of long0 position and long1 position must be less than or equal to long amount.
  /// @dev The long0 positions and long1 positions will already be minted to the recipients.
  /// @return long0Amount Amount of long0 position to be withdrawn.
  /// @return long1Amount Amount of long1 position to be withdrawn.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolLeverageChoiceCallback(
    TimeswapV2PoolLeverageChoiceCallbackParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);

  /// @dev Require the transfer of short position into the pool.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolLeverageCallback(
    TimeswapV2PoolLeverageCallbackParam calldata param
  ) external returns (bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {SafeCast} from "@timeswap-labs/v2-library/contracts/SafeCast.sol";
import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";
import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";

import {ITimeswapV2Option} from "@timeswap-labs/v2-option/contracts/interfaces/ITimeswapV2Option.sol";

import {TimeswapV2OptionPosition} from "@timeswap-labs/v2-option/contracts/enums/Position.sol";

import {StrikeConversion} from "@timeswap-labs/v2-library/contracts/StrikeConversion.sol";

import {DurationCalculation} from "../libraries/DurationCalculation.sol";
import {DurationWeight} from "../libraries/DurationWeight.sol";
import {ConstantProduct} from "../libraries/ConstantProduct.sol";
import {ConstantSum} from "../libraries/ConstantSum.sol";
import {FeeCalculation} from "../libraries/FeeCalculation.sol";

import {ITimeswapV2PoolMintCallback} from "../interfaces/callbacks/ITimeswapV2PoolMintCallback.sol";
import {ITimeswapV2PoolBurnCallback} from "../interfaces/callbacks/ITimeswapV2PoolBurnCallback.sol";
import {ITimeswapV2PoolDeleverageCallback} from "../interfaces/callbacks/ITimeswapV2PoolDeleverageCallback.sol";
import {ITimeswapV2PoolLeverageCallback} from "../interfaces/callbacks/ITimeswapV2PoolLeverageCallback.sol";

import {LiquidityPosition, LiquidityPositionLibrary} from "./LiquidityPosition.sol";

import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "../enums/Transaction.sol";

import {TimeswapV2PoolCollectProtocolFeesParam, TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam} from "./Param.sol";
import {TimeswapV2PoolMintChoiceCallbackParam, TimeswapV2PoolBurnChoiceCallbackParam, TimeswapV2PoolDeleverageChoiceCallbackParam, TimeswapV2PoolLeverageChoiceCallbackParam} from "./CallbackParam.sol";

/// @param liquidity The current total liquidity of the pool. Follows UQ 112.48
/// @param lastTimestamp The last block timestamp the pool was interacted with.
/// @param sqrtInterestRate The current square root interest rate of the pool. Follows UQ16.144.
/// @param long0Balance The current amount of long0 positions in the pool.
/// @param long1Balance The current amount of long1 positions in the pool.
/// @param long0FeeGrowth The global long0 position fee growth the last time the pool was interacted with.
/// @param long1FeeGrowth The global long1 position fee growth the last time the pool was interacted with.
/// @param shortFeeGrowth The global short position fee growth the last time the pool was interacted with.
/// @param long0ProtocolFees The amount of long0 position protocol fees earned.
/// @param long1ProtocolFees The amount of long1 position protocol fees earned.
/// @param shortProtocolFees The amount of short position protocol fees earned.
/// @param liquidityPositions The mapping of liquidity positions owned by liquidity providers.
struct Pool {
  uint160 liquidity;
  uint96 lastTimestamp;
  uint160 sqrtInterestRate;
  uint256 long0Balance;
  uint256 long1Balance;
  uint256 long0FeeGrowth;
  uint256 long1FeeGrowth;
  uint256 shortFeeGrowth;
  uint256 shortReturnedGrowth;
  uint256 long0ProtocolFees;
  uint256 long1ProtocolFees;
  uint256 shortProtocolFees;
  mapping(address => LiquidityPosition) liquidityPositions;
}

/// @title library for pool core
/// @author Timeswap Labs
library PoolLibrary {
  using LiquidityPositionLibrary for LiquidityPosition;
  using Math for uint256;
  using SafeCast for uint256;

  /// @dev It calculates the global fee growth, which is fee increased per unit of liquidity token.
  /// @param pool The state of the pool.
  /// @return long0FeeGrowth The global fee increased per unit of liquidity token for long0.
  /// @return long1FeeGrowth The global fee increased per unit of liquidity token for long1.
  /// @return shortFeeGrowth The global fee increased per unit of liquidity token for short.
  /// @return shortReturnedGrowth The global short returned increased per unit of liquidity token for short returned.
  function feesEarnedAndShortReturnedGrowth(
    Pool storage pool,
    uint256 maturity,
    uint96 blockTimestamp
  )
    external
    view
    returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth)
  {
    long0FeeGrowth = pool.long0FeeGrowth;
    long1FeeGrowth = pool.long1FeeGrowth;
    shortFeeGrowth = pool.shortFeeGrowth;
    shortReturnedGrowth = pool.shortReturnedGrowth;

    if (pool.liquidity != 0) {
      if (maturity > blockTimestamp) {
        (, shortReturnedGrowth) = updateDurationWeight(
          pool.liquidity,
          pool.sqrtInterestRate,
          pool.shortReturnedGrowth,
          DurationCalculation.unsafeDurationFromLastTimestampToNow(pool.lastTimestamp, blockTimestamp),
          blockTimestamp
        );
      } else if (pool.lastTimestamp < maturity) {
        (, shortReturnedGrowth) = updateDurationWeight(
          pool.liquidity,
          pool.sqrtInterestRate,
          pool.shortReturnedGrowth,
          DurationCalculation.unsafeDurationFromLastTimestampToMaturity(pool.lastTimestamp, maturity),
          blockTimestamp
        );
      }
    }
  }

  /// @param pool The state of the pool.
  /// @param owner The address to query the fees earned of.
  /// @return long0Fees The amount of long0 fees owned by the given address.
  /// @return long1Fees The amount of long1 fees owned by the given address.
  /// @return shortFees The amount of short fees owned by the given address.
  /// @return shortReturned The amount of short returned owned by the given address.
  function feesEarnedAndShortReturnedOf(
    Pool storage pool,
    uint256 maturity,
    address owner,
    uint96 blockTimestamp
  ) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    uint256 shortReturnedGrowth = pool.shortReturnedGrowth;
    if (pool.liquidity != 0) {
      if (maturity > blockTimestamp) {
        (, shortReturnedGrowth) = updateDurationWeight(
          pool.liquidity,
          pool.sqrtInterestRate,
          pool.shortReturnedGrowth,
          DurationCalculation.unsafeDurationFromLastTimestampToNow(pool.lastTimestamp, blockTimestamp),
          blockTimestamp
        );
      } else if (pool.lastTimestamp < maturity) {
        (, shortReturnedGrowth) = updateDurationWeight(
          pool.liquidity,
          pool.sqrtInterestRate,
          pool.shortReturnedGrowth,
          DurationCalculation.unsafeDurationFromLastTimestampToMaturity(pool.lastTimestamp, maturity),
          blockTimestamp
        );
      }
    }

    return
      pool.liquidityPositions[owner].feesEarnedAndShortReturnedOf(
        pool.long0FeeGrowth,
        pool.long1FeeGrowth,
        pool.shortFeeGrowth,
        shortReturnedGrowth
      );
  }

  /// @param pool The state of the pool.
  /// @return long0ProtocolFees The amount of long0 protocol fees owned by the owner of the factory contract.
  /// @return long1ProtocolFees The amount of long1 protocol fees owned by the owner of the factory contract.
  /// @return shortProtocolFees The amount of short protocol fees owned by the owner of the factory contract.
  function protocolFeesEarned(
    Pool storage pool
  ) external view returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees) {
    return (pool.long0ProtocolFees, pool.long1ProtocolFees, pool.shortProtocolFees);
  }

  /// @dev Returns the amount of long0 and long1 adjusted for the protocol and transaction fee.
  /// @param pool The state of the pool.
  /// @return long0Amount The amount of long0 in the pool, adjusted for the protocol and transaction fee.
  /// @return long1Amount The amount of long1 in the pool, adjusted for the protocol and transaction fee.
  function totalLongBalanceAdjustFees(
    Pool storage pool,
    uint256 transactionFee
  ) external view returns (uint256 long0Amount, uint256 long1Amount) {
    long0Amount = FeeCalculation.removeFees(pool.long0Balance, transactionFee);
    long1Amount = FeeCalculation.removeFees(pool.long1Balance, transactionFee);
  }

  /// @dev Returns the amount of sum of long0 and long1 converted to base denomination in the pool.
  /// @dev Returns the amount of short positions in the pool.
  /// @param pool The state of the pool.
  /// @return longAmount The amount of sum of long0 and long1 converted to base denomination in the pool.
  /// @return shortAmount The amount of short in the pool.
  function totalPositions(
    Pool storage pool,
    uint256 maturity,
    uint96 blockTimestamp
  ) external view returns (uint256 longAmount, uint256 shortAmount) {
    longAmount = ConstantProduct.getLong(pool.liquidity, pool.sqrtInterestRate, false);
    shortAmount = maturity > blockTimestamp
      ? ConstantProduct.getShort(
        pool.liquidity,
        pool.sqrtInterestRate,
        DurationCalculation.unsafeDurationFromNowToMaturity(maturity, blockTimestamp),
        false
      )
      : 0;
  }

  /// @dev Move short positions to short fee growth due to duration of the pool decreasing as time moves forward.
  /// @param liquidity The liquidity in the pool.
  /// @param rate The square root interest rate in the pool.
  /// @param shortReturnedGrowth The previous short returned growth from last transaction in the pool.
  /// @param duration The duration time of the pool.
  /// @param blockTimestamp The block timestamp.
  /// @return newLastTimestamp The new current last timestamp.
  /// @return newShortReturnedGrowth The newly updated short fee growth.
  function updateDurationWeight(
    uint160 liquidity,
    uint160 rate,
    uint256 shortReturnedGrowth,
    uint96 duration,
    uint96 blockTimestamp
  ) private pure returns (uint96 newLastTimestamp, uint256 newShortReturnedGrowth) {
    newLastTimestamp = blockTimestamp;
    newShortReturnedGrowth = DurationWeight.update(
      liquidity,
      shortReturnedGrowth,
      ConstantProduct.getShort(liquidity, rate, duration, false)
    );
  }

  /// @dev Move short positions to short fee growth due to duration of the pool decreasing as time moves forward when pool is before maturity.
  /// @param pool The state of the pool.
  /// @param blockTimestamp The block timestamp.
  function updateDurationWeightBeforeMaturity(Pool storage pool, uint96 blockTimestamp) private {
    if (pool.lastTimestamp < blockTimestamp)
      (pool.lastTimestamp, pool.shortReturnedGrowth) = updateDurationWeight(
        pool.liquidity,
        pool.sqrtInterestRate,
        pool.shortReturnedGrowth,
        DurationCalculation.unsafeDurationFromLastTimestampToNow(pool.lastTimestamp, blockTimestamp),
        blockTimestamp
      );
  }

  /// @dev Move short positions to short fee growth due to duration of the pool decreasing as time moves forward when pool is after maturity.
  /// @param pool The state of the pool.
  /// @param maturity The maturity of the pool
  /// @param blockTimestamp The block timestamp.
  function updateDurationWeightAfterMaturity(Pool storage pool, uint256 maturity, uint96 blockTimestamp) private {
    (pool.lastTimestamp, pool.shortReturnedGrowth) = updateDurationWeight(
      pool.liquidity,
      pool.sqrtInterestRate,
      pool.shortReturnedGrowth,
      DurationCalculation.unsafeDurationFromLastTimestampToMaturity(pool.lastTimestamp, maturity),
      blockTimestamp
    );
  }

  /// @dev Transfer liquidity positions to another address.
  /// @notice Does not transfer the transaction fees earned by the sender.
  /// @param pool The state of the pool.
  /// @param to The recipient of the liquidity positions.
  /// @param liquidityAmount The amount of liquidity positions transferred.
  /// @param blockTimestamp The current block timestamp.
  function transferLiquidity(
    Pool storage pool,
    uint256 maturity,
    address to,
    uint160 liquidityAmount,
    uint96 blockTimestamp
  ) external {
    // Update the state of the pool first for the short fee growth.
    if (pool.liquidity != 0) {
      if (maturity > blockTimestamp) updateDurationWeightBeforeMaturity(pool, blockTimestamp);
      else if (pool.lastTimestamp < maturity) updateDurationWeightAfterMaturity(pool, maturity, blockTimestamp);
    }

    // Update the fee growth and fees of msg.sender.
    LiquidityPosition storage liquidityPosition = pool.liquidityPositions[msg.sender];

    liquidityPosition.update(pool.long0FeeGrowth, pool.long1FeeGrowth, pool.shortFeeGrowth, pool.shortReturnedGrowth);
    liquidityPosition.burn(liquidityAmount);

    // Update the fee growth and fees of recipient.
    LiquidityPosition storage newLiquidityPosition = pool.liquidityPositions[to];

    newLiquidityPosition.update(
      pool.long0FeeGrowth,
      pool.long1FeeGrowth,
      pool.shortFeeGrowth,
      pool.shortReturnedGrowth
    );
    newLiquidityPosition.mint(liquidityAmount);
  }

  /// @dev initializes the pool with the given parameters.
  /// @param pool The state of the pool.
  /// @param rate The square root of the interest rate of the pool.
  function initialize(Pool storage pool, uint160 rate) external {
    if (pool.liquidity != 0) Error.alreadyHaveLiquidity(pool.liquidity);
    pool.sqrtInterestRate = rate;
  }

  /// @dev Collects the protocol fees of the pool.
  /// @dev only protocol owner can call this function.
  /// @dev if the owner enters an amount which is greater than the fee amount they have earned, withdraw only the amount they have.
  /// @param pool The state of the pool.
  /// @param long0Requested The maximum amount of long0 positions wanted.
  /// @param long1Requested The maximum amount of long1 positions wanted.
  /// @param shortRequested The maximum amount of short positions wanted.
  /// @return long0Amount The amount of long0 collected.
  /// @return long1Amount The amount of long1 collected.
  /// @return shortAmount The amount of short collected.
  function collectProtocolFees(
    Pool storage pool,
    uint256 long0Requested,
    uint256 long1Requested,
    uint256 shortRequested
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount) {
    if (long0Requested >= pool.long0ProtocolFees) {
      long0Amount = pool.long0ProtocolFees;
      pool.long0ProtocolFees = 0;
    } else {
      long0Amount = long0Requested;
      pool.long0ProtocolFees = pool.long0ProtocolFees.unsafeSub(long0Requested);
    }

    if (long1Requested >= pool.long1ProtocolFees) {
      long1Amount = pool.long1ProtocolFees;
      pool.long1ProtocolFees = 0;
    } else {
      long1Amount = long1Requested;
      pool.long1ProtocolFees = pool.long1ProtocolFees.unsafeSub(long1Requested);
    }

    if (shortRequested >= pool.shortProtocolFees) {
      shortAmount = pool.shortProtocolFees;
      pool.shortProtocolFees = 0;
    } else {
      shortAmount = shortRequested;
      pool.shortProtocolFees = pool.shortProtocolFees.unsafeSub(shortRequested);
    }
  }

  /// @dev Collects the transaction fees and short returned of the pool.
  /// @dev only liquidity provider can call this function.
  /// @dev if the owner enters an amount which is greater than the fee amount they have earned, withdraw only the amount they have.
  /// @param pool The state of the pool.
  /// @param maturity The maturity of the pool.
  /// @param long0FeesRequested The maximum amount of long0 fees wanted.
  /// @param long1FeesRequested The maximum amount of long1 fees wanted.
  /// @param shortFeesRequested The maximum amount of short fees wanted.
  /// @param shortReturnedRequested The maximum amount of short returned wanted.
  /// @param blockTimestamp The current blockTimestamp
  /// @return long0Fees The amount of long0 fees collected.
  /// @return long1Fees The amount of long1 fees collected.
  /// @return shortFees The amount of short fees collected.
  /// @return shortReturned The amount of short returned collected.
  function collectTransactionFeesAndShortReturned(
    Pool storage pool,
    uint256 maturity,
    uint256 long0FeesRequested,
    uint256 long1FeesRequested,
    uint256 shortFeesRequested,
    uint256 shortReturnedRequested,
    uint96 blockTimestamp
  ) external returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned) {
    // Update the state of the pool first for the short fee growth.
    if (pool.liquidity != 0) {
      if (maturity > blockTimestamp) updateDurationWeightBeforeMaturity(pool, blockTimestamp);
      else if (pool.lastTimestamp < maturity) updateDurationWeightAfterMaturity(pool, maturity, blockTimestamp);
    }

    LiquidityPosition storage liquidityPosition = pool.liquidityPositions[msg.sender];

    if (pool.liquidity != 0)
      liquidityPosition.update(pool.long0FeeGrowth, pool.long1FeeGrowth, pool.shortFeeGrowth, pool.shortReturnedGrowth);

    (long0Fees, long1Fees, shortFees, shortReturned) = liquidityPosition.collectTransactionFeesAndShortReturned(
      long0FeesRequested,
      long1FeesRequested,
      shortFeesRequested,
      shortReturnedRequested
    );
  }

  /// @dev deposit Short and Long tokens and mints Liquidity
  /// @dev can be only called before the maturity.
  /// @param pool The state of the pool.
  /// @param param it is a struct that contains the parameters of the mint function.
  /// @param blockTimestamp The current block timestamp.
  /// @return liquidityAmount The amount of liquidity minted.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function mint(
    Pool storage pool,
    TimeswapV2PoolMintParam memory param,
    uint96 blockTimestamp
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    // Update the state of the pool first for the short fee growth.
    if (pool.liquidity == 0) pool.lastTimestamp = blockTimestamp;
    else updateDurationWeightBeforeMaturity(pool, blockTimestamp);
    // Update the fee growth and fees of caller.
    LiquidityPosition storage liquidityPosition = pool.liquidityPositions[param.to];

    liquidityPosition.update(pool.long0FeeGrowth, pool.long1FeeGrowth, pool.shortFeeGrowth, pool.shortReturnedGrowth);

    uint256 longAmount;
    if (param.transaction == TimeswapV2PoolMint.GivenLiquidity) {
      (longAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityDelta(
        pool.sqrtInterestRate,
        liquidityAmount = param.delta.toUint160(),
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        true
      );

      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolMint.GivenLong) {
      (liquidityAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityLong(
        pool.sqrtInterestRate,
        longAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        true
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolMint.GivenShort) {
      (liquidityAmount, longAmount) = ConstantProduct.calculateGivenLiquidityShort(
        pool.sqrtInterestRate,
        shortAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        true
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (longAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolMint.GivenLarger) {
      (liquidityAmount, longAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityLargerOrSmaller(
        pool.sqrtInterestRate,
        param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        true
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    }

    liquidityPosition.mint(liquidityAmount);
    pool.liquidity += liquidityAmount;
    // Ask the msg.sender how much long0 position and long1 position wanted.
    (long0Amount, long1Amount, data) = ITimeswapV2PoolMintCallback(msg.sender).timeswapV2PoolMintChoiceCallback(
      TimeswapV2PoolMintChoiceCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        longAmount: longAmount,
        shortAmount: shortAmount,
        liquidityAmount: liquidityAmount,
        data: param.data
      })
    );
    Error.checkEnough(StrikeConversion.combine(long0Amount, long1Amount, param.strike, false), longAmount);

    if (long0Amount != 0) pool.long0Balance += long0Amount;
    if (long1Amount != 0) pool.long1Balance += long1Amount;
  }

  /// @dev burn Liquidity and receive Short and Long tokens
  /// @dev can be only called before the maturity.
  /// @dev after the maturity of the pool, the long0 and long1 tokens are zero. And the short tokens are added into the transaction fees.
  /// @dev if the user wants to burn the liquidity after the maturity, they should call the collectTransactionFee function.
  /// @param pool The state of the pool.
  /// @param param it is a struct that contains the parameters of the burn function.
  /// @param blockTimestamp The current block timestamp.
  /// @return liquidityAmount The amount of liquidity burned.
  /// @return long0Amount The amount of long0 withdrawn.
  /// @return long1Amount The amount of long1 withdrawn.
  /// @return shortAmount The amount of short withdrawn.
  /// @return data the data used for the callbacks.
  function burn(
    Pool storage pool,
    TimeswapV2PoolBurnParam memory param,
    uint96 blockTimestamp
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data)
  {
    if (pool.liquidity == 0) Error.requireLiquidity();

    // Update the state of the pool first for the short fee growth.
    updateDurationWeightBeforeMaturity(pool, blockTimestamp);

    LiquidityPosition storage liquidityPosition = pool.liquidityPositions[msg.sender];

    liquidityPosition.update(pool.long0FeeGrowth, pool.long1FeeGrowth, pool.shortFeeGrowth, pool.shortReturnedGrowth);

    uint256 longAmount;
    if (param.transaction == TimeswapV2PoolBurn.GivenLiquidity) {
      (longAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityDelta(
        pool.sqrtInterestRate,
        liquidityAmount = param.delta.toUint160(),
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        false
      );

      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolBurn.GivenLong) {
      (liquidityAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityLong(
        pool.sqrtInterestRate,
        longAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        false
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolBurn.GivenShort) {
      (liquidityAmount, longAmount) = ConstantProduct.calculateGivenLiquidityShort(
        pool.sqrtInterestRate,
        shortAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        false
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (longAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolBurn.GivenSmaller) {
      (liquidityAmount, longAmount, shortAmount) = ConstantProduct.calculateGivenLiquidityLargerOrSmaller(
        pool.sqrtInterestRate,
        param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        false
      );

      if (liquidityAmount == 0) Error.zeroOutput();
      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    }

    (long0Amount, long1Amount, data) = ITimeswapV2PoolBurnCallback(msg.sender).timeswapV2PoolBurnChoiceCallback(
      TimeswapV2PoolBurnChoiceCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        long0Balance: pool.long0Balance,
        long1Balance: pool.long1Balance,
        longAmount: longAmount,
        shortAmount: shortAmount,
        liquidityAmount: liquidityAmount,
        data: param.data
      })
    );

    Error.checkEnough(longAmount, StrikeConversion.combine(long0Amount, long1Amount, param.strike, true));

    if (long0Amount != 0) pool.long0Balance -= long0Amount;
    if (long1Amount != 0) pool.long1Balance -= long1Amount;

    pool.liquidity -= liquidityAmount;
  }

  /// @dev deposit Long tokens and receive Short tokens
  /// @dev can be only called before the maturity.
  /// @param pool The state of the pool.
  /// @param param it is a struct that contains the parameters of the deleverage function
  /// @param transactionFee The transaction fee rate.
  /// @param protocolFee The protocol fee rate.
  /// @param blockTimestamp The current block timestamp.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short received.
  /// @return data the data used for the callbacks.
  function deleverage(
    Pool storage pool,
    TimeswapV2PoolDeleverageParam memory param,
    uint256 transactionFee,
    uint256 protocolFee,
    uint96 blockTimestamp
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    if (pool.liquidity == 0) Error.requireLiquidity();
    // Update the state of the pool first for the short fee growth.
    updateDurationWeightBeforeMaturity(pool, blockTimestamp);

    uint256 longAmount;
    uint256 shortFees;
    if (param.transaction == TimeswapV2PoolDeleverage.GivenDeltaSqrtInterestRate) {
      (pool.sqrtInterestRate, longAmount, shortAmount, shortFees) = ConstantProduct.updateGivenSqrtInterestRateDelta(
        pool.liquidity,
        pool.sqrtInterestRate,
        param.delta.toUint160(),
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        transactionFee,
        false
      );

      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolDeleverage.GivenLong) {
      (pool.sqrtInterestRate, shortAmount, shortFees) = ConstantProduct.updateGivenLong(
        pool.liquidity,
        pool.sqrtInterestRate,
        longAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        transactionFee,
        true
      );

      if (shortAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolDeleverage.GivenShort) {
      (pool.sqrtInterestRate, longAmount, shortFees) = ConstantProduct.updateGivenShort(
        pool.liquidity,
        pool.sqrtInterestRate,
        shortAmount = param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        transactionFee,
        false
      );

      if (longAmount == 0) Error.zeroOutput();
    } else if (param.transaction == TimeswapV2PoolDeleverage.GivenSum) {
      (pool.sqrtInterestRate, longAmount, shortAmount, shortFees) = ConstantProduct.updateGivenSumLong(
        pool.liquidity,
        pool.sqrtInterestRate,
        param.delta,
        DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
        transactionFee,
        true
      );

      if (longAmount == 0) Error.zeroOutput();
      if (shortAmount == 0) Error.zeroOutput();
    }

    (pool.shortFeeGrowth, pool.shortProtocolFees) = FeeCalculation.update(
      TimeswapV2OptionPosition.Short,
      pool.liquidity,
      pool.shortFeeGrowth,
      pool.shortProtocolFees,
      shortFees,
      protocolFee
    );

    (long0Amount, long1Amount, data) = ITimeswapV2PoolDeleverageCallback(msg.sender)
      .timeswapV2PoolDeleverageChoiceCallback(
        TimeswapV2PoolDeleverageChoiceCallbackParam({
          strike: param.strike,
          maturity: param.maturity,
          longAmount: longAmount,
          shortAmount: shortAmount,
          data: param.data
        })
      );
    Error.checkEnough(StrikeConversion.combine(long0Amount, long1Amount, param.strike, false), longAmount);

    if (long0Amount != 0) pool.long0Balance += long0Amount;
    if (long1Amount != 0) pool.long1Balance += long1Amount;
  }

  /// @dev deposit Short tokens and receive Long tokens
  /// @dev can be only called before the maturity.
  /// @param pool The state of the pool.
  /// @param param it is a struct that contains the parameters of the leverage function.
  /// @param transactionFee The transaction fee rate.
  /// @param protocolFee The protocol fee rate.
  /// @param blockTimestamp The current block timestamp.
  /// @return long0Amount The amount of long0 received.
  /// @return long1Amount The amount of long1 received.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function leverage(
    Pool storage pool,
    TimeswapV2PoolLeverageParam memory param,
    uint256 transactionFee,
    uint256 protocolFee,
    uint96 blockTimestamp
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data) {
    if (pool.liquidity == 0) Error.requireLiquidity();

    // Update the state of the pool first for the short fee growth.
    updateDurationWeightBeforeMaturity(pool, blockTimestamp);

    uint256 long0BalanceAdjustFees = FeeCalculation.removeFees(pool.long0Balance, transactionFee);
    uint256 long1BalanceAdjustFees = FeeCalculation.removeFees(pool.long1Balance, transactionFee);
    {
      uint256 longAmount;
      if (param.transaction == TimeswapV2PoolLeverage.GivenDeltaSqrtInterestRate) {
        (pool.sqrtInterestRate, longAmount, shortAmount, ) = ConstantProduct.updateGivenSqrtInterestRateDelta(
          pool.liquidity,
          pool.sqrtInterestRate,
          param.delta.toUint160(),
          DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
          transactionFee,
          true
        );

        if (longAmount == 0) Error.zeroOutput();
        if (shortAmount == 0) Error.zeroOutput();
      } else if (param.transaction == TimeswapV2PoolLeverage.GivenLong) {
        (pool.sqrtInterestRate, shortAmount, ) = ConstantProduct.updateGivenLong(
          pool.liquidity,
          pool.sqrtInterestRate,
          longAmount = param.delta,
          DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
          transactionFee,
          false
        );

        if (shortAmount == 0) Error.zeroOutput();
      } else if (param.transaction == TimeswapV2PoolLeverage.GivenShort) {
        (pool.sqrtInterestRate, longAmount, ) = ConstantProduct.updateGivenShort(
          pool.liquidity,
          pool.sqrtInterestRate,
          shortAmount = param.delta,
          DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
          transactionFee,
          true
        );

        if (longAmount == 0) Error.zeroOutput();
      } else if (param.transaction == TimeswapV2PoolLeverage.GivenSum) {
        (pool.sqrtInterestRate, longAmount, shortAmount, ) = ConstantProduct.updateGivenSumLong(
          pool.liquidity,
          pool.sqrtInterestRate,
          param.delta,
          DurationCalculation.unsafeDurationFromNowToMaturity(param.maturity, blockTimestamp),
          transactionFee,
          false
        );
        if (longAmount == 0) Error.zeroOutput();
        if (shortAmount == 0) Error.zeroOutput();
      }

      (long0Amount, long1Amount, data) = ITimeswapV2PoolLeverageCallback(msg.sender)
        .timeswapV2PoolLeverageChoiceCallback(
          TimeswapV2PoolLeverageChoiceCallbackParam({
            strike: param.strike,
            maturity: param.maturity,
            long0Balance: long0BalanceAdjustFees,
            long1Balance: long1BalanceAdjustFees,
            longAmount: longAmount,
            shortAmount: shortAmount,
            data: param.data
          })
        );
      Error.checkEnough(longAmount, StrikeConversion.combine(long0Amount, long1Amount, param.strike, true));
    }

    if (long0Amount != 0) {
      uint256 long0Fees;
      if (long0Amount == long0BalanceAdjustFees) {
        long0Fees = pool.long0Balance.unsafeSub(long0Amount);
        pool.long0Balance = 0;
      } else {
        long0Fees = FeeCalculation.getFeesAdditional(long0Amount, transactionFee);
        pool.long0Balance -= (long0Amount + long0Fees);
      }

      (pool.long0FeeGrowth, pool.long0ProtocolFees) = FeeCalculation.update(
        TimeswapV2OptionPosition.Long0,
        pool.liquidity,
        pool.long0FeeGrowth,
        pool.long0ProtocolFees,
        long0Fees,
        protocolFee
      );
    }

    if (long1Amount != 0) {
      uint256 long1Fees;
      if (long1Amount == long1BalanceAdjustFees) {
        long1Fees = pool.long1Balance.unsafeSub(long1Amount);
        pool.long1Balance = 0;
      } else {
        long1Fees = FeeCalculation.getFeesAdditional(long1Amount, transactionFee);

        pool.long1Balance -= (long1Amount + long1Fees);
      }

      (pool.long1FeeGrowth, pool.long1ProtocolFees) = FeeCalculation.update(
        TimeswapV2OptionPosition.Long1,
        pool.liquidity,
        pool.long1FeeGrowth,
        pool.long1ProtocolFees,
        long1Fees,
        protocolFee
      );
    }
  }

  /// @dev Deposit Long0 to receive Long1 or deposit Long1 to receive Long0.
  /// @dev can be only called before the maturity.
  /// @param pool The state of the pool.
  /// @param param it is a struct that contains the parameters of the rebalance function.
  /// @param transactionFee The transaction fee rate.
  /// @param protocolFee The protocol fee rate.
  /// @return long0Amount The amount of long0 received/deposited.
  /// @return long1Amount The amount of long1 deposited/received.
  function rebalance(
    Pool storage pool,
    TimeswapV2PoolRebalanceParam memory param,
    uint256 transactionFee,
    uint256 protocolFee
  ) external returns (uint256 long0Amount, uint256 long1Amount) {
    if (pool.liquidity == 0) Error.requireLiquidity();

    // No need to update short returned growth.

    uint256 longFees;
    if (param.isLong0ToLong1) {
      if (param.transaction == TimeswapV2PoolRebalance.GivenLong0) {
        (long1Amount, longFees) = ConstantSum.calculateGivenLongIn(
          param.strike,
          long0Amount = param.delta,
          transactionFee,
          true
        );

        if (long1Amount == 0) Error.zeroOutput();

        pool.long1Balance -= (long1Amount + longFees);
      } else if (param.transaction == TimeswapV2PoolRebalance.GivenLong1) {
        uint256 long1AmountAdjustFees = FeeCalculation.removeFees(pool.long1Balance, transactionFee);

        if ((long1Amount = param.delta) == long1AmountAdjustFees) {
          long0Amount = ConstantSum.calculateGivenLongOutAlreadyAdjustFees(param.strike, pool.long1Balance, true);

          longFees = pool.long1Balance.unsafeSub(long1Amount);
          pool.long1Balance = 0;
        } else {
          (long0Amount, longFees) = ConstantSum.calculateGivenLongOut(param.strike, long1Amount, transactionFee, true);

          pool.long1Balance -= (long1Amount + longFees);
        }

        if (long0Amount == 0) Error.zeroOutput();
      }

      pool.long0Balance += long0Amount;

      (pool.long1FeeGrowth, pool.long1ProtocolFees) = FeeCalculation.update(
        TimeswapV2OptionPosition.Long1,
        pool.liquidity,
        pool.long1FeeGrowth,
        pool.long1ProtocolFees,
        longFees,
        protocolFee
      );
    } else {
      if (param.transaction == TimeswapV2PoolRebalance.GivenLong0) {
        uint256 long0AmountAdjustFees = FeeCalculation.removeFees(pool.long0Balance, transactionFee);

        if ((long0Amount = param.delta) == long0AmountAdjustFees) {
          long1Amount = ConstantSum.calculateGivenLongOutAlreadyAdjustFees(param.strike, pool.long0Balance, false);

          longFees = pool.long0Balance.unsafeSub(long0Amount);
          pool.long0Balance = 0;
        } else {
          (long1Amount, longFees) = ConstantSum.calculateGivenLongOut(param.strike, long0Amount, transactionFee, false);

          pool.long0Balance -= (long0Amount + longFees);
        }

        if (long1Amount == 0) Error.zeroOutput();
      } else if (param.transaction == TimeswapV2PoolRebalance.GivenLong1) {
        (long0Amount, longFees) = ConstantSum.calculateGivenLongIn(
          param.strike,
          long1Amount = param.delta,
          transactionFee,
          false
        );

        if (long0Amount == 0) Error.zeroOutput();

        pool.long0Balance -= (long0Amount + longFees);
      }

      pool.long1Balance += long1Amount;

      (pool.long0FeeGrowth, pool.long0ProtocolFees) = FeeCalculation.update(
        TimeswapV2OptionPosition.Long0,
        pool.liquidity,
        pool.long0FeeGrowth,
        pool.long0ProtocolFees,
        longFees,
        protocolFee
      );
    }
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Math} from "@timeswap-labs/v2-library/contracts/Math.sol";
import {FullMath} from "@timeswap-labs/v2-library/contracts/FullMath.sol";

import {TimeswapV2OptionPosition} from "@timeswap-labs/v2-option/contracts/enums/Position.sol";

///@title library for fees related calculations
library FeeCalculation {
  using Math for uint256;

  event ReceiveTransactionFees(TimeswapV2OptionPosition position, uint256 fees);

  /// @dev Reverts when fee overflow.
  error FeeOverflow();

  /// @dev reverts to overflow fee.
  function feeOverflow() private pure {
    revert FeeOverflow();
  }

  /// @dev Updates the new fee growth and protocol fee given the current fee growth and protocol fee.
  /// @param position The position to be updated.
  /// @param liquidity The current liquidity in the pool.
  /// @param feeGrowth The current feeGrowth in the pool.
  /// @param protocolFees The current protocolFees in the pool.
  /// @param fees The fees to be earned.
  /// @param protocolFee The protocol fee rate.
  /// @return newFeeGrowth The newly updated fee growth.
  /// @return newProtocolFees The newly updated protocol fees.
  function update(
    TimeswapV2OptionPosition position,
    uint160 liquidity,
    uint256 feeGrowth,
    uint256 protocolFees,
    uint256 fees,
    uint256 protocolFee
  ) internal returns (uint256 newFeeGrowth, uint256 newProtocolFees) {
    uint256 protocolFeesToAdd = getFeesRemoval(fees, protocolFee);
    uint256 transactionFees = fees.unsafeSub(protocolFeesToAdd);

    newFeeGrowth = feeGrowth.unsafeAdd(getFeeGrowth(transactionFees, liquidity));

    newProtocolFees = protocolFees + protocolFeesToAdd;

    emit ReceiveTransactionFees(position, transactionFees);
  }

  /// @dev get the fee given the last fee growth and the global fee growth
  /// @notice returns zero if the last fee growth is equal to the global fee growth
  /// @param liquidity The current liquidity in the pool.
  /// @param lastFeeGrowth The previous global fee growth when owner enters.
  /// @param globalFeeGrowth The current global fee growth.
  function getFees(uint160 liquidity, uint256 lastFeeGrowth, uint256 globalFeeGrowth) internal pure returns (uint256) {
    return
      globalFeeGrowth != lastFeeGrowth
        ? FullMath.mulDiv(liquidity, globalFeeGrowth.unsafeSub(lastFeeGrowth), uint256(1) << 128, false)
        : 0;
  }

  /// @dev Adds the fees to the amount.
  /// @param amount The original amount.
  /// @param fee The transaction fee rate.
  function addFees(uint256 amount, uint256 fee) internal pure returns (uint256) {
    return FullMath.mulDiv(amount, (uint256(1) << 16), (uint256(1) << 16).unsafeSub(fee), true);
  }

  /// @dev Removes the fees from the amount.
  /// @param amount The original amount.
  /// @param fee The transaction fee rate.
  function removeFees(uint256 amount, uint256 fee) internal pure returns (uint256) {
    return FullMath.mulDiv(amount, (uint256(1) << 16).unsafeSub(fee), uint256(1) << 16, false);
  }

  /// @dev Get the fees from an amount with fees.
  /// @param amount The amount with fees.
  /// @param fee The transaction fee rate.
  function getFeesRemoval(uint256 amount, uint256 fee) internal pure returns (uint256) {
    return FullMath.mulDiv(amount, fee, uint256(1) << 16, true);
  }

  /// @dev Get the fees from an amount.
  /// @param amount The amount with fees.
  /// @param fee The transaction fee rate.
  function getFeesAdditional(uint256 amount, uint256 fee) internal pure returns (uint256) {
    return FullMath.mulDiv(amount, fee, (uint256(1) << 16).unsafeSub(fee), true);
  }

  /// @dev Get the fee growth.
  /// @param feeAmount The fee amount.
  /// @param liquidity The current liquidity in the pool.
  function getFeeGrowth(uint256 feeAmount, uint160 liquidity) internal pure returns (uint256) {
    return FullMath.mulDiv(feeAmount, uint256(1) << 128, liquidity, false);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2PoolBurnChoiceCallbackParam, TimeswapV2PoolBurnCallbackParam} from "../../structs/CallbackParam.sol";

/// @dev The interface that needs to be implemented by a contract calling the burn function.
interface ITimeswapV2PoolBurnCallback {
  /// @dev Returns the amount of long0 position and long1 positions chosen to be withdrawn.
  /// @notice The StrikeConversion.combine of long0 position and long1 position must be less than or equal to long amount.
  /// @return long0Amount Amount of long0 position to be withdrawn.
  /// @return long1Amount Amount of long1 position to be withdrawn.
  /// @return data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolBurnChoiceCallback(
    TimeswapV2PoolBurnChoiceCallbackParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);

  /// @dev Require enough liquidity position by the msg.sender.
  /// @return data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolBurnCallback(
    TimeswapV2PoolBurnCallbackParam calldata param
  ) external returns (bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

interface IOwnableTwoSteps {
  /// @dev Emits when the pending owner is chosen.
  /// @param pendingOwner The new pending owner.
  event SetOwner(address pendingOwner);

  /// @dev Emits when the pending owner accepted and become the new owner.
  /// @param owner The new owner.
  event AcceptOwner(address owner);

  /// @dev The address of the current owner.
  /// @return address
  function owner() external view returns (address);

  /// @dev The address of the current pending owner.
  /// @notice The address can be zero which signifies no pending owner.
  /// @return address
  function pendingOwner() external view returns (address);

  /// @dev The owner sets the new pending owner.
  /// @notice Can only be called by the owner.
  /// @param chosenPendingOwner The newly chosen pending owner.
  function setPendingOwner(address chosenPendingOwner) external;

  /// @dev The pending owner accepts being the new owner.
  /// @notice Can only be called by the pending owner.
  function acceptOwner() external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev The three type of native token positions.
/// @dev Long0 is denominated as the underlying Token0.
/// @dev Long1 is denominated as the underlying Token1.
/// @dev When strike greater than uint128 then Short is denominated as Token0 (the base token denomination).
/// @dev When strike is uint128 then Short is denominated as Token1 (the base token denomination).
enum TimeswapV2OptionPosition {
  Long0,
  Long1,
  Short
}

/// @title library for position utils
/// @author Timeswap Labs
/// @dev Helper functions for the TimeswapOptionPosition enum.
library PositionLibrary {
  /// @dev Reverts when the given type of position is invalid.
  error InvalidPosition();

  /// @dev Checks that the position input is correct.
  /// @param position The position input.
  function check(TimeswapV2OptionPosition position) internal pure {
    if (uint256(position) >= 3) revert InvalidPosition();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {StrikeAndMaturity} from "@timeswap-labs/v2-option/contracts/structs/StrikeAndMaturity.sol";

import {TimeswapV2PoolCollectProtocolFeesParam, TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam, TimeswapV2PoolMintParam, TimeswapV2PoolBurnParam, TimeswapV2PoolDeleverageParam, TimeswapV2PoolLeverageParam, TimeswapV2PoolRebalanceParam} from "../structs/Param.sol";

/// @title An interface for Timeswap V2 Pool contract.
interface ITimeswapV2Pool {
  /// @dev Emits when liquidity position is transferred.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param from The sender of liquidity position.
  /// @param to The receipeint of liquidity position.
  /// @param liquidityAmount The amount of liquidity position transferred.
  event TransferLiquidity(
    uint256 indexed strike,
    uint256 indexed maturity,
    address from,
    address to,
    uint160 liquidityAmount
  );

  /// @dev Emits when protocol fees are withdrawn by the factory contract owner.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the collectProtocolFees function.
  /// @param long0To The recipient of long0 position fees.
  /// @param long1To The recipient of long1 position fees.
  /// @param shortTo The recipient of short position fees.
  /// @param long0Amount The amount of long0 position fees withdrawn.
  /// @param long1Amount The amount of long1 position fees withdrawn.
  /// @param shortAmount The amount of short position fees withdrawn.
  event CollectProtocolFees(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address long0To,
    address long1To,
    address shortTo,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when transaction fees are withdrawn by a liquidity provider.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the collectTransactionFees function.
  /// @param long0FeesTo The recipient of long0 position fees.
  /// @param long1FeesTo The recipient of long1 position fees.
  /// @param shortFeesTo The recipient of short position fees.
  /// @param shortReturnedTo The recipient of short position returned.
  /// @param long0Fees The amount of long0 position fees withdrawn.
  /// @param long1Fees The amount of long1 position fees withdrawn.
  /// @param shortFees The amount of short position fees withdrawn.
  /// @param shortReturned The amount of short position returned withdrawn.
  event CollectTransactionFeesAndShortReturned(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address long0FeesTo,
    address long1FeesTo,
    address shortFeesTo,
    address shortReturnedTo,
    uint256 long0Fees,
    uint256 long1Fees,
    uint256 shortFees,
    uint256 shortReturned
  );

  /// @dev Emits when the mint transaction is called.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the mint function.
  /// @param to The recipient of liquidity positions.
  /// @param liquidityAmount The amount of liquidity positions minted.
  /// @param long0Amount The amount of long0 positions deposited.
  /// @param long1Amount The amount of long1 positions deposited.
  /// @param shortAmount The amount of short positions deposited.
  event Mint(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address to,
    uint160 liquidityAmount,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when the burn transaction is called.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the burn function.
  /// @param long0To The recipient of long0 positions.
  /// @param long1To The recipient of long1 positions.
  /// @param shortTo The recipient of short positions.
  /// @param liquidityAmount The amount of liquidity positions burnt.
  /// @param long0Amount The amount of long0 positions withdrawn.
  /// @param long1Amount The amount of long1 positions withdrawn.
  /// @param shortAmount The amount of short positions withdrawn.
  event Burn(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address long0To,
    address long1To,
    address shortTo,
    uint160 liquidityAmount,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when deleverage transaction is called.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the deleverage function.
  /// @param to The recipient of short positions.
  /// @param long0Amount The amount of long0 positions deposited.
  /// @param long1Amount The amount of long1 positions deposited.
  /// @param shortAmount The amount of short positions withdrawn.
  event Deleverage(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address to,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when leverage transaction is called.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the leverage function.
  /// @param long0To The recipient of long0 positions.
  /// @param long1To The recipient of long1 positions.
  /// @param long0Amount The amount of long0 positions withdrawn.
  /// @param long1Amount The amount of long1 positions withdrawn.
  /// @param shortAmount The amount of short positions deposited.
  event Leverage(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address long0To,
    address long1To,
    uint256 long0Amount,
    uint256 long1Amount,
    uint256 shortAmount
  );

  /// @dev Emits when rebalance transaction is called.
  /// @param strike The strike of the option and pool.
  /// @param maturity The maturity of the option and pool.
  /// @param caller The caller of the rebalance function.
  /// @param to If isLong0ToLong1 then recipient of long0 positions, ekse recipient of long1 positions.
  /// @param isLong0ToLong1 Long0ToLong1 if true. Long1ToLong0 if false.
  /// @param long0Amount If isLong0ToLong1, amount of long0 positions deposited.
  /// If isLong1ToLong0, amount of long0 positions withdrawn.
  /// @param long1Amount If isLong0ToLong1, amount of long1 positions withdrawn.
  /// If isLong1ToLong0, amount of long1 positions deposited.
  event Rebalance(
    uint256 indexed strike,
    uint256 indexed maturity,
    address indexed caller,
    address to,
    bool isLong0ToLong1,
    uint256 long0Amount,
    uint256 long1Amount
  );

  error Quote();

  /* ===== VIEW ===== */

  /// @dev Returns the factory address that deployed this contract.
  function poolFactory() external view returns (address);

  /// @dev Returns the Timeswap V2 Option of the pair.
  function optionPair() external view returns (address);

  /// @dev Returns the transaction fee earned by the liquidity providers.
  function transactionFee() external view returns (uint256);

  /// @dev Returns the protocol fee earned by the protocol.
  function protocolFee() external view returns (uint256);

  /// @dev Get the strike and maturity of the pool in the pool enumeration list.
  /// @param id The chosen index.
  function getByIndex(uint256 id) external view returns (StrikeAndMaturity memory);

  /// @dev Get the number of pools being interacted.
  function numberOfPools() external view returns (uint256);

  /// @dev Returns the total amount of liquidity in the pool.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return liquidityAmount The liquidity amount of the pool.
  function totalLiquidity(uint256 strike, uint256 maturity) external view returns (uint160 liquidityAmount);

  /// @dev Returns the square root of the interest rate of the pool.
  /// @dev the square root of interest rate is z/(x+y) where z is the short amount, x+y is the long0 amount, and y is the long1 amount.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return rate The square root of the interest rate of the pool.
  function sqrtInterestRate(uint256 strike, uint256 maturity) external view returns (uint160 rate);

  /// @dev Returns the amount of liquidity owned by the given address.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param owner The address to query the liquidity of.
  /// @return liquidityAmount The amount of liquidity owned by the given address.
  function liquidityOf(uint256 strike, uint256 maturity, address owner) external view returns (uint160 liquidityAmount);

  /// @dev It calculates the global fee and global short returned growth, which is fee increased per unit of liquidity token.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return long0FeeGrowth The global fee increased per unit of liquidity token for long0.
  /// @return long1FeeGrowth The global fee increased per unit of liquidity token for long1.
  /// @return shortFeeGrowth The global fee increased per unit of liquidity token for short.
  /// @return shortReturnedGrowth The global returned increased per unit of liquidity token for short.
  function feesEarnedAndShortReturnedGrowth(
    uint256 strike,
    uint256 maturity
  )
    external
    view
    returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth);

  /// @dev It calculates the global fee and global short returned growth, which is fee increased per unit of liquidity token.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param durationForward The duration of time moved forward.
  /// @return long0FeeGrowth The global fee increased per unit of liquidity token for long0.
  /// @return long1FeeGrowth The global fee increased per unit of liquidity token for long1.
  /// @return shortFeeGrowth The global fee increased per unit of liquidity token for short.
  /// @return shortReturnedGrowth The global returned increased per unit of liquidity token for short.
  function feesEarnedAndShortReturnedGrowth(
    uint256 strike,
    uint256 maturity,
    uint96 durationForward
  )
    external
    view
    returns (uint256 long0FeeGrowth, uint256 long1FeeGrowth, uint256 shortFeeGrowth, uint256 shortReturnedGrowth);

  /// @dev It calculates the fee earned and global short returned growth, which is short returned per unit of liquidity token.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param owner The address to query the fees earned of.
  /// @return long0Fees The amount of long0 fees owned by the given address.
  /// @return long1Fees The amount of long1 fees owned by the given address.
  /// @return shortFees The amount of short fees owned by the given address.
  /// @return shortReturned The amount of short returned owned by the given address.
  function feesEarnedAndShortReturnedOf(
    uint256 strike,
    uint256 maturity,
    address owner
  ) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned);

  /// @dev It calculates the fee earned and global short returned growth, which is short returned per unit of liquidity token.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param owner The address to query the fees earned of.
  /// @param durationForward The duration of time moved forward.
  /// @return long0Fees The amount of long0 fees owned by the given address.
  /// @return long1Fees The amount of long1 fees owned by the given address.
  /// @return shortFees The amount of short fees owned by the given address.
  /// @return shortReturned The amount of short returned owned by the given address.
  function feesEarnedAndShortReturnedOf(
    uint256 strike,
    uint256 maturity,
    address owner,
    uint96 durationForward
  ) external view returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned);

  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return long0ProtocolFees The amount of long0 protocol fees owned by the owner of the factory contract.
  /// @return long1ProtocolFees The amount of long1 protocol fees owned by the owner of the factory contract.
  /// @return shortProtocolFees The amount of short protocol fees owned by the owner of the factory contract.
  function protocolFeesEarned(
    uint256 strike,
    uint256 maturity
  ) external view returns (uint256 long0ProtocolFees, uint256 long1ProtocolFees, uint256 shortProtocolFees);

  /// @dev Returns the amount of long0 and long1 in the pool.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return long0Amount The amount of long0 in the pool.
  /// @return long1Amount The amount of long1 in the pool.
  function totalLongBalance(
    uint256 strike,
    uint256 maturity
  ) external view returns (uint256 long0Amount, uint256 long1Amount);

  /// @dev Returns the amount of long0 and long1 adjusted for the protocol and transaction fee.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return long0Amount The amount of long0 in the pool, adjusted for the protocol and transaction fee.
  /// @return long1Amount The amount of long1 in the pool, adjusted for the protocol and transaction fee.
  function totalLongBalanceAdjustFees(
    uint256 strike,
    uint256 maturity
  ) external view returns (uint256 long0Amount, uint256 long1Amount);

  /// @dev Returns the amount of sum of long0 and long1 converted to base denomination in the pool.
  /// @dev Returns the amount of short positions in the pool.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @return longAmount The amount of sum of long0 and long1 converted to base denomination in the pool.
  /// @return shortAmount The amount of short in the pool.
  function totalPositions(
    uint256 strike,
    uint256 maturity
  ) external view returns (uint256 longAmount, uint256 shortAmount);

  /* ===== UPDATE ===== */

  /// @dev Transfer liquidity positions to another address.
  /// @notice Does not transfer the transaction fees earned by the sender.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param to The recipient of the liquidity positions.
  /// @param liquidityAmount The amount of liquidity positions transferred
  function transferLiquidity(uint256 strike, uint256 maturity, address to, uint160 liquidityAmount) external;

  /// @dev initializes the pool with the given parameters.
  /// @param strike The strike price of the pool.
  /// @param maturity The maturity of the pool.
  /// @param rate The square root of the interest rate of the pool.
  function initialize(uint256 strike, uint256 maturity, uint160 rate) external;

  /// @dev Collects the protocol fees of the pool.
  /// @dev only protocol owner can call this function.
  /// @dev if the owner enters an amount which is greater than the fee amount they have earned, withdraw only the amount they have.
  /// @param param The parameters of the collectProtocolFees.
  /// @return long0Amount The amount of long0 collected.
  /// @return long1Amount The amount of long1 collected.
  /// @return shortAmount The amount of short collected.
  function collectProtocolFees(
    TimeswapV2PoolCollectProtocolFeesParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount);

  /// @dev Collects the transaction fees of the pool.
  /// @dev only liquidity provider can call this function.
  /// @dev if the owner enters an amount which is greater than the fee amount they have earned, withdraw only the amount they have.
  /// @param param The parameters of the collectTransactionFee.
  /// @return long0Fees The amount of long0 fees collected.
  /// @return long1Fees The amount of long1 fees collected.
  /// @return shortFees The amount of short fees collected.
  /// @return shortReturned The amount of short returned collected.
  function collectTransactionFeesAndShortReturned(
    TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam calldata param
  ) external returns (uint256 long0Fees, uint256 long1Fees, uint256 shortFees, uint256 shortReturned);

  /// @dev deposit Short and Long tokens and mints Liquidity
  /// @dev can be only called before the maturity.
  /// @param param it is a struct that contains the parameters of the mint function
  /// @return liquidityAmount The amount of liquidity minted.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function mint(
    TimeswapV2PoolMintParam calldata param
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev deposit Short and Long tokens and mints Liquidity
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the mint function.
  /// @param durationForward The duration of time moved forward.
  /// @return liquidityAmount The amount of liquidity minted.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function mint(
    TimeswapV2PoolMintParam calldata param,
    uint96 durationForward
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev burn Liquidity and receive Short and Long tokens
  /// @dev can be only called before the maturity.
  /// @dev after the maturity of the pool, the long0 and long1 tokens are zero. And the short tokens are added into the transaction fees.
  /// @dev if the user wants to burn the liquidity after the maturity, they should call the collectTransactionFee function.
  /// @param param it is a struct that contains the parameters of the burn function
  /// @return liquidityAmount The amount of liquidity burned.
  /// @return long0Amount The amount of long0 withdrawn.
  /// @return long1Amount The amount of long1 withdrawn.
  /// @return shortAmount The amount of short withdrawn.
  /// @return data the data used for the callbacks.
  function burn(
    TimeswapV2PoolBurnParam calldata param
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev burn Liquidity and receive Short and Long tokens
  /// @dev can be only called before the maturity.
  /// @dev after the maturity of the pool, the long0 and long1 tokens are zero. And the short tokens are added into the transaction fees.
  /// @dev if the user wants to burn the liquidity after the maturity, they should call the collectTransactionFee function.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the burn function.
  /// @param durationForward The duration of time moved forward.
  /// @return liquidityAmount The amount of liquidity burned.
  /// @return long0Amount The amount of long0 withdrawn.
  /// @return long1Amount The amount of long1 withdrawn.
  /// @return shortAmount The amount of short withdrawn.
  /// @return data the data used for the callbacks.
  function burn(
    TimeswapV2PoolBurnParam calldata param,
    uint96 durationForward
  )
    external
    returns (uint160 liquidityAmount, uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev deposit Long tokens and receive Short tokens
  /// @dev can be only called before the maturity.
  /// @param param it is a struct that contains the parameters of the deleverage function
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short received.
  /// @return data the data used for the callbacks.
  function deleverage(
    TimeswapV2PoolDeleverageParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev deposit Long tokens and receive Short tokens
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the deleverage function.
  /// @param durationForward The duration of time moved forward.
  /// @return long0Amount The amount of long0 deposited.
  /// @return long1Amount The amount of long1 deposited.
  /// @return shortAmount The amount of short received.
  /// @return data the data used for the callbacks.
  function deleverage(
    TimeswapV2PoolDeleverageParam calldata param,
    uint96 durationForward
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev deposit Short tokens and receive Long tokens
  /// @dev can be only called before the maturity.
  /// @param param it is a struct that contains the parameters of the leverage function.
  /// @return long0Amount The amount of long0 received.
  /// @return long1Amount The amount of long1 received.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function leverage(
    TimeswapV2PoolLeverageParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev deposit Short tokens and receive Long tokens
  /// @dev can be only called before the maturity.
  /// @notice Will always revert with error Quote after the final callback.
  /// @param param it is a struct that contains the parameters of the leverage function.
  /// @param durationForward The duration of time moved forward.
  /// @return long0Amount The amount of long0 received.
  /// @return long1Amount The amount of long1 received.
  /// @return shortAmount The amount of short deposited.
  /// @return data the data used for the callbacks.
  function leverage(
    TimeswapV2PoolLeverageParam calldata param,
    uint96 durationForward
  ) external returns (uint256 long0Amount, uint256 long1Amount, uint256 shortAmount, bytes memory data);

  /// @dev Deposit Long0 to receive Long1 or deposit Long1 to receive Long0.
  /// @dev can be only called before the maturity.
  /// @param param it is a struct that contains the parameters of the rebalance function
  /// @return long0Amount The amount of long0 received/deposited.
  /// @return long1Amount The amount of long1 deposited/received.
  /// @return data the data used for the callbacks.
  function rebalance(
    TimeswapV2PoolRebalanceParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title Library for math related utils
/// @author Timeswap Labs
library Math {
  /// @dev Reverts when divide by zero.
  error DivideByZero();
  error Overflow();

  /// @dev Add two uint256.
  /// @notice May overflow.
  /// @param addend1 The first addend.
  /// @param addend2 The second addend.
  /// @return sum The sum.
  function unsafeAdd(uint256 addend1, uint256 addend2) internal pure returns (uint256 sum) {
    unchecked {
      sum = addend1 + addend2;
    }
  }

  /// @dev Subtract two uint256.
  /// @notice May underflow.
  /// @param minuend The minuend.
  /// @param subtrahend The subtrahend.
  /// @return difference The difference.
  function unsafeSub(uint256 minuend, uint256 subtrahend) internal pure returns (uint256 difference) {
    unchecked {
      difference = minuend - subtrahend;
    }
  }

  /// @dev Multiply two uint256.
  /// @notice May overflow.
  /// @param multiplicand The multiplicand.
  /// @param multiplier The multiplier.
  /// @return product The product.
  function unsafeMul(uint256 multiplicand, uint256 multiplier) internal pure returns (uint256 product) {
    unchecked {
      product = multiplicand * multiplier;
    }
  }

  /// @dev Divide two uint256.
  /// @notice Reverts when divide by zero.
  /// @param dividend The dividend.
  /// @param divisor The divisor.
  //// @param roundUp Round up the result when true. Round down if false.
  /// @return quotient The quotient.
  function div(uint256 dividend, uint256 divisor, bool roundUp) internal pure returns (uint256 quotient) {
    quotient = dividend / divisor;

    if (roundUp && dividend % divisor != 0) quotient++;
  }

  /// @dev Shift right a uint256 number.
  /// @param dividend The dividend.
  /// @param divisorBit The divisor in bits.
  /// @param roundUp True if ceiling the result. False if floor the result.
  /// @return quotient The quotient.
  function shr(uint256 dividend, uint8 divisorBit, bool roundUp) internal pure returns (uint256 quotient) {
    quotient = dividend >> divisorBit;

    if (roundUp && dividend % (1 << divisorBit) != 0) quotient++;
  }

  /// @dev Gets the square root of a value.
  /// @param value The value being square rooted.
  /// @param roundUp Round up the result when true. Round down if false.
  /// @return result The resulting value of the square root.
  function sqrt(uint256 value, bool roundUp) internal pure returns (uint256 result) {
    if (value == type(uint256).max) return result = type(uint128).max;
    if (value == 0) return 0;
    unchecked {
      uint256 estimate = (value + 1) >> 1;
      result = value;
      while (estimate < result) {
        result = estimate;
        estimate = (value / estimate + estimate) >> 1;
      }
    }

    if (roundUp && result * result < value) result++;
  }

  /// @dev Gets the min of two uint256 number.
  /// @param value1 The first value to be compared.
  /// @param value2 The second value to be compared.
  /// @return result The min result.
  function min(uint256 value1, uint256 value2) internal pure returns (uint256 result) {
    return value1 < value2 ? value1 : value2;
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {TimeswapV2PoolMintChoiceCallbackParam, TimeswapV2PoolMintCallbackParam} from "../../structs/CallbackParam.sol";

/// @dev The interface that needs to be implemented by a contract calling the mint function.
interface ITimeswapV2PoolMintCallback {
  /// @dev Returns the amount of long0 position and long1 positions chosen to be deposited to the pool.
  /// @notice The StrikeConversion.combine of long0 position and long1 position must be greater than or equal to long amount.
  /// @dev The liquidity positions will already be minted to the recipient.
  /// @return long0Amount Amount of long0 position to be deposited.
  /// @return long1Amount Amount of long1 position to be deposited.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolMintChoiceCallback(
    TimeswapV2PoolMintChoiceCallbackParam calldata param
  ) external returns (uint256 long0Amount, uint256 long1Amount, bytes memory data);

  /// @dev Require the transfer of long0 position, long1 position, and short position into the pool.
  /// @param data The bytes of data to be sent to msg.sender.
  function timeswapV2PoolMintCallback(
    TimeswapV2PoolMintCallbackParam calldata param
  ) external returns (bytes memory data);
}

File 34 of 39 : ITimeswapV2PoolDeployer.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title An interface for a contract that is capable of deploying Timeswap V2 Pool
/// @notice A contract that constructs a pair must implement this to pass arguments to the pair.
/// @dev This is used to avoid having constructor arguments in the pair contract, which results in the init code hash
/// of the pair being constant allowing the CREATE2 address of the pair to be cheaply computed on-chain.
interface ITimeswapV2PoolDeployer {
  /* ===== VIEW ===== */

  /// @notice Get the parameters to be used in constructing the pair, set transiently during pair creation.
  /// @dev Called by the pair constructor to fetch the parameters of the pair.
  /// @return poolFactory The poolFactory address.
  /// @param optionPair The Timeswap V2 OptionPair address.
  /// @param transactionFee The transaction fee earned by the liquidity providers.
  /// @param protocolFee The protocol fee earned by the DAO.
  function parameter()
    external
    view
    returns (address poolFactory, address optionPair, uint256 transactionFee, uint256 protocolFee);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Math} from "./Math.sol";

/// @title Library for math utils for uint512
/// @author Timeswap Labs
library FullMath {
  using Math for uint256;

  /// @dev Reverts when modulo by zero.
  error ModuloByZero();

  /// @dev Reverts when add512 overflows over uint512.
  /// @param addendA0 The least significant part of first addend.
  /// @param addendA1 The most significant part of first addend.
  /// @param addendB0 The least significant part of second addend.
  /// @param addendB1 The most significant part of second addend.
  error AddOverflow(uint256 addendA0, uint256 addendA1, uint256 addendB0, uint256 addendB1);

  /// @dev Reverts when sub512 underflows.
  /// @param minuend0 The least significant part of minuend.
  /// @param minuend1 The most significant part of minuend.
  /// @param subtrahend0 The least significant part of subtrahend.
  /// @param subtrahend1 The most significant part of subtrahend.
  error SubUnderflow(uint256 minuend0, uint256 minuend1, uint256 subtrahend0, uint256 subtrahend1);

  /// @dev Reverts when div512To256 overflows over uint256.
  /// @param dividend0 The least significant part of dividend.
  /// @param dividend1 The most significant part of dividend.
  /// @param divisor The divisor.
  error DivOverflow(uint256 dividend0, uint256 dividend1, uint256 divisor);

  /// @dev Reverts when mulDiv overflows over uint256.
  /// @param multiplicand The multiplicand.
  /// @param multiplier The multiplier.
  /// @param divisor The divisor.
  error MulDivOverflow(uint256 multiplicand, uint256 multiplier, uint256 divisor);

  /// @dev Calculates the sum of two uint512 numbers.
  /// @notice Reverts on overflow over uint512.
  /// @param addendA0 The least significant part of addendA.
  /// @param addendA1 The most significant part of addendA.
  /// @param addendB0 The least significant part of addendB.
  /// @param addendB1 The most significant part of addendB.
  /// @return sum0 The least significant part of sum.
  /// @return sum1 The most significant part of sum.
  function add512(
    uint256 addendA0,
    uint256 addendA1,
    uint256 addendB0,
    uint256 addendB1
  ) internal pure returns (uint256 sum0, uint256 sum1) {
    uint256 carry;
    assembly {
      sum0 := add(addendA0, addendB0)
      carry := lt(sum0, addendA0)
      sum1 := add(add(addendA1, addendB1), carry)
    }

    if (carry == 0 ? addendA1 > sum1 : (sum1 == 0 || addendA1 > sum1 - 1))
      revert AddOverflow(addendA0, addendA1, addendB0, addendB1);
  }

  /// @dev Calculates the difference of two uint512 numbers.
  /// @notice Reverts on underflow.
  /// @param minuend0 The least significant part of minuend.
  /// @param minuend1 The most significant part of minuend.
  /// @param subtrahend0 The least significant part of subtrahend.
  /// @param subtrahend1 The most significant part of subtrahend.
  /// @return difference0 The least significant part of difference.
  /// @return difference1 The most significant part of difference.
  function sub512(
    uint256 minuend0,
    uint256 minuend1,
    uint256 subtrahend0,
    uint256 subtrahend1
  ) internal pure returns (uint256 difference0, uint256 difference1) {
    assembly {
      difference0 := sub(minuend0, subtrahend0)
      difference1 := sub(sub(minuend1, subtrahend1), lt(minuend0, subtrahend0))
    }

    if (subtrahend1 > minuend1 || (subtrahend1 == minuend1 && subtrahend0 > minuend0))
      revert SubUnderflow(minuend0, minuend1, subtrahend0, subtrahend1);
  }

  /// @dev Calculate the product of two uint256 numbers that may result to uint512 product.
  /// @notice Can never overflow.
  /// @param multiplicand The multiplicand.
  /// @param multiplier The multiplier.
  /// @return product0 The least significant part of product.
  /// @return product1 The most significant part of product.
  function mul512(uint256 multiplicand, uint256 multiplier) internal pure returns (uint256 product0, uint256 product1) {
    assembly {
      let mm := mulmod(multiplicand, multiplier, not(0))
      product0 := mul(multiplicand, multiplier)
      product1 := sub(sub(mm, product0), lt(mm, product0))
    }
  }

  /// @dev Divide 2 to 256 power by the divisor.
  /// @dev Rounds down the result.
  /// @notice Reverts when divide by zero.
  /// @param divisor The divisor.
  /// @return quotient The quotient.
  function div256(uint256 divisor) private pure returns (uint256 quotient) {
    if (divisor == 0) revert Math.DivideByZero();
    assembly {
      quotient := add(div(sub(0, divisor), divisor), 1)
    }
  }

  /// @dev Compute 2 to 256 power modulo the given value.
  /// @notice Reverts when modulo by zero.
  /// @param value The given value.
  /// @return result The result.
  function mod256(uint256 value) private pure returns (uint256 result) {
    if (value == 0) revert ModuloByZero();
    assembly {
      result := mod(sub(0, value), value)
    }
  }

  /// @dev Divide a uint512 number by uint256 number to return a uint512 number.
  /// @dev Rounds down the result.
  /// @param dividend0 The least significant part of dividend.
  /// @param dividend1 The most significant part of dividend.
  /// @param divisor The divisor.
  /// @param quotient0 The least significant part of quotient.
  /// @param quotient1 The most significant part of quotient.
  function div512(
    uint256 dividend0,
    uint256 dividend1,
    uint256 divisor
  ) private pure returns (uint256 quotient0, uint256 quotient1) {
    if (dividend1 == 0) quotient0 = dividend0.div(divisor, false);
    else {
      uint256 q = div256(divisor);
      uint256 r = mod256(divisor);
      while (dividend1 != 0) {
        (uint256 t0, uint256 t1) = mul512(dividend1, q);
        (quotient0, quotient1) = add512(quotient0, quotient1, t0, t1);
        (t0, t1) = mul512(dividend1, r);
        (dividend0, dividend1) = add512(t0, t1, dividend0, 0);
      }
      (quotient0, quotient1) = add512(quotient0, quotient1, dividend0.div(divisor, false), 0);
    }
  }

  /// @dev Divide a uint512 number by a uint256 number.
  /// @dev Reverts when result is greater than uint256.
  /// @notice Skips div512 if dividend1 is zero.
  /// @param dividend0 The least significant part of dividend.
  /// @param dividend1 The most significant part of dividend.
  /// @param divisor The divisor.
  /// @param roundUp Round up the result when true. Round down if false.
  /// @param quotient The quotient.
  function div512To256(
    uint256 dividend0,
    uint256 dividend1,
    uint256 divisor,
    bool roundUp
  ) internal pure returns (uint256 quotient) {
    uint256 quotient1;
    (quotient, quotient1) = div512(dividend0, dividend1, divisor);

    if (quotient1 != 0) revert DivOverflow(dividend0, dividend1, divisor);

    if (roundUp) {
      (uint256 productA0, uint256 productA1) = mul512(quotient, divisor);
      if (dividend1 > productA1 || dividend0 > productA0) quotient++;
    }
  }

  /// @dev Divide a uint512 number by a uint256 number.
  /// @notice Skips div512 if dividend1 is zero.
  /// @param dividend0 The least significant part of dividend.
  /// @param dividend1 The most significant part of dividend.
  /// @param divisor The divisor.
  /// @param roundUp Round up the result when true. Round down if false.
  /// @param quotient0 The least significant part of quotient.
  /// @param quotient1 The most significant part of quotient.
  function div512(
    uint256 dividend0,
    uint256 dividend1,
    uint256 divisor,
    bool roundUp
  ) internal pure returns (uint256 quotient0, uint256 quotient1) {
    (quotient0, quotient1) = div512(dividend0, dividend1, divisor);

    if (roundUp) {
      (uint256 productA0, uint256 productA1) = mul512(quotient0, divisor);
      productA1 += (quotient1 * divisor);
      if (dividend1 > productA1 || dividend0 > productA0) {
        if (quotient0 == type(uint256).max) {
          quotient0 = 0;
          quotient1++;
        } else quotient0++;
      }
    }
  }

  /// @dev Multiply two uint256 number then divide it by a uint256 number.
  /// @notice Skips mulDiv if product of multiplicand and multiplier is uint256 number.
  /// @dev Reverts when result is greater than uint256.
  /// @param multiplicand The multiplicand.
  /// @param multiplier The multiplier.
  /// @param divisor The divisor.
  /// @param roundUp Round up the result when true. Round down if false.
  /// @return result The result.
  function mulDiv(
    uint256 multiplicand,
    uint256 multiplier,
    uint256 divisor,
    bool roundUp
  ) internal pure returns (uint256 result) {
    (uint256 product0, uint256 product1) = mul512(multiplicand, multiplier);

    // Handle non-overflow cases, 256 by 256 division
    if (product1 == 0) return result = product0.div(divisor, roundUp);

    // Make sure the result is less than 2**256.
    // Also prevents divisor == 0
    if (divisor <= product1) revert MulDivOverflow(multiplicand, multiplier, divisor);

    unchecked {
      ///////////////////////////////////////////////
      // 512 by 256 division.
      ///////////////////////////////////////////////

      // Make division exact by subtracting the remainder from [product1 product0]
      // Compute remainder using mulmod
      uint256 remainder;
      assembly {
        remainder := mulmod(multiplicand, multiplier, divisor)
      }
      // Subtract 256 bit number from 512 bit number
      assembly {
        product1 := sub(product1, gt(remainder, product0))
        product0 := sub(product0, remainder)
      }

      // Factor powers of two out of divisor
      // Compute largest power of two divisor of divisor.
      // Always >= 1.
      uint256 twos;
      twos = (0 - divisor) & divisor;
      // Divide denominator by power of two
      assembly {
        divisor := div(divisor, twos)
      }

      // Divide [product1 product0] by the factors of two
      assembly {
        product0 := div(product0, twos)
      }
      // Shift in bits from product1 into product0. For this we need
      // to flip `twos` such that it is 2**256 / twos.
      // If twos is zero, then it becomes one
      assembly {
        twos := add(div(sub(0, twos), twos), 1)
      }
      product0 |= product1 * twos;

      // Invert divisor mod 2**256
      // Now that divisor is an odd number, it has an inverse
      // modulo 2**256 such that divisor * inv = 1 mod 2**256.
      // Compute the inverse by starting with a seed that is correct
      // correct for four bits. That is, divisor * inv = 1 mod 2**4
      uint256 inv;
      inv = (3 * divisor) ^ 2;

      // Now use Newton-Raphson iteration to improve the precision.
      // Thanks to Hensel's lifting lemma, this also works in modular
      // arithmetic, doubling the correct bits in each step.
      inv *= 2 - divisor * inv; // inverse mod 2**8
      inv *= 2 - divisor * inv; // inverse mod 2**16
      inv *= 2 - divisor * inv; // inverse mod 2**32
      inv *= 2 - divisor * inv; // inverse mod 2**64
      inv *= 2 - divisor * inv; // inverse mod 2**128
      inv *= 2 - divisor * inv; // inverse mod 2**256

      // Because the division is now exact we can divide by multiplying
      // with the modular inverse of divisor. This will give us the
      // correct result modulo 2**256. Since the preconditions guarantee
      // that the outcome is less than 2**256, this is the final result.
      // We don't need to compute the high bits of the result and product1
      // is no longer required.
      result = product0 * inv;
    }

    if (roundUp && mulmod(multiplicand, multiplier, divisor) != 0) result++;
  }

  /// @dev Get the square root of a uint512 number.
  /// @param value0 The least significant of the number.
  /// @param value1 The most significant of the number.
  /// @param roundUp Round up the result when true. Round down if false.
  /// @return result The result.
  function sqrt512(uint256 value0, uint256 value1, bool roundUp) internal pure returns (uint256 result) {
    if (value1 == 0) result = value0.sqrt(roundUp);
    else {
      uint256 estimate = sqrt512Estimate(value0, value1, type(uint256).max);
      result = type(uint256).max;
      while (estimate < result) {
        result = estimate;
        estimate = sqrt512Estimate(value0, value1, estimate);
      }

      if (roundUp) {
        (uint256 product0, uint256 product1) = mul512(result, result);
        if (value1 > product1 || value0 > product0) result++;
      }
    }
  }

  /// @dev An iterative process of getting sqrt512 following Newtonian method.
  /// @param value0 The least significant of the number.
  /// @param value1 The most significant of the number.
  /// @param currentEstimate The current estimate of the iteration.
  /// @param estimate The new estimate of the iteration.
  function sqrt512Estimate(
    uint256 value0,
    uint256 value1,
    uint256 currentEstimate
  ) private pure returns (uint256 estimate) {
    uint256 r0 = div512To256(value0, value1, currentEstimate, false);
    uint256 r1;
    (r0, r1) = add512(r0, 0, currentEstimate, 0);
    estimate = div512To256(r0, r1, 2, false);
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";

import {TimeswapV2OptionMint, TimeswapV2OptionBurn, TimeswapV2OptionSwap, TimeswapV2OptionCollect, TransactionLibrary} from "../enums/Transaction.sol";

/// @dev The parameter to call the mint function.
/// @param strike The strike of the option.
/// @param maturity The maturity of the option.
/// @param long0To The recipient of long0 positions.
/// @param long1To The recipient of long1 positions.
/// @param shortTo The recipient of short positions.
/// @param transaction The type of mint transaction, more information in Transaction module.
/// @param amount0 If transaction is givenTokensAndLongs, the amount of token0 deposited, and amount of long0 position minted.
/// If transaction is givenShorts, the amount of short minted, where the equivalent strike converted amount is long0 positions.
/// @param amount1 If transaction is givenTokensAndLongs, the amount of token1 deposited, and amount of long1 position minted.
/// If transaction is givenShorts, the amount of short minted, where the equivalent strike converted amount is long1 positions.
/// @param data The data to be sent to the function, which will go to the mint callback.
struct TimeswapV2OptionMintParam {
  uint256 strike;
  uint256 maturity;
  address long0To;
  address long1To;
  address shortTo;
  TimeswapV2OptionMint transaction;
  uint256 amount0;
  uint256 amount1;
  bytes data;
}

/// @dev The parameter to call the burn function.
/// @param strike The strike of the option.
/// @param maturity The maturity of the option.
/// @param token0To The recipient of token0 withdrawn.
/// @param token1To The recipient of token1 withdrawn.
/// @param transaction The type of burn transaction, more information in Transaction module.
/// @param amount0 If transaction is givenTokensAndLongs, the amount of token0 withdrawn, and amount of long0 position burnt.
/// If transaction is givenShorts, the amount of short burnt, where the equivalent strike converted amount is long0 positions.
/// @param amount1 If transaction is givenTokensAndLongs, the amount of token1 withdrawn, and amount of long1 position burnt.
/// If transaction is givenShorts, the amount of short burnt, where the equivalent strike converted amount is long1 positions.
/// @param data The data to be sent to the function, which will go to the burn callback.
/// @notice If data length is zero, skips the callback.
struct TimeswapV2OptionBurnParam {
  uint256 strike;
  uint256 maturity;
  address token0To;
  address token1To;
  TimeswapV2OptionBurn transaction;
  uint256 amount0;
  uint256 amount1;
  bytes data;
}

/// @dev The parameter to call the swap function.
/// @param strike The strike of the option.
/// @param maturity The maturity of the option.
/// @param tokenTo The recipient of token0 when isLong0ToLong1 or token1 when isLong1ToLong0.
/// @param longTo The recipient of long1 positions when isLong0ToLong1 or long0 when isLong1ToLong0.
/// @param isLong0ToLong1 Transform long0 positions to long1 positions when true. Transform long1 positions to long0 positions when false.
/// @param transaction The type of swap transaction, more information in Transaction module.
/// @param amount If isLong0ToLong1 and transaction is GivenToken0AndLong0, this is the amount of token0 withdrawn, and the amount of long0 position burnt.
/// If isLong1ToLong0 and transaction is GivenToken0AndLong0, this is the amount of token0 to be deposited, and the amount of long0 position minted.
/// If isLong0ToLong1 and transaction is GivenToken1AndLong1, this is the amount of token1 to be deposited, and the amount of long1 position minted.
/// If isLong1ToLong0 and transaction is GivenToken1AndLong1, this is the amount of token1 withdrawn, and the amount of long1 position burnt.
/// @param data The data to be sent to the function, which will go to the swap callback.
struct TimeswapV2OptionSwapParam {
  uint256 strike;
  uint256 maturity;
  address tokenTo;
  address longTo;
  bool isLong0ToLong1;
  TimeswapV2OptionSwap transaction;
  uint256 amount;
  bytes data;
}

/// @dev The parameter to call the collect function.
/// @param strike The strike of the option.
/// @param maturity The maturity of the option.
/// @param token0To The recipient of token0 withdrawn.
/// @param token1To The recipient of token1 withdrawn.
/// @param transaction The type of collect transaction, more information in Transaction module.
/// @param amount If transaction is GivenShort, the amount of short position burnt.
/// If transaction is GivenToken0, the amount of token0 withdrawn.
/// If transaction is GivenToken1, the amount of token1 withdrawn.
/// @param data The data to be sent to the function, which will go to the collect callback.
/// @notice If data length is zero, skips the callback.
struct TimeswapV2OptionCollectParam {
  uint256 strike;
  uint256 maturity;
  address token0To;
  address token1To;
  TimeswapV2OptionCollect transaction;
  uint256 amount;
  bytes data;
}

library ParamLibrary {
  /// @dev Sanity checks
  /// @param param the parameter for mint transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2OptionMintParam memory param, uint96 blockTimestamp) internal pure {
    if (param.strike == 0) Error.zeroInput();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.shortTo == address(0)) Error.zeroAddress();
    if (param.long0To == address(0)) Error.zeroAddress();
    if (param.long1To == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.amount0 == 0 && param.amount1 == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for burn transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2OptionBurnParam memory param, uint96 blockTimestamp) internal pure {
    if (param.strike == 0) Error.zeroInput();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.token0To == address(0)) Error.zeroAddress();
    if (param.token1To == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.amount0 == 0 && param.amount1 == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for swap transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2OptionSwapParam memory param, uint96 blockTimestamp) internal pure {
    if (param.strike == 0) Error.zeroInput();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.tokenTo == address(0)) Error.zeroAddress();
    if (param.longTo == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.amount == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for collect transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2OptionCollectParam memory param, uint96 blockTimestamp) internal pure {
    if (param.strike == 0) Error.zeroInput();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity >= blockTimestamp) Error.stillActive(param.maturity, blockTimestamp);
    if (param.token0To == address(0)) Error.zeroAddress();
    if (param.token1To == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.amount == 0) Error.zeroInput();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title The interface for the contract that deploys Timeswap V2 Option pair contracts
/// @notice The Timeswap V2 Option Factory facilitates creation of Timeswap V2 Options pair.
interface ITimeswapV2OptionFactory {
  /* ===== EVENT ===== */

  /// @dev Emits when a new Timeswap V2 Option contract is created.
  /// @param caller The address of the caller of create function.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  /// @param optionPair The address of the Timeswap V2 Option contract created.
  event Create(address indexed caller, address indexed token0, address indexed token1, address optionPair);

  /* ===== VIEW ===== */

  /// @dev Returns the address of a Timeswap V2 Option.
  /// @dev Returns a zero address if the Timeswap V2 Option does not exist.
  /// @notice The token0 address must be smaller than token1 address.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  /// @return optionPair The address of the Timeswap V2 Option contract or a zero address.
  function get(address token0, address token1) external view returns (address optionPair);

  /// @dev Get the address of the option pair in the option pair enumeration list.
  /// @param id The chosen index.
  function getByIndex(uint256 id) external view returns (address optionPair);

  /// @dev The number of option pairs deployed.
  function numberOfPairs() external view returns (uint256);

  /* ===== UPDATE ===== */

  /// @dev Creates a Timeswap V2 Option based on pair parameters.
  /// @dev Cannot create a duplicate Timeswap V2 Option with the same pair parameters.
  /// @notice The token0 address must be smaller than token1 address.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  /// @param optionPair The address of the Timeswap V2 Option contract created.
  function create(address token0, address token1) external returns (address optionPair);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {Error} from "@timeswap-labs/v2-library/contracts/Error.sol";

import {TimeswapV2PoolMint, TimeswapV2PoolBurn, TimeswapV2PoolDeleverage, TimeswapV2PoolLeverage, TimeswapV2PoolRebalance, TransactionLibrary} from "../enums/Transaction.sol";

/// @dev The parameter for collectProtocolFees functions.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0To The recipient of long0 positions.
/// @param long1To The recipient of long1 positions.
/// @param shortTo The recipient of short positions.
/// @param long0Requested The maximum amount of long0 positions wanted.
/// @param long1Requested The maximum amount of long1 positions wanted.
/// @param shortRequested The maximum amount of short positions wanted.
struct TimeswapV2PoolCollectProtocolFeesParam {
  uint256 strike;
  uint256 maturity;
  address long0To;
  address long1To;
  address shortTo;
  uint256 long0Requested;
  uint256 long1Requested;
  uint256 shortRequested;
}

/// @dev The parameter for collectTransactionFeesAndShortReturned functions.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0FeesTo The recipient of long0 fees.
/// @param long1FeesTo The recipient of long1 fees.
/// @param shortFeesTo The recipient of short fees.
/// @param shortReturnedTo The recipient of short returned.
/// @param long0FeesRequested The maximum amount of long0 fees wanted.
/// @param long1FeesRequested The maximum amount of long1 fees wanted.
/// @param shortFeesRequested The maximum amount of short fees wanted.
/// @param shortReturnedRequested The maximum amount of short returned wanted.
struct TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam {
  uint256 strike;
  uint256 maturity;
  address long0FeesTo;
  address long1FeesTo;
  address shortFeesTo;
  address shortReturnedTo;
  uint256 long0FeesRequested;
  uint256 long1FeesRequested;
  uint256 shortFeesRequested;
  uint256 shortReturnedRequested;
}

/// @dev The parameter for mint function.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param to The recipient of liquidity positions.
/// @param transaction The type of mint transaction, more information in Transaction module.
/// @param delta If transaction is GivenLiquidity, the amount of liquidity minted. Note that this value must be uint160.
/// If transaction is GivenLong, the amount of long position in base denomination to be deposited.
/// If transaction is GivenShort, the amount of short position to be deposited.
/// @param data The data to be sent to the function, which will go to the mint choice callback.
struct TimeswapV2PoolMintParam {
  uint256 strike;
  uint256 maturity;
  address to;
  TimeswapV2PoolMint transaction;
  uint256 delta;
  bytes data;
}

/// @dev The parameter for burn function.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0To The recipient of long0 positions.
/// @param long1To The recipient of long1 positions.
/// @param shortTo The recipient of short positions.
/// @param transaction The type of burn transaction, more information in Transaction module.
/// @param delta If transaction is GivenLiquidity, the amount of liquidity burnt. Note that this value must be uint160.
/// If transaction is GivenLong, the amount of long position in base denomination to be withdrawn.
/// If transaction is GivenShort, the amount of short position to be withdrawn.
/// @param data The data to be sent to the function, which will go to the burn choice callback.
struct TimeswapV2PoolBurnParam {
  uint256 strike;
  uint256 maturity;
  address long0To;
  address long1To;
  address shortTo;
  TimeswapV2PoolBurn transaction;
  uint256 delta;
  bytes data;
}

/// @dev The parameter for deleverage function.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param to The recipient of short positions.
/// @param transaction The type of deleverage transaction, more information in Transaction module.
/// @param delta If transaction is GivenDeltaSqrtInterestRate, the decrease in square root interest rate.
/// If transaction is GivenLong, the amount of long position in base denomination to be deposited.
/// If transaction is GivenShort, the amount of short position to be withdrawn.
/// If transaction is  GivenSum, the sum amount of long position in base denomination to be deposited, and short position to be withdrawn.
/// @param data The data to be sent to the function, which will go to the deleverage choice callback.
struct TimeswapV2PoolDeleverageParam {
  uint256 strike;
  uint256 maturity;
  address to;
  TimeswapV2PoolDeleverage transaction;
  uint256 delta;
  bytes data;
}

/// @dev The parameter for leverage function.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param long0To The recipient of long0 positions.
/// @param long1To The recipient of long1 positions.
/// @param transaction The type of leverage transaction, more information in Transaction module.
/// @param delta If transaction is GivenDeltaSqrtInterestRate, the increase in square root interest rate.
/// If transaction is GivenLong, the amount of long position in base denomination to be withdrawn.
/// If transaction is GivenShort, the amount of short position to be deposited.
/// If transaction is  GivenSum, the sum amount of long position in base denomination to be withdrawn, and short position to be deposited.
/// @param data The data to be sent to the function, which will go to the leverage choice callback.
struct TimeswapV2PoolLeverageParam {
  uint256 strike;
  uint256 maturity;
  address long0To;
  address long1To;
  TimeswapV2PoolLeverage transaction;
  uint256 delta;
  bytes data;
}

/// @dev The parameter for rebalance function.
/// @param strike The strike price of the pool.
/// @param maturity The maturity of the pool.
/// @param to When Long0ToLong1, the recipient of long1 positions.
/// When Long1ToLong0, the recipient of long0 positions.
/// @param isLong0ToLong1 Long0ToLong1 when true. Long1ToLong0 when false.
/// @param transaction The type of rebalance transaction, more information in Transaction module.
/// @param delta If transaction is GivenLong0 and Long0ToLong1, the amount of long0 positions to be deposited.
/// If transaction is GivenLong0 and Long1ToLong0, the amount of long1 positions to be withdrawn.
/// If transaction is GivenLong1 and Long0ToLong1, the amount of long1 positions to be withdrawn.
/// If transaction is GivenLong1 and Long1ToLong0, the amount of long1 positions to be deposited.
/// @param data The data to be sent to the function, which will go to the rebalance callback.
struct TimeswapV2PoolRebalanceParam {
  uint256 strike;
  uint256 maturity;
  address to;
  bool isLong0ToLong1;
  TimeswapV2PoolRebalance transaction;
  uint256 delta;
  bytes data;
}

library ParamLibrary {
  /// @dev Sanity checks
  /// @param param the parameter for collectProtocolFees transaction.
  function check(TimeswapV2PoolCollectProtocolFeesParam memory param) internal pure {
    if (param.long0To == address(0) || param.long1To == address(0) || param.shortTo == address(0)) Error.zeroAddress();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if ((param.long0Requested == 0 && param.long1Requested == 0 && param.shortRequested == 0) || param.strike == 0)
      Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for collectTransactionFeesAndShortReturned transaction.
  function check(TimeswapV2PoolCollectTransactionFeesAndShortReturnedParam memory param) internal pure {
    if (
      param.long0FeesTo == address(0) ||
      param.long1FeesTo == address(0) ||
      param.shortFeesTo == address(0) ||
      param.shortReturnedTo == address(0)
    ) Error.zeroAddress();
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (
      (param.long0FeesRequested == 0 &&
        param.long1FeesRequested == 0 &&
        param.shortFeesRequested == 0 &&
        param.shortReturnedRequested == 0) || param.strike == 0
    ) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for mint transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2PoolMintParam memory param, uint96 blockTimestamp) internal pure {
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.to == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.delta == 0 || param.strike == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for burn transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2PoolBurnParam memory param, uint96 blockTimestamp) internal pure {
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.long0To == address(0) || param.long1To == address(0) || param.shortTo == address(0)) Error.zeroAddress();

    TransactionLibrary.check(param.transaction);
    if (param.delta == 0 || param.strike == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for deleverage transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2PoolDeleverageParam memory param, uint96 blockTimestamp) internal pure {
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.to == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.delta == 0 || param.strike == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for leverage transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2PoolLeverageParam memory param, uint96 blockTimestamp) internal pure {
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.long0To == address(0) || param.long1To == address(0)) Error.zeroAddress();

    TransactionLibrary.check(param.transaction);
    if (param.delta == 0 || param.strike == 0) Error.zeroInput();
  }

  /// @dev Sanity checks
  /// @param param the parameter for rebalance transaction.
  /// @param blockTimestamp the current block timestamp.
  function check(TimeswapV2PoolRebalanceParam memory param, uint96 blockTimestamp) internal pure {
    if (param.maturity > type(uint96).max) Error.incorrectMaturity(param.maturity);
    if (param.maturity < blockTimestamp) Error.alreadyMatured(param.maturity, blockTimestamp);
    if (param.to == address(0)) Error.zeroAddress();
    TransactionLibrary.check(param.transaction);
    if (param.delta == 0 || param.strike == 0) Error.zeroInput();
  }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @title library for renentrancy protection
/// @author Timeswap Labs
library ReentrancyGuard {
  /// @dev Reverts when their is a reentrancy to a single option.
  error NoReentrantCall();

  /// @dev Reverts when the option, pool, or token id is not interacted yet.
  error NotInteracted();

  /// @dev The initial state which must be change to NOT_ENTERED when first interacting.
  uint96 internal constant NOT_INTERACTED = 0;

  /// @dev The initial and ending state of balanceTarget in the Option struct.
  uint96 internal constant NOT_ENTERED = 1;

  /// @dev The state where the contract is currently being interacted with.
  uint96 internal constant ENTERED = 2;

  /// @dev Check if there is a reentrancy in an option.
  /// @notice Reverts when balanceTarget is not zero.
  /// @param reentrancyGuard The balance being inquired.
  function check(uint96 reentrancyGuard) internal pure {
    if (reentrancyGuard == NOT_INTERACTED) revert NotInteracted();
    if (reentrancyGuard == ENTERED) revert NoReentrantCall();
  }
}

Settings
{
  "libraries": {
    "@timeswap-labs/v2-pool/contracts/structs/Pool.sol": {
      "PoolLibrary": "0x9ebf82942d914628623e2ff432deffd54dccfe27"
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"chosenOwner","type":"address"},{"internalType":"address","name":"chosenOptionFactory","type":"address"},{"internalType":"uint256","name":"chosenTransactionFee","type":"uint256"},{"internalType":"uint256","name":"chosenProtocolFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"AlreadyTheOwner","type":"error"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"IncorrectFeeInitialization","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"NotTheOwner","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"NotThePendingOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"AcceptOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"option","type":"address"},{"indexed":true,"internalType":"address","name":"poolPair","type":"address"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"SetOwner","type":"event"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"name":"create","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"optionPair","type":"address"}],"name":"get","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"}],"name":"get","outputs":[{"internalType":"address","name":"pair","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getByIndex","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfPairs","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parameter","outputs":[{"internalType":"address","name":"poolFactory","type":"address"},{"internalType":"address","name":"optionPair","type":"address"},{"internalType":"uint256","name":"transactionFee","type":"uint256"},{"internalType":"uint256","name":"protocolFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"chosenPendingOwner","type":"address"}],"name":"setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transactionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60e060405234801561001057600080fd5b5060405161565d38038061565d83398101604081905261002f916100d6565b600480546001600160a01b0319166001600160a01b03861617905561ffff82111561007557604051634403315760e01b8152600481018390526024015b60405180910390fd5b61ffff81111561009b57604051634403315760e01b81526004810182905260240161006c565b60609290921b6001600160601b03191660805260a05260c05250610119565b80516001600160a01b03811681146100d157600080fd5b919050565b600080600080608085870312156100ec57600080fd5b6100f5856100ba565b9350610103602086016100ba565b6040860151606090960151949790965092505050565b60805160601c60a05160c0516154f6610167600039600081816101b101526103ac01526000818161012f015261038b0152600081816101d8015281816102c7015261053201526154f66000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063b3461c871161008c578063d81e842311610066578063d81e84231461023b578063e30c39781461024e578063ebbc496514610261578063fc0d92bd1461026957600080fd5b8063b3461c87146101d3578063c2bc2efc146101fa578063c42069ec1461022657600080fd5b80632d883a73146100d45780633e68680a146101045780638da5cb5b146101175780639ed3edf01461012a578063ad4d4e291461015f578063b0e21e8a146101ac575b600080fd5b6100e76100e23660046107fc565b610271565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e761011236600461082a565b61029b565b6004546100e7906001600160a01b031681565b6101517f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100fb565b600054600154600254600354610181936001600160a01b039081169316919084565b604080516001600160a01b0395861681529490931660208501529183015260608201526080016100fb565b6101517f000000000000000000000000000000000000000000000000000000000000000081565b6100e77f000000000000000000000000000000000000000000000000000000000000000081565b6100e7610208366004610863565b6001600160a01b039081166000908152600660205260409020541690565b610239610234366004610863565b61046e565b005b6100e761024936600461082a565b610506565b6005546100e7906001600160a01b031681565b6102396105cf565b600754610151565b6007818154811061028157600080fd5b6000918252602090912001546001600160a01b0316905081565b60405163d81e842360e01b81526001600160a01b038381166004830152828116602483015260009182917f0000000000000000000000000000000000000000000000000000000000000000169063d81e84239060440160206040518083038186803b15801561030957600080fd5b505afa15801561031d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103419190610887565b90506001600160a01b0381166103595761035961063a565b6001600160a01b0380821660009081526006602052604090205416915081156103845761038461063a565b6103d030827f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610653565b6001600160a01b0380831660008181526006602052604080822080549486166001600160a01b03199586168117909155600780546001810182559084527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180549095168117909455519395509192909133917fb224da6575b2c2ffd42454faedb236f7dbe5f92a0c96bb99c0273dbe98464c7e9190a45092915050565b600454610483906001600160a01b0316610726565b6001600160a01b0381166104995761049961063a565b6004546104b2906001600160a01b038381169116610768565b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f167d3e9c1016ab80e58802ca9da10ce5c6a0f4debc46a2e7a2cd9e56899a4fb59060200160405180910390a150565b60405163d81e842360e01b81526001600160a01b038381166004830152828116602483015260009182917f0000000000000000000000000000000000000000000000000000000000000000169063d81e84239060440160206040518083038186803b15801561057457600080fd5b505afa158015610588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ac9190610887565b6001600160a01b0390811660009081526006602052604090205416949350505050565b6005546105e69033906001600160a01b03166107aa565b60048054336001600160a01b031991821681179092556005805490911690556040519081527f31775a018194f26a792a0203e972faf15303e08a913cdc54497278e48de669099060200160405180910390a1565b60405163d92e233d60e01b815260040160405180910390fd5b604080516080810182526001600160a01b0386811680835290861660208084018290528385018790526060909301859052600080546001600160a01b03199081169093178155600180549093168217909255600286905560038590558351928301529101604051602081830303815290604052805190602001206040516106d9906107ef565b8190604051809103906000f59050801580156106f9573d6000803e3d6000fd5b50600080546001600160a01b03199081168255600180549091169055600281905560035595945050505050565b336001600160a01b0382161461076557604051631bf9565d60e21b81523360048201526001600160a01b03821660248201526044015b60405180910390fd5b50565b806001600160a01b0316826001600160a01b031614156107a657604051632de305cb60e11b81526001600160a01b038216600482015260240161075c565b5050565b806001600160a01b0316826001600160a01b0316146107a65760405163f93f289d60e01b81526001600160a01b0380841660048301528216602482015260440161075c565b614c1c806108a583390190565b60006020828403121561080e57600080fd5b5035919050565b6001600160a01b038116811461076557600080fd5b6000806040838503121561083d57600080fd5b823561084881610815565b9150602083013561085881610815565b809150509250929050565b60006020828403121561087557600080fd5b813561088081610815565b9392505050565b60006020828403121561089957600080fd5b81516108808161081556fe6101206040523480156200001257600080fd5b503060601b60809081526040805163ad4d4e2960e01b81529051339263ad4d4e299260048082019391829003018186803b1580156200005057600080fd5b505afa15801562000065573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200008b9190620000cf565b6101005260e0526001600160601b0319606091821b811660c05291901b1660a05262000117565b80516001600160a01b0381168114620000ca57600080fd5b919050565b60008060008060808587031215620000e657600080fd5b620000f185620000b2565b93506200010160208601620000b2565b6040860151606090960151949790965092505050565b60805160601c60a05160601c60c05160601c60e051610100516149d06200024c600039600081816104c0015281816108740152818161295b0152612f9301526000818161043c0152818161071b015281816108520152818161293a0152612f720152600081816101e401528181610918015281816109e801528181610bc10152818161133801528181611c2901528181611cd201528181611d8d01528181611f2701528181611fe101528181612095015281816122a301528181612345015281816123e101528181612724015281816127b20152818161284001528181612a2201528181612ae101528181612b8901528181612d0601528181612d680152818161305501528181613102015281816131a4015261331c0152600081816102d30152610e860152600061196501526149d06000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806382b9008f11610104578063b0e21e8a116100a2578063c0e0c4c611610071578063c0e0c4c61461053c578063de64af931461054f578063e0260d1114610562578063f78333a31461057557600080fd5b8063b0e21e8a146104bb578063b15044ac146104e2578063bfec5c8a14610516578063c0d27cc51461052957600080fd5b80639ed3edf0116100de5780639ed3edf0146104375780639eee6e511461045e578063a8f403b714610471578063ad118b02146104a657600080fd5b806382b9008f146103fe578063847a1013146104115780638fdc5c991461042457600080fd5b80635e2659f31161017c5780636f682a531161014b5780636f682a531461038057806372f8f85c1461039257806378fb9d38146103a55780637ffd3a70146103c857600080fd5b80635e2659f3146103195780636016477d1461032c578063630b15f31461033f578063641972451461035257600080fd5b80633a9d71e7116101b85780633a9d71e7146102845780633aee875c146102ac5780634219dc40146102ce5780634e9ca003146102f557600080fd5b806311c959fd146101df5780632d883a73146102235780633377633314610251575b600080fd5b6102067f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6102366102313660046137f3565b6105b6565b6040805182518152602092830151928101929092520161021a565b61026461025f36600461380c565b610611565b60408051948552602085019390935291830152606082015260800161021a565b61029761029236600461380c565b6106eb565b6040805192835260208301919091520161021a565b6102bf6102ba366004613846565b6107bb565b60405161021a939291906138de565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b610308610303366004613934565b610d11565b60405161021a959493929190613981565b6103086103273660046139d2565b610d38565b61026461033a366004613a07565b610d4c565b61030861034d366004613a3c565b610e28565b610365610360366004613a70565b610e4e565b6040805193845260208401929092529082015260600161021a565b6002545b60405190815260200161021a565b6103656103a036600461380c565b6110db565b6103b86103b3366004613a94565b611189565b60405161021a9493929190613ac8565b6102976103d636600461380c565b6000918252600160209081526040808420928452919052902060028101546003909101549091565b61026461040c366004613af7565b6111ab565b61030861041f366004613a94565b611499565b61029761043236600461380c565b6114ad565b6103847f000000000000000000000000000000000000000000000000000000000000000081565b6103b861046c366004613b0a565b611528565b61020661047f36600461380c565b6000918252600160208181526040808520938552929052912001546001600160a01b031690565b6104b96104b4366004613b54565b61153b565b005b6103847f000000000000000000000000000000000000000000000000000000000000000081565b6102066104f036600461380c565b60009182526001602090815260408084209284529190529020546001600160a01b031690565b6103b8610524366004613934565b61162f565b610264610537366004613b54565b611642565b6104b961054a366004613b98565b6116d0565b61026461055d366004613be2565b611841565b6103b8610570366004613846565b61192c565b610206610583366004613b54565b60009283526001602090815260408085209385529281528284206001600160a01b039283168552600b0190529120541690565b6040805180820190915260008082526020820152600282815481106105dd576105dd613c2a565b9060005260206000209060020201604051806040016040529081600082015481526020016001820154815250509050919050565b60008281526001602090815260408083208484529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe279063f4af1a4b90876106568561193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b0316604482015260640160806040518083038186803b1580156106a357600080fd5b505af41580156106b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106db9190613c40565b9299919850965090945092505050565b600082815260016020908152604080832084845290915280822090516313571e0d60e11b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000060248201528190739ebf82942d914628623e2ff432deffd54dccfe27906326ae3c1a906044015b604080518083038186803b15801561077857600080fd5b505af415801561078c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b09190613c76565b909590945092505050565b60008060606107c861195a565b6107d7843560208601356119a5565b6107f26107e385613e0c565b6107ed600061193f565b6119d8565b61080184356020860135611a5f565b833560009081526001602090815260408083208288013584529091529081902090516302b65c4f60e11b8152739ebf82942d914628623e2ff432deffd54dccfe279163056cb89e9161089c919088907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401613f2d565b604080518083038186803b1580156108b357600080fd5b505af41580156108c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108eb9190613c76565b909350915060006109026080860160608701613fdf565b61090c578261090e565b835b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663af2f91ea873560208901353061095560808c0160608d01613fdf565b610960576001610963565b60005b6040518563ffffffff1660e01b8152600401610982949392919061400e565b60206040518083038186803b15801561099a57600080fd5b505afa1580156109ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d29190614038565b6109dc9190614067565b90506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca7786356020880135610a2460608a0160408b0161407f565b610a3460808b0160608c01613fdf565b610a3f576000610a42565b60015b610a5260808c0160608d01613fdf565b610a5c5789610a5e565b885b6040518663ffffffff1660e01b8152600401610a7e95949392919061409c565b600060405180830381600087803b158015610a9857600080fd5b505af1158015610aac573d6000803e3d6000fd5b50506040805160c081018252883581526020808a01359082015233935063c84a17499250908101610ae360808a0160608b01613fdf565b151581526020810188905260408101879052606001610b0560c08a018a6140d6565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b168152610b5c919060040161411c565b600060405180830381600087803b158015610b7657600080fd5b505af1158015610b8a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bb291908101906141b0565b9150610c816001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663af2f91ea8735602089013530610bfe60808c0160608d01613fdf565b610c09576001610c0c565b60005b6040518563ffffffff1660e01b8152600401610c2b949392919061400e565b60206040518083038186803b158015610c4357600080fd5b505afa158015610c57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7b9190614038565b82611ab3565b610c9085356020870135611ae3565b33602086013586357fb5203bea7ad91c36603035fd1320703929688fbe9d58e3513a502f9359c96037610cc960608a0160408b0161407f565b610cd960808b0160608c01613fdf565b604080516001600160a01b039093168352901515602083015281018990526060810188905260800160405180910390a4509193909250565b6000806000806060610d2587600188611b0d565b939b929a50909850965090945092505050565b6000806000806060610d258760018861218a565b60008381526001602090815260408083208584529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe279063f4af1a4b9088610d918961193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b031660448201526064015b60806040518083038186803b158015610ddf57600080fd5b505af4158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e179190613c40565b935093509350935093509350935093565b6000806000806060610e3c8660008061218a565b939a9299509097509550909350915050565b6000806000610e5b61195a565b610e72610e6d368690038601866141e4565b612629565b610e8184356020860135611a5f565b610f237f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610edd57600080fd5b505afa158015610ef1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f15919061428d565b6001600160a01b03166126cd565b8335600090815260016020908152604080832082880135845290915290819020905163e0b364cb60e01b8152600481019190915260a0850135602482015260c0850135604482015260e08501356064820152739ebf82942d914628623e2ff432deffd54dccfe279063e0b364cb9060840160606040518083038186803b158015610fac57600080fd5b505af4158015610fc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe491906142aa565b9194509250905061102d84356020860135611005606088016040890161407f565b6110156080890160608a0161407f565b61102560a08a0160808b0161407f565b888888612707565b61103c84356020860135611ae3565b33602085013585357f9f89f4362c689657241ac1ec1cc69db4d75234f93386d70e9519f271a4fdabb86110756060890160408a0161407f565b61108560808a0160608b0161407f565b61109560a08b0160808c0161407f565b604080516001600160a01b0394851681529284166020840152921681830152606081018990526080810188905260a0810187905290519081900360c00190a49193909250565b60008281526001602090815260408083208484529091528082209051636e4f4d7d60e11b8152600481019190915281908190739ebf82942d914628623e2ff432deffd54dccfe279063dc9e9afa9060240160606040518083038186803b15801561114457600080fd5b505af4158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c91906142aa565b9250925092509250925092565b6000806000606061119c856000806128bb565b93509350935093509193509193565b6000806000806111b961195a565b6111d06111cb368790038701876142d8565b612e2b565b6111df85356020870135611a5f565b843560009081526001602090815260408083208289013580855292528220739ebf82942d914628623e2ff432deffd54dccfe27926316a3a129929060c08a01359060e08b0135906101008c0135906101208d01359061123d9061193f565b6040516001600160e01b031960e08a901b1681526004810197909752602487019590955260448601939093526064850191909152608484015260a48301526001600160601b031660c482015260e40160806040518083038186803b1580156112a457600080fd5b505af41580156112b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112dc9190613c40565b92965090945092509050611328853560208701356113006060890160408a0161407f565b61131060808a0160608b0161407f565b61132060a08b0160808c0161407f565b898989612707565b80156113ca576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778635602088013561137460c08a0160a08b0161407f565b6002866040518663ffffffff1660e01b815260040161139795949392919061409c565b600060405180830381600087803b1580156113b157600080fd5b505af11580156113c5573d6000803e3d6000fd5b505050505b6113d985356020870135611ae3565b33602086013586357f19230150e464ac0c9fbd0ce4c7a7e38b10dd3385fcd80446f1850b27475130dc61141260608a0160408b0161407f565b61142260808b0160608c0161407f565b61143260a08c0160808d0161407f565b61144260c08d0160a08e0161407f565b604080516001600160a01b0395861681529385166020850152918416838301529092166060820152608081018a905260a0810189905260c0810188905260e081018790529051908190036101000190a49193509193565b6000806000806060610e3c86600080611b0d565b600082815260016020908152604080832084845290915281208190739ebf82942d914628623e2ff432deffd54dccfe2790636512a69d90856114ee8561193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b03166044820152606401610761565b600080600060606106db86600187612eee565b61154361195a565b82611550576115506133f7565b61155a600061193f565b6001600160601b031682101561157d5761157d82611578600061193f565b613410565b6001600160a01b038116611593576115936133f7565b61159d838361343b565b600083815260016020908152604080832085845290915290819020905163c3218b1d60e01b815260048101919091526001600160a01b0382166024820152739ebf82942d914628623e2ff432deffd54dccfe279063c3218b1d9060440160006040518083038186803b15801561161257600080fd5b505af4158015611626573d6000803e3d6000fd5b50505050505050565b600080600060606106db866001876128bb565b60008381526001602090815260408083208584529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe2790639d85a5849088886116888661193f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b031660448301526001600160601b03166064820152608401610dc7565b6116da84846119a5565b826116e5600061193f565b6001600160601b031611156117025761170283611578600061193f565b6001600160a01b038216611718576117186134fb565b6001600160a01b03811661172e5761172e613514565b60008481526001602090815260408083208684529091528120739ebf82942d914628623e2ff432deffd54dccfe2791634a93649f91908690869086906117739061193f565b6040516001600160e01b031960e088901b168152600481019590955260248501939093526001600160a01b0391821660448501521660648301526001600160601b0316608482015260a40160006040518083038186803b1580156117d657600080fd5b505af41580156117ea573d6000803e3d6000fd5b5050604080513381526001600160a01b03868116602083015285168183015290518693508792507f76b4de7b53928b4b4b5be34802c5db3b6ddf280ba50c509b55f939a91ea758f69181900360600190a350505050565b60008481526001602090815260408083208684529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe2790639d85a5849089896118878a61193f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b031660448301526001600160601b0316606482015260840160806040518083038186803b1580156118e257600080fd5b505af41580156118f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191a9190613c40565b929b919a509850909650945050505050565b6000806000606061119c85600080612eee565b60006119546001600160601b03831642614067565b92915050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146119a357604051634118e0bb60e11b815260040160405180910390fd5b565b60008281526001602090815260408083208484529091529020546001600160a01b03166119d4576119d461352d565b5050565b60208201516001600160601b0310156119f8576119f88260200151613546565b806001600160601b031682602001511015611a1b57611a1b826020015182613410565b60408201516001600160a01b0316611a3557611a356134fb565b611a428260800151613562565b60a08201511580611a5257508151155b156119d4576119d4613514565b600082815260208181526040808320848452909152902054611a89906001600160601b0316613594565b600091825260208281526040808420928452919052902080546001600160601b0319166002179055565b808210156119d457604051631c22ff0160e21b815260048101839052602481018290526044015b60405180910390fd5b600091825260208281526040808420928452919052902080546001600160601b0319166001179055565b6000806000806060611b1d61195a565b611b37611b2989614390565b611b328861193f565b6135e6565b611b46883560208a0135611a5f565b87356000908152600160209081526040808320828c013584529091529020739ebf82942d914628623e2ff432deffd54dccfe2790636951fc8c908a611b8a8a61193f565b6040518463ffffffff1660e01b8152600401611ba893929190614429565b60006040518083038186803b158015611bc057600080fd5b505af4158015611bd4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bfc91908101906144c7565b93985091965094509250905060008415611cc757604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90611c6a908d359060208f013590309060009060040161400e565b60206040518083038186803b158015611c8257600080fd5b505afa158015611c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cba9190614038565b611cc49190614067565b90505b60008415611d8857847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8c600001358d602001353060016040518563ffffffff1660e01b8152600401611d2b949392919061400e565b60206040518083038186803b158015611d4357600080fd5b505afa158015611d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7b9190614038565b611d859190614067565b90505b6000847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060026040518563ffffffff1660e01b8152600401611de6949392919061400e565b60206040518083038186803b158015611dfe57600080fd5b505afa158015611e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e369190614038565b611e409190614067565b6040805160e0810182528d3581526020808f0135908201528082018a905260608101899052608081018890526001600160a01b038b1660a082015260c08101879052905163321c8bed60e01b8152919250339163321c8bed91611ea59160040161458e565b600060405180830381600087803b158015611ebf57600080fd5b505af1158015611ed3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611efb91908101906141b0565b93508915611f1c5760405163335b3bef60e01b815260040160405180910390fd5b8615611fd657611fd67f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060006040518563ffffffff1660e01b8152600401611f80949392919061400e565b60206040518083038186803b158015611f9857600080fd5b505afa158015611fac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd09190614038565b84611ab3565b8515612090576120907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060016040518563ffffffff1660e01b815260040161203a949392919061400e565b60206040518083038186803b15801561205257600080fd5b505afa158015612066573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208a9190614038565b83611ab3565b6120ee7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060026040518563ffffffff1660e01b8152600401610c2b949392919061400e565b6120fd8b3560208d0135611ae3565b336001600160a01b03168b602001358c600001357f86838b111645453d71257de3cf164de8e18622d66009889e94f13128f595ef648e6040016020810190612145919061407f565b604080516001600160a01b039283168152918e16602083015281018c9052606081018b9052608081018a905260a00160405180910390a4505050939792965093509350565b600080600080606061219a61195a565b6121a9883560208a01356119a5565b6121c36121b5896145a1565b6121be8861193f565b61366a565b6121d2883560208a0135611a5f565b87356000908152600160209081526040808320828c013584529091529020739ebf82942d914628623e2ff432deffd54dccfe276312a71d0c828b6122158b61193f565b6040518463ffffffff1660e01b815260040161223393929190614652565b60006040518083038186803b15801561224b57600080fd5b505af415801561225f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261228791908101906144c7565b9399509197509550935091508415612335576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c01356122df60608e0160408f0161407f565b60008a6040518663ffffffff1660e01b815260040161230295949392919061409c565b600060405180830381600087803b15801561231c57600080fd5b505af1158015612330573d6000803e3d6000fd5b505050505b83156123d7576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c013561238160808e0160608f0161407f565b6001896040518663ffffffff1660e01b81526004016123a495949392919061409c565b600060405180830381600087803b1580156123be57600080fd5b505af11580156123d2573d6000803e3d6000fd5b505050505b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c013561241d60a08e0160808f0161407f565b6002886040518663ffffffff1660e01b815260040161244095949392919061409c565b600060405180830381600087803b15801561245a57600080fd5b505af115801561246e573d6000803e3d6000fd5b50506040805160e0810182528c3581526020808e01359082015280820189905260608101889052608081018790526001600160a01b038a1660a082015260c081018690529051638bb7053d60e01b8152339350638bb7053d92506124d5919060040161458e565b600060405180830381600087803b1580156124ef57600080fd5b505af1158015612503573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261252b91908101906141b0565b9150871561254c5760405163335b3bef60e01b815260040160405180910390fd5b336000908152600b820160205260409020612567908761371c565b612576893560208b0135611ae3565b3360208a01358a357fc36176a3330276b641a24dfd635dc6cfc1c9b9afcd0c5d677f73e06143fe0aff6125af60608e0160408f0161407f565b8d60600160208101906125c2919061407f565b8e60800160208101906125d5919061407f565b604080516001600160a01b039485168152928416602084015290831690820152908b166060820152608081018a905260a0810189905260c0810188905260e00160405180910390a450939792965093509350565b60408101516001600160a01b0316158061264e575060608101516001600160a01b0316155b80612664575060808101516001600160a01b0316155b15612671576126716134fb565b60208101516001600160601b031015612691576126918160200151613546565b60a08101511580156126a5575060c0810151155b80156126b3575060e0810151155b806126bd57508051155b156126ca576126ca613514565b50565b336001600160a01b038216146126ca57604051631bf9565d60e21b81523360048201526001600160a01b0382166024820152604401611ada565b82156127955760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca7790612762908b908b908b906000908a9060040161409c565b600060405180830381600087803b15801561277c57600080fd5b505af1158015612790573d6000803e3d6000fd5b505050505b81156128235760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca77906127f0908b908b908a90600190899060040161409c565b600060405180830381600087803b15801561280a57600080fd5b505af115801561281e573d6000803e3d6000fd5b505050505b80156128b15760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca779061287e908b908b908990600290889060040161409c565b600060405180830381600087803b15801561289857600080fd5b505af11580156128ac573d6000803e3d6000fd5b505050505b5050505050505050565b600080600060606128ca61195a565b6128d9873560208901356119a5565b6128ee6128e588614390565b611b328761193f565b6128fd87356020890135611a5f565b86356000908152600160209081526040808320828b013584529091529020739ebf82942d914628623e2ff432deffd54dccfe2790630fc5313390897f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006129838b61193f565b6040518663ffffffff1660e01b81526004016129a3959493929190614724565b60006040518083038186803b1580156129bb57600080fd5b505af41580156129cf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526129f791908101906147ce565b9296509094509250905060008415612ac057604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90612a63908c359060208e013590309060009060040161400e565b60206040518083038186803b158015612a7b57600080fd5b505afa158015612a8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab39190614038565b612abd9190614067565b90505b60008415612b7f57604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90612b22908d359060208f013590309060019060040161400e565b60206040518083038186803b158015612b3a57600080fd5b505afa158015612b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b729190614038565b612b7c9190614067565b90505b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c0135612bc560608e0160408f0161407f565b6002896040518663ffffffff1660e01b8152600401612be895949392919061409c565b600060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b50506040805160c0810182528c3581526020808e0135908201528082018a9052606081018990526080810188905260a0810187905290516343d44c9d60e11b81523393506387a8993a9250612c6e919060040161486e565b600060405180830381600087803b158015612c8857600080fd5b505af1158015612c9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cc491908101906141b0565b92508715612ce55760405163335b3bef60e01b815260040160405180910390fd5b8515612d4757604051635797c8f560e11b8152612d47906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea9061203a908d359060208f013590309060009060040161400e565b8415612da957604051635797c8f560e11b8152612da9906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90610c2b908d359060208f013590309060019060040161400e565b612db8893560208b0135611ae3565b3360208a01358a357f76ce53f2a5022311227d4d8c5c7b06eb0f9e0e238e30a85af33487e66bebfc9b612df160608e0160408f0161407f565b604080516001600160a01b039092168252602082018c905281018a90526060810189905260800160405180910390a4505093509350935093565b60408101516001600160a01b03161580612e50575060608101516001600160a01b0316155b80612e66575060808101516001600160a01b0316155b80612e7c575060a08101516001600160a01b0316155b15612e8957612e896134fb565b60208101516001600160601b031015612ea957612ea98160200151613546565b60c0810151158015612ebd575060e0810151155b8015612ecc5750610100810151155b80156126b3575061012081015115806126bd575080516126ca576126ca613514565b60008060006060612efd61195a565b612f0c873560208901356119a5565b612f26612f1888614881565b612f218761193f565b613761565b612f3587356020890135611a5f565b86356000908152600160209081526040808320828b013584529091529020739ebf82942d914628623e2ff432deffd54dccfe279063e0f1a7bb90897f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612fbb8b61193f565b6040518663ffffffff1660e01b8152600401612fdb9594939291906148d7565b60006040518083038186803b158015612ff357600080fd5b505af4158015613007573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261302f91908101906147ce565b604051635797c8f560e11b81529397509195509350915060009083906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90613096908c359060208e013590309060029060040161400e565b60206040518083038186803b1580156130ae57600080fd5b505afa1580156130c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e69190614038565b6130f09190614067565b90508415613194576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca77893560208b013561313e60608d0160408e0161407f565b60008a6040518663ffffffff1660e01b815260040161316195949392919061409c565b600060405180830381600087803b15801561317b57600080fd5b505af115801561318f573d6000803e3d6000fd5b505050505b8315613236576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca77893560208b01356131e060808d0160608e0161407f565b6001896040518663ffffffff1660e01b815260040161320395949392919061409c565b600060405180830381600087803b15801561321d57600080fd5b505af1158015613231573d6000803e3d6000fd5b505050505b6040805160c081018252893581526020808b013590820152808201879052606081018690526080810185905260a0810184905290516343317d3560e11b81523391638662fa6a9161328a919060040161486e565b600060405180830381600087803b1580156132a457600080fd5b505af11580156132b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132e091908101906141b0565b915086156133015760405163335b3bef60e01b815260040160405180910390fd5b604051635797c8f560e11b815261335d906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90610c2b908c359060208e013590309060029060040161400e565b61336c883560208a0135611ae3565b33602089013589357f4af09027ce1a32b4cf538f4feab5ae0d95f6498fcd7fbdcbacf5ab3e1ccc22066133a560608d0160408e0161407f565b6133b560808e0160608f0161407f565b604080516001600160a01b03938416815292909116602083015281018a9052606081018990526080810188905260a00160405180910390a45093509350935093565b604051631e1d0ab560e01b815260040160405180910390fd5b60405163647284e960e11b8152600481018390526001600160601b0382166024820152604401611ada565b6000828152602081815260408083208484529091529020546001600160601b03166119d457600082815260208181526040808320848452825280832080546001600160601b03191660019081179091558151808301909252948152908101928352600280549485018155918290525192027f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace810192909255517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf90910155565b60405163d92e233d60e01b815260040160405180910390fd5b60405163af458c0760e01b815260040160405180910390fd5b60405163277c720560e01b815260040160405180910390fd5b6040516335f135d360e01b815260048101829052602401611ada565b600281600181111561357657613576613ea2565b106126ca5760405163280503e760e11b815260040160405180910390fd5b6001600160601b0381166135bb5760405163e2228b1560e01b815260040160405180910390fd5b6001600160601b038116600214156126ca5760405163865a6de560e01b815260040160405180910390fd5b60208201516001600160601b031015613606576136068260200151613546565b806001600160601b03168260200151101561362957613629826020015182613410565b60408201516001600160a01b0316613643576136436134fb565b61365082606001516137df565b60808201511580611a52575081516119d4576119d4613514565b60208201516001600160601b03101561368a5761368a8260200151613546565b806001600160601b0316826020015110156136ad576136ad826020015182613410565b60408201516001600160a01b031615806136d2575060608201516001600160a01b0316155b806136e8575060808201516001600160a01b0316155b156136f5576136f56134fb565b6137028260a001516137df565b60c08201511580611a52575081516119d4576119d4613514565b8154819083906000906137399084906001600160a01b0316614972565b92506101000a8154816001600160a01b0302191690836001600160a01b031602179055505050565b60208201516001600160601b031015613781576137818260200151613546565b806001600160601b0316826020015110156137a4576137a4826020015182613410565b60408201516001600160a01b031615806137c9575060608201516001600160a01b0316155b156137d6576137d66134fb565b611a4282608001515b600481600381111561357657613576613ea2565b60006020828403121561380557600080fd5b5035919050565b6000806040838503121561381f57600080fd5b50508035926020909101359150565b600060e0828403121561384057600080fd5b50919050565b60006020828403121561385857600080fd5b81356001600160401b0381111561386e57600080fd5b61387a8482850161382e565b949350505050565b60005b8381101561389d578181015183820152602001613885565b838111156138ac576000848401525b50505050565b600081518084526138ca816020860160208601613882565b601f01601f19169290920160200192915050565b8381528260208201526060604082015260006138fd60608301846138b2565b95945050505050565b600060c0828403121561384057600080fd5b80356001600160601b038116811461392f57600080fd5b919050565b6000806040838503121561394757600080fd5b82356001600160401b0381111561395d57600080fd5b61396985828601613906565b92505061397860208401613918565b90509250929050565b60018060a01b038616815284602082015283604082015282606082015260a0608082015260006139b460a08301846138b2565b979650505050505050565b6000610100828403121561384057600080fd5b600080604083850312156139e557600080fd5b82356001600160401b038111156139fb57600080fd5b613969858286016139bf565b600080600060608486031215613a1c57600080fd5b8335925060208401359150613a3360408501613918565b90509250925092565b600060208284031215613a4e57600080fd5b81356001600160401b03811115613a6457600080fd5b61387a848285016139bf565b60006101008284031215613a8357600080fd5b613a8d83836139bf565b9392505050565b600060208284031215613aa657600080fd5b81356001600160401b03811115613abc57600080fd5b61387a84828501613906565b848152836020820152826040820152608060608201526000613aed60808301846138b2565b9695505050505050565b6000610140828403121561384057600080fd5b60008060408385031215613b1d57600080fd5b82356001600160401b03811115613b3357600080fd5b6139698582860161382e565b6001600160a01b03811681146126ca57600080fd5b600080600060608486031215613b6957600080fd5b83359250602084013591506040840135613b8281613b3f565b809150509250925092565b803561392f81613b3f565b60008060008060808587031215613bae57600080fd5b84359350602085013592506040850135613bc781613b3f565b91506060850135613bd781613b3f565b939692955090935050565b60008060008060808587031215613bf857600080fd5b84359350602085013592506040850135613c1181613b3f565b9150613c1f60608601613918565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b60008060008060808587031215613c5657600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008060408385031215613c8957600080fd5b505080516020909101519092909150565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613cd257613cd2613c9a565b60405290565b60405161014081016001600160401b0381118282101715613cd257613cd2613c9a565b60405160c081016001600160401b0381118282101715613cd257613cd2613c9a565b60405161010081016001600160401b0381118282101715613cd257613cd2613c9a565b604051601f8201601f191681016001600160401b0381118282101715613d6857613d68613c9a565b604052919050565b8035801515811461392f57600080fd5b80356002811061392f57600080fd5b60006001600160401b03821115613da857613da8613c9a565b50601f01601f191660200190565b600082601f830112613dc757600080fd5b8135613dda613dd582613d8f565b613d40565b818152846020838601011115613def57600080fd5b816020850160208301376000918101602001919091529392505050565b600060e08236031215613e1e57600080fd5b613e26613cb0565b8235815260208301356020820152613e4060408401613b8d565b6040820152613e5160608401613d70565b6060820152613e6260808401613d80565b608082015260a083013560a082015260c08301356001600160401b03811115613e8a57600080fd5b613e9636828601613db6565b60c08301525092915050565b634e487b7160e01b600052602160045260246000fd5b6000808335601e19843603018112613ecf57600080fd5b83016020810192503590506001600160401b03811115613eee57600080fd5b803603831315613efd57600080fd5b9250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526080602082015283356080820152602084013560a082015260006040850135613f5881613b3f565b6001600160a01b031660c0830152613f7260608601613d70565b151560e0830152613f8560808601613d80565b60028110613f9557613f95613ea2565b61010083015260a0850135610120830152613fb360c0860186613eb8565b60e0610140850152613fca61016085018284613f04565b60408501969096525050506060015292915050565b600060208284031215613ff157600080fd5b613a8d82613d70565b6003811061400a5761400a613ea2565b9052565b848152602081018490526001600160a01b0383166040820152608081016138fd6060830184613ffa565b60006020828403121561404a57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561407a5761407a614051565b500190565b60006020828403121561409157600080fd5b8135613a8d81613b3f565b858152602081018590526001600160a01b038416604082015260a081016140c66060830185613ffa565b8260808301529695505050505050565b6000808335601e198436030181126140ed57600080fd5b8301803591506001600160401b0382111561410757600080fd5b602001915036819003821315613efd57600080fd5b60208152815160208201526020820151604082015260408201511515606082015260608201516080820152608082015160a0820152600060a083015160c08084015261387a60e08401826138b2565b600082601f83011261417c57600080fd5b815161418a613dd582613d8f565b81815284602083860101111561419f57600080fd5b61387a826020830160208701613882565b6000602082840312156141c257600080fd5b81516001600160401b038111156141d857600080fd5b61387a8482850161416b565b60006101008083850312156141f857600080fd5b604051908101906001600160401b038211818310171561421a5761421a613c9a565b8160405283358152602084013560208201526040840135915061423c82613b3f565b81604082015261424e60608501613b8d565b606082015261425f60808501613b8d565b608082015260a084013560a082015260c084013560c082015260e084013560e0820152809250505092915050565b60006020828403121561429f57600080fd5b8151613a8d81613b3f565b6000806000606084860312156142bf57600080fd5b8351925060208401519150604084015190509250925092565b600061014082840312156142eb57600080fd5b6142f3613cd8565b823581526020830135602082015261430d60408401613b8d565b604082015261431e60608401613b8d565b606082015261432f60808401613b8d565b608082015261434060a08401613b8d565b60a082015260c0838101359082015260e080840135908201526101008084013590820152610120928301359281019290925250919050565b600481106126ca57600080fd5b803561392f81614378565b600060c082360312156143a257600080fd5b6143aa613cfb565b823581526020830135602082015260408301356143c681613b3f565b604082015260608301356143d981614378565b60608201526080838101359082015260a08301356001600160401b0381111561440157600080fd5b61440d36828601613db6565b60a08301525092915050565b600481106126ca576126ca613ea2565b8381526060602082015282356060820152602083013560808201526000604084013561445481613b3f565b6001600160a01b031660a0830152606084013561447081614378565b61447981614419565b60c0830152608084013560e083015261449560a0850185613eb8565b60c06101008501526144ac61012085018284613f04565b925050506001600160601b0383166040830152949350505050565b600080600080600060a086880312156144df57600080fd5b85516144ea81613b3f565b8095505060208601519350604086015192506060860151915060808601516001600160401b0381111561451c57600080fd5b6145288882890161416b565b9150509295509295909350565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260018060a01b0360a08201511660a0830152600060c082015160e060c085015261387a60e08501826138b2565b602081526000613a8d6020830184614535565b600061010082360312156145b457600080fd5b6145bc613d1d565b82358152602083013560208201526145d660408401613b8d565b60408201526145e760608401613b8d565b60608201526145f860808401613b8d565b608082015261460960a08401614385565b60a082015260c083013560c082015260e08301356001600160401b0381111561463157600080fd5b61463d36828601613db6565b60e08301525092915050565b61400a81614419565b8381526060602082015282356060820152602083013560808201526000604084013561467d81613b3f565b6001600160a01b031660a083015261469760608501613b8d565b6001600160a01b031660c08301526146b160808501613b8d565b6001600160a01b031660e08301526146cb60a08501614385565b6101006146da81850183614649565b60c08601356101208501526146f260e0870187613eb8565b92508161014086015261470a61016086018483613f04565b935050505061387a60408301846001600160601b03169052565b85815260a06020820152843560a0820152602085013560c08201526000604086013561474f81613b3f565b6001600160a01b031660e0830152606086013561476b81614378565b61477481614419565b610100830152608086013561012083015261479260a0870187613eb8565b60c06101408501526147a961016085018284613f04565b92505050846040830152836060830152613aed60808301846001600160601b03169052565b600080600080608085870312156147e457600080fd5b84519350602085015192506040850151915060608501516001600160401b0381111561480f57600080fd5b61481b8782880161416b565b91505092959194509250565b8051825260208101516020830152604081015160408301526060810151606083015260808101516080830152600060a082015160c060a085015261387a60c08501826138b2565b602081526000613a8d6020830184614827565b600060e0823603121561489357600080fd5b61489b613cb0565b82358152602083013560208201526148b560408401613b8d565b60408201526148c660608401613b8d565b6060820152613e6260808401614385565b85815260a06020820152843560a0820152602085013560c08201526000604086013561490281613b3f565b6001600160a01b0390811660e084015260608701359061492182613b3f565b1661010083015261493460808701614385565b614942610120840182614649565b5060a086013561014083015261495b60c0870187613eb8565b60e06101608501526147a961018085018284613f04565b60006001600160a01b038381169083168181101561499257614992614051565b03939250505056fea264697066735822122066046bd44d388e62516ef1c5989c4c938b6a66df609121c5cf10154ae8ac5d6d64736f6c63430008080033a2646970667358221220c4e40d78b2407bde37951e56bc5d45641553fb7436a728d1bc55b8eeced6fe3764736f6c6343000808003300000000000000000000000051b420775f39c39b04fd11cf8d89b8a3204f647900000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c4000000000000000000000000000000000000000000000000000000000000199a0000000000000000000000000000000000000000000000000000000000002aaa

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063b3461c871161008c578063d81e842311610066578063d81e84231461023b578063e30c39781461024e578063ebbc496514610261578063fc0d92bd1461026957600080fd5b8063b3461c87146101d3578063c2bc2efc146101fa578063c42069ec1461022657600080fd5b80632d883a73146100d45780633e68680a146101045780638da5cb5b146101175780639ed3edf01461012a578063ad4d4e291461015f578063b0e21e8a146101ac575b600080fd5b6100e76100e23660046107fc565b610271565b6040516001600160a01b0390911681526020015b60405180910390f35b6100e761011236600461082a565b61029b565b6004546100e7906001600160a01b031681565b6101517f000000000000000000000000000000000000000000000000000000000000199a81565b6040519081526020016100fb565b600054600154600254600354610181936001600160a01b039081169316919084565b604080516001600160a01b0395861681529490931660208501529183015260608201526080016100fb565b6101517f0000000000000000000000000000000000000000000000000000000000002aaa81565b6100e77f00000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c481565b6100e7610208366004610863565b6001600160a01b039081166000908152600660205260409020541690565b610239610234366004610863565b61046e565b005b6100e761024936600461082a565b610506565b6005546100e7906001600160a01b031681565b6102396105cf565b600754610151565b6007818154811061028157600080fd5b6000918252602090912001546001600160a01b0316905081565b60405163d81e842360e01b81526001600160a01b038381166004830152828116602483015260009182917f00000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c4169063d81e84239060440160206040518083038186803b15801561030957600080fd5b505afa15801561031d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103419190610887565b90506001600160a01b0381166103595761035961063a565b6001600160a01b0380821660009081526006602052604090205416915081156103845761038461063a565b6103d030827f000000000000000000000000000000000000000000000000000000000000199a7f0000000000000000000000000000000000000000000000000000000000002aaa610653565b6001600160a01b0380831660008181526006602052604080822080549486166001600160a01b03199586168117909155600780546001810182559084527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180549095168117909455519395509192909133917fb224da6575b2c2ffd42454faedb236f7dbe5f92a0c96bb99c0273dbe98464c7e9190a45092915050565b600454610483906001600160a01b0316610726565b6001600160a01b0381166104995761049961063a565b6004546104b2906001600160a01b038381169116610768565b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f167d3e9c1016ab80e58802ca9da10ce5c6a0f4debc46a2e7a2cd9e56899a4fb59060200160405180910390a150565b60405163d81e842360e01b81526001600160a01b038381166004830152828116602483015260009182917f00000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c4169063d81e84239060440160206040518083038186803b15801561057457600080fd5b505afa158015610588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ac9190610887565b6001600160a01b0390811660009081526006602052604090205416949350505050565b6005546105e69033906001600160a01b03166107aa565b60048054336001600160a01b031991821681179092556005805490911690556040519081527f31775a018194f26a792a0203e972faf15303e08a913cdc54497278e48de669099060200160405180910390a1565b60405163d92e233d60e01b815260040160405180910390fd5b604080516080810182526001600160a01b0386811680835290861660208084018290528385018790526060909301859052600080546001600160a01b03199081169093178155600180549093168217909255600286905560038590558351928301529101604051602081830303815290604052805190602001206040516106d9906107ef565b8190604051809103906000f59050801580156106f9573d6000803e3d6000fd5b50600080546001600160a01b03199081168255600180549091169055600281905560035595945050505050565b336001600160a01b0382161461076557604051631bf9565d60e21b81523360048201526001600160a01b03821660248201526044015b60405180910390fd5b50565b806001600160a01b0316826001600160a01b031614156107a657604051632de305cb60e11b81526001600160a01b038216600482015260240161075c565b5050565b806001600160a01b0316826001600160a01b0316146107a65760405163f93f289d60e01b81526001600160a01b0380841660048301528216602482015260440161075c565b614c1c806108a583390190565b60006020828403121561080e57600080fd5b5035919050565b6001600160a01b038116811461076557600080fd5b6000806040838503121561083d57600080fd5b823561084881610815565b9150602083013561085881610815565b809150509250929050565b60006020828403121561087557600080fd5b813561088081610815565b9392505050565b60006020828403121561089957600080fd5b81516108808161081556fe6101206040523480156200001257600080fd5b503060601b60809081526040805163ad4d4e2960e01b81529051339263ad4d4e299260048082019391829003018186803b1580156200005057600080fd5b505afa15801562000065573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200008b9190620000cf565b6101005260e0526001600160601b0319606091821b811660c05291901b1660a05262000117565b80516001600160a01b0381168114620000ca57600080fd5b919050565b60008060008060808587031215620000e657600080fd5b620000f185620000b2565b93506200010160208601620000b2565b6040860151606090960151949790965092505050565b60805160601c60a05160601c60c05160601c60e051610100516149d06200024c600039600081816104c0015281816108740152818161295b0152612f9301526000818161043c0152818161071b015281816108520152818161293a0152612f720152600081816101e401528181610918015281816109e801528181610bc10152818161133801528181611c2901528181611cd201528181611d8d01528181611f2701528181611fe101528181612095015281816122a301528181612345015281816123e101528181612724015281816127b20152818161284001528181612a2201528181612ae101528181612b8901528181612d0601528181612d680152818161305501528181613102015281816131a4015261331c0152600081816102d30152610e860152600061196501526149d06000f3fe608060405234801561001057600080fd5b50600436106101da5760003560e01c806382b9008f11610104578063b0e21e8a116100a2578063c0e0c4c611610071578063c0e0c4c61461053c578063de64af931461054f578063e0260d1114610562578063f78333a31461057557600080fd5b8063b0e21e8a146104bb578063b15044ac146104e2578063bfec5c8a14610516578063c0d27cc51461052957600080fd5b80639ed3edf0116100de5780639ed3edf0146104375780639eee6e511461045e578063a8f403b714610471578063ad118b02146104a657600080fd5b806382b9008f146103fe578063847a1013146104115780638fdc5c991461042457600080fd5b80635e2659f31161017c5780636f682a531161014b5780636f682a531461038057806372f8f85c1461039257806378fb9d38146103a55780637ffd3a70146103c857600080fd5b80635e2659f3146103195780636016477d1461032c578063630b15f31461033f578063641972451461035257600080fd5b80633a9d71e7116101b85780633a9d71e7146102845780633aee875c146102ac5780634219dc40146102ce5780634e9ca003146102f557600080fd5b806311c959fd146101df5780632d883a73146102235780633377633314610251575b600080fd5b6102067f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6102366102313660046137f3565b6105b6565b6040805182518152602092830151928101929092520161021a565b61026461025f36600461380c565b610611565b60408051948552602085019390935291830152606082015260800161021a565b61029761029236600461380c565b6106eb565b6040805192835260208301919091520161021a565b6102bf6102ba366004613846565b6107bb565b60405161021a939291906138de565b6102067f000000000000000000000000000000000000000000000000000000000000000081565b610308610303366004613934565b610d11565b60405161021a959493929190613981565b6103086103273660046139d2565b610d38565b61026461033a366004613a07565b610d4c565b61030861034d366004613a3c565b610e28565b610365610360366004613a70565b610e4e565b6040805193845260208401929092529082015260600161021a565b6002545b60405190815260200161021a565b6103656103a036600461380c565b6110db565b6103b86103b3366004613a94565b611189565b60405161021a9493929190613ac8565b6102976103d636600461380c565b6000918252600160209081526040808420928452919052902060028101546003909101549091565b61026461040c366004613af7565b6111ab565b61030861041f366004613a94565b611499565b61029761043236600461380c565b6114ad565b6103847f000000000000000000000000000000000000000000000000000000000000000081565b6103b861046c366004613b0a565b611528565b61020661047f36600461380c565b6000918252600160208181526040808520938552929052912001546001600160a01b031690565b6104b96104b4366004613b54565b61153b565b005b6103847f000000000000000000000000000000000000000000000000000000000000000081565b6102066104f036600461380c565b60009182526001602090815260408084209284529190529020546001600160a01b031690565b6103b8610524366004613934565b61162f565b610264610537366004613b54565b611642565b6104b961054a366004613b98565b6116d0565b61026461055d366004613be2565b611841565b6103b8610570366004613846565b61192c565b610206610583366004613b54565b60009283526001602090815260408085209385529281528284206001600160a01b039283168552600b0190529120541690565b6040805180820190915260008082526020820152600282815481106105dd576105dd613c2a565b9060005260206000209060020201604051806040016040529081600082015481526020016001820154815250509050919050565b60008281526001602090815260408083208484529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe279063f4af1a4b90876106568561193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b0316604482015260640160806040518083038186803b1580156106a357600080fd5b505af41580156106b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106db9190613c40565b9299919850965090945092505050565b600082815260016020908152604080832084845290915280822090516313571e0d60e11b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000060248201528190739ebf82942d914628623e2ff432deffd54dccfe27906326ae3c1a906044015b604080518083038186803b15801561077857600080fd5b505af415801561078c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b09190613c76565b909590945092505050565b60008060606107c861195a565b6107d7843560208601356119a5565b6107f26107e385613e0c565b6107ed600061193f565b6119d8565b61080184356020860135611a5f565b833560009081526001602090815260408083208288013584529091529081902090516302b65c4f60e11b8152739ebf82942d914628623e2ff432deffd54dccfe279163056cb89e9161089c919088907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090600401613f2d565b604080518083038186803b1580156108b357600080fd5b505af41580156108c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108eb9190613c76565b909350915060006109026080860160608701613fdf565b61090c578261090e565b835b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663af2f91ea873560208901353061095560808c0160608d01613fdf565b610960576001610963565b60005b6040518563ffffffff1660e01b8152600401610982949392919061400e565b60206040518083038186803b15801561099a57600080fd5b505afa1580156109ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d29190614038565b6109dc9190614067565b90506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca7786356020880135610a2460608a0160408b0161407f565b610a3460808b0160608c01613fdf565b610a3f576000610a42565b60015b610a5260808c0160608d01613fdf565b610a5c5789610a5e565b885b6040518663ffffffff1660e01b8152600401610a7e95949392919061409c565b600060405180830381600087803b158015610a9857600080fd5b505af1158015610aac573d6000803e3d6000fd5b50506040805160c081018252883581526020808a01359082015233935063c84a17499250908101610ae360808a0160608b01613fdf565b151581526020810188905260408101879052606001610b0560c08a018a6140d6565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b168152610b5c919060040161411c565b600060405180830381600087803b158015610b7657600080fd5b505af1158015610b8a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610bb291908101906141b0565b9150610c816001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663af2f91ea8735602089013530610bfe60808c0160608d01613fdf565b610c09576001610c0c565b60005b6040518563ffffffff1660e01b8152600401610c2b949392919061400e565b60206040518083038186803b158015610c4357600080fd5b505afa158015610c57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7b9190614038565b82611ab3565b610c9085356020870135611ae3565b33602086013586357fb5203bea7ad91c36603035fd1320703929688fbe9d58e3513a502f9359c96037610cc960608a0160408b0161407f565b610cd960808b0160608c01613fdf565b604080516001600160a01b039093168352901515602083015281018990526060810188905260800160405180910390a4509193909250565b6000806000806060610d2587600188611b0d565b939b929a50909850965090945092505050565b6000806000806060610d258760018861218a565b60008381526001602090815260408083208584529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe279063f4af1a4b9088610d918961193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b031660448201526064015b60806040518083038186803b158015610ddf57600080fd5b505af4158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e179190613c40565b935093509350935093509350935093565b6000806000806060610e3c8660008061218a565b939a9299509097509550909350915050565b6000806000610e5b61195a565b610e72610e6d368690038601866141e4565b612629565b610e8184356020860135611a5f565b610f237f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610edd57600080fd5b505afa158015610ef1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f15919061428d565b6001600160a01b03166126cd565b8335600090815260016020908152604080832082880135845290915290819020905163e0b364cb60e01b8152600481019190915260a0850135602482015260c0850135604482015260e08501356064820152739ebf82942d914628623e2ff432deffd54dccfe279063e0b364cb9060840160606040518083038186803b158015610fac57600080fd5b505af4158015610fc0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe491906142aa565b9194509250905061102d84356020860135611005606088016040890161407f565b6110156080890160608a0161407f565b61102560a08a0160808b0161407f565b888888612707565b61103c84356020860135611ae3565b33602085013585357f9f89f4362c689657241ac1ec1cc69db4d75234f93386d70e9519f271a4fdabb86110756060890160408a0161407f565b61108560808a0160608b0161407f565b61109560a08b0160808c0161407f565b604080516001600160a01b0394851681529284166020840152921681830152606081018990526080810188905260a0810187905290519081900360c00190a49193909250565b60008281526001602090815260408083208484529091528082209051636e4f4d7d60e11b8152600481019190915281908190739ebf82942d914628623e2ff432deffd54dccfe279063dc9e9afa9060240160606040518083038186803b15801561114457600080fd5b505af4158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c91906142aa565b9250925092509250925092565b6000806000606061119c856000806128bb565b93509350935093509193509193565b6000806000806111b961195a565b6111d06111cb368790038701876142d8565b612e2b565b6111df85356020870135611a5f565b843560009081526001602090815260408083208289013580855292528220739ebf82942d914628623e2ff432deffd54dccfe27926316a3a129929060c08a01359060e08b0135906101008c0135906101208d01359061123d9061193f565b6040516001600160e01b031960e08a901b1681526004810197909752602487019590955260448601939093526064850191909152608484015260a48301526001600160601b031660c482015260e40160806040518083038186803b1580156112a457600080fd5b505af41580156112b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112dc9190613c40565b92965090945092509050611328853560208701356113006060890160408a0161407f565b61131060808a0160608b0161407f565b61132060a08b0160808c0161407f565b898989612707565b80156113ca576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778635602088013561137460c08a0160a08b0161407f565b6002866040518663ffffffff1660e01b815260040161139795949392919061409c565b600060405180830381600087803b1580156113b157600080fd5b505af11580156113c5573d6000803e3d6000fd5b505050505b6113d985356020870135611ae3565b33602086013586357f19230150e464ac0c9fbd0ce4c7a7e38b10dd3385fcd80446f1850b27475130dc61141260608a0160408b0161407f565b61142260808b0160608c0161407f565b61143260a08c0160808d0161407f565b61144260c08d0160a08e0161407f565b604080516001600160a01b0395861681529385166020850152918416838301529092166060820152608081018a905260a0810189905260c0810188905260e081018790529051908190036101000190a49193509193565b6000806000806060610e3c86600080611b0d565b600082815260016020908152604080832084845290915281208190739ebf82942d914628623e2ff432deffd54dccfe2790636512a69d90856114ee8561193f565b6040516001600160e01b031960e086901b168152600481019390935260248301919091526001600160601b03166044820152606401610761565b600080600060606106db86600187612eee565b61154361195a565b82611550576115506133f7565b61155a600061193f565b6001600160601b031682101561157d5761157d82611578600061193f565b613410565b6001600160a01b038116611593576115936133f7565b61159d838361343b565b600083815260016020908152604080832085845290915290819020905163c3218b1d60e01b815260048101919091526001600160a01b0382166024820152739ebf82942d914628623e2ff432deffd54dccfe279063c3218b1d9060440160006040518083038186803b15801561161257600080fd5b505af4158015611626573d6000803e3d6000fd5b50505050505050565b600080600060606106db866001876128bb565b60008381526001602090815260408083208584529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe2790639d85a5849088886116888661193f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b031660448301526001600160601b03166064820152608401610dc7565b6116da84846119a5565b826116e5600061193f565b6001600160601b031611156117025761170283611578600061193f565b6001600160a01b038216611718576117186134fb565b6001600160a01b03811661172e5761172e613514565b60008481526001602090815260408083208684529091528120739ebf82942d914628623e2ff432deffd54dccfe2791634a93649f91908690869086906117739061193f565b6040516001600160e01b031960e088901b168152600481019590955260248501939093526001600160a01b0391821660448501521660648301526001600160601b0316608482015260a40160006040518083038186803b1580156117d657600080fd5b505af41580156117ea573d6000803e3d6000fd5b5050604080513381526001600160a01b03868116602083015285168183015290518693508792507f76b4de7b53928b4b4b5be34802c5db3b6ddf280ba50c509b55f939a91ea758f69181900360600190a350505050565b60008481526001602090815260408083208684529091528120819081908190739ebf82942d914628623e2ff432deffd54dccfe2790639d85a5849089896118878a61193f565b6040516001600160e01b031960e087901b168152600481019490945260248401929092526001600160a01b031660448301526001600160601b0316606482015260840160806040518083038186803b1580156118e257600080fd5b505af41580156118f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061191a9190613c40565b929b919a509850909650945050505050565b6000806000606061119c85600080612eee565b60006119546001600160601b03831642614067565b92915050565b306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146119a357604051634118e0bb60e11b815260040160405180910390fd5b565b60008281526001602090815260408083208484529091529020546001600160a01b03166119d4576119d461352d565b5050565b60208201516001600160601b0310156119f8576119f88260200151613546565b806001600160601b031682602001511015611a1b57611a1b826020015182613410565b60408201516001600160a01b0316611a3557611a356134fb565b611a428260800151613562565b60a08201511580611a5257508151155b156119d4576119d4613514565b600082815260208181526040808320848452909152902054611a89906001600160601b0316613594565b600091825260208281526040808420928452919052902080546001600160601b0319166002179055565b808210156119d457604051631c22ff0160e21b815260048101839052602481018290526044015b60405180910390fd5b600091825260208281526040808420928452919052902080546001600160601b0319166001179055565b6000806000806060611b1d61195a565b611b37611b2989614390565b611b328861193f565b6135e6565b611b46883560208a0135611a5f565b87356000908152600160209081526040808320828c013584529091529020739ebf82942d914628623e2ff432deffd54dccfe2790636951fc8c908a611b8a8a61193f565b6040518463ffffffff1660e01b8152600401611ba893929190614429565b60006040518083038186803b158015611bc057600080fd5b505af4158015611bd4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bfc91908101906144c7565b93985091965094509250905060008415611cc757604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90611c6a908d359060208f013590309060009060040161400e565b60206040518083038186803b158015611c8257600080fd5b505afa158015611c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cba9190614038565b611cc49190614067565b90505b60008415611d8857847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8c600001358d602001353060016040518563ffffffff1660e01b8152600401611d2b949392919061400e565b60206040518083038186803b158015611d4357600080fd5b505afa158015611d57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7b9190614038565b611d859190614067565b90505b6000847f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060026040518563ffffffff1660e01b8152600401611de6949392919061400e565b60206040518083038186803b158015611dfe57600080fd5b505afa158015611e12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e369190614038565b611e409190614067565b6040805160e0810182528d3581526020808f0135908201528082018a905260608101899052608081018890526001600160a01b038b1660a082015260c08101879052905163321c8bed60e01b8152919250339163321c8bed91611ea59160040161458e565b600060405180830381600087803b158015611ebf57600080fd5b505af1158015611ed3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611efb91908101906141b0565b93508915611f1c5760405163335b3bef60e01b815260040160405180910390fd5b8615611fd657611fd67f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060006040518563ffffffff1660e01b8152600401611f80949392919061400e565b60206040518083038186803b158015611f9857600080fd5b505afa158015611fac573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd09190614038565b84611ab3565b8515612090576120907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060016040518563ffffffff1660e01b815260040161203a949392919061400e565b60206040518083038186803b15801561205257600080fd5b505afa158015612066573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208a9190614038565b83611ab3565b6120ee7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663af2f91ea8d600001358e602001353060026040518563ffffffff1660e01b8152600401610c2b949392919061400e565b6120fd8b3560208d0135611ae3565b336001600160a01b03168b602001358c600001357f86838b111645453d71257de3cf164de8e18622d66009889e94f13128f595ef648e6040016020810190612145919061407f565b604080516001600160a01b039283168152918e16602083015281018c9052606081018b9052608081018a905260a00160405180910390a4505050939792965093509350565b600080600080606061219a61195a565b6121a9883560208a01356119a5565b6121c36121b5896145a1565b6121be8861193f565b61366a565b6121d2883560208a0135611a5f565b87356000908152600160209081526040808320828c013584529091529020739ebf82942d914628623e2ff432deffd54dccfe276312a71d0c828b6122158b61193f565b6040518463ffffffff1660e01b815260040161223393929190614652565b60006040518083038186803b15801561224b57600080fd5b505af415801561225f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261228791908101906144c7565b9399509197509550935091508415612335576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c01356122df60608e0160408f0161407f565b60008a6040518663ffffffff1660e01b815260040161230295949392919061409c565b600060405180830381600087803b15801561231c57600080fd5b505af1158015612330573d6000803e3d6000fd5b505050505b83156123d7576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c013561238160808e0160608f0161407f565b6001896040518663ffffffff1660e01b81526004016123a495949392919061409c565b600060405180830381600087803b1580156123be57600080fd5b505af11580156123d2573d6000803e3d6000fd5b505050505b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c013561241d60a08e0160808f0161407f565b6002886040518663ffffffff1660e01b815260040161244095949392919061409c565b600060405180830381600087803b15801561245a57600080fd5b505af115801561246e573d6000803e3d6000fd5b50506040805160e0810182528c3581526020808e01359082015280820189905260608101889052608081018790526001600160a01b038a1660a082015260c081018690529051638bb7053d60e01b8152339350638bb7053d92506124d5919060040161458e565b600060405180830381600087803b1580156124ef57600080fd5b505af1158015612503573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261252b91908101906141b0565b9150871561254c5760405163335b3bef60e01b815260040160405180910390fd5b336000908152600b820160205260409020612567908761371c565b612576893560208b0135611ae3565b3360208a01358a357fc36176a3330276b641a24dfd635dc6cfc1c9b9afcd0c5d677f73e06143fe0aff6125af60608e0160408f0161407f565b8d60600160208101906125c2919061407f565b8e60800160208101906125d5919061407f565b604080516001600160a01b039485168152928416602084015290831690820152908b166060820152608081018a905260a0810189905260c0810188905260e00160405180910390a450939792965093509350565b60408101516001600160a01b0316158061264e575060608101516001600160a01b0316155b80612664575060808101516001600160a01b0316155b15612671576126716134fb565b60208101516001600160601b031015612691576126918160200151613546565b60a08101511580156126a5575060c0810151155b80156126b3575060e0810151155b806126bd57508051155b156126ca576126ca613514565b50565b336001600160a01b038216146126ca57604051631bf9565d60e21b81523360048201526001600160a01b0382166024820152604401611ada565b82156127955760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca7790612762908b908b908b906000908a9060040161409c565b600060405180830381600087803b15801561277c57600080fd5b505af1158015612790573d6000803e3d6000fd5b505050505b81156128235760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca77906127f0908b908b908a90600190899060040161409c565b600060405180830381600087803b15801561280a57600080fd5b505af115801561281e573d6000803e3d6000fd5b505050505b80156128b15760405163b2ceca7760e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b2ceca779061287e908b908b908990600290889060040161409c565b600060405180830381600087803b15801561289857600080fd5b505af11580156128ac573d6000803e3d6000fd5b505050505b5050505050505050565b600080600060606128ca61195a565b6128d9873560208901356119a5565b6128ee6128e588614390565b611b328761193f565b6128fd87356020890135611a5f565b86356000908152600160209081526040808320828b013584529091529020739ebf82942d914628623e2ff432deffd54dccfe2790630fc5313390897f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006129838b61193f565b6040518663ffffffff1660e01b81526004016129a3959493929190614724565b60006040518083038186803b1580156129bb57600080fd5b505af41580156129cf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526129f791908101906147ce565b9296509094509250905060008415612ac057604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90612a63908c359060208e013590309060009060040161400e565b60206040518083038186803b158015612a7b57600080fd5b505afa158015612a8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ab39190614038565b612abd9190614067565b90505b60008415612b7f57604051635797c8f560e11b815285906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90612b22908d359060208f013590309060019060040161400e565b60206040518083038186803b158015612b3a57600080fd5b505afa158015612b4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b729190614038565b612b7c9190614067565b90505b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca778a3560208c0135612bc560608e0160408f0161407f565b6002896040518663ffffffff1660e01b8152600401612be895949392919061409c565b600060405180830381600087803b158015612c0257600080fd5b505af1158015612c16573d6000803e3d6000fd5b50506040805160c0810182528c3581526020808e0135908201528082018a9052606081018990526080810188905260a0810187905290516343d44c9d60e11b81523393506387a8993a9250612c6e919060040161486e565b600060405180830381600087803b158015612c8857600080fd5b505af1158015612c9c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cc491908101906141b0565b92508715612ce55760405163335b3bef60e01b815260040160405180910390fd5b8515612d4757604051635797c8f560e11b8152612d47906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea9061203a908d359060208f013590309060009060040161400e565b8415612da957604051635797c8f560e11b8152612da9906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90610c2b908d359060208f013590309060019060040161400e565b612db8893560208b0135611ae3565b3360208a01358a357f76ce53f2a5022311227d4d8c5c7b06eb0f9e0e238e30a85af33487e66bebfc9b612df160608e0160408f0161407f565b604080516001600160a01b039092168252602082018c905281018a90526060810189905260800160405180910390a4505093509350935093565b60408101516001600160a01b03161580612e50575060608101516001600160a01b0316155b80612e66575060808101516001600160a01b0316155b80612e7c575060a08101516001600160a01b0316155b15612e8957612e896134fb565b60208101516001600160601b031015612ea957612ea98160200151613546565b60c0810151158015612ebd575060e0810151155b8015612ecc5750610100810151155b80156126b3575061012081015115806126bd575080516126ca576126ca613514565b60008060006060612efd61195a565b612f0c873560208901356119a5565b612f26612f1888614881565b612f218761193f565b613761565b612f3587356020890135611a5f565b86356000908152600160209081526040808320828b013584529091529020739ebf82942d914628623e2ff432deffd54dccfe279063e0f1a7bb90897f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612fbb8b61193f565b6040518663ffffffff1660e01b8152600401612fdb9594939291906148d7565b60006040518083038186803b158015612ff357600080fd5b505af4158015613007573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261302f91908101906147ce565b604051635797c8f560e11b81529397509195509350915060009083906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90613096908c359060208e013590309060029060040161400e565b60206040518083038186803b1580156130ae57600080fd5b505afa1580156130c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e69190614038565b6130f09190614067565b90508415613194576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca77893560208b013561313e60608d0160408e0161407f565b60008a6040518663ffffffff1660e01b815260040161316195949392919061409c565b600060405180830381600087803b15801561317b57600080fd5b505af115801561318f573d6000803e3d6000fd5b505050505b8315613236576001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663b2ceca77893560208b01356131e060808d0160608e0161407f565b6001896040518663ffffffff1660e01b815260040161320395949392919061409c565b600060405180830381600087803b15801561321d57600080fd5b505af1158015613231573d6000803e3d6000fd5b505050505b6040805160c081018252893581526020808b013590820152808201879052606081018690526080810185905260a0810184905290516343317d3560e11b81523391638662fa6a9161328a919060040161486e565b600060405180830381600087803b1580156132a457600080fd5b505af11580156132b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526132e091908101906141b0565b915086156133015760405163335b3bef60e01b815260040160405180910390fd5b604051635797c8f560e11b815261335d906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063af2f91ea90610c2b908c359060208e013590309060029060040161400e565b61336c883560208a0135611ae3565b33602089013589357f4af09027ce1a32b4cf538f4feab5ae0d95f6498fcd7fbdcbacf5ab3e1ccc22066133a560608d0160408e0161407f565b6133b560808e0160608f0161407f565b604080516001600160a01b03938416815292909116602083015281018a9052606081018990526080810188905260a00160405180910390a45093509350935093565b604051631e1d0ab560e01b815260040160405180910390fd5b60405163647284e960e11b8152600481018390526001600160601b0382166024820152604401611ada565b6000828152602081815260408083208484529091529020546001600160601b03166119d457600082815260208181526040808320848452825280832080546001600160601b03191660019081179091558151808301909252948152908101928352600280549485018155918290525192027f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace810192909255517f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf90910155565b60405163d92e233d60e01b815260040160405180910390fd5b60405163af458c0760e01b815260040160405180910390fd5b60405163277c720560e01b815260040160405180910390fd5b6040516335f135d360e01b815260048101829052602401611ada565b600281600181111561357657613576613ea2565b106126ca5760405163280503e760e11b815260040160405180910390fd5b6001600160601b0381166135bb5760405163e2228b1560e01b815260040160405180910390fd5b6001600160601b038116600214156126ca5760405163865a6de560e01b815260040160405180910390fd5b60208201516001600160601b031015613606576136068260200151613546565b806001600160601b03168260200151101561362957613629826020015182613410565b60408201516001600160a01b0316613643576136436134fb565b61365082606001516137df565b60808201511580611a52575081516119d4576119d4613514565b60208201516001600160601b03101561368a5761368a8260200151613546565b806001600160601b0316826020015110156136ad576136ad826020015182613410565b60408201516001600160a01b031615806136d2575060608201516001600160a01b0316155b806136e8575060808201516001600160a01b0316155b156136f5576136f56134fb565b6137028260a001516137df565b60c08201511580611a52575081516119d4576119d4613514565b8154819083906000906137399084906001600160a01b0316614972565b92506101000a8154816001600160a01b0302191690836001600160a01b031602179055505050565b60208201516001600160601b031015613781576137818260200151613546565b806001600160601b0316826020015110156137a4576137a4826020015182613410565b60408201516001600160a01b031615806137c9575060608201516001600160a01b0316155b156137d6576137d66134fb565b611a4282608001515b600481600381111561357657613576613ea2565b60006020828403121561380557600080fd5b5035919050565b6000806040838503121561381f57600080fd5b50508035926020909101359150565b600060e0828403121561384057600080fd5b50919050565b60006020828403121561385857600080fd5b81356001600160401b0381111561386e57600080fd5b61387a8482850161382e565b949350505050565b60005b8381101561389d578181015183820152602001613885565b838111156138ac576000848401525b50505050565b600081518084526138ca816020860160208601613882565b601f01601f19169290920160200192915050565b8381528260208201526060604082015260006138fd60608301846138b2565b95945050505050565b600060c0828403121561384057600080fd5b80356001600160601b038116811461392f57600080fd5b919050565b6000806040838503121561394757600080fd5b82356001600160401b0381111561395d57600080fd5b61396985828601613906565b92505061397860208401613918565b90509250929050565b60018060a01b038616815284602082015283604082015282606082015260a0608082015260006139b460a08301846138b2565b979650505050505050565b6000610100828403121561384057600080fd5b600080604083850312156139e557600080fd5b82356001600160401b038111156139fb57600080fd5b613969858286016139bf565b600080600060608486031215613a1c57600080fd5b8335925060208401359150613a3360408501613918565b90509250925092565b600060208284031215613a4e57600080fd5b81356001600160401b03811115613a6457600080fd5b61387a848285016139bf565b60006101008284031215613a8357600080fd5b613a8d83836139bf565b9392505050565b600060208284031215613aa657600080fd5b81356001600160401b03811115613abc57600080fd5b61387a84828501613906565b848152836020820152826040820152608060608201526000613aed60808301846138b2565b9695505050505050565b6000610140828403121561384057600080fd5b60008060408385031215613b1d57600080fd5b82356001600160401b03811115613b3357600080fd5b6139698582860161382e565b6001600160a01b03811681146126ca57600080fd5b600080600060608486031215613b6957600080fd5b83359250602084013591506040840135613b8281613b3f565b809150509250925092565b803561392f81613b3f565b60008060008060808587031215613bae57600080fd5b84359350602085013592506040850135613bc781613b3f565b91506060850135613bd781613b3f565b939692955090935050565b60008060008060808587031215613bf857600080fd5b84359350602085013592506040850135613c1181613b3f565b9150613c1f60608601613918565b905092959194509250565b634e487b7160e01b600052603260045260246000fd5b60008060008060808587031215613c5657600080fd5b505082516020840151604085015160609095015191969095509092509050565b60008060408385031215613c8957600080fd5b505080516020909101519092909150565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613cd257613cd2613c9a565b60405290565b60405161014081016001600160401b0381118282101715613cd257613cd2613c9a565b60405160c081016001600160401b0381118282101715613cd257613cd2613c9a565b60405161010081016001600160401b0381118282101715613cd257613cd2613c9a565b604051601f8201601f191681016001600160401b0381118282101715613d6857613d68613c9a565b604052919050565b8035801515811461392f57600080fd5b80356002811061392f57600080fd5b60006001600160401b03821115613da857613da8613c9a565b50601f01601f191660200190565b600082601f830112613dc757600080fd5b8135613dda613dd582613d8f565b613d40565b818152846020838601011115613def57600080fd5b816020850160208301376000918101602001919091529392505050565b600060e08236031215613e1e57600080fd5b613e26613cb0565b8235815260208301356020820152613e4060408401613b8d565b6040820152613e5160608401613d70565b6060820152613e6260808401613d80565b608082015260a083013560a082015260c08301356001600160401b03811115613e8a57600080fd5b613e9636828601613db6565b60c08301525092915050565b634e487b7160e01b600052602160045260246000fd5b6000808335601e19843603018112613ecf57600080fd5b83016020810192503590506001600160401b03811115613eee57600080fd5b803603831315613efd57600080fd5b9250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b8481526080602082015283356080820152602084013560a082015260006040850135613f5881613b3f565b6001600160a01b031660c0830152613f7260608601613d70565b151560e0830152613f8560808601613d80565b60028110613f9557613f95613ea2565b61010083015260a0850135610120830152613fb360c0860186613eb8565b60e0610140850152613fca61016085018284613f04565b60408501969096525050506060015292915050565b600060208284031215613ff157600080fd5b613a8d82613d70565b6003811061400a5761400a613ea2565b9052565b848152602081018490526001600160a01b0383166040820152608081016138fd6060830184613ffa565b60006020828403121561404a57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561407a5761407a614051565b500190565b60006020828403121561409157600080fd5b8135613a8d81613b3f565b858152602081018590526001600160a01b038416604082015260a081016140c66060830185613ffa565b8260808301529695505050505050565b6000808335601e198436030181126140ed57600080fd5b8301803591506001600160401b0382111561410757600080fd5b602001915036819003821315613efd57600080fd5b60208152815160208201526020820151604082015260408201511515606082015260608201516080820152608082015160a0820152600060a083015160c08084015261387a60e08401826138b2565b600082601f83011261417c57600080fd5b815161418a613dd582613d8f565b81815284602083860101111561419f57600080fd5b61387a826020830160208701613882565b6000602082840312156141c257600080fd5b81516001600160401b038111156141d857600080fd5b61387a8482850161416b565b60006101008083850312156141f857600080fd5b604051908101906001600160401b038211818310171561421a5761421a613c9a565b8160405283358152602084013560208201526040840135915061423c82613b3f565b81604082015261424e60608501613b8d565b606082015261425f60808501613b8d565b608082015260a084013560a082015260c084013560c082015260e084013560e0820152809250505092915050565b60006020828403121561429f57600080fd5b8151613a8d81613b3f565b6000806000606084860312156142bf57600080fd5b8351925060208401519150604084015190509250925092565b600061014082840312156142eb57600080fd5b6142f3613cd8565b823581526020830135602082015261430d60408401613b8d565b604082015261431e60608401613b8d565b606082015261432f60808401613b8d565b608082015261434060a08401613b8d565b60a082015260c0838101359082015260e080840135908201526101008084013590820152610120928301359281019290925250919050565b600481106126ca57600080fd5b803561392f81614378565b600060c082360312156143a257600080fd5b6143aa613cfb565b823581526020830135602082015260408301356143c681613b3f565b604082015260608301356143d981614378565b60608201526080838101359082015260a08301356001600160401b0381111561440157600080fd5b61440d36828601613db6565b60a08301525092915050565b600481106126ca576126ca613ea2565b8381526060602082015282356060820152602083013560808201526000604084013561445481613b3f565b6001600160a01b031660a0830152606084013561447081614378565b61447981614419565b60c0830152608084013560e083015261449560a0850185613eb8565b60c06101008501526144ac61012085018284613f04565b925050506001600160601b0383166040830152949350505050565b600080600080600060a086880312156144df57600080fd5b85516144ea81613b3f565b8095505060208601519350604086015192506060860151915060808601516001600160401b0381111561451c57600080fd5b6145288882890161416b565b9150509295509295909350565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260018060a01b0360a08201511660a0830152600060c082015160e060c085015261387a60e08501826138b2565b602081526000613a8d6020830184614535565b600061010082360312156145b457600080fd5b6145bc613d1d565b82358152602083013560208201526145d660408401613b8d565b60408201526145e760608401613b8d565b60608201526145f860808401613b8d565b608082015261460960a08401614385565b60a082015260c083013560c082015260e08301356001600160401b0381111561463157600080fd5b61463d36828601613db6565b60e08301525092915050565b61400a81614419565b8381526060602082015282356060820152602083013560808201526000604084013561467d81613b3f565b6001600160a01b031660a083015261469760608501613b8d565b6001600160a01b031660c08301526146b160808501613b8d565b6001600160a01b031660e08301526146cb60a08501614385565b6101006146da81850183614649565b60c08601356101208501526146f260e0870187613eb8565b92508161014086015261470a61016086018483613f04565b935050505061387a60408301846001600160601b03169052565b85815260a06020820152843560a0820152602085013560c08201526000604086013561474f81613b3f565b6001600160a01b031660e0830152606086013561476b81614378565b61477481614419565b610100830152608086013561012083015261479260a0870187613eb8565b60c06101408501526147a961016085018284613f04565b92505050846040830152836060830152613aed60808301846001600160601b03169052565b600080600080608085870312156147e457600080fd5b84519350602085015192506040850151915060608501516001600160401b0381111561480f57600080fd5b61481b8782880161416b565b91505092959194509250565b8051825260208101516020830152604081015160408301526060810151606083015260808101516080830152600060a082015160c060a085015261387a60c08501826138b2565b602081526000613a8d6020830184614827565b600060e0823603121561489357600080fd5b61489b613cb0565b82358152602083013560208201526148b560408401613b8d565b60408201526148c660608401613b8d565b6060820152613e6260808401614385565b85815260a06020820152843560a0820152602085013560c08201526000604086013561490281613b3f565b6001600160a01b0390811660e084015260608701359061492182613b3f565b1661010083015261493460808701614385565b614942610120840182614649565b5060a086013561014083015261495b60c0870187613eb8565b60e06101608501526147a961018085018284613f04565b60006001600160a01b038381169083168181101561499257614992614051565b03939250505056fea264697066735822122066046bd44d388e62516ef1c5989c4c938b6a66df609121c5cf10154ae8ac5d6d64736f6c63430008080033a2646970667358221220c4e40d78b2407bde37951e56bc5d45641553fb7436a728d1bc55b8eeced6fe3764736f6c63430008080033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000051b420775f39c39b04fd11cf8d89b8a3204f647900000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c4000000000000000000000000000000000000000000000000000000000000199a0000000000000000000000000000000000000000000000000000000000002aaa

-----Decoded View---------------
Arg [0] : chosenOwner (address): 0x51B420775f39C39b04FD11cf8D89B8A3204f6479
Arg [1] : chosenOptionFactory (address): 0x17385e95cb74A20150E4fA092Aa72D57330896C4
Arg [2] : chosenTransactionFee (uint256): 6554
Arg [3] : chosenProtocolFee (uint256): 10922

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 00000000000000000000000051b420775f39c39b04fd11cf8d89b8a3204f6479
Arg [1] : 00000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c4
Arg [2] : 000000000000000000000000000000000000000000000000000000000000199a
Arg [3] : 0000000000000000000000000000000000000000000000000000000000002aaa


Block Transaction Gas Used Reward
view all blocks ##produced##

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.