Contract 0x1f6d98638eee9f689684767c3021230dd68df419 7

Lyra 
 

Contract Overview

Lyra: ETH Option Market
Balance:
0 ETH

EtherValue:
$0.00

Token:
My Name Tag:
Not Available, login to update

ContractCreator:
GENESIS at txn GENESIS_1f6d98638eee9f689684767c3021230dd68df419
Txn Hash Method
Block
From
To
Value
0x82640bdcd07241dd7c67e36ada98abed6c7aa3627110f3c48c8e79b2feb8ab6eLiquidate Expire...1147474072024-01-12 21:06:3175 days 15 hrs ago0x0363132e188caf85598111a5450d8bc20dd45378 IN  Lyra: ETH Option Market0 ETH0.0006531696871.503056291
0x12ed8883856ef3b577c907c452e61a454b21c0dda29499ccab70a28a2736bf7aLiquidate Expire...1122028622023-11-14 23:28:21134 days 13 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0006548006631.508548389
0x73f02acb16a381b23b0e81a7bbff396c4d5d95280b281bebd96abbca4b8dd0beLiquidate Expire...1083178052023-08-17 1:06:27224 days 11 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0006569227211.500184771
0xf2ba1a3f00e17c85264efdf5d6ea7b3c78cbd79c49707108a4bd69dffee1c98dLiquidate Expire...1082278782023-08-14 23:08:53226 days 13 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0006348245341.500000285
0x25f8708f3cbea26182a5514ad343cffc945cb014223482cdb1e9bdd1fe1a9c1aSettle Options1077613862023-08-04 3:59:09237 days 8 hrs ago0x187d60b2ee210acbb71fdce25e2a0ff0f635a18e IN  Lyra: ETH Option Market0 ETH0.0000210183120.000006843
0x0cf9e0121ce206996e7f277fdbb0ae1e60d67ba151a689651fff041576def8b4Liquidate Expire...998197832023-05-19 21:06:19313 days 15 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0001593311340.001
0xa90f25528d5fabbbc3140e11ba613056170f096ca6d8ff8b6b03be44d057286bLiquidate Expire...828850622023-03-22 11:09:47372 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000527267660.001
0x4c30e5f36d686d38f9c3092b290f1d708eed309878bf88cac66f926ebf818ca1Liquidate Expire...748277712023-02-16 22:08:48405 days 14 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0001338297780.001
0x3e421d9d612755eee5754842b07fd6f1153a4a4b035f6b052eba689646f40e2fLiquidate Expire...642726102023-01-11 11:11:18442 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000624225170.001
0xa746f8557072a323fb7c82a237c854247ff887ae3b2e8a54cb370c13b38d4de5Settle Options573625442022-12-29 19:32:50454 days 17 hrs ago0x24749aedf18208ab74a8110e07e820286bb5acf8 IN  Lyra: ETH Option Market0 ETH0.000077007680.001
0x7386aa54efba9824b76ba6d2711f9e612326c3099fc3468c22012864724c79d2Liquidate Expire...463182992022-12-07 9:12:52477 days 3 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000511030130.001
0x98648718dd5d4f399b1245598a3d18627c6fd4c2db551b25317a469059d862a5Liquidate Expire...320629762022-10-27 19:37:41517 days 17 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000846994990.001
0xa6afccbe29f077c5c60f8d996d236bfc2fa00fc912218f0062332749b7be1abfSettle Options243545632022-09-21 20:36:45553 days 16 hrs ago0xfc575528274abbc0c7341d71ebcb3533b62b6840 IN  Lyra: ETH Option Market0 ETH0.0000398548760.00100155
0x055051604ffc2ffcb55927e886e259d582eaf7a07b442716d7b5e713e9513202Liquidate Expire...239214142022-09-20 11:35:23555 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000157182680.001
0x5d866d670578c83cfcce4232427f29950e65c8eca2a82bce7a27f4f833060fc4Liquidate Expire...211557302022-08-30 11:37:06576 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000271876250.001
0x685dae2baaa6611de60e452c045a414317d000e7a9f9dcb24e58fcafb9d2889cLiquidate Expire...191769572022-08-15 11:35:38591 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000744777020.001
0x0b82688d22a72e2119864a4dc342e6021735e5c20bd5a90e5a14e18c0a0ec3b0Liquidate Expire...176476662022-08-04 12:29:50602 days 16 mins ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000530170640.001
0xf99f87f9e15840a5803a0d55c43afe77b88a3213a1bf93b0e6489a660bc59cd6Settle Options176256152022-08-04 9:58:15602 days 2 hrs ago0x122d7c00ebc9de5b9924b696da2d2d2f872d4e89 IN  Lyra: ETH Option Market0 ETH0.0000318949230.001
0x357ebbd2aafd2a7964a19a3cae086631d793e81496be7c7d98696a87eedb3cccSettle Options175261872022-08-03 22:54:15602 days 13 hrs ago0x9678d83b3ba7bd961fb4106041993daca95fb75b IN  Lyra: ETH Option Market0 ETH0.0000397292610.001
0xe672dcffc4ac54edc01b8a6b3cebb113cfbbc9d8d96e3f52af6f8044413520f6Liquidate Expire...160133522022-07-27 11:20:47610 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000227213760.001
0xfe60a1befe74ae3d0ba17013f88e4f9cdbf9f3e906cd78c1d2fbf6ff0c3751c3Liquidate Expire...148583192022-07-18 11:00:23619 days 1 hr ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000421140320.001
0x390cb152eec522fc16c30d029cbdbd3a72613d7de2640e5967a3e170370160aeSettle Options143060912022-07-12 4:49:17625 days 7 hrs ago0x39143272e8927312f9152822f7fe16cfecadbe76 IN  Lyra: ETH Option Market0 ETH0.0000482468320.001
0xe8e4332180ef56b0d9ca39baa0d209a829ba7b86987ceea01cf3f1126279f58cLiquidate Expire...141881982022-07-10 3:55:20627 days 8 hrs ago0xe1a6cb5b3b04761d583de2d829a759f0932319e2 IN  Lyra: ETH Option Market0 ETH0.0000327727240.001
0x613efd5529699cd69cdf5e6163f7276a10d06e00254143c0002257431c1cf3abSettle Options139298092022-07-06 3:47:00631 days 8 hrs ago0x628d896988f2c56921f09df2aed3549cac359774 IN  Lyra: ETH Option Market0 ETH0.0000903675420.001
0x3a16a1d96af995644ba7aeb2eac1ae9ef39cd2da5fc126b939a1e67608e9b7a2Settle Options136416562022-07-01 16:10:33635 days 20 hrs ago0xb386b3370f3c258d241fed394050d60f7c421be7 IN  Lyra: ETH Option Market0 ETH0.0003354907780.001
[ Download CSV Export 
Latest 11 internal transactions
Parent Txn Hash Block From To Value
0x5074025cf36837166131ddd3f5e0c231ce654f8d08221163aa43cd5ac3914a371055399872023-06-13 17:52:31288 days 18 hrs ago Lyra: ETH Liquidity Pool Lyra: ETH Option Market0 ETH
0xb9030cf6c963b0208fcdbd087a1eb2813dbe10f6b74dba7d00f1151f5b74fb071053058442023-06-08 7:47:45294 days 4 hrs ago 0xfa923aa6b4df5bea456df37fa044b37f0fddcdb4 Lyra: ETH Option Market0 ETH
0xb9030cf6c963b0208fcdbd087a1eb2813dbe10f6b74dba7d00f1151f5b74fb071053058442023-06-08 7:47:45294 days 4 hrs ago 0xfa923aa6b4df5bea456df37fa044b37f0fddcdb4 Lyra: ETH Option Market0 ETH
0xb9030cf6c963b0208fcdbd087a1eb2813dbe10f6b74dba7d00f1151f5b74fb071053058442023-06-08 7:47:45294 days 4 hrs ago 0x331cf6e3e59b18a8bc776a0f652af9e2b42781c5 Lyra: ETH Option Market0 ETH
0xb9030cf6c963b0208fcdbd087a1eb2813dbe10f6b74dba7d00f1151f5b74fb071053058442023-06-08 7:47:45294 days 4 hrs ago 0x331cf6e3e59b18a8bc776a0f652af9e2b42781c5 Lyra: ETH Option Market0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago Lyra: ETH Option Greek Cache Lyra: ETH Option Market0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago Lyra: ETH Option Greek Cache Lyra: ETH Option Market0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago Lyra: ETH Option Greek Cache Lyra: ETH Option Market0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago Lyra: ETH Option Market Lyra: ETH Option Greek Cache0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago Lyra: ETH Option Market Lyra: ETH Liquidity Pool0 ETH
0x7661c1a5f4baa865dfa9f5c377776a4bb4c2f07049e617d31d4b84ea8ad57e021053055782023-06-08 7:38:53294 days 5 hrs ago 0xf6f583141590577eae48a99b7f09ad3a7274ce40 Lyra: ETH Option Market0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Genesis Bytecode Match Only)

Contract Name:
OptionMarket

Compiler Version
v0.7.6

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion
File 1 of 34 : OptionMarket.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Libraries
import "./synthetix/SafeDecimalMath.sol";

// Interfaces
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./LyraGlobals.sol";
import "./LiquidityPool.sol";
import "./OptionToken.sol";
import "./OptionGreekCache.sol";
import "./LyraGlobals.sol";
import "./ShortCollateral.sol";
import "./interfaces/IOptionToken.sol";

/**
 * @title OptionMarket
 * @author Lyra
 * @dev An AMM which allows users to trade options. Supports both buying and selling options, which determine the value
 * for the listing's IV. Also allows for auto cash settling options as at expiry.
 */
contract OptionMarket is IOptionMarket {
  using SafeMath for uint;
  using SafeDecimalMath for uint;

  ILyraGlobals internal globals;
  ILiquidityPool internal liquidityPool;
  IOptionMarketPricer internal optionPricer;
  IOptionGreekCache internal greekCache;
  IShortCollateral internal shortCollateral;
  IOptionToken internal optionToken;
  IERC20 internal quoteAsset;
  IERC20 internal baseAsset;

  mapping(uint => string) internal errorMessages;
  address internal owner;
  bool internal initialized = false;
  uint internal nextListingId = 1;
  uint internal nextBoardId = 1;
  uint[] internal liveBoards;

  uint public override maxExpiryTimestamp;
  mapping(uint => OptionBoard) public override optionBoards;
  mapping(uint => OptionListing) public override optionListings;
  mapping(uint => uint) public override boardToPriceAtExpiry;
  mapping(uint => uint) public override listingToBaseReturnedRatio;

  constructor() {
    owner = msg.sender;
  }

  /**
   * @dev Initialize the contract.
   *
   * @param _globals LyraGlobals address
   * @param _liquidityPool LiquidityPool address
   * @param _optionPricer OptionMarketPricer address
   * @param _greekCache OptionGreekCache address
   * @param _quoteAsset Quote asset address
   * @param _baseAsset Base asset address
   */
  function init(
    ILyraGlobals _globals,
    ILiquidityPool _liquidityPool,
    IOptionMarketPricer _optionPricer,
    IOptionGreekCache _greekCache,
    IShortCollateral _shortCollateral,
    IOptionToken _optionToken,
    IERC20 _quoteAsset,
    IERC20 _baseAsset,
    string[] memory _errorMessages
  ) external {
    require(!initialized, "already initialized");
    globals = _globals;
    liquidityPool = _liquidityPool;
    optionPricer = _optionPricer;
    greekCache = _greekCache;
    shortCollateral = _shortCollateral;
    optionToken = _optionToken;
    quoteAsset = _quoteAsset;
    baseAsset = _baseAsset;
    require(_errorMessages.length == uint(Error.Last), "error msg count");
    for (uint i = 0; i < _errorMessages.length; i++) {
      errorMessages[i] = _errorMessages[i];
    }
    initialized = true;
  }

  /////////////////////
  // Admin functions //
  /////////////////////

  /**
   * @dev Transfer this contract ownership to `newOwner`.
   * @param newOwner The address of the new contract owner.
   */
  function transferOwnership(address newOwner) external override onlyOwner {
    _require(newOwner != address(0), Error.TransferOwnerToZero);
    emit OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

  /**
   * @dev Sets the frozen state of an OptionBoard.
   * @param boardId The id of the OptionBoard.
   * @param frozen Whether the board will be frozen or not.
   */
  function setBoardFrozen(uint boardId, bool frozen) external override onlyOwner {
    OptionBoard storage board = optionBoards[boardId];
    _require(board.id == boardId, Error.InvalidBoardId);
    optionBoards[boardId].frozen = frozen;
    emit BoardFrozen(boardId, frozen);
  }

  /**
   * @dev Sets the baseIv of a frozen OptionBoard.
   * @param boardId The id of the OptionBoard.
   * @param baseIv The new baseIv value.
   */
  function setBoardBaseIv(uint boardId, uint baseIv) external override onlyOwner {
    OptionBoard storage board = optionBoards[boardId];
    _require(board.id == boardId && board.frozen, Error.InvalidBoardIdOrNotFrozen);
    board.iv = baseIv;
    greekCache.setBoardIv(boardId, baseIv);
    emit BoardBaseIvSet(boardId, baseIv);
  }

  /**
   * @dev Sets the skew of an OptionListing of a frozen OptionBoard.
   * @param listingId The id of the listing being modified.
   * @param skew The new skew value.
   */
  function setListingSkew(uint listingId, uint skew) external override onlyOwner {
    OptionListing storage listing = optionListings[listingId];
    OptionBoard memory board = optionBoards[listing.boardId];
    _require(listing.id == listingId && board.frozen, Error.InvalidListingIdOrNotFrozen);
    listing.skew = skew;
    greekCache.setListingSkew(listingId, skew);
    emit ListingSkewSet(listingId, skew);
  }

  /**
   * @dev Creates a new OptionBoard which contains OptionListings.
   * This only allows a new maxExpiryTimestamp to be added if the previous one has been passed. This is done to create a
   * system of "rounds" where PnL for LPs can be computed easily across all boards.
   *
   * @param expiry The timestamp when the board expires.
   * @param baseIV The initial value for implied volatility.
   * @param strikes The array of strikes offered for this expiry.
   * @param skews The array of skews for each strike.
   */
  function createOptionBoard(
    uint expiry,
    uint baseIV,
    uint[] memory strikes,
    uint[] memory skews
  ) external override onlyOwner returns (uint) {
    // strike and skew length must match and must have at least 1
    _require(strikes.length == skews.length && strikes.length > 0, Error.StrikeSkewLengthMismatch);
    // We do not support expiry more than 10 weeks out, as it locks collateral for the entire duration
    _require(expiry.sub(block.timestamp) < 10 weeks, Error.BoardMaxExpiryReached);

    if (expiry > maxExpiryTimestamp) {
      _require(liveBoards.length == 0, Error.CannotStartNewRoundWhenBoardsExist);
      liquidityPool.startRound(maxExpiryTimestamp, expiry);
      maxExpiryTimestamp = expiry;
    }

    uint boardId = nextBoardId++;
    optionBoards[boardId].id = boardId;
    optionBoards[boardId].expiry = expiry;
    optionBoards[boardId].iv = baseIV;

    liveBoards.push(boardId);

    emit BoardCreated(boardId, expiry, baseIV);

    for (uint i = 0; i < strikes.length; i++) {
      _addListingToBoard(boardId, strikes[i], skews[i]);
    }

    greekCache.addBoard(boardId);

    return boardId;
  }

  /**
   * @dev Add a listing to an existing board in the OptionMarket.
   *
   * @param boardId The id of the board which the listing will be added
   * @param strike Strike of the Listing
   * @param skew Skew of the Listing
   */
  function addListingToBoard(
    uint boardId,
    uint strike,
    uint skew
  ) external override onlyOwner {
    OptionBoard storage board = optionBoards[boardId];
    _require(board.id == boardId, Error.InvalidBoardId);

    uint listingId = _addListingToBoard(boardId, strike, skew);
    greekCache.addListingToBoard(boardId, listingId);
  }

  /**
   * @dev Add a listing to an existing board.
   */
  function _addListingToBoard(
    uint boardId,
    uint strike,
    uint skew
  ) internal returns (uint listingId) {
    listingId = nextListingId;
    nextListingId += 4;
    optionListings[listingId] = OptionListing(listingId, strike, skew, 0, 0, 0, 0, boardId);
    optionBoards[boardId].listingIds.push(listingId);
    emit ListingAdded(boardId, listingId, strike, skew);
  }

  ///////////
  // Views //
  ///////////

  /**
   * @dev Returns the list of live board ids.
   */
  function getLiveBoards() external view override returns (uint[] memory _liveBoards) {
    _liveBoards = new uint[](liveBoards.length);
    for (uint i = 0; i < liveBoards.length; i++) {
      _liveBoards[i] = liveBoards[i];
    }
  }

  /**
   * @dev Returns the listing ids for a given `boardId`.
   *
   * @param boardId The id of the relevant OptionBoard.
   */
  function getBoardListings(uint boardId) external view override returns (uint[] memory) {
    uint[] memory listingIds = new uint[](optionBoards[boardId].listingIds.length);
    for (uint i = 0; i < optionBoards[boardId].listingIds.length; i++) {
      listingIds[i] = optionBoards[boardId].listingIds[i];
    }
    return listingIds;
  }

  ////////////////////
  // User functions //
  ////////////////////

  /**
   * @dev Opens a position, which may be long call, long put, short call or short put.
   *
   * @param _listingId The id of the relevant OptionListing.
   * @param tradeType Is the trade long or short?
   * @param amount The amount the user has requested to trade.
   */
  function openPosition(
    uint _listingId,
    TradeType tradeType,
    uint amount
  ) external override returns (uint totalCost) {
    _require(int(amount) > 0 && uint(TradeType.SHORT_PUT) >= uint(tradeType), Error.ZeroAmountOrInvalidTradeType);

    bool isLong = tradeType == TradeType.LONG_CALL || tradeType == TradeType.LONG_PUT;

    OptionListing storage listing = optionListings[_listingId];
    OptionBoard storage board = optionBoards[listing.boardId];

    (
      LyraGlobals.PricingGlobals memory pricingGlobals,
      LyraGlobals.ExchangeGlobals memory exchangeGlobals,
      uint tradingCutoff
    ) = globals.getGlobalsForOptionTrade(address(this), isLong);

    // Note: call will fail here if it is an invalid boardId (expiry will be 0)
    _require(!board.frozen && block.timestamp + tradingCutoff < board.expiry, Error.BoardFrozenOrTradingCutoffReached);

    Trade memory trade =
      Trade({
        isBuy: isLong,
        amount: amount,
        vol: board.iv.multiplyDecimalRound(listing.skew),
        expiry: board.expiry,
        liquidity: liquidityPool.getLiquidity(exchangeGlobals.spotPrice, exchangeGlobals.short)
      });

    optionToken.mint(msg.sender, _listingId + uint(tradeType), amount);

    if (tradeType == TradeType.LONG_CALL) {
      listing.longCall = listing.longCall.add(amount);
    } else if (tradeType == TradeType.SHORT_CALL) {
      listing.shortCall = listing.shortCall.add(amount);
    } else if (tradeType == TradeType.LONG_PUT) {
      listing.longPut = listing.longPut.add(amount);
    } else {
      listing.shortPut = listing.shortPut.add(amount);
    }

    totalCost = _doTrade(listing, board, trade, pricingGlobals);

    if (tradeType == TradeType.LONG_CALL) {
      liquidityPool.lockBase(amount, exchangeGlobals, trade.liquidity);
      _require(quoteAsset.transferFrom(msg.sender, address(liquidityPool), totalCost), Error.QuoteTransferFailed);
    } else if (tradeType == TradeType.LONG_PUT) {
      liquidityPool.lockQuote(amount.multiplyDecimal(listing.strike), trade.liquidity.freeCollatLiquidity);
      _require(quoteAsset.transferFrom(msg.sender, address(liquidityPool), totalCost), Error.QuoteTransferFailed);
    } else if (tradeType == TradeType.SHORT_CALL) {
      _require(baseAsset.transferFrom(msg.sender, address(shortCollateral), amount), Error.BaseTransferFailed);
      liquidityPool.sendPremium(msg.sender, totalCost, trade.liquidity.freeCollatLiquidity);
    } else {
      _require(
        quoteAsset.transferFrom(msg.sender, address(shortCollateral), amount.multiplyDecimal(listing.strike)),
        Error.QuoteTransferFailed
      );
      liquidityPool.sendPremium(msg.sender, totalCost, trade.liquidity.freeCollatLiquidity);
    }

    emit PositionOpened(msg.sender, _listingId, tradeType, amount, totalCost);
  }

  /**
   * @dev Closes some amount of an open position. The user does not have to close the whole position.
   *
   * @param _listingId The id of the relevant OptionListing.
   * @param tradeType Is the trade long or short?
   * @param amount The amount the user has requested to trade.
   */
  function closePosition(
    uint _listingId,
    TradeType tradeType,
    uint amount
  ) external override returns (uint totalCost) {
    _require(int(amount) > 0 && uint(TradeType.SHORT_PUT) >= uint(tradeType), Error.ZeroAmountOrInvalidTradeType);

    bool isLong = tradeType == TradeType.LONG_CALL || tradeType == TradeType.LONG_PUT;

    OptionListing storage listing = optionListings[_listingId];
    OptionBoard storage board = optionBoards[listing.boardId];

    (
      LyraGlobals.PricingGlobals memory pricingGlobals,
      LyraGlobals.ExchangeGlobals memory exchangeGlobals,
      uint tradingCutoff
    ) = globals.getGlobalsForOptionTrade(address(this), !isLong);

    _require(!board.frozen && block.timestamp + tradingCutoff < board.expiry, Error.BoardFrozenOrTradingCutoffReached);

    Trade memory trade =
      Trade({
        isBuy: !isLong,
        amount: amount,
        vol: board.iv.multiplyDecimalRound(listing.skew),
        expiry: board.expiry,
        liquidity: liquidityPool.getLiquidity(exchangeGlobals.spotPrice, exchangeGlobals.short)
      });

    optionToken.burn(msg.sender, _listingId + uint(tradeType), amount);

    if (tradeType == TradeType.LONG_CALL) {
      listing.longCall = listing.longCall.sub(amount);
    } else if (tradeType == TradeType.SHORT_CALL) {
      listing.shortCall = listing.shortCall.sub(amount);
    } else if (tradeType == TradeType.LONG_PUT) {
      listing.longPut = listing.longPut.sub(amount);
    } else {
      listing.shortPut = listing.shortPut.sub(amount);
    }
    totalCost = _doTrade(listing, board, trade, pricingGlobals);

    if (tradeType == TradeType.LONG_CALL) {
      liquidityPool.freeBase(amount);
      liquidityPool.sendPremium(msg.sender, totalCost, trade.liquidity.freeCollatLiquidity);
    } else if (tradeType == TradeType.LONG_PUT) {
      liquidityPool.freeQuoteCollateral(amount.multiplyDecimal(listing.strike));
      liquidityPool.sendPremium(msg.sender, totalCost, trade.liquidity.freeCollatLiquidity);
    } else if (tradeType == TradeType.SHORT_CALL) {
      shortCollateral.sendBaseCollateral(msg.sender, amount);
      _require(quoteAsset.transferFrom(msg.sender, address(liquidityPool), totalCost), Error.QuoteTransferFailed);
    } else {
      shortCollateral.sendQuoteCollateral(msg.sender, amount.multiplyDecimal(listing.strike).sub(totalCost));
      shortCollateral.sendQuoteCollateral(address(liquidityPool), totalCost);
    }

    emit PositionClosed(msg.sender, _listingId, tradeType, amount, totalCost);
  }

  /**
   * @dev Determine the cost of the trade and update the system's iv/skew parameters.
   *
   * @param listing The relevant OptionListing.
   * @param board The relevant OptionBoard.
   * @param trade The trade parameters.
   * @param pricingGlobals The pricing globals.
   */
  function _doTrade(
    OptionListing storage listing,
    OptionBoard storage board,
    Trade memory trade,
    LyraGlobals.PricingGlobals memory pricingGlobals
  ) internal returns (uint) {
    (uint totalCost, uint newIv, uint newSkew) =
      optionPricer.updateCacheAndGetTotalCost(listing, trade, pricingGlobals, board.iv);
    listing.skew = newSkew;
    board.iv = newIv;

    emit BoardBaseIvSet(board.id, newIv);
    emit ListingSkewSet(listing.id, newSkew);
    return totalCost;
  }

  /**
   * @dev Liquidates a board that has passed expiry. This function will not preserve the ordering of liveBoards.
   *
   * @param boardId The id of the relevant OptionBoard.
   */
  function liquidateExpiredBoard(uint boardId) external override {
    OptionBoard memory board = optionBoards[boardId];
    _require(board.expiry <= block.timestamp, Error.BoardNotExpired);
    bool popped = false;
    // Find and remove the board from the list of live boards
    for (uint i = 0; i < liveBoards.length; i++) {
      if (liveBoards[i] == boardId) {
        liveBoards[i] = liveBoards[liveBoards.length - 1];
        liveBoards.pop();
        popped = true;
        break;
      }
    }
    // prevent old boards being liquidated
    _require(popped, Error.BoardAlreadyLiquidated);

    _liquidateExpiredBoard(board);
    greekCache.removeBoard(boardId);
  }

  /**
   * @dev Liquidates an expired board.
   * It will transfer all short collateral for ITM options that the market owns.
   * It will reserve collateral for users to settle their ITM long options.
   *
   * @param board The relevant OptionBoard.
   */
  function _liquidateExpiredBoard(OptionBoard memory board) internal {
    LyraGlobals.ExchangeGlobals memory exchangeGlobals =
      globals.getExchangeGlobals(address(this), ILyraGlobals.ExchangeType.ALL);

    uint totalUserLongProfitQuote;
    uint totalBoardLongCallCollateral;
    uint totalBoardLongPutCollateral;
    uint totalAMMShortCallProfitBase;
    uint totalAMMShortPutProfitQuote;

    // Store the price now for when users come to settle their options
    boardToPriceAtExpiry[board.id] = exchangeGlobals.spotPrice;

    for (uint i = 0; i < board.listingIds.length; i++) {
      OptionListing memory listing = optionListings[board.listingIds[i]];

      totalBoardLongCallCollateral = totalBoardLongCallCollateral.add(listing.longCall);
      totalBoardLongPutCollateral = totalBoardLongPutCollateral.add(listing.longPut.multiplyDecimal(listing.strike));

      if (exchangeGlobals.spotPrice > listing.strike) {
        // For long calls
        totalUserLongProfitQuote = totalUserLongProfitQuote.add(
          listing.longCall.multiplyDecimal(exchangeGlobals.spotPrice - listing.strike)
        );

        // Per unit of shortCalls
        uint amountReservedBase =
          (exchangeGlobals.spotPrice - listing.strike)
            .divideDecimal(SafeDecimalMath.UNIT.sub(exchangeGlobals.baseQuoteFeeRate))
            .divideDecimal(exchangeGlobals.spotPrice);
        // This is impossible unless the baseAsset price has gone up ~900%+
        if (amountReservedBase > SafeDecimalMath.UNIT) {
          amountReservedBase = SafeDecimalMath.UNIT;
        }

        totalAMMShortCallProfitBase = totalAMMShortCallProfitBase.add(
          amountReservedBase.multiplyDecimal(listing.shortCall)
        );
        listingToBaseReturnedRatio[listing.id] = SafeDecimalMath.UNIT.sub(amountReservedBase);
      } else {
        listingToBaseReturnedRatio[listing.id] = SafeDecimalMath.UNIT;
      }

      if (exchangeGlobals.spotPrice < listing.strike) {
        // if amount > 0 can be skipped as it will be multiplied by 0
        totalUserLongProfitQuote = totalUserLongProfitQuote.add(
          listing.longPut.multiplyDecimal(listing.strike - exchangeGlobals.spotPrice)
        );
        totalAMMShortPutProfitQuote = totalAMMShortPutProfitQuote.add(
          (listing.strike - exchangeGlobals.spotPrice).multiplyDecimal(listing.shortPut)
        );
      }
    }

    shortCollateral.sendToLP(totalAMMShortCallProfitBase, totalAMMShortPutProfitQuote);

    // This will batch all base we want to convert to quote and sell it in one transaction
    liquidityPool.boardLiquidation(totalBoardLongPutCollateral, totalUserLongProfitQuote, totalBoardLongCallCollateral);

    emit BoardLiquidated(
      board.id,
      totalUserLongProfitQuote,
      totalBoardLongCallCollateral,
      totalBoardLongPutCollateral,
      totalAMMShortCallProfitBase,
      totalAMMShortPutProfitQuote
    );
  }

  /**
   * @dev Settles options for expired and liquidated listings. Also functions as the way to reclaim capital for options
   * sold to the market.
   *
   * @param listingId The id of the relevant OptionListing.
   */
  function settleOptions(uint listingId, TradeType tradeType) external override {
    uint amount = optionToken.balanceOf(msg.sender, listingId + uint(tradeType));

    shortCollateral.processSettle(
      listingId,
      msg.sender,
      tradeType,
      amount,
      optionListings[listingId].strike,
      boardToPriceAtExpiry[optionListings[listingId].boardId],
      listingToBaseReturnedRatio[listingId]
    );

    optionToken.burn(msg.sender, listingId + uint(tradeType), amount);
  }

  ////
  // Misc
  ////

  function _require(bool pass, Error error) internal view {
    require(pass, errorMessages[uint(error)]);
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner virtual {
    _require(owner == msg.sender, Error.OnlyOwner);
    _;
  }

  // Events
  /**
   * @dev Emitted when a Board is created.
   */
  event BoardCreated(uint indexed boardId, uint expiry, uint baseIv);

  /**
   * @dev Emitted when a Board frozen is updated.
   */
  event BoardFrozen(uint indexed boardId, bool frozen);

  /**
   * @dev Emitted when a Board new baseIv is set.
   */
  event BoardBaseIvSet(uint indexed boardId, uint baseIv);

  /**
   * @dev Emitted when a Listing new skew is set.
   */
  event ListingSkewSet(uint indexed listingId, uint skew);

  /**
   * @dev Emitted when a Listing is added to a board
   */
  event ListingAdded(uint indexed boardId, uint indexed listingId, uint strike, uint skew);

  /**
   * @dev Emitted when a Position is opened.
   */
  event PositionOpened(
    address indexed trader,
    uint indexed listingId,
    TradeType indexed tradeType,
    uint amount,
    uint totalCost
  );

  /**
   * @dev Emitted when a Position is closed.
   */
  event PositionClosed(
    address indexed trader,
    uint indexed listingId,
    TradeType indexed tradeType,
    uint amount,
    uint totalCost
  );

  /**
   * @dev Emitted when a Board is liquidated.
   */
  event BoardLiquidated(
    uint indexed boardId,
    uint totalUserLongProfitQuote,
    uint totalBoardLongCallCollateral,
    uint totalBoardLongPutCollateral,
    uint totalAMMShortCallProfitBase,
    uint totalAMMShortPutProfitQuote
  );

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
}

File 2 of 34 : SafeDecimalMath.sol
//SPDX-License-Identifier: MIT
//
//Copyright (c) 2019 Synthetix
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.

pragma solidity ^0.7.6;

// Libraries
import "@openzeppelin/contracts/math/SafeMath.sol";

// https://docs.synthetix.io/contracts/source/libraries/SafeDecimalMath/
library SafeDecimalMath {
  using SafeMath for uint;

  /* Number of decimal places in the representations. */
  uint8 public constant decimals = 18;
  uint8 public constant highPrecisionDecimals = 27;

  /* The number representing 1.0. */
  uint public constant UNIT = 10**uint(decimals);

  /* The number representing 1.0 for higher fidelity numbers. */
  uint public constant PRECISE_UNIT = 10**uint(highPrecisionDecimals);
  uint private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = 10**uint(highPrecisionDecimals - decimals);

  /**
   * @return Provides an interface to UNIT.
   */
  function unit() external pure returns (uint) {
    return UNIT;
  }

  /**
   * @return Provides an interface to PRECISE_UNIT.
   */
  function preciseUnit() external pure returns (uint) {
    return PRECISE_UNIT;
  }

  /**
   * @return The result of multiplying x and y, interpreting the operands as fixed-point
   * decimals.
   *
   * @dev A unit factor is divided out after the product of x and y is evaluated,
   * so that product must be less than 2**256. As this is an integer division,
   * the internal division always rounds down. This helps save on gas. Rounding
   * is more expensive on gas.
   */
  function multiplyDecimal(uint x, uint y) internal pure returns (uint) {
    /* Divide by UNIT to remove the extra factor introduced by the product. */
    return x.mul(y) / UNIT;
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of the specified precision unit.
   *
   * @dev The operands should be in the form of a the specified unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function _multiplyDecimalRound(
    uint x,
    uint y,
    uint precisionUnit
  ) private pure returns (uint) {
    /* Divide by UNIT to remove the extra factor introduced by the product. */
    uint quotientTimesTen = x.mul(y) / (precisionUnit / 10);

    if (quotientTimesTen % 10 >= 5) {
      quotientTimesTen += 10;
    }

    return quotientTimesTen / 10;
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of a precise unit.
   *
   * @dev The operands should be in the precise unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function multiplyDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
    return _multiplyDecimalRound(x, y, PRECISE_UNIT);
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of a standard unit.
   *
   * @dev The operands should be in the standard unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function multiplyDecimalRound(uint x, uint y) internal pure returns (uint) {
    return _multiplyDecimalRound(x, y, UNIT);
  }

  /**
   * @return The result of safely dividing x and y. The return value is a high
   * precision decimal.
   *
   * @dev y is divided after the product of x and the standard precision unit
   * is evaluated, so the product of x and UNIT must be less than 2**256. As
   * this is an integer division, the result is always rounded down.
   * This helps save on gas. Rounding is more expensive on gas.
   */
  function divideDecimal(uint x, uint y) internal pure returns (uint) {
    /* Reintroduce the UNIT factor that will be divided out by y. */
    return x.mul(UNIT).div(y);
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * decimal in the precision unit specified in the parameter.
   *
   * @dev y is divided after the product of x and the specified precision unit
   * is evaluated, so the product of x and the specified precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function _divideDecimalRound(
    uint x,
    uint y,
    uint precisionUnit
  ) private pure returns (uint) {
    uint resultTimesTen = x.mul(precisionUnit * 10).div(y);

    if (resultTimesTen % 10 >= 5) {
      resultTimesTen += 10;
    }

    return resultTimesTen / 10;
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * standard precision decimal.
   *
   * @dev y is divided after the product of x and the standard precision unit
   * is evaluated, so the product of x and the standard precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function divideDecimalRound(uint x, uint y) internal pure returns (uint) {
    return _divideDecimalRound(x, y, UNIT);
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * high precision decimal.
   *
   * @dev y is divided after the product of x and the high precision unit
   * is evaluated, so the product of x and the high precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function divideDecimalRoundPrecise(uint x, uint y) internal pure returns (uint) {
    return _divideDecimalRound(x, y, PRECISE_UNIT);
  }

  /**
   * @dev Convert a standard decimal representation to a high precision one.
   */
  function decimalToPreciseDecimal(uint i) internal pure returns (uint) {
    return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
  }

  /**
   * @dev Convert a high precision decimal to a standard decimal representation.
   */
  function preciseDecimalToDecimal(uint i) internal pure returns (uint) {
    uint quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);

    if (quotientTimesTen % 10 >= 5) {
      quotientTimesTen += 10;
    }

    return quotientTimesTen / 10;
  }
}

File 3 of 34 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @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);
}

File 4 of 34 : LyraGlobals.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Libraries
import "./synthetix/SafeDecimalMath.sol";

// Inherited
import "@openzeppelin/contracts/access/Ownable.sol";

// Interfaces
import "./interfaces/IExchanger.sol";
import "./interfaces/ICollateralShort.sol";
import "./interfaces/ISynthetix.sol";
import "./interfaces/IExchangeRates.sol";
import "./interfaces/ILiquidityPool.sol";

/**
 * @title LyraGlobals
 * @author Lyra
 * @dev Manages variables across all OptionMarkets, along with managing access to Synthetix.
 * Groups access to variables needed during a trade to reduce the gas costs associated with repetitive
 * inter-contract calls.
 * The OptionMarket contract address is used as the key to access the variables for the market.
 */
contract LyraGlobals is ILyraGlobals, Ownable {
  using SafeDecimalMath for uint;

  ISynthetix public override synthetix;
  IExchanger public override exchanger;
  IExchangeRates public override exchangeRates;
  ICollateralShort public override collateralShort;

  /// @dev Pause the whole system. Note; this will not pause settling previously expired options.
  bool public override isPaused = false;

  /// @dev Don't sell options this close to expiry
  mapping(address => uint) public override tradingCutoff;

  // Variables related to calculating premium/fees
  mapping(address => uint) public override optionPriceFeeCoefficient;
  mapping(address => uint) public override spotPriceFeeCoefficient;
  mapping(address => uint) public override vegaFeeCoefficient;
  mapping(address => uint) public override vegaNormFactor;
  mapping(address => uint) public override standardSize;
  mapping(address => uint) public override skewAdjustmentFactor;
  mapping(address => int) public override rateAndCarry;
  mapping(address => int) public override minDelta;
  mapping(address => uint) public override volatilityCutoff;
  mapping(address => bytes32) public override quoteKey;
  mapping(address => bytes32) public override baseKey;

  constructor() Ownable() {}

  /**
   * @dev Set the globals that apply to all OptionMarkets.
   *
   * @param _synthetix The address of Synthetix.
   * @param _exchanger The address of Synthetix's Exchanger.
   * @param _exchangeRates The address of Synthetix's ExchangeRates.
   * @param _collateralShort The address of Synthetix's CollateralShort.
   */
  function setGlobals(
    ISynthetix _synthetix,
    IExchanger _exchanger,
    IExchangeRates _exchangeRates,
    ICollateralShort _collateralShort
  ) external override onlyOwner {
    synthetix = _synthetix;
    exchanger = _exchanger;
    exchangeRates = _exchangeRates;
    collateralShort = _collateralShort;

    emit GlobalsSet(_synthetix, _exchanger, _exchangeRates, _collateralShort);
  }

  /**
   * @dev Set the globals for a specific OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _tradingCutoff The time to stop trading.
   * @param pricingGlobals The PricingGlobals.
   * @param _quoteKey The key of the quoteAsset.
   * @param _baseKey The key of the baseAsset.
   */
  function setGlobalsForContract(
    address _contractAddress,
    uint _tradingCutoff,
    PricingGlobals memory pricingGlobals,
    bytes32 _quoteKey,
    bytes32 _baseKey
  ) external override onlyOwner {
    setTradingCutoff(_contractAddress, _tradingCutoff);
    setOptionPriceFeeCoefficient(_contractAddress, pricingGlobals.optionPriceFeeCoefficient);
    setSpotPriceFeeCoefficient(_contractAddress, pricingGlobals.spotPriceFeeCoefficient);
    setVegaFeeCoefficient(_contractAddress, pricingGlobals.vegaFeeCoefficient);
    setVegaNormFactor(_contractAddress, pricingGlobals.vegaNormFactor);
    setStandardSize(_contractAddress, pricingGlobals.standardSize);
    setSkewAdjustmentFactor(_contractAddress, pricingGlobals.skewAdjustmentFactor);
    setRateAndCarry(_contractAddress, pricingGlobals.rateAndCarry);
    setMinDelta(_contractAddress, pricingGlobals.minDelta);
    setVolatilityCutoff(_contractAddress, pricingGlobals.volatilityCutoff);
    setQuoteKey(_contractAddress, _quoteKey);
    setBaseKey(_contractAddress, _baseKey);
  }

  /**
   * @dev Pauses the contract.
   *
   * @param _isPaused Whether getting globals will revert or not.
   */
  function setPaused(bool _isPaused) external override onlyOwner {
    isPaused = _isPaused;

    emit Paused(isPaused);
  }

  /**
   * @dev Set the time when the OptionMarket will cease trading before expiry.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _tradingCutoff The time to stop trading.
   */
  function setTradingCutoff(address _contractAddress, uint _tradingCutoff) public override onlyOwner {
    require(_tradingCutoff >= 6 hours && _tradingCutoff <= 14 days, "tradingCutoff value out of range");
    tradingCutoff[_contractAddress] = _tradingCutoff;
    emit TradingCutoffSet(_contractAddress, _tradingCutoff);
  }

  /**
   * @notice Set the option price fee coefficient for the OptionMarket.

   * @param _contractAddress The address of the OptionMarket.
   * @param _optionPriceFeeCoefficient The option price fee coefficient.
   */
  function setOptionPriceFeeCoefficient(address _contractAddress, uint _optionPriceFeeCoefficient)
    public
    override
    onlyOwner
  {
    require(_optionPriceFeeCoefficient <= 5e17, "optionPriceFeeCoefficient value out of range");
    optionPriceFeeCoefficient[_contractAddress] = _optionPriceFeeCoefficient;
    emit OptionPriceFeeCoefficientSet(_contractAddress, _optionPriceFeeCoefficient);
  }

  /**
   * @notice Set the spot price fee coefficient for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _spotPriceFeeCoefficient The spot price fee coefficient.
   */
  function setSpotPriceFeeCoefficient(address _contractAddress, uint _spotPriceFeeCoefficient)
    public
    override
    onlyOwner
  {
    require(_spotPriceFeeCoefficient <= 1e17, "optionPriceFeeCoefficient value out of range");
    spotPriceFeeCoefficient[_contractAddress] = _spotPriceFeeCoefficient;
    emit SpotPriceFeeCoefficientSet(_contractAddress, _spotPriceFeeCoefficient);
  }

  /**
   * @notice Set the vega fee coefficient for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _vegaFeeCoefficient The vega fee coefficient.
   */
  function setVegaFeeCoefficient(address _contractAddress, uint _vegaFeeCoefficient) public override onlyOwner {
    require(_vegaFeeCoefficient <= 100000e18, "optionPriceFeeCoefficient value out of range");
    vegaFeeCoefficient[_contractAddress] = _vegaFeeCoefficient;
    emit VegaFeeCoefficientSet(_contractAddress, _vegaFeeCoefficient);
  }

  /**
   * @notice Set the vega normalisation factor for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _vegaNormFactor The vega normalisation factor.
   */
  function setVegaNormFactor(address _contractAddress, uint _vegaNormFactor) public override onlyOwner {
    require(_vegaNormFactor <= 10e18, "optionPriceFeeCoefficient value out of range");
    vegaNormFactor[_contractAddress] = _vegaNormFactor;
    emit VegaNormFactorSet(_contractAddress, _vegaNormFactor);
  }

  /**
   * @notice Set the standard size for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _standardSize The size of an average trade.
   */
  function setStandardSize(address _contractAddress, uint _standardSize) public override onlyOwner {
    require(_standardSize >= 1e15 && _standardSize <= 100000e18, "standardSize value out of range");
    standardSize[_contractAddress] = _standardSize;
    emit StandardSizeSet(_contractAddress, _standardSize);
  }

  /**
   * @notice Set the skew adjustment factor for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _skewAdjustmentFactor The skew adjustment factor.
   */
  function setSkewAdjustmentFactor(address _contractAddress, uint _skewAdjustmentFactor) public override onlyOwner {
    require(_skewAdjustmentFactor <= 10e18, "skewAdjustmentFactor value out of range");
    skewAdjustmentFactor[_contractAddress] = _skewAdjustmentFactor;
    emit SkewAdjustmentFactorSet(_contractAddress, _skewAdjustmentFactor);
  }

  /**
   * @notice Set the rate for the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _rateAndCarry The rate.
   */
  function setRateAndCarry(address _contractAddress, int _rateAndCarry) public override onlyOwner {
    require(_rateAndCarry <= 3e18 && _rateAndCarry >= -3e18, "rateAndCarry value out of range");
    rateAndCarry[_contractAddress] = _rateAndCarry;
    emit RateAndCarrySet(_contractAddress, _rateAndCarry);
  }

  /**
   * @notice Set the minimum Delta that the OptionMarket will trade.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _minDelta The minimum delta value.
   */
  function setMinDelta(address _contractAddress, int _minDelta) public override onlyOwner {
    require(_minDelta >= 0 && _minDelta <= 2e17, "minDelta value out of range");
    minDelta[_contractAddress] = _minDelta;
    emit MinDeltaSet(_contractAddress, _minDelta);
  }

  /**
   * @notice Set the minimum volatility option that the OptionMarket will trade.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _volatilityCutoff The minimum volatility value.
   */
  function setVolatilityCutoff(address _contractAddress, uint _volatilityCutoff) public override onlyOwner {
    require(_volatilityCutoff <= 2e18, "volatilityCutoff value out of range");
    volatilityCutoff[_contractAddress] = _volatilityCutoff;
    emit VolatilityCutoffSet(_contractAddress, _volatilityCutoff);
  }

  /**
   * @notice Set the quoteKey of the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _quoteKey The key of the quoteAsset.
   */
  function setQuoteKey(address _contractAddress, bytes32 _quoteKey) public override onlyOwner {
    quoteKey[_contractAddress] = _quoteKey;
    emit QuoteKeySet(_contractAddress, _quoteKey);
  }

  /**
   * @notice Set the baseKey of the OptionMarket.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param _baseKey The key of the baseAsset.
   */
  function setBaseKey(address _contractAddress, bytes32 _baseKey) public override onlyOwner {
    baseKey[_contractAddress] = _baseKey;
    emit BaseKeySet(_contractAddress, _baseKey);
  }

  // Getters

  /**
   * @notice Returns the price of the baseAsset.
   *
   * @param _contractAddress The address of the OptionMarket.
   */
  function getSpotPriceForMarket(address _contractAddress) external view override returns (uint) {
    return getSpotPrice(baseKey[_contractAddress]);
  }

  /**
   * @notice Gets spot price of an asset.
   * @dev All rates are denominated in terms of sUSD,
   * so the price of sUSD is always $1.00, and is never stale.
   *
   * @param to The key of the synthetic asset.
   */
  function getSpotPrice(bytes32 to) public view override returns (uint) {
    (uint rate, bool invalid) = exchangeRates.rateAndInvalid(to);
    require(!invalid && rate != 0, "rate is invalid");
    return rate;
  }

  /**
   * @notice Returns a PricingGlobals struct for a given market address.
   *
   * @param _contractAddress The address of the OptionMarket.
   */
  function getPricingGlobals(address _contractAddress)
    external
    view
    override
    notPaused
    returns (PricingGlobals memory)
  {
    return
      PricingGlobals({
        optionPriceFeeCoefficient: optionPriceFeeCoefficient[_contractAddress],
        spotPriceFeeCoefficient: spotPriceFeeCoefficient[_contractAddress],
        vegaFeeCoefficient: vegaFeeCoefficient[_contractAddress],
        vegaNormFactor: vegaNormFactor[_contractAddress],
        standardSize: standardSize[_contractAddress],
        skewAdjustmentFactor: skewAdjustmentFactor[_contractAddress],
        rateAndCarry: rateAndCarry[_contractAddress],
        minDelta: minDelta[_contractAddress],
        volatilityCutoff: volatilityCutoff[_contractAddress],
        spotPrice: getSpotPrice(baseKey[_contractAddress])
      });
  }

  /**
   * @notice Returns the GreekCacheGlobals.
   *
   * @param _contractAddress The address of the OptionMarket.
   */
  function getGreekCacheGlobals(address _contractAddress)
    external
    view
    override
    notPaused
    returns (GreekCacheGlobals memory)
  {
    return
      GreekCacheGlobals({
        rateAndCarry: rateAndCarry[_contractAddress],
        spotPrice: getSpotPrice(baseKey[_contractAddress])
      });
  }

  /**
   * @notice Returns the ExchangeGlobals.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param exchangeType The ExchangeType.
   */
  function getExchangeGlobals(address _contractAddress, ExchangeType exchangeType)
    public
    view
    override
    notPaused
    returns (ExchangeGlobals memory exchangeGlobals)
  {
    exchangeGlobals = ExchangeGlobals({
      spotPrice: 0,
      quoteKey: quoteKey[_contractAddress],
      baseKey: baseKey[_contractAddress],
      synthetix: synthetix,
      short: collateralShort,
      quoteBaseFeeRate: 0,
      baseQuoteFeeRate: 0
    });

    exchangeGlobals.spotPrice = getSpotPrice(exchangeGlobals.baseKey);

    if (exchangeType == ExchangeType.BASE_QUOTE || exchangeType == ExchangeType.ALL) {
      exchangeGlobals.baseQuoteFeeRate = exchanger.feeRateForExchange(
        exchangeGlobals.baseKey,
        exchangeGlobals.quoteKey
      );
    }

    if (exchangeType == ExchangeType.QUOTE_BASE || exchangeType == ExchangeType.ALL) {
      exchangeGlobals.quoteBaseFeeRate = exchanger.feeRateForExchange(
        exchangeGlobals.quoteKey,
        exchangeGlobals.baseKey
      );
    }
  }

  /**
   * @dev Returns the globals needed to perform a trade.
   * The purpose of this function is to provide all the necessary variables in 1 call. Note that GreekCacheGlobals are a
   * subset of PricingGlobals, so we generate that struct when OptionMarketPricer calls OptionGreekCache.
   *
   * @param _contractAddress The address of the OptionMarket.
   * @param isBuy  Is the trade buying or selling options to the OptionMarket.
   */
  function getGlobalsForOptionTrade(address _contractAddress, bool isBuy)
    external
    view
    override
    notPaused
    returns (
      PricingGlobals memory pricingGlobals,
      ExchangeGlobals memory exchangeGlobals,
      uint tradeCutoff
    )
  {
    // exchangeGlobals aren't necessary apart from long calls, but since they are the most expensive transaction
    // we add this overhead to other types of calls, to save gas on long calls.
    exchangeGlobals = getExchangeGlobals(_contractAddress, isBuy ? ExchangeType.QUOTE_BASE : ExchangeType.BASE_QUOTE);
    pricingGlobals = PricingGlobals({
      optionPriceFeeCoefficient: optionPriceFeeCoefficient[_contractAddress],
      spotPriceFeeCoefficient: spotPriceFeeCoefficient[_contractAddress],
      vegaFeeCoefficient: vegaFeeCoefficient[_contractAddress],
      vegaNormFactor: vegaNormFactor[_contractAddress],
      standardSize: standardSize[_contractAddress],
      skewAdjustmentFactor: skewAdjustmentFactor[_contractAddress],
      rateAndCarry: rateAndCarry[_contractAddress],
      minDelta: minDelta[_contractAddress],
      volatilityCutoff: volatilityCutoff[_contractAddress],
      spotPrice: exchangeGlobals.spotPrice
    });
    tradeCutoff = tradingCutoff[_contractAddress];
  }

  modifier notPaused {
    require(!isPaused, "contracts are paused");
    _;
  }

  /** Emitted when globals are set.
   */
  event GlobalsSet(
    ISynthetix _synthetix,
    IExchanger _exchanger,
    IExchangeRates _exchangeRates,
    ICollateralShort _collateralShort
  );
  /**
   * @dev Emitted when paused.
   */
  event Paused(bool isPaused);
  /**
   * @dev Emitted when trading cut-off is set.
   */
  event TradingCutoffSet(address indexed _contractAddress, uint _tradingCutoff);
  /**
   * @dev Emitted when option price fee coefficient is set.
   */
  event OptionPriceFeeCoefficientSet(address indexed _contractAddress, uint _optionPriceFeeCoefficient);
  /**
   * @dev Emitted when spot price fee coefficient is set.
   */
  event SpotPriceFeeCoefficientSet(address indexed _contractAddress, uint _spotPriceFeeCoefficient);
  /**
   * @dev Emitted when vega fee coefficient is set.
   */
  event VegaFeeCoefficientSet(address indexed _contractAddress, uint _vegaFeeCoefficient);
  /**
   * @dev Emitted when standard size is set.
   */
  event StandardSizeSet(address indexed _contractAddress, uint _standardSize);
  /**
   * @dev Emitted when skew ddjustment factor is set.
   */
  event SkewAdjustmentFactorSet(address indexed _contractAddress, uint _skewAdjustmentFactor);
  /**
   * @dev Emitted when vegaNorm factor is set.
   */
  event VegaNormFactorSet(address indexed _contractAddress, uint _vegaNormFactor);
  /**
   * @dev Emitted when rate and carry is set.
   */
  event RateAndCarrySet(address indexed _contractAddress, int _rateAndCarry);
  /**
   * @dev Emitted when min delta is set.
   */
  event MinDeltaSet(address indexed _contractAddress, int _minDelta);
  /**
   * @dev Emitted when volatility cutoff is set.
   */
  event VolatilityCutoffSet(address indexed _contractAddress, uint _volatilityCutoff);
  /**
   * @dev Emitted when quote key is set.
   */
  event QuoteKeySet(address indexed _contractAddress, bytes32 _quoteKey);
  /**
   * @dev Emitted when base key is set.
   */
  event BaseKeySet(address indexed _contractAddress, bytes32 _baseKey);
}

File 5 of 34 : LiquidityPool.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Libraries
import "./synthetix/SafeDecimalMath.sol";

// Interfaces
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IOptionMarket.sol";
import "./interfaces/ILiquidityCertificate.sol";
import "./interfaces/IPoolHedger.sol";
import "./interfaces/IShortCollateral.sol";

/**
 * @title LiquidityPool
 * @author Lyra
 * @dev Holds funds from LPs, which are used for the following purposes:
 * 1. Collateralizing options sold by the OptionMarket.
 * 2. Buying options from users.
 * 3. Delta hedging the LPs.
 * 4. Storing funds for expired in the money options.
 */
contract LiquidityPool is ILiquidityPool {
  using SafeMath for uint;
  using SafeDecimalMath for uint;

  ////
  // Constants
  ////
  ILyraGlobals internal globals;
  IOptionMarket internal optionMarket;
  ILiquidityCertificate internal liquidityCertificate;
  IShortCollateral internal shortCollateral;
  IPoolHedger internal poolHedger;
  IERC20 internal quoteAsset;
  IERC20 internal baseAsset;
  uint internal constant INITIAL_RATE = 1e18;

  ////
  // Variables
  ////
  mapping(uint => string) internal errorMessages;

  bool internal initialized = false;

  /// @dev Amount of collateral locked for outstanding calls and puts sold to users
  Collateral public override lockedCollateral;
  /**
   * @dev Total amount of quoteAsset held to pay out users who have locked/waited for their tokens to be burnable. As
   * well as keeping track of all settled option's usd value.
   */
  uint internal totalQuoteAmountReserved;
  /// @dev Total number of tokens that will be removed from the totalTokenSupply at the end of the round.
  uint internal tokensBurnableForRound;
  /// @dev Funds entering the pool in the next round.
  uint public override queuedQuoteFunds;
  /// @dev Total amount of tokens that represents the total amount of pool shares
  uint internal totalTokenSupply;
  /// @dev Counter for reentrancy guard.
  uint internal counter = 1;

  /**
   * @dev Mapping of timestamps to conversion rates of liquidity to tokens. To get the token value of a certificate;
   * `certificate.liquidity / expiryToTokenValue[certificate.enteredAt]`
   */
  mapping(uint => uint) public override expiryToTokenValue;

  constructor() {}

  /**
   * @dev Initialize the contract.
   *
   * @param _optionMarket OptionMarket address
   * @param _liquidityCertificate LiquidityCertificate address
   * @param _quoteAsset Quote Asset address
   * @param _poolHedger PoolHedger address
   */
  function init(
    ILyraGlobals _globals,
    IOptionMarket _optionMarket,
    ILiquidityCertificate _liquidityCertificate,
    IPoolHedger _poolHedger,
    IShortCollateral _shortCollateral,
    IERC20 _quoteAsset,
    IERC20 _baseAsset,
    string[] memory _errorMessages
  ) external {
    require(!initialized, "already initialized");
    globals = _globals;
    optionMarket = _optionMarket;
    liquidityCertificate = _liquidityCertificate;
    shortCollateral = _shortCollateral;
    poolHedger = _poolHedger;
    quoteAsset = _quoteAsset;
    baseAsset = _baseAsset;
    require(_errorMessages.length == uint(Error.Last), "error msg count");
    for (uint i = 0; i < _errorMessages.length; i++) {
      errorMessages[i] = _errorMessages[i];
    }
    initialized = true;
  }

  ////////////////////////////////////////////////////////////////
  // Dealing with providing liquidity and withdrawing liquidity //
  ////////////////////////////////////////////////////////////////

  /**
   * @dev Deposits liquidity to the pool. This assumes users have authorised access to the quote ERC20 token. Will add
   * any deposited amount to the queuedQuoteFunds until the next round begins.
   *
   * @param beneficiary The account that will receive the liquidity certificate.
   * @param amount The amount of quoteAsset to deposit.
   */
  function deposit(address beneficiary, uint amount) external override returns (uint) {
    // Assume we have the allowance to take the amount they are depositing
    queuedQuoteFunds = queuedQuoteFunds.add(amount);
    uint certificateId = liquidityCertificate.mint(beneficiary, amount, optionMarket.maxExpiryTimestamp());
    emit Deposit(beneficiary, certificateId, amount);
    _require(quoteAsset.transferFrom(msg.sender, address(this), amount), Error.QuoteTransferFailed);
    return certificateId;
  }

  /**
   * @notice Signals withdraw of liquidity from the pool.
   * @dev It is not possible to withdraw during a round, thus a user can signal to withdraw at the time the round ends.
   *
   * @param certificateId The id of the LiquidityCertificate.
   */
  function signalWithdrawal(uint certificateId) external override {
    ILiquidityCertificate.CertificateData memory certificateData = liquidityCertificate.certificateData(certificateId);
    uint maxExpiryTimestamp = optionMarket.maxExpiryTimestamp();

    _require(certificateData.burnableAt == 0, Error.AlreadySignalledWithdrawal);
    _require(
      certificateData.enteredAt != maxExpiryTimestamp && expiryToTokenValue[maxExpiryTimestamp] == 0,
      Error.SignallingBetweenRounds
    );

    if (certificateData.enteredAt == 0) {
      // Dividing by INITIAL_RATE is redundant as initial rate is 1 unit
      tokensBurnableForRound = tokensBurnableForRound.add(certificateData.liquidity);
    } else {
      tokensBurnableForRound = tokensBurnableForRound.add(
        certificateData.liquidity.divideDecimal(expiryToTokenValue[certificateData.enteredAt])
      );
    }

    liquidityCertificate.setBurnableAt(msg.sender, certificateId, maxExpiryTimestamp);

    emit WithdrawSignaled(certificateId, tokensBurnableForRound);
  }

  /**
   * @dev Undo a previously signalled withdraw. Certificate owner must have signalled withdraw to call this function,
   * and cannot unsignal if the token is already burnable or burnt.
   *
   * @param certificateId The id of the LiquidityCertificate.
   */
  function unSignalWithdrawal(uint certificateId) external override {
    ILiquidityCertificate.CertificateData memory certificateData = liquidityCertificate.certificateData(certificateId);

    // Cannot unsignal withdrawal if the token is burnable/hasn't signalled exit
    _require(certificateData.burnableAt != 0, Error.UnSignalMustSignalFirst);
    _require(expiryToTokenValue[certificateData.burnableAt] == 0, Error.UnSignalAlreadyBurnable);

    liquidityCertificate.setBurnableAt(msg.sender, certificateId, 0);

    if (certificateData.enteredAt == 0) {
      // Dividing by INITIAL_RATE is redundant as initial rate is 1 unit
      tokensBurnableForRound = tokensBurnableForRound.sub(certificateData.liquidity);
    } else {
      tokensBurnableForRound = tokensBurnableForRound.sub(
        certificateData.liquidity.divideDecimal(expiryToTokenValue[certificateData.enteredAt])
      );
    }

    emit WithdrawUnSignaled(certificateId, tokensBurnableForRound);
  }

  /**
   * @dev Withdraws liquidity from the pool.
   *
   * This requires tokens to have been locked until the round ending at the burnableAt timestamp has been ended.
   * This will burn the liquidityCertificates and have the quote asset equivalent at the time be reserved for the users.
   *
   * @param beneficiary The account that will receive the withdrawn funds.
   * @param certificateId The id of the LiquidityCertificate.
   */
  function withdraw(address beneficiary, uint certificateId) external override returns (uint value) {
    ILiquidityCertificate.CertificateData memory certificateData = liquidityCertificate.certificateData(certificateId);
    uint maxExpiryTimestamp = optionMarket.maxExpiryTimestamp();

    // We allow people to withdraw if their funds haven't entered the system
    if (certificateData.enteredAt == maxExpiryTimestamp) {
      queuedQuoteFunds = queuedQuoteFunds.sub(certificateData.liquidity);
      liquidityCertificate.burn(msg.sender, certificateId);
      emit Withdraw(beneficiary, certificateId, certificateData.liquidity, totalQuoteAmountReserved);
      _require(quoteAsset.transfer(beneficiary, certificateData.liquidity), Error.QuoteTransferFailed);
      return certificateData.liquidity;
    }

    uint enterValue = certificateData.enteredAt == 0 ? INITIAL_RATE : expiryToTokenValue[certificateData.enteredAt];

    // expiryToTokenValue will only be set if the previous round has ended, and the next has not started
    uint currentRoundValue = expiryToTokenValue[maxExpiryTimestamp];

    // If they haven't signaled withdrawal, and it is between rounds
    if (certificateData.burnableAt == 0 && currentRoundValue != 0) {
      uint tokenAmt = certificateData.liquidity.divideDecimal(enterValue);
      totalTokenSupply = totalTokenSupply.sub(tokenAmt);
      value = tokenAmt.multiplyDecimal(currentRoundValue);
      liquidityCertificate.burn(msg.sender, certificateId);
      emit Withdraw(beneficiary, certificateId, value, totalQuoteAmountReserved);
      _require(quoteAsset.transfer(beneficiary, value), Error.QuoteTransferFailed);
      return value;
    }

    uint exitValue = expiryToTokenValue[certificateData.burnableAt];

    _require(certificateData.burnableAt != 0 && exitValue != 0, Error.WithdrawNotBurnable);

    value = certificateData.liquidity.multiplyDecimal(exitValue).divideDecimal(enterValue);

    // We can allow a 0 expiry for options created before any boards exist
    liquidityCertificate.burn(msg.sender, certificateId);

    totalQuoteAmountReserved = totalQuoteAmountReserved.sub(value);
    emit Withdraw(beneficiary, certificateId, value, totalQuoteAmountReserved);
    _require(quoteAsset.transfer(beneficiary, value), Error.QuoteTransferFailed);
    return value;
  }

  //////////////////////////////////////////////
  // Dealing with locking and expiry rollover //
  //////////////////////////////////////////////

  /**
   * @dev Return Token value.
   *
   * This token price is only accurate within the period between rounds.
   */
  function tokenPriceQuote() public view override returns (uint) {
    ILyraGlobals.ExchangeGlobals memory exchangeGlobals =
      globals.getExchangeGlobals(address(optionMarket), ILyraGlobals.ExchangeType.ALL);

    if (totalTokenSupply == 0) {
      return INITIAL_RATE;
    }

    uint poolValue =
      getTotalPoolValueQuote(
        exchangeGlobals.spotPrice,
        poolHedger.getValueQuote(exchangeGlobals.short, exchangeGlobals.spotPrice)
      );
    return poolValue.divideDecimal(totalTokenSupply);
  }

  /**
   * @notice Ends a round.
   * @dev Should only be called after all boards have been liquidated.
   */
  function endRound() external override {
    // Round can only be ended if all boards have been liquidated, and can only be called once.
    uint maxExpiryTimestamp = optionMarket.maxExpiryTimestamp();
    // We must ensure all boards have been expired
    _require(optionMarket.getLiveBoards().length == 0, Error.EndRoundWithLiveBoards);
    // We can only end the round once
    _require(expiryToTokenValue[maxExpiryTimestamp] == 0, Error.EndRoundAlreadyEnded);
    // We want to make sure all base collateral has been exchanged
    _require(baseAsset.balanceOf(address(this)) == 0, Error.EndRoundMustExchangeBase);
    // We want to make sure there is no outstanding poolHedger balance. If there is collateral left in the poolHedger
    // it will not affect calculations.
    _require(poolHedger.getCurrentHedgedNetDelta() == 0, Error.EndRoundMustHedgeDelta);

    uint pricePerToken = tokenPriceQuote();

    // Store the value for the tokens that are burnable for this round
    expiryToTokenValue[maxExpiryTimestamp] = pricePerToken;

    // Reserve the amount of quote we need for the tokens that are burnable
    totalQuoteAmountReserved = totalQuoteAmountReserved.add(tokensBurnableForRound.multiplyDecimal(pricePerToken));
    emit QuoteReserved(tokensBurnableForRound.multiplyDecimal(pricePerToken), totalQuoteAmountReserved);

    totalTokenSupply = totalTokenSupply.sub(tokensBurnableForRound);
    emit RoundEnded(maxExpiryTimestamp, pricePerToken, totalQuoteAmountReserved, tokensBurnableForRound);
    tokensBurnableForRound = 0;
  }

  /**
   * @dev Starts a round. Can only be called by optionMarket contract when adding a board.
   *
   * @param lastMaxExpiryTimestamp The time at which the previous round ended.
   * @param newMaxExpiryTimestamp The time which funds will be locked until.
   */
  function startRound(uint lastMaxExpiryTimestamp, uint newMaxExpiryTimestamp) external override onlyOptionMarket {
    // As the value is never reset, this is when the first board is added
    if (lastMaxExpiryTimestamp == 0) {
      totalTokenSupply = queuedQuoteFunds;
    } else {
      _require(expiryToTokenValue[lastMaxExpiryTimestamp] != 0, Error.StartRoundMustEndRound);
      totalTokenSupply = totalTokenSupply.add(
        queuedQuoteFunds.divideDecimal(expiryToTokenValue[lastMaxExpiryTimestamp])
      );
    }
    queuedQuoteFunds = 0;

    emit RoundStarted(
      lastMaxExpiryTimestamp,
      newMaxExpiryTimestamp,
      totalTokenSupply,
      lastMaxExpiryTimestamp == 0 ? SafeDecimalMath.UNIT : expiryToTokenValue[lastMaxExpiryTimestamp]
    );
  }

  /////////////////////////////////////////
  // Dealing with collateral for options //
  /////////////////////////////////////////

  /**
   * @dev external override function that will bring the base balance of this contract to match locked.base. This cannot be done
   * in the same transaction as locking the base, as exchanging on synthetix is too costly gas-wise.
   */
  function exchangeBase() external override reentrancyGuard {
    uint currentBaseBalance = baseAsset.balanceOf(address(this));

    // Add this additional check to prevent any soft locks at round end, as the base balance must be 0 to end the round.
    if (optionMarket.getLiveBoards().length == 0) {
      lockedCollateral.base = 0;
    }

    if (currentBaseBalance > lockedCollateral.base) {
      // Sell excess baseAsset
      ILyraGlobals.ExchangeGlobals memory exchangeGlobals =
        globals.getExchangeGlobals(address(optionMarket), ILyraGlobals.ExchangeType.BASE_QUOTE);
      uint amount = currentBaseBalance - lockedCollateral.base;
      uint quoteReceived =
        exchangeGlobals.synthetix.exchange(exchangeGlobals.baseKey, amount, exchangeGlobals.quoteKey);
      _require(quoteReceived > 0, Error.ReceivedZeroFromBaseQuoteExchange);
      emit BaseSold(msg.sender, amount, quoteReceived);
    } else if (lockedCollateral.base > currentBaseBalance) {
      // Buy required amount of baseAsset
      ILyraGlobals.ExchangeGlobals memory exchangeGlobals =
        globals.getExchangeGlobals(address(optionMarket), ILyraGlobals.ExchangeType.QUOTE_BASE);
      uint quoteToSpend =
        (lockedCollateral.base - currentBaseBalance)
          .divideDecimalRound(SafeDecimalMath.UNIT.sub(exchangeGlobals.quoteBaseFeeRate))
          .multiplyDecimalRound(exchangeGlobals.spotPrice);
      uint totalQuoteAvailable =
        quoteAsset.balanceOf(address(this)).sub(totalQuoteAmountReserved).sub(lockedCollateral.quote).sub(
          queuedQuoteFunds
        );
      // We want to always buy as much collateral as we can, even if it dips into the delta hedging portion.
      // But we cannot compromise funds that aren't useable by the pool.
      quoteToSpend = quoteToSpend > totalQuoteAvailable ? totalQuoteAvailable : quoteToSpend;
      uint amtReceived =
        exchangeGlobals.synthetix.exchange(exchangeGlobals.quoteKey, quoteToSpend, exchangeGlobals.baseKey);
      _require(amtReceived > 0, Error.ReceivedZeroFromQuoteBaseExchange);
      emit BasePurchased(msg.sender, quoteToSpend, amtReceived);
    }
  }

  /**
   * @notice Locks quote when the system sells a put option.
   *
   * @param amount The amount of quote to lock.
   * @param freeCollatLiq The amount of free collateral that can be locked.
   */
  function lockQuote(uint amount, uint freeCollatLiq) external override onlyOptionMarket {
    _require(amount <= freeCollatLiq, Error.LockingMoreQuoteThanIsFree);
    lockedCollateral.quote = lockedCollateral.quote.add(amount);
    emit QuoteLocked(amount, lockedCollateral.quote);
  }

  /**
   * @notice Purchases and locks base when the system sells a call option.
   *
   * @param amount The amount of baseAsset to purchase and lock.
   * @param exchangeGlobals The exchangeGlobals.
   * @param liquidity Free and used liquidity amounts.
   */
  function lockBase(
    uint amount,
    ILyraGlobals.ExchangeGlobals memory exchangeGlobals,
    Liquidity memory liquidity
  ) external override onlyOptionMarket {
    uint currentBaseBal = baseAsset.balanceOf(address(this));

    uint desiredBase;
    uint availableQuote = liquidity.freeCollatLiquidity;

    if (lockedCollateral.base >= currentBaseBal) {
      uint outstanding = lockedCollateral.base - currentBaseBal;
      // We need to ignore any base we haven't purchased yet from our availableQuote
      availableQuote = availableQuote.add(outstanding.multiplyDecimal(exchangeGlobals.spotPrice));
      // But we want to make sure we will have enough quote to cover the debt owed on top of new base we want to lock
      desiredBase = amount.add(outstanding);
    } else {
      // We actually need to buy less, or none, if we already have excess balance
      uint excess = currentBaseBal - lockedCollateral.base;
      if (excess >= amount) {
        desiredBase = 0;
      } else {
        desiredBase = amount.sub(excess);
      }
    }
    uint quoteToSpend =
      desiredBase.divideDecimalRound(SafeDecimalMath.UNIT.sub(exchangeGlobals.quoteBaseFeeRate)).multiplyDecimalRound(
        exchangeGlobals.spotPrice
      );

    _require(availableQuote >= quoteToSpend, Error.LockingMoreBaseThanCanBeExchanged);

    lockedCollateral.base = lockedCollateral.base.add(amount);
    emit BaseLocked(amount, lockedCollateral.base);
  }

  /**
   * @notice Frees quote when the system buys back a put from the user.
   *
   * @param amount The amount of quote to free.
   */
  function freeQuoteCollateral(uint amount) external override onlyOptionMarket {
    _freeQuoteCollateral(amount);
  }

  /**
   * @notice Frees quote when the system buys back a put from the user.
   *
   * @param amount The amount of quote to free.
   */
  function _freeQuoteCollateral(uint amount) internal {
    // Handle rounding errors by returning the full amount when the requested amount is greater
    if (amount > lockedCollateral.quote) {
      amount = lockedCollateral.quote;
    }
    lockedCollateral.quote = lockedCollateral.quote.sub(amount);
    emit QuoteFreed(amount, lockedCollateral.quote);
  }

  /**
   * @notice Sells base and frees the proceeds of the sale.
   *
   * @param amountBase The amount of base to sell.
   */
  function freeBase(uint amountBase) external override onlyOptionMarket {
    _require(amountBase <= lockedCollateral.base, Error.FreeingMoreBaseThanLocked);
    lockedCollateral.base = lockedCollateral.base.sub(amountBase);
    emit BaseFreed(amountBase, lockedCollateral.base);
  }

  /**
   * @notice Sends the premium to a user who is selling an option to the pool.
   * @dev The caller must be the OptionMarket.
   *
   * @param recipient The address of the recipient.
   * @param amount The amount to transfer.
   * @param freeCollatLiq The amount of free collateral liquidity.
   */
  function sendPremium(
    address recipient,
    uint amount,
    uint freeCollatLiq
  ) external override onlyOptionMarket reentrancyGuard {
    _require(freeCollatLiq >= amount, Error.SendPremiumNotEnoughCollateral);
    _require(quoteAsset.transfer(recipient, amount), Error.QuoteTransferFailed);

    emit CollateralQuoteTransferred(recipient, amount);
  }

  //////////////////////////////////////////
  // Dealing with expired option premiums //
  //////////////////////////////////////////

  /**
   * @notice Manages collateral at the time of board liquidation, also converting base sent here from the OptionMarket.
   *
   * @param amountQuoteFreed Total amount of base to convert to quote, including profits from short calls.
   * @param amountQuoteReserved Total amount of base to convert to quote, including profits from short calls.
   * @param amountBaseFreed Total amount of collateral to liquidate.
   */
  function boardLiquidation(
    uint amountQuoteFreed,
    uint amountQuoteReserved,
    uint amountBaseFreed
  ) external override onlyOptionMarket {
    _freeQuoteCollateral(amountQuoteFreed);

    totalQuoteAmountReserved = totalQuoteAmountReserved.add(amountQuoteReserved);
    emit QuoteReserved(amountQuoteReserved, totalQuoteAmountReserved);

    lockedCollateral.base = lockedCollateral.base.sub(amountBaseFreed);
    emit BaseFreed(amountBaseFreed, lockedCollateral.base);
  }

  /**
   * @dev Transfers reserved quote. Sends `amount` of reserved quoteAsset to `user`.
   *
   * Requirements:
   *
   * - the caller must be `OptionMarket`.
   *
   * @param user The address of the user to send the quote.
   * @param amount The amount of quote to send.
   */
  function sendReservedQuote(address user, uint amount) external override onlyShortCollateral reentrancyGuard {
    // Should never happen, but added to prevent any potential rounding errors
    if (amount > totalQuoteAmountReserved) {
      amount = totalQuoteAmountReserved;
    }
    totalQuoteAmountReserved = totalQuoteAmountReserved.sub(amount);
    _require(quoteAsset.transfer(user, amount), Error.QuoteTransferFailed);

    emit ReservedQuoteSent(user, amount, totalQuoteAmountReserved);
  }

  ////////////////////////////
  // Getting Pool Liquidity //
  ////////////////////////////

  /**
   * @notice Returns the total pool value in quoteAsset.
   *
   * @param basePrice The price of the baseAsset.
   * @param usedDeltaLiquidity The amout of delta liquidity that has been used for hedging.
   */
  function getTotalPoolValueQuote(uint basePrice, uint usedDeltaLiquidity) public view override returns (uint) {
    return
      quoteAsset
        .balanceOf(address(this))
        .add(baseAsset.balanceOf(address(this)).multiplyDecimal(basePrice))
        .add(usedDeltaLiquidity)
        .sub(totalQuoteAmountReserved)
        .sub(queuedQuoteFunds);
  }

  /**
   * @notice Returns the used and free amounts for collateral and delta liquidity.
   *
   * @param basePrice The price of the base asset.
   * @param short The address of the short contract.
   */
  function getLiquidity(uint basePrice, ICollateralShort short) public view override returns (Liquidity memory) {
    Liquidity memory liquidity;

    liquidity.usedDeltaLiquidity = poolHedger.getValueQuote(short, basePrice);
    liquidity.usedCollatLiquidity = lockedCollateral.quote.add(lockedCollateral.base.multiplyDecimal(basePrice));

    uint totalLiquidity = getTotalPoolValueQuote(basePrice, liquidity.usedDeltaLiquidity);

    uint collatPortion = (totalLiquidity * 2) / 3;
    uint deltaPortion = totalLiquidity.sub(collatPortion);

    if (liquidity.usedCollatLiquidity > collatPortion) {
      collatPortion = liquidity.usedCollatLiquidity;
      deltaPortion = totalLiquidity.sub(collatPortion);
    } else if (liquidity.usedDeltaLiquidity > deltaPortion) {
      deltaPortion = liquidity.usedDeltaLiquidity;
      collatPortion = totalLiquidity.sub(deltaPortion);
    }

    liquidity.freeDeltaLiquidity = deltaPortion.sub(liquidity.usedDeltaLiquidity);
    liquidity.freeCollatLiquidity = collatPortion.sub(liquidity.usedCollatLiquidity);

    return liquidity;
  }

  //////////
  // Misc //
  //////////

  /**
   * @notice Sends quoteAsset to the PoolHedger.
   * @dev This function will transfer whatever free delta liquidity is available.
   * The hedger must determine what to do with the amount received.
   *
   * @param exchangeGlobals The exchangeGlobals.
   * @param amount The amount requested by the PoolHedger.
   */
  function transferQuoteToHedge(ILyraGlobals.ExchangeGlobals memory exchangeGlobals, uint amount)
    external
    override
    onlyPoolHedger
    reentrancyGuard
    returns (uint)
  {
    Liquidity memory liquidity = getLiquidity(exchangeGlobals.spotPrice, exchangeGlobals.short);

    uint available = liquidity.freeDeltaLiquidity;
    if (available < amount) {
      amount = available;
    }
    _require(quoteAsset.transfer(address(poolHedger), amount), Error.QuoteTransferFailed);

    emit DeltaQuoteTransferredToPoolHedger(amount);

    return amount;
  }

  function _require(bool pass, Error error) internal view {
    require(pass, errorMessages[uint(error)]);
  }

  ///////////////
  // Modifiers //
  ///////////////

  modifier onlyPoolHedger virtual {
    _require(msg.sender == address(poolHedger), Error.OnlyPoolHedger);
    _;
  }

  modifier onlyOptionMarket virtual {
    _require(msg.sender == address(optionMarket), Error.OnlyOptionMarket);
    _;
  }

  modifier onlyShortCollateral virtual {
    _require(msg.sender == address(shortCollateral), Error.OnlyShortCollateral);
    _;
  }

  modifier reentrancyGuard virtual {
    counter = counter.add(1); // counter adds 1 to the existing 1 so becomes 2
    uint guard = counter; // assigns 2 to the "guard" variable
    _;
    _require(guard == counter, Error.ReentrancyDetected);
  }

  /**
   * @dev Emitted when liquidity is deposited.
   */
  event Deposit(address indexed beneficiary, uint indexed certificateId, uint amount);
  /**
   * @dev Emitted when withdrawal is signaled.
   */
  event WithdrawSignaled(uint indexed certificateId, uint tokensBurnableForRound);
  /**
   * @dev Emitted when a withdrawal is unsignaled.
   */
  event WithdrawUnSignaled(uint indexed certificateId, uint tokensBurnableForRound);
  /**
   * @dev Emitted when liquidity is withdrawn.
   */
  event Withdraw(address indexed beneficiary, uint indexed certificateId, uint value, uint totalQuoteAmountReserved);
  /**
   * @dev Emitted when a round ends.
   */
  event RoundEnded(
    uint indexed maxExpiryTimestamp,
    uint pricePerToken,
    uint totalQuoteAmountReserved,
    uint tokensBurnableForRound
  );
  /**
   * @dev Emitted when a round starts.
   */
  event RoundStarted(
    uint indexed lastMaxExpiryTimestamp,
    uint indexed newMaxExpiryTimestamp,
    uint totalTokenSupply,
    uint tokenValue
  );
  /**
   * @dev Emitted when quote is locked.
   */
  event QuoteLocked(uint quoteLocked, uint lockedCollateralQuote);
  /**
   * @dev Emitted when base is locked.
   */
  event BaseLocked(uint baseLocked, uint lockedCollateralBase);
  /**
   * @dev Emitted when quote is freed.
   */
  event QuoteFreed(uint quoteFreed, uint lockedCollateralQuote);
  /**
   * @dev Emitted when base is freed.
   */
  event BaseFreed(uint baseFreed, uint lockedCollateralBase);
  /**
   * @dev Emitted when base is purchased.
   */
  event BasePurchased(address indexed caller, uint quoteSpent, uint amountPurchased);
  /**
   * @dev Emitted when base is sold.
   */
  event BaseSold(address indexed caller, uint amountSold, uint quoteReceived);
  /**
   * @dev Emitted when collateral is liquidated. This combines LP profit from short calls and freeing base collateral
   */
  event CollateralLiquidated(
    uint totalAmountToLiquidate,
    uint baseFreed,
    uint quoteReceived,
    uint lockedCollateralBase
  );
  /**
   * @dev Emitted when quote is reserved.
   */
  event QuoteReserved(uint amountQuoteReserved, uint totalQuoteAmountReserved);
  /**
   * @dev Emitted when reserved quote is sent.
   */
  event ReservedQuoteSent(address indexed user, uint amount, uint totalQuoteAmountReserved);
  /**
   * @dev Emitted when collatQuote is transferred.
   */
  event CollateralQuoteTransferred(address indexed recipient, uint amount);
  /**
   * @dev Emitted when quote is transferred to hedge.
   */
  event DeltaQuoteTransferredToPoolHedger(uint amount);
}

File 6 of 34 : OptionToken.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Inherited
import "./openzeppelin-l2/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IOptionToken.sol";

/**
 * @title OptionToken
 * @author Lyra
 * @dev Provides a tokenised representation of each OptionListing offered
 * by the OptionMarket.
 */
contract OptionToken is IOptionToken, ERC1155, Ownable {
  bool internal initialized = false;
  address internal optionMarket;

  constructor(string memory uri_) ERC1155(uri_) Ownable() {}

  /**
   * @dev Initialise the contract.
   * @param _optionMarket The OptionMarket contract address.
   */
  function init(address _optionMarket) external {
    require(!initialized, "contract already initialized");
    optionMarket = _optionMarket;
    initialized = true;
  }

  /**
   * @dev Initialise the contract.
   * @param newURI The new uri definition for the contract.
   */
  function setURI(string memory newURI) external override onlyOwner {
    _setURI(newURI);
  }

  /**
   * @dev Initialise the contract.
   *
   * @param account The owner of the tokens.
   * @param id The listingId + tradeType of the option.
   * @param amount The amount of options.
   */
  function mint(
    address account,
    uint id,
    uint amount
  ) external override onlyOptionMarket {
    bytes memory data;
    _mint(account, id, amount, data);
  }

  /**
   * @dev Burn the specified amount of token for the account.
   *
   * @param account The owner of the tokens.
   * @param id The listingId + tradeType of the option.
   * @param amount The amount of options.
   */
  function burn(
    address account,
    uint id,
    uint amount
  ) external override onlyOptionMarket {
    _burn(account, id, amount);
  }

  modifier onlyOptionMarket virtual {
    require(msg.sender == address(optionMarket), "only OptionMarket");
    _;
  }
}

File 7 of 34 : OptionGreekCache.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Libraries
import "./synthetix/SafeDecimalMath.sol";
import "./synthetix/SignedSafeDecimalMath.sol";

// Inherited
import "@openzeppelin/contracts/access/Ownable.sol";

// Interfaces
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IBlackScholes.sol";
import "./interfaces/ILyraGlobals.sol";
import "./interfaces/IOptionMarket.sol";
import "./interfaces/IOptionMarketPricer.sol";
import "./interfaces/IOptionGreekCache.sol";

/**
 * @title OptionGreekCache
 * @author Lyra
 * @dev Aggregates the netDelta and netStdVega of the OptionMarket by iterating over current listings.
 * Needs to be called by an external override actor as it's not feasible to do all the computation during the trade flow and
 * because delta/vega change over time and with movements in asset price and volatility.
 */
contract OptionGreekCache is IOptionGreekCache, Ownable {
  using SafeMath for uint;
  using SafeDecimalMath for uint;
  using SignedSafeMath for int;
  using SignedSafeDecimalMath for int;

  ILyraGlobals internal globals;
  IOptionMarket internal optionMarket;
  IOptionMarketPricer internal optionPricer;
  IBlackScholes internal blackScholes;

  // Limit due to gas constraints when updating
  uint public constant override MAX_LISTINGS_PER_BOARD = 10;

  // For calculating if the cache is stale based on spot price
  // These values can be quite wide as per listing updates occur whenever a trade does.
  uint public override staleUpdateDuration = 2 days;
  uint public override priceScalingPeriod = 7 days;
  uint public override maxAcceptablePercent = (1e18 / 100) * 20; // 20%
  uint public override minAcceptablePercent = (1e18 / 100) * 10; // 10%

  bool internal initialized;

  uint[] public override liveBoards; // Should be a clone of OptionMarket.liveBoards
  mapping(uint => OptionListingCache) public override listingCaches;
  mapping(uint => OptionBoardCache) public override boardCaches;
  GlobalCache public override globalCache;

  constructor() Ownable() {}

  /**
   * @dev Initialize the contract.
   *
   * @param _globals LyraGlobals address
   * @param _optionMarket OptionMarket address
   * @param _optionPricer OptionMarketPricer address
   */
  function init(
    ILyraGlobals _globals,
    IOptionMarket _optionMarket,
    IOptionMarketPricer _optionPricer,
    IBlackScholes _blackScholes
  ) external {
    require(!initialized, "Contract already initialized");
    globals = _globals;
    optionMarket = _optionMarket;
    optionPricer = _optionPricer;
    blackScholes = _blackScholes;
    initialized = true;
  }

  function setStaleCacheParameters(
    uint _staleUpdateDuration,
    uint _priceScalingPeriod,
    uint _maxAcceptablePercent,
    uint _minAcceptablePercent
  ) external override onlyOwner {
    require(_staleUpdateDuration >= 2 hours, "staleUpdateDuration too low");
    require(_maxAcceptablePercent >= _minAcceptablePercent, "maxAcceptablePercent must be >= min");
    require(_minAcceptablePercent >= (1e18 / 100) * 1, "minAcceptablePercent too low");
    // Note: this value can be zero even though it is in the divisor as timeToExpiry must be < priceScalingPeriod for it
    // to be used.
    priceScalingPeriod = _priceScalingPeriod;
    minAcceptablePercent = _minAcceptablePercent;
    maxAcceptablePercent = _maxAcceptablePercent;
    staleUpdateDuration = _staleUpdateDuration;

    emit StaleCacheParametersUpdated(
      priceScalingPeriod,
      minAcceptablePercent,
      maxAcceptablePercent,
      staleUpdateDuration
    );
  }

  ////
  // Add/Remove boards
  ////

  /**
   * @notice Adds a new OptionBoardCache.
   * @dev Called by the OptionMarket when an OptionBoard is added.
   *
   * @param boardId The id of the OptionBoard.
   */
  function addBoard(uint boardId) external override onlyOptionMarket {
    // Load in board from OptionMarket, adding net positions to global count
    (, uint expiry, uint iv, ) = optionMarket.optionBoards(boardId);
    uint[] memory listings = optionMarket.getBoardListings(boardId);

    require(listings.length <= MAX_LISTINGS_PER_BOARD, "too many listings for board");

    OptionBoardCache storage boardCache = boardCaches[boardId];
    boardCache.id = boardId;
    boardCache.expiry = expiry;
    boardCache.iv = iv;
    liveBoards.push(boardId);

    for (uint i = 0; i < listings.length; i++) {
      _addNewListingToListingCache(boardCache, listings[i]);
    }

    _updateBoardLastUpdatedAt(boardCache);
  }

  /**
   * @notice Removes an OptionBoardCache.
   * @dev Called by the OptionMarket when an OptionBoard is liquidated.
   *
   * @param boardId The id of the OptionBoard.
   */
  function removeBoard(uint boardId) external override onlyOptionMarket {
    // Remove board from cache, removing net positions from global count
    OptionBoardCache memory boardCache = boardCaches[boardId];
    globalCache.netDelta = globalCache.netDelta.sub(boardCache.netDelta);
    globalCache.netStdVega = globalCache.netStdVega.sub(boardCache.netStdVega);
    // Clean up, cache isn't necessary for settle logic
    for (uint i = 0; i < boardCache.listings.length; i++) {
      delete listingCaches[boardCache.listings[i]];
    }
    for (uint i = 0; i < liveBoards.length; i++) {
      if (liveBoards[i] == boardId) {
        liveBoards[i] = liveBoards[liveBoards.length - 1];
        liveBoards.pop();
        break;
      }
    }
    delete boardCaches[boardId];
    emit GlobalCacheUpdated(globalCache.netDelta, globalCache.netStdVega);
  }

  /**
   * @dev modifies an OptionBoard's baseIv
   *
   * @param boardId The id of the OptionBoard.
   * @param newIv The baseIv of the OptionBoard.
   */
  function setBoardIv(uint boardId, uint newIv) external override onlyOptionMarket {
    // Remove board from cache, removing net positions from global count
    OptionBoardCache storage boardCache = boardCaches[boardId];
    boardCache.iv = newIv;
  }

  /**
   * @dev modifies an OptionListing's skew
   *
   * @param listingId The id of the OptionListing.
   * @param newSkew The skew of the OptionListing.
   */
  function setListingSkew(uint listingId, uint newSkew) external override onlyOptionMarket {
    // Remove board from cache, removing net positions from global count
    OptionListingCache storage listingCache = listingCaches[listingId];
    listingCache.skew = newSkew;
  }

  /**
   * @notice Add a new listing to the listingCaches and the listingId to the boardCache
   *
   * @param boardId The id of the Board
   * @param listingId The id of the OptionListing.
   */
  function addListingToBoard(uint boardId, uint listingId) external override onlyOptionMarket {
    OptionBoardCache storage boardCache = boardCaches[boardId];
    require(boardCache.listings.length + 1 <= MAX_LISTINGS_PER_BOARD, "too many listings for board");
    _addNewListingToListingCache(boardCache, listingId);
  }

  /**
   * @notice Add a new listing to the listingCaches
   *
   * @param boardCache The OptionBoardCache object the listing is being added to
   * @param listingId The id of the OptionListing.
   */
  function _addNewListingToListingCache(OptionBoardCache storage boardCache, uint listingId) internal {
    IOptionMarket.OptionListing memory listing = getOptionMarketListing(listingId);

    // This is only called when a new board or a new listing is added, so exposure values will be 0
    OptionListingCache storage listingCache = listingCaches[listing.id];
    listingCache.id = listing.id;
    listingCache.strike = listing.strike;
    listingCache.boardId = listing.boardId;
    listingCache.skew = listing.skew;

    boardCache.listings.push(listingId);
  }

  /**
   * @notice Retrieves an OptionListing from the OptionMarket.
   *
   * @param listingId The id of the OptionListing.
   */
  function getOptionMarketListing(uint listingId) internal view returns (IOptionMarket.OptionListing memory) {
    (uint id, uint strike, uint skew, uint longCall, uint shortCall, uint longPut, uint shortPut, uint boardId) =
      optionMarket.optionListings(listingId);
    return IOptionMarket.OptionListing(id, strike, skew, longCall, shortCall, longPut, shortPut, boardId);
  }

  ////
  // Updating greeks/caches
  ////

  /**
   * @notice Updates all stale boards.
   */
  function updateAllStaleBoards() external override returns (int) {
    // Check all boards to see if they are stale
    ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals = globals.getGreekCacheGlobals(address(optionMarket));
    _updateAllStaleBoards(greekCacheGlobals);
    return globalCache.netDelta;
  }

  /**
   * @dev Updates all stale boards.
   *
   * @param greekCacheGlobals The GreekCacheGlobals.
   */
  function _updateAllStaleBoards(ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals) internal {
    for (uint i = 0; i < liveBoards.length; i++) {
      uint boardId = liveBoards[i];
      if (_isBoardCacheStale(boardId, greekCacheGlobals.spotPrice)) {
        // This updates all listings in the board, even though it is not strictly necessary
        _updateBoardCachedGreeks(greekCacheGlobals, boardId);
      }
    }
  }

  /**
   * @notice Updates the cached greeks for an OptionBoardCache.
   *
   * @param boardCacheId The id of the OptionBoardCache.
   */
  function updateBoardCachedGreeks(uint boardCacheId) external override {
    _updateBoardCachedGreeks(globals.getGreekCacheGlobals(address(optionMarket)), boardCacheId);
  }

  /**
   * @dev Updates the cached greeks for an OptionBoardCache.
   *
   * @param greekCacheGlobals The GreekCacheGlobals.
   * @param boardCacheId The id of the OptionBoardCache.
   */
  function _updateBoardCachedGreeks(ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals, uint boardCacheId)
    internal
  {
    OptionBoardCache storage boardCache = boardCaches[boardCacheId];
    // In the case the board doesnt exist, listings.length is 0, so nothing happens
    for (uint i = 0; i < boardCache.listings.length; i++) {
      OptionListingCache storage listingCache = listingCaches[boardCache.listings[i]];
      _updateListingCachedGreeks(
        greekCacheGlobals,
        listingCache,
        boardCache,
        true,
        listingCache.callExposure,
        listingCache.putExposure
      );
    }

    boardCache.minUpdatedAt = block.timestamp;
    boardCache.minUpdatedAtPrice = greekCacheGlobals.spotPrice;
    boardCache.maxUpdatedAtPrice = greekCacheGlobals.spotPrice;
    _updateGlobalLastUpdatedAt();
  }

  /**
   * @notice Updates the OptionListingCache to reflect the new exposure.
   *
   * @param greekCacheGlobals The GreekCacheGlobals.
   * @param listingCacheId The id of the OptionListingCache.
   * @param newCallExposure The new call exposure of the OptionListing.
   * @param newPutExposure The new put exposure of the OptionListing.
   * @param iv The new iv of the OptionBoardCache.
   * @param skew The new skew of the OptionListingCache.
   */
  function updateListingCacheAndGetPrice(
    ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals,
    uint listingCacheId,
    int newCallExposure,
    int newPutExposure,
    uint iv,
    uint skew
  ) external override onlyOptionMarketPricer returns (IOptionMarketPricer.Pricing memory) {
    require(!_isGlobalCacheStale(greekCacheGlobals.spotPrice), "Global cache is stale");
    OptionListingCache storage listingCache = listingCaches[listingCacheId];
    OptionBoardCache storage boardCache = boardCaches[listingCache.boardId];

    int callExposureDiff = newCallExposure.sub(listingCache.callExposure);
    int putExposureDiff = newPutExposure.sub(listingCache.putExposure);

    require(callExposureDiff == 0 || putExposureDiff == 0, "both call and put exposure updated");

    boardCache.iv = iv;
    listingCache.skew = skew;

    // The AMM's net std vega is opposite to the global sum of user's std vega
    int preTradeAmmNetStdVega = -globalCache.netStdVega;

    IOptionMarketPricer.Pricing memory pricing =
      _updateListingCachedGreeks(
        greekCacheGlobals,
        listingCache,
        boardCache,
        callExposureDiff != 0,
        newCallExposure,
        newPutExposure
      );
    pricing.preTradeAmmNetStdVega = preTradeAmmNetStdVega;

    _updateBoardLastUpdatedAt(boardCache);

    return pricing;
  }

  /**
   * @dev Updates an OptionListingCache.
   *
   * @param greekCacheGlobals The GreekCacheGlobals.
   * @param listingCache The OptionListingCache.
   * @param boardCache The OptionBoardCache.
   * @param returnCallPrice If true, return the call price, otherwise return the put price.
   */
  function _updateListingCachedGreeks(
    ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals,
    OptionListingCache storage listingCache,
    OptionBoardCache storage boardCache,
    bool returnCallPrice,
    int newCallExposure,
    int newPutExposure
  ) internal returns (IOptionMarketPricer.Pricing memory pricing) {
    IBlackScholes.PricesDeltaStdVega memory pricesDeltaStdVega =
      blackScholes.pricesDeltaStdVega(
        timeToMaturitySeconds(boardCache.expiry),
        boardCache.iv.multiplyDecimal(listingCache.skew),
        greekCacheGlobals.spotPrice,
        listingCache.strike,
        greekCacheGlobals.rateAndCarry
      );

    // (newCallExposure * newCallDelta - oldCallExposure * oldCallDelta)
    // + (newPutExposure * newPutDelta - oldPutExposure * oldPutDelta)
    int netDeltaDiff =
      (
        (newCallExposure.multiplyDecimal(pricesDeltaStdVega.callDelta)) // newCall
          .sub(listingCache.callExposure.multiplyDecimal(listingCache.callDelta))
          .add(
          (newPutExposure.multiplyDecimal(pricesDeltaStdVega.putDelta)).sub(
            listingCache.putExposure.multiplyDecimal(listingCache.putDelta)
          )
        )
      );

    int netStdVegaDiff =
      newCallExposure.add(newPutExposure).multiplyDecimal(int(pricesDeltaStdVega.stdVega)).sub(
        listingCache.callExposure.add(listingCache.putExposure).multiplyDecimal(int(listingCache.stdVega))
      );

    if (listingCache.callExposure != newCallExposure || listingCache.putExposure != newPutExposure) {
      emit ListingExposureUpdated(listingCache.id, newCallExposure, newPutExposure);
    }

    listingCache.callExposure = newCallExposure;
    listingCache.putExposure = newPutExposure;
    listingCache.callDelta = pricesDeltaStdVega.callDelta;
    listingCache.putDelta = pricesDeltaStdVega.putDelta;
    listingCache.stdVega = pricesDeltaStdVega.stdVega;

    listingCache.updatedAt = block.timestamp;
    listingCache.updatedAtPrice = greekCacheGlobals.spotPrice;

    boardCache.netDelta = boardCache.netDelta.add(netDeltaDiff);
    boardCache.netStdVega = boardCache.netStdVega.add(netStdVegaDiff);

    globalCache.netDelta = globalCache.netDelta.add(netDeltaDiff);
    globalCache.netStdVega = globalCache.netStdVega.add(netStdVegaDiff);

    pricing.optionPrice = returnCallPrice ? pricesDeltaStdVega.callPrice : pricesDeltaStdVega.putPrice;
    // AMM's net positions are the inverse of the user's net position
    pricing.postTradeAmmNetStdVega = -globalCache.netStdVega;
    pricing.callDelta = pricesDeltaStdVega.callDelta;

    emit ListingGreeksUpdated(
      listingCache.id,
      pricesDeltaStdVega.callDelta,
      pricesDeltaStdVega.putDelta,
      pricesDeltaStdVega.stdVega,
      greekCacheGlobals.spotPrice,
      boardCache.iv,
      listingCache.skew
    );
    emit GlobalCacheUpdated(globalCache.netDelta, globalCache.netStdVega);

    return pricing;
  }

  /**
   * @notice Checks if the GlobalCache is stale.
   */
  function isGlobalCacheStale() external view override returns (bool) {
    // Check all boards to see if they are stale
    uint currentPrice = getCurrentPrice();
    return _isGlobalCacheStale(currentPrice);
  }

  /**
   * @dev Checks if the GlobalCache is stale.
   *
   * @param spotPrice The price of the baseAsset.
   */
  function _isGlobalCacheStale(uint spotPrice) internal view returns (bool) {
    // Check all boards to see if they are stale
    return (isUpdatedAtTimeStale(globalCache.minUpdatedAt) ||
      !isPriceMoveAcceptable(
        globalCache.minUpdatedAtPrice,
        spotPrice,
        timeToMaturitySeconds(globalCache.minExpiryTimestamp)
      ) ||
      !isPriceMoveAcceptable(
        globalCache.maxUpdatedAtPrice,
        spotPrice,
        timeToMaturitySeconds(globalCache.minExpiryTimestamp)
      ));
  }

  /**
   * @notice Checks if the OptionBoardCache is stale.
   *
   * @param boardCacheId The OptionBoardCache id.
   */
  function isBoardCacheStale(uint boardCacheId) external view override returns (bool) {
    uint spotPrice = getCurrentPrice();
    return _isBoardCacheStale(boardCacheId, spotPrice);
  }

  /**
   * @dev Checks if the OptionBoardCache is stale.
   *
   * @param boardCacheId The OptionBoardCache id.
   * @param spotPrice The price of the baseAsset.
   */
  function _isBoardCacheStale(uint boardCacheId, uint spotPrice) internal view returns (bool) {
    // We do not have to check every individual listing, as the OptionBoardCache
    // should always keep the minimum values.
    OptionBoardCache memory boardCache = boardCaches[boardCacheId];
    require(boardCache.id != 0, "Board does not exist");

    return
      isUpdatedAtTimeStale(boardCache.minUpdatedAt) ||
      !isPriceMoveAcceptable(boardCache.minUpdatedAtPrice, spotPrice, timeToMaturitySeconds(boardCache.expiry)) ||
      !isPriceMoveAcceptable(boardCache.maxUpdatedAtPrice, spotPrice, timeToMaturitySeconds(boardCache.expiry));
  }

  /**
   * @dev Checks if `updatedAt` is stale.
   *
   * @param updatedAt The time of the last update.
   */
  function isUpdatedAtTimeStale(uint updatedAt) internal view returns (bool) {
    // This can be more complex than just checking the item wasn't updated in the last two hours
    return getSecondsTo(updatedAt, block.timestamp) > staleUpdateDuration;
  }

  /**
   * @dev Check if the price move of an asset is acceptable given the time to expiry.
   *
   * @param pastPrice The previous price.
   * @param currentPrice The current price.
   * @param timeToExpirySec The time to expiry in seconds.
   */
  function isPriceMoveAcceptable(
    uint pastPrice,
    uint currentPrice,
    uint timeToExpirySec
  ) internal view returns (bool) {
    uint acceptablePriceMovementPercent = maxAcceptablePercent;

    if (timeToExpirySec < priceScalingPeriod) {
      acceptablePriceMovementPercent = ((maxAcceptablePercent.sub(minAcceptablePercent)).mul(timeToExpirySec))
        .div(priceScalingPeriod)
        .add(minAcceptablePercent);
    }

    uint acceptablePriceMovement = pastPrice.multiplyDecimal(acceptablePriceMovementPercent);

    if (currentPrice > pastPrice) {
      return currentPrice.sub(pastPrice) < acceptablePriceMovement;
    } else {
      return pastPrice.sub(currentPrice) < acceptablePriceMovement;
    }
  }

  /**
   * @dev Updates `lastUpdatedAt` for an OptionBoardCache.
   *
   * @param boardCache The OptionBoardCache.
   */
  function _updateBoardLastUpdatedAt(OptionBoardCache storage boardCache) internal {
    OptionListingCache memory listingCache = listingCaches[boardCache.listings[0]];
    uint minUpdate = listingCache.updatedAt;
    uint minPrice = listingCache.updatedAtPrice;
    uint maxPrice = listingCache.updatedAtPrice;

    for (uint i = 1; i < boardCache.listings.length; i++) {
      listingCache = listingCaches[boardCache.listings[i]];
      if (listingCache.updatedAt < minUpdate) {
        minUpdate = listingCache.updatedAt;
      }
      if (listingCache.updatedAtPrice < minPrice) {
        minPrice = listingCache.updatedAtPrice;
      } else if (listingCache.updatedAtPrice > maxPrice) {
        maxPrice = listingCache.updatedAtPrice;
      }
    }
    boardCache.minUpdatedAt = minUpdate;
    boardCache.minUpdatedAtPrice = minPrice;
    boardCache.maxUpdatedAtPrice = maxPrice;

    _updateGlobalLastUpdatedAt();
  }

  /**
   * @dev Updates global `lastUpdatedAt`.
   */
  function _updateGlobalLastUpdatedAt() internal {
    OptionBoardCache memory boardCache = boardCaches[liveBoards[0]];
    uint minUpdate = boardCache.minUpdatedAt;
    uint minPrice = boardCache.minUpdatedAtPrice;
    uint minExpiry = boardCache.expiry;
    uint maxPrice = boardCache.maxUpdatedAtPrice;

    for (uint i = 1; i < liveBoards.length; i++) {
      boardCache = boardCaches[liveBoards[i]];
      if (boardCache.minUpdatedAt < minUpdate) {
        minUpdate = boardCache.minUpdatedAt;
      }
      if (boardCache.minUpdatedAtPrice < minPrice) {
        minPrice = boardCache.minUpdatedAtPrice;
      }
      if (boardCache.maxUpdatedAtPrice > maxPrice) {
        maxPrice = boardCache.maxUpdatedAtPrice;
      }
      if (boardCache.expiry < minExpiry) {
        minExpiry = boardCache.expiry;
      }
    }

    globalCache.minUpdatedAt = minUpdate;
    globalCache.minUpdatedAtPrice = minPrice;
    globalCache.maxUpdatedAtPrice = maxPrice;
    globalCache.minExpiryTimestamp = minExpiry;
  }

  /**
   * @dev Returns time to maturity for a given expiry.
   */
  function timeToMaturitySeconds(uint expiry) internal view returns (uint) {
    return getSecondsTo(block.timestamp, expiry);
  }

  /**
   * @dev Returns the difference in seconds between two dates.
   */
  function getSecondsTo(uint fromTime, uint toTime) internal pure returns (uint) {
    if (toTime > fromTime) {
      return toTime - fromTime;
    }
    return 0;
  }

  /**
   * @dev Get the price of the baseAsset for the OptionMarket.
   */
  function getCurrentPrice() internal view returns (uint) {
    return globals.getSpotPriceForMarket(address(optionMarket));
  }

  /**
   * @dev Get the current cached global netDelta value.
   */
  function getGlobalNetDelta() external view override returns (int) {
    return globalCache.netDelta;
  }

  modifier onlyOptionMarket virtual {
    require(msg.sender == address(optionMarket), "Only optionMarket permitted");
    _;
  }

  modifier onlyOptionMarketPricer virtual {
    require(msg.sender == address(optionPricer), "Only optionPricer permitted");
    _;
  }

  /**
   * @dev Emitted when stale cache parameters are updated.
   */
  event StaleCacheParametersUpdated(
    uint priceScalingPeriod,
    uint minAcceptablePercent,
    uint maxAcceptablePercent,
    uint staleUpdateDuration
  );

  /**
   * @dev Emitted when the cache of an OptionListing is updated.
   */
  event ListingGreeksUpdated(
    uint indexed listingId,
    int callDelta,
    int putDelta,
    uint vega,
    uint price,
    uint baseIv,
    uint skew
  );

  /**
   * @dev Emitted when the exposure of an OptionListing is updated.
   */
  event ListingExposureUpdated(uint indexed listingId, int newCallExposure, int newPutExposure);

  /**
   * @dev Emitted when the GlobalCache is updated.
   */
  event GlobalCacheUpdated(int netDelta, int netStdVega);
}

File 8 of 34 : ShortCollateral.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

// Libraries
import "./synthetix/SafeDecimalMath.sol";
// Inherited
// Interfaces
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IPoolHedger.sol";
import "./interfaces/ILyraGlobals.sol";
import "./interfaces/ILiquidityPool.sol";
import "./interfaces/IOptionMarket.sol";
import "./interfaces/IShortCollateral.sol";

/**
 * @title ShortCollateral
 * @author Lyra
 * @dev Holds collateral from users who are selling (shorting) options to the OptionMarket.
 */
contract ShortCollateral is IShortCollateral {
  using SafeMath for uint;
  using SafeDecimalMath for uint;

  bool internal initialized = false;

  IOptionMarket internal optionMarket;
  ILiquidityPool internal liquidityPool;
  IERC20 internal quoteAsset;
  IERC20 internal baseAsset;

  constructor() {}

  /**
   * @dev Initialize the contract.
   *
   * @param _optionMarket OptionMarket address
   * @param _liquidityPool LiquidityPool address
   * @param _quoteAsset Quote asset address
   * @param _baseAsset Base asset address
   */
  function init(
    IOptionMarket _optionMarket,
    ILiquidityPool _liquidityPool,
    IERC20 _quoteAsset,
    IERC20 _baseAsset
  ) external {
    require(!initialized, "contract already initialized");
    optionMarket = _optionMarket;
    liquidityPool = _liquidityPool;
    quoteAsset = _quoteAsset;
    baseAsset = _baseAsset;
    initialized = true;
  }

  /**
   * @dev Transfers quoteAsset to the recipient.
   *
   * @param recipient The recipient of the transfer.
   * @param amount The amount to send.
   */
  function sendQuoteCollateral(address recipient, uint amount) external override onlyOptionMarket {
    uint currentBalance = quoteAsset.balanceOf(address(this));
    if (amount > currentBalance) {
      amount = currentBalance;
    }
    require(quoteAsset.transfer(recipient, amount), "transfer failed");
    emit QuoteSent(recipient, amount);
  }

  /**
   * @dev Transfers baseAsset to the recipient.
   *
   * @param recipient The recipient of the transfer.
   * @param amount The amount to send.
   */
  function sendBaseCollateral(address recipient, uint amount) external override onlyOptionMarket {
    uint currentBalance = baseAsset.balanceOf(address(this));
    if (amount > currentBalance) {
      amount = currentBalance;
    }
    require(baseAsset.transfer(recipient, amount), "transfer failed");
    emit BaseSent(recipient, amount);
  }

  /**
   * @dev Transfers quoteAsset and baseAsset to the LiquidityPool.
   *
   * @param amountBase The amount of baseAsset to transfer.
   * @param amountQuote The amount of quoteAsset to transfer.
   */
  function sendToLP(uint amountBase, uint amountQuote) external override onlyOptionMarket {
    uint currentBaseBalance = baseAsset.balanceOf(address(this));
    if (amountBase > currentBaseBalance) {
      amountBase = currentBaseBalance;
    }
    if (amountBase > 0) {
      require(baseAsset.transfer(address(liquidityPool), amountBase), "base transfer failed");
      emit BaseSent(address(liquidityPool), amountBase);
    }

    uint currentQuoteBalance = quoteAsset.balanceOf(address(this));
    if (amountQuote > currentQuoteBalance) {
      amountQuote = currentQuoteBalance;
    }
    if (amountQuote > 0) {
      require(quoteAsset.transfer(address(liquidityPool), amountQuote), "quote transfer failed");
      emit QuoteSent(address(liquidityPool), amountQuote);
    }
  }

  /**
   * @dev Called by the OptionMarket when the owner of an option settles.
   *
   * @param listingId The OptionListing.
   * @param receiver The address of the receiver.
   * @param tradeType The TradeType.
   * @param amount The amount to settle.
   * @param strike The strike price of the OptionListing.
   * @param priceAtExpiry The price of baseAsset at expiry.
   * @param listingToShortCallBaseReturned The amount of baseAsset to be returned.
   */
  function processSettle(
    uint listingId,
    address receiver,
    IOptionMarket.TradeType tradeType,
    uint amount,
    uint strike,
    uint priceAtExpiry,
    uint listingToShortCallBaseReturned
  ) external override onlyOptionMarket {
    // Check board has been liquidated
    require(priceAtExpiry != 0, "board must be liquidated");
    require(amount > 0, "option position is 0");

    if (tradeType == IOptionMarket.TradeType.SHORT_CALL) {
      require(
        baseAsset.transfer(receiver, listingToShortCallBaseReturned.multiplyDecimal(amount)),
        "base transfer failed"
      );
    } else if (tradeType == IOptionMarket.TradeType.LONG_CALL && strike < priceAtExpiry) {
      // long call finished in the money
      liquidityPool.sendReservedQuote(receiver, (priceAtExpiry - strike).multiplyDecimal(amount));
    } else if (tradeType == IOptionMarket.TradeType.SHORT_PUT) {
      // If the listing finished in the money;
      // = we pay out the priceAtExpiry (strike - (strike - priceAtExpiry) == priceAtExpiry)
      // Otherwise pay back the strike...
      uint balance = quoteAsset.balanceOf(address(this));
      uint owed = amount.multiplyDecimal((strike > priceAtExpiry) ? priceAtExpiry : strike);
      require(
        quoteAsset.transfer(
          receiver,
          // Return the full balance if owed > balance due to rounding errors
          owed > balance ? balance : owed
        ),
        "quote transfer failed"
      );
    } else if (tradeType == IOptionMarket.TradeType.LONG_PUT && strike > priceAtExpiry) {
      // user was long put and it finished in the money
      liquidityPool.sendReservedQuote(receiver, (strike - priceAtExpiry).multiplyDecimal(amount));
    }

    emit OptionsSettled(listingId, receiver, strike, priceAtExpiry, tradeType, amount);
  }

  // Modifiers

  modifier onlyOptionMarket virtual {
    require(msg.sender == address(optionMarket), "only OptionMarket");
    _;
  }

  // Events

  /**
   * @dev Emitted when an Option is settled.
   */
  event OptionsSettled(
    uint indexed listingId,
    address indexed optionOwner,
    uint strike,
    uint priceAtExpiry,
    IOptionMarket.TradeType tradeType,
    uint amount
  );

  /**
   * @dev Emitted when quote is sent to either a user or the LiquidityPool
   */
  event QuoteSent(address indexed receiver, uint amount);
  /**
   * @dev Emitted when base is sent to either a user or the LiquidityPool
   */
  event BaseSent(address indexed receiver, uint amount);
}

File 9 of 34 : IOptionToken.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol";

interface IOptionToken is IERC1155, IERC1155MetadataURI {
  function setURI(string memory newURI) external;

  function mint(
    address account,
    uint id,
    uint amount
  ) external;

  function burn(
    address account,
    uint id,
    uint amount
  ) external;
}

File 10 of 34 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 11 of 34 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../utils/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 12 of 34 : IExchanger.sol
//SPDX-License-Identifier:MIT
pragma solidity ^0.7.6;

// https://docs.synthetix.io/contracts/source/interfaces/iexchanger
interface IExchanger {
  function feeRateForExchange(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey)
    external
    view
    returns (uint exchangeFeeRate);
}

File 13 of 34 : ICollateralShort.sol
//SPDX-License-Identifier: ISC
pragma solidity >=0.7.6;
pragma experimental ABIEncoderV2;

interface ICollateralShort {
  struct Loan {
    // ID for the loan
    uint id;
    //  Account that created the loan
    address account;
    //  Amount of collateral deposited
    uint collateral;
    // The synth that was borrowed
    bytes32 currency;
    //  Amount of synths borrowed
    uint amount;
    // Indicates if the position was short sold
    bool short;
    // interest amounts accrued
    uint accruedInterest;
    // last interest index
    uint interestIndex;
    // time of last interaction.
    uint lastInteraction;
  }

  function loans(uint id)
    external
    returns (
      uint,
      address,
      uint,
      bytes32,
      uint,
      bool,
      uint,
      uint,
      uint
    );

  function minCratio() external returns (uint);

  function minCollateral() external returns (uint);

  function issueFeeRate() external returns (uint);

  function open(
    uint collateral,
    uint amount,
    bytes32 currency
  ) external returns (uint id);

  function repay(
    address borrower,
    uint id,
    uint amount
  ) external returns (uint short, uint collateral);

  function repayWithCollateral(uint id, uint repayAmount) external returns (uint short, uint collateral);

  function draw(uint id, uint amount) external returns (uint short, uint collateral);

  // Same as before
  function deposit(
    address borrower,
    uint id,
    uint amount
  ) external returns (uint short, uint collateral);

  // Same as before
  function withdraw(uint id, uint amount) external returns (uint short, uint collateral);

  // function to return the loan details in one call, without needing to know about the collateralstate
  function getShortAndCollateral(address account, uint id) external view returns (uint short, uint collateral);
}

File 14 of 34 : ISynthetix.sol
//SPDX-License-Identifier: ISC
pragma solidity >=0.7.6;

interface ISynthetix {
  function exchange(
    bytes32 sourceCurrencyKey,
    uint sourceAmount,
    bytes32 destinationCurrencyKey
  ) external returns (uint amountReceived);

  function exchangeOnBehalf(
    address exchangeForAddress,
    bytes32 sourceCurrencyKey,
    uint sourceAmount,
    bytes32 destinationCurrencyKey
  ) external returns (uint amountReceived);
}

File 15 of 34 : IExchangeRates.sol
//SPDX-License-Identifier:MIT
pragma solidity ^0.7.6;

// https://docs.synthetix.io/contracts/source/interfaces/iexchangerates
interface IExchangeRates {
  function rateAndInvalid(bytes32 currencyKey) external view returns (uint rate, bool isInvalid);
}

File 16 of 34 : ILiquidityPool.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ILyraGlobals.sol";

interface ILiquidityPool {
  struct Collateral {
    uint quote;
    uint base;
  }

  /// @dev These are all in quoteAsset amounts.
  struct Liquidity {
    uint freeCollatLiquidity;
    uint usedCollatLiquidity;
    uint freeDeltaLiquidity;
    uint usedDeltaLiquidity;
  }

  enum Error {
    QuoteTransferFailed,
    AlreadySignalledWithdrawal,
    SignallingBetweenRounds,
    UnSignalMustSignalFirst,
    UnSignalAlreadyBurnable,
    WithdrawNotBurnable,
    EndRoundWithLiveBoards,
    EndRoundAlreadyEnded,
    EndRoundMustExchangeBase,
    EndRoundMustHedgeDelta,
    StartRoundMustEndRound,
    ReceivedZeroFromBaseQuoteExchange,
    ReceivedZeroFromQuoteBaseExchange,
    LockingMoreQuoteThanIsFree,
    LockingMoreBaseThanCanBeExchanged,
    FreeingMoreBaseThanLocked,
    SendPremiumNotEnoughCollateral,
    OnlyPoolHedger,
    OnlyOptionMarket,
    OnlyShortCollateral,
    ReentrancyDetected,
    Last
  }

  function lockedCollateral() external view returns (uint, uint);

  function queuedQuoteFunds() external view returns (uint);

  function expiryToTokenValue(uint) external view returns (uint);

  function deposit(address beneficiary, uint amount) external returns (uint);

  function signalWithdrawal(uint certificateId) external;

  function unSignalWithdrawal(uint certificateId) external;

  function withdraw(address beneficiary, uint certificateId) external returns (uint value);

  function tokenPriceQuote() external view returns (uint);

  function endRound() external;

  function startRound(uint lastMaxExpiryTimestamp, uint newMaxExpiryTimestamp) external;

  function exchangeBase() external;

  function lockQuote(uint amount, uint freeCollatLiq) external;

  function lockBase(
    uint amount,
    ILyraGlobals.ExchangeGlobals memory exchangeGlobals,
    Liquidity memory liquidity
  ) external;

  function freeQuoteCollateral(uint amount) external;

  function freeBase(uint amountBase) external;

  function sendPremium(
    address recipient,
    uint amount,
    uint freeCollatLiq
  ) external;

  function boardLiquidation(
    uint amountQuoteFreed,
    uint amountQuoteReserved,
    uint amountBaseFreed
  ) external;

  function sendReservedQuote(address user, uint amount) external;

  function getTotalPoolValueQuote(uint basePrice, uint usedDeltaLiquidity) external view returns (uint);

  function getLiquidity(uint basePrice, ICollateralShort short) external view returns (Liquidity memory);

  function transferQuoteToHedge(ILyraGlobals.ExchangeGlobals memory exchangeGlobals, uint amount)
    external
    returns (uint);
}

File 17 of 34 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 18 of 34 : ILyraGlobals.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ICollateralShort.sol";
import "./IExchangeRates.sol";
import "./IExchanger.sol";
import "./ISynthetix.sol";

interface ILyraGlobals {
  enum ExchangeType {BASE_QUOTE, QUOTE_BASE, ALL}

  /**
   * @dev Structs to help reduce the number of calls between other contracts and this one
   * Grouped in usage for a particular contract/use case
   */
  struct ExchangeGlobals {
    uint spotPrice;
    bytes32 quoteKey;
    bytes32 baseKey;
    ISynthetix synthetix;
    ICollateralShort short;
    uint quoteBaseFeeRate;
    uint baseQuoteFeeRate;
  }

  struct GreekCacheGlobals {
    int rateAndCarry;
    uint spotPrice;
  }

  struct PricingGlobals {
    uint optionPriceFeeCoefficient;
    uint spotPriceFeeCoefficient;
    uint vegaFeeCoefficient;
    uint vegaNormFactor;
    uint standardSize;
    uint skewAdjustmentFactor;
    int rateAndCarry;
    int minDelta;
    uint volatilityCutoff;
    uint spotPrice;
  }

  function synthetix() external view returns (ISynthetix);

  function exchanger() external view returns (IExchanger);

  function exchangeRates() external view returns (IExchangeRates);

  function collateralShort() external view returns (ICollateralShort);

  function isPaused() external view returns (bool);

  function tradingCutoff(address) external view returns (uint);

  function optionPriceFeeCoefficient(address) external view returns (uint);

  function spotPriceFeeCoefficient(address) external view returns (uint);

  function vegaFeeCoefficient(address) external view returns (uint);

  function vegaNormFactor(address) external view returns (uint);

  function standardSize(address) external view returns (uint);

  function skewAdjustmentFactor(address) external view returns (uint);

  function rateAndCarry(address) external view returns (int);

  function minDelta(address) external view returns (int);

  function volatilityCutoff(address) external view returns (uint);

  function quoteKey(address) external view returns (bytes32);

  function baseKey(address) external view returns (bytes32);

  function setGlobals(
    ISynthetix _synthetix,
    IExchanger _exchanger,
    IExchangeRates _exchangeRates,
    ICollateralShort _collateralShort
  ) external;

  function setGlobalsForContract(
    address _contractAddress,
    uint _tradingCutoff,
    PricingGlobals memory pricingGlobals,
    bytes32 _quoteKey,
    bytes32 _baseKey
  ) external;

  function setPaused(bool _isPaused) external;

  function setTradingCutoff(address _contractAddress, uint _tradingCutoff) external;

  function setOptionPriceFeeCoefficient(address _contractAddress, uint _optionPriceFeeCoefficient) external;

  function setSpotPriceFeeCoefficient(address _contractAddress, uint _spotPriceFeeCoefficient) external;

  function setVegaFeeCoefficient(address _contractAddress, uint _vegaFeeCoefficient) external;

  function setVegaNormFactor(address _contractAddress, uint _vegaNormFactor) external;

  function setStandardSize(address _contractAddress, uint _standardSize) external;

  function setSkewAdjustmentFactor(address _contractAddress, uint _skewAdjustmentFactor) external;

  function setRateAndCarry(address _contractAddress, int _rateAndCarry) external;

  function setMinDelta(address _contractAddress, int _minDelta) external;

  function setVolatilityCutoff(address _contractAddress, uint _volatilityCutoff) external;

  function setQuoteKey(address _contractAddress, bytes32 _quoteKey) external;

  function setBaseKey(address _contractAddress, bytes32 _baseKey) external;

  function getSpotPriceForMarket(address _contractAddress) external view returns (uint);

  function getSpotPrice(bytes32 to) external view returns (uint);

  function getPricingGlobals(address _contractAddress) external view returns (PricingGlobals memory);

  function getGreekCacheGlobals(address _contractAddress) external view returns (GreekCacheGlobals memory);

  function getExchangeGlobals(address _contractAddress, ExchangeType exchangeType)
    external
    view
    returns (ExchangeGlobals memory exchangeGlobals);

  function getGlobalsForOptionTrade(address _contractAddress, bool isBuy)
    external
    view
    returns (
      PricingGlobals memory pricingGlobals,
      ExchangeGlobals memory exchangeGlobals,
      uint tradeCutoff
    );
}

File 19 of 34 : IOptionMarket.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ILyraGlobals.sol";
import "./ILiquidityPool.sol";

interface IOptionMarket {
  struct OptionListing {
    uint id;
    uint strike;
    uint skew;
    uint longCall;
    uint shortCall;
    uint longPut;
    uint shortPut;
    uint boardId;
  }

  struct OptionBoard {
    uint id;
    uint expiry;
    uint iv;
    bool frozen;
    uint[] listingIds;
  }

  struct Trade {
    bool isBuy;
    uint amount;
    uint vol;
    uint expiry;
    ILiquidityPool.Liquidity liquidity;
  }

  enum TradeType {LONG_CALL, SHORT_CALL, LONG_PUT, SHORT_PUT}

  enum Error {
    TransferOwnerToZero,
    InvalidBoardId,
    InvalidBoardIdOrNotFrozen,
    InvalidListingIdOrNotFrozen,
    StrikeSkewLengthMismatch,
    BoardMaxExpiryReached,
    CannotStartNewRoundWhenBoardsExist,
    ZeroAmountOrInvalidTradeType,
    BoardFrozenOrTradingCutoffReached,
    QuoteTransferFailed,
    BaseTransferFailed,
    BoardNotExpired,
    BoardAlreadyLiquidated,
    OnlyOwner,
    Last
  }

  function maxExpiryTimestamp() external view returns (uint);

  function optionBoards(uint)
    external
    view
    returns (
      uint id,
      uint expiry,
      uint iv,
      bool frozen
    );

  function optionListings(uint)
    external
    view
    returns (
      uint id,
      uint strike,
      uint skew,
      uint longCall,
      uint shortCall,
      uint longPut,
      uint shortPut,
      uint boardId
    );

  function boardToPriceAtExpiry(uint) external view returns (uint);

  function listingToBaseReturnedRatio(uint) external view returns (uint);

  function transferOwnership(address newOwner) external;

  function setBoardFrozen(uint boardId, bool frozen) external;

  function setBoardBaseIv(uint boardId, uint baseIv) external;

  function setListingSkew(uint listingId, uint skew) external;

  function createOptionBoard(
    uint expiry,
    uint baseIV,
    uint[] memory strikes,
    uint[] memory skews
  ) external returns (uint);

  function addListingToBoard(
    uint boardId,
    uint strike,
    uint skew
  ) external;

  function getLiveBoards() external view returns (uint[] memory _liveBoards);

  function getBoardListings(uint boardId) external view returns (uint[] memory);

  function openPosition(
    uint _listingId,
    TradeType tradeType,
    uint amount
  ) external returns (uint totalCost);

  function closePosition(
    uint _listingId,
    TradeType tradeType,
    uint amount
  ) external returns (uint totalCost);

  function liquidateExpiredBoard(uint boardId) external;

  function settleOptions(uint listingId, TradeType tradeType) external;
}

File 20 of 34 : ILiquidityCertificate.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

interface ILiquidityCertificate {
  struct CertificateData {
    uint liquidity;
    uint enteredAt;
    uint burnableAt;
  }

  function MIN_LIQUIDITY() external view returns (uint);

  function liquidityPool() external view returns (address);

  function certificates(address owner) external view returns (uint[] memory);

  function liquidity(uint certificateId) external view returns (uint);

  function enteredAt(uint certificateId) external view returns (uint);

  function burnableAt(uint certificateId) external view returns (uint);

  function certificateData(uint certificateId) external view returns (CertificateData memory);

  function mint(
    address owner,
    uint liquidityAmount,
    uint expiryAtCreation
  ) external returns (uint);

  function setBurnableAt(
    address spender,
    uint certificateId,
    uint timestamp
  ) external;

  function burn(address spender, uint certificateId) external;

  function split(uint certificateId, uint percentageSplit) external returns (uint);
}

File 21 of 34 : IPoolHedger.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ICollateralShort.sol";

interface IPoolHedger {
  function shortingInitialized() external view returns (bool);

  function shortId() external view returns (uint);

  function shortBuffer() external view returns (uint);

  function lastInteraction() external view returns (uint);

  function interactionDelay() external view returns (uint);

  function setShortBuffer(uint newShortBuffer) external;

  function setInteractionDelay(uint newInteractionDelay) external;

  function initShort() external;

  function reopenShort() external;

  function hedgeDelta() external;

  function getShortPosition(ICollateralShort short) external view returns (uint shortBalance, uint collateral);

  function getCurrentHedgedNetDelta() external view returns (int);

  function getValueQuote(ICollateralShort short, uint spotPrice) external view returns (uint value);
}

File 22 of 34 : IShortCollateral.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./IOptionMarket.sol";

interface IShortCollateral {
  function sendQuoteCollateral(address recipient, uint amount) external;

  function sendBaseCollateral(address recipient, uint amount) external;

  function sendToLP(uint amountBase, uint amountQuote) external;

  function processSettle(
    uint listingId,
    address receiver,
    IOptionMarket.TradeType tradeType,
    uint amount,
    uint strike,
    uint priceAtExpiry,
    uint listingToShortCallEthReturned
  ) external;
}

File 23 of 34 : ERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/introspection/ERC165.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./Address.sol";

/**
 *
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
  using SafeMath for uint;
  using Address for address;

  // Mapping from token ID to account balances
  mapping(uint => mapping(address => uint)) private _balances;

  // Mapping from account to operator approvals
  mapping(address => mapping(address => bool)) private _operatorApprovals;

  // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
  string private _uri;

  /*
   *     bytes4(keccak256('balanceOf(address,uint256)')) == 0x00fdd58e
   *     bytes4(keccak256('balanceOfBatch(address[],uint256[])')) == 0x4e1273f4
   *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
   *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
   *     bytes4(keccak256('safeTransferFrom(address,address,uint256,uint256,bytes)')) == 0xf242432a
   *     bytes4(keccak256('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)')) == 0x2eb2c2d6
   *
   *     => 0x00fdd58e ^ 0x4e1273f4 ^ 0xa22cb465 ^
   *        0xe985e9c5 ^ 0xf242432a ^ 0x2eb2c2d6 == 0xd9b67a26
   */
  bytes4 private constant _INTERFACE_ID_ERC1155 = 0xd9b67a26;

  /*
   *     bytes4(keccak256('uri(uint256)')) == 0x0e89341c
   */
  bytes4 private constant _INTERFACE_ID_ERC1155_METADATA_URI = 0x0e89341c;

  /**
   * @dev See {_setURI}.
   */
  constructor(string memory uri_) {
    _setURI(uri_);

    // register the supported interfaces to conform to ERC1155 via ERC165
    _registerInterface(_INTERFACE_ID_ERC1155);

    // register the supported interfaces to conform to ERC1155MetadataURI via ERC165
    _registerInterface(_INTERFACE_ID_ERC1155_METADATA_URI);
  }

  /**
   * @dev See {IERC1155MetadataURI-uri}.
   *
   * This implementation returns the same URI for *all* token types. It relies
   * on the token type ID substitution mechanism
   * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
   *
   * Clients calling this function must replace the `\{id\}` substring with the
   * actual token type ID.
   */
  function uri(uint) external view virtual override returns (string memory) {
    return _uri;
  }

  /**
   * @dev See {IERC1155-balanceOf}.
   *
   * Requirements:
   *
   * - `account` cannot be the zero address.
   */
  function balanceOf(address account, uint id) public view virtual override returns (uint) {
    require(account != address(0), "ERC1155: balance query for the zero address");
    return _balances[id][account];
  }

  /**
   * @dev See {IERC1155-balanceOfBatch}.
   *
   * Requirements:
   *
   * - `accounts` and `ids` must have the same length.
   */
  function balanceOfBatch(address[] memory accounts, uint[] memory ids)
    public
    view
    virtual
    override
    returns (uint[] memory)
  {
    require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

    uint[] memory batchBalances = new uint[](accounts.length);

    for (uint i = 0; i < accounts.length; ++i) {
      batchBalances[i] = balanceOf(accounts[i], ids[i]);
    }

    return batchBalances;
  }

  /**
   * @dev See {IERC1155-setApprovalForAll}.
   */
  function setApprovalForAll(address operator, bool approved) public virtual override {
    require(_msgSender() != operator, "ERC1155: setting approval status for self");

    _operatorApprovals[_msgSender()][operator] = approved;
    emit ApprovalForAll(_msgSender(), operator, approved);
  }

  /**
   * @dev See {IERC1155-isApprovedForAll}.
   */
  function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
    return _operatorApprovals[account][operator];
  }

  /**
   * @dev See {IERC1155-safeTransferFrom}.
   */
  function safeTransferFrom(
    address from,
    address to,
    uint id,
    uint amount,
    bytes memory data
  ) public virtual override {
    require(to != address(0), "ERC1155: transfer to the zero address");
    require(from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not owner nor approved");

    address operator = _msgSender();

    _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data);

    _balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
    _balances[id][to] = _balances[id][to].add(amount);

    emit TransferSingle(operator, from, to, id, amount);
  }

  /**
   * @dev See {IERC1155-safeBatchTransferFrom}.
   */
  function safeBatchTransferFrom(
    address from,
    address to,
    uint[] memory ids,
    uint[] memory amounts,
    bytes memory data
  ) public virtual override {
    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
    require(to != address(0), "ERC1155: transfer to the zero address");
    require(
      from == _msgSender() || isApprovedForAll(from, _msgSender()),
      "ERC1155: transfer caller is not owner nor approved"
    );

    address operator = _msgSender();

    _beforeTokenTransfer(operator, from, to, ids, amounts, data);

    for (uint i = 0; i < ids.length; ++i) {
      uint id = ids[i];
      uint amount = amounts[i];

      _balances[id][from] = _balances[id][from].sub(amount, "ERC1155: insufficient balance for transfer");
      _balances[id][to] = _balances[id][to].add(amount);
    }

    emit TransferBatch(operator, from, to, ids, amounts);
  }

  /**
   * @dev Sets a new URI for all token types, by relying on the token type ID
   * substitution mechanism
   * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
   *
   * By this mechanism, any occurrence of the `\{id\}` substring in either the
   * URI or any of the amounts in the JSON file at said URI will be replaced by
   * clients with the token type ID.
   *
   * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
   * interpreted by clients as
   * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
   * for token type ID 0x4cce0.
   *
   * See {uri}.
   *
   * Because these URIs cannot be meaningfully represented by the {URI} event,
   * this function emits no events.
   */
  function _setURI(string memory newuri) internal virtual {
    _uri = newuri;
  }

  /**
   * @dev Creates `amount` tokens of token type `id`, and assigns them to `account`.
   *
   * Emits a {TransferSingle} event.
   *
   * Requirements:
   *
   * - `account` cannot be the zero address.
   * - If `account` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
   * acceptance magic value.
   */
  function _mint(
    address account,
    uint id,
    uint amount,
    bytes memory data
  ) internal virtual {
    require(account != address(0), "ERC1155: mint to the zero address");

    address operator = _msgSender();

    _beforeTokenTransfer(operator, address(0), account, _asSingletonArray(id), _asSingletonArray(amount), data);

    _balances[id][account] = _balances[id][account].add(amount);
    emit TransferSingle(operator, address(0), account, id, amount);
  }

  /**
   * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
   *
   * Requirements:
   *
   * - `ids` and `amounts` must have the same length.
   * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
   * acceptance magic value.
   */
  function _mintBatch(
    address to,
    uint[] memory ids,
    uint[] memory amounts,
    bytes memory data
  ) internal virtual {
    require(to != address(0), "ERC1155: mint to the zero address");
    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

    address operator = _msgSender();

    _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

    for (uint i = 0; i < ids.length; i++) {
      _balances[ids[i]][to] = amounts[i].add(_balances[ids[i]][to]);
    }

    emit TransferBatch(operator, address(0), to, ids, amounts);
  }

  /**
   * @dev Destroys `amount` tokens of token type `id` from `account`
   *
   * Requirements:
   *
   * - `account` cannot be the zero address.
   * - `account` must have at least `amount` tokens of token type `id`.
   */
  function _burn(
    address account,
    uint id,
    uint amount
  ) internal virtual {
    require(account != address(0), "ERC1155: burn from the zero address");

    address operator = _msgSender();

    _beforeTokenTransfer(operator, account, address(0), _asSingletonArray(id), _asSingletonArray(amount), "");

    _balances[id][account] = _balances[id][account].sub(amount, "ERC1155: burn amount exceeds balance");

    emit TransferSingle(operator, account, address(0), id, amount);
  }

  /**
   * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
   *
   * Requirements:
   *
   * - `ids` and `amounts` must have the same length.
   */
  function _burnBatch(
    address account,
    uint[] memory ids,
    uint[] memory amounts
  ) internal virtual {
    require(account != address(0), "ERC1155: burn from the zero address");
    require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

    address operator = _msgSender();

    _beforeTokenTransfer(operator, account, address(0), ids, amounts, "");

    for (uint i = 0; i < ids.length; i++) {
      _balances[ids[i]][account] = _balances[ids[i]][account].sub(amounts[i], "ERC1155: burn amount exceeds balance");
    }

    emit TransferBatch(operator, account, address(0), ids, amounts);
  }

  /**
   * @dev Hook that is called before any token transfer. This includes minting
   * and burning, as well as batched variants.
   *
   * The same hook is called on both single and batched variants. For single
   * transfers, the length of the `id` and `amount` arrays will be 1.
   *
   * Calling conditions (for each `id` and `amount` pair):
   *
   * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
   * of token type `id` will be  transferred to `to`.
   * - When `from` is zero, `amount` tokens of token type `id` will be minted
   * for `to`.
   * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
   * will be burned.
   * - `from` and `to` are never both zero.
   * - `ids` and `amounts` have the same, non-zero length.
   *
   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
   */
  function _beforeTokenTransfer(
    address operator,
    address from,
    address to,
    uint[] memory ids,
    uint[] memory amounts,
    bytes memory data
  ) internal virtual {}

  function _asSingletonArray(uint element) private pure returns (uint[] memory) {
    uint[] memory array = new uint[](1);
    array[0] = element;

    return array;
  }
}

File 24 of 34 : IERC1155.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

import "../../introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

File 25 of 34 : IERC1155MetadataURI.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

import "./IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

File 26 of 34 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../../introspection/IERC165.sol";

/**
 * _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {

    /**
        @dev Handles the receipt of a single ERC1155 token type. This function is
        called at the end of a `safeTransferFrom` after the balance has been updated.
        To accept the transfer, this must return
        `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
        (i.e. 0xf23a6e61, or its own function selector).
        @param operator The address which initiated the transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param id The ID of the token being transferred
        @param value The amount of tokens being transferred
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
    */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    )
        external
        returns(bytes4);

    /**
        @dev Handles the receipt of a multiple ERC1155 token types. This function
        is called at the end of a `safeBatchTransferFrom` after the balances have
        been updated. To accept the transfer(s), this must return
        `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
        (i.e. 0xbc197c81, or its own function selector).
        @param operator The address which initiated the batch transfer (i.e. msg.sender)
        @param from The address which previously owned the token
        @param ids An array containing ids of each token being transferred (order and length must match values array)
        @param values An array containing amounts of each token being transferred (order and length must match ids array)
        @param data Additional data with no specified format
        @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
    */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    )
        external
        returns(bytes4);
}

File 27 of 34 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
abstract contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () internal {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal virtual {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

File 28 of 34 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.7.6;

/**
 * @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
   * ====
   */
  function isContract(address account) internal view returns (bool) {
    // This method relies on extcodesize, which returns 0 for contracts in
    // construction, since the code is only stored at the end of the
    // constructor execution.

    uint size;
    // solhint-disable-next-line no-inline-assembly
    assembly {
      size := extcodesize(account)
    }
    return size > 0;
  }

  /**
   * @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 functionCall(target, data, "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 functionCallWithoutValue(target, data, 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 functionCallWithoutValue(address target, bytes memory data) internal returns (bytes memory) {
    return functionCallWithoutValue(target, data, "Address: low-level call with value failed");
  }

  /**
   * @dev Same as {xref-Address-functionCallWithoutValue-address-bytes-uint256-}[`functionCallWithoutValue`], but
   * with `errorMessage` as a fallback revert reason when `target` reverts.
   *
   * _Available since v3.1._
   */
  function functionCallWithoutValue(
    address target,
    bytes memory data,
    string memory errorMessage
  ) internal returns (bytes memory) {
    require(isContract(target), "Address: call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.call(data);
    return _verifyCallResult(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) {
    require(isContract(target), "Address: static call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.staticcall(data);
    return _verifyCallResult(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) {
    require(isContract(target), "Address: delegate call to non-contract");

    // solhint-disable-next-line avoid-low-level-calls
    (bool success, bytes memory returndata) = target.delegatecall(data);
    return _verifyCallResult(success, returndata, errorMessage);
  }

  function _verifyCallResult(
    bool success,
    bytes memory returndata,
    string memory errorMessage
  ) private pure returns (bytes memory) {
    if (success) {
      return returndata;
    } else {
      // 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

        // solhint-disable-next-line no-inline-assembly
        assembly {
          let returndata_size := mload(returndata)
          revert(add(32, returndata), returndata_size)
        }
      } else {
        revert(errorMessage);
      }
    }
  }
}

File 29 of 34 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 30 of 34 : SignedSafeDecimalMath.sol
//SPDX-License-Identifier: MIT
//MIT License
//
//Copyright (c) 2019 Synthetix
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.

pragma solidity ^0.7.6;

// Libraries
import "@openzeppelin/contracts/math/SignedSafeMath.sol";

// https://docs.synthetix.io/contracts/source/libraries/safedecimalmath
library SignedSafeDecimalMath {
  using SignedSafeMath for int;

  /* Number of decimal places in the representations. */
  uint8 public constant decimals = 18;
  uint8 public constant highPrecisionDecimals = 27;

  /* The number representing 1.0. */
  int public constant UNIT = int(10**uint(decimals));

  /* The number representing 1.0 for higher fidelity numbers. */
  int public constant PRECISE_UNIT = int(10**uint(highPrecisionDecimals));
  int private constant UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR = int(10**uint(highPrecisionDecimals - decimals));

  /**
   * @return Provides an interface to UNIT.
   */
  function unit() external pure returns (int) {
    return UNIT;
  }

  /**
   * @return Provides an interface to PRECISE_UNIT.
   */
  function preciseUnit() external pure returns (int) {
    return PRECISE_UNIT;
  }

  /**
   * @return The result of multiplying x and y, interpreting the operands as fixed-point
   * decimals.
   *
   * @dev A unit factor is divided out after the product of x and y is evaluated,
   * so that product must be less than 2**256. As this is an integer division,
   * the internal division always rounds down. This helps save on gas. Rounding
   * is more expensive on gas.
   */
  function multiplyDecimal(int x, int y) internal pure returns (int) {
    /* Divide by UNIT to remove the extra factor introduced by the product. */
    return x.mul(y) / UNIT;
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of the specified precision unit.
   *
   * @dev The operands should be in the form of a the specified unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function _multiplyDecimalRound(
    int x,
    int y,
    int precisionUnit
  ) private pure returns (int) {
    /* Divide by UNIT to remove the extra factor introduced by the product. */
    int quotientTimesTen = x.mul(y) / (precisionUnit / 10);

    if (quotientTimesTen % 10 >= 5) {
      quotientTimesTen += 10;
    }

    return quotientTimesTen / 10;
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of a precise unit.
   *
   * @dev The operands should be in the precise unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function multiplyDecimalRoundPrecise(int x, int y) internal pure returns (int) {
    return _multiplyDecimalRound(x, y, PRECISE_UNIT);
  }

  /**
   * @return The result of safely multiplying x and y, interpreting the operands
   * as fixed-point decimals of a standard unit.
   *
   * @dev The operands should be in the standard unit factor which will be
   * divided out after the product of x and y is evaluated, so that product must be
   * less than 2**256.
   *
   * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
   * Rounding is useful when you need to retain fidelity for small decimal numbers
   * (eg. small fractions or percentages).
   */
  function multiplyDecimalRound(int x, int y) internal pure returns (int) {
    return _multiplyDecimalRound(x, y, UNIT);
  }

  /**
   * @return The result of safely dividing x and y. The return value is a high
   * precision decimal.
   *
   * @dev y is divided after the product of x and the standard precision unit
   * is evaluated, so the product of x and UNIT must be less than 2**256. As
   * this is an integer division, the result is always rounded down.
   * This helps save on gas. Rounding is more expensive on gas.
   */
  function divideDecimal(int x, int y) internal pure returns (int) {
    /* Reintroduce the UNIT factor that will be divided out by y. */
    return x.mul(UNIT).div(y);
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * decimal in the precision unit specified in the parameter.
   *
   * @dev y is divided after the product of x and the specified precision unit
   * is evaluated, so the product of x and the specified precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function _divideDecimalRound(
    int x,
    int y,
    int precisionUnit
  ) private pure returns (int) {
    int resultTimesTen = x.mul(precisionUnit * 10).div(y);

    if (resultTimesTen % 10 >= 5) {
      resultTimesTen += 10;
    }

    return resultTimesTen / 10;
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * standard precision decimal.
   *
   * @dev y is divided after the product of x and the standard precision unit
   * is evaluated, so the product of x and the standard precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function divideDecimalRound(int x, int y) internal pure returns (int) {
    return _divideDecimalRound(x, y, UNIT);
  }

  /**
   * @return The result of safely dividing x and y. The return value is as a rounded
   * high precision decimal.
   *
   * @dev y is divided after the product of x and the high precision unit
   * is evaluated, so the product of x and the high precision unit must
   * be less than 2**256. The result is rounded to the nearest increment.
   */
  function divideDecimalRoundPrecise(int x, int y) internal pure returns (int) {
    return _divideDecimalRound(x, y, PRECISE_UNIT);
  }

  /**
   * @dev Convert a standard decimal representation to a high precision one.
   */
  function decimalToPreciseDecimal(int i) internal pure returns (int) {
    return i.mul(UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR);
  }

  /**
   * @dev Convert a high precision decimal to a standard decimal representation.
   */
  function preciseDecimalToDecimal(int i) internal pure returns (int) {
    int quotientTimesTen = i / (UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR / 10);

    if (quotientTimesTen % 10 >= 5) {
      quotientTimesTen += 10;
    }

    return quotientTimesTen / 10;
  }
}

File 31 of 34 : IBlackScholes.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

interface IBlackScholes {
  struct PricesDeltaStdVega {
    uint callPrice;
    uint putPrice;
    int callDelta;
    int putDelta;
    uint stdVega;
  }

  function abs(int x) external pure returns (uint);

  function exp(uint x) external pure returns (uint);

  function exp(int x) external pure returns (uint);

  function sqrt(uint x) external pure returns (uint y);

  function optionPrices(
    uint timeToExpirySec,
    uint volatilityDecimal,
    uint spotDecimal,
    uint strikeDecimal,
    int rateDecimal
  ) external pure returns (uint call, uint put);

  function pricesDeltaStdVega(
    uint timeToExpirySec,
    uint volatilityDecimal,
    uint spotDecimal,
    uint strikeDecimal,
    int rateDecimal
  ) external pure returns (PricesDeltaStdVega memory);
}

File 32 of 34 : IOptionMarketPricer.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ILyraGlobals.sol";
import "./IOptionMarket.sol";

interface IOptionMarketPricer {
  struct Pricing {
    uint optionPrice;
    int preTradeAmmNetStdVega;
    int postTradeAmmNetStdVega;
    int callDelta;
  }

  function ivImpactForTrade(
    IOptionMarket.OptionListing memory listing,
    IOptionMarket.Trade memory trade,
    ILyraGlobals.PricingGlobals memory pricingGlobals,
    uint boardBaseIv
  ) external pure returns (uint, uint);

  function updateCacheAndGetTotalCost(
    IOptionMarket.OptionListing memory listing,
    IOptionMarket.Trade memory trade,
    ILyraGlobals.PricingGlobals memory pricingGlobals,
    uint boardBaseIv
  )
    external
    returns (
      uint totalCost,
      uint newBaseIv,
      uint newSkew
    );

  function getPremium(
    IOptionMarket.Trade memory trade,
    Pricing memory pricing,
    ILyraGlobals.PricingGlobals memory pricingGlobals
  ) external pure returns (uint premium);

  function getVegaUtil(
    IOptionMarket.Trade memory trade,
    Pricing memory pricing,
    ILyraGlobals.PricingGlobals memory pricingGlobals
  ) external pure returns (uint vegaUtil);

  function getFee(
    ILyraGlobals.PricingGlobals memory pricingGlobals,
    uint amount,
    uint optionPrice,
    uint vegaUtil
  ) external pure returns (uint fee);
}

File 33 of 34 : IOptionGreekCache.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;

import "./ILyraGlobals.sol";
import "./IOptionMarketPricer.sol";

interface IOptionGreekCache {
  struct OptionListingCache {
    uint id;
    uint strike;
    uint skew;
    uint boardId;
    int callDelta;
    int putDelta;
    uint stdVega;
    int callExposure; // long - short
    int putExposure; // long - short
    uint updatedAt;
    uint updatedAtPrice;
  }

  struct OptionBoardCache {
    uint id;
    uint expiry;
    uint iv;
    uint[] listings;
    uint minUpdatedAt; // This should be the minimum value of all the listings
    uint minUpdatedAtPrice;
    uint maxUpdatedAtPrice;
    int netDelta;
    int netStdVega;
  }

  struct GlobalCache {
    int netDelta;
    int netStdVega;
    uint minUpdatedAt; // This should be the minimum value of all the listings
    uint minUpdatedAtPrice;
    uint maxUpdatedAtPrice;
    uint minExpiryTimestamp;
  }

  function MAX_LISTINGS_PER_BOARD() external view returns (uint);

  function staleUpdateDuration() external view returns (uint);

  function priceScalingPeriod() external view returns (uint);

  function maxAcceptablePercent() external view returns (uint);

  function minAcceptablePercent() external view returns (uint);

  function liveBoards(uint) external view returns (uint);

  function listingCaches(uint)
    external
    view
    returns (
      uint id,
      uint strike,
      uint skew,
      uint boardId,
      int callDelta,
      int putDelta,
      uint stdVega,
      int callExposure,
      int putExposure,
      uint updatedAt,
      uint updatedAtPrice
    );

  function boardCaches(uint)
    external
    view
    returns (
      uint id,
      uint expiry,
      uint iv,
      uint minUpdatedAt,
      uint minUpdatedAtPrice,
      uint maxUpdatedAtPrice,
      int netDelta,
      int netStdVega
    );

  function globalCache()
    external
    view
    returns (
      int netDelta,
      int netStdVega,
      uint minUpdatedAt,
      uint minUpdatedAtPrice,
      uint maxUpdatedAtPrice,
      uint minExpiryTimestamp
    );

  function setStaleCacheParameters(
    uint _staleUpdateDuration,
    uint _priceScalingPeriod,
    uint _maxAcceptablePercent,
    uint _minAcceptablePercent
  ) external;

  function addBoard(uint boardId) external;

  function removeBoard(uint boardId) external;

  function setBoardIv(uint boardId, uint newIv) external;

  function setListingSkew(uint listingId, uint newSkew) external;

  function addListingToBoard(uint boardId, uint listingId) external;

  function updateAllStaleBoards() external returns (int);

  function updateBoardCachedGreeks(uint boardCacheId) external;

  function updateListingCacheAndGetPrice(
    ILyraGlobals.GreekCacheGlobals memory greekCacheGlobals,
    uint listingCacheId,
    int newCallExposure,
    int newPutExposure,
    uint iv,
    uint skew
  ) external returns (IOptionMarketPricer.Pricing memory);

  function isGlobalCacheStale() external view returns (bool);

  function isBoardCacheStale(uint boardCacheId) external view returns (bool);

  function getGlobalNetDelta() external view returns (int);
}

File 34 of 34 : SignedSafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @title SignedSafeMath
 * @dev Signed math operations with safety checks that revert on error.
 */
library SignedSafeMath {
    int256 constant private _INT256_MIN = -2**255;

    /**
     * @dev Returns the multiplication of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(int256 a, int256 b) internal pure returns (int256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");

        int256 c = a * b;
        require(c / a == b, "SignedSafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two signed integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(int256 a, int256 b) internal pure returns (int256) {
        require(b != 0, "SignedSafeMath: division by zero");
        require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");

        int256 c = a / b;

        return c;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a - b;
        require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");

        return c;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        int256 c = a + b;
        require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");

        return c;
    }
}

Settings
{
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boardId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseIv","type":"uint256"}],"name":"BoardBaseIvSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boardId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiry","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseIv","type":"uint256"}],"name":"BoardCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boardId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"frozen","type":"bool"}],"name":"BoardFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boardId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalUserLongProfitQuote","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBoardLongCallCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBoardLongPutCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAMMShortCallProfitBase","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAMMShortPutProfitQuote","type":"uint256"}],"name":"BoardLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"boardId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"listingId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"strike","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"skew","type":"uint256"}],"name":"ListingAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"listingId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"skew","type":"uint256"}],"name":"ListingSkewSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint256","name":"listingId","type":"uint256"},{"indexed":true,"internalType":"enum IOptionMarket.TradeType","name":"tradeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCost","type":"uint256"}],"name":"PositionClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"trader","type":"address"},{"indexed":true,"internalType":"uint256","name":"listingId","type":"uint256"},{"indexed":true,"internalType":"enum IOptionMarket.TradeType","name":"tradeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalCost","type":"uint256"}],"name":"PositionOpened","type":"event"},{"inputs":[{"internalType":"uint256","name":"boardId","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"skew","type":"uint256"}],"name":"addListingToBoard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"boardToPriceAtExpiry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_listingId","type":"uint256"},{"internalType":"enum IOptionMarket.TradeType","name":"tradeType","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"closePosition","outputs":[{"internalType":"uint256","name":"totalCost","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"baseIV","type":"uint256"},{"internalType":"uint256[]","name":"strikes","type":"uint256[]"},{"internalType":"uint256[]","name":"skews","type":"uint256[]"}],"name":"createOptionBoard","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"boardId","type":"uint256"}],"name":"getBoardListings","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiveBoards","outputs":[{"internalType":"uint256[]","name":"_liveBoards","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILyraGlobals","name":"_globals","type":"address"},{"internalType":"contract ILiquidityPool","name":"_liquidityPool","type":"address"},{"internalType":"contract IOptionMarketPricer","name":"_optionPricer","type":"address"},{"internalType":"contract IOptionGreekCache","name":"_greekCache","type":"address"},{"internalType":"contract IShortCollateral","name":"_shortCollateral","type":"address"},{"internalType":"contract IOptionToken","name":"_optionToken","type":"address"},{"internalType":"contract IERC20","name":"_quoteAsset","type":"address"},{"internalType":"contract IERC20","name":"_baseAsset","type":"address"},{"internalType":"string[]","name":"_errorMessages","type":"string[]"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"boardId","type":"uint256"}],"name":"liquidateExpiredBoard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"listingToBaseReturnedRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxExpiryTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_listingId","type":"uint256"},{"internalType":"enum IOptionMarket.TradeType","name":"tradeType","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"openPosition","outputs":[{"internalType":"uint256","name":"totalCost","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"optionBoards","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"iv","type":"uint256"},{"internalType":"bool","name":"frozen","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"optionListings","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"strike","type":"uint256"},{"internalType":"uint256","name":"skew","type":"uint256"},{"internalType":"uint256","name":"longCall","type":"uint256"},{"internalType":"uint256","name":"shortCall","type":"uint256"},{"internalType":"uint256","name":"longPut","type":"uint256"},{"internalType":"uint256","name":"shortPut","type":"uint256"},{"internalType":"uint256","name":"boardId","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"boardId","type":"uint256"},{"internalType":"uint256","name":"baseIv","type":"uint256"}],"name":"setBoardBaseIv","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"boardId","type":"uint256"},{"internalType":"bool","name":"frozen","type":"bool"}],"name":"setBoardFrozen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"listingId","type":"uint256"},{"internalType":"uint256","name":"skew","type":"uint256"}],"name":"setListingSkew","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"listingId","type":"uint256"},{"internalType":"enum IOptionMarket.TradeType","name":"tradeType","type":"uint8"}],"name":"settleOptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526000600960146101000a8162000019620000d1565b8160ff021916908315150217906200003062000136565b5050506001600a6200004162000136565b50506001600b6200005162000136565b50503480156200006b57600080620000686200019d565b50505b505a620000776200020d565b600960006101000a816200008a620000d1565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790620000c862000136565b5050506200026c565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620001315760008183015260208101905062000115565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b604081101562000198576000818301526020810190506200017c565b505050565b632a2a7adb598160e01b8152600481016020815285602082015260005b86811015620001da578086015181604084010152602081019050620001ba565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b604081101562000267576000818301526020810190506200024b565b505050565b6163fc806200027c6000396000f3fe6080604052348015610019576000806100166144f0565b50505b506004361061011f5760003560e01c80639b805da8116100ab578063d1e9e8111161007a578063d1e9e8111461031f578063d6ef9be31461034f578063dd2f0d361461036b578063f2333a581461039b578063f2fde38b146103b75761011f565b80639b805da814610287578063a92e61eb146102b7578063a9c9d125146102d3578063c39eeefb146102ef5761011f565b806335359675116100f257806335359675146101ba57806342a47e13146101ed5780637e7088eb14610209578063862a33941461022757806397dd2524146102575761011f565b806318cc7e861461012d5780632eb6534f146101495780632f4aff0e146101675780633289cf8f14610183575b60008061012a6144f0565b50505b610147600480360381019061014291906152c3565b6103d3565b005b610151610598565b60405161015e9190615c57565b60405180910390f35b610181600480360381019061017c91906153b6565b6105a5565b005b61019d6004803603810190610198919061517d565b610710565b6040516101b1989796959493929190615e3b565b60405180910390f35b6101d460048036038101906101cf919061517d565b610790565b6040516101e49493929190615da3565b60405180910390f35b6102076004803603810190610202919061517d565b6107e9565b005b610211610ab0565b60405161021e9190615b6f565b60405180910390f35b610241600480360381019061023c919061526b565b610b76565b60405161024e9190615c57565b60405180910390f35b610271600480360381019061026c919061526b565b611990565b60405161027e9190615c57565b60405180910390f35b6102a1600480360381019061029c919061517d565b6126ce565b6040516102ae9190615c57565b60405180910390f35b6102d160048036038101906102cc9190615226565b6126ed565b005b6102ed60048036038101906102e891906151e1565b6129f2565b005b6103096004803603810190610304919061517d565b612b06565b6040516103169190615b6f565b60405180910390f35b6103396004803603810190610334919061517d565b612c10565b6040516103469190615c57565b60405180910390f35b610369600480360381019061036491906152c3565b612c2f565b005b61038560048036038101906103809190615308565b612ed0565b6040516103929190615c57565b60405180910390f35b6103b560048036038101906103b09190614fce565b6132a2565b005b6103d160048036038101906103cc9190614f6a565b61366b565b005b61043d5a6103df61455e565b73ffffffffffffffffffffffffffffffffffffffff1660096000906104026145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e6000848152602001908152602001600020905061048d83826000016104646145bb565b14801561048657508160030160009061047b6145bb565b906101000a900460ff165b60026137e7565b8181600201819061049c61461e565b50505060036000906104ac6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663551c855584846040518363ffffffff1660e01b8152600401610503929190615d43565b60006040518083038160008780610518614683565b15801561052d5760008061052a6144f0565b50505b505a6105376146e6565b505050505050158015610557573d6000803e3d60006105546144f0565b50505b50505050827f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce8818360405161058b9190615c57565b60405180910390a2505050565b600d6105a26145bb565b81565b61060f5a6105b161455e565b73ffffffffffffffffffffffffffffffffffffffff1660096000906105d46145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e6000858152602001908152602001600020905061063e84826000016106366145bb565b1460016137e7565b600061064b858585613855565b9050600360009061065a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d59f359386836040518363ffffffff1660e01b81526004016106b1929190615d43565b600060405180830381600087806106c6614683565b1580156106db576000806106d86144f0565b50505b505a6106e56146e6565b505050505050158015610705573d6000803e3d60006107026144f0565b50505b505050505050505050565b600f602052806000526040600020600091509050806000016107306145bb565b908060010161073d6145bb565b908060020161074a6145bb565b90806003016107576145bb565b90806004016107646145bb565b90806005016107716145bb565b908060060161077e6145bb565b908060070161078b6145bb565b905088565b600e602052806000526040600020600091509050806000016107b06145bb565b90806001016107bd6145bb565b90806002016107ca6145bb565b90806003016000906107da6145bb565b906101000a900460ff16905084565b6000600e60008381526020019081526020016000206040518060a0016040529081600082016108166145bb565b8152602001600182016108276145bb565b8152602001600282016108386145bb565b81526020016003820160009061084c6145bb565b906101000a900460ff16151515158152602001600482018061086c6145bb565b806020026020016040519081016040528092919081815260200182806108906145bb565b80156108c257602002820191906000526020600020905b816108b06145bb565b815260200190600101908083116108a7575b50505050508152505090506108e75a6108d96147e6565b82602001511115600b6137e7565b6000805b600c806108f66145bb565b90508110156109dc5783600c828161090c6145bb565b811061091457fe5b906000526020600020016109266145bb565b14156109cf57600c6001600c8061093b6145bb565b905003816109476145bb565b811061094f57fe5b906000526020600020016109616145bb565b600c828161096d6145bb565b811061097557fe5b90600052602060002001819061098961461e565b505050600c806109976145bb565b8061099e57fe5b600190038181906000526020600020016000906109b961461e565b5050906109c461461e565b5050600191506109dc565b80806001019150506108eb565b506109e881600c6137e7565b6109f182613a08565b60036000906109fe6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dba5082a846040518263ffffffff1660e01b8152600401610a539190615c57565b60006040518083038160008780610a68614683565b158015610a7d57600080610a7a6144f0565b50505b505a610a876146e6565b505050505050158015610aa7573d6000803e3d6000610aa46144f0565b50505b50505050505050565b6060600c80610abd6145bb565b905067ffffffffffffffff81118015610ade57600080610adb6144f0565b50505b50604051908082528060200260200182016040528015610b0d5781602001602082028036833780820191505090505b50905060005b600c80610b1e6145bb565b9050811015610b7257600c8181610b336145bb565b8110610b3b57fe5b90600052602060002001610b4d6145bb565b828281518110610b5957fe5b6020026020010181815250508080600101915050610b13565b5090565b6000610ba8600083138015610ba15750836003811115610b9257fe5b600380811115610b9e57fe5b10155b60076137e7565b6000806003811115610bb657fe5b846003811115610bc257fe5b1480610be4575060026003811115610bd657fe5b846003811115610be257fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701610c0f6145bb565b81526020019081526020016000209050600080600080600090610c306145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b125a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015610cc157600081830152602081019050610ca7565b505050886040518363ffffffff1660e01b8152600401610ce2929190615af4565b610240604051808303818680610cf6614683565b158015610d0b57600080610d086144f0565b50505b505a610d15614843565b5050505050158015610d34573d6000803e3d6000610d316144f0565b50505b505050506040513d601f19601f82011682018060405250810190610d589190615122565b925092509250610da284600301600090610d706145bb565b906101000a900460ff16158015610d9b575084600101610d8e6145bb565b825a610d986147e6565b01105b60086137e7565b60006040518060a0016040528088151581526020018a8152602001610de688600201610dcc6145bb565b88600201610dd86145bb565b61402b90919063ffffffff16565b815260200186600101610df76145bb565b81526020016001600090610e096145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401610e68929190615ce1565b6080604051808303818680610e7b614683565b158015610e9057600080610e8d6144f0565b50505b505a610e9a614843565b5050505050158015610eb9573d6000803e3d6000610eb66144f0565b50505b505050506040513d601f19601f82011682018060405250810190610edd91906150f0565b81525090506005600090610eef6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663156e29f65a610f3061455e565b8c6003811115610f3c57fe5b8e018c6040518463ffffffff1660e01b8152600401610f5d93929190615abd565b60006040518083038160008780610f72614683565b158015610f8757600080610f846144f0565b50505b505a610f916146e6565b505050505050158015610fb1573d6000803e3d6000610fae6144f0565b50505b5050505060006003811115610fc257fe5b8a6003811115610fce57fe5b141561100857610ff28987600301610fe46145bb565b61404790919063ffffffff16565b86600301819061100061461e565b5050506110e0565b6001600381111561101557fe5b8a600381111561102157fe5b141561105b5761104589876004016110376145bb565b61404790919063ffffffff16565b86600401819061105361461e565b5050506110df565b6002600381111561106857fe5b8a600381111561107457fe5b14156110ae57611098898760050161108a6145bb565b61404790919063ffffffff16565b8660050181906110a661461e565b5050506110de565b6110cc89876006016110be6145bb565b61404790919063ffffffff16565b8660060181906110da61461e565b5050505b5b5b6110ec868683876140d8565b9750600060038111156110fb57fe5b8a600381111561110757fe5b14156112ed57600160009061111a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a00dad228a8584608001516040518463ffffffff1660e01b815260040161117793929190615d0a565b6000604051808303816000878061118c614683565b1580156111a15760008061119e6144f0565b50505b505a6111ab6146e6565b5050505050501580156111cb573d6000803e3d60006111c86144f0565b50505b505050506112e860066000906111df6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61122061455e565b600160009061122d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b815260040161126993929190615a5d565b6020604051808303816000878061127e614683565b158015611293576000806112906144f0565b50505b505a61129d6146e6565b5050505050501580156112bd573d6000803e3d60006112ba6144f0565b50505b505050506040513d601f19601f820116820180604052508101906112e19190614f9c565b60096137e7565b61191d565b600260038111156112fa57fe5b8a600381111561130657fe5b141561150b5760016000906113196145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f238ff5c61136f886001016113606145bb565b8c61428790919063ffffffff16565b8360800151600001516040518363ffffffff1660e01b8152600401611395929190615d43565b600060405180830381600087806113aa614683565b1580156113bf576000806113bc6144f0565b50505b505a6113c96146e6565b5050505050501580156113e9573d6000803e3d60006113e66144f0565b50505b5050505061150660066000906113fd6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61143e61455e565b600160009061144b6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b815260040161148793929190615a5d565b6020604051808303816000878061149c614683565b1580156114b1576000806114ae6144f0565b50505b505a6114bb6146e6565b5050505050501580156114db573d6000803e3d60006114d86144f0565b50505b505050506040513d601f19601f820116820180604052508101906114ff9190614f9c565b60096137e7565b61191c565b6001600381111561151857fe5b8a600381111561152457fe5b141561171657611643600760009061153a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61157b61455e565b60046000906115886145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168d6040518463ffffffff1660e01b81526004016115c493929190615a5d565b602060405180830381600087806115d9614683565b1580156115ee576000806115eb6144f0565b50505b505a6115f86146e6565b505050505050158015611618573d6000803e3d60006116156144f0565b50505b505050506040513d601f19601f8201168201806040525081019061163c9190614f9c565b600a6137e7565b60016000906116506145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61169161455e565b8a8460800151600001516040518463ffffffff1660e01b81526004016116b993929190615abd565b600060405180830381600087806116ce614683565b1580156116e3576000806116e06144f0565b50505b505a6116ed6146e6565b50505050505015801561170d573d6000803e3d600061170a6144f0565b50505b5050505061191b565b61184c60066000906117266145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61176761455e565b60046000906117746145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff166117af8b6001016117a06145bb565b8f61428790919063ffffffff16565b6040518463ffffffff1660e01b81526004016117cd93929190615a5d565b602060405180830381600087806117e2614683565b1580156117f7576000806117f46144f0565b50505b505a6118016146e6565b505050505050158015611821573d6000803e3d600061181e6144f0565b50505b505050506040513d601f19601f820116820180604052508101906118459190614f9c565b60096137e7565b60016000906118596145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61189a61455e565b8a8460800151600001516040518463ffffffff1660e01b81526004016118c293929190615abd565b600060405180830381600087806118d7614683565b1580156118ec576000806118e96144f0565b50505b505a6118f66146e6565b505050505050158015611916573d6000803e3d60006119136144f0565b50505b505050505b5b5b89600381111561192957fe5b8b5a61193361455e565b73ffffffffffffffffffffffffffffffffffffffff167f9a99f0d83ea15216987ecd4ed332a7338eafafe12db86adf283528ec63c02d3f8c8c60405161197a929190615d43565b60405180910390a4505050505050509392505050565b60006119c26000831380156119bb57508360038111156119ac57fe5b6003808111156119b857fe5b10155b60076137e7565b60008060038111156119d057fe5b8460038111156119dc57fe5b14806119fe5750600260038111156119f057fe5b8460038111156119fc57fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701611a296145bb565b81526020019081526020016000209050600080600080600090611a4a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b125a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015611adb57600081830152602081019050611ac1565b50505088156040518363ffffffff1660e01b8152600401611afd929190615af4565b610240604051808303818680611b11614683565b158015611b2657600080611b236144f0565b50505b505a611b30614843565b5050505050158015611b4f573d6000803e3d6000611b4c6144f0565b50505b505050506040513d601f19601f82011682018060405250810190611b739190615122565b925092509250611bbd84600301600090611b8b6145bb565b906101000a900460ff16158015611bb6575084600101611ba96145bb565b825a611bb36147e6565b01105b60086137e7565b60006040518060a001604052808815151581526020018a8152602001611c0288600201611be86145bb565b88600201611bf46145bb565b61402b90919063ffffffff16565b815260200186600101611c136145bb565b81526020016001600090611c256145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401611c84929190615ce1565b6080604051808303818680611c97614683565b158015611cac57600080611ca96144f0565b50505b505a611cb6614843565b5050505050158015611cd5573d6000803e3d6000611cd26144f0565b50505b505050506040513d601f19601f82011682018060405250810190611cf991906150f0565b81525090506005600090611d0b6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca5a611d4c61455e565b8c6003811115611d5857fe5b8e018c6040518463ffffffff1660e01b8152600401611d7993929190615abd565b60006040518083038160008780611d8e614683565b158015611da357600080611da06144f0565b50505b505a611dad6146e6565b505050505050158015611dcd573d6000803e3d6000611dca6144f0565b50505b5050505060006003811115611dde57fe5b8a6003811115611dea57fe5b1415611e2457611e0e8987600301611e006145bb565b6142b490919063ffffffff16565b866003018190611e1c61461e565b505050611efc565b60016003811115611e3157fe5b8a6003811115611e3d57fe5b1415611e7757611e618987600401611e536145bb565b6142b490919063ffffffff16565b866004018190611e6f61461e565b505050611efb565b60026003811115611e8457fe5b8a6003811115611e9057fe5b1415611eca57611eb48987600501611ea66145bb565b6142b490919063ffffffff16565b866005018190611ec261461e565b505050611efa565b611ee88987600601611eda6145bb565b6142b490919063ffffffff16565b866006018190611ef661461e565b5050505b5b5b611f08868683876140d8565b975060006003811115611f1757fe5b8a6003811115611f2357fe5b14156120b6576001600090611f366145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dc62c5818a6040518263ffffffff1660e01b8152600401611f8b9190615c57565b60006040518083038160008780611fa0614683565b158015611fb557600080611fb26144f0565b50505b505a611fbf6146e6565b505050505050158015611fdf573d6000803e3d6000611fdc6144f0565b50505b505050506001600090611ff06145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61203161455e565b8a8460800151600001516040518463ffffffff1660e01b815260040161205993929190615abd565b6000604051808303816000878061206e614683565b158015612083576000806120806144f0565b50505b505a61208d6146e6565b5050505050501580156120ad573d6000803e3d60006120aa6144f0565b50505b5050505061265b565b600260038111156120c357fe5b8a60038111156120cf57fe5b141561227f5760016000906120e26145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663db32232f612138886001016121296145bb565b8c61428790919063ffffffff16565b6040518263ffffffff1660e01b81526004016121549190615c57565b60006040518083038160008780612169614683565b15801561217e5760008061217b6144f0565b50505b505a6121886146e6565b5050505050501580156121a8573d6000803e3d60006121a56144f0565b50505b5050505060016000906121b96145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a6121fa61455e565b8a8460800151600001516040518463ffffffff1660e01b815260040161222293929190615abd565b60006040518083038160008780612237614683565b15801561224c576000806122496144f0565b50505b505a6122566146e6565b505050505050158015612276573d6000803e3d60006122736144f0565b50505b5050505061265a565b6001600381111561228c57fe5b8a600381111561229857fe5b14156124805760046000906122ab6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638b89a2425a6122ec61455e565b8b6040518363ffffffff1660e01b815260040161230a929190615a94565b6000604051808303816000878061231f614683565b158015612334576000806123316144f0565b50505b505a61233e6146e6565b50505050505015801561235e573d6000803e3d600061235b6144f0565b50505b5050505061247b60066000906123726145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a6123b361455e565b60016000906123c06145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b81526004016123fc93929190615a5d565b60206040518083038160008780612411614683565b158015612426576000806124236144f0565b50505b505a6124306146e6565b505050505050158015612450573d6000803e3d600061244d6144f0565b50505b505050506040513d601f19601f820116820180604052508101906124749190614f9c565b60096137e7565b612659565b600460009061248d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b3075a6124ce61455e565b6124fe8b6124f08b6001016124e16145bb565b8f61428790919063ffffffff16565b6142b490919063ffffffff16565b6040518363ffffffff1660e01b815260040161251b929190615a94565b60006040518083038160008780612530614683565b158015612545576000806125426144f0565b50505b505a61254f6146e6565b50505050505015801561256f573d6000803e3d600061256c6144f0565b50505b5050505060046000906125806145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b30760016000906125c56145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6040518363ffffffff1660e01b8152600401612600929190615b46565b60006040518083038160008780612615614683565b15801561262a576000806126276144f0565b50505b505a6126346146e6565b505050505050158015612654573d6000803e3d60006126516144f0565b50505b505050505b5b5b89600381111561266757fe5b8b5a61267161455e565b73ffffffffffffffffffffffffffffffffffffffff167f5a0cd91f269c536b8167c8fc4e2b859e3db49a3fb022e5db3e380a58975405848c8c6040516126b8929190615d43565b60405180910390a4505050505050509392505050565b60116020528060005260406000206000915090506126ea6145bb565b81565b600060056000906126fc6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1662fdd58e5a61273c61455e565b84600381111561274857fe5b86016040518363ffffffff1660e01b8152600401612767929190615a94565b602060405180830381868061277a614683565b15801561278f5760008061278c6144f0565b50505b505a612799614843565b50505050501580156127b8573d6000803e3d60006127b56144f0565b50505b505050506040513d601f19601f820116820180604052508101906127dc91906151af565b905060046000906127eb6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166337af0a28845a61282d61455e565b8585600f60008a815260200190815260200160002060010161284d6145bb565b60106000600f60008d815260200190815260200160002060070161286f6145bb565b81526020019081526020016000206128856145bb565b601160008c81526020019081526020016000206128a06145bb565b6040518863ffffffff1660e01b81526004016128c29796959493929190615c72565b600060405180830381600087806128d7614683565b1580156128ec576000806128e96144f0565b50505b505a6128f66146e6565b505050505050158015612916573d6000803e3d60006129136144f0565b50505b5050505060056000906129276145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca5a61296861455e565b84600381111561297457fe5b8601846040518463ffffffff1660e01b815260040161299593929190615abd565b600060405180830381600087806129aa614683565b1580156129bf576000806129bc6144f0565b50505b505a6129c96146e6565b5050505050501580156129e9573d6000803e3d60006129e66144f0565b50505b50505050505050565b612a5c5a6129fe61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612a216145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e60008481526020019081526020016000209050612a8b8382600001612a836145bb565b1460016137e7565b81600e600085815260200190815260200160002060030160006101000a81612ab16145bb565b8160ff02191690831515021790612ac661461e565b505050827fab7e756517bb425436c10403644a884802e0b2d5105f9f5386823b7c42ca5d5f83604051612af99190615b91565b60405180910390a2505050565b60606000600e600084815260200190815260200160002060040180612b296145bb565b905067ffffffffffffffff81118015612b4a57600080612b476144f0565b50505b50604051908082528060200260200182016040528015612b795781602001602082028036833780820191505090505b50905060005b600e600085815260200190815260200160002060040180612b9e6145bb565b9050811015612c0657600e60008581526020019081526020016000206004018181612bc76145bb565b8110612bcf57fe5b90600052602060002001612be16145bb565b828281518110612bed57fe5b6020026020010181815250508080600101915050612b7f565b5080915050919050565b6010602052806000526040600020600091509050612c2c6145bb565b81565b612c995a612c3b61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612c5e6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600f600084815260200190815260200160002090506000600e600083600701612cc26145bb565b81526020019081526020016000206040518060a001604052908160008201612ce86145bb565b815260200160018201612cf96145bb565b815260200160028201612d0a6145bb565b815260200160038201600090612d1e6145bb565b906101000a900460ff161515151581526020016004820180612d3e6145bb565b80602002602001604051908101604052809291908181526020018280612d626145bb565b8015612d9457602002820191906000526020600020905b81612d826145bb565b81526020019060010190808311612d79575b5050505050815250509050612dc48483600001612daf6145bb565b148015612dbd575081606001515b60036137e7565b82826002018190612dd361461e565b5050506003600090612de36145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d6ef9be385856040518363ffffffff1660e01b8152600401612e3a929190615d43565b60006040518083038160008780612e4f614683565b158015612e6457600080612e616144f0565b50505b505a612e6e6146e6565b505050505050158015612e8e573d6000803e3d6000612e8b6144f0565b50505b50505050837f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a95784604051612ec29190615c57565b60405180910390a250505050565b6000612f3c5a612ede61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612f016145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b612f5882518451148015612f51575060008451115b60046137e7565b612f82625c4900612f7a5a612f6b6147e6565b886142b490919063ffffffff16565b1060056137e7565b600d612f8c6145bb565b85111561308357612fad6000600c80612fa36145bb565b90501460066137e7565b6001600090612fba6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3964372600d612ffc6145bb565b876040518363ffffffff1660e01b815260040161301a929190615d43565b6000604051808303816000878061302f614683565b158015613044576000806130416144f0565b50505b505a61304e6146e6565b50505050505015801561306e573d6000803e3d600061306b6144f0565b50505b5050505084600d819061307f61461e565b5050505b6000600b6000816130926145bb565b809291906001019190506130a461461e565b5050905080600e600083815260200190815260200160002060000181906130c961461e565b50505085600e600083815260200190815260200160002060010181906130ed61461e565b50505084600e6000838152602001908152602001600020600201819061311161461e565b505050600c8190806001816131246145bb565b01808261312f61461e565b505080915050600190039060005260206000200160009091909190915061315461461e565b5050807f2eb8cef7f145b51f7d30c1ffc842e46161a2b1624415ce69192c99ec2106ad878787604051613188929190615d43565b60405180910390a260005b84518110156131db576131cd828683815181106131ac57fe5b60200260200101518684815181106131c057fe5b6020026020010151613855565b508080600101915050613193565b5060036000906131e96145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d5766f7826040518263ffffffff1660e01b815260040161323e9190615c57565b60006040518083038160008780613253614683565b158015613268576000806132656144f0565b50505b505a6132726146e6565b505050505050158015613292573d6000803e3d600061328f6144f0565b50505b5050505080915050949350505050565b60096014906132af6145bb565b906101000a900460ff1615613302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132f090615bce565b604051809103906132ff6144f0565b50505b886000806101000a816133136145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061334f61461e565b50505087600160006101000a816133646145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906133a061461e565b50505086600260006101000a816133b56145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906133f161461e565b50505085600360006101000a816134066145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061344261461e565b50505084600460006101000a816134576145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061349361461e565b50505083600560006101000a816134a86145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906134e461461e565b50505082600660006101000a816134f96145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061353561461e565b50505081600760006101000a8161354a6145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061358661461e565b505050600e8081111561359557fe5b8151146135e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016135ce90615bee565b604051809103906135dd6144f0565b50505b60005b8151811015613634578181815181106135f857fe5b6020026020010151600860008381526020019081526020016000209080519060200190613626929190614943565b5080806001019150506135e3565b506001600960146101000a816136486145bb565b8160ff0219169083151502179061365d61461e565b505050505050505050505050565b6136d55a61367761455e565b73ffffffffffffffffffffffffffffffffffffffff16600960009061369a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b613710600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141560006137e7565b8073ffffffffffffffffffffffffffffffffffffffff1660096000906137346145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600960006101000a816137a56145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906137e161461e565b50505050565b816008600083600e8111156137f857fe5b815260200190815260200160002090613850576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161383e9190615bac565b6040518091039061384d6144f0565b50505b505050565b6000600a6138616145bb565b90506004600a600082826138736145bb565b01925050819061388161461e565b5050506040518061010001604052808281526020018481526020018381526020016000815260200160008152602001600081526020016000815260200185815250600f60008381526020019081526020016000206000820151816000016138e661461e565b50506020820151816001016138f961461e565b505060408201518160020161390c61461e565b505060608201518160030161391f61461e565b505060808201518160040161393261461e565b505060a08201518160050161394561461e565b505060c08201518160060161395861461e565b505060e08201518160070161396b61461e565b5050905050600e60008581526020019081526020016000206004018190806001816139946145bb565b01808261399f61461e565b50508091505060019003906000526020600020016000909190919091506139c461461e565b505080847fb022fbd99a76d7d6d20bd5b22c69b0e8bd1b756764c76b117306d29bbc16e09d85856040516139f9929190615d43565b60405180910390a39392505050565b600080600090613a166145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166305b7f2f65a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015613aa757600081830152602081019050613a8d565b50505060026040518363ffffffff1660e01b8152600401613ac9929190615b1d565b60e0604051808303818680613adc614683565b158015613af157600080613aee6144f0565b50505b505a613afb614843565b5050505050158015613b1a573d6000803e3d6000613b176144f0565b50505b505050506040513d601f19601f82011682018060405250810190613b3e91906150be565b90506000806000806000856000015160106000896000015181526020019081526020016000208190613b6e61461e565b50505060005b876080015151811015613e63576000600f60008a608001518481518110613b9757fe5b602002602001015181526020019081526020016000206040518061010001604052908160008201613bc66145bb565b815260200160018201613bd76145bb565b815260200160028201613be86145bb565b815260200160038201613bf96145bb565b815260200160048201613c0a6145bb565b815260200160058201613c1b6145bb565b815260200160068201613c2c6145bb565b815260200160078201613c3d6145bb565b815250509050613c5a81606001518761404790919063ffffffff16565b9550613c89613c7a82602001518360a0015161428790919063ffffffff16565b8661404790919063ffffffff16565b9450806020015188600001511115613dad57613cce613cbf82602001518a6000015103836060015161428790919063ffffffff16565b8861404790919063ffffffff16565b96506000613d228960000151613d14613cfb8c60c00151601260ff16600a0a6142b490919063ffffffff16565b85602001518d600001510361434090919063ffffffff16565b61434090919063ffffffff16565b9050601260ff16600a0a811115613d3e57601260ff16600a0a90505b613d67613d5883608001518361428790919063ffffffff16565b8661404790919063ffffffff16565b9450613d8381601260ff16600a0a6142b490919063ffffffff16565b60116000846000015181526020019081526020016000208190613da461461e565b50505050613dda565b601260ff16600a0a60116000836000015181526020019081526020016000208190613dd661461e565b5050505b806020015188600001511015613e5557613e1d613e0e89600001518360200151038360a0015161428790919063ffffffff16565b8861404790919063ffffffff16565b9650613e52613e438260c001518a6000015184602001510361428790919063ffffffff16565b8461404790919063ffffffff16565b92505b508080600101915050613b74565b506004600090613e716145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663226ec41583836040518363ffffffff1660e01b8152600401613ec8929190615d43565b60006040518083038160008780613edd614683565b158015613ef257600080613eef6144f0565b50505b505a613efc6146e6565b505050505050158015613f1c573d6000803e3d6000613f196144f0565b50505b505050506001600090613f2d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f6b50198487876040518463ffffffff1660e01b8152600401613f8693929190615d6c565b60006040518083038160008780613f9b614683565b158015613fb057600080613fad6144f0565b50505b505a613fba6146e6565b505050505050158015613fda573d6000803e3d6000613fd76144f0565b50505b5050505086600001517f3f24fabf249b0bbc2acff84ed6a1b001961a11a3d32fc78ec28407708d3c238b868686868660405161401a959493929190615de8565b60405180910390a250505050505050565b600061403f8383601260ff16600a0a614376565b905092915050565b6000808284019050838110156140ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815250602001915050604051809103906140cb6144f0565b50505b8091505092915050565b60008060008060026000906140eb6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663af579a118988888b6002016141326145bb565b6040518563ffffffff1660e01b81526004016141519493929190615c0e565b60606040518083038160008780614166614683565b15801561417b576000806141786144f0565b50505b505a6141856146e6565b5050505050501580156141a5573d6000803e3d60006141a26144f0565b50505b505050506040513d601f19601f820116820180604052508101906141c9919061540e565b925092509250808860020181906141de61461e565b505050818760020181906141f061461e565b505050866000016141ff6145bb565b7f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce8818360405161422e9190615c57565b60405180910390a2876000016142426145bb565b7f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957826040516142719190615c57565b60405180910390a2829350505050949350505050565b6000601260ff16600a0a6142a483856143cf90919063ffffffff16565b816142ab57fe5b04905092915050565b600082821115614335576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250602001915050604051809103906143326144f0565b50505b818303905092915050565b600061436e82614360601260ff16600a0a866143cf90919063ffffffff16565b61445e90919063ffffffff16565b905092915050565b600080600a838161438357fe5b0461439785876143cf90919063ffffffff16565b8161439e57fe5b0490506005600a82816143ad57fe5b06106143ba57600a810190505b600a81816143c457fe5b049150509392505050565b6000808314156143e25760009050614458565b60008284029050828482816143f357fe5b0414614453576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806163db60219139604001915050604051809103906144506144f0565b50505b809150505b92915050565b60008082116144de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250602001915050604051809103906144db6144f0565b50505b8183816144e757fe5b04905092915050565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561452b57808601518160408401015260208101905061450d565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b60408110156145b65760008183015260208101905061459c565b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015614619576000818301526020810190506145ff565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b604081101561467e57600081830152602081019050614664565b505050565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b60408110156146e1576000818301526020810190506146c7565b505050565b6385979f76598160e01b8152614725565b600081905081831115614708578290505b92915050565b60008190508183101561471f578290505b92915050565b836004820152846024820152606060448201528760648201526084810160005b89811015614760578089015181830152602081019050614745565b506060828a60a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8c8c82606087013350600060045af150596147b58e3d61470e565b8d016147c181876146f7565b5b828110156147d957600081526020810190506147c2565b50839e5050505050505050565b63bdbf8c36598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b604081101561483e57600081830152602081019050614824565b505050565b638540661f598160e01b8152614882565b600081905081831115614865578290505b92915050565b60008190508183101561487c578290505b92915050565b836004820152846024820152606060448201528660648201526084810160005b888110156148bd5780880151818301526020810190506148a2565b506060828960a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8b8b82606087013350600060045af150596149128d3d61486b565b8c0161491e8187614854565b5b82811015614936576000815260208101905061491f565b50839d5050505050505050565b828061494d6145bb565b600181600116156101000203166002900490600052602060002090601f0160209004810192826149895760008561498261461e565b50506149eb565b82601f106149ab57805160ff191683800117856149a461461e565b50506149eb565b828001600101856149ba61461e565b505082156149eb579182015b828111156149ea578251826149d961461e565b5050916020019190600101906149c6565b5b5090506149f891906149fc565b5090565b5b80821115614a1e57600081600090614a1361461e565b5050506001016149fd565b5090565b6000614a35614a3084615eea565b615eb9565b9050808382526020820190508260005b85811015614a755781358501614a5b8882614cdc565b845260208401935060208301925050600181019050614a45565b5050509392505050565b6000614a92614a8d84615f16565b615eb9565b90508083825260208201905082856020860282011115614aba57600080614ab76144f0565b50505b60005b85811015614aea5781614ad08882614f40565b845260208401935060208301925050600181019050614abd565b5050509392505050565b6000614b07614b0284615f42565b615eb9565b905082815260208101848484011115614b2857600080614b256144f0565b50505b614b338482856161a1565b509392505050565b600081359050614b4a81616201565b92915050565b600082601f830112614b6a57600080614b676144f0565b50505b8135614b7a848260208601614a22565b91505092915050565b600082601f830112614b9d57600080614b9a6144f0565b50505b8135614bad848260208601614a7f565b91505092915050565b600081359050614bc581616221565b92915050565b600081519050614bda81616221565b92915050565b600081519050614bef81616241565b92915050565b600081519050614c0481616261565b92915050565b600081359050614c1981616281565b92915050565b600081359050614c2e816162a1565b92915050565b600081359050614c43816162c1565b92915050565b600081359050614c58816162e1565b92915050565b600081359050614c6d81616301565b92915050565b600081359050614c8281616321565b92915050565b600081359050614c9781616341565b92915050565b600081519050614cac81616361565b92915050565b600081359050614cc181616381565b92915050565b600081519050614cd68161639a565b92915050565b600082601f830112614cf657600080614cf36144f0565b50505b8135614d06848260208601614af4565b91505092915050565b600060e08284031215614d2a57600080614d276144f0565b50505b614d3460e0615eb9565b90506000614d4484828501614f55565b6000830152506020614d5884828501614be0565b6020830152506040614d6c84828501614be0565b6040830152506060614d8084828501614c9d565b6060830152506080614d9484828501614bf5565b60808301525060a0614da884828501614f55565b60a08301525060c0614dbc84828501614f55565b60c08301525092915050565b600060808284031215614de357600080614de06144f0565b50505b614ded6080615eb9565b90506000614dfd84828501614f55565b6000830152506020614e1184828501614f55565b6020830152506040614e2584828501614f55565b6040830152506060614e3984828501614f55565b60608301525092915050565b60006101408284031215614e6157600080614e5e6144f0565b50505b614e6c610140615eb9565b90506000614e7c84828501614f55565b6000830152506020614e9084828501614f55565b6020830152506040614ea484828501614f55565b6040830152506060614eb884828501614f55565b6060830152506080614ecc84828501614f55565b60808301525060a0614ee084828501614f55565b60a08301525060c0614ef484828501614cc7565b60c08301525060e0614f0884828501614cc7565b60e083015250610100614f1d84828501614f55565b61010083015250610120614f3384828501614f55565b6101208301525092915050565b600081359050614f4f816163ba565b92915050565b600081519050614f64816163ba565b92915050565b600060208284031215614f8557600080614f826144f0565b50505b6000614f9384828501614b3b565b91505092915050565b600060208284031215614fb757600080614fb46144f0565b50505b6000614fc584828501614bcb565b91505092915050565b60008060008060008060008060006101208a8c031215614ff657600080614ff36144f0565b50505b60006150048c828d01614c34565b99505060206150158c828d01614c1f565b98505060406150268c828d01614c5e565b97505060606150378c828d01614c49565b96505060806150488c828d01614c88565b95505060a06150598c828d01614c73565b94505060c061506a8c828d01614c0a565b93505060e061507b8c828d01614c0a565b9250506101008a013567ffffffffffffffff8111156150a25760008061509f6144f0565b50505b6150ae8c828d01614b50565b9150509295985092959850929598565b600060e082840312156150d9576000806150d66144f0565b50505b60006150e784828501614d0f565b91505092915050565b60006080828403121561510b576000806151086144f0565b50505b600061511984828501614dc8565b91505092915050565b600080600061024084860312156151415760008061513e6144f0565b50505b600061514f86828701614e45565b93505061014061516186828701614d0f565b92505061022061517386828701614f55565b9150509250925092565b600060208284031215615198576000806151956144f0565b50505b60006151a684828501614f40565b91505092915050565b6000602082840312156151ca576000806151c76144f0565b50505b60006151d884828501614f55565b91505092915050565b600080604083850312156151fd576000806151fa6144f0565b50505b600061520b85828601614f40565b925050602061521c85828601614bb6565b9150509250929050565b600080604083850312156152425760008061523f6144f0565b50505b600061525085828601614f40565b925050602061526185828601614cb2565b9150509250929050565b600080600060608486031215615289576000806152866144f0565b50505b600061529786828701614f40565b93505060206152a886828701614cb2565b92505060406152b986828701614f40565b9150509250925092565b600080604083850312156152df576000806152dc6144f0565b50505b60006152ed85828601614f40565b92505060206152fe85828601614f40565b9150509250929050565b60008060008060808587031215615327576000806153246144f0565b50505b600061533587828801614f40565b945050602061534687828801614f40565b935050604085013567ffffffffffffffff81111561536c576000806153696144f0565b50505b61537887828801614b83565b925050606085013567ffffffffffffffff81111561539e5760008061539b6144f0565b50505b6153aa87828801614b83565b91505092959194509250565b6000806000606084860312156153d4576000806153d16144f0565b50505b60006153e286828701614f40565b93505060206153f386828701614f40565b925050604061540486828701614f40565b9150509250925092565b60008060006060848603121561542c576000806154296144f0565b50505b600061543a86828701614f55565b935050602061544b86828701614f55565b925050604061545c86828701614f55565b9150509250925092565b60006154728383615a3f565b60208301905092915050565b615487816160ff565b82525050565b61549681615fdb565b82525050565b60006154a782615f97565b6154b18185615faf565b93506154bc83615f72565b8060005b838110156154ed5781516154d48882615466565b97506154df83615fa2565b9250506001810190506154c0565b5085935050505092915050565b61550381615fed565b82525050565b61551281615fed565b82525050565b61552181615ff9565b82525050565b61553081616111565b82525050565b61553f81616111565b82525050565b61554e81616135565b82525050565b61555d81616159565b82525050565b61556c8161616b565b82525050565b61557b816160cb565b82525050565b60008161558c6145bb565b60018116600081146155a557600181146155cb57615616565b607f60028304166155b68187615fc0565b955060ff198316865260208601935050615616565b600282046155d98187615fc0565b95506155e485615f82565b60005b8281101561560d57816155f86145bb565b818901526001820191506020810190506155e7565b80880195505050505b505092915050565b600061562b601383615fc0565b91507f616c726561647920696e697469616c697a6564000000000000000000000000006000830152602082019050919050565b600061566b600f83615fc0565b91507f6572726f72206d736720636f756e7400000000000000000000000000000000006000830152602082019050919050565b60e0820160008201516156b46000850182615a3f565b5060208201516156c76020850182615518565b5060408201516156da6040850182615518565b5060608201516156ed6060850182615545565b5060808201516157006080850182615527565b5060a082015161571360a0850182615a3f565b5060c082015161572660c0850182615a3f565b50505050565b6080820160008201516157426000850182615a3f565b5060208201516157556020850182615a3f565b5060408201516157686040850182615a3f565b50606082015161577b6060850182615a3f565b50505050565b6080820160008201516157976000850182615a3f565b5060208201516157aa6020850182615a3f565b5060408201516157bd6040850182615a3f565b5060608201516157d06060850182615a3f565b50505050565b610100820160008083016157e86145bb565b90506157f3816161b0565b6158006000860182615a3f565b506001830161580d6145bb565b9050615818816161b0565b6158256020860182615a3f565b50600283016158326145bb565b905061583d816161b0565b61584a6040860182615a3f565b50600383016158576145bb565b9050615862816161b0565b61586f6060860182615a3f565b506004830161587c6145bb565b9050615887816161b0565b6158946080860182615a3f565b50600583016158a16145bb565b90506158ac816161b0565b6158b960a0860182615a3f565b50600683016158c66145bb565b90506158d1816161b0565b6158de60c0860182615a3f565b50600783016158eb6145bb565b90506158f6816161b0565b61590360e0860182615a3f565b5050505050565b610140820160008201516159216000850182615a3f565b5060208201516159346020850182615a3f565b5060408201516159476040850182615a3f565b50606082015161595a6060850182615a3f565b50608082015161596d6080850182615a3f565b5060a082015161598060a0850182615a3f565b5060c082015161599360c0850182615572565b5060e08201516159a660e0850182615572565b506101008201516159bb610100850182615a3f565b506101208201516159d0610120850182615a3f565b50505050565b610100820160008201516159ed60008501826154fa565b506020820151615a006020850182615a3f565b506040820151615a136040850182615a3f565b506060820151615a266060850182615a3f565b506080820151615a39608085018261572c565b50505050565b615a48816160f5565b82525050565b615a57816160f5565b82525050565b6000606082019050615a72600083018661547e565b615a7f602083018561548d565b615a8c6040830184615a4e565b949350505050565b6000604082019050615aa9600083018561547e565b615ab66020830184615a4e565b9392505050565b6000606082019050615ad2600083018661547e565b615adf6020830185615a4e565b615aec6040830184615a4e565b949350505050565b6000604082019050615b09600083018561548d565b615b166020830184615509565b9392505050565b6000604082019050615b32600083018561548d565b615b3f6020830184615554565b9392505050565b6000604082019050615b5b600083018561548d565b615b686020830184615a4e565b9392505050565b60006020820190508181036000830152615b89818461549c565b905092915050565b6000602082019050615ba66000830184615509565b92915050565b60006020820190508181036000830152615bc68184615581565b905092915050565b60006020820190508181036000830152615be78161561e565b9050919050565b60006020820190508181036000830152615c078161565e565b9050919050565b600061036082019050615c2460008301876157d6565b615c326101008301866159d6565b615c4061020083018561590a565b615c4e610340830184615a4e565b95945050505050565b6000602082019050615c6c6000830184615a4e565b92915050565b600060e082019050615c87600083018a615a4e565b615c94602083018961547e565b615ca16040830188615563565b615cae6060830187615a4e565b615cbb6080830186615a4e565b615cc860a0830185615a4e565b615cd560c0830184615a4e565b98975050505050505050565b6000604082019050615cf66000830185615a4e565b615d036020830184615536565b9392505050565b600061018082019050615d206000830186615a4e565b615d2d602083018561569e565b615d3b610100830184615781565b949350505050565b6000604082019050615d586000830185615a4e565b615d656020830184615a4e565b9392505050565b6000606082019050615d816000830186615a4e565b615d8e6020830185615a4e565b615d9b6040830184615a4e565b949350505050565b6000608082019050615db86000830187615a4e565b615dc56020830186615a4e565b615dd26040830185615a4e565b615ddf6060830184615509565b95945050505050565b600060a082019050615dfd6000830188615a4e565b615e0a6020830187615a4e565b615e176040830186615a4e565b615e246060830185615a4e565b615e316080830184615a4e565b9695505050505050565b600061010082019050615e51600083018b615a4e565b615e5e602083018a615a4e565b615e6b6040830189615a4e565b615e786060830188615a4e565b615e856080830187615a4e565b615e9260a0830186615a4e565b615e9f60c0830185615a4e565b615eac60e0830184615a4e565b9998505050505050505050565b6000604051905081810181811067ffffffffffffffff82111715615ee057615edf6161ca565b5b8060405250919050565b600067ffffffffffffffff821115615f0557615f046161ca565b5b602082029050602081019050919050565b600067ffffffffffffffff821115615f3157615f306161ca565b5b602082029050602081019050919050565b600067ffffffffffffffff821115615f5d57615f5c6161ca565b5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b60008190508160005260206000209050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000819050919050565b6000615fe6826160d5565b9050919050565b60008115159050919050565b6000819050919050565b600061600e82615fdb565b9050919050565b600061602082615fdb565b9050919050565b600061603282615fdb565b9050919050565b600061604482615fdb565b9050919050565b600061605682615fdb565b9050919050565b600061606882615fdb565b9050919050565b600061607a82615fdb565b9050919050565b600061608c82615fdb565b9050919050565b600061609e82615fdb565b9050919050565b60008190506160b3826161d9565b919050565b60008190506160c6826161ed565b919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061610a8261617d565b9050919050565b600061611c82616123565b9050919050565b600061612e826160d5565b9050919050565b600061614082616147565b9050919050565b6000616152826160d5565b9050919050565b6000616164826160a5565b9050919050565b6000616176826160b8565b9050919050565b60006161888261618f565b9050919050565b600061619a826160d5565b9050919050565b82818337600083830152505050565b60006161c36161be836161cc565b615fd1565b9050919050565bfe5b60008160001c9050919050565b600381106161ea576161e96161ca565b5b50565b600481106161fe576161fd6161ca565b5b50565b61620a81615fdb565b811461621e5760008061621b6144f0565b50505b50565b61622a81615fed565b811461623e5760008061623b6144f0565b50505b50565b61624a81615ff9565b811461625e5760008061625b6144f0565b50505b50565b61626a81616003565b811461627e5760008061627b6144f0565b50505b50565b61628a81616015565b811461629e5760008061629b6144f0565b50505b50565b6162aa81616027565b81146162be576000806162bb6144f0565b50505b50565b6162ca81616039565b81146162de576000806162db6144f0565b50505b50565b6162ea8161604b565b81146162fe576000806162fb6144f0565b50505b50565b61630a8161605d565b811461631e5760008061631b6144f0565b50505b50565b61632a8161606f565b811461633e5760008061633b6144f0565b50505b50565b61634a81616081565b811461635e5760008061635b6144f0565b50505b50565b61636a81616093565b811461637e5760008061637b6144f0565b50505b50565b60048110616397576000806163946144f0565b50505b50565b6163a3816160cb565b81146163b7576000806163b46144f0565b50505b50565b6163c3816160f5565b81146163d7576000806163d46144f0565b50505b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639b805da8116100a2578063d1e9e81111610071578063d1e9e8111461030d578063d6ef9be31461033d578063dd2f0d3614610359578063f2333a5814610389578063f2fde38b146103a557610116565b80639b805da814610275578063a92e61eb146102a5578063a9c9d125146102c1578063c39eeefb146102dd57610116565b806335359675116100e957806335359675146101a857806342a47e13146101db5780637e7088eb146101f7578063862a33941461021557806397dd25241461024557610116565b806318cc7e861461011b5780632eb6534f146101375780632f4aff0e146101555780633289cf8f14610171575b600080fd5b6101356004803603810190610130919061400f565b6103c1565b005b61013f610533565b60405161014c9190614927565b60405180910390f35b61016f600480360381019061016a91906140de565b610539565b005b61018b60048036038101906101869190613ef6565b610661565b60405161019f989796959493929190614b0b565b60405180910390f35b6101c260048036038101906101bd9190613ef6565b6106a9565b6040516101d29493929190614a73565b60405180910390f35b6101f560048036038101906101f09190613ef6565b6106e6565b005b6101ff6108f4565b60405161020c919061483f565b60405180910390f35b61022f600480360381019061022a9190613fc0565b610995565b60405161023c9190614927565b60405180910390f35b61025f600480360381019061025a9190613fc0565b61148b565b60405161026c9190614927565b60405180910390f35b61028f600480360381019061028a9190613ef6565b611ebb565b60405161029c9190614927565b60405180910390f35b6102bf60048036038101906102ba9190613f84565b611ed3565b005b6102db60048036038101906102d69190613f48565b61211e565b005b6102f760048036038101906102f29190613ef6565b61220c565b604051610304919061483f565b60405180910390f35b61032760048036038101906103229190613ef6565b6122f1565b6040516103349190614927565b60405180910390f35b6103576004803603810190610352919061400f565b612309565b005b610373600480360381019061036e919061404b565b612526565b6040516103809190614927565b60405180910390f35b6103a3600480360381019061039e9190613d74565b612825565b005b6103bf60048036038101906103ba9190613d22565b612b45565b005b61041c3373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e6000848152602001908152602001600020905061045e83826000015414801561045757508160030160009054906101000a900460ff165b6002612c9b565b818160020181905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663551c855584846040518363ffffffff1660e01b81526004016104c4929190614a13565b600060405180830381600087803b1580156104de57600080fd5b505af11580156104f2573d6000803e3d6000fd5b50505050827f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce881836040516105269190614927565b60405180910390a2505050565b600d5481565b6105943373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e600085815260200190815260200160002090506105bc848260000154146001612c9b565b60006105c9858585612d00565b9050600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d59f359386836040518363ffffffff1660e01b8152600401610628929190614a13565b600060405180830381600087803b15801561064257600080fd5b505af1158015610656573d6000803e3d6000fd5b505050505050505050565b600f6020528060005260406000206000915090508060000154908060010154908060020154908060030154908060040154908060050154908060060154908060070154905088565b600e6020528060005260406000206000915090508060000154908060010154908060020154908060030160009054906101000a900460ff16905084565b6000600e60008381526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820160009054906101000a900460ff161515151581526020016004820180548060200260200160405190810160405280929190818152602001828054801561078e57602002820191906000526020600020905b81548152602001906001019080831161077a575b50505050508152505090506107ab4282602001511115600b612c9b565b6000805b600c8054905081101561084d5783600c82815481106107ca57fe5b9060005260206000200154141561084057600c6001600c8054905003815481106107f057fe5b9060005260206000200154600c828154811061080857fe5b9060005260206000200181905550600c80548061082157fe5b600190038181906000526020600020016000905590556001915061084d565b80806001019150506107af565b5061085981600c612c9b565b61086282612e3b565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dba5082a846040518263ffffffff1660e01b81526004016108bd9190614927565b600060405180830381600087803b1580156108d757600080fd5b505af11580156108eb573d6000803e3d6000fd5b50505050505050565b6060600c8054905067ffffffffffffffff8111801561091257600080fd5b506040519080825280602002602001820160405280156109415781602001602082028036833780820191505090505b50905060005b600c8054905081101561099157600c818154811061096157fe5b906000526020600020015482828151811061097857fe5b6020026020010181815250508080600101915050610947565b5090565b60006109c76000831380156109c057508360038111156109b157fe5b6003808111156109bd57fe5b10155b6007612c9b565b60008060038111156109d557fe5b8460038111156109e157fe5b1480610a035750600260038111156109f557fe5b846003811115610a0157fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701548152602001908152602001600020905060008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b1230886040518363ffffffff1660e01b8152600401610a989291906147c4565b6102406040518083038186803b158015610ab157600080fd5b505afa158015610ac5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae99190613ea4565b925092509250610b1d8460030160009054906101000a900460ff16158015610b1657508460010154824201105b6008612c9b565b60006040518060a0016040528088151581526020018a8152602001610b538860020154886002015461332a90919063ffffffff16565b815260200186600101548152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401610bc79291906149b1565b60806040518083038186803b158015610bdf57600080fd5b505afa158015610bf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c179190613e7b565b8152509050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663156e29f6338c6003811115610c6757fe5b8e018c6040518463ffffffff1660e01b8152600401610c889392919061478d565b600060405180830381600087803b158015610ca257600080fd5b505af1158015610cb6573d6000803e3d6000fd5b5050505060006003811115610cc757fe5b8a6003811115610cd357fe5b1415610cfd57610cf089876003015461334690919063ffffffff16565b8660030181905550610da5565b60016003811115610d0a57fe5b8a6003811115610d1657fe5b1415610d4057610d3389876004015461334690919063ffffffff16565b8660040181905550610da4565b60026003811115610d4d57fe5b8a6003811115610d5957fe5b1415610d8357610d7689876005015461334690919063ffffffff16565b8660050181905550610da3565b610d9a89876006015461334690919063ffffffff16565b86600601819055505b5b5b610db1868683876133ce565b975060006003811115610dc057fe5b8a6003811115610dcc57fe5b1415610f4957600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a00dad228a8584608001516040518463ffffffff1660e01b8152600401610e35939291906149da565b600060405180830381600087803b158015610e4f57600080fd5b505af1158015610e63573d6000803e3d6000fd5b50505050610f44600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b8152600401610eeb9392919061472d565b602060405180830381600087803b158015610f0557600080fd5b505af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190613d4b565b6009612c9b565b611420565b60026003811115610f5657fe5b8a6003811115610f6257fe5b14156110f757600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f238ff5c610fbd88600101548c61352990919063ffffffff16565b8360800151600001516040518363ffffffff1660e01b8152600401610fe3929190614a13565b600060405180830381600087803b158015610ffd57600080fd5b505af1158015611011573d6000803e3d6000fd5b505050506110f2600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b81526004016110999392919061472d565b602060405180830381600087803b1580156110b357600080fd5b505af11580156110c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110eb9190613d4b565b6009612c9b565b61141f565b6001600381111561110457fe5b8a600381111561111057fe5b1415611291576111f3600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d6040518463ffffffff1660e01b815260040161119a9392919061472d565b602060405180830381600087803b1580156111b457600080fd5b505af11580156111c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ec9190613d4b565b600a612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b815260040161125a9392919061478d565b600060405180830381600087803b15801561127457600080fd5b505af1158015611288573d6000803e3d6000fd5b5050505061141e565b611384600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1661130d8b600101548f61352990919063ffffffff16565b6040518463ffffffff1660e01b815260040161132b9392919061472d565b602060405180830381600087803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137d9190613d4b565b6009612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b81526004016113eb9392919061478d565b600060405180830381600087803b15801561140557600080fd5b505af1158015611419573d6000803e3d6000fd5b505050505b5b5b89600381111561142c57fe5b8b3373ffffffffffffffffffffffffffffffffffffffff167f9a99f0d83ea15216987ecd4ed332a7338eafafe12db86adf283528ec63c02d3f8c8c604051611475929190614a13565b60405180910390a4505050505050509392505050565b60006114bd6000831380156114b657508360038111156114a757fe5b6003808111156114b357fe5b10155b6007612c9b565b60008060038111156114cb57fe5b8460038111156114d757fe5b14806114f95750600260038111156114eb57fe5b8460038111156114f757fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701548152602001908152602001600020905060008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b123088156040518363ffffffff1660e01b815260040161158f9291906147c4565b6102406040518083038186803b1580156115a857600080fd5b505afa1580156115bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e09190613ea4565b9250925092506116148460030160009054906101000a900460ff1615801561160d57508460010154824201105b6008612c9b565b60006040518060a001604052808815151581526020018a815260200161164b8860020154886002015461332a90919063ffffffff16565b815260200186600101548152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b81526004016116bf9291906149b1565b60806040518083038186803b1580156116d757600080fd5b505afa1580156116eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170f9190613e7b565b8152509050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca338c600381111561175f57fe5b8e018c6040518463ffffffff1660e01b81526004016117809392919061478d565b600060405180830381600087803b15801561179a57600080fd5b505af11580156117ae573d6000803e3d6000fd5b50505050600060038111156117bf57fe5b8a60038111156117cb57fe5b14156117f5576117e889876003015461355690919063ffffffff16565b866003018190555061189d565b6001600381111561180257fe5b8a600381111561180e57fe5b14156118385761182b89876004015461355690919063ffffffff16565b866004018190555061189c565b6002600381111561184557fe5b8a600381111561185157fe5b141561187b5761186e89876005015461355690919063ffffffff16565b866005018190555061189b565b61189289876006015461355690919063ffffffff16565b86600601819055505b5b5b6118a9868683876133ce565b9750600060038111156118b857fe5b8a60038111156118c457fe5b14156119f557600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dc62c5818a6040518263ffffffff1660e01b81526004016119259190614927565b600060405180830381600087803b15801561193f57600080fd5b505af1158015611953573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b81526004016119be9392919061478d565b600060405180830381600087803b1580156119d857600080fd5b505af11580156119ec573d6000803e3d6000fd5b50505050611e50565b60026003811115611a0257fe5b8a6003811115611a0e57fe5b1415611b5557600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663db32232f611a6988600101548c61352990919063ffffffff16565b6040518263ffffffff1660e01b8152600401611a859190614927565b600060405180830381600087803b158015611a9f57600080fd5b505af1158015611ab3573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b8152600401611b1e9392919061478d565b600060405180830381600087803b158015611b3857600080fd5b505af1158015611b4c573d6000803e3d6000fd5b50505050611e4f565b60016003811115611b6257fe5b8a6003811115611b6e57fe5b1415611ce557600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638b89a242338b6040518363ffffffff1660e01b8152600401611bd1929190614764565b600060405180830381600087803b158015611beb57600080fd5b505af1158015611bff573d6000803e3d6000fd5b50505050611ce0600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b8152600401611c879392919061472d565b602060405180830381600087803b158015611ca157600080fd5b505af1158015611cb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd99190613d4b565b6009612c9b565b611e4e565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b30733611d4d8b611d3f8b600101548f61352990919063ffffffff16565b61355690919063ffffffff16565b6040518363ffffffff1660e01b8152600401611d6a929190614764565b600060405180830381600087803b158015611d8457600080fd5b505af1158015611d98573d6000803e3d6000fd5b50505050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b307600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6040518363ffffffff1660e01b8152600401611e1b929190614816565b600060405180830381600087803b158015611e3557600080fd5b505af1158015611e49573d6000803e3d6000fd5b505050505b5b5b896003811115611e5c57fe5b8b3373ffffffffffffffffffffffffffffffffffffffff167f5a0cd91f269c536b8167c8fc4e2b859e3db49a3fb022e5db3e380a58975405848c8c604051611ea5929190614a13565b60405180910390a4505050505050509392505050565b60116020528060005260406000206000915090505481565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1662fdd58e33846003811115611f1f57fe5b86016040518363ffffffff1660e01b8152600401611f3e929190614764565b60206040518083038186803b158015611f5657600080fd5b505afa158015611f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8e9190613f1f565b9050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166337af0a2884338585600f60008a81526020019081526020016000206001015460106000600f60008d815260200190815260200160002060070154815260200190815260200160002054601160008c8152602001908152602001600020546040518863ffffffff1660e01b81526004016120499796959493929190614942565b600060405180830381600087803b15801561206357600080fd5b505af1158015612077573d6000803e3d6000fd5b50505050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca338460038111156120c657fe5b8601846040518463ffffffff1660e01b81526004016120e79392919061478d565b600060405180830381600087803b15801561210157600080fd5b505af1158015612115573d6000803e3d6000fd5b50505050505050565b6121793373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e600084815260200190815260200160002090506121a1838260000154146001612c9b565b81600e600085815260200190815260200160002060030160006101000a81548160ff021916908315150217905550827fab7e756517bb425436c10403644a884802e0b2d5105f9f5386823b7c42ca5d5f836040516121ff9190614861565b60405180910390a2505050565b60606000600e60008481526020019081526020016000206004018054905067ffffffffffffffff8111801561224057600080fd5b5060405190808252806020026020018201604052801561226f5781602001602082028036833780820191505090505b50905060005b600e6000858152602001908152602001600020600401805490508110156122e757600e600085815260200190815260200160002060040181815481106122b757fe5b90600052602060002001548282815181106122ce57fe5b6020026020010181815250508080600101915050612275565b5080915050919050565b60106020528060005260406000206000915090505481565b6123643373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600f600084815260200190815260200160002090506000600e6000836007015481526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820160009054906101000a900460ff161515151581526020016004820180548060200260200160405190810160405280929190818152602001828054801561242757602002820191906000526020600020905b815481526020019060010190808311612413575b5050505050815250509050612450848360000154148015612449575081606001515b6003612c9b565b828260020181905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d6ef9be385856040518363ffffffff1660e01b81526004016124b6929190614a13565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b50505050837f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957846040516125189190614927565b60405180910390a250505050565b60006125833373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b61259f82518451148015612598575060008451115b6004612c9b565b6125c1625c49006125b9428861355690919063ffffffff16565b106005612c9b565b600d54851115612677576125de6000600c80549050146006612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3964372600d54876040518363ffffffff1660e01b815260040161263d929190614a13565b600060405180830381600087803b15801561265757600080fd5b505af115801561266b573d6000803e3d6000fd5b5050505084600d819055505b6000600b600081548092919060010191905055905080600e60008381526020019081526020016000206000018190555085600e60008381526020019081526020016000206001018190555084600e600083815260200190815260200160002060020181905550600c819080600181540180825580915050600190039060005260206000200160009091909190915055807f2eb8cef7f145b51f7d30c1ffc842e46161a2b1624415ce69192c99ec2106ad878787604051612738929190614a13565b60405180910390a260005b845181101561278b5761277d8286838151811061275c57fe5b602002602001015186848151811061277057fe5b6020026020010151612d00565b508080600101915050612743565b50600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d5766f7826040518263ffffffff1660e01b81526004016127e79190614927565b600060405180830381600087803b15801561280157600080fd5b505af1158015612815573d6000803e3d6000fd5b5050505080915050949350505050565b600960149054906101000a900460ff1615612875576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161286c9061489e565b60405180910390fd5b886000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555087600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555086600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555085600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555084600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555083600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600e80811115612a8857fe5b815114612aca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ac1906148be565b60405180910390fd5b60005b8151811015612b1e57818181518110612ae257fe5b6020026020010151600860008381526020019081526020016000209080519060200190612b10929190613777565b508080600101915050612acd565b506001600960146101000a81548160ff021916908315150217905550505050505050505050565b612ba03373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b612bdb600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156000612c9b565b8073ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b816008600083600e811115612cac57fe5b815260200190815260200160002090612cfb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cf2919061487c565b60405180910390fd5b505050565b6000600a5490506004600a600082825401925050819055506040518061010001604052808281526020018481526020018381526020016000815260200160008152602001600081526020016000815260200185815250600f6000838152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070155905050600e600085815260200190815260200160002060040181908060018154018082558091505060019003906000526020600020016000909190919091505580847fb022fbd99a76d7d6d20bd5b22c69b0e8bd1b756764c76b117306d29bbc16e09d8585604051612e2c929190614a13565b60405180910390a39392505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166305b7f2f63060026040518363ffffffff1660e01b8152600401612e9a9291906147ed565b60e06040518083038186803b158015612eb257600080fd5b505afa158015612ec6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eea9190613e52565b90506000806000806000856000015160106000896000015181526020019081526020016000208190555060005b8760800151518110156131bc576000600f60008a608001518481518110612f3a57fe5b6020026020010151815260200190815260200160002060405180610100016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815260200160058201548152602001600682015481526020016007820154815250509050612fc581606001518761334690919063ffffffff16565b9550612ff4612fe582602001518360a0015161352990919063ffffffff16565b8661334690919063ffffffff16565b945080602001518860000151111561310f5761303961302a82602001518a6000015103836060015161352990919063ffffffff16565b8861334690919063ffffffff16565b9650600061308d896000015161307f6130668c60c00151601260ff16600a0a61355690919063ffffffff16565b85602001518d60000151036135d990919063ffffffff16565b6135d990919063ffffffff16565b9050601260ff16600a0a8111156130a957601260ff16600a0a90505b6130d26130c383608001518361352990919063ffffffff16565b8661334690919063ffffffff16565b94506130ee81601260ff16600a0a61355690919063ffffffff16565b60116000846000015181526020019081526020016000208190555050613133565b601260ff16600a0a6011600083600001518152602001908152602001600020819055505b8060200151886000015110156131ae5761317661316789600001518360200151038360a0015161352990919063ffffffff16565b8861334690919063ffffffff16565b96506131ab61319c8260c001518a6000015184602001510361352990919063ffffffff16565b8461334690919063ffffffff16565b92505b508080600101915050612f17565b50600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663226ec41583836040518363ffffffff1660e01b815260040161321a929190614a13565b600060405180830381600087803b15801561323457600080fd5b505af1158015613248573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f6b50198487876040518463ffffffff1660e01b81526004016132ab93929190614a3c565b600060405180830381600087803b1580156132c557600080fd5b505af11580156132d9573d6000803e3d6000fd5b5050505086600001517f3f24fabf249b0bbc2acff84ed6a1b001961a11a3d32fc78ec28407708d3c238b8686868686604051613319959493929190614ab8565b60405180910390a250505050505050565b600061333e8383601260ff16600a0a61360f565b905092915050565b6000808284019050838110156133c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080600080600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663af579a118988888b600201546040518563ffffffff1660e01b815260040161343994939291906148de565b606060405180830381600087803b15801561345357600080fd5b505af1158015613467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061348b919061412d565b92509250925080886002018190555081876002018190555086600001547f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce881836040516134d79190614927565b60405180910390a287600001547f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957826040516135139190614927565b60405180910390a2829350505050949350505050565b6000601260ff16600a0a613546838561366890919063ffffffff16565b8161354d57fe5b04905092915050565b6000828211156135ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525060200191505060405180910390fd5b818303905092915050565b6000613607826135f9601260ff16600a0a8661366890919063ffffffff16565b6136ee90919063ffffffff16565b905092915050565b600080600a838161361c57fe5b04613630858761366890919063ffffffff16565b8161363757fe5b0490506005600a828161364657fe5b061061365357600a810190505b600a818161365d57fe5b049150509392505050565b60008083141561367b57600090506136e8565b600082840290508284828161368c57fe5b04146136e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806150246021913960400191505060405180910390fd5b809150505b92915050565b6000808211613765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525060200191505060405180910390fd5b81838161376e57fe5b04905092915050565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826137ad57600085556137f4565b82601f106137c657805160ff19168380011785556137f4565b828001600101855582156137f4579182015b828111156137f35782518255916020019190600101906137d8565b5b5090506138019190613805565b5090565b5b8082111561381e576000816000905550600101613806565b5090565b600061383561383084614bba565b614b89565b9050808382526020820190508260005b85811015613875578135850161385b8882613ab8565b845260208401935060208301925050600181019050613845565b5050509392505050565b600061389261388d84614be6565b614b89565b905080838252602082019050828560208602820111156138b157600080fd5b60005b858110156138e157816138c78882613cf8565b8452602084019350602083019250506001810190506138b4565b5050509392505050565b60006138fe6138f984614c12565b614b89565b90508281526020810184848401111561391657600080fd5b613921848285614e71565b509392505050565b60008135905061393881614ed1565b92915050565b600082601f83011261394f57600080fd5b813561395f848260208601613822565b91505092915050565b600082601f83011261397957600080fd5b813561398984826020860161387f565b91505092915050565b6000813590506139a181614ee8565b92915050565b6000815190506139b681614ee8565b92915050565b6000815190506139cb81614eff565b92915050565b6000815190506139e081614f16565b92915050565b6000813590506139f581614f2d565b92915050565b600081359050613a0a81614f44565b92915050565b600081359050613a1f81614f5b565b92915050565b600081359050613a3481614f72565b92915050565b600081359050613a4981614f89565b92915050565b600081359050613a5e81614fa0565b92915050565b600081359050613a7381614fb7565b92915050565b600081519050613a8881614fce565b92915050565b600081359050613a9d81614fe5565b92915050565b600081519050613ab281614ff5565b92915050565b600082601f830112613ac957600080fd5b8135613ad98482602086016138eb565b91505092915050565b600060e08284031215613af457600080fd5b613afe60e0614b89565b90506000613b0e84828501613d0d565b6000830152506020613b22848285016139bc565b6020830152506040613b36848285016139bc565b6040830152506060613b4a84828501613a79565b6060830152506080613b5e848285016139d1565b60808301525060a0613b7284828501613d0d565b60a08301525060c0613b8684828501613d0d565b60c08301525092915050565b600060808284031215613ba457600080fd5b613bae6080614b89565b90506000613bbe84828501613d0d565b6000830152506020613bd284828501613d0d565b6020830152506040613be684828501613d0d565b6040830152506060613bfa84828501613d0d565b60608301525092915050565b60006101408284031215613c1957600080fd5b613c24610140614b89565b90506000613c3484828501613d0d565b6000830152506020613c4884828501613d0d565b6020830152506040613c5c84828501613d0d565b6040830152506060613c7084828501613d0d565b6060830152506080613c8484828501613d0d565b60808301525060a0613c9884828501613d0d565b60a08301525060c0613cac84828501613aa3565b60c08301525060e0613cc084828501613aa3565b60e083015250610100613cd584828501613d0d565b61010083015250610120613ceb84828501613d0d565b6101208301525092915050565b600081359050613d078161500c565b92915050565b600081519050613d1c8161500c565b92915050565b600060208284031215613d3457600080fd5b6000613d4284828501613929565b91505092915050565b600060208284031215613d5d57600080fd5b6000613d6b848285016139a7565b91505092915050565b60008060008060008060008060006101208a8c031215613d9357600080fd5b6000613da18c828d01613a10565b9950506020613db28c828d016139fb565b9850506040613dc38c828d01613a3a565b9750506060613dd48c828d01613a25565b9650506080613de58c828d01613a64565b95505060a0613df68c828d01613a4f565b94505060c0613e078c828d016139e6565b93505060e0613e188c828d016139e6565b9250506101008a013567ffffffffffffffff811115613e3657600080fd5b613e428c828d0161393e565b9150509295985092959850929598565b600060e08284031215613e6457600080fd5b6000613e7284828501613ae2565b91505092915050565b600060808284031215613e8d57600080fd5b6000613e9b84828501613b92565b91505092915050565b60008060006102408486031215613eba57600080fd5b6000613ec886828701613c06565b935050610140613eda86828701613ae2565b925050610220613eec86828701613d0d565b9150509250925092565b600060208284031215613f0857600080fd5b6000613f1684828501613cf8565b91505092915050565b600060208284031215613f3157600080fd5b6000613f3f84828501613d0d565b91505092915050565b60008060408385031215613f5b57600080fd5b6000613f6985828601613cf8565b9250506020613f7a85828601613992565b9150509250929050565b60008060408385031215613f9757600080fd5b6000613fa585828601613cf8565b9250506020613fb685828601613a8e565b9150509250929050565b600080600060608486031215613fd557600080fd5b6000613fe386828701613cf8565b9350506020613ff486828701613a8e565b925050604061400586828701613cf8565b9150509250925092565b6000806040838503121561402257600080fd5b600061403085828601613cf8565b925050602061404185828601613cf8565b9150509250929050565b6000806000806080858703121561406157600080fd5b600061406f87828801613cf8565b945050602061408087828801613cf8565b935050604085013567ffffffffffffffff81111561409d57600080fd5b6140a987828801613968565b925050606085013567ffffffffffffffff8111156140c657600080fd5b6140d287828801613968565b91505092959194509250565b6000806000606084860312156140f357600080fd5b600061410186828701613cf8565b935050602061411286828701613cf8565b925050604061412386828701613cf8565b9150509250925092565b60008060006060848603121561414257600080fd5b600061415086828701613d0d565b935050602061416186828701613d0d565b925050604061417286828701613d0d565b9150509250925092565b6000614188838361470f565b60208301905092915050565b61419d81614dcf565b82525050565b6141ac81614cab565b82525050565b60006141bd82614c67565b6141c78185614c7f565b93506141d283614c42565b8060005b838110156142035781516141ea888261417c565b97506141f583614c72565b9250506001810190506141d6565b5085935050505092915050565b61421981614cbd565b82525050565b61422881614cbd565b82525050565b61423781614cc9565b82525050565b61424681614de1565b82525050565b61425581614de1565b82525050565b61426481614e05565b82525050565b61427381614e29565b82525050565b61428281614e3b565b82525050565b61429181614d9b565b82525050565b6000815460018116600081146142b457600181146142da5761431e565b607f60028304166142c58187614c90565b955060ff19831686526020860193505061431e565b600282046142e88187614c90565b95506142f385614c52565b60005b82811015614315578154818901526001820191506020810190506142f6565b80880195505050505b505092915050565b6000614333601383614c90565b91507f616c726561647920696e697469616c697a6564000000000000000000000000006000830152602082019050919050565b6000614373600f83614c90565b91507f6572726f72206d736720636f756e7400000000000000000000000000000000006000830152602082019050919050565b60e0820160008201516143bc600085018261470f565b5060208201516143cf602085018261422e565b5060408201516143e2604085018261422e565b5060608201516143f5606085018261425b565b506080820151614408608085018261423d565b5060a082015161441b60a085018261470f565b5060c082015161442e60c085018261470f565b50505050565b60808201600082015161444a600085018261470f565b50602082015161445d602085018261470f565b506040820151614470604085018261470f565b506060820151614483606085018261470f565b50505050565b60808201600082015161449f600085018261470f565b5060208201516144b2602085018261470f565b5060408201516144c5604085018261470f565b5060608201516144d8606085018261470f565b50505050565b610100820160008083015490506144f481614e80565b614501600086018261470f565b506001830154905061451281614e80565b61451f602086018261470f565b506002830154905061453081614e80565b61453d604086018261470f565b506003830154905061454e81614e80565b61455b606086018261470f565b506004830154905061456c81614e80565b614579608086018261470f565b506005830154905061458a81614e80565b61459760a086018261470f565b50600683015490506145a881614e80565b6145b560c086018261470f565b50600783015490506145c681614e80565b6145d360e086018261470f565b5050505050565b610140820160008201516145f1600085018261470f565b506020820151614604602085018261470f565b506040820151614617604085018261470f565b50606082015161462a606085018261470f565b50608082015161463d608085018261470f565b5060a082015161465060a085018261470f565b5060c082015161466360c0850182614288565b5060e082015161467660e0850182614288565b5061010082015161468b61010085018261470f565b506101208201516146a061012085018261470f565b50505050565b610100820160008201516146bd6000850182614210565b5060208201516146d0602085018261470f565b5060408201516146e3604085018261470f565b5060608201516146f6606085018261470f565b5060808201516147096080850182614434565b50505050565b61471881614dc5565b82525050565b61472781614dc5565b82525050565b60006060820190506147426000830186614194565b61474f60208301856141a3565b61475c604083018461471e565b949350505050565b60006040820190506147796000830185614194565b614786602083018461471e565b9392505050565b60006060820190506147a26000830186614194565b6147af602083018561471e565b6147bc604083018461471e565b949350505050565b60006040820190506147d960008301856141a3565b6147e6602083018461421f565b9392505050565b600060408201905061480260008301856141a3565b61480f602083018461426a565b9392505050565b600060408201905061482b60008301856141a3565b614838602083018461471e565b9392505050565b6000602082019050818103600083015261485981846141b2565b905092915050565b6000602082019050614876600083018461421f565b92915050565b600060208201905081810360008301526148968184614297565b905092915050565b600060208201905081810360008301526148b781614326565b9050919050565b600060208201905081810360008301526148d781614366565b9050919050565b6000610360820190506148f460008301876144de565b6149026101008301866146a6565b6149106102008301856145da565b61491e61034083018461471e565b95945050505050565b600060208201905061493c600083018461471e565b92915050565b600060e082019050614957600083018a61471e565b6149646020830189614194565b6149716040830188614279565b61497e606083018761471e565b61498b608083018661471e565b61499860a083018561471e565b6149a560c083018461471e565b98975050505050505050565b60006040820190506149c6600083018561471e565b6149d3602083018461424c565b9392505050565b6000610180820190506149f0600083018661471e565b6149fd60208301856143a6565b614a0b610100830184614489565b949350505050565b6000604082019050614a28600083018561471e565b614a35602083018461471e565b9392505050565b6000606082019050614a51600083018661471e565b614a5e602083018561471e565b614a6b604083018461471e565b949350505050565b6000608082019050614a88600083018761471e565b614a95602083018661471e565b614aa2604083018561471e565b614aaf606083018461421f565b95945050505050565b600060a082019050614acd600083018861471e565b614ada602083018761471e565b614ae7604083018661471e565b614af4606083018561471e565b614b01608083018461471e565b9695505050505050565b600061010082019050614b21600083018b61471e565b614b2e602083018a61471e565b614b3b604083018961471e565b614b48606083018861471e565b614b55608083018761471e565b614b6260a083018661471e565b614b6f60c083018561471e565b614b7c60e083018461471e565b9998505050505050505050565b6000604051905081810181811067ffffffffffffffff82111715614bb057614baf614e9a565b5b8060405250919050565b600067ffffffffffffffff821115614bd557614bd4614e9a565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614c0157614c00614e9a565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614c2d57614c2c614e9a565b5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b60008190508160005260206000209050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000819050919050565b6000614cb682614da5565b9050919050565b60008115159050919050565b6000819050919050565b6000614cde82614cab565b9050919050565b6000614cf082614cab565b9050919050565b6000614d0282614cab565b9050919050565b6000614d1482614cab565b9050919050565b6000614d2682614cab565b9050919050565b6000614d3882614cab565b9050919050565b6000614d4a82614cab565b9050919050565b6000614d5c82614cab565b9050919050565b6000614d6e82614cab565b9050919050565b6000819050614d8382614ea9565b919050565b6000819050614d9682614ebd565b919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000614dda82614e4d565b9050919050565b6000614dec82614df3565b9050919050565b6000614dfe82614da5565b9050919050565b6000614e1082614e17565b9050919050565b6000614e2282614da5565b9050919050565b6000614e3482614d75565b9050919050565b6000614e4682614d88565b9050919050565b6000614e5882614e5f565b9050919050565b6000614e6a82614da5565b9050919050565b82818337600083830152505050565b6000614e93614e8e83614e9c565b614ca1565b9050919050565bfe5b60008160001c9050919050565b60038110614eba57614eb9614e9a565b5b50565b60048110614ece57614ecd614e9a565b5b50565b614eda81614cab565b8114614ee557600080fd5b50565b614ef181614cbd565b8114614efc57600080fd5b50565b614f0881614cc9565b8114614f1357600080fd5b50565b614f1f81614cd3565b8114614f2a57600080fd5b50565b614f3681614ce5565b8114614f4157600080fd5b50565b614f4d81614cf7565b8114614f5857600080fd5b50565b614f6481614d09565b8114614f6f57600080fd5b50565b614f7b81614d1b565b8114614f8657600080fd5b50565b614f9281614d2d565b8114614f9d57600080fd5b50565b614fa981614d3f565b8114614fb457600080fd5b50565b614fc081614d51565b8114614fcb57600080fd5b50565b614fd781614d63565b8114614fe257600080fd5b50565b60048110614ff257600080fd5b50565b614ffe81614d9b565b811461500957600080fd5b50565b61501581614dc5565b811461502057600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122025cb595d612b5cbe13cf48f8a36c92b410f3ba316734ab31b5332ff4aa39086464736f6c63430007060033

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.