My Name Tag:
Not Available, login to update
ContractCreator:
GENESIS at txn GENESIS_1f6d98638eee9f689684767c3021230dd68df419
[ Download CSV Export ]
Latest 11 internal transactions
[ Download CSV Export ]
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
Contract Source Code (Solidity Standard Json-Input format)
//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); }
//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; } }
// 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); }
//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); }
//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); }
//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"); _; } }
//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); }
//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); }
//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; }
// 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; } }
// 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; } }
//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); }
//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); }
//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); }
//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); }
//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); }
// 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; } }
//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 ); }
//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; }
//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); }
//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); }
//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; }
// 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; } }
// 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; }
// 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); }
// 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); }
// 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; } }
// 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); } } } }
// 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); }
//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; } }
//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); }
//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); }
//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); }
// 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; } }
{ "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "abi" ] } }, "optimizer": { "enabled": false, "runs": 200 }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
60806040526000600960146101000a8162000019620000d1565b8160ff021916908315150217906200003062000136565b5050506001600a6200004162000136565b50506001600b6200005162000136565b50503480156200006b57600080620000686200019d565b50505b505a620000776200020d565b600960006101000a816200008a620000d1565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790620000c862000136565b5050506200026c565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015620001315760008183015260208101905062000115565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b604081101562000198576000818301526020810190506200017c565b505050565b632a2a7adb598160e01b8152600481016020815285602082015260005b86811015620001da578086015181604084010152602081019050620001ba565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b604081101562000267576000818301526020810190506200024b565b505050565b6163fc806200027c6000396000f3fe6080604052348015610019576000806100166144f0565b50505b506004361061011f5760003560e01c80639b805da8116100ab578063d1e9e8111161007a578063d1e9e8111461031f578063d6ef9be31461034f578063dd2f0d361461036b578063f2333a581461039b578063f2fde38b146103b75761011f565b80639b805da814610287578063a92e61eb146102b7578063a9c9d125146102d3578063c39eeefb146102ef5761011f565b806335359675116100f257806335359675146101ba57806342a47e13146101ed5780637e7088eb14610209578063862a33941461022757806397dd2524146102575761011f565b806318cc7e861461012d5780632eb6534f146101495780632f4aff0e146101675780633289cf8f14610183575b60008061012a6144f0565b50505b610147600480360381019061014291906152c3565b6103d3565b005b610151610598565b60405161015e9190615c57565b60405180910390f35b610181600480360381019061017c91906153b6565b6105a5565b005b61019d6004803603810190610198919061517d565b610710565b6040516101b1989796959493929190615e3b565b60405180910390f35b6101d460048036038101906101cf919061517d565b610790565b6040516101e49493929190615da3565b60405180910390f35b6102076004803603810190610202919061517d565b6107e9565b005b610211610ab0565b60405161021e9190615b6f565b60405180910390f35b610241600480360381019061023c919061526b565b610b76565b60405161024e9190615c57565b60405180910390f35b610271600480360381019061026c919061526b565b611990565b60405161027e9190615c57565b60405180910390f35b6102a1600480360381019061029c919061517d565b6126ce565b6040516102ae9190615c57565b60405180910390f35b6102d160048036038101906102cc9190615226565b6126ed565b005b6102ed60048036038101906102e891906151e1565b6129f2565b005b6103096004803603810190610304919061517d565b612b06565b6040516103169190615b6f565b60405180910390f35b6103396004803603810190610334919061517d565b612c10565b6040516103469190615c57565b60405180910390f35b610369600480360381019061036491906152c3565b612c2f565b005b61038560048036038101906103809190615308565b612ed0565b6040516103929190615c57565b60405180910390f35b6103b560048036038101906103b09190614fce565b6132a2565b005b6103d160048036038101906103cc9190614f6a565b61366b565b005b61043d5a6103df61455e565b73ffffffffffffffffffffffffffffffffffffffff1660096000906104026145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e6000848152602001908152602001600020905061048d83826000016104646145bb565b14801561048657508160030160009061047b6145bb565b906101000a900460ff165b60026137e7565b8181600201819061049c61461e565b50505060036000906104ac6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663551c855584846040518363ffffffff1660e01b8152600401610503929190615d43565b60006040518083038160008780610518614683565b15801561052d5760008061052a6144f0565b50505b505a6105376146e6565b505050505050158015610557573d6000803e3d60006105546144f0565b50505b50505050827f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce8818360405161058b9190615c57565b60405180910390a2505050565b600d6105a26145bb565b81565b61060f5a6105b161455e565b73ffffffffffffffffffffffffffffffffffffffff1660096000906105d46145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e6000858152602001908152602001600020905061063e84826000016106366145bb565b1460016137e7565b600061064b858585613855565b9050600360009061065a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d59f359386836040518363ffffffff1660e01b81526004016106b1929190615d43565b600060405180830381600087806106c6614683565b1580156106db576000806106d86144f0565b50505b505a6106e56146e6565b505050505050158015610705573d6000803e3d60006107026144f0565b50505b505050505050505050565b600f602052806000526040600020600091509050806000016107306145bb565b908060010161073d6145bb565b908060020161074a6145bb565b90806003016107576145bb565b90806004016107646145bb565b90806005016107716145bb565b908060060161077e6145bb565b908060070161078b6145bb565b905088565b600e602052806000526040600020600091509050806000016107b06145bb565b90806001016107bd6145bb565b90806002016107ca6145bb565b90806003016000906107da6145bb565b906101000a900460ff16905084565b6000600e60008381526020019081526020016000206040518060a0016040529081600082016108166145bb565b8152602001600182016108276145bb565b8152602001600282016108386145bb565b81526020016003820160009061084c6145bb565b906101000a900460ff16151515158152602001600482018061086c6145bb565b806020026020016040519081016040528092919081815260200182806108906145bb565b80156108c257602002820191906000526020600020905b816108b06145bb565b815260200190600101908083116108a7575b50505050508152505090506108e75a6108d96147e6565b82602001511115600b6137e7565b6000805b600c806108f66145bb565b90508110156109dc5783600c828161090c6145bb565b811061091457fe5b906000526020600020016109266145bb565b14156109cf57600c6001600c8061093b6145bb565b905003816109476145bb565b811061094f57fe5b906000526020600020016109616145bb565b600c828161096d6145bb565b811061097557fe5b90600052602060002001819061098961461e565b505050600c806109976145bb565b8061099e57fe5b600190038181906000526020600020016000906109b961461e565b5050906109c461461e565b5050600191506109dc565b80806001019150506108eb565b506109e881600c6137e7565b6109f182613a08565b60036000906109fe6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dba5082a846040518263ffffffff1660e01b8152600401610a539190615c57565b60006040518083038160008780610a68614683565b158015610a7d57600080610a7a6144f0565b50505b505a610a876146e6565b505050505050158015610aa7573d6000803e3d6000610aa46144f0565b50505b50505050505050565b6060600c80610abd6145bb565b905067ffffffffffffffff81118015610ade57600080610adb6144f0565b50505b50604051908082528060200260200182016040528015610b0d5781602001602082028036833780820191505090505b50905060005b600c80610b1e6145bb565b9050811015610b7257600c8181610b336145bb565b8110610b3b57fe5b90600052602060002001610b4d6145bb565b828281518110610b5957fe5b6020026020010181815250508080600101915050610b13565b5090565b6000610ba8600083138015610ba15750836003811115610b9257fe5b600380811115610b9e57fe5b10155b60076137e7565b6000806003811115610bb657fe5b846003811115610bc257fe5b1480610be4575060026003811115610bd657fe5b846003811115610be257fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701610c0f6145bb565b81526020019081526020016000209050600080600080600090610c306145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b125a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015610cc157600081830152602081019050610ca7565b505050886040518363ffffffff1660e01b8152600401610ce2929190615af4565b610240604051808303818680610cf6614683565b158015610d0b57600080610d086144f0565b50505b505a610d15614843565b5050505050158015610d34573d6000803e3d6000610d316144f0565b50505b505050506040513d601f19601f82011682018060405250810190610d589190615122565b925092509250610da284600301600090610d706145bb565b906101000a900460ff16158015610d9b575084600101610d8e6145bb565b825a610d986147e6565b01105b60086137e7565b60006040518060a0016040528088151581526020018a8152602001610de688600201610dcc6145bb565b88600201610dd86145bb565b61402b90919063ffffffff16565b815260200186600101610df76145bb565b81526020016001600090610e096145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401610e68929190615ce1565b6080604051808303818680610e7b614683565b158015610e9057600080610e8d6144f0565b50505b505a610e9a614843565b5050505050158015610eb9573d6000803e3d6000610eb66144f0565b50505b505050506040513d601f19601f82011682018060405250810190610edd91906150f0565b81525090506005600090610eef6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663156e29f65a610f3061455e565b8c6003811115610f3c57fe5b8e018c6040518463ffffffff1660e01b8152600401610f5d93929190615abd565b60006040518083038160008780610f72614683565b158015610f8757600080610f846144f0565b50505b505a610f916146e6565b505050505050158015610fb1573d6000803e3d6000610fae6144f0565b50505b5050505060006003811115610fc257fe5b8a6003811115610fce57fe5b141561100857610ff28987600301610fe46145bb565b61404790919063ffffffff16565b86600301819061100061461e565b5050506110e0565b6001600381111561101557fe5b8a600381111561102157fe5b141561105b5761104589876004016110376145bb565b61404790919063ffffffff16565b86600401819061105361461e565b5050506110df565b6002600381111561106857fe5b8a600381111561107457fe5b14156110ae57611098898760050161108a6145bb565b61404790919063ffffffff16565b8660050181906110a661461e565b5050506110de565b6110cc89876006016110be6145bb565b61404790919063ffffffff16565b8660060181906110da61461e565b5050505b5b5b6110ec868683876140d8565b9750600060038111156110fb57fe5b8a600381111561110757fe5b14156112ed57600160009061111a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a00dad228a8584608001516040518463ffffffff1660e01b815260040161117793929190615d0a565b6000604051808303816000878061118c614683565b1580156111a15760008061119e6144f0565b50505b505a6111ab6146e6565b5050505050501580156111cb573d6000803e3d60006111c86144f0565b50505b505050506112e860066000906111df6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61122061455e565b600160009061122d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b815260040161126993929190615a5d565b6020604051808303816000878061127e614683565b158015611293576000806112906144f0565b50505b505a61129d6146e6565b5050505050501580156112bd573d6000803e3d60006112ba6144f0565b50505b505050506040513d601f19601f820116820180604052508101906112e19190614f9c565b60096137e7565b61191d565b600260038111156112fa57fe5b8a600381111561130657fe5b141561150b5760016000906113196145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f238ff5c61136f886001016113606145bb565b8c61428790919063ffffffff16565b8360800151600001516040518363ffffffff1660e01b8152600401611395929190615d43565b600060405180830381600087806113aa614683565b1580156113bf576000806113bc6144f0565b50505b505a6113c96146e6565b5050505050501580156113e9573d6000803e3d60006113e66144f0565b50505b5050505061150660066000906113fd6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61143e61455e565b600160009061144b6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b815260040161148793929190615a5d565b6020604051808303816000878061149c614683565b1580156114b1576000806114ae6144f0565b50505b505a6114bb6146e6565b5050505050501580156114db573d6000803e3d60006114d86144f0565b50505b505050506040513d601f19601f820116820180604052508101906114ff9190614f9c565b60096137e7565b61191c565b6001600381111561151857fe5b8a600381111561152457fe5b141561171657611643600760009061153a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61157b61455e565b60046000906115886145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168d6040518463ffffffff1660e01b81526004016115c493929190615a5d565b602060405180830381600087806115d9614683565b1580156115ee576000806115eb6144f0565b50505b505a6115f86146e6565b505050505050158015611618573d6000803e3d60006116156144f0565b50505b505050506040513d601f19601f8201168201806040525081019061163c9190614f9c565b600a6137e7565b60016000906116506145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61169161455e565b8a8460800151600001516040518463ffffffff1660e01b81526004016116b993929190615abd565b600060405180830381600087806116ce614683565b1580156116e3576000806116e06144f0565b50505b505a6116ed6146e6565b50505050505015801561170d573d6000803e3d600061170a6144f0565b50505b5050505061191b565b61184c60066000906117266145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a61176761455e565b60046000906117746145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff166117af8b6001016117a06145bb565b8f61428790919063ffffffff16565b6040518463ffffffff1660e01b81526004016117cd93929190615a5d565b602060405180830381600087806117e2614683565b1580156117f7576000806117f46144f0565b50505b505a6118016146e6565b505050505050158015611821573d6000803e3d600061181e6144f0565b50505b505050506040513d601f19601f820116820180604052508101906118459190614f9c565b60096137e7565b60016000906118596145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61189a61455e565b8a8460800151600001516040518463ffffffff1660e01b81526004016118c293929190615abd565b600060405180830381600087806118d7614683565b1580156118ec576000806118e96144f0565b50505b505a6118f66146e6565b505050505050158015611916573d6000803e3d60006119136144f0565b50505b505050505b5b5b89600381111561192957fe5b8b5a61193361455e565b73ffffffffffffffffffffffffffffffffffffffff167f9a99f0d83ea15216987ecd4ed332a7338eafafe12db86adf283528ec63c02d3f8c8c60405161197a929190615d43565b60405180910390a4505050505050509392505050565b60006119c26000831380156119bb57508360038111156119ac57fe5b6003808111156119b857fe5b10155b60076137e7565b60008060038111156119d057fe5b8460038111156119dc57fe5b14806119fe5750600260038111156119f057fe5b8460038111156119fc57fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701611a296145bb565b81526020019081526020016000209050600080600080600090611a4a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b125a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015611adb57600081830152602081019050611ac1565b50505088156040518363ffffffff1660e01b8152600401611afd929190615af4565b610240604051808303818680611b11614683565b158015611b2657600080611b236144f0565b50505b505a611b30614843565b5050505050158015611b4f573d6000803e3d6000611b4c6144f0565b50505b505050506040513d601f19601f82011682018060405250810190611b739190615122565b925092509250611bbd84600301600090611b8b6145bb565b906101000a900460ff16158015611bb6575084600101611ba96145bb565b825a611bb36147e6565b01105b60086137e7565b60006040518060a001604052808815151581526020018a8152602001611c0288600201611be86145bb565b88600201611bf46145bb565b61402b90919063ffffffff16565b815260200186600101611c136145bb565b81526020016001600090611c256145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401611c84929190615ce1565b6080604051808303818680611c97614683565b158015611cac57600080611ca96144f0565b50505b505a611cb6614843565b5050505050158015611cd5573d6000803e3d6000611cd26144f0565b50505b505050506040513d601f19601f82011682018060405250810190611cf991906150f0565b81525090506005600090611d0b6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca5a611d4c61455e565b8c6003811115611d5857fe5b8e018c6040518463ffffffff1660e01b8152600401611d7993929190615abd565b60006040518083038160008780611d8e614683565b158015611da357600080611da06144f0565b50505b505a611dad6146e6565b505050505050158015611dcd573d6000803e3d6000611dca6144f0565b50505b5050505060006003811115611dde57fe5b8a6003811115611dea57fe5b1415611e2457611e0e8987600301611e006145bb565b6142b490919063ffffffff16565b866003018190611e1c61461e565b505050611efc565b60016003811115611e3157fe5b8a6003811115611e3d57fe5b1415611e7757611e618987600401611e536145bb565b6142b490919063ffffffff16565b866004018190611e6f61461e565b505050611efb565b60026003811115611e8457fe5b8a6003811115611e9057fe5b1415611eca57611eb48987600501611ea66145bb565b6142b490919063ffffffff16565b866005018190611ec261461e565b505050611efa565b611ee88987600601611eda6145bb565b6142b490919063ffffffff16565b866006018190611ef661461e565b5050505b5b5b611f08868683876140d8565b975060006003811115611f1757fe5b8a6003811115611f2357fe5b14156120b6576001600090611f366145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dc62c5818a6040518263ffffffff1660e01b8152600401611f8b9190615c57565b60006040518083038160008780611fa0614683565b158015611fb557600080611fb26144f0565b50505b505a611fbf6146e6565b505050505050158015611fdf573d6000803e3d6000611fdc6144f0565b50505b505050506001600090611ff06145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a61203161455e565b8a8460800151600001516040518463ffffffff1660e01b815260040161205993929190615abd565b6000604051808303816000878061206e614683565b158015612083576000806120806144f0565b50505b505a61208d6146e6565b5050505050501580156120ad573d6000803e3d60006120aa6144f0565b50505b5050505061265b565b600260038111156120c357fe5b8a60038111156120cf57fe5b141561227f5760016000906120e26145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663db32232f612138886001016121296145bb565b8c61428790919063ffffffff16565b6040518263ffffffff1660e01b81526004016121549190615c57565b60006040518083038160008780612169614683565b15801561217e5760008061217b6144f0565b50505b505a6121886146e6565b5050505050501580156121a8573d6000803e3d60006121a56144f0565b50505b5050505060016000906121b96145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b585a6121fa61455e565b8a8460800151600001516040518463ffffffff1660e01b815260040161222293929190615abd565b60006040518083038160008780612237614683565b15801561224c576000806122496144f0565b50505b505a6122566146e6565b505050505050158015612276573d6000803e3d60006122736144f0565b50505b5050505061265a565b6001600381111561228c57fe5b8a600381111561229857fe5b14156124805760046000906122ab6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638b89a2425a6122ec61455e565b8b6040518363ffffffff1660e01b815260040161230a929190615a94565b6000604051808303816000878061231f614683565b158015612334576000806123316144f0565b50505b505a61233e6146e6565b50505050505015801561235e573d6000803e3d600061235b6144f0565b50505b5050505061247b60066000906123726145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd5a6123b361455e565b60016000906123c06145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b81526004016123fc93929190615a5d565b60206040518083038160008780612411614683565b158015612426576000806124236144f0565b50505b505a6124306146e6565b505050505050158015612450573d6000803e3d600061244d6144f0565b50505b505050506040513d601f19601f820116820180604052508101906124749190614f9c565b60096137e7565b612659565b600460009061248d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b3075a6124ce61455e565b6124fe8b6124f08b6001016124e16145bb565b8f61428790919063ffffffff16565b6142b490919063ffffffff16565b6040518363ffffffff1660e01b815260040161251b929190615a94565b60006040518083038160008780612530614683565b158015612545576000806125426144f0565b50505b505a61254f6146e6565b50505050505015801561256f573d6000803e3d600061256c6144f0565b50505b5050505060046000906125806145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b30760016000906125c56145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6040518363ffffffff1660e01b8152600401612600929190615b46565b60006040518083038160008780612615614683565b15801561262a576000806126276144f0565b50505b505a6126346146e6565b505050505050158015612654573d6000803e3d60006126516144f0565b50505b505050505b5b5b89600381111561266757fe5b8b5a61267161455e565b73ffffffffffffffffffffffffffffffffffffffff167f5a0cd91f269c536b8167c8fc4e2b859e3db49a3fb022e5db3e380a58975405848c8c6040516126b8929190615d43565b60405180910390a4505050505050509392505050565b60116020528060005260406000206000915090506126ea6145bb565b81565b600060056000906126fc6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1662fdd58e5a61273c61455e565b84600381111561274857fe5b86016040518363ffffffff1660e01b8152600401612767929190615a94565b602060405180830381868061277a614683565b15801561278f5760008061278c6144f0565b50505b505a612799614843565b50505050501580156127b8573d6000803e3d60006127b56144f0565b50505b505050506040513d601f19601f820116820180604052508101906127dc91906151af565b905060046000906127eb6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166337af0a28845a61282d61455e565b8585600f60008a815260200190815260200160002060010161284d6145bb565b60106000600f60008d815260200190815260200160002060070161286f6145bb565b81526020019081526020016000206128856145bb565b601160008c81526020019081526020016000206128a06145bb565b6040518863ffffffff1660e01b81526004016128c29796959493929190615c72565b600060405180830381600087806128d7614683565b1580156128ec576000806128e96144f0565b50505b505a6128f66146e6565b505050505050158015612916573d6000803e3d60006129136144f0565b50505b5050505060056000906129276145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca5a61296861455e565b84600381111561297457fe5b8601846040518463ffffffff1660e01b815260040161299593929190615abd565b600060405180830381600087806129aa614683565b1580156129bf576000806129bc6144f0565b50505b505a6129c96146e6565b5050505050501580156129e9573d6000803e3d60006129e66144f0565b50505b50505050505050565b612a5c5a6129fe61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612a216145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600e60008481526020019081526020016000209050612a8b8382600001612a836145bb565b1460016137e7565b81600e600085815260200190815260200160002060030160006101000a81612ab16145bb565b8160ff02191690831515021790612ac661461e565b505050827fab7e756517bb425436c10403644a884802e0b2d5105f9f5386823b7c42ca5d5f83604051612af99190615b91565b60405180910390a2505050565b60606000600e600084815260200190815260200160002060040180612b296145bb565b905067ffffffffffffffff81118015612b4a57600080612b476144f0565b50505b50604051908082528060200260200182016040528015612b795781602001602082028036833780820191505090505b50905060005b600e600085815260200190815260200160002060040180612b9e6145bb565b9050811015612c0657600e60008581526020019081526020016000206004018181612bc76145bb565b8110612bcf57fe5b90600052602060002001612be16145bb565b828281518110612bed57fe5b6020026020010181815250508080600101915050612b7f565b5080915050919050565b6010602052806000526040600020600091509050612c2c6145bb565b81565b612c995a612c3b61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612c5e6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b6000600f600084815260200190815260200160002090506000600e600083600701612cc26145bb565b81526020019081526020016000206040518060a001604052908160008201612ce86145bb565b815260200160018201612cf96145bb565b815260200160028201612d0a6145bb565b815260200160038201600090612d1e6145bb565b906101000a900460ff161515151581526020016004820180612d3e6145bb565b80602002602001604051908101604052809291908181526020018280612d626145bb565b8015612d9457602002820191906000526020600020905b81612d826145bb565b81526020019060010190808311612d79575b5050505050815250509050612dc48483600001612daf6145bb565b148015612dbd575081606001515b60036137e7565b82826002018190612dd361461e565b5050506003600090612de36145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d6ef9be385856040518363ffffffff1660e01b8152600401612e3a929190615d43565b60006040518083038160008780612e4f614683565b158015612e6457600080612e616144f0565b50505b505a612e6e6146e6565b505050505050158015612e8e573d6000803e3d6000612e8b6144f0565b50505b50505050837f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a95784604051612ec29190615c57565b60405180910390a250505050565b6000612f3c5a612ede61455e565b73ffffffffffffffffffffffffffffffffffffffff166009600090612f016145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b612f5882518451148015612f51575060008451115b60046137e7565b612f82625c4900612f7a5a612f6b6147e6565b886142b490919063ffffffff16565b1060056137e7565b600d612f8c6145bb565b85111561308357612fad6000600c80612fa36145bb565b90501460066137e7565b6001600090612fba6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3964372600d612ffc6145bb565b876040518363ffffffff1660e01b815260040161301a929190615d43565b6000604051808303816000878061302f614683565b158015613044576000806130416144f0565b50505b505a61304e6146e6565b50505050505015801561306e573d6000803e3d600061306b6144f0565b50505b5050505084600d819061307f61461e565b5050505b6000600b6000816130926145bb565b809291906001019190506130a461461e565b5050905080600e600083815260200190815260200160002060000181906130c961461e565b50505085600e600083815260200190815260200160002060010181906130ed61461e565b50505084600e6000838152602001908152602001600020600201819061311161461e565b505050600c8190806001816131246145bb565b01808261312f61461e565b505080915050600190039060005260206000200160009091909190915061315461461e565b5050807f2eb8cef7f145b51f7d30c1ffc842e46161a2b1624415ce69192c99ec2106ad878787604051613188929190615d43565b60405180910390a260005b84518110156131db576131cd828683815181106131ac57fe5b60200260200101518684815181106131c057fe5b6020026020010151613855565b508080600101915050613193565b5060036000906131e96145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d5766f7826040518263ffffffff1660e01b815260040161323e9190615c57565b60006040518083038160008780613253614683565b158015613268576000806132656144f0565b50505b505a6132726146e6565b505050505050158015613292573d6000803e3d600061328f6144f0565b50505b5050505080915050949350505050565b60096014906132af6145bb565b906101000a900460ff1615613302576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016132f090615bce565b604051809103906132ff6144f0565b50505b886000806101000a816133136145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061334f61461e565b50505087600160006101000a816133646145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906133a061461e565b50505086600260006101000a816133b56145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906133f161461e565b50505085600360006101000a816134066145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061344261461e565b50505084600460006101000a816134576145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061349361461e565b50505083600560006101000a816134a86145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906134e461461e565b50505082600660006101000a816134f96145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061353561461e565b50505081600760006101000a8161354a6145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179061358661461e565b505050600e8081111561359557fe5b8151146135e0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016135ce90615bee565b604051809103906135dd6144f0565b50505b60005b8151811015613634578181815181106135f857fe5b6020026020010151600860008381526020019081526020016000209080519060200190613626929190614943565b5080806001019150506135e3565b506001600960146101000a816136486145bb565b8160ff0219169083151502179061365d61461e565b505050505050505050505050565b6136d55a61367761455e565b73ffffffffffffffffffffffffffffffffffffffff16600960009061369a6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d6137e7565b613710600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141560006137e7565b8073ffffffffffffffffffffffffffffffffffffffff1660096000906137346145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600960006101000a816137a56145bb565b8173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217906137e161461e565b50505050565b816008600083600e8111156137f857fe5b815260200190815260200160002090613850576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161383e9190615bac565b6040518091039061384d6144f0565b50505b505050565b6000600a6138616145bb565b90506004600a600082826138736145bb565b01925050819061388161461e565b5050506040518061010001604052808281526020018481526020018381526020016000815260200160008152602001600081526020016000815260200185815250600f60008381526020019081526020016000206000820151816000016138e661461e565b50506020820151816001016138f961461e565b505060408201518160020161390c61461e565b505060608201518160030161391f61461e565b505060808201518160040161393261461e565b505060a08201518160050161394561461e565b505060c08201518160060161395861461e565b505060e08201518160070161396b61461e565b5050905050600e60008581526020019081526020016000206004018190806001816139946145bb565b01808261399f61461e565b50508091505060019003906000526020600020016000909190919091506139c461461e565b505080847fb022fbd99a76d7d6d20bd5b22c69b0e8bd1b756764c76b117306d29bbc16e09d85856040516139f9929190615d43565b60405180910390a39392505050565b600080600090613a166145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166305b7f2f65a63996d79a5598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051925060005b6040811015613aa757600081830152602081019050613a8d565b50505060026040518363ffffffff1660e01b8152600401613ac9929190615b1d565b60e0604051808303818680613adc614683565b158015613af157600080613aee6144f0565b50505b505a613afb614843565b5050505050158015613b1a573d6000803e3d6000613b176144f0565b50505b505050506040513d601f19601f82011682018060405250810190613b3e91906150be565b90506000806000806000856000015160106000896000015181526020019081526020016000208190613b6e61461e565b50505060005b876080015151811015613e63576000600f60008a608001518481518110613b9757fe5b602002602001015181526020019081526020016000206040518061010001604052908160008201613bc66145bb565b815260200160018201613bd76145bb565b815260200160028201613be86145bb565b815260200160038201613bf96145bb565b815260200160048201613c0a6145bb565b815260200160058201613c1b6145bb565b815260200160068201613c2c6145bb565b815260200160078201613c3d6145bb565b815250509050613c5a81606001518761404790919063ffffffff16565b9550613c89613c7a82602001518360a0015161428790919063ffffffff16565b8661404790919063ffffffff16565b9450806020015188600001511115613dad57613cce613cbf82602001518a6000015103836060015161428790919063ffffffff16565b8861404790919063ffffffff16565b96506000613d228960000151613d14613cfb8c60c00151601260ff16600a0a6142b490919063ffffffff16565b85602001518d600001510361434090919063ffffffff16565b61434090919063ffffffff16565b9050601260ff16600a0a811115613d3e57601260ff16600a0a90505b613d67613d5883608001518361428790919063ffffffff16565b8661404790919063ffffffff16565b9450613d8381601260ff16600a0a6142b490919063ffffffff16565b60116000846000015181526020019081526020016000208190613da461461e565b50505050613dda565b601260ff16600a0a60116000836000015181526020019081526020016000208190613dd661461e565b5050505b806020015188600001511015613e5557613e1d613e0e89600001518360200151038360a0015161428790919063ffffffff16565b8861404790919063ffffffff16565b9650613e52613e438260c001518a6000015184602001510361428790919063ffffffff16565b8461404790919063ffffffff16565b92505b508080600101915050613b74565b506004600090613e716145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663226ec41583836040518363ffffffff1660e01b8152600401613ec8929190615d43565b60006040518083038160008780613edd614683565b158015613ef257600080613eef6144f0565b50505b505a613efc6146e6565b505050505050158015613f1c573d6000803e3d6000613f196144f0565b50505b505050506001600090613f2d6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f6b50198487876040518463ffffffff1660e01b8152600401613f8693929190615d6c565b60006040518083038160008780613f9b614683565b158015613fb057600080613fad6144f0565b50505b505a613fba6146e6565b505050505050158015613fda573d6000803e3d6000613fd76144f0565b50505b5050505086600001517f3f24fabf249b0bbc2acff84ed6a1b001961a11a3d32fc78ec28407708d3c238b868686868660405161401a959493929190615de8565b60405180910390a250505050505050565b600061403f8383601260ff16600a0a614376565b905092915050565b6000808284019050838110156140ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815250602001915050604051809103906140cb6144f0565b50505b8091505092915050565b60008060008060026000906140eb6145bb565b906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663af579a118988888b6002016141326145bb565b6040518563ffffffff1660e01b81526004016141519493929190615c0e565b60606040518083038160008780614166614683565b15801561417b576000806141786144f0565b50505b505a6141856146e6565b5050505050501580156141a5573d6000803e3d60006141a26144f0565b50505b505050506040513d601f19601f820116820180604052508101906141c9919061540e565b925092509250808860020181906141de61461e565b505050818760020181906141f061461e565b505050866000016141ff6145bb565b7f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce8818360405161422e9190615c57565b60405180910390a2876000016142426145bb565b7f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957826040516142719190615c57565b60405180910390a2829350505050949350505050565b6000601260ff16600a0a6142a483856143cf90919063ffffffff16565b816142ab57fe5b04905092915050565b600082821115614335576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250602001915050604051809103906143326144f0565b50505b818303905092915050565b600061436e82614360601260ff16600a0a866143cf90919063ffffffff16565b61445e90919063ffffffff16565b905092915050565b600080600a838161438357fe5b0461439785876143cf90919063ffffffff16565b8161439e57fe5b0490506005600a82816143ad57fe5b06106143ba57600a810190505b600a81816143c457fe5b049150509392505050565b6000808314156143e25760009050614458565b60008284029050828482816143f357fe5b0414614453576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806163db60219139604001915050604051809103906144506144f0565b50505b809150505b92915050565b60008082116144de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250602001915050604051809103906144db6144f0565b50505b8183816144e757fe5b04905092915050565b632a2a7adb598160e01b8152600481016020815285602082015260005b8681101561452b57808601518160408401015260208101905061450d565b506020828760640184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b505050565b6373509064598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b60408110156145b65760008183015260208101905061459c565b505050565b6303daa959598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b6040811015614619576000818301526020810190506145ff565b505050565b6322bd64c0598160e01b8152836004820152846024820152600081604483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b60005b604081101561467e57600081830152602081019050614664565b505050565b638435035b598160e01b8152836004820152602081602483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b60408110156146e1576000818301526020810190506146c7565b505050565b6385979f76598160e01b8152614725565b600081905081831115614708578290505b92915050565b60008190508183101561471f578290505b92915050565b836004820152846024820152606060448201528760648201526084810160005b89811015614760578089015181830152602081019050614745565b506060828a60a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8c8c82606087013350600060045af150596147b58e3d61470e565b8d016147c181876146f7565b5b828110156147d957600081526020810190506147c2565b50839e5050505050505050565b63bdbf8c36598160e01b8152602081600483336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b8051935060005b604081101561483e57600081830152602081019050614824565b505050565b638540661f598160e01b8152614882565b600081905081831115614865578290505b92915050565b60008190508183101561487c578290505b92915050565b836004820152846024820152606060448201528660648201526084810160005b888110156148bd5780880151818301526020810190506148a2565b506060828960a40184336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b815160408301513d6000853e8b8b82606087013350600060045af150596149128d3d61486b565b8c0161491e8187614854565b5b82811015614936576000815260208101905061491f565b50839d5050505050505050565b828061494d6145bb565b600181600116156101000203166002900490600052602060002090601f0160209004810192826149895760008561498261461e565b50506149eb565b82601f106149ab57805160ff191683800117856149a461461e565b50506149eb565b828001600101856149ba61461e565b505082156149eb579182015b828111156149ea578251826149d961461e565b5050916020019190600101906149c6565b5b5090506149f891906149fc565b5090565b5b80821115614a1e57600081600090614a1361461e565b5050506001016149fd565b5090565b6000614a35614a3084615eea565b615eb9565b9050808382526020820190508260005b85811015614a755781358501614a5b8882614cdc565b845260208401935060208301925050600181019050614a45565b5050509392505050565b6000614a92614a8d84615f16565b615eb9565b90508083825260208201905082856020860282011115614aba57600080614ab76144f0565b50505b60005b85811015614aea5781614ad08882614f40565b845260208401935060208301925050600181019050614abd565b5050509392505050565b6000614b07614b0284615f42565b615eb9565b905082815260208101848484011115614b2857600080614b256144f0565b50505b614b338482856161a1565b509392505050565b600081359050614b4a81616201565b92915050565b600082601f830112614b6a57600080614b676144f0565b50505b8135614b7a848260208601614a22565b91505092915050565b600082601f830112614b9d57600080614b9a6144f0565b50505b8135614bad848260208601614a7f565b91505092915050565b600081359050614bc581616221565b92915050565b600081519050614bda81616221565b92915050565b600081519050614bef81616241565b92915050565b600081519050614c0481616261565b92915050565b600081359050614c1981616281565b92915050565b600081359050614c2e816162a1565b92915050565b600081359050614c43816162c1565b92915050565b600081359050614c58816162e1565b92915050565b600081359050614c6d81616301565b92915050565b600081359050614c8281616321565b92915050565b600081359050614c9781616341565b92915050565b600081519050614cac81616361565b92915050565b600081359050614cc181616381565b92915050565b600081519050614cd68161639a565b92915050565b600082601f830112614cf657600080614cf36144f0565b50505b8135614d06848260208601614af4565b91505092915050565b600060e08284031215614d2a57600080614d276144f0565b50505b614d3460e0615eb9565b90506000614d4484828501614f55565b6000830152506020614d5884828501614be0565b6020830152506040614d6c84828501614be0565b6040830152506060614d8084828501614c9d565b6060830152506080614d9484828501614bf5565b60808301525060a0614da884828501614f55565b60a08301525060c0614dbc84828501614f55565b60c08301525092915050565b600060808284031215614de357600080614de06144f0565b50505b614ded6080615eb9565b90506000614dfd84828501614f55565b6000830152506020614e1184828501614f55565b6020830152506040614e2584828501614f55565b6040830152506060614e3984828501614f55565b60608301525092915050565b60006101408284031215614e6157600080614e5e6144f0565b50505b614e6c610140615eb9565b90506000614e7c84828501614f55565b6000830152506020614e9084828501614f55565b6020830152506040614ea484828501614f55565b6040830152506060614eb884828501614f55565b6060830152506080614ecc84828501614f55565b60808301525060a0614ee084828501614f55565b60a08301525060c0614ef484828501614cc7565b60c08301525060e0614f0884828501614cc7565b60e083015250610100614f1d84828501614f55565b61010083015250610120614f3384828501614f55565b6101208301525092915050565b600081359050614f4f816163ba565b92915050565b600081519050614f64816163ba565b92915050565b600060208284031215614f8557600080614f826144f0565b50505b6000614f9384828501614b3b565b91505092915050565b600060208284031215614fb757600080614fb46144f0565b50505b6000614fc584828501614bcb565b91505092915050565b60008060008060008060008060006101208a8c031215614ff657600080614ff36144f0565b50505b60006150048c828d01614c34565b99505060206150158c828d01614c1f565b98505060406150268c828d01614c5e565b97505060606150378c828d01614c49565b96505060806150488c828d01614c88565b95505060a06150598c828d01614c73565b94505060c061506a8c828d01614c0a565b93505060e061507b8c828d01614c0a565b9250506101008a013567ffffffffffffffff8111156150a25760008061509f6144f0565b50505b6150ae8c828d01614b50565b9150509295985092959850929598565b600060e082840312156150d9576000806150d66144f0565b50505b60006150e784828501614d0f565b91505092915050565b60006080828403121561510b576000806151086144f0565b50505b600061511984828501614dc8565b91505092915050565b600080600061024084860312156151415760008061513e6144f0565b50505b600061514f86828701614e45565b93505061014061516186828701614d0f565b92505061022061517386828701614f55565b9150509250925092565b600060208284031215615198576000806151956144f0565b50505b60006151a684828501614f40565b91505092915050565b6000602082840312156151ca576000806151c76144f0565b50505b60006151d884828501614f55565b91505092915050565b600080604083850312156151fd576000806151fa6144f0565b50505b600061520b85828601614f40565b925050602061521c85828601614bb6565b9150509250929050565b600080604083850312156152425760008061523f6144f0565b50505b600061525085828601614f40565b925050602061526185828601614cb2565b9150509250929050565b600080600060608486031215615289576000806152866144f0565b50505b600061529786828701614f40565b93505060206152a886828701614cb2565b92505060406152b986828701614f40565b9150509250925092565b600080604083850312156152df576000806152dc6144f0565b50505b60006152ed85828601614f40565b92505060206152fe85828601614f40565b9150509250929050565b60008060008060808587031215615327576000806153246144f0565b50505b600061533587828801614f40565b945050602061534687828801614f40565b935050604085013567ffffffffffffffff81111561536c576000806153696144f0565b50505b61537887828801614b83565b925050606085013567ffffffffffffffff81111561539e5760008061539b6144f0565b50505b6153aa87828801614b83565b91505092959194509250565b6000806000606084860312156153d4576000806153d16144f0565b50505b60006153e286828701614f40565b93505060206153f386828701614f40565b925050604061540486828701614f40565b9150509250925092565b60008060006060848603121561542c576000806154296144f0565b50505b600061543a86828701614f55565b935050602061544b86828701614f55565b925050604061545c86828701614f55565b9150509250925092565b60006154728383615a3f565b60208301905092915050565b615487816160ff565b82525050565b61549681615fdb565b82525050565b60006154a782615f97565b6154b18185615faf565b93506154bc83615f72565b8060005b838110156154ed5781516154d48882615466565b97506154df83615fa2565b9250506001810190506154c0565b5085935050505092915050565b61550381615fed565b82525050565b61551281615fed565b82525050565b61552181615ff9565b82525050565b61553081616111565b82525050565b61553f81616111565b82525050565b61554e81616135565b82525050565b61555d81616159565b82525050565b61556c8161616b565b82525050565b61557b816160cb565b82525050565b60008161558c6145bb565b60018116600081146155a557600181146155cb57615616565b607f60028304166155b68187615fc0565b955060ff198316865260208601935050615616565b600282046155d98187615fc0565b95506155e485615f82565b60005b8281101561560d57816155f86145bb565b818901526001820191506020810190506155e7565b80880195505050505b505092915050565b600061562b601383615fc0565b91507f616c726561647920696e697469616c697a6564000000000000000000000000006000830152602082019050919050565b600061566b600f83615fc0565b91507f6572726f72206d736720636f756e7400000000000000000000000000000000006000830152602082019050919050565b60e0820160008201516156b46000850182615a3f565b5060208201516156c76020850182615518565b5060408201516156da6040850182615518565b5060608201516156ed6060850182615545565b5060808201516157006080850182615527565b5060a082015161571360a0850182615a3f565b5060c082015161572660c0850182615a3f565b50505050565b6080820160008201516157426000850182615a3f565b5060208201516157556020850182615a3f565b5060408201516157686040850182615a3f565b50606082015161577b6060850182615a3f565b50505050565b6080820160008201516157976000850182615a3f565b5060208201516157aa6020850182615a3f565b5060408201516157bd6040850182615a3f565b5060608201516157d06060850182615a3f565b50505050565b610100820160008083016157e86145bb565b90506157f3816161b0565b6158006000860182615a3f565b506001830161580d6145bb565b9050615818816161b0565b6158256020860182615a3f565b50600283016158326145bb565b905061583d816161b0565b61584a6040860182615a3f565b50600383016158576145bb565b9050615862816161b0565b61586f6060860182615a3f565b506004830161587c6145bb565b9050615887816161b0565b6158946080860182615a3f565b50600583016158a16145bb565b90506158ac816161b0565b6158b960a0860182615a3f565b50600683016158c66145bb565b90506158d1816161b0565b6158de60c0860182615a3f565b50600783016158eb6145bb565b90506158f6816161b0565b61590360e0860182615a3f565b5050505050565b610140820160008201516159216000850182615a3f565b5060208201516159346020850182615a3f565b5060408201516159476040850182615a3f565b50606082015161595a6060850182615a3f565b50608082015161596d6080850182615a3f565b5060a082015161598060a0850182615a3f565b5060c082015161599360c0850182615572565b5060e08201516159a660e0850182615572565b506101008201516159bb610100850182615a3f565b506101208201516159d0610120850182615a3f565b50505050565b610100820160008201516159ed60008501826154fa565b506020820151615a006020850182615a3f565b506040820151615a136040850182615a3f565b506060820151615a266060850182615a3f565b506080820151615a39608085018261572c565b50505050565b615a48816160f5565b82525050565b615a57816160f5565b82525050565b6000606082019050615a72600083018661547e565b615a7f602083018561548d565b615a8c6040830184615a4e565b949350505050565b6000604082019050615aa9600083018561547e565b615ab66020830184615a4e565b9392505050565b6000606082019050615ad2600083018661547e565b615adf6020830185615a4e565b615aec6040830184615a4e565b949350505050565b6000604082019050615b09600083018561548d565b615b166020830184615509565b9392505050565b6000604082019050615b32600083018561548d565b615b3f6020830184615554565b9392505050565b6000604082019050615b5b600083018561548d565b615b686020830184615a4e565b9392505050565b60006020820190508181036000830152615b89818461549c565b905092915050565b6000602082019050615ba66000830184615509565b92915050565b60006020820190508181036000830152615bc68184615581565b905092915050565b60006020820190508181036000830152615be78161561e565b9050919050565b60006020820190508181036000830152615c078161565e565b9050919050565b600061036082019050615c2460008301876157d6565b615c326101008301866159d6565b615c4061020083018561590a565b615c4e610340830184615a4e565b95945050505050565b6000602082019050615c6c6000830184615a4e565b92915050565b600060e082019050615c87600083018a615a4e565b615c94602083018961547e565b615ca16040830188615563565b615cae6060830187615a4e565b615cbb6080830186615a4e565b615cc860a0830185615a4e565b615cd560c0830184615a4e565b98975050505050505050565b6000604082019050615cf66000830185615a4e565b615d036020830184615536565b9392505050565b600061018082019050615d206000830186615a4e565b615d2d602083018561569e565b615d3b610100830184615781565b949350505050565b6000604082019050615d586000830185615a4e565b615d656020830184615a4e565b9392505050565b6000606082019050615d816000830186615a4e565b615d8e6020830185615a4e565b615d9b6040830184615a4e565b949350505050565b6000608082019050615db86000830187615a4e565b615dc56020830186615a4e565b615dd26040830185615a4e565b615ddf6060830184615509565b95945050505050565b600060a082019050615dfd6000830188615a4e565b615e0a6020830187615a4e565b615e176040830186615a4e565b615e246060830185615a4e565b615e316080830184615a4e565b9695505050505050565b600061010082019050615e51600083018b615a4e565b615e5e602083018a615a4e565b615e6b6040830189615a4e565b615e786060830188615a4e565b615e856080830187615a4e565b615e9260a0830186615a4e565b615e9f60c0830185615a4e565b615eac60e0830184615a4e565b9998505050505050505050565b6000604051905081810181811067ffffffffffffffff82111715615ee057615edf6161ca565b5b8060405250919050565b600067ffffffffffffffff821115615f0557615f046161ca565b5b602082029050602081019050919050565b600067ffffffffffffffff821115615f3157615f306161ca565b5b602082029050602081019050919050565b600067ffffffffffffffff821115615f5d57615f5c6161ca565b5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b60008190508160005260206000209050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000819050919050565b6000615fe6826160d5565b9050919050565b60008115159050919050565b6000819050919050565b600061600e82615fdb565b9050919050565b600061602082615fdb565b9050919050565b600061603282615fdb565b9050919050565b600061604482615fdb565b9050919050565b600061605682615fdb565b9050919050565b600061606882615fdb565b9050919050565b600061607a82615fdb565b9050919050565b600061608c82615fdb565b9050919050565b600061609e82615fdb565b9050919050565b60008190506160b3826161d9565b919050565b60008190506160c6826161ed565b919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061610a8261617d565b9050919050565b600061611c82616123565b9050919050565b600061612e826160d5565b9050919050565b600061614082616147565b9050919050565b6000616152826160d5565b9050919050565b6000616164826160a5565b9050919050565b6000616176826160b8565b9050919050565b60006161888261618f565b9050919050565b600061619a826160d5565b9050919050565b82818337600083830152505050565b60006161c36161be836161cc565b615fd1565b9050919050565bfe5b60008160001c9050919050565b600381106161ea576161e96161ca565b5b50565b600481106161fe576161fd6161ca565b5b50565b61620a81615fdb565b811461621e5760008061621b6144f0565b50505b50565b61622a81615fed565b811461623e5760008061623b6144f0565b50505b50565b61624a81615ff9565b811461625e5760008061625b6144f0565b50505b50565b61626a81616003565b811461627e5760008061627b6144f0565b50505b50565b61628a81616015565b811461629e5760008061629b6144f0565b50505b50565b6162aa81616027565b81146162be576000806162bb6144f0565b50505b50565b6162ca81616039565b81146162de576000806162db6144f0565b50505b50565b6162ea8161604b565b81146162fe576000806162fb6144f0565b50505b50565b61630a8161605d565b811461631e5760008061631b6144f0565b50505b50565b61632a8161606f565b811461633e5760008061633b6144f0565b50505b50565b61634a81616081565b811461635e5760008061635b6144f0565b50505b50565b61636a81616093565b811461637e5760008061637b6144f0565b50505b50565b60048110616397576000806163946144f0565b50505b50565b6163a3816160cb565b81146163b7576000806163b46144f0565b50505b50565b6163c3816160f5565b81146163d7576000806163d46144f0565b50505b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101165760003560e01c80639b805da8116100a2578063d1e9e81111610071578063d1e9e8111461030d578063d6ef9be31461033d578063dd2f0d3614610359578063f2333a5814610389578063f2fde38b146103a557610116565b80639b805da814610275578063a92e61eb146102a5578063a9c9d125146102c1578063c39eeefb146102dd57610116565b806335359675116100e957806335359675146101a857806342a47e13146101db5780637e7088eb146101f7578063862a33941461021557806397dd25241461024557610116565b806318cc7e861461011b5780632eb6534f146101375780632f4aff0e146101555780633289cf8f14610171575b600080fd5b6101356004803603810190610130919061400f565b6103c1565b005b61013f610533565b60405161014c9190614927565b60405180910390f35b61016f600480360381019061016a91906140de565b610539565b005b61018b60048036038101906101869190613ef6565b610661565b60405161019f989796959493929190614b0b565b60405180910390f35b6101c260048036038101906101bd9190613ef6565b6106a9565b6040516101d29493929190614a73565b60405180910390f35b6101f560048036038101906101f09190613ef6565b6106e6565b005b6101ff6108f4565b60405161020c919061483f565b60405180910390f35b61022f600480360381019061022a9190613fc0565b610995565b60405161023c9190614927565b60405180910390f35b61025f600480360381019061025a9190613fc0565b61148b565b60405161026c9190614927565b60405180910390f35b61028f600480360381019061028a9190613ef6565b611ebb565b60405161029c9190614927565b60405180910390f35b6102bf60048036038101906102ba9190613f84565b611ed3565b005b6102db60048036038101906102d69190613f48565b61211e565b005b6102f760048036038101906102f29190613ef6565b61220c565b604051610304919061483f565b60405180910390f35b61032760048036038101906103229190613ef6565b6122f1565b6040516103349190614927565b60405180910390f35b6103576004803603810190610352919061400f565b612309565b005b610373600480360381019061036e919061404b565b612526565b6040516103809190614927565b60405180910390f35b6103a3600480360381019061039e9190613d74565b612825565b005b6103bf60048036038101906103ba9190613d22565b612b45565b005b61041c3373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e6000848152602001908152602001600020905061045e83826000015414801561045757508160030160009054906101000a900460ff165b6002612c9b565b818160020181905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663551c855584846040518363ffffffff1660e01b81526004016104c4929190614a13565b600060405180830381600087803b1580156104de57600080fd5b505af11580156104f2573d6000803e3d6000fd5b50505050827f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce881836040516105269190614927565b60405180910390a2505050565b600d5481565b6105943373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e600085815260200190815260200160002090506105bc848260000154146001612c9b565b60006105c9858585612d00565b9050600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d59f359386836040518363ffffffff1660e01b8152600401610628929190614a13565b600060405180830381600087803b15801561064257600080fd5b505af1158015610656573d6000803e3d6000fd5b505050505050505050565b600f6020528060005260406000206000915090508060000154908060010154908060020154908060030154908060040154908060050154908060060154908060070154905088565b600e6020528060005260406000206000915090508060000154908060010154908060020154908060030160009054906101000a900460ff16905084565b6000600e60008381526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820160009054906101000a900460ff161515151581526020016004820180548060200260200160405190810160405280929190818152602001828054801561078e57602002820191906000526020600020905b81548152602001906001019080831161077a575b50505050508152505090506107ab4282602001511115600b612c9b565b6000805b600c8054905081101561084d5783600c82815481106107ca57fe5b9060005260206000200154141561084057600c6001600c8054905003815481106107f057fe5b9060005260206000200154600c828154811061080857fe5b9060005260206000200181905550600c80548061082157fe5b600190038181906000526020600020016000905590556001915061084d565b80806001019150506107af565b5061085981600c612c9b565b61086282612e3b565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dba5082a846040518263ffffffff1660e01b81526004016108bd9190614927565b600060405180830381600087803b1580156108d757600080fd5b505af11580156108eb573d6000803e3d6000fd5b50505050505050565b6060600c8054905067ffffffffffffffff8111801561091257600080fd5b506040519080825280602002602001820160405280156109415781602001602082028036833780820191505090505b50905060005b600c8054905081101561099157600c818154811061096157fe5b906000526020600020015482828151811061097857fe5b6020026020010181815250508080600101915050610947565b5090565b60006109c76000831380156109c057508360038111156109b157fe5b6003808111156109bd57fe5b10155b6007612c9b565b60008060038111156109d557fe5b8460038111156109e157fe5b1480610a035750600260038111156109f557fe5b846003811115610a0157fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701548152602001908152602001600020905060008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b1230886040518363ffffffff1660e01b8152600401610a989291906147c4565b6102406040518083038186803b158015610ab157600080fd5b505afa158015610ac5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ae99190613ea4565b925092509250610b1d8460030160009054906101000a900460ff16158015610b1657508460010154824201105b6008612c9b565b60006040518060a0016040528088151581526020018a8152602001610b538860020154886002015461332a90919063ffffffff16565b815260200186600101548152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b8152600401610bc79291906149b1565b60806040518083038186803b158015610bdf57600080fd5b505afa158015610bf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c179190613e7b565b8152509050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663156e29f6338c6003811115610c6757fe5b8e018c6040518463ffffffff1660e01b8152600401610c889392919061478d565b600060405180830381600087803b158015610ca257600080fd5b505af1158015610cb6573d6000803e3d6000fd5b5050505060006003811115610cc757fe5b8a6003811115610cd357fe5b1415610cfd57610cf089876003015461334690919063ffffffff16565b8660030181905550610da5565b60016003811115610d0a57fe5b8a6003811115610d1657fe5b1415610d4057610d3389876004015461334690919063ffffffff16565b8660040181905550610da4565b60026003811115610d4d57fe5b8a6003811115610d5957fe5b1415610d8357610d7689876005015461334690919063ffffffff16565b8660050181905550610da3565b610d9a89876006015461334690919063ffffffff16565b86600601819055505b5b5b610db1868683876133ce565b975060006003811115610dc057fe5b8a6003811115610dcc57fe5b1415610f4957600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a00dad228a8584608001516040518463ffffffff1660e01b8152600401610e35939291906149da565b600060405180830381600087803b158015610e4f57600080fd5b505af1158015610e63573d6000803e3d6000fd5b50505050610f44600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b8152600401610eeb9392919061472d565b602060405180830381600087803b158015610f0557600080fd5b505af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190613d4b565b6009612c9b565b611420565b60026003811115610f5657fe5b8a6003811115610f6257fe5b14156110f757600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f238ff5c610fbd88600101548c61352990919063ffffffff16565b8360800151600001516040518363ffffffff1660e01b8152600401610fe3929190614a13565b600060405180830381600087803b158015610ffd57600080fd5b505af1158015611011573d6000803e3d6000fd5b505050506110f2600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b81526004016110999392919061472d565b602060405180830381600087803b1580156110b357600080fd5b505af11580156110c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110eb9190613d4b565b6009612c9b565b61141f565b6001600381111561110457fe5b8a600381111561111057fe5b1415611291576111f3600760009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168d6040518463ffffffff1660e01b815260040161119a9392919061472d565b602060405180830381600087803b1580156111b457600080fd5b505af11580156111c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ec9190613d4b565b600a612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b815260040161125a9392919061478d565b600060405180830381600087803b15801561127457600080fd5b505af1158015611288573d6000803e3d6000fd5b5050505061141e565b611384600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1661130d8b600101548f61352990919063ffffffff16565b6040518463ffffffff1660e01b815260040161132b9392919061472d565b602060405180830381600087803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137d9190613d4b565b6009612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b81526004016113eb9392919061478d565b600060405180830381600087803b15801561140557600080fd5b505af1158015611419573d6000803e3d6000fd5b505050505b5b5b89600381111561142c57fe5b8b3373ffffffffffffffffffffffffffffffffffffffff167f9a99f0d83ea15216987ecd4ed332a7338eafafe12db86adf283528ec63c02d3f8c8c604051611475929190614a13565b60405180910390a4505050505050509392505050565b60006114bd6000831380156114b657508360038111156114a757fe5b6003808111156114b357fe5b10155b6007612c9b565b60008060038111156114cb57fe5b8460038111156114d757fe5b14806114f95750600260038111156114eb57fe5b8460038111156114f757fe5b145b90506000600f600087815260200190815260200160002090506000600e600083600701548152602001908152602001600020905060008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663be146b123088156040518363ffffffff1660e01b815260040161158f9291906147c4565b6102406040518083038186803b1580156115a857600080fd5b505afa1580156115bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e09190613ea4565b9250925092506116148460030160009054906101000a900460ff1615801561160d57508460010154824201105b6008612c9b565b60006040518060a001604052808815151581526020018a815260200161164b8860020154886002015461332a90919063ffffffff16565b815260200186600101548152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f2f757d866000015187608001516040518363ffffffff1660e01b81526004016116bf9291906149b1565b60806040518083038186803b1580156116d757600080fd5b505afa1580156116eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170f9190613e7b565b8152509050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca338c600381111561175f57fe5b8e018c6040518463ffffffff1660e01b81526004016117809392919061478d565b600060405180830381600087803b15801561179a57600080fd5b505af11580156117ae573d6000803e3d6000fd5b50505050600060038111156117bf57fe5b8a60038111156117cb57fe5b14156117f5576117e889876003015461355690919063ffffffff16565b866003018190555061189d565b6001600381111561180257fe5b8a600381111561180e57fe5b14156118385761182b89876004015461355690919063ffffffff16565b866004018190555061189c565b6002600381111561184557fe5b8a600381111561185157fe5b141561187b5761186e89876005015461355690919063ffffffff16565b866005018190555061189b565b61189289876006015461355690919063ffffffff16565b86600601819055505b5b5b6118a9868683876133ce565b9750600060038111156118b857fe5b8a60038111156118c457fe5b14156119f557600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663dc62c5818a6040518263ffffffff1660e01b81526004016119259190614927565b600060405180830381600087803b15801561193f57600080fd5b505af1158015611953573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b81526004016119be9392919061478d565b600060405180830381600087803b1580156119d857600080fd5b505af11580156119ec573d6000803e3d6000fd5b50505050611e50565b60026003811115611a0257fe5b8a6003811115611a0e57fe5b1415611b5557600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663db32232f611a6988600101548c61352990919063ffffffff16565b6040518263ffffffff1660e01b8152600401611a859190614927565b600060405180830381600087803b158015611a9f57600080fd5b505af1158015611ab3573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c9061b58338a8460800151600001516040518463ffffffff1660e01b8152600401611b1e9392919061478d565b600060405180830381600087803b158015611b3857600080fd5b505af1158015611b4c573d6000803e3d6000fd5b50505050611e4f565b60016003811115611b6257fe5b8a6003811115611b6e57fe5b1415611ce557600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16638b89a242338b6040518363ffffffff1660e01b8152600401611bd1929190614764565b600060405180830381600087803b158015611beb57600080fd5b505af1158015611bff573d6000803e3d6000fd5b50505050611ce0600660009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166323b872dd33600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168c6040518463ffffffff1660e01b8152600401611c879392919061472d565b602060405180830381600087803b158015611ca157600080fd5b505af1158015611cb5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cd99190613d4b565b6009612c9b565b611e4e565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b30733611d4d8b611d3f8b600101548f61352990919063ffffffff16565b61355690919063ffffffff16565b6040518363ffffffff1660e01b8152600401611d6a929190614764565b600060405180830381600087803b158015611d8457600080fd5b505af1158015611d98573d6000803e3d6000fd5b50505050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16635318b307600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168a6040518363ffffffff1660e01b8152600401611e1b929190614816565b600060405180830381600087803b158015611e3557600080fd5b505af1158015611e49573d6000803e3d6000fd5b505050505b5b5b896003811115611e5c57fe5b8b3373ffffffffffffffffffffffffffffffffffffffff167f5a0cd91f269c536b8167c8fc4e2b859e3db49a3fb022e5db3e380a58975405848c8c604051611ea5929190614a13565b60405180910390a4505050505050509392505050565b60116020528060005260406000206000915090505481565b6000600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1662fdd58e33846003811115611f1f57fe5b86016040518363ffffffff1660e01b8152600401611f3e929190614764565b60206040518083038186803b158015611f5657600080fd5b505afa158015611f6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f8e9190613f1f565b9050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166337af0a2884338585600f60008a81526020019081526020016000206001015460106000600f60008d815260200190815260200160002060070154815260200190815260200160002054601160008c8152602001908152602001600020546040518863ffffffff1660e01b81526004016120499796959493929190614942565b600060405180830381600087803b15801561206357600080fd5b505af1158015612077573d6000803e3d6000fd5b50505050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663f5298aca338460038111156120c657fe5b8601846040518463ffffffff1660e01b81526004016120e79392919061478d565b600060405180830381600087803b15801561210157600080fd5b505af1158015612115573d6000803e3d6000fd5b50505050505050565b6121793373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600e600084815260200190815260200160002090506121a1838260000154146001612c9b565b81600e600085815260200190815260200160002060030160006101000a81548160ff021916908315150217905550827fab7e756517bb425436c10403644a884802e0b2d5105f9f5386823b7c42ca5d5f836040516121ff9190614861565b60405180910390a2505050565b60606000600e60008481526020019081526020016000206004018054905067ffffffffffffffff8111801561224057600080fd5b5060405190808252806020026020018201604052801561226f5781602001602082028036833780820191505090505b50905060005b600e6000858152602001908152602001600020600401805490508110156122e757600e600085815260200190815260200160002060040181815481106122b757fe5b90600052602060002001548282815181106122ce57fe5b6020026020010181815250508080600101915050612275565b5080915050919050565b60106020528060005260406000206000915090505481565b6123643373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b6000600f600084815260200190815260200160002090506000600e6000836007015481526020019081526020016000206040518060a00160405290816000820154815260200160018201548152602001600282015481526020016003820160009054906101000a900460ff161515151581526020016004820180548060200260200160405190810160405280929190818152602001828054801561242757602002820191906000526020600020905b815481526020019060010190808311612413575b5050505050815250509050612450848360000154148015612449575081606001515b6003612c9b565b828260020181905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d6ef9be385856040518363ffffffff1660e01b81526004016124b6929190614a13565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b50505050837f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957846040516125189190614927565b60405180910390a250505050565b60006125833373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b61259f82518451148015612598575060008451115b6004612c9b565b6125c1625c49006125b9428861355690919063ffffffff16565b106005612c9b565b600d54851115612677576125de6000600c80549050146006612c9b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663c3964372600d54876040518363ffffffff1660e01b815260040161263d929190614a13565b600060405180830381600087803b15801561265757600080fd5b505af115801561266b573d6000803e3d6000fd5b5050505084600d819055505b6000600b600081548092919060010191905055905080600e60008381526020019081526020016000206000018190555085600e60008381526020019081526020016000206001018190555084600e600083815260200190815260200160002060020181905550600c819080600181540180825580915050600190039060005260206000200160009091909190915055807f2eb8cef7f145b51f7d30c1ffc842e46161a2b1624415ce69192c99ec2106ad878787604051612738929190614a13565b60405180910390a260005b845181101561278b5761277d8286838151811061275c57fe5b602002602001015186848151811061277057fe5b6020026020010151612d00565b508080600101915050612743565b50600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630d5766f7826040518263ffffffff1660e01b81526004016127e79190614927565b600060405180830381600087803b15801561280157600080fd5b505af1158015612815573d6000803e3d6000fd5b5050505080915050949350505050565b600960149054906101000a900460ff1615612875576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161286c9061489e565b60405180910390fd5b886000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555087600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555086600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555085600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555084600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555083600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600660006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600760006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600e80811115612a8857fe5b815114612aca576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612ac1906148be565b60405180910390fd5b60005b8151811015612b1e57818181518110612ae257fe5b6020026020010151600860008381526020019081526020016000209080519060200190612b10929190613777565b508080600101915050612acd565b506001600960146101000a81548160ff021916908315150217905550505050505050505050565b612ba03373ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614600d612c9b565b612bdb600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156000612c9b565b8073ffffffffffffffffffffffffffffffffffffffff16600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a380600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b816008600083600e811115612cac57fe5b815260200190815260200160002090612cfb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612cf2919061487c565b60405180910390fd5b505050565b6000600a5490506004600a600082825401925050819055506040518061010001604052808281526020018481526020018381526020016000815260200160008152602001600081526020016000815260200185815250600f6000838152602001908152602001600020600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070155905050600e600085815260200190815260200160002060040181908060018154018082558091505060019003906000526020600020016000909190919091505580847fb022fbd99a76d7d6d20bd5b22c69b0e8bd1b756764c76b117306d29bbc16e09d8585604051612e2c929190614a13565b60405180910390a39392505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166305b7f2f63060026040518363ffffffff1660e01b8152600401612e9a9291906147ed565b60e06040518083038186803b158015612eb257600080fd5b505afa158015612ec6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eea9190613e52565b90506000806000806000856000015160106000896000015181526020019081526020016000208190555060005b8760800151518110156131bc576000600f60008a608001518481518110612f3a57fe5b6020026020010151815260200190815260200160002060405180610100016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815260200160058201548152602001600682015481526020016007820154815250509050612fc581606001518761334690919063ffffffff16565b9550612ff4612fe582602001518360a0015161352990919063ffffffff16565b8661334690919063ffffffff16565b945080602001518860000151111561310f5761303961302a82602001518a6000015103836060015161352990919063ffffffff16565b8861334690919063ffffffff16565b9650600061308d896000015161307f6130668c60c00151601260ff16600a0a61355690919063ffffffff16565b85602001518d60000151036135d990919063ffffffff16565b6135d990919063ffffffff16565b9050601260ff16600a0a8111156130a957601260ff16600a0a90505b6130d26130c383608001518361352990919063ffffffff16565b8661334690919063ffffffff16565b94506130ee81601260ff16600a0a61355690919063ffffffff16565b60116000846000015181526020019081526020016000208190555050613133565b601260ff16600a0a6011600083600001518152602001908152602001600020819055505b8060200151886000015110156131ae5761317661316789600001518360200151038360a0015161352990919063ffffffff16565b8861334690919063ffffffff16565b96506131ab61319c8260c001518a6000015184602001510361352990919063ffffffff16565b8461334690919063ffffffff16565b92505b508080600101915050612f17565b50600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663226ec41583836040518363ffffffff1660e01b815260040161321a929190614a13565b600060405180830381600087803b15801561323457600080fd5b505af1158015613248573d6000803e3d6000fd5b50505050600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633f6b50198487876040518463ffffffff1660e01b81526004016132ab93929190614a3c565b600060405180830381600087803b1580156132c557600080fd5b505af11580156132d9573d6000803e3d6000fd5b5050505086600001517f3f24fabf249b0bbc2acff84ed6a1b001961a11a3d32fc78ec28407708d3c238b8686868686604051613319959493929190614ab8565b60405180910390a250505050505050565b600061333e8383601260ff16600a0a61360f565b905092915050565b6000808284019050838110156133c4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b600080600080600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663af579a118988888b600201546040518563ffffffff1660e01b815260040161343994939291906148de565b606060405180830381600087803b15801561345357600080fd5b505af1158015613467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061348b919061412d565b92509250925080886002018190555081876002018190555086600001547f27dc10bc12529bac536af6dbf5d4b270673ac7aeb848c334e4038ef55ecce881836040516134d79190614927565b60405180910390a287600001547f807f7e992c23befc534fdba4f8206693b5b6c4dd10b67620b355911654d0a957826040516135139190614927565b60405180910390a2829350505050949350505050565b6000601260ff16600a0a613546838561366890919063ffffffff16565b8161354d57fe5b04905092915050565b6000828211156135ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525060200191505060405180910390fd5b818303905092915050565b6000613607826135f9601260ff16600a0a8661366890919063ffffffff16565b6136ee90919063ffffffff16565b905092915050565b600080600a838161361c57fe5b04613630858761366890919063ffffffff16565b8161363757fe5b0490506005600a828161364657fe5b061061365357600a810190505b600a818161365d57fe5b049150509392505050565b60008083141561367b57600090506136e8565b600082840290508284828161368c57fe5b04146136e3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806150246021913960400191505060405180910390fd5b809150505b92915050565b6000808211613765576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525060200191505060405180910390fd5b81838161376e57fe5b04905092915050565b828054600181600116156101000203166002900490600052602060002090601f0160209004810192826137ad57600085556137f4565b82601f106137c657805160ff19168380011785556137f4565b828001600101855582156137f4579182015b828111156137f35782518255916020019190600101906137d8565b5b5090506138019190613805565b5090565b5b8082111561381e576000816000905550600101613806565b5090565b600061383561383084614bba565b614b89565b9050808382526020820190508260005b85811015613875578135850161385b8882613ab8565b845260208401935060208301925050600181019050613845565b5050509392505050565b600061389261388d84614be6565b614b89565b905080838252602082019050828560208602820111156138b157600080fd5b60005b858110156138e157816138c78882613cf8565b8452602084019350602083019250506001810190506138b4565b5050509392505050565b60006138fe6138f984614c12565b614b89565b90508281526020810184848401111561391657600080fd5b613921848285614e71565b509392505050565b60008135905061393881614ed1565b92915050565b600082601f83011261394f57600080fd5b813561395f848260208601613822565b91505092915050565b600082601f83011261397957600080fd5b813561398984826020860161387f565b91505092915050565b6000813590506139a181614ee8565b92915050565b6000815190506139b681614ee8565b92915050565b6000815190506139cb81614eff565b92915050565b6000815190506139e081614f16565b92915050565b6000813590506139f581614f2d565b92915050565b600081359050613a0a81614f44565b92915050565b600081359050613a1f81614f5b565b92915050565b600081359050613a3481614f72565b92915050565b600081359050613a4981614f89565b92915050565b600081359050613a5e81614fa0565b92915050565b600081359050613a7381614fb7565b92915050565b600081519050613a8881614fce565b92915050565b600081359050613a9d81614fe5565b92915050565b600081519050613ab281614ff5565b92915050565b600082601f830112613ac957600080fd5b8135613ad98482602086016138eb565b91505092915050565b600060e08284031215613af457600080fd5b613afe60e0614b89565b90506000613b0e84828501613d0d565b6000830152506020613b22848285016139bc565b6020830152506040613b36848285016139bc565b6040830152506060613b4a84828501613a79565b6060830152506080613b5e848285016139d1565b60808301525060a0613b7284828501613d0d565b60a08301525060c0613b8684828501613d0d565b60c08301525092915050565b600060808284031215613ba457600080fd5b613bae6080614b89565b90506000613bbe84828501613d0d565b6000830152506020613bd284828501613d0d565b6020830152506040613be684828501613d0d565b6040830152506060613bfa84828501613d0d565b60608301525092915050565b60006101408284031215613c1957600080fd5b613c24610140614b89565b90506000613c3484828501613d0d565b6000830152506020613c4884828501613d0d565b6020830152506040613c5c84828501613d0d565b6040830152506060613c7084828501613d0d565b6060830152506080613c8484828501613d0d565b60808301525060a0613c9884828501613d0d565b60a08301525060c0613cac84828501613aa3565b60c08301525060e0613cc084828501613aa3565b60e083015250610100613cd584828501613d0d565b61010083015250610120613ceb84828501613d0d565b6101208301525092915050565b600081359050613d078161500c565b92915050565b600081519050613d1c8161500c565b92915050565b600060208284031215613d3457600080fd5b6000613d4284828501613929565b91505092915050565b600060208284031215613d5d57600080fd5b6000613d6b848285016139a7565b91505092915050565b60008060008060008060008060006101208a8c031215613d9357600080fd5b6000613da18c828d01613a10565b9950506020613db28c828d016139fb565b9850506040613dc38c828d01613a3a565b9750506060613dd48c828d01613a25565b9650506080613de58c828d01613a64565b95505060a0613df68c828d01613a4f565b94505060c0613e078c828d016139e6565b93505060e0613e188c828d016139e6565b9250506101008a013567ffffffffffffffff811115613e3657600080fd5b613e428c828d0161393e565b9150509295985092959850929598565b600060e08284031215613e6457600080fd5b6000613e7284828501613ae2565b91505092915050565b600060808284031215613e8d57600080fd5b6000613e9b84828501613b92565b91505092915050565b60008060006102408486031215613eba57600080fd5b6000613ec886828701613c06565b935050610140613eda86828701613ae2565b925050610220613eec86828701613d0d565b9150509250925092565b600060208284031215613f0857600080fd5b6000613f1684828501613cf8565b91505092915050565b600060208284031215613f3157600080fd5b6000613f3f84828501613d0d565b91505092915050565b60008060408385031215613f5b57600080fd5b6000613f6985828601613cf8565b9250506020613f7a85828601613992565b9150509250929050565b60008060408385031215613f9757600080fd5b6000613fa585828601613cf8565b9250506020613fb685828601613a8e565b9150509250929050565b600080600060608486031215613fd557600080fd5b6000613fe386828701613cf8565b9350506020613ff486828701613a8e565b925050604061400586828701613cf8565b9150509250925092565b6000806040838503121561402257600080fd5b600061403085828601613cf8565b925050602061404185828601613cf8565b9150509250929050565b6000806000806080858703121561406157600080fd5b600061406f87828801613cf8565b945050602061408087828801613cf8565b935050604085013567ffffffffffffffff81111561409d57600080fd5b6140a987828801613968565b925050606085013567ffffffffffffffff8111156140c657600080fd5b6140d287828801613968565b91505092959194509250565b6000806000606084860312156140f357600080fd5b600061410186828701613cf8565b935050602061411286828701613cf8565b925050604061412386828701613cf8565b9150509250925092565b60008060006060848603121561414257600080fd5b600061415086828701613d0d565b935050602061416186828701613d0d565b925050604061417286828701613d0d565b9150509250925092565b6000614188838361470f565b60208301905092915050565b61419d81614dcf565b82525050565b6141ac81614cab565b82525050565b60006141bd82614c67565b6141c78185614c7f565b93506141d283614c42565b8060005b838110156142035781516141ea888261417c565b97506141f583614c72565b9250506001810190506141d6565b5085935050505092915050565b61421981614cbd565b82525050565b61422881614cbd565b82525050565b61423781614cc9565b82525050565b61424681614de1565b82525050565b61425581614de1565b82525050565b61426481614e05565b82525050565b61427381614e29565b82525050565b61428281614e3b565b82525050565b61429181614d9b565b82525050565b6000815460018116600081146142b457600181146142da5761431e565b607f60028304166142c58187614c90565b955060ff19831686526020860193505061431e565b600282046142e88187614c90565b95506142f385614c52565b60005b82811015614315578154818901526001820191506020810190506142f6565b80880195505050505b505092915050565b6000614333601383614c90565b91507f616c726561647920696e697469616c697a6564000000000000000000000000006000830152602082019050919050565b6000614373600f83614c90565b91507f6572726f72206d736720636f756e7400000000000000000000000000000000006000830152602082019050919050565b60e0820160008201516143bc600085018261470f565b5060208201516143cf602085018261422e565b5060408201516143e2604085018261422e565b5060608201516143f5606085018261425b565b506080820151614408608085018261423d565b5060a082015161441b60a085018261470f565b5060c082015161442e60c085018261470f565b50505050565b60808201600082015161444a600085018261470f565b50602082015161445d602085018261470f565b506040820151614470604085018261470f565b506060820151614483606085018261470f565b50505050565b60808201600082015161449f600085018261470f565b5060208201516144b2602085018261470f565b5060408201516144c5604085018261470f565b5060608201516144d8606085018261470f565b50505050565b610100820160008083015490506144f481614e80565b614501600086018261470f565b506001830154905061451281614e80565b61451f602086018261470f565b506002830154905061453081614e80565b61453d604086018261470f565b506003830154905061454e81614e80565b61455b606086018261470f565b506004830154905061456c81614e80565b614579608086018261470f565b506005830154905061458a81614e80565b61459760a086018261470f565b50600683015490506145a881614e80565b6145b560c086018261470f565b50600783015490506145c681614e80565b6145d360e086018261470f565b5050505050565b610140820160008201516145f1600085018261470f565b506020820151614604602085018261470f565b506040820151614617604085018261470f565b50606082015161462a606085018261470f565b50608082015161463d608085018261470f565b5060a082015161465060a085018261470f565b5060c082015161466360c0850182614288565b5060e082015161467660e0850182614288565b5061010082015161468b61010085018261470f565b506101208201516146a061012085018261470f565b50505050565b610100820160008201516146bd6000850182614210565b5060208201516146d0602085018261470f565b5060408201516146e3604085018261470f565b5060608201516146f6606085018261470f565b5060808201516147096080850182614434565b50505050565b61471881614dc5565b82525050565b61472781614dc5565b82525050565b60006060820190506147426000830186614194565b61474f60208301856141a3565b61475c604083018461471e565b949350505050565b60006040820190506147796000830185614194565b614786602083018461471e565b9392505050565b60006060820190506147a26000830186614194565b6147af602083018561471e565b6147bc604083018461471e565b949350505050565b60006040820190506147d960008301856141a3565b6147e6602083018461421f565b9392505050565b600060408201905061480260008301856141a3565b61480f602083018461426a565b9392505050565b600060408201905061482b60008301856141a3565b614838602083018461471e565b9392505050565b6000602082019050818103600083015261485981846141b2565b905092915050565b6000602082019050614876600083018461421f565b92915050565b600060208201905081810360008301526148968184614297565b905092915050565b600060208201905081810360008301526148b781614326565b9050919050565b600060208201905081810360008301526148d781614366565b9050919050565b6000610360820190506148f460008301876144de565b6149026101008301866146a6565b6149106102008301856145da565b61491e61034083018461471e565b95945050505050565b600060208201905061493c600083018461471e565b92915050565b600060e082019050614957600083018a61471e565b6149646020830189614194565b6149716040830188614279565b61497e606083018761471e565b61498b608083018661471e565b61499860a083018561471e565b6149a560c083018461471e565b98975050505050505050565b60006040820190506149c6600083018561471e565b6149d3602083018461424c565b9392505050565b6000610180820190506149f0600083018661471e565b6149fd60208301856143a6565b614a0b610100830184614489565b949350505050565b6000604082019050614a28600083018561471e565b614a35602083018461471e565b9392505050565b6000606082019050614a51600083018661471e565b614a5e602083018561471e565b614a6b604083018461471e565b949350505050565b6000608082019050614a88600083018761471e565b614a95602083018661471e565b614aa2604083018561471e565b614aaf606083018461421f565b95945050505050565b600060a082019050614acd600083018861471e565b614ada602083018761471e565b614ae7604083018661471e565b614af4606083018561471e565b614b01608083018461471e565b9695505050505050565b600061010082019050614b21600083018b61471e565b614b2e602083018a61471e565b614b3b604083018961471e565b614b48606083018861471e565b614b55608083018761471e565b614b6260a083018661471e565b614b6f60c083018561471e565b614b7c60e083018461471e565b9998505050505050505050565b6000604051905081810181811067ffffffffffffffff82111715614bb057614baf614e9a565b5b8060405250919050565b600067ffffffffffffffff821115614bd557614bd4614e9a565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614c0157614c00614e9a565b5b602082029050602081019050919050565b600067ffffffffffffffff821115614c2d57614c2c614e9a565b5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b60008190508160005260206000209050919050565b600081519050919050565b6000602082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b6000819050919050565b6000614cb682614da5565b9050919050565b60008115159050919050565b6000819050919050565b6000614cde82614cab565b9050919050565b6000614cf082614cab565b9050919050565b6000614d0282614cab565b9050919050565b6000614d1482614cab565b9050919050565b6000614d2682614cab565b9050919050565b6000614d3882614cab565b9050919050565b6000614d4a82614cab565b9050919050565b6000614d5c82614cab565b9050919050565b6000614d6e82614cab565b9050919050565b6000819050614d8382614ea9565b919050565b6000819050614d9682614ebd565b919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000614dda82614e4d565b9050919050565b6000614dec82614df3565b9050919050565b6000614dfe82614da5565b9050919050565b6000614e1082614e17565b9050919050565b6000614e2282614da5565b9050919050565b6000614e3482614d75565b9050919050565b6000614e4682614d88565b9050919050565b6000614e5882614e5f565b9050919050565b6000614e6a82614da5565b9050919050565b82818337600083830152505050565b6000614e93614e8e83614e9c565b614ca1565b9050919050565bfe5b60008160001c9050919050565b60038110614eba57614eb9614e9a565b5b50565b60048110614ece57614ecd614e9a565b5b50565b614eda81614cab565b8114614ee557600080fd5b50565b614ef181614cbd565b8114614efc57600080fd5b50565b614f0881614cc9565b8114614f1357600080fd5b50565b614f1f81614cd3565b8114614f2a57600080fd5b50565b614f3681614ce5565b8114614f4157600080fd5b50565b614f4d81614cf7565b8114614f5857600080fd5b50565b614f6481614d09565b8114614f6f57600080fd5b50565b614f7b81614d1b565b8114614f8657600080fd5b50565b614f9281614d2d565b8114614f9d57600080fd5b50565b614fa981614d3f565b8114614fb457600080fd5b50565b614fc081614d51565b8114614fcb57600080fd5b50565b614fd781614d63565b8114614fe257600080fd5b50565b60048110614ff257600080fd5b50565b614ffe81614d9b565b811461500957600080fd5b50565b61501581614dc5565b811461502057600080fd5b5056fe536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a264697066735822122025cb595d612b5cbe13cf48f8a36c92b410f3ba316734ab31b5332ff4aa39086464736f6c63430007060033
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.