ETH Price: $3,102.17 (+1.10%)
Gas: 12 Gwei

Contract

0xbfC1633213856da811d6049cd969Ff11e7B55c68
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value

There are no matching entries

Please try again later

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To Value
190116372024-01-15 10:18:23177 days ago1705313903  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x456D8d6D...883A69F68
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
TimeswapV2Option

Compiler Version
v0.8.8+commit.dddeac2f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 24 : TimeswapV2Option.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

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

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

import {ITimeswapV2Option} from "./interfaces/ITimeswapV2Option.sol";
import {ITimeswapV2OptionDeployer} from "./interfaces/ITimeswapV2OptionDeployer.sol";
import {ITimeswapV2OptionMintCallback} from "./interfaces/callbacks/ITimeswapV2OptionMintCallback.sol";
import {ITimeswapV2OptionBurnCallback} from "./interfaces/callbacks/ITimeswapV2OptionBurnCallback.sol";
import {ITimeswapV2OptionSwapCallback} from "./interfaces/callbacks/ITimeswapV2OptionSwapCallback.sol";
import {ITimeswapV2OptionCollectCallback} from "./interfaces/callbacks/ITimeswapV2OptionCollectCallback.sol";

import {Option, OptionLibrary} from "./structs/Option.sol";
import {Process, ProcessLibrary} from "./structs/Process.sol";
import {StrikeAndMaturity} from "./structs/StrikeAndMaturity.sol";

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

import {TimeswapV2OptionMintParam, TimeswapV2OptionBurnParam, TimeswapV2OptionSwapParam, TimeswapV2OptionCollectParam, ParamLibrary} from "./structs/Param.sol";
import {TimeswapV2OptionMintCallbackParam, TimeswapV2OptionBurnCallbackParam, TimeswapV2OptionSwapCallbackParam, TimeswapV2OptionCollectCallbackParam} from "./structs/CallbackParam.sol";

/// @title Timeswap V2 Options for a given pair
/// @author Timeswap Labs
/// @notice Holds the option of all strikes and maturities.
contract TimeswapV2Option is ITimeswapV2Option, NoDelegateCall {
  using OptionLibrary for Option;
  using ProcessLibrary for Process[];
  using Math for uint256;
  using SafeERC20 for IERC20;

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

  /// @inheritdoc ITimeswapV2Option
  address public immutable override optionFactory;
  /// @inheritdoc ITimeswapV2Option
  address public immutable override token0;
  /// @inheritdoc ITimeswapV2Option
  address public immutable override token1;

  /// @dev mapping of all option state for all strikes and maturities.
  mapping(uint256 => mapping(uint256 => Option)) private options;
  /// @dev Always start and end as an empty array for every transaction.
  /// Process the token requirement for every option interaction call.
  Process[] private processing;

  mapping(uint256 => mapping(uint256 => bool)) private hasInteracted;
  StrikeAndMaturity[] private listOfOptions;

  function addOptionEnumerationIfNecessary(uint256 strike, uint256 maturity) private {
    if (!hasInteracted[strike][maturity]) {
      hasInteracted[strike][maturity] = true;
      listOfOptions.push(StrikeAndMaturity({strike: strike, maturity: maturity}));
    }
  }

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

  constructor() NoDelegateCall() {
    (optionFactory, token0, token1) = ITimeswapV2OptionDeployer(msg.sender).parameter();
  }

  // Can be overridden for testing purposes.
  function blockTimestamp() internal view virtual returns (uint96) {
    return uint96(block.timestamp); // truncation is desired
  }

  /* ===== VIEW ===== */
  /// @inheritdoc ITimeswapV2Option
  function getByIndex(uint256 id) external view override returns (StrikeAndMaturity memory) {
    return listOfOptions[id];
  }

  /// @inheritdoc ITimeswapV2Option
  function numberOfOptions() external view override returns (uint256) {
    return listOfOptions.length;
  }

  /// @inheritdoc ITimeswapV2Option
  function totalPosition(
    uint256 strike,
    uint256 maturity,
    TimeswapV2OptionPosition position
  ) external view override returns (uint256) {
    return options[strike][maturity].totalPosition(strike, position);
  }

  /// @inheritdoc ITimeswapV2Option
  function positionOf(
    uint256 strike,
    uint256 maturity,
    address owner,
    TimeswapV2OptionPosition position
  ) external view override returns (uint256) {
    return options[strike][maturity].positionOf(owner, position);
  }

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

  /// @inheritdoc ITimeswapV2Option
  function transferPosition(
    uint256 strike,
    uint256 maturity,
    address to,
    TimeswapV2OptionPosition position,
    uint256 amount
  ) external override {
    if (!hasInteracted[strike][maturity]) Error.inactiveOptionChoice(strike, maturity);
    if (to == address(0)) Error.zeroAddress();
    if (amount == 0) Error.zeroInput();
    PositionLibrary.check(position);

    options[strike][maturity].transferPosition(to, position, amount);

    emit TransferPosition(strike, maturity, msg.sender, to, position, amount);
  }

  /// @inheritdoc ITimeswapV2Option
  function mint(
    TimeswapV2OptionMintParam calldata param
  )
    external
    override
    noDelegateCall
    returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data)
  {
    ParamLibrary.check(param, blockTimestamp());
    addOptionEnumerationIfNecessary(param.strike, param.maturity);

    Option storage option = options[param.strike][param.maturity];

    // does main mint logic calculation
    (token0AndLong0Amount, token1AndLong1Amount, shortAmount) = option.mint(
      param.strike,
      param.long0To,
      param.long1To,
      param.shortTo,
      param.transaction,
      param.amount0,
      param.amount1
    );

    // update token0 and token1 balance target for any previous concurrent option transactions.
    processing.updateProcess(token0AndLong0Amount, token1AndLong1Amount, true, true);

    // add a new process
    // stores the token0 and token1 balance target required from the msg.sender to achieve.
    Process storage currentProcess = (processing.push() = Process(
      param.strike,
      param.maturity,
      IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount,
      IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount
    ));

    // ask the msg.sender to transfer token0 and/or token1 to this contract.
    data = ITimeswapV2OptionMintCallback(msg.sender).timeswapV2OptionMintCallback(
      TimeswapV2OptionMintCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        token0AndLong0Amount: token0AndLong0Amount,
        token1AndLong1Amount: token1AndLong1Amount,
        shortAmount: shortAmount,
        data: param.data
      })
    );

    // check if the token0 balance target is achieved.
    if (token0AndLong0Amount != 0)
      Error.checkEnough(IERC20(token0).balanceOf(address(this)), currentProcess.balance0Target);

    // check if the token1 balance target is achieved.
    if (token1AndLong1Amount != 0)
      Error.checkEnough(IERC20(token1).balanceOf(address(this)), currentProcess.balance1Target);

    // finish the process.
    processing.pop();

    emit Mint(
      param.strike,
      param.maturity,
      msg.sender,
      param.long0To,
      param.long1To,
      param.shortTo,
      token0AndLong0Amount,
      token1AndLong1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Option
  function burn(
    TimeswapV2OptionBurnParam calldata param
  )
    external
    override
    noDelegateCall
    returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount, bytes memory data)
  {
    if (!hasInteracted[param.strike][param.maturity]) Error.inactiveOptionChoice(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp());

    Option storage option = options[param.strike][param.maturity];

    // does main burn logic calculation
    (token0AndLong0Amount, token1AndLong1Amount, shortAmount) = option.burn(
      param.strike,
      param.transaction,
      param.amount0,
      param.amount1
    );

    // update token0 and token1 balance target for any previous concurrent option transactions.
    processing.updateProcess(token0AndLong0Amount, token1AndLong1Amount, false, false);

    // transfer token0 amount to recipient.
    if (token0AndLong0Amount != 0) IERC20(token0).safeTransfer(param.token0To, token0AndLong0Amount);

    // transfer token1 amount to recipient.
    if (token1AndLong1Amount != 0) IERC20(token1).safeTransfer(param.token1To, token1AndLong1Amount);

    // skip callback if there is no data.
    if (param.data.length != 0)
      data = ITimeswapV2OptionBurnCallback(msg.sender).timeswapV2OptionBurnCallback(
        TimeswapV2OptionBurnCallbackParam({
          strike: param.strike,
          maturity: param.maturity,
          token0AndLong0Amount: token0AndLong0Amount,
          token1AndLong1Amount: token1AndLong1Amount,
          shortAmount: shortAmount,
          data: param.data
        })
      );

    option.long0[msg.sender] -= token0AndLong0Amount;
    option.long1[msg.sender] -= token1AndLong1Amount;
    option.short[msg.sender] -= shortAmount;

    emit Burn(
      param.strike,
      param.maturity,
      msg.sender,
      param.token0To,
      param.token1To,
      token0AndLong0Amount,
      token1AndLong1Amount,
      shortAmount
    );
  }

  /// @inheritdoc ITimeswapV2Option
  function swap(
    TimeswapV2OptionSwapParam calldata param
  )
    external
    override
    noDelegateCall
    returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, bytes memory data)
  {
    if (!hasInteracted[param.strike][param.maturity]) Error.inactiveOptionChoice(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp());

    Option storage option = options[param.strike][param.maturity];

    // does main swap logic calculation
    (token0AndLong0Amount, token1AndLong1Amount) = option.swap(
      param.strike,
      param.longTo,
      param.isLong0ToLong1,
      param.transaction,
      param.amount
    );

    // update token0 and token1 balance target for any previous concurrent option transactions.
    processing.updateProcess(token0AndLong0Amount, token1AndLong1Amount, !param.isLong0ToLong1, param.isLong0ToLong1);

    // add a new process
    // stores the token0 and token1 balance target required from the msg.sender to achieve.
    Process storage currentProcess = (processing.push() = Process(
      param.strike,
      param.maturity,
      param.isLong0ToLong1
        ? IERC20(token0).balanceOf(address(this)) - token0AndLong0Amount
        : IERC20(token0).balanceOf(address(this)) + token0AndLong0Amount,
      param.isLong0ToLong1
        ? IERC20(token1).balanceOf(address(this)) + token1AndLong1Amount
        : IERC20(token1).balanceOf(address(this)) - token1AndLong1Amount
    ));

    // transfer token to recipient.
    IERC20(param.isLong0ToLong1 ? token0 : token1).safeTransfer(
      param.tokenTo,
      param.isLong0ToLong1 ? token0AndLong0Amount : token1AndLong1Amount
    );

    // ask the msg.sender to transfer token0 or token1 to this contract.
    data = ITimeswapV2OptionSwapCallback(msg.sender).timeswapV2OptionSwapCallback(
      TimeswapV2OptionSwapCallbackParam({
        strike: param.strike,
        maturity: param.maturity,
        isLong0ToLong1: param.isLong0ToLong1,
        token0AndLong0Amount: token0AndLong0Amount,
        token1AndLong1Amount: token1AndLong1Amount,
        data: param.data
      })
    );

    // check if the token0 or token1 balance target is achieved.
    Error.checkEnough(
      IERC20(param.isLong0ToLong1 ? token1 : token0).balanceOf(address(this)),
      param.isLong0ToLong1 ? currentProcess.balance1Target : currentProcess.balance0Target
    );

    if (param.isLong0ToLong1) option.long0[msg.sender] -= token0AndLong0Amount;
    else option.long1[msg.sender] -= token1AndLong1Amount;

    // finish the process.
    processing.pop();

    emit Swap(
      param.strike,
      param.maturity,
      msg.sender,
      param.tokenTo,
      param.longTo,
      param.isLong0ToLong1,
      token0AndLong0Amount,
      token1AndLong1Amount
    );
  }

  /// @inheritdoc ITimeswapV2Option
  function collect(
    TimeswapV2OptionCollectParam calldata param
  )
    external
    override
    noDelegateCall
    returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount, bytes memory data)
  {
    if (!hasInteracted[param.strike][param.maturity]) Error.inactiveOptionChoice(param.strike, param.maturity);
    ParamLibrary.check(param, blockTimestamp());

    Option storage option = options[param.strike][param.maturity];

    // does main collect logic calculation
    (token0Amount, token1Amount, shortAmount) = option.collect(param.strike, param.transaction, param.amount);

    // update token0 and token1 balance target for any previous concurrent option transactions.
    processing.updateProcess(token0Amount, token1Amount, false, false);

    // transfer token0 amount to recipient.
    if (token0Amount != 0) IERC20(token0).safeTransfer(param.token0To, token0Amount);

    // transfer token1 amount to recipient.
    if (token1Amount != 0) IERC20(token1).safeTransfer(param.token1To, token1Amount);

    // skip callback if there is no data.
    if (param.data.length != 0)
      data = ITimeswapV2OptionCollectCallback(msg.sender).timeswapV2OptionCollectCallback(
        TimeswapV2OptionCollectCallbackParam({
          strike: param.strike,
          maturity: param.maturity,
          token0Amount: token0Amount,
          token1Amount: token1Amount,
          shortAmount: shortAmount,
          data: param.data
        })
      );

    option.short[msg.sender] -= shortAmount;

    emit Collect(
      param.strike,
      param.maturity,
      msg.sender,
      param.token0To,
      param.token1To,
      token0Amount,
      token1Amount,
      shortAmount
    );
  }
}

File 2 of 24 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 3 of 24 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 4 of 24 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 5 of 24 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 6 of 24 : Error.sol
// 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);
  }
}

File 7 of 24 : FullMath.sol
// 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);
  }
}

File 8 of 24 : Math.sol
// 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;
  }
}

File 9 of 24 : StrikeConversion.sol
// 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));
  }
}

File 10 of 24 : Position.sol
// 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();
  }
}

File 11 of 24 : Transaction.sol
// 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();
  }
}

File 12 of 24 : ITimeswapV2OptionBurnCallback.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

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

/// @title Callback for ITimeswapV2Option#burn
/// @notice Any contract that calls ITimeswapV2Option#burn can optionally implement this interface.
interface ITimeswapV2OptionBurnCallback {
  /// @notice Called to `msg.sender` after initiating a burn from ITimeswapV2Option#burn.
  /// @dev In the implementation, you must have enough long0 positions, long1 positions, and short positions for the burn transaction.
  /// The caller of this method must be checked to be a Timeswap V2 Option pair deployed by the canonical Timeswap V2 Factory.
  /// @dev The token0 and token1 will already transferred to the recipients.
  /// @param param The parameter of the callback.
  /// @return data The bytes code returned from the callback.
  function timeswapV2OptionBurnCallback(
    TimeswapV2OptionBurnCallbackParam calldata param
  ) external returns (bytes memory data);
}

File 13 of 24 : ITimeswapV2OptionCollectCallback.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

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

/// @title Callback for ITimeswapV2Option#collect
/// @notice Any contract that calls ITimeswapV2Option#collect can optionally implement this interface.
interface ITimeswapV2OptionCollectCallback {
  /// @notice Called to `msg.sender` after initiating a collect from ITimeswapV2Option#collect.
  /// @dev In the implementation, you must have enough short positions for the collect transaction.
  /// The caller of this method must be checked to be a Timeswap V2 Option pair deployed by the canonical Timeswap V2 Factory.
  /// @dev The token0 and token1 will already transferred to the recipients.
  /// @param param The parameter of the callback.
  /// @return data The bytes code returned from the callback.
  function timeswapV2OptionCollectCallback(
    TimeswapV2OptionCollectCallbackParam calldata param
  ) external returns (bytes memory data);
}

File 14 of 24 : ITimeswapV2OptionMintCallback.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

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

/// @title Callback for ITimeswapV2Option#mint
/// @notice Any contract that calls ITimeswapV2Option#mint must implement this interface.
interface ITimeswapV2OptionMintCallback {
  /// @notice Called to `msg.sender` after initiating a mint from ITimeswapV2Option#mint.
  /// @dev In the implementation, you must transfer token0 and token1 for the mint transaction.
  /// The caller of this method must be checked to be a Timeswap V2 Option pair deployed by the canonical Timeswap V2 Factory.
  /// @dev The long0 positions, long1 positions, and/or short positions will already minted to the recipients.
  /// @param param The parameter of the callback.
  /// @return data The bytes code returned from the callback.
  function timeswapV2OptionMintCallback(
    TimeswapV2OptionMintCallbackParam calldata param
  ) external returns (bytes memory data);
}

File 15 of 24 : ITimeswapV2OptionSwapCallback.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

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

/// @title Callback for ITimeswapV2Option#swap
/// @notice Any contract that calls ITimeswapV2Option#swap must implement this interface.
interface ITimeswapV2OptionSwapCallback {
  /// @notice Called to `msg.sender` after initiating a swap from ITimeswapV2Option#swap.
  /// @dev In the implementation, you must transfer token0 for the swap transaction.
  /// The caller of this method must be checked to be a Timeswap V2 Option pair deployed by the canonical Timeswap V2 Factory.
  /// @dev The long0 positions or long1 positions will already minted to the recipients.
  /// @param param The param of the swap callback.
  /// @return data The bytes code returned from the callback.
  function timeswapV2OptionSwapCallback(
    TimeswapV2OptionSwapCallbackParam calldata param
  ) external returns (bytes memory data);
}

File 16 of 24 : ITimeswapV2Option.sol
// 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);
}

File 17 of 24 : ITimeswapV2OptionDeployer.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 Option
/// @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 ITimeswapV2OptionDeployer {
  /* ===== 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 optionFactory The factory address.
  /// @param token0 The first ERC20 token address of the pair.
  /// @param token1 The second ERC20 token address of the pair.
  function parameter() external view returns (address optionFactory, address token0, address token1);
}

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

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

/// @title library for proportion utils
/// @author Timeswap Labs
library Proportion {
  /// @dev Get the balance proportion calculation.
  /// @notice Round down the result.
  /// @param multiplicand The multiplicand balance.
  /// @param multiplier The multiplier balance.
  /// @param divisor The divisor balance.
  function proportion(
    uint256 multiplicand,
    uint256 multiplier,
    uint256 divisor,
    bool roundUp
  ) internal pure returns (uint256) {
    return FullMath.mulDiv(multiplicand, multiplier, divisor, roundUp);
  }
}

File 19 of 24 : NoDelegateCall.sol
// 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();
    _;
  }
}

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

/// @dev Parameter for the mint callback.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param token0AndLong0Amount The token0 amount to be deposited and the long0 amount minted.
/// @param token1AndLong1Amount The token1 amount to be deposited and the long1 amount minted.
/// @param shortAmount The short amount minted.
/// @param data The bytes code data sent to the callback.
struct TimeswapV2OptionMintCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 token0AndLong0Amount;
  uint256 token1AndLong1Amount;
  uint256 shortAmount;
  bytes data;
}

/// @dev Parameter for the burn callback.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param token0AndLong0Amount The token0 amount to be withdrawn and the long0 amount burnt.
/// @param token1AndLong1Amount The token1 amount to be withdrawn and the long1 amount burnt.
/// @param shortAmount The short amount burnt.
/// @param data The bytes code data sent to the callback.
struct TimeswapV2OptionBurnCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 token0AndLong0Amount;
  uint256 token1AndLong1Amount;
  uint256 shortAmount;
  bytes data;
}

/// @dev Parameter for the swap callback.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param isLong0ToLong1 True when swapping long0 for long1. False when swapping long1 for long0.
/// @param token0AndLong0Amount If isLong0ToLong1 is true, the amount of long0 burnt and token0 to be withdrawn.
/// If isLong0ToLong1 is false, the amount of long0 minted and token0 to be deposited.
/// @param token1AndLong1Amount If isLong0ToLong1 is true, the amount of long1 withdrawn and token0 to be deposited.
/// If isLong0ToLong1 is false, the amount of long1 burnt and token1 to be withdrawn.
/// @param data The bytes code data sent to the callback.
struct TimeswapV2OptionSwapCallbackParam {
  uint256 strike;
  uint256 maturity;
  bool isLong0ToLong1;
  uint256 token0AndLong0Amount;
  uint256 token1AndLong1Amount;
  bytes data;
}

/// @dev Parameter for the collect callback.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param token0Amount The token0 amount to be withdrawn.
/// @param token1Amount The token1 amount to be withdrawn.
/// @param shortAmount The short amount burnt.
/// @param data The bytes code data sent to the callback.
struct TimeswapV2OptionCollectCallbackParam {
  uint256 strike;
  uint256 maturity;
  uint256 token0Amount;
  uint256 token1Amount;
  uint256 shortAmount;
  bytes data;
}

File 21 of 24 : Option.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

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

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

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

/// @dev The state per option of a given strike and maturity
/// @param totalLong0 The total amount of long token0 supply.
/// @param totalLong1 The total amount of long token1 supply.
/// @param long0 The mapping of addresses to long token0 owned.
/// @param long1 The mapping of addresses to long token1 owned.
/// @param short The mapping of addresses to short owned.
/// @notice The sum of strike converted totalLong0 and strike converted totalLong1 is the total amount of short token supply.
struct Option {
  uint256 totalLong0;
  uint256 totalLong1;
  mapping(address => uint256) long0;
  mapping(address => uint256) long1;
  mapping(address => uint256) short;
}

/// @title library for position utils
/// @author Timeswap Labs
/// @dev internal library handling important business logic of the option.
library OptionLibrary {
  using Math for uint256;
  using Proportion for uint256;

  /// @dev Get the total position of Long0, Long1, or Short.
  /// @param option The option struct stored.
  /// @param strike The strike of the option.
  /// @param position The position being inquired.
  /// @return balance The total supply positions result.
  function totalPosition(
    Option storage option,
    uint256 strike,
    TimeswapV2OptionPosition position
  ) internal view returns (uint256 balance) {
    if (position == TimeswapV2OptionPosition.Long0) balance = option.totalLong0;
    else if (position == TimeswapV2OptionPosition.Long1) balance = option.totalLong1;
    else if (position == TimeswapV2OptionPosition.Short)
      balance = StrikeConversion.combine(option.totalLong0, option.totalLong1, strike, true);
  }

  /// @dev Get the position of Long0, Long1, or Short owned by an address.
  /// @param option The option struct stored.
  /// @param owner The owner being inquired upon.
  /// @param position The position being inquired.
  /// @return balance The total positions owned result.
  function positionOf(
    Option storage option,
    address owner,
    TimeswapV2OptionPosition position
  ) internal view returns (uint256 balance) {
    if (position == TimeswapV2OptionPosition.Long0) balance = option.long0[owner];
    else if (position == TimeswapV2OptionPosition.Long1) balance = option.long1[owner];
    else if (position == TimeswapV2OptionPosition.Short) balance = option.short[owner];
  }

  /// @dev Transfer position of Long0, Long1, or Short to an address.
  /// @param option The option struct stored.
  /// @param to The target recipient.
  /// @param position The position being inquired.
  /// @param amount The amount being transferred.
  function transferPosition(
    Option storage option,
    address to,
    TimeswapV2OptionPosition position,
    uint256 amount
  ) internal {
    if (position == TimeswapV2OptionPosition.Long0) {
      option.long0[msg.sender] -= amount;
      option.long0[to] += amount;
    } else if (position == TimeswapV2OptionPosition.Long1) {
      option.long1[to] += amount;
      option.long1[msg.sender] -= amount;
    } else if (position == TimeswapV2OptionPosition.Short) {
      option.short[msg.sender] -= amount;
      option.short[to] += amount;
    }
  }

  /// @dev Handles main mint logic.
  /// @param option The option struct stored.
  /// @param strike The strike of the option.
  /// @param long0To The recipient of long0 token.
  /// @param long1To The recipient of long1 token.
  /// @param shortTo The recipient of short token.
  /// @param transaction The mint transaction type.
  /// @param amount0 The first amount based on transaction type.
  /// @param amount1 The second amount based on transaction type.
  /// @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.
  function mint(
    Option storage option,
    uint256 strike,
    address long0To,
    address long1To,
    address shortTo,
    TimeswapV2OptionMint transaction,
    uint256 amount0,
    uint256 amount1
  ) internal returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount) {
    if (transaction == TimeswapV2OptionMint.GivenTokensAndLongs)
      shortAmount = StrikeConversion.combine(
        token0AndLong0Amount = amount0,
        token1AndLong1Amount = amount1,
        strike,
        false
      );
    else if (transaction == TimeswapV2OptionMint.GivenShorts) {
      shortAmount = amount0 + amount1;
      token0AndLong0Amount = StrikeConversion.turn(amount0, strike, false, true);
      token1AndLong1Amount = StrikeConversion.turn(amount1, strike, true, true);
    }

    if (token0AndLong0Amount != 0) {
      option.totalLong0 += token0AndLong0Amount;
      option.long0[long0To] += token0AndLong0Amount;
    }

    if (token1AndLong1Amount != 0) {
      option.totalLong1 += token1AndLong1Amount;
      option.long1[long1To] += token1AndLong1Amount;
    }

    option.short[shortTo] += shortAmount;

    // Checks overflow. Reverts when overflow.
    StrikeConversion.combine(option.totalLong0, option.totalLong1, strike, true);
  }

  /// @dev Handles main burn logic.
  /// @param option The option struct stored.
  /// @param strike The strike of the option.
  /// @param transaction The burn transaction type.
  /// @param amount0 The first amount based on transaction type.
  /// @param amount1 The second amount based on transaction type.
  /// @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(
    Option storage option,
    uint256 strike,
    TimeswapV2OptionBurn transaction,
    uint256 amount0,
    uint256 amount1
  ) internal returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount, uint256 shortAmount) {
    if (transaction == TimeswapV2OptionBurn.GivenTokensAndLongs)
      shortAmount = StrikeConversion.combine(
        token0AndLong0Amount = amount0,
        token1AndLong1Amount = amount1,
        strike,
        true
      );
    else if (transaction == TimeswapV2OptionBurn.GivenShorts) {
      shortAmount = amount0 + amount1;
      token0AndLong0Amount = StrikeConversion.turn(amount0, strike, false, false);
      token1AndLong1Amount = StrikeConversion.turn(amount1, strike, true, false);
    }

    option.totalLong0 -= token0AndLong0Amount;
    option.totalLong1 -= token1AndLong1Amount;
  }

  /// @dev Handles main mint logic.
  /// @param option The option struct stored.
  /// @param strike The strike of the option.
  /// @param longTo The recipient of long0 or long1 token.
  /// @param isLong0ToLong1 True if transforming long0 for long1 and false if transforming long1 for long0.
  /// @param transaction The swap transaction type.
  /// @param amount The amount based on transaction type.
  /// @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.
  function swap(
    Option storage option,
    uint256 strike,
    address longTo,
    bool isLong0ToLong1,
    TimeswapV2OptionSwap transaction,
    uint256 amount
  ) internal returns (uint256 token0AndLong0Amount, uint256 token1AndLong1Amount) {
    if (transaction == TimeswapV2OptionSwap.GivenToken0AndLong0) {
      token0AndLong0Amount = amount;
      token1AndLong1Amount = StrikeConversion.convert(token0AndLong0Amount, strike, true, isLong0ToLong1);
    } else if (transaction == TimeswapV2OptionSwap.GivenToken1AndLong1) {
      token1AndLong1Amount = amount;
      token0AndLong0Amount = StrikeConversion.convert(token1AndLong1Amount, strike, false, !isLong0ToLong1);
    }

    if (isLong0ToLong1) {
      option.totalLong0 -= token0AndLong0Amount;
      option.totalLong1 += token1AndLong1Amount;
      option.long1[longTo] += token1AndLong1Amount;
    } else {
      option.totalLong1 -= token1AndLong1Amount;
      option.totalLong0 += token0AndLong0Amount;
      option.long0[longTo] += token0AndLong0Amount;
    }
  }

  /// @dev Handles main mint logic.
  /// @param option The option struct stored.
  /// @param strike The strike of the option.
  /// @param transaction The collect transaction type.
  /// @param amount The amount based on transaction type.
  /// @return token0Amount The token0 amount withdrawn.
  /// @return token1Amount The token1 amount withdrawn.
  /// @return shortAmount The short amount burnt.
  function collect(
    Option storage option,
    uint256 strike,
    TimeswapV2OptionCollect transaction,
    uint256 amount
  ) internal returns (uint256 token0Amount, uint256 token1Amount, uint256 shortAmount) {
    uint256 denominator = StrikeConversion.combine(option.totalLong0, option.totalLong1, strike, true);

    if (transaction == TimeswapV2OptionCollect.GivenShort) {
      shortAmount = amount;
      token0Amount = shortAmount.proportion(option.totalLong0, denominator, false);
      token1Amount = shortAmount.proportion(option.totalLong1, denominator, false);
    } else if (transaction == TimeswapV2OptionCollect.GivenToken0) {
      token0Amount = amount;
      shortAmount = token0Amount.proportion(denominator, option.totalLong0, true);
      token1Amount = shortAmount.proportion(option.totalLong1, denominator, false);
    } else if (transaction == TimeswapV2OptionCollect.GivenToken1) {
      token1Amount = amount;
      shortAmount = token1Amount.proportion(denominator, option.totalLong1, true);
      token0Amount = shortAmount.proportion(option.totalLong0, denominator, false);
    }

    option.totalLong0 -= token0Amount;
    option.totalLong1 -= token1Amount;
  }
}

File 22 of 24 : Param.sol
// 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();
  }
}

File 23 of 24 : Process.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.8;

/// @dev Processing information required for interacting multiple options in a single contract.
/// @notice Since mint, burn, and swap are all flashable transactions.
/// When doing a mint transaction, do not burn or swap the newly minted positions,
/// or risk transaction failure.
/// When doing a swap transaction, do not burn or swap the newly swapped positions,
/// or risk transaction failure.
/// @notice For example, if only long0 is minted.
/// Then calling swap on that long0 position of the same strike and maturity can risk transaction failure.
/// If calling swap on long1 position received elsewhere, that will be fine.
/// If calling swap on long1 but different strike and maturity is fine as well.
/// @param strike The strike ratio of token1 per token0 of the option.
/// @param maturity The maturity of the option.
/// @param balance0Target The required balance of token0 to be held in the option.
/// @param balance1Target The required balance of token1 to be held in the option.
struct Process {
  uint256 strike;
  uint256 maturity;
  uint256 balance0Target;
  uint256 balance1Target;
}

library ProcessLibrary {
  /// @dev Revert when processing more than 16 processes.
  error ProccessOverload();

  /// @dev update process for managing how many tokens required from msg.sender.
  /// @dev reentrancy safety as well.
  /// @notice Can only process up to 16 proccesses. Will revert if more than 16.
  /// @param processing The current array of processes.
  /// @param token0Amount If isAddToken0 then token0 amount to be deposited, else the token0 amount withdrawn.
  /// @param token1Amount If isAddToken1 then token1 amount to be deposited, else the token1 amount withdrawn.
  /// @param isAddToken0 IsAddToken0 if true. IsSubToken0 if false.
  /// @param isAddToken1 IsAddToken1 if true. IsSubToken0 if false.
  function updateProcess(
    Process[] storage processing,
    uint256 token0Amount,
    uint256 token1Amount,
    bool isAddToken0,
    bool isAddToken1
  ) internal {
    if (processing.length > 16) revert ProccessOverload();

    for (uint256 i; i < processing.length; ) {
      Process storage process = processing[i];

      if (token0Amount != 0)
        process.balance0Target = isAddToken0
          ? process.balance0Target + token0Amount
          : process.balance0Target - token0Amount;

      if (token1Amount != 0)
        process.balance1Target = isAddToken1
          ? process.balance1Target + token1Amount
          : process.balance1Target - token1Amount;

      unchecked {
        i++;
      }
    }
  }
}

File 24 of 24 : 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;
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint96","name":"blockTimestamp","type":"uint96"}],"name":"AlreadyMatured","type":"error"},{"inputs":[],"name":"CannotBeDelegateCalled","type":"error"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"}],"name":"InactiveOption","type":"error"},{"inputs":[{"internalType":"uint256","name":"maturity","type":"uint256"}],"name":"IncorrectMaturity","type":"error"},{"inputs":[],"name":"InvalidPosition","type":"error"},{"inputs":[],"name":"InvalidTransaction","type":"error"},{"inputs":[{"internalType":"uint256","name":"multiplicand","type":"uint256"},{"internalType":"uint256","name":"multiplier","type":"uint256"},{"internalType":"uint256","name":"divisor","type":"uint256"}],"name":"MulDivOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"minuend","type":"uint256"},{"internalType":"uint256","name":"subtrahend","type":"uint256"}],"name":"NotEnoughReceived","type":"error"},{"inputs":[],"name":"ProccessOverload","type":"error"},{"inputs":[{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint96","name":"blockTimestamp","type":"uint96"}],"name":"StillActive","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroInput","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"token0To","type":"address"},{"indexed":false,"internalType":"address","name":"token1To","type":"address"},{"indexed":false,"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortAmount","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"token0To","type":"address"},{"indexed":false,"internalType":"address","name":"token1To","type":"address"},{"indexed":false,"internalType":"uint256","name":"long0AndToken0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"long1AndToken1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortAmount","type":"uint256"}],"name":"Collect","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"long0To","type":"address"},{"indexed":false,"internalType":"address","name":"long1To","type":"address"},{"indexed":false,"internalType":"address","name":"shortTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortAmount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"address","name":"tokenTo","type":"address"},{"indexed":false,"internalType":"address","name":"longTo","type":"address"},{"indexed":false,"internalType":"bool","name":"isLong0toLong1","type":"bool"},{"indexed":false,"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"maturity","type":"uint256"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"enum TimeswapV2OptionPosition","name":"position","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferPosition","type":"event"},{"inputs":[{"components":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"token0To","type":"address"},{"internalType":"address","name":"token1To","type":"address"},{"internalType":"enum TimeswapV2OptionBurn","name":"transaction","type":"uint8"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TimeswapV2OptionBurnParam","name":"param","type":"tuple"}],"name":"burn","outputs":[{"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"},{"internalType":"uint256","name":"shortAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"token0To","type":"address"},{"internalType":"address","name":"token1To","type":"address"},{"internalType":"enum TimeswapV2OptionCollect","name":"transaction","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TimeswapV2OptionCollectParam","name":"param","type":"tuple"}],"name":"collect","outputs":[{"internalType":"uint256","name":"token0Amount","type":"uint256"},{"internalType":"uint256","name":"token1Amount","type":"uint256"},{"internalType":"uint256","name":"shortAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getByIndex","outputs":[{"components":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"}],"internalType":"struct StrikeAndMaturity","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"long0To","type":"address"},{"internalType":"address","name":"long1To","type":"address"},{"internalType":"address","name":"shortTo","type":"address"},{"internalType":"enum TimeswapV2OptionMint","name":"transaction","type":"uint8"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TimeswapV2OptionMintParam","name":"param","type":"tuple"}],"name":"mint","outputs":[{"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"},{"internalType":"uint256","name":"shortAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"numberOfOptions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"enum TimeswapV2OptionPosition","name":"position","type":"uint8"}],"name":"positionOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"tokenTo","type":"address"},{"internalType":"address","name":"longTo","type":"address"},{"internalType":"bool","name":"isLong0ToLong1","type":"bool"},{"internalType":"enum TimeswapV2OptionSwap","name":"transaction","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct TimeswapV2OptionSwapParam","name":"param","type":"tuple"}],"name":"swap","outputs":[{"internalType":"uint256","name":"token0AndLong0Amount","type":"uint256"},{"internalType":"uint256","name":"token1AndLong1Amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"enum TimeswapV2OptionPosition","name":"position","type":"uint8"}],"name":"totalPosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"enum TimeswapV2OptionPosition","name":"position","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferPosition","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100b45760003560e01c8063af2f91ea11610071578063af2f91ea14610186578063b084c25214610199578063b2ceca77146101bb578063b3461c87146101d0578063b7fb542d146101f7578063d21220a71461020a57600080fd5b80630dfe1681146100b95780632d883a73146100fd578063622fb68f1461012b5780637087c1f61461014e57806380e066791461016157806387ed3ecf14610174575b600080fd5b6100e07f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a359381565b6040516001600160a01b0390911681526020015b60405180910390f35b61011061010b3660046129a9565b610231565b604080518251815260209283015192810192909252016100f4565b61013e6101393660046129db565b61028c565b6040516100f49493929190612a67565b61013e61015c366004612a96565b6105df565b61013e61016f366004612ad0565b61082f565b6003545b6040519081526020016100f4565b610178610194366004612b3f565b610e43565b6101ac6101a73660046129db565b610e70565b6040516100f493929190612b87565b6101ce6101c9366004612ba6565b6116da565b005b6100e07f00000000000000000000000017385e95cb74a20150e4fa092aa72d57330896c481565b610178610205366004612bf6565b61179a565b6100e07f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b60408051808201909152600080825260208201526003828154811061025857610258612c2f565b9060005260206000209060020201604051806040016040529081600082015481526020016001820154815250509050919050565b6000806000606061029b6117c6565b8435600090815260026020908152604080832082890135845290915290205460ff166102d0576102d085356020870135611811565b6102e26102dc86612d8e565b42611839565b8435600081815260208181526040808320828a013584529091529020906103259061031360a0890160808a01612e2f565b83919060a08a013560c08b01356118f0565b9196509450925061033b600186866000806119ae565b8415610385576103856103546060880160408901612e4c565b6001600160a01b037f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a3593169087611a80565b83156103cf576103cf61039e6080880160608901612e4c565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169086611a80565b6103dc60e0870187612e67565b1590506104e357336001600160a01b0316638c5f08516040518060c001604052808960000135815260200189602001358152602001888152602001878152602001868152602001898060e001906104339190612e67565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b16815261048a9190600401612efb565b600060405180830381600087803b1580156104a457600080fd5b505af11580156104b8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e09190810190612f0e565b91505b33600090815260028201602052604081208054879290610504908490612f91565b90915550503360009081526003820160205260408120805486929061052a908490612f91565b909155505033600090815260048201602052604081208054859290610550908490612f91565b90915550339050602087013587357f5e34e67b3e203229e29d54244b2c750f9038bc8d484cbcb7693ab665c35c962c61058f60608b0160408c01612e4c565b61059f60808c0160608d01612e4c565b604080516001600160a01b03938416815292909116602083015281018a9052606081018990526080810188905260a00160405180910390a4509193509193565b600080600060606105ee6117c6565b8435600090815260026020908152604080832082890135845290915290205460ff166106235761062385356020870135611811565b61063561062f86612fa8565b42611ad7565b8435600081815260208181526040808320828a013584529091529020906106739061066660a0890160808a0161303e565b83919060a08a0135611b79565b91965094509250610689600186866000806119ae565b84156106a2576106a26103546060880160408901612e4c565b83156106bb576106bb61039e6080880160608901612e4c565b6106c860c0870187612e67565b1590506107cf57336001600160a01b031663dc6fddcf6040518060c001604052808960000135815260200189602001358152602001888152602001878152602001868152602001898060c0019061071f9190612e67565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b1681526107769190600401612efb565b600060405180830381600087803b15801561079057600080fd5b505af11580156107a4573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526107cc9190810190612f0e565b91505b336000908152600482016020526040812080548592906107f0908490612f91565b90915550339050602087013587357fa9321c5c860b13f184d4b6bc6793bede025ef1a51e4c3527d0733f225df69db661058f60608b0160408c01612e4c565b6000806000606061083e6117c6565b61085061084a8661305b565b42611cab565b61085f85356020870135611d75565b8435600081815260208181526040808320828a0135845290915290819020916108d7916108929060608a01908a01612e4c565b6108a260808a0160608b01612e4c565b6108b260a08b0160808c01612e4c565b6108c260c08c0160a08d01612e2f565b86949392919060c08d013560e08e0135611e2d565b919650945092506108ec6001868682806119ae565b60408051608081018252873581526020808901359082015281516370a0823160e01b815230600482015260009282019088906001600160a01b037f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a359316906370a082319060240160206040518083038186803b15801561096a57600080fd5b505afa15801561097e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a29190613111565b6109ac919061312a565b81526040516370a0823160e01b815230600482015260209091019087906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a082319060240160206040518083038186803b158015610a1557600080fd5b505afa158015610a29573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a4d9190613111565b610a57919061312a565b9052600180548082018255600091909152815160049091027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf681019182556020838101517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf78301556040808501517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf88401556060948501517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf990930192909255815160c0810183528b3581528b820135918101919091529081018990529182018790526080820186905291503390634a358d659060a08101610b5d6101008c018c612e67565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b168152610bb49190600401612efb565b600060405180830381600087803b158015610bce57600080fd5b505af1158015610be2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610c0a9190810190612f0e565b92508515610cb7576040516370a0823160e01b8152306004820152610cb7907f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a35936001600160a01b0316906370a082319060240160206040518083038186803b158015610c7557600080fd5b505afa158015610c89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cad9190613111565b8260020154611f9a565b8415610d62576040516370a0823160e01b8152306004820152610d62907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b158015610d2057600080fd5b505afa158015610d34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d589190613111565b8260030154611f9a565b6001805480610d7357610d73613142565b600082815260208082206004600019909401938402018281556001810183905560028101839055600301919091559155339088013588357f0991839fbc46f2e3816b51c806bee50f76ee23053c1b131a9fbf07fc2d6df8aa610ddb60608c0160408d01612e4c565b610deb60808d0160608e01612e4c565b610dfb60a08e0160808f01612e4c565b604080516001600160a01b0394851681529284166020840152921681830152606081018c9052608081018b905260a081018a905290519081900360c00190a450509193509193565b6000848152602081815260408083208684529091528120610e65908484611fc5565b90505b949350505050565b6000806060610e7d6117c6565b8335600090815260026020908152604080832082880135845290915290205460ff16610eb257610eb284356020860135611811565b610ec4610ebe85613171565b42612077565b8335600081815260208181526040808320828901358452909152902090610f2490610ef56080880160608901612e4c565b610f0560a0890160808a01613201565b610f1560c08a0160a08b01612e2f565b859392919060c08b013561211a565b9094509250610f5a8484610f3e60a0890160808a01613201565b15610f4f60a08a0160808b01613201565b6001939291906119ae565b600060405180608001604052808760000135815260200187602001358152602001876080016020810190610f8e9190613201565b61103a576040516370a0823160e01b815230600482015287907f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a35936001600160a01b0316906370a082319060240160206040518083038186803b158015610ff357600080fd5b505afa158015611007573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061102b9190613111565b611035919061312a565b6110dd565b6040516370a0823160e01b815230600482015287907f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a35936001600160a01b0316906370a082319060240160206040518083038186803b15801561109b57600080fd5b505afa1580156110af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d39190613111565b6110dd9190612f91565b81526020016110f260a0890160808a01613201565b61119e576040516370a0823160e01b815230600482015286907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b15801561115757600080fd5b505afa15801561116b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118f9190613111565b6111999190612f91565b611241565b6040516370a0823160e01b815230600482015286907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a082319060240160206040518083038186803b1580156111ff57600080fd5b505afa158015611213573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112379190613111565b611241919061312a565b9052600180548082018255600091909152815160049091027fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6810191825560208301517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf78201556040808401517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf88301556060938401517fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf99092019190915590925061139e9161131691908901908901612e4c565b61132660a0890160808a01613201565b6113305785611332565b865b61134260a08a0160808b01613201565b61136c577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc261138e565b7f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a35935b6001600160a01b03169190611a80565b6040805160c0810182528735815260208089013590820152339163dc4a4847919081016113d160a08b0160808c01613201565b1515815260208101899052604081018890526060016113f360e08b018b612e67565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509152506040516001600160e01b031960e084901b16815261144a919060040161321e565b600060405180830381600087803b15801561146457600080fd5b505af1158015611478573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526114a09190810190612f0e565b92506115986114b560a0880160808901613201565b6114df577f0000000000000000000000009559aaa82d9649c7a7b220e7c461d2e74c9a3593611501565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b15801561154257600080fd5b505afa158015611556573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061157a9190613111565b61158a60a0890160808a01613201565b610d58578260020154611f9a565b6115a860a0870160808801613201565b156115d957336000908152600283016020526040812080548792906115ce908490612f91565b909155506116009050565b336000908152600383016020526040812080548692906115fa908490612f91565b90915550505b600180548061161157611611613142565b600082815260208082206004600019909401938402018281556001810183905560028101839055600301919091559155339087013587357e92a054e9363f3efd42f73f1da82239b3dc6edafea865e59f1cffa4dd7dc22661167860608b0160408c01612e4c565b61168860808c0160608d01612e4c565b61169860a08d0160808e01613201565b604080516001600160a01b039485168152939092166020840152151590820152606081018a90526080810189905260a00160405180910390a450509193909250565b600085815260026020908152604080832087845290915290205460ff16611705576117058585611811565b6001600160a01b03831661171b5761171b612256565b806117285761172861226f565b61173182612288565b6000858152602081815260408083208784529091529020611754908484846122bd565b83857f70faa8e36be9e16810b2a40b964c4b9ae20a221c53bc3fd03fc843110d336dbb3386868660405161178b9493929190613283565b60405180910390a35050505050565b60008381526020818152604080832085845290915281206117bc908584612412565b90505b9392505050565b306001600160a01b037f000000000000000000000000bfc1633213856da811d6049cd969ff11e7b55c68161461180f57604051634118e0bb60e11b815260040160405180910390fd5b565b60405163e407078d60e01b815260048101839052602481018290526044015b60405180910390fd5b81516118475761184761226f565b60208201516001600160601b031015611867576118678260200151612489565b806001600160601b03168260200151101561188a5761188a8260200151826124a5565b60408201516001600160a01b03166118a4576118a4612256565b60608201516001600160a01b03166118be576118be612256565b6118cb82608001516124d0565b60a08201511580156118df575060c0820151155b156118ec576118ec61226f565b5050565b60008080808660018111156119075761190761326d565b1415611927576119208593508385935083896001612502565b905061196d565b600186600181111561193b5761193b61326d565b141561196d5761194b848661312a565b905061195a8588600080612545565b925061196a848860016000612545565b91505b828860000160008282546119819190612f91565b925050819055508188600101600082825461199c9190612f91565b92505081905550955095509592505050565b8454601010156119d15760405163a6a2b99f60e01b815260040160405180910390fd5b60005b8554811015611a785760008682815481106119f1576119f1612c2f565b9060005260206000209060040201905085600014611a395783611a2357858160020154611a1e9190612f91565b611a33565b858160020154611a33919061312a565b60028201555b8415611a6f5782611a5957848160030154611a549190612f91565b611a69565b848160030154611a69919061312a565b60038201555b506001016119d4565b505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611ad2908490612585565b505050565b8151611ae557611ae561226f565b60208201516001600160601b031015611b0557611b058260200151612489565b806001600160601b0316826020015110611b2757611b2782602001518261265a565b60408201516001600160a01b0316611b4157611b41612256565b60608201516001600160a01b0316611b5b57611b5b612256565b611b688260800151612685565b60a08201516118ec576118ec61226f565b600080600080611b9488600001548960010154896001612502565b90506000866002811115611baa57611baa61326d565b1415611be0578754859250611bc3908390836000612699565b6001890154909450611bd9908390836000612699565b9250611c6a565b6001866002811115611bf457611bf461326d565b1415611c24578754859450611c0e90859083906001612699565b6001890154909250611bd9908390836000612699565b6002866002811115611c3857611c3861326d565b1415611c6a57600180890154869450611c549185918491612699565b8854909250611c67908390836000612699565b93505b83886000016000828254611c7e9190612f91565b9250508190555082886001016000828254611c999190612f91565b92505081905550509450945094915050565b8151611cb957611cb961226f565b60208201516001600160601b031015611cd957611cd98260200151612489565b806001600160601b031682602001511015611cfc57611cfc8260200151826124a5565b60808201516001600160a01b0316611d1657611d16612256565b60408201516001600160a01b0316611d3057611d30612256565b60608201516001600160a01b0316611d4a57611d4a612256565b611d578260a001516124d0565b60c08201511580156118df575060e08201516118ec576118ec61226f565b600082815260026020908152604080832084845290915290205460ff166118ec5760008281526002602081815260408084208585528252808420805460ff191660019081179091558151808301909252958152908101938452600380549586018155909252905192027fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b810192909255517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85c90910155565b6000808080866001811115611e4457611e4461326d565b1415611e6457611e5d85935083859350838c6000612502565b9050611eaa565b6001866001811115611e7857611e7861326d565b1415611eaa57611e88848661312a565b9050611e98858b60006001612545565b9250611ea7848b600180612545565b91505b8215611ef957828b6000016000828254611ec4919061312a565b90915550506001600160a01b038916600090815260028c01602052604081208054859290611ef390849061312a565b90915550505b8115611f4857818b6001016000828254611f13919061312a565b90915550506001600160a01b038816600090815260038c01602052604081208054849290611f4290849061312a565b90915550505b6001600160a01b038716600090815260048c01602052604081208054839290611f7290849061312a565b90915550508a546001808d0154611f8b92918d90612502565b50985098509895505050505050565b808210156118ec57604051631c22ff0160e21b81526004810183905260248101829052604401611830565b600080826002811115611fda57611fda61326d565b141561200157506001600160a01b03821660009081526002840160205260409020546117bf565b60018260028111156120155761201561326d565b141561203c57506001600160a01b03821660009081526003840160205260409020546117bf565b60028260028111156120505761205061326d565b14156117bf5750506001600160a01b03166000908152600491909101602052604090205490565b81516120855761208561226f565b60208201516001600160601b0310156120a5576120a58260200151612489565b806001600160601b0316826020015110156120c8576120c88260200151826124a5565b60408201516001600160a01b03166120e2576120e2612256565b60608201516001600160a01b03166120fc576120fc612256565b6121098260a001516124d0565b60c08201516118ec576118ec61226f565b600080808460018111156121305761213061326d565b141561214d5782915061214682886001886126a7565b905061217a565b60018460018111156121615761216161326d565b141561217a5750816121778188600088156126a7565b91505b84156121e957818860000160008282546121949190612f91565b92505081905550808860010160008282546121af919061312a565b90915550506001600160a01b0386166000908152600389016020526040812080548392906121de90849061312a565b9091555061224b9050565b808860010160008282546121fd9190612f91565b909155505087548290899060009061221690849061312a565b90915550506001600160a01b03861660009081526002890160205260408120805484929061224590849061312a565b90915550505b965096945050505050565b60405163d92e233d60e01b815260040160405180910390fd5b60405163af458c0760e01b815260040160405180910390fd5b600381600281111561229c5761229c61326d565b106122ba5760405163673f032f60e11b815260040160405180910390fd5b50565b60008260028111156122d1576122d161326d565b141561233257336000908152600285016020526040812080548392906122f8908490612f91565b90915550506001600160a01b03831660009081526002850160205260408120805483929061232790849061312a565b9091555061240c9050565b60018260028111156123465761234661326d565b141561239c576001600160a01b03831660009081526003850160205260408120805483929061237690849061312a565b909155505033600090815260038501602052604081208054839290612327908490612f91565b60028260028111156123b0576123b061326d565b141561240c57336000908152600485016020526040812080548392906123d7908490612f91565b90915550506001600160a01b03831660009081526004850160205260408120805483929061240690849061312a565b90915550505b50505050565b6000808260028111156124275761242761326d565b1415612435575082546117bf565b60018260028111156124495761244961326d565b141561245a575060018301546117bf565b600282600281111561246e5761246e61326d565b14156117bf576117bc84600001548560010154856001612502565b6040516335f135d360e01b815260048101829052602401611830565b60405163647284e960e11b8152600481018390526001600160601b0382166024820152604401611830565b60028160018111156124e4576124e461326d565b106122ba5760405163280503e760e11b815260040160405180910390fd5b60006001600160801b03831161252e5761251f85846001856126a7565b612529908561312a565b610e65565b61253b84846000856126a7565b610e65908661312a565b60006001600160801b03841161256d57826125675761252985856000856126a7565b84610e65565b826125785784610e65565b610e6585856001856126a7565b60006125da826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166126ce9092919063ffffffff16565b90508051600014806125fb5750808060200190518101906125fb91906132cb565b611ad25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401611830565b60405163b9e0f8d560e01b8152600481018390526001600160601b0382166024820152604401611830565b60038160028111156124e4576124e461326d565b6000610e65858585856126dd565b6000826126be5761252985600160801b86856126dd565b610e658585600160801b856126dd565b60606117bc84846000856127d9565b60008060006126ec87876128b4565b91509150806000141561270d576127048286866128d3565b92505050610e68565b80851161273e5760405163362ced0960e11b8152600481018890526024810187905260448101869052606401611830565b600085878909600087810388169788900497600260038a028118808b02820302808b02820302808b02820302808b02820302808b02820302808b029091030291819003819004600101868411909503948502929095039490940417928302935084905080156127bc575084806127b6576127b66132e8565b86880915155b156127cf57826127cb816132fe565b9350505b5050949350505050565b60608247101561283a5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401611830565b600080866001600160a01b031685876040516128569190613319565b60006040518083038185875af1925050503d8060008114612893576040519150601f19603f3d011682016040523d82523d6000602084013e612898565b606091505b50915091506128a98783838761290e565b979650505050505050565b6000806000198385098385029250828110838203039150509250929050565b60006128df8385613335565b90508180156128f657506128f38385613349565b15155b156117bf5780612905816132fe565b95945050505050565b6060831561297a578251612973576001600160a01b0385163b6129735760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401611830565b5081610e68565b610e68838381511561298f5781518083602001fd5b8060405162461bcd60e51b8152600401611830919061335d565b6000602082840312156129bb57600080fd5b5035919050565b600061010082840312156129d557600080fd5b50919050565b6000602082840312156129ed57600080fd5b81356001600160401b03811115612a0357600080fd5b610e68848285016129c2565b60005b83811015612a2a578181015183820152602001612a12565b8381111561240c5750506000910152565b60008151808452612a53816020860160208601612a0f565b601f01601f19169290920160200192915050565b848152836020820152826040820152608060608201526000612a8c6080830184612a3b565b9695505050505050565b600060208284031215612aa857600080fd5b81356001600160401b03811115612abe57600080fd5b820160e081850312156117bf57600080fd5b600060208284031215612ae257600080fd5b81356001600160401b03811115612af857600080fd5b820161012081850312156117bf57600080fd5b80356001600160a01b0381168114612b2257600080fd5b919050565b600381106122ba57600080fd5b8035612b2281612b27565b60008060008060808587031215612b5557600080fd5b8435935060208501359250612b6c60408601612b0b565b91506060850135612b7c81612b27565b939692955090935050565b838152826020820152606060408201526000610e656060830184612a3b565b600080600080600060a08688031215612bbe57600080fd5b8535945060208601359350612bd560408701612b0b565b92506060860135612be581612b27565b949793965091946080013592915050565b600080600060608486031215612c0b57600080fd5b83359250602084013591506040840135612c2481612b27565b809150509250925092565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b0381118282101715612c7e57612c7e612c45565b60405290565b60405160e081016001600160401b0381118282101715612c7e57612c7e612c45565b60405161012081016001600160401b0381118282101715612c7e57612c7e612c45565b604051601f8201601f191681016001600160401b0381118282101715612cf157612cf1612c45565b604052919050565b600281106122ba57600080fd5b8035612b2281612cf9565b60006001600160401b03821115612d2a57612d2a612c45565b50601f01601f191660200190565b600082601f830112612d4957600080fd5b8135612d5c612d5782612d11565b612cc9565b818152846020838601011115612d7157600080fd5b816020850160208301376000918101602001919091529392505050565b60006101008236031215612da157600080fd5b612da9612c5b565b8235815260208301356020820152612dc360408401612b0b565b6040820152612dd460608401612b0b565b6060820152612de560808401612d06565b608082015260a083013560a082015260c083013560c082015260e08301356001600160401b03811115612e1757600080fd5b612e2336828601612d38565b60e08301525092915050565b600060208284031215612e4157600080fd5b81356117bf81612cf9565b600060208284031215612e5e57600080fd5b6117bf82612b0b565b6000808335601e19843603018112612e7e57600080fd5b8301803591506001600160401b03821115612e9857600080fd5b602001915036819003821315612ead57600080fd5b9250929050565b8051825260208101516020830152604081015160408301526060810151606083015260808101516080830152600060a082015160c060a0850152610e6860c0850182612a3b565b6020815260006117bf6020830184612eb4565b600060208284031215612f2057600080fd5b81516001600160401b03811115612f3657600080fd5b8201601f81018413612f4757600080fd5b8051612f55612d5782612d11565b818152856020838501011115612f6a57600080fd5b612905826020830160208601612a0f565b634e487b7160e01b600052601160045260246000fd5b600082821015612fa357612fa3612f7b565b500390565b600060e08236031215612fba57600080fd5b612fc2612c84565b8235815260208301356020820152612fdc60408401612b0b565b6040820152612fed60608401612b0b565b6060820152612ffe60808401612b34565b608082015260a083013560a082015260c08301356001600160401b0381111561302657600080fd5b61303236828601612d38565b60c08301525092915050565b60006020828403121561305057600080fd5b81356117bf81612b27565b6000610120823603121561306e57600080fd5b613076612ca6565b823581526020830135602082015261309060408401612b0b565b60408201526130a160608401612b0b565b60608201526130b260808401612b0b565b60808201526130c360a08401612d06565b60a082015260c083013560c082015260e083013560e0820152610100808401356001600160401b038111156130f757600080fd5b61310336828701612d38565b918301919091525092915050565b60006020828403121561312357600080fd5b5051919050565b6000821982111561313d5761313d612f7b565b500190565b634e487b7160e01b600052603160045260246000fd5b80151581146122ba57600080fd5b8035612b2281613158565b6000610100823603121561318457600080fd5b61318c612c5b565b82358152602083013560208201526131a660408401612b0b565b60408201526131b760608401612b0b565b60608201526131c860808401613166565b60808201526131d960a08401612d06565b60a082015260c083013560c082015260e08301356001600160401b03811115612e1757600080fd5b60006020828403121561321357600080fd5b81356117bf81613158565b60208152815160208201526020820151604082015260408201511515606082015260608201516080820152608082015160a0820152600060a083015160c080840152610e6860e0840182612a3b565b634e487b7160e01b600052602160045260246000fd5b6001600160a01b0385811682528416602082015260808101600384106132b957634e487b7160e01b600052602160045260246000fd5b60408201939093526060015292915050565b6000602082840312156132dd57600080fd5b81516117bf81613158565b634e487b7160e01b600052601260045260246000fd5b600060001982141561331257613312612f7b565b5060010190565b6000825161332b818460208701612a0f565b9190910192915050565b600082613344576133446132e8565b500490565b600082613358576133586132e8565b500690565b6020815260006117bf6020830184612a3b56fea2646970667358221220bbe7d6b0de1ed1da3ecaaf7929d17d270a39c5916955234df7b07b9efcc59b4264736f6c63430008080033

Block Transaction Difficulty 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
Loading...
Loading
[ 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.