Contract 0x0A9A47f9c19b486237A398c27bAFC60cac2Cb15C 1

 
Txn Hash Method
Index
From
To
Value
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583Withdraw234030902022-09-16 9:21:5514 days 12 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000266495750.001
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19Open Position216975212022-09-02 14:40:0228 days 7 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.000091774430.001
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9aDeposit216973852022-09-02 14:38:3028 days 7 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000666542480.001
0xb94372218d56fd3305620ede6b44c3dde11253450df702d81147ac75705c8dbdWithdraw206069622022-08-26 8:24:3035 days 13 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000362027640.001
0xa90458962151026e0947ee359e1620d36ce0a93845747c3083aafe008eaf3760Open Position197751342022-08-19 13:23:1542 days 8 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000676754540.001043428
0x2fcc527b7a92c667152548f0f445e476b27fd8f9434b6380af22d2b6bd99d924Deposit197747952022-08-19 13:19:0242 days 8 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000632896140.001043428
0xcbea9003127b9c87b47d575dfed85ceb65635326e7591f690041cc0c0b8951bbWithdraw197746392022-08-19 13:17:3042 days 8 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0001525120440.001043428
0xd6bb6a96f163f94cad1e3236dc184948f62a331befd877b0a453ec61d448c9d3Withdraw170479832022-08-01 7:09:4860 days 14 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000330402870.001
0x6b0c2d46b484a1e86c904ceb326748d919d7a910b4652c9c9e821c12fd61f469Open Position157393712022-07-25 14:53:3467 days 6 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.000122908070.001
0xe7e6a85d2253c0ab76644286fbb273d520f77312a175a03781d39d36977b9503Deposit157392872022-07-25 14:51:3567 days 6 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether0.0000873760740.001
0xde52dc7bd66911f617fc7e0175ccac08af1c58b4092f371dabed5f9a5d56992f0x60a06040157211312022-07-25 11:26:1667 days 10 hrs ago0xae75b29ade678372d77a8b41225654138a7e6ff1 IN  Create: LyraPositionHandlerL20 Ether0.0063157563560.001
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago Wrapped Ether0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0.01219878830008447 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c Uniswap V3: Router0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c 0xbfa31380ed380ceb325153ea08f296a45a4891080 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0x6575f73806029b0f445d6231972f153f42a552fd941b3b391f102d926351f583234030902022-09-16 9:21:5514 days 12 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c 0x44002215a269797e067b7f9c864bef6403f169610 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c 0x1d42a98848e022908069c2c545ae44cc78509bc80 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0xcc210e540a2cde94a59f486625ae949fc830c437a02e674529716b78a8811d19216975212022-09-02 14:40:0228 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cSynthetix: sUSD Token0 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c Uniswap V3: Router0 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c 0xbfa31380ed380ceb325153ea08f296a45a4891080 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
0x3e57f1859ff3c097114f337b0f5d68e5489cc0efa64aa3adc8e85103b034ff9a216973852022-09-02 14:38:3028 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0.461756028389985207 Ether
0xd4cb5463aa94b85022e895d20d8f606628c9c896d60a78ed4915843cd7f4c774216960422022-09-02 14:28:5728 days 7 hrs ago 0x86ca30bef97fb651b8d866d45503684b90cb33120x0a9a47f9c19b486237a398c27bafc60cac2cb15c0.250075480074626184 Ether
0xf24707958c6c3bb550d6917d542d4e931afffeb7eaf38d5307ea1ec677e6bddc216957872022-09-02 14:25:3828 days 7 hrs ago Wrapped Ether0x0a9a47f9c19b486237a398c27bafc60cac2cb15c0 Ether
0xf24707958c6c3bb550d6917d542d4e931afffeb7eaf38d5307ea1ec677e6bddc216957872022-09-02 14:25:3828 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
0xf24707958c6c3bb550d6917d542d4e931afffeb7eaf38d5307ea1ec677e6bddc216957872022-09-02 14:25:3828 days 7 hrs ago 0x0a9a47f9c19b486237a398c27bafc60cac2cb15cWrapped Ether0 Ether
[ Download CSV Export 
Latest 1 Deposit
L2 Txn Hash L1 Deposit Txn Value Token
0xa84847dbd660a0ceadb526880a23b320e2a705ec36a43aa616fe14007a99607c2022-09-30 12:28:289 hrs 13 mins ago0x566243d60efad7247f69894247f0c9884ee56f80667d5c2968dfacd97a5a09db0.30850121169991553 Ether (ETH)
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
LyraPositionHandlerL2

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 100 runs

Other Settings:
default evmVersion
File 1 of 43 : LyraPositionHandlerL2.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "./LyraController.sol";
import "./OptimismL2Wrapper.sol";
import "./SocketV1Controller.sol";
import "./UniswapV3Controller.sol";

import "./interfaces/IPositionHandler.sol";
import "../../interfaces/IWETH9.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {BasicFeeCounter} from "@lyrafinance/protocol/contracts/periphery/BasicFeeCounter.sol";

/// @title LyraPositionHandlerL2
/// @author Bapireddy and 0xAd1
/// @notice Acts as positon handler and token bridger on L2 Optimism
contract LyraPositionHandlerL2 is
    IPositionHandler,
    LyraController,
    SocketV1Controller,
    OptimismL2Wrapper,
    UniswapV3Controller
{
    /*///////////////////////////////////////////////////////////////
                            STATE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// @notice wantTokenL2 address
    address public override wantTokenL2;

    /// @notice Address of LyraTradeExecutor on L1
    address public positionHandlerL1;

    /// @notice Address of socket registry on L2
    address public socketRegistry;

    /// @notice Keeper address
    address public keeper;

    /// @notice Governance address
    address public governance;

    /// @notice Pengin governance address
    address public pendingGovernance;

    /*///////////////////////////////////////////////////////////////
                            EVENT LOGS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when keeper is updated.
    /// @param oldKeeper The address of the current keeper.
    /// @param newKeeper The address of new keeper.
    event UpdatedKeeper(address indexed oldKeeper, address indexed newKeeper);

    /// @notice Emitted when governance is updated.
    /// @param oldGovernance The address of the current governance.
    /// @param newGovernance The address of new governance.
    event UpdatedGovernance(
        address indexed oldGovernance,
        address indexed newGovernance
    );

    /// @notice Emitted when socket registry is updated.
    /// @param oldRegistry The address of the current Registry.
    /// @param newRegistry The address of new Registry.
    event UpdatedSocketRegistry(
        address indexed oldRegistry,
        address indexed newRegistry
    );

    /// @notice Emitted when slippage is updated.
    /// @param oldSlippage The current slippage.
    /// @param newSlippage Newnew slippage.
    event UpdatedSlippage(uint256 oldSlippage, uint256 newSlippage);

    /*///////////////////////////////////////////////////////////////
                            INITIALIZING
    //////////////////////////////////////////////////////////////*/
    constructor(
        address _wantTokenL2,
        address _positionHandlerL1,
        address _lyraOptionMarket,
        address _keeper,
        address _governance,
        address _socketRegistry,
        uint256 _slippage
    ) {
        wantTokenL2 = _wantTokenL2;
        positionHandlerL1 = _positionHandlerL1;
        keeper = _keeper;
        socketRegistry = _socketRegistry;

        slippage = _slippage;
        governance = _governance;

        _configHandler(_lyraOptionMarket);

        // approve max want token L2 balance to uniV3 router
        IERC20(wantTokenL2).approve(
            address(UniswapV3Controller.uniswapRouter),
            type(uint256).max
        );
        // approve max susd balance to uniV3 router
        LyraController.sUSD.approve(
            address(UniswapV3Controller.uniswapRouter),
            type(uint256).max
        );

        // deploy basic fee counter and set trusted counter
        BasicFeeCounter feeCounter = new BasicFeeCounter();
        feeCounter.setTrustedCounter(address(this), true);

        // set Lyra Adapter
        LyraAdapter.setLyraAddresses(
            0xF5A0442D4753cA1Ea36427ec071aa5E786dA5916,
            _lyraOptionMarket,
            0xA5407eAE9Ba41422680e2e00537571bcC53efBfD,
            // fee counter
            address(feeCounter)
        );
    }

    /*///////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/
    function positionInWantToken()
        public
        view
        override
        returns (uint256, uint256)
    {
        /// Get balance in susd and convert it into ETH
        uint256 sUSDbalance = LyraController._positionInWantToken();
        uint256 ETHPriceInsUSD = LyraAdapter
            .synthetixAdapter
            .getSpotPriceForMarket(LYRA_ETH_OPTIONS_MARKET);
        /// Adding ETH balance of contract as wantToken is wrapped ETH
        return (
            (sUSDbalance * NORMALIZATION_FACTOR) /
                ETHPriceInsUSD +
                IERC20(wantTokenL2).balanceOf(address(this)) +
                address(this).balance,
            block.number
        );
    }

    function isCurrentPositionActive() public view override returns (bool) {
        return LyraController._isCurrentPositionActive();
    }

    /*///////////////////////////////////////////////////////////////
                        DEPOSIT / WITHDRAW LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Converts the whole wantToken to sUSD.
    function deposit() public override onlyKeeper {
        require(
            address(this).balance > 0 ||
                IWETH9(wantTokenL2).balanceOf(address(this)) > 0,
            "INSUFFICIENT_BALANCE"
        );
        IWETH9(wantTokenL2).deposit{value: address(this).balance}();
        UniswapV3Controller._estimateAndSwap(
            true,
            IERC20(wantTokenL2).balanceOf(address(this))
        );
    }

    /// @notice Bridges wantToken back to strategy on L1
    /// @dev Check MovrV1Controller for more details on implementation of token bridging
    /// @param amountOut amount needed to be sent to strategy
    /// @param _socketRegistry address of movr contract to send txn to
    /// @param socketData movr txn calldata
    function withdraw(
        uint256 amountOut,
        address _socketRegistry,
        bytes calldata socketData
    ) public override onlyAuthorized {
        if (LyraController.sUSD.balanceOf(address(this)) > 0) {
            UniswapV3Controller._estimateAndSwap(
                false,
                LyraController.sUSD.balanceOf(address(this))
            );
        }

        IWETH9(wantTokenL2).withdraw(
            IWETH9(wantTokenL2).balanceOf(address(this))
        );

        require(address(this).balance >= amountOut, "NOT_ENOUGH_TOKENS");

        if (amountOut > 0) {
            require(socketRegistry == _socketRegistry, "INVALID_REGISTRY");
            SocketV1Controller.sendTokens(
                wantTokenL2,
                socketRegistry,
                positionHandlerL1,
                amountOut,
                1,
                socketData
            );
        }
    }

    /*///////////////////////////////////////////////////////////////
                        OPEN / CLOSE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Purchases new option on lyra.
    /// @dev Will use all sUSD balance to purchase option on Lyra.
    /// @param strikeId Strike ID of the option based on strike price
    /// @param isCall boolean indication call or put option to purchase.
    /// @param amount amount of options to buy
    /// @param updateExistingPosition boolean indication of if existing position should be updated
    function openPosition(
        uint256 strikeId,
        bool isCall,
        uint256 amount,
        bool updateExistingPosition
    )
        public
        override
        onlyAuthorized
        returns (LyraAdapter.TradeResult memory tradeResult)
    {
        tradeResult = LyraController._openPosition(
            strikeId,
            isCall,
            amount,
            updateExistingPosition
        );
    }

    /// @notice Exercises/Sell option on lyra.
    /// @dev Will sell back or settle the option on Lyra.
    /// @param toSettle boolean if true settle position, else close position
    function closePosition(bool toSettle) public override onlyAuthorized {
        LyraController._closePosition(toSettle);
    }

    /*///////////////////////////////////////////////////////////////
                            MAINTAINANCE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /// @notice Sweep tokens
    /// @param _token Address of the token to sweepr
    function sweep(address _token) public override onlyGovernance {
        IERC20(_token).transfer(
            msg.sender,
            IERC20(_token).balanceOf(address(this))
        );
    }

    /// @notice slippage setter
    /// @param _slippage updated slippage value
    function setSlippage(uint256 _slippage) public onlyGovernance {
        emit UpdatedSlippage(slippage, _slippage);
        slippage = _slippage;
    }

    /// @notice socket registry setter
    /// @param _socketRegistry new address of socket registry
    function setSocketRegistry(address _socketRegistry) public onlyGovernance {
        emit UpdatedSocketRegistry(socketRegistry, _socketRegistry);
        socketRegistry = _socketRegistry;
    }

    /// @notice keeper setter
    /// @param _keeper new keeper address
    function setKeeper(address _keeper) public onlyGovernance {
        emit UpdatedKeeper(keeper, _keeper);
        keeper = _keeper;
    }

    /// @notice Governance setter
    /// @param _pendingGovernance new governance address
    function setGovernance(address _pendingGovernance) public onlyGovernance {
        pendingGovernance = _pendingGovernance;
    }

    /// @notice Governance accepter
    function acceptGovernance() public {
        require(msg.sender == pendingGovernance, "NOT_PENDING_GOVERNANCE");
        emit UpdatedGovernance(governance, pendingGovernance);
        governance = pendingGovernance;
    }

    /// @notice checks wether txn sender is keeper address or LyraTradeExecutor using optimism gateway
    modifier onlyAuthorized() {
        require(
            ((msg.sender == L2CrossDomainMessenger &&
                OptimismL2Wrapper.messageSender() == positionHandlerL1) ||
                msg.sender == keeper),
            "ONLY_AUTHORIZED"
        );
        _;
    }

    /// @notice only keeper can call this function
    modifier onlyKeeper() {
        require(msg.sender == keeper, "ONLY_KEEPER");
        _;
    }

    /// @notice only governance can call this function
    modifier onlyGovernance() {
        require(msg.sender == governance, "ONLY_GOVERNANCE");
        _;
    }

    /// @notice Receive native ETH and do nothing
    receive() external payable {}
}

File 2 of 43 : LyraController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import {IOptionMarket} from "@lyrafinance/protocol/contracts/interfaces/IOptionMarket.sol";
import {LyraAdapter} from "@lyrafinance/protocol/contracts/periphery/LyraAdapter.sol";
import {DecimalMath} from "@lyrafinance/protocol/contracts/synthetix/DecimalMath.sol";

/// @title LyraPositionHandlerL2
/// @author Pradeep and Bapireddy
/// @notice Acts as controller to interact with lyra protocol.
contract LyraController is LyraAdapter {
    using DecimalMath for uint256;

    /*///////////////////////////////////////////////////////////////
                          STRUCTS FOR STORAGE
  //////////////////////////////////////////////////////////////*/

    /// @notice Params required to close a position
    /// @dev send these params encoded in bytes
    /// @param toSettle boolean if true settle position, else close position
    struct ClosePositionParams {
        bool toSettle;
    }

    /// @notice struct indicating the current position
    /// @param strikeId Strike ID of the option based on strike price
    /// @param optionType call or put option
    /// @param amount Amount of sUSD used to purchase option
    struct CurrentPosition {
        uint256 strikeId;
        uint256 positionId;
        LyraAdapter.OptionType optionType;
        uint256 amount;
        uint256 optionsPurchased;
    }

    /*///////////////////////////////////////////////////////////////
                          IMMUTABLES
  //////////////////////////////////////////////////////////////*/

    IERC20 public constant sUSD =
        IERC20(0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9);

    /*///////////////////////////////////////////////////////////////
                          MUTABLES
  //////////////////////////////////////////////////////////////*/
    /// @notice address of the option market we trade on
    IOptionMarket public lyraOptionMarket;

    // 0x1f6d98638eee9f689684767c3021230dd68df419

    /// @notice struct inidcating the current position
    CurrentPosition public currentPosition;

    /// @notice Configures the handlers with base state.
    /// @param _lyraOptionMarket The option market we buy options on.
    /// @param _lyraOptionMarket The short collateral contract to settle options.
    function _configHandler(address _lyraOptionMarket) internal {
        lyraOptionMarket = IOptionMarket(_lyraOptionMarket);
    }

    /*///////////////////////////////////////////////////////////////
                        OPEN / CLOSE LOGIC
    //////////////////////////////////////////////////////////////*/

    /// @notice Purchases new option on lyra.
    /// @dev Will use all sUSD balance to purchase option on Lyra.
    /// @param strikeId Strike ID of the option based on strike price
    /// @param isCall boolean indication call or put option
    /// @param amount amount of options to buy
    /// @param updateExistingPosition boolean indication of if existing position should be updated
    function _openPosition(
        uint256 strikeId,
        bool isCall,
        uint256 amount,
        bool updateExistingPosition
    ) internal returns (LyraAdapter.TradeResult memory tradeResult) {
        LyraAdapter.OptionType optionType;
        LyraAdapter.TradeInputParameters memory openPositionParams;

        uint256 sUSDBal = sUSD.balanceOf(address(this));
        require(sUSDBal > 0, "NO_BALANCE");

        sUSD.approve(address(lyraOptionMarket), sUSDBal);

        if (updateExistingPosition) {
            optionType = currentPosition.optionType;
            openPositionParams = _getTradeInputParams(
                currentPosition.strikeId,
                currentPosition.positionId,
                currentPosition.optionType,
                amount,
                sUSD.balanceOf(address(this))
            );
        } else {
            optionType = isCall
                ? LyraAdapter.OptionType.LONG_CALL
                : LyraAdapter.OptionType.LONG_PUT;

            /// Use params to open new position on lyra
            openPositionParams = _getTradeInputParams(
                strikeId,
                0,
                optionType,
                amount,
                sUSD.balanceOf(address(this))
            );
        }

        tradeResult = LyraAdapter._openPosition(openPositionParams);

        uint256 sUSDSent = sUSDBal - sUSD.balanceOf(address(this));

        currentPosition = CurrentPosition({
            strikeId: strikeId,
            positionId: tradeResult.positionId,
            optionType: optionType,
            amount: sUSDSent +
                (updateExistingPosition ? currentPosition.amount : 0),
            optionsPurchased: amount +
                (updateExistingPosition ? currentPosition.optionsPurchased : 0)
        });
    }

    /// @notice Exercises/Sell option on lyra.
    /// @dev Will sell back or settle the option on Lyra.
    /// @param toSettle boolean if true settle position, else close position
    function _closePosition(bool toSettle) internal {
        require(_isCurrentPositionActive(), "NO_ACTIVE_POSITION");

        /// Check if option has to be settled.
        if (toSettle == true) {
            uint256[] memory positionsToClose = new uint256[](1);
            positionsToClose[0] = currentPosition.positionId;

            LyraAdapter.shortCollateral.settleOptions(positionsToClose);
        } else {
            LyraAdapter.TradeInputParameters
                memory closePositionParams = _getTradeInputParams(
                    currentPosition.strikeId,
                    currentPosition.positionId,
                    currentPosition.optionType,
                    currentPosition.optionsPurchased,
                    type(uint256).max
                );
            LyraAdapter._closeOrForceClosePosition(closePositionParams);
        }

        // reset the current position value.
        currentPosition = CurrentPosition({
            strikeId: 0,
            positionId: 0,
            optionType: LyraAdapter.OptionType.SHORT_CALL_BASE,
            amount: 0,
            optionsPurchased: 0
        });
    }

    /// @notice Get the value of current active position on Lyra.
    /// @dev Gives the total value of position handler in susd.
    function _positionInWantToken() public view returns (uint256) {
        if (_isCurrentPositionActive()) {
            (uint256 callPremium, uint256 putPremium) = LyraAdapter
                ._optionPriceGWAV(currentPosition.strikeId, 60);
            uint256 totalPremium = (
                currentPosition.optionType == LyraAdapter.OptionType.LONG_CALL
                    ? callPremium
                    : putPremium
            ).multiplyDecimal(currentPosition.optionsPurchased);

            return totalPremium + sUSD.balanceOf(address(this));
        } else {
            return sUSD.balanceOf(address(this));
        }
    }

    /// @notice helper function to get trade input parameters for opening and closing positions
    /// @param strikeId strike id of the option
    /// @param positionId position ID of the ERC721 position
    /// @param optionType type of option (LONG_CALL|LONG_PUT)
    /// @param amount amount of options
    function _getTradeInputParams(
        uint256 strikeId,
        uint256 positionId,
        LyraAdapter.OptionType optionType,
        uint256 amount,
        uint256 maxCost
    ) internal returns (LyraAdapter.TradeInputParameters memory) {
        return
            LyraAdapter.TradeInputParameters({
                strikeId: strikeId,
                positionId: positionId,
                iterations: 3,
                optionType: optionType,
                amount: amount,
                setCollateralTo: 0,
                minTotalCost: 0,
                maxTotalCost: maxCost,
                rewardRecipient: address(this)
            });
    }

    /// @notice helper function to check if the current position is active
    /// @return isPositionActive
    function _isCurrentPositionActive() internal view returns (bool) {
        uint256[] memory positions = new uint256[](1);
        positions[0] = currentPosition.positionId;

        LyraAdapter.OptionPosition[] memory allPositions = LyraAdapter
            ._getPositions(positions);
        LyraAdapter.OptionPosition memory positionData = allPositions[0];

        return positionData.state == LyraAdapter.PositionState.ACTIVE;
    }
}

File 3 of 43 : OptimismL2Wrapper.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "../../interfaces/ICrossDomainMessenger.sol";

contract OptimismL2Wrapper {
    /// @notice Address of Optimism L2CrossDomainMessenger
    /// @dev Address is hardcoded, stays same on L2 mainnet and L2 testnet
    address public L2CrossDomainMessenger =
        0x4200000000000000000000000000000000000007;

    ICrossDomainMessenger public optimismMessenger =
        ICrossDomainMessenger(L2CrossDomainMessenger);

    /// @notice Returns the true sender of transaction sent from Optimism L1CrossDomainMessenger
    /// @return address of sender
    function messageSender() public view returns (address) {
        return optimismMessenger.xDomainMessageSender();
    }
}

File 4 of 43 : SocketV1Controller.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title SocketV1Controller
/// @author 0xAd1
/// @notice Used to bridge ERC20 tokens cross chain
contract SocketV1Controller {
    struct MiddlewareRequest {
        uint256 id;
        uint256 optionalNativeAmount;
        address inputToken;
        bytes data;
    }
    struct BridgeRequest {
        uint256 id;
        uint256 optionalNativeAmount;
        address inputToken;
        bytes data;
    }
    struct UserRequest {
        address receiverAddress;
        uint256 toChainId;
        uint256 amount;
        MiddlewareRequest middlewareRequest;
        BridgeRequest bridgeRequest;
    }

    /// @notice Decode the Bungee request calldata
    /// @param _data Bungee txn calldata
    /// @return userRequest parsed calldata
    function decodeSocketRegistryCalldata(bytes calldata _data)
        internal
        pure
        returns (UserRequest memory userRequest)
    {
        (userRequest) = abi.decode(_data[4:], (UserRequest));
    }

    function verifySocketCalldata(
        bytes calldata _data,
        uint256 _chainId,
        address _inputToken,
        address _receiverAddress
    ) internal pure {
        UserRequest memory userRequest;
        (userRequest) = decodeSocketRegistryCalldata(_data);
        if (userRequest.toChainId != _chainId) {
            revert("INVALID_CHAINID");
        }
        if (userRequest.receiverAddress != _receiverAddress) {
            revert("INVALID_RECEIVER_ADDRESS");
        }
        // if (userRequest.bridgeRequest.inputToken != _inputToken) {
        //     revert("INVALID_INPUT_TOKEN");
        // }
    }

    /// @notice Sends tokens using Bungee middleware. Assumes tokens already present in contract. Manages allowance and transfer.
    /// @dev Currently not verifying the middleware request calldata. Use very carefully
    /// @param token address of IERC20 token to be sent
    // / @param allowanceTarget address to allow tokens to swipe
    /// @param socketRegistry address to send bridge txn to
    /// @param destinationAddress address of receiver
    /// @param amount amount of tokens to bridge
    /// @param destinationChainId chain Id of receiving chain
    /// @param data calldata of txn to be sent
    function sendTokens(
        address token,
        address socketRegistry,
        address destinationAddress,
        uint256 amount,
        uint256 destinationChainId,
        bytes calldata data
    ) internal {
        verifySocketCalldata(
            data,
            destinationChainId,
            token,
            destinationAddress
        );
        // IERC20(token).approve(allowanceTarget, amount);
        (bool success, ) = socketRegistry.call{value: amount}(data);
        require(success, "FAILED_SOCKET_CALL");
    }
}

File 5 of 43 : UniswapV3Controller.sol
//SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import "../../interfaces/IUniswapSwapRouter.sol";

import "./interfaces/IPositionHandler.sol";

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

import {ILyraRegistry, ISynthetixAdapter} from "@lyrafinance/protocol/contracts/periphery/LyraAdapter.sol";

/// @title UniswapV3Controller
/// @author Pradeep
/// @notice Used to perform swaps for synthetix tokens
contract UniswapV3Controller {
    using SafeERC20 for IERC20;

    /// @notice maximum basis points for all numeric operations
    uint256 public constant MAX_BPS = 10000;
    /// @notice normalization factor for decimals
    uint256 public constant NORMALIZATION_FACTOR = 1e18;
    /// @notice uniswap swap fee
    uint24 public constant UNISWAP_FEE = 3000;
    /// @notice address of lyra eth options market
    address public constant LYRA_ETH_OPTIONS_MARKET =
        0x1d42a98848e022908069c2c545aE44Cc78509Bc8;

    /// @notice uniswap router to swap tokens
    IUniswapSwapRouter public constant uniswapRouter =
        IUniswapSwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
    /// @notice sUSD IERC20 address
    address public constant sUSDAddr =
        0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9;

    /// @notice SyntheticAdapter contract
    ISynthetixAdapter public immutable lyraSynthetixAdapter;

    /// @notice slippage for swaps
    uint256 public slippage;

    constructor() {
        lyraSynthetixAdapter = ISynthetixAdapter(
            ILyraRegistry(0xF5A0442D4753cA1Ea36427ec071aa5E786dA5916)
                .getGlobalAddress("SYNTHETIX_ADAPTER")
        );
    }

    function _setSlippage(uint256 _slippage) internal {
        slippage = _slippage;
    }

    function _estimateAndSwap(bool direction, uint256 amountToSwap) internal {
        require(amountToSwap > 0, "INVALID_AMOUNT");

        // direction = true -> swap WETH -> USD else swap USD -> ETH
        address srcToken = direction
            ? IPositionHandler(address(this)).wantTokenL2()
            : sUSDAddr;
        address destToken = direction
            ? sUSDAddr
            : IPositionHandler(address(this)).wantTokenL2();

        require(
            IERC20(srcToken).balanceOf(address(this)) >= amountToSwap,
            "INSUFFICIENT_BALANCE"
        );

        // get ETH price and estimate amount out to account for slippage
        uint256 ETHPriceInsUSD = lyraSynthetixAdapter.getSpotPriceForMarket(
            LYRA_ETH_OPTIONS_MARKET
        );
        uint256 amountOutExpected = direction
            ? (amountToSwap * ETHPriceInsUSD) / NORMALIZATION_FACTOR
            : (amountToSwap * NORMALIZATION_FACTOR) / ETHPriceInsUSD;

        IUniswapSwapRouter.ExactInputSingleParams
            memory params = IUniswapSwapRouter.ExactInputSingleParams({
                tokenIn: srcToken,
                tokenOut: destToken,
                fee: UNISWAP_FEE,
                recipient: address(this),
                deadline: block.timestamp,
                amountIn: amountToSwap,
                amountOutMinimum: (amountOutExpected * (MAX_BPS - slippage)) /
                    MAX_BPS,
                sqrtPriceLimitX96: 0
            });
        uniswapRouter.exactInputSingle(params);
    }
}

File 6 of 43 : IPositionHandler.sol
/// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {LyraAdapter} from "@lyrafinance/protocol/contracts/periphery/LyraAdapter.sol";

interface IPositionHandler {
    function wantTokenL2() external view returns (address);

    function positionInWantToken() external view returns (uint256, uint256);

    function openPosition(
        uint256 listingId,
        bool isCall,
        uint256 amount,
        bool updateExistingPosition
    ) external returns (LyraAdapter.TradeResult memory);

    function closePosition(bool toSettle) external;

    function deposit() external;

    function withdraw(
        uint256 amountOut,
        address socketRegistry,
        bytes calldata socketData
    ) external;

    function sweep(address _token) external;

    function isCurrentPositionActive() external view returns (bool);
}

File 7 of 43 : IWETH9.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.4;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IWETH9 is IERC20 {
    function deposit() external payable;

    function withdraw(uint256 _amount) external;
}

File 8 of 43 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 9 of 43 : BasicFeeCounter.sol
//SPDX-License-Identifier:ISC
pragma solidity 0.8.9;

import "../synthetix/Owned.sol";
import "../interfaces/IFeeCounter.sol";

/**
 * @title BasicFeeCounter
 */
contract BasicFeeCounter is IFeeCounter, Owned {
  mapping(address => bool) public trustedCounter;
  mapping(address => mapping(address => uint)) public totalFeesPerMarket;

  constructor() Owned() {}

  function setTrustedCounter(address counter, bool isTrusted) external onlyOwner {
    trustedCounter[counter] = isTrusted;
  }

  function trackFee(
    address market,
    address trader,
    uint,
    uint,
    uint totalFee
  ) external onlyTrustedCounter {
    totalFeesPerMarket[market][trader] += totalFee;
  }

  modifier onlyTrustedCounter() {
    require(trustedCounter[msg.sender], "not trusted counter");
    _;
  }
}

File 10 of 43 : IOptionMarket.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

import "./ILiquidityPool.sol";
import "./ISynthetixAdapter.sol";
import "./IOptionMarketPricer.sol";


// For full documentation refer to @lyrafinance/protocol/contracts/OptionMarket.sol";
interface IOptionMarket {

  enum TradeDirection {
    OPEN,
    CLOSE,
    LIQUIDATE
  }

  enum OptionType {
    LONG_CALL,
    LONG_PUT,
    SHORT_CALL_BASE,
    SHORT_CALL_QUOTE,
    SHORT_PUT_QUOTE
  }

  /// @notice For returning more specific errors
  enum NonZeroValues {
    BASE_IV,
    SKEW,
    STRIKE_PRICE,
    ITERATIONS,
    STRIKE_ID
  }

  ///////////////////
  // Internal Data //
  ///////////////////

  struct Strike {
    // strike listing identifier
    uint id;
    // strike price
    uint strikePrice;
    // volatility component specific to the strike listing (boardIv * skew = vol of strike)
    uint skew;
    // total user long call exposure
    uint longCall;
    // total user short call (base collateral) exposure
    uint shortCallBase;
    // total user short call (quote collateral) exposure
    uint shortCallQuote;
    // total user long put exposure
    uint longPut;
    // total user short put (quote collateral) exposure
    uint shortPut;
    // id of board to which strike belongs
    uint boardId;
  }

  struct OptionBoard {
    // board identifier
    uint id;
    // expiry of all strikes belonging to board
    uint expiry;
    // volatility component specific to board (boardIv * skew = vol of strike)
    uint iv;
    // admin settable flag blocking all trading on this board
    bool frozen;
    // list of all strikes belonging to this board
    uint[] strikeIds;
  }

  ///////////////
  // In-memory //
  ///////////////

  struct OptionMarketParameters {
    // max allowable expiry of added boards
    uint maxBoardExpiry;
    // security module address
    address securityModule;
    // fee portion reserved for Lyra DAO
    uint feePortionReserved;
    // expected fee charged to LPs, used for pricing short_call_base settlement
    uint staticBaseSettlementFee;
  }

  struct TradeInputParameters {
    // id of strike
    uint strikeId;
    // OptionToken ERC721 id for position (set to 0 for new positions)
    uint positionId;
    // number of sub-orders to break order into (reduces slippage)
    uint iterations;
    // type of option to trade
    OptionType optionType;
    // number of contracts to trade
    uint amount;
    // final amount of collateral to leave in OptionToken position
    uint setCollateralTo;
    // revert trade if totalCost is below this value
    uint minTotalCost;
    // revert trade if totalCost is above this value
    uint maxTotalCost;
  }

  struct TradeParameters {
    bool isBuy;
    bool isForceClose;
    TradeDirection tradeDirection;
    OptionType optionType;
    uint amount;
    uint expiry;
    uint strikePrice;
    ILiquidityPool.Liquidity liquidity;
    ISynthetixAdapter.ExchangeParams exchangeParams;
  }

  struct TradeEventData {
    uint expiry;
    uint strikePrice;
    OptionType optionType;
    TradeDirection tradeDirection;
    uint amount;
    uint setCollateralTo;
    bool isForceClose;
    uint spotPrice;
    uint reservedFee;
    uint totalCost;
  }

  struct LiquidationEventData {
    address rewardBeneficiary;
    address caller;
    uint returnCollateral; // quote || base
    uint lpPremiums; // quote || base
    uint lpFee; // quote || base
    uint liquidatorFee; // quote || base
    uint smFee; // quote || base
    uint insolventAmount; // quote
  }

  struct Result {
    uint positionId;
    uint totalCost;
    uint totalFee;
  }

  ///////////////
  // Variables //
  ///////////////

  /// @notice claim all reserved option fees
  function smClaim() external;

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

  function getOptionMarketParams() external view returns (OptionMarketParameters memory);

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

  function getNumLiveBoards() external view returns (uint numLiveBoards);

  function getStrikeAndExpiry(uint strikeId) external view returns (uint strikePrice, uint expiry);

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

  function getStrike(uint strikeId) external view returns (Strike memory);

  function getOptionBoard(uint boardId) external view returns (OptionBoard memory);

  function getStrikeAndBoard(uint strikeId) external view returns (Strike memory, OptionBoard memory);

  function getBoardAndStrikeDetails(uint boardId)
    external
    view
    returns (
      OptionBoard memory,
      Strike[] memory,
      uint[] memory,
      uint
    );

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

  function openPosition(TradeInputParameters memory params) external returns (Result memory result);

  function closePosition(TradeInputParameters memory params) external returns (Result memory result);

  /**
   * @notice Attempts to reduce or fully close position within cost bounds while ignoring delta trading cutoffs.
   *
   * @param params The parameters for the requested trade
   */
  function forceClosePosition(TradeInputParameters memory params) external returns (Result memory result);

  function addCollateral(uint positionId, uint amountCollateral) external;

  function liquidatePosition(uint positionId, address rewardBeneficiary) external;


  /////////////////////////////////
  // Board Expiry and settlement //
  /////////////////////////////////

  function settleExpiredBoard(uint boardId) external;

  function getSettlementParameters(uint strikeId)
    external
    view
    returns (
      uint strikePrice,
      uint priceAtExpiry,
      uint strikeToBaseReturned
    );

  ////////////
  // Events //
  ////////////

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

  /**
   * @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 Strike new skew is set.
   */
  event StrikeSkewSet(uint indexed strikeId, uint skew);

  /**
   * @dev Emitted when a Strike is added to a board
   */
  event StrikeAdded(uint indexed boardId, uint indexed strikeId, uint strikePrice, uint skew);

  /**
   * @dev Emitted when parameters for the option market are adjusted
   */
  event OptionMarketParamsSet(OptionMarketParameters optionMarketParams);

  /**
   * @dev Emitted whenever the security module claims their portion of fees
   */
  event SMClaimed(address securityModule, uint quoteAmount, uint baseAmount);

  /**
   * @dev Emitted when a Position is opened, closed or liquidated.
   */
  event Trade(
    address indexed trader,
    uint indexed strikeId,
    uint indexed positionId,
    TradeEventData trade,
    IOptionMarketPricer.TradeResult[] tradeResults,
    LiquidationEventData liquidation,
    uint timestamp
  );

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

  ////////////
  // Errors //
  ////////////
  // General purpose
  error ExpectedNonZeroValue(address thrower, NonZeroValues valueType);

  // Admin
  error InvalidOptionMarketParams(address thrower, OptionMarketParameters optionMarketParams);

  // Board related
  error InvalidBoardId(address thrower, uint boardId);
  error InvalidExpiryTimestamp(address thrower, uint currentTime, uint expiry, uint maxBoardExpiry);
  error BoardNotFrozen(address thrower, uint boardId);
  error BoardAlreadySettled(address thrower, uint boardId);
  error BoardNotExpired(address thrower, uint boardId);

  // Strike related
  error InvalidStrikeId(address thrower, uint strikeId);
  error StrikeSkewLengthMismatch(address thrower, uint strikesLength, uint skewsLength);

  // Trade
  error TotalCostOutsideOfSpecifiedBounds(address thrower, uint totalCost, uint minCost, uint maxCost);
  error BoardIsFrozen(address thrower, uint boardId);
  error BoardExpired(address thrower, uint boardId, uint boardExpiry, uint currentTime);
  error TradeIterationsHasRemainder(
    address thrower,
    uint iterations,
    uint expectedAmount,
    uint tradeAmount,
    uint totalAmount
  );

  // Access
  error OnlySecurityModule(address thrower, address caller, address securityModule);

  // Token transfers
  error BaseTransferFailed(address thrower, address from, address to, uint amount);
  error QuoteTransferFailed(address thrower, address from, address to, uint amount);
}

File 11 of 43 : LyraAdapter.sol
//SPDX-License-Identifier:ISC
pragma solidity 0.8.9;

// Libraries
import "../libraries/GWAV.sol";
import "../libraries/BlackScholes.sol";
import "../synthetix/DecimalMath.sol";

// Inherited
import "openzeppelin-contracts-4.4.1/access/Ownable.sol";
import "openzeppelin-contracts-4.4.1/token/ERC20/IERC20.sol";

// Interfaces
import "../interfaces/IOptionToken.sol";
import "../interfaces/IOptionMarket.sol";
import "../interfaces/ILiquidityPool.sol";
import "../interfaces/IShortCollateral.sol";
import "../interfaces/IOptionGreekCache.sol";
import "../interfaces/ISynthetixAdapter.sol";
import "../interfaces/IDelegateApprovals.sol";
import "../interfaces/ICurve.sol";
import "../interfaces/IGWAVOracle.sol";
import "../interfaces/ILyraRegistry.sol";
import "./BasicFeeCounter.sol";


/**
 * @title LyraAdapter
 * @author Lyra
 * @dev Provides helpful functions for any Lyra trading/market data/vault related actions in one contract
 *      To earn trading rewards, integrators must request to be whitelisted by Lyra
 */

contract LyraAdapter is Ownable {
  using DecimalMath for uint;

  ///////////////////////
  // Abstract Contract //
  ///////////////////////

  struct Strike {
    // strike listing identifier
    uint id;
    // expiry of strike
    uint expiry;
    // strike price
    uint strikePrice;
    // volatility component specific to the strike listing (boardIv * skew = vol of strike)
    uint skew;
    // volatility component specific to the board (boardIv * skew = vol of strike)
    uint boardIv;
  }

  struct Board {
    // board identifier
    uint id;
    // expiry of all strikes belong to
    uint expiry;
    // volatility component specific to the board (boardIv * skew = vol of strike)
    uint boardIv;
    // all strikes belonging to board
    uint[] strikeIds;
  }

  struct OptionPosition {
    // OptionToken ERC721 identifier for position
    uint positionId;
    // strike identifier
    uint strikeId;
    // LONG_CALL | LONG_PUT | SHORT_CALL_BASE | SHORT_CALL_QUOTE | SHORT_PUT_QUOTE
    OptionType optionType;
    // number of options contract owned by position
    uint amount;
    // collateral held in position (only applies to shorts)
    uint collateral;
    // EMPTY | ACTIVE | CLOSED | LIQUIDATED | SETTLED | MERGED
    PositionState state;
  }

  enum OptionType {
    LONG_CALL,
    LONG_PUT,
    SHORT_CALL_BASE,
    SHORT_CALL_QUOTE,
    SHORT_PUT_QUOTE
  }

  enum PositionState {
    EMPTY,
    ACTIVE,
    CLOSED,
    LIQUIDATED,
    SETTLED,
    MERGED
  }

  struct TradeInputParameters {
    // id of strike
    uint strikeId;
    // OptionToken ERC721 id for position (set to 0 for new positions)
    uint positionId;
    // number of sub-orders to break order into (reduces slippage)
    uint iterations;
    // type of option to trade
    OptionType optionType;
    // number of contracts to trade
    uint amount;
    // final amount of collateral to leave in OptionToken position
    uint setCollateralTo;
    // revert trade if totalCost is below this value
    uint minTotalCost;
    // revert trade if totalCost is above this value
    uint maxTotalCost;
    // address of recipient for Lyra trading rewards (must request Lyra to be whitelisted for rewards)
    address rewardRecipient;
  }

  struct TradeResult {
    // OptionToken ERC721 id for position
    uint positionId;
    // total option cost paid/received during trade including premium and totalFee
    uint totalCost;
    // trading fees as determined in OptionMarketPricer.sol
    uint totalFee;
  }

  struct Liquidity {
    // Amount of liquidity available for option collateral and premiums
    uint freeLiquidity;
    // Amount of liquidity available for withdrawals - different to freeLiquidity
    uint burnableLiquidity;
    // Amount of liquidity reserved for long options sold to traders
    uint usedCollatLiquidity;
    // Portion of liquidity reserved for delta hedging (quote outstanding)
    uint pendingDeltaLiquidity;
    // Current value of delta hedge
    uint usedDeltaLiquidity;
    // Net asset value, including everything and netOptionValue
    uint NAV;
  }

  struct MarketParams {
    // The amount of options traded to move baseIv for the board up or down 1 point (depending on trade direction)
    uint standardSize;
    // Determines relative move of skew for a given strike compared to shift in baseIv
    uint skewAdjustmentParam;
    // Interest/risk free rate used in BlackScholes
    int rateAndCarry;
    // Delta cutoff past which options can be traded (optionD > minD && optionD < 1 - minD) - can use forceClose to bypass
    int deltaCutOff;
    // Time when trading closes - can use forceClose to bypass
    uint tradingCutoff;
    // Delta cutoff at which forceClose can be called (optionD < minD || optionD > 1 - minD) - using call delta
    int minForceCloseDelta;
  }

  struct ExchangeRateParams {
    // current snx oracle base price
    uint spotPrice;
    // snx spot exchange rate from quote to base
    uint quoteBaseFeeRate;
    // snx spot exchange rate from base to quote
    uint baseQuoteFeeRate;
  }

  ///////////////
  // Variables //
  ///////////////

  ILyraRegistry public lyraRegistry;
  ISynthetixAdapter internal synthetixAdapter;
  IOptionMarket public optionMarket;
  IOptionToken public optionToken;
  ILiquidityPool public liquidityPool;
  IShortCollateral public shortCollateral;
  IGWAVOracle public gwavOracle;
  IOptionMarketPricer public optionPricer;
  IOptionGreekCache public greekCache;
  IERC20 public quoteAsset;
  IERC20 public baseAsset;

  ICurve public curveSwap;
  BasicFeeCounter public feeCounter;
  bytes32 private constant SNX_ADAPTER = "SYNTHETIX_ADAPTER";

  ///////////
  // Admin //
  ///////////

  constructor() Ownable() {}

  /**
   * @dev Assigns all lyra contracts

   * @param _lyraRegistry LyraRegistry address which holds latest market and global addressess
   * @param _optionMarket OptionMarket address
   * @param _curveSwap Curve pool address for swapping sUSD and other stables via `exchange_with_best_rate`
   * @param _feeCounter Fee counter addressu used to determine Lyra trading rewards
   */

  function setLyraAddresses(
    address _lyraRegistry,
    address _optionMarket,
    address _curveSwap,
    address _feeCounter
  ) public onlyOwner {
    // remove allowance from old assets
    if (address(quoteAsset) != address(0)) {
      quoteAsset.approve(address(optionMarket), 0);
    }
    if (address(baseAsset) != address(0)) {
      baseAsset.approve(address(optionMarket), 0);
    }

    optionMarket = IOptionMarket(_optionMarket);

    // Get market & global addresses via LyraRegistry
    lyraRegistry = ILyraRegistry(_lyraRegistry);
    synthetixAdapter = ISynthetixAdapter(lyraRegistry.getGlobalAddress(SNX_ADAPTER));
    _assignLyraRegistryMarketAddresses();

    // assign curve and Lyra reward counter
    curveSwap = ICurve(_curveSwap);
    feeCounter = BasicFeeCounter(_feeCounter);

    // Do approvals
    IDelegateApprovals(synthetixAdapter.delegateApprovals()).approveExchangeOnBehalf(address(synthetixAdapter));
    quoteAsset.approve(address(optionMarket), type(uint).max);
    baseAsset.approve(address(optionMarket), type(uint).max);
  }

  /// @notice In case of an update to the synthetix contract that revokes the approval
  function updateDelegateApproval() external onlyOwner {
    IDelegateApprovals(synthetixAdapter.delegateApprovals()).approveExchangeOnBehalf(address(synthetixAdapter));
  }

  ////////////////////
  // Market Actions //
  ////////////////////

  /**
   * @notice Attempts to open positions within cost bounds.
   * @dev If a positionId is specified params.amount will be added to the position
   * @dev params.amount can be zero when adjusting an existing position
   *
   * @param params The parameters for the requested trade
   */
  function _openPosition(TradeInputParameters memory params) internal returns (TradeResult memory tradeResult) {
    IOptionMarket.Result memory result = optionMarket.openPosition(_convertParams(params));
    if (params.rewardRecipient != address(0)) {
      feeCounter.trackFee(
        address(optionMarket),
        params.rewardRecipient,
        _convertParams(params).amount,
        result.totalCost,
        result.totalFee
      );
    }
    return TradeResult({positionId: result.positionId, totalCost: result.totalCost, totalFee: result.totalFee});
  }

  /**
   * @notice Attempt close under normal condition or forceClose
   *          if position is outside of delta or too close to expiry.
   *
   * @param params The parameters for the requested trade
   */
  function _closeOrForceClosePosition(TradeInputParameters memory params)
    internal
    returns (TradeResult memory tradeResult)
  {
    if (!_isOutsideDeltaCutoff(params.strikeId) && !_isWithinTradingCutoff(params.strikeId)) {
      return _closePosition(params);
    } else {
      // will pay less competitive price to close position but bypasses Lyra delta/trading cutoffs
      return _forceClosePosition(params);
    }
  }

  /**
   * @notice Attempts to close an existing position within cost bounds.
   * @dev If a positionId is specified params.amount will be subtracted from the position
   * @dev params.amount can be zero when adjusting an existing position
   *
   * @param params The parameters for the requested trade
   */
  function _closePosition(TradeInputParameters memory params) internal returns (TradeResult memory tradeResult) {
    IOptionMarket.Result memory result = optionMarket.closePosition(_convertParams(params));
    if (params.rewardRecipient != address(0)) {
      feeCounter.trackFee(
        address(optionMarket),
        params.rewardRecipient,
        _convertParams(params).amount,
        result.totalCost,
        result.totalFee
      );
    }
    return TradeResult({positionId: result.positionId, totalCost: result.totalCost, totalFee: result.totalFee});
  }

  /**
   * @notice Attempts to close an existing position outside of the delta or trading cutoffs (as specified in MarketParams).
   * @dev This market action will charge higher fees than the standard `closePosition()`
   *
   * @param params The parameters for the requested trade
   */
  function _forceClosePosition(TradeInputParameters memory params) internal returns (TradeResult memory tradeResult) {
    IOptionMarket.Result memory result = optionMarket.forceClosePosition(_convertParams(params));
    if (params.rewardRecipient != address(0)) {
      feeCounter.trackFee(
        address(optionMarket),
        params.rewardRecipient,
        _convertParams(params).amount,
        result.totalCost,
        result.totalFee
      );
    }
    return TradeResult({positionId: result.positionId, totalCost: result.totalCost, totalFee: result.totalFee});
  }

  //////////////
  // Exchange //
  //////////////

  /// @notice Exchange an exact amount of quote for a minimum amount of base (revert otherwise)
  function _exchangeFromExactQuote(uint amountQuote, uint minBaseReceived) internal returns (uint baseReceived) {
    baseReceived = synthetixAdapter.exchangeFromExactQuote(address(optionMarket), amountQuote);
    if (baseReceived < minBaseReceived) {
      revert ExchangerBaseReceivedTooLow(address(this), minBaseReceived, baseReceived);
    }
  }

  /// @notice Exchange to an exact amount of quote for a maximum amount of base (revert otherwise)
  function _exchangeToExactQuote(uint amountQuote, uint maxBaseUsed) internal returns (uint quoteReceived) {
    ISynthetixAdapter.ExchangeParams memory exchangeParams = synthetixAdapter.getExchangeParams(address(optionMarket));
    (, quoteReceived) = synthetixAdapter.exchangeToExactQuoteWithLimit(
      exchangeParams,
      address(optionMarket),
      amountQuote,
      maxBaseUsed
    );
  }

  /// @notice Exchange an exact amount of base for a minimum amount of quote (revert otherwise)
  function _exchangeFromExactBase(uint amountBase, uint minQuoteReceived) internal returns (uint quoteReceived) {
    quoteReceived = synthetixAdapter.exchangeFromExactBase(address(optionMarket), amountBase);
    if (quoteReceived < minQuoteReceived) {
      revert ExchangerQuoteReceivedTooLow(address(this), minQuoteReceived, quoteReceived);
    }
  }

  /// @notice Exchange to an exact amount of base for a maximum amount of quote (revert otherwise)
  function _exchangeToExactBase(uint amountBase, uint maxQuoteUsed) internal returns (uint baseReceived) {
    ISynthetixAdapter.ExchangeParams memory exchangeParams = synthetixAdapter.getExchangeParams(address(optionMarket));
    (, baseReceived) = synthetixAdapter.exchangeToExactBaseWithLimit(
      exchangeParams,
      address(optionMarket),
      amountBase,
      maxQuoteUsed
    );
  }

  /// @notice Returns the ExchangeParams for current market.
  function _getExchangeParams() internal view returns (ExchangeRateParams memory) {
    ISynthetixAdapter.ExchangeParams memory params = synthetixAdapter.getExchangeParams(address(optionMarket));
    return
      ExchangeRateParams({
        spotPrice: params.spotPrice,
        quoteBaseFeeRate: params.quoteBaseFeeRate,
        baseQuoteFeeRate: params.baseQuoteFeeRate
      });
  }

  /**
   * @notice WARNING: ENSURE CURVE HAS SUFFICIENT sUSD LIQUIDITY
   *         Exchange between stables within the curveSwap sUSD pool.
   *
   * @param from start ERC20
   * @param to destination ERC20
   * @param amount amount of "from" currency to exchange
   * @param expected minimum expected amount of "to" currency
   * @param receiver address of recipient of "to" currency
   *
   * @return amountOut received amount
   */
  function _swapStables(
    address from,
    address to,
    uint amount,
    uint expected,
    address receiver
  ) internal returns (uint amountOut) {
    amountOut = curveSwap.exchange_with_best_rate(from, to, amount, expected, receiver);
  }

  //////////////////////////
  // Option Token Actions //
  //////////////////////////

  /// @notice Get position info for given positionIds
  function _getPositions(uint[] memory positionIds) internal view returns (OptionPosition[] memory) {
    IOptionToken.OptionPosition[] memory positions = optionToken.getOptionPositions(positionIds);

    uint positionsLen = positions.length;
    OptionPosition[] memory convertedPositions = new OptionPosition[](positionsLen);
    for (uint i = 0; i < positionsLen; ++i) {
      convertedPositions[i] = OptionPosition({
        positionId: positions[i].positionId,
        strikeId: positions[i].strikeId,
        optionType: OptionType(uint(positions[i].optionType)),
        amount: positions[i].amount,
        collateral: positions[i].collateral,
        state: PositionState(uint(positions[i].state))
      });
    }

    return convertedPositions;
  }

  /**
   * @notice Allows a user to split a curent position into two. The amount of the original position will
   *         be subtracted from and a new position will be minted with the desired amount and collateral.
   * @dev Only ACTIVE positions can be owned by users, so status does not need to be checked
   * @dev Both resulting positions must not be liquidatable
   *
   * @param positionId the positionId of the original position to be split
   * @param newAmount the amount in the new position
   * @param newCollateral the amount of collateral for the new position
   * @param recipient recipient of new position
   */
  function _splitPosition(
    uint positionId,
    uint newAmount,
    uint newCollateral,
    address recipient
  ) internal returns (uint newPositionId) {
    newPositionId = optionToken.split(positionId, newAmount, newCollateral, recipient);
  }

  /**
   * @notice User can merge many positions with matching strike and optionType into a single position
   * @dev Only ACTIVE positions can be owned by users, so status does not need to be checked.
   * @dev Merged position must not be liquidatable.
   *
   * @param positionIds the positionIds to be merged together
   */
  function _mergePositions(uint[] memory positionIds) internal {
    optionToken.merge(positionIds);
  }

  ////////////////////
  // Market Getters //
  ////////////////////

  /// @notice Returns the list of live board ids.
  function _getLiveBoards() internal view returns (uint[] memory liveBoards) {
    liveBoards = optionMarket.getLiveBoards();
  }

  /// @notice Returns Board struct for a given boardId
  function _getBoard(uint boardId) internal view returns (Board memory) {
    IOptionMarket.OptionBoard memory board = optionMarket.getOptionBoard(boardId);
    return Board({id: board.id, expiry: board.expiry, boardIv: board.iv, strikeIds: board.strikeIds});
  }

  /// @notice Returns all Strike structs for a list of strikeIds
  function _getStrikes(uint[] memory strikeIds) internal view returns (Strike[] memory allStrikes) {
    uint strikesLen = strikeIds.length;

    allStrikes = new Strike[](strikesLen);
    for (uint i = 0; i < strikesLen; ++i) {
      (IOptionMarket.Strike memory strike, IOptionMarket.OptionBoard memory board) = optionMarket.getStrikeAndBoard(
        strikeIds[i]
      );

      allStrikes[i] = Strike({
        id: strike.id,
        expiry: board.expiry,
        strikePrice: strike.strikePrice,
        skew: strike.skew,
        boardIv: board.iv
      });
    }
    return allStrikes;
  }

  /// @notice Returns current spot volatilities for given strikeIds (boardIv * skew)
  function _getVols(uint[] memory strikeIds) internal view returns (uint[] memory vols) {
    uint strikesLen = strikeIds.length;

    vols = new uint[](strikesLen);
    for (uint i = 0; i < strikesLen; ++i) {
      (IOptionMarket.Strike memory strike, IOptionMarket.OptionBoard memory board) = optionMarket.getStrikeAndBoard(
        strikeIds[i]
      );

      vols[i] = board.iv.multiplyDecimal(strike.skew);
    }
    return vols;
  }

  /// @notice Returns current spot deltas for given strikeIds (using BlackScholes and spot volatilities)
  function _getDeltas(uint[] memory strikeIds) internal view returns (int[] memory callDeltas) {
    uint strikesLen = strikeIds.length;

    callDeltas = new int[](strikesLen);
    for (uint i = 0; i < strikesLen; ++i) {
      BlackScholes.BlackScholesInputs memory bsInput = _getBsInput(strikeIds[i]);
      (callDeltas[i], ) = BlackScholes.delta(bsInput);
    }
  }

  /// @notice Returns current spot vegas for given strikeIds (using BlackScholes and spot volatilities)
  function _getVegas(uint[] memory strikeIds) internal view returns (uint[] memory vegas) {
    uint strikesLen = strikeIds.length;

    vegas = new uint[](strikesLen);
    for (uint i = 0; i < strikesLen; ++i) {
      BlackScholes.BlackScholesInputs memory bsInput = _getBsInput(strikeIds[i]);
      vegas[i] = BlackScholes.vega(bsInput);
    }
  }

  /// @notice Calculate the pure black-scholes premium for given params
  function _getPurePremium(
    uint secondsToExpiry,
    uint vol,
    uint spotPrice,
    uint strikePrice
  ) internal view returns (uint call, uint put) {
    BlackScholes.BlackScholesInputs memory bsInput = BlackScholes.BlackScholesInputs({
      timeToExpirySec: secondsToExpiry,
      volatilityDecimal: vol,
      spotDecimal: spotPrice,
      strikePriceDecimal: strikePrice,
      rateDecimal: greekCache.getGreekCacheParams().rateAndCarry
    });
    (call, put) = BlackScholes.optionPrices(bsInput);
  }

  /// @notice Calculate the spot black-scholes premium for a given strike
  /// @dev Does not include slippage or trading fees
  function _getPurePremiumForStrike(uint strikeId) internal view returns (uint call, uint put) {
    BlackScholes.BlackScholesInputs memory bsInput = _getBsInput(strikeId);
    (call, put) = BlackScholes.optionPrices(bsInput);
  }

  /// @notice Returns the breakdown of current liquidity usage (see Liquidity struct)
  function _getLiquidity() internal view returns (Liquidity memory) {
    ILiquidityPool.Liquidity memory liquidity = liquidityPool.getCurrentLiquidity();
    return
      Liquidity({
        freeLiquidity: liquidity.freeLiquidity,
        burnableLiquidity: liquidity.burnableLiquidity,
        usedCollatLiquidity: liquidity.usedCollatLiquidity,
        pendingDeltaLiquidity: liquidity.pendingDeltaLiquidity,
        usedDeltaLiquidity: liquidity.usedDeltaLiquidity,
        NAV: liquidity.NAV
      });
  }

  /// @notice Returns the amount of liquidity available for trading
  function _getFreeLiquidity() internal view returns (uint freeLiquidity) {
    freeLiquidity = liquidityPool.getCurrentLiquidity().freeLiquidity;
  }

  /// @notice Returns the most critical Lyra market trading parameters that determine pricing/slippage/trading restrictions
  function _getMarketParams() internal view returns (MarketParams memory) {
    IOptionMarketPricer.PricingParameters memory pricingParams = optionPricer.getPricingParams();
    IOptionMarketPricer.TradeLimitParameters memory tradeLimitParams = optionPricer.getTradeLimitParams();
    return
      MarketParams({
        standardSize: pricingParams.standardSize,
        skewAdjustmentParam: pricingParams.skewAdjustmentFactor,
        rateAndCarry: greekCache.getGreekCacheParams().rateAndCarry,
        deltaCutOff: tradeLimitParams.minDelta,
        tradingCutoff: tradeLimitParams.tradingCutoff,
        minForceCloseDelta: tradeLimitParams.minForceCloseDelta
      });
  }

  /// @notice use latest optionMarket delta cutoff to determine whether trade delta is out of bounds
  function _isOutsideDeltaCutoff(uint strikeId) internal view returns (bool) {
    MarketParams memory marketParams = _getMarketParams();
    uint[] memory dynamicArray = new uint[](1);
    dynamicArray[0] = strikeId;

    int callDelta = _getDeltas(dynamicArray)[0];
    return callDelta > (int(DecimalMath.UNIT) - marketParams.deltaCutOff) || callDelta < marketParams.deltaCutOff;
  }

  /// @notice use latest optionMarket trading cutoff to determine whether trade is too close to expiry
  function _isWithinTradingCutoff(uint strikeId) internal view returns (bool) {
    MarketParams memory marketParams = _getMarketParams();
    uint[] memory dynamicArray = new uint[](1);
    dynamicArray[0] = strikeId;

    Strike memory strike = _getStrikes(dynamicArray)[0];
    return strike.expiry - block.timestamp <= marketParams.tradingCutoff;
  }

  ////////////////////////
  // Minimum Collateral //
  ////////////////////////

  /// @notice Estimate minimum collateral required for given parameters
  /// @dev Position is liquidatable when position.collateral < minCollateral
  function _getMinCollateral(
    OptionType optionType,
    uint strikePrice,
    uint expiry,
    uint spotPrice,
    uint amount
  ) internal view returns (uint) {
    return
      greekCache.getMinCollateral(IOptionMarket.OptionType(uint(optionType)), strikePrice, expiry, spotPrice, amount);
  }

  /// @notice Estimate minimum collateral required for an existing position
  function _getMinCollateralForPosition(uint positionId) internal view returns (uint) {
    IOptionToken.PositionWithOwner memory position = optionToken.getPositionWithOwner(positionId);
    if (_isLong(OptionType(uint(position.optionType)))) return 0;

    uint strikePrice;
    uint expiry;
    (strikePrice, expiry) = optionMarket.getStrikeAndExpiry(position.strikeId);

    return
      _getMinCollateral(
        OptionType(uint(position.optionType)),
        strikePrice,
        expiry,
        synthetixAdapter.getSpotPriceForMarket(address(optionMarket)),
        position.amount
      );
  }

  /// @notice Estimate minimum collateral required for a given strike with manual amount
  function _getMinCollateralForStrike(
    OptionType optionType,
    uint strikeId,
    uint amount
  ) internal view returns (uint) {
    if (_isLong(optionType)) return 0;

    uint strikePrice;
    uint expiry;
    (strikePrice, expiry) = optionMarket.getStrikeAndExpiry(strikeId);

    return
      _getMinCollateral(
        optionType,
        strikePrice,
        expiry,
        synthetixAdapter.getSpotPriceForMarket(address(optionMarket)),
        amount
      );
  }

  /////////////////
  // GWAV Oracle //
  /////////////////

  /// @notice the `baseIv` GWAV for a given `boardId` with GWAV interval `secondsAgo`
  function _ivGWAV(uint boardId, uint secondsAgo) internal view returns (uint) {
    return gwavOracle.ivGWAV(boardId, secondsAgo);
  }

  /// @notice the volatility `skew` GWAV for a given `strikeId` with GWAV interval `secondsAgo`
  function _skewGWAV(uint strikeId, uint secondsAgo) internal view returns (uint) {
    return gwavOracle.skewGWAV(strikeId, secondsAgo);
  }

  /// @notice the resultant volatility =`skew` * 'baseIv' for a given `strikeId` with GWAV interval `secondsAgo`
  function _volGWAV(uint strikeId, uint secondsAgo) internal view returns (uint) {
    return gwavOracle.volGWAV(strikeId, secondsAgo);
  }

  /// @notice the delta GWAV for a given `strikeId` with GWAV interval `secondsAgo`
  function _deltaGWAV(uint strikeId, uint secondsAgo) internal view returns (int callDelta) {
    return gwavOracle.deltaGWAV(strikeId, secondsAgo);
  }

  /// @notice the non-normalized vega GWAV for a given `strikeId` with GWAV interval `secondsAgo`
  function _vegaGWAV(uint strikeId, uint secondsAgo) internal view returns (uint) {
    return gwavOracle.vegaGWAV(strikeId, secondsAgo);
  }

  /// @notice the option price GWAV for a given `strikeId` with GWAV interval `secondsAgo`
  function _optionPriceGWAV(uint strikeId, uint secondsAgo) internal view returns (uint callPrice, uint putPrice) {
    return gwavOracle.optionPriceGWAV(strikeId, secondsAgo);
  }

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

  /// @dev format all strike related params before input into BlackScholes
  function _getBsInput(uint strikeId) internal view returns (BlackScholes.BlackScholesInputs memory bsInput) {
    (IOptionMarket.Strike memory strike, IOptionMarket.OptionBoard memory board) = optionMarket.getStrikeAndBoard(
      strikeId
    );
    bsInput = BlackScholes.BlackScholesInputs({
      timeToExpirySec: board.expiry - block.timestamp,
      volatilityDecimal: board.iv.multiplyDecimal(strike.skew),
      spotDecimal: synthetixAdapter.getSpotPriceForMarket(address(optionMarket)),
      strikePriceDecimal: strike.strikePrice,
      rateDecimal: greekCache.getGreekCacheParams().rateAndCarry
    });
  }

  /// @dev Check if position is long
  function _isLong(OptionType optionType) internal pure returns (bool) {
    return (optionType < OptionType.SHORT_CALL_BASE);
  }

  /// @dev Convert LyraAdapter.TradeInputParameters into OptionMarket.TradeInputParameters
  function _convertParams(TradeInputParameters memory _params)
    internal
    pure
    returns (IOptionMarket.TradeInputParameters memory)
  {
    return
      IOptionMarket.TradeInputParameters({
        strikeId: _params.strikeId,
        positionId: _params.positionId,
        iterations: _params.iterations,
        optionType: IOptionMarket.OptionType(uint(_params.optionType)),
        amount: _params.amount,
        setCollateralTo: _params.setCollateralTo,
        minTotalCost: _params.minTotalCost,
        maxTotalCost: _params.maxTotalCost
      });
  }

  /// @dev get lyra market addresses from LyraRegistry
  function _assignLyraRegistryMarketAddresses() internal {
    ILyraRegistry.OptionMarketAddresses memory addresses = lyraRegistry.getMarketAddresses(address(optionMarket));

    liquidityPool = ILiquidityPool(addresses.liquidityPool);
    greekCache = IOptionGreekCache(addresses.greekCache);
    optionPricer = IOptionMarketPricer(addresses.optionMarketPricer);
    optionToken = IOptionToken(addresses.optionToken);
    shortCollateral = IShortCollateral(addresses.shortCollateral);
    gwavOracle = IGWAVOracle(addresses.gwavOracle);
    quoteAsset = addresses.quoteAsset;
    baseAsset = addresses.baseAsset;
  }

  ////////////
  // Errors //
  ////////////

  error ExchangerBaseReceivedTooLow(address thrower, uint baseExpected, uint baseReceived);
  error ExchangerQuoteReceivedTooLow(address thrower, uint quoteExpected, uint quoteReceived);
}

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

pragma solidity ^0.8.9;

/**
 * @title DecimalMath
 * @author Lyra
 * @dev Modified synthetix SafeDecimalMath to include internal arithmetic underflow/overflow.
 * @dev https://docs.synthetix.io/contracts/source/libraries/SafeDecimalMath/
 */

library DecimalMath {
  /* 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 * 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 * 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 * UNIT) / 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 * (precisionUnit * 10)) / 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 * UNIT_TO_HIGH_PRECISION_CONVERSION_FACTOR;
  }

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

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

    return quotientTimesTen / 10;
  }
}

File 13 of 43 : ILiquidityPool.sol
//SPDX-License-Identifier: ISC

pragma solidity 0.8.9;

// For full documentation refer to @lyrafinance/protocol/contracts/LiquidityPool.sol";
interface ILiquidityPool {
  struct Collateral {
    uint quote;
    uint base;
  }

  /// These values are all in quoteAsset amounts.
  struct Liquidity {
    // Amount of liquidity available for option collateral and premiums
    uint freeLiquidity;
    // Amount of liquidity available for withdrawals - different to freeLiquidity
    uint burnableLiquidity;
    // Amount of liquidity reserved for long options sold to traders
    uint usedCollatLiquidity;
    // Portion of liquidity reserved for delta hedging (quote outstanding)
    uint pendingDeltaLiquidity;
    // Current value of delta hedge
    uint usedDeltaLiquidity;
    // Net asset value, including everything and netOptionValue
    uint NAV;
  }

  struct QueuedDeposit {
    uint id;
    // Who will receive the LiquidityToken minted for this deposit after the wait time
    address beneficiary;
    // The amount of quoteAsset deposited to be converted to LiquidityToken after wait time
    uint amountLiquidity;
    // The amount of LiquidityToken minted. Will equal to 0 if not processed
    uint mintedTokens;
    uint depositInitiatedTime;
  }

  struct QueuedWithdrawal {
    uint id;
    // Who will receive the quoteAsset returned after burning the LiquidityToken
    address beneficiary;
    // The amount of LiquidityToken being burnt after the wait time
    uint amountTokens;
    // The amount of quote transferred. Will equal to 0 if process not started
    uint quoteSent;
    uint withdrawInitiatedTime;
  }

  struct LiquidityPoolParameters {
    // The minimum amount of quoteAsset for a deposit, or the amount of LiquidityToken for a withdrawal
    uint minDepositWithdraw;
    // Time between initiating a deposit and when it can be processed
    uint depositDelay;
    // Time between initiating a withdrawal and when it can be processed
    uint withdrawalDelay;
    // Fee charged on withdrawn funds
    uint withdrawalFee;
    // Percentage of NAV below which the liquidity CB fires
    uint liquidityCBThreshold;
    // Length of time after the liq. CB stops firing during which deposits/withdrawals are still blocked
    uint liquidityCBTimeout;
    // Difference between the spot and GWAV baseline IVs after which point the vol CB will fire
    uint ivVarianceCBThreshold;
    // Difference between the spot and GWAV skew ratios after which point the vol CB will fire
    uint skewVarianceCBThreshold;
    // Length of time after the (base) vol. CB stops firing during which deposits/withdrawals are still blocked
    uint ivVarianceCBTimeout;
    // Length of time after the (skew) vol. CB stops firing during which deposits/withdrawals are still blocked
    uint skewVarianceCBTimeout;
    // The address of the "guardian"
    address guardianMultisig;
    // Length of time a deposit/withdrawal since initiation for before a guardian can force process their transaction
    uint guardianDelay;
    // When a new board is listed, block deposits/withdrawals
    uint boardSettlementCBTimeout;
    // When exchanging, don't exchange if fee is above this value
    uint maxFeePaid;
  }

  function poolHedger() external view returns (address);
  function queuedDeposits(uint id) external view returns (QueuedDeposit memory);
  function totalQueuedDeposits() external view returns (uint);
  function queuedDepositHead() external view returns (uint);
  function nextQueuedDepositId() external view returns (uint);
  function queuedWithdrawals(uint id) external view returns (QueuedWithdrawal memory);
  function totalQueuedWithdrawals() external view returns (uint);
  function queuedWithdrawalHead() external view returns (uint);
  function nextQueuedWithdrawalId() external view returns (uint);
  function CBTimestamp() external view returns (uint);

  /// @dev Amount of collateral locked for outstanding calls and puts sold to users
  function lockedCollateral() external view returns (Collateral memory);

  /// @dev Total amount of quoteAsset reserved for all settled options that have yet to be paid out
  function totalOutstandingSettlements() external view returns (uint);

  /// @dev Total value not transferred to this contract for all shorts that didn't have enough collateral after expiry
  function insolventSettlementAmount() external view returns (uint);

  /// @dev Total value not transferred to this contract for all liquidations that didn't have enough collateral when liquidated
  function liquidationInsolventAmount() external view returns (uint);

  function initiateDeposit(address beneficiary, uint amountQuote) external;

  function initiateWithdraw(address beneficiary, uint amountLiquidityToken) external;

  function processDepositQueue(uint limit) external;

  function processWithdrawalQueue(uint limit) external; 

  function updateCBs() external;

  function getTotalTokenSupply() external view returns (uint);

  function getTokenPriceWithCheck()
    external
    view
    returns (
      uint tokenPrice,
      bool isStale,
      uint circuitBreakerExpiry
    );

  function getTokenPrice() external view returns (uint);

  function getCurrentLiquidity() external view returns (Liquidity memory);

  function getLiquidity(uint spotPrice) external view returns (Liquidity memory);

  function getTotalPoolValueQuote() external view returns (uint);

  function exchangeBase() external;

  function getLpParams() external view returns (LiquidityPoolParameters memory);

  ////////////
  // Events //
  ////////////

  /// @dev Emitted whenever the pool paramters are updated
  event LiquidityPoolParametersUpdated(LiquidityPoolParameters lpParams);

  /// @dev Emitted whenever the poolHedger address is modified
  event PoolHedgerUpdated(address poolHedger);

  /// @dev Emitted when quote is locked.
  event QuoteLocked(uint quoteLocked, uint lockedCollateralQuote);

  /// @dev Emitted when quote is freed.
  event QuoteFreed(uint quoteFreed, uint lockedCollateralQuote);

  /// @dev Emitted when base is locked.
  event BaseLocked(uint baseLocked, uint lockedCollateralBase);

  /// @dev Emitted when base is freed.
  event BaseFreed(uint baseFreed, uint lockedCollateralBase);

  /// @dev Emitted when a board is settled.
  event BoardSettlement(uint insolventSettlementAmount, uint amountQuoteReserved, uint totalOutstandingSettlements);

  /// @dev Emitted when reserved quote is sent.
  event OutstandingSettlementSent(address indexed user, uint amount, uint totalOutstandingSettlements);

  /// @dev Emitted whenever quote is exchanged for base
  event BasePurchased(uint quoteSpent, uint baseReceived);

  /// @dev Emitted whenever base is exchanged for quote
  event BaseSold(uint amountBase, uint quoteReceived);

  /// @dev Emitted whenever premium is sent to a trader closing their position
  event PremiumTransferred(address indexed recipient, uint recipientPortion, uint optionMarketPortion);

  /// @dev Emitted whenever quote is sent to the PoolHedger
  event QuoteTransferredToPoolHedger(uint amountQuote);

  /// @dev Emitted whenever the insolvent settlement amount is updated (settlement and excess)
  event InsolventSettlementAmountUpdated(uint amountQuoteAdded, uint totalInsolventSettlementAmount);

  /// @dev Emitted whenever a user deposits and enters the queue.
  event DepositQueued(
    address indexed depositor,
    address indexed beneficiary,
    uint indexed depositQueueId,
    uint amountDeposited,
    uint totalQueuedDeposits,
    uint timestamp
  );

  /// @dev Emitted whenever a deposit gets processed. Note, can be processed without being queued.
  ///  QueueId of 0 indicates it was not queued.
  event DepositProcessed(
    address indexed caller,
    address indexed beneficiary,
    uint indexed depositQueueId,
    uint amountDeposited,
    uint tokenPrice,
    uint tokensReceived,
    uint timestamp
  );

  /// @dev Emitted whenever a deposit gets processed. Note, can be processed without being queued.
  ///  QueueId of 0 indicates it was not queued.
  event WithdrawProcessed(
    address indexed caller,
    address indexed beneficiary,
    uint indexed withdrawalQueueId,
    uint amountWithdrawn,
    uint tokenPrice,
    uint quoteReceived,
    uint totalQueuedWithdrawals,
    uint timestamp
  );
  event WithdrawPartiallyProcessed(
    address indexed caller,
    address indexed beneficiary,
    uint indexed withdrawalQueueId,
    uint amountWithdrawn,
    uint tokenPrice,
    uint quoteReceived,
    uint totalQueuedWithdrawals,
    uint timestamp
  );
  event WithdrawQueued(
    address indexed withdrawer,
    address indexed beneficiary,
    uint indexed withdrawalQueueId,
    uint amountWithdrawn,
    uint totalQueuedWithdrawals,
    uint timestamp
  );

  /// @dev Emitted whenever the CB timestamp is updated
  event CircuitBreakerUpdated(
    uint newTimestamp,
    bool ivVarianceThresholdCrossed,
    bool skewVarianceThresholdCrossed,
    bool liquidityThresholdCrossed
  );

  /// @dev Emitted whenever the CB timestamp is updated from a board settlement
  event BoardSettlementCircuitBreakerUpdated(uint newTimestamp);

  /// @dev Emitted whenever a queue item is checked for the ability to be processed
  event CheckingCanProcess(uint entryId, bool boardNotStale, bool validEntry, bool guardianBypass, bool delaysExpired);

  ////////////
  // Errors //
  ////////////

  // Admin
  error InvalidLiquidityPoolParameters(address thrower, LiquidityPoolParameters lpParams);

  // Deposits and withdrawals
  error InvalidBeneficiaryAddress(address thrower, address beneficiary);
  error MinimumDepositNotMet(address thrower, uint amountQuote, uint minDeposit);
  error MinimumWithdrawNotMet(address thrower, uint amountLiquidityToken, uint minWithdraw);

  // Liquidity and accounting
  error LockingMoreQuoteThanIsFree(address thrower, uint quoteToLock, uint freeLiquidity, Collateral lockedCollateral);
  error SendPremiumNotEnoughCollateral(address thrower, uint premium, uint reservedFee, uint freeLiquidity);
  error NotEnoughFreeToReclaimInsolvency(address thrower, uint amountQuote, Liquidity liquidity);
  error OptionValueDebtExceedsTotalAssets(address thrower, int totalAssetValue, int optionValueDebt);
  error InsufficientFreeLiquidityForBaseExchange(
    address thrower,
    uint pendingBase,
    uint estimatedExchangeCost,
    uint freeLiquidity
  );

  // Access
  error OnlyPoolHedger(address thrower, address caller, address poolHedger);
  error OnlyOptionMarket(address thrower, address caller, address optionMarket);
  error OnlyShortCollateral(address thrower, address caller, address poolHedger);

  // Token transfers
  error QuoteTransferFailed(address thrower, address from, address to, uint amount);
  error BaseTransferFailed(address thrower, address from, address to, uint amount);
}

File 14 of 43 : ISynthetixAdapter.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

import "./IAddressResolver.sol";
import "./ISynthetix.sol";
import "./IExchanger.sol";
import "./IExchangeRates.sol";
import "./IDelegateApprovals.sol";


// For full documentation refer to @lyrafinance/protocol/contracts/SynthetixAdapter.sol";
interface ISynthetixAdapter {

  struct ExchangeParams {
    // snx oracle exchange rate for base
    uint spotPrice;
    // snx quote asset identifier key
    bytes32 quoteKey;
    // snx base asset identifier key
    bytes32 baseKey;
    // snx spot exchange rate from quote to base
    uint quoteBaseFeeRate;
    // snx spot exchange rate from base to quote
    uint baseQuoteFeeRate;
  }

  /// @dev Pause the whole system. Note; this will not pause settling previously expired options.
  function isMarketPaused(address market) external view returns (bool);
  function isGlobalPaused() external view returns (bool);

  function addressResolver() external view returns (address);
  function synthetix() external view returns (address);
  function exchanger() external view returns (address);
  function exchangeRates() external view returns (address);
  function delegateApprovals() external view returns (address);

  // Variables related to calculating premium/fees
  function quoteKey(address market) external view returns (bytes32);
  function baseKey(address market) external view returns (bytes32);
  function rewardAddress(address market) external view returns (bytes32);
  function trackingCode(address market) external view returns (bytes32);


  function updateSynthetixAddresses() external;

  /////////////
  // Getters //
  /////////////
  
  function getSpotPriceForMarket(address _contractAddress)
    external
    view
    returns (uint spotPrice);

  function getSpotPrice(bytes32 to) external view returns (uint);
  
  function getExchangeParams(address optionMarket)
    external
    view
    returns (ExchangeParams memory exchangeParams);

  function requireNotGlobalPaused(address optionMarket) external view;

  /////////////////////////////////////////
  // Exchanging QuoteAsset for BaseAsset //
  /////////////////////////////////////////

  function exchangeFromExactQuote(address optionMarket, uint amountQuote) external returns (uint baseReceived);

  function exchangeToExactBase(
    ExchangeParams memory exchangeParams,
    address optionMarket,
    uint amountBase
  ) external returns (uint quoteSpent, uint baseReceived);
  
  function exchangeToExactBaseWithLimit(
    ExchangeParams memory exchangeParams,
    address optionMarket,
    uint amountBase,
    uint quoteLimit
  ) external returns (uint quoteSpent, uint baseReceived);
  
  function estimateExchangeToExactBase(ExchangeParams memory exchangeParams, uint amountBase)
    external
    pure
    returns (uint quoteNeeded);

  /////////////////////////////////////////
  // Exchanging BaseAsset for QuoteAsset //
  /////////////////////////////////////////

  function exchangeFromExactBase(address optionMarket, uint amountBase) external returns (uint quoteReceived);
  
  function exchangeToExactQuote(
    ExchangeParams memory exchangeParams,
    address optionMarket,
    uint amountQuote
  ) external returns (uint baseSpent, uint quoteReceived);

  function exchangeToExactQuoteWithLimit(
    ExchangeParams memory exchangeParams,
    address optionMarket,
    uint amountQuote,
    uint baseLimit
  ) external returns (uint baseSpent, uint quoteReceived);

  function estimateExchangeToExactQuote(ExchangeParams memory exchangeParams, uint amountQuote)
    external
    pure
    returns (uint baseNeeded);

  ////////////
  // Events //
  ////////////

  /**
   * @dev Emitted when the address resolver is set.
   */
  event AddressResolverSet(IAddressResolver addressResolver);
  /**
   * @dev Emitted when synthetix contracts are updated.
   */
  event SynthetixAddressesUpdated(
    ISynthetix synthetix,
    IExchanger exchanger,
    IExchangeRates exchangeRates,
    IDelegateApprovals delegateApprovals
  );
  /**
   * @dev Emitted when values for a given option market are set.
   */
  event GlobalsSetForContract(
    address indexed market,
    bytes32 quoteKey,
    bytes32 baseKey,
    address rewardAddress,
    bytes32 trackingCode
  );
  /**
   * @dev Emitted when GlobalPause.
   */
  event GlobalPausedSet(bool isPaused);
  /**
   * @dev Emitted when single market paused.
   */
  event MarketPausedSet(address contractAddress, bool isPaused);
  /**
   * @dev Emitted when an exchange for base to quote occurs.
   * Which base and quote were swapped can be determined by the given marketAddress.
   */
  event BaseSwappedForQuote(
    address indexed marketAddress,
    address indexed exchanger,
    uint baseSwapped,
    uint quoteReceived
  );
  /**
   * @dev Emitted when an exchange for quote to base occurs.
   * Which base and quote were swapped can be determined by the given marketAddress.
   */
  event QuoteSwappedForBase(
    address indexed marketAddress,
    address indexed exchanger,
    uint quoteSwapped,
    uint baseReceived
  );

  ////////////
  // Errors //
  ////////////
  // Admin
  error InvalidRewardAddress(address thrower, address rewardAddress);

  // Market Paused
  error AllMarketsPaused(address thrower, address marketAddress);
  error MarketIsPaused(address thrower, address marketAddress);

  // Exchanging
  error ReceivedZeroFromExchange(
    address thrower,
    bytes32 fromKey,
    bytes32 toKey,
    uint amountSwapped,
    uint amountReceived
  );
  error QuoteBaseExchangeExceedsLimit(
    address thrower,
    uint amountBaseRequested,
    uint quoteToSpend,
    uint quoteLimit,
    uint spotPrice,
    bytes32 quoteKey,
    bytes32 baseKey
  );
  error BaseQuoteExchangeExceedsLimit(
    address thrower,
    uint amountQuoteRequested,
    uint baseToSpend,
    uint baseLimit,
    uint spotPrice,
    bytes32 baseKey,
    bytes32 quoteKey
  );
  error RateIsInvalid(address thrower, uint spotPrice, bool invalid);
}

File 15 of 43 : IOptionMarketPricer.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

import "./IOptionMarket.sol";
import "./IOptionGreekCache.sol";

// For full documentation refer to @lyrafinance/protocol/contracts/OptionMarketPricer.sol";
interface IOptionMarketPricer {

  struct PricingParameters {
    // Percentage of option price that is charged as a fee
    uint optionPriceFeeCoefficient;
    // Refer to: getTimeWeightedFee()
    uint optionPriceFee1xPoint;
    uint optionPriceFee2xPoint;
    // Percentage of spot price that is charged as a fee per option
    uint spotPriceFeeCoefficient;
    // Refer to: getTimeWeightedFee()
    uint spotPriceFee1xPoint;
    uint spotPriceFee2xPoint;
    // Refer to: getVegaUtilFee()
    uint vegaFeeCoefficient;
    // The amount of options traded to move baseIv for the board up or down 1 point (depending on trade direction)
    uint standardSize;
    // The relative move of skew for a given strike based on standard sizes traded
    uint skewAdjustmentFactor;
  }

  struct TradeLimitParameters {
    // Delta cutoff past which no options can be traded (optionD > minD && optionD < 1 - minD) - using call delta
    int minDelta;
    // Delta cutoff at which ForceClose can be called (optionD < minD || optionD > 1 - minD) - using call delta
    int minForceCloseDelta;
    // Time when trading closes. Only ForceClose can be called after this
    uint tradingCutoff;
    // Lowest baseIv for a board that can be traded for regular option opens/closes
    uint minBaseIV;
    // Maximal baseIv for a board that can be traded for regular option opens/closes
    uint maxBaseIV;
    // Lowest skew for a strike that can be traded for regular option opens/closes
    uint minSkew;
    // Maximal skew for a strike that can be traded for regular option opens/closes
    uint maxSkew;
    // Minimal vol traded for regular option opens/closes (baseIv * skew)
    uint minVol;
    // Maximal vol traded for regular option opens/closes (baseIv * skew)
    uint maxVol;
    // Absolute lowest skew that ForceClose can go to
    uint absMinSkew;
    // Absolute highest skew that ForceClose can go to
    uint absMaxSkew;
    // Cap the skew the abs max/min skews - only relevant to liquidations
    bool capSkewsToAbs;
  }

  struct VarianceFeeParameters {
    uint defaultVarianceFeeCoefficient;
    uint forceCloseVarianceFeeCoefficient;
    // coefficient that allows the skew component of the fee to be scaled up
    uint skewAdjustmentCoefficient;
    // measures the difference of the skew to a reference skew
    uint referenceSkew;
    // constant to ensure small vega terms have a fee
    uint minimumStaticSkewAdjustment;
    // coefficient that allows the vega component of the fee to be scaled up
    uint vegaCoefficient;
    // constant to ensure small vega terms have a fee
    uint minimumStaticVega;
    // coefficient that allows the ivVariance component of the fee to be scaled up
    uint ivVarianceCoefficient;
    // constant to ensure small variance terms have a fee
    uint minimumStaticIvVariance;
  }

  ///////////////
  // In-memory //
  ///////////////
  struct TradeResult {
    uint amount;
    uint premium;
    uint optionPriceFee;
    uint spotPriceFee;
    VegaUtilFeeComponents vegaUtilFee;
    VarianceFeeComponents varianceFee;
    uint totalFee;
    uint totalCost;
    uint volTraded;
    uint newBaseIv;
    uint newSkew;
  }

  struct VegaUtilFeeComponents {
    int preTradeAmmNetStdVega;
    int postTradeAmmNetStdVega;
    uint vegaUtil;
    uint volTraded;
    uint NAV;
    uint vegaUtilFee;
  }

  struct VarianceFeeComponents {
    uint varianceFeeCoefficient;
    uint vega;
    uint vegaCoefficient;
    uint skew;
    uint skewCoefficient;
    uint ivVariance;
    uint ivVarianceCoefficient;
    uint varianceFee;
  }

  struct VolComponents {
    uint vol;
    uint baseIv;
    uint skew;
  }

  ///////////////
  // Variables //
  ///////////////

  function pricingParams() external view returns (PricingParameters memory);
  function tradeLimitParams() external view returns (TradeLimitParameters memory);
  function varianceFeeParams() external view returns (VarianceFeeParameters memory);

  function ivImpactForTrade(
    IOptionMarket.TradeParameters memory trade,
    uint boardBaseIv,
    uint strikeSkew
  ) external view returns (uint newBaseIv, uint newSkew);

  function getTradeResult(
    IOptionMarket.TradeParameters memory trade,
    IOptionGreekCache.TradePricing memory pricing,
    uint newBaseIv,
    uint newSkew
  ) external view returns (TradeResult memory tradeResult);
  
  function getTimeWeightedFee(
    uint expiry,
    uint pointA,
    uint pointB,
    uint coefficient
  ) external view returns (uint timeWeightedFee);

  function getVegaUtilFee(IOptionMarket.TradeParameters memory trade, IOptionGreekCache.TradePricing memory pricing)
    external
    view
    returns (VegaUtilFeeComponents memory vegaUtilFeeComponents);

  function getVarianceFee(
    IOptionMarket.TradeParameters memory trade,
    IOptionGreekCache.TradePricing memory pricing,
    uint skew
  ) external view returns (VarianceFeeComponents memory varianceFeeComponents);

  /////////////////////////////
  // External View functions //
  /////////////////////////////

  function getPricingParams() external view returns (PricingParameters memory pricingParameters);

  function getTradeLimitParams() external view returns (TradeLimitParameters memory tradeLimitParameters);

  function getVarianceFeeParams() external view returns (VarianceFeeParameters memory varianceFeeParameters);

  ////////////
  // Events //
  ////////////

  event PricingParametersSet(PricingParameters pricingParams);
  event TradeLimitParametersSet(TradeLimitParameters tradeLimitParams);
  event VarianceFeeParametersSet(VarianceFeeParameters varianceFeeParams);

  ////////////
  // Errors //
  ////////////
  // Admin
  error InvalidTradeLimitParameters(address thrower, TradeLimitParameters tradeLimitParams);
  error InvalidPricingParameters(address thrower, PricingParameters pricingParams);

  // Trade limitations
  error TradingCutoffReached(address thrower, uint tradingCutoff, uint boardExpiry, uint currentTime);
  error ForceCloseSkewOutOfRange(address thrower, bool isBuy, uint newSkew, uint minSkew, uint maxSkew);
  error VolSkewOrBaseIvOutsideOfTradingBounds(
    address thrower,
    bool isBuy,
    VolComponents currentVol,
    VolComponents newVol,
    VolComponents tradeBounds
  );
  error TradeDeltaOutOfRange(address thrower, int strikeCallDelta, int minDelta, int maxDelta);
  error ForceCloseDeltaOutOfRange(address thrower, int strikeCallDelta, int minDelta, int maxDelta);

  // Access
  error OnlyOptionMarket(address thrower, address caller, address optionMarket);
}

File 16 of 43 : IAddressResolver.sol
//SPDX-License-Identifier: ISC
pragma solidity ^0.8.9;

// https://docs.synthetix.io/contracts/source/interfaces/iaddressresolver
interface IAddressResolver {
  function getAddress(bytes32 name) external view returns (address);
}

File 17 of 43 : ISynthetix.sol
//SPDX-License-Identifier: ISC
pragma solidity ^0.8.9;

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

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

File 18 of 43 : IExchanger.sol
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.9;

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

File 19 of 43 : IExchangeRates.sol
//SPDX-License-Identifier:MIT
pragma solidity ^0.8.9;

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

File 20 of 43 : IDelegateApprovals.sol
//SPDX-License-Identifier: ISC
pragma solidity ^0.8.9;

interface IDelegateApprovals {
  function approveExchangeOnBehalf(address delegate) external;

  function canExchangeOnBehalf(address exchanger, address beneficiary) external view returns (bool);
}

File 21 of 43 : IOptionGreekCache.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

import "./IOptionMarket.sol";

// For full documentation refer to @lyrafinance/protocol/contracts/interfaces/IOptionGreekCache.sol";
interface IOptionGreekCache {

  struct GreekCacheParameters {
    // Cap the number of strikes per board to avoid hitting gasLimit constraints
    uint maxStrikesPerBoard;
    // How much spot price can move since last update before deposits/withdrawals are blocked
    uint acceptableSpotPricePercentMove;
    // How much time has passed since last update before deposits/withdrawals are blocked
    uint staleUpdateDuration;
    // Length of the GWAV for the baseline volatility used to fire the vol circuit breaker
    uint varianceIvGWAVPeriod;
    // Length of the GWAV for the skew ratios used to fire the vol circuit breaker
    uint varianceSkewGWAVPeriod;
    // Length of the GWAV for the baseline used to determine the NAV of the pool
    uint optionValueIvGWAVPeriod;
    // Length of the GWAV for the skews used to determine the NAV of the pool
    uint optionValueSkewGWAVPeriod;
    // Minimum skew that will be fed into the GWAV calculation
    // Prevents near 0 values being used to heavily manipulate the GWAV
    uint gwavSkewFloor;
    // Maximum skew that will be fed into the GWAV calculation
    uint gwavSkewCap;
    // Interest/risk free rate
    int rateAndCarry;
  }

  struct ForceCloseParameters {
    // Length of the GWAV for the baseline vol used in ForceClose() and liquidations
    uint ivGWAVPeriod;
    // Length of the GWAV for the skew ratio used in ForceClose() and liquidations
    uint skewGWAVPeriod;
    // When a user buys back an option using ForceClose() we increase the GWAV vol to penalise the trader
    uint shortVolShock;
    // Increase the penalty when within the trading cutoff
    uint shortPostCutoffVolShock;
    // When a user sells back an option to the AMM using ForceClose(), we decrease the GWAV to penalise the seller
    uint longVolShock;
    // Increase the penalty when within the trading cutoff
    uint longPostCutoffVolShock;
    // Same justification as shortPostCutoffVolShock
    uint liquidateVolShock;
    // Increase the penalty when within the trading cutoff
    uint liquidatePostCutoffVolShock;
    // Minimum price the AMM will sell back an option at for force closes (as a % of current spot)
    uint shortSpotMin;
    // Minimum price the AMM will sell back an option at for liquidations (as a % of current spot)
    uint liquidateSpotMin;
  }

  struct MinCollateralParameters {
    // Minimum collateral that must be posted for a short to be opened (denominated in quote)
    uint minStaticQuoteCollateral;
    // Minimum collateral that must be posted for a short to be opened (denominated in base)
    uint minStaticBaseCollateral;
    /* Shock Vol:
     * Vol used to compute the minimum collateral requirements for short positions.
     * This value is derived from the following chart, created by using the 4 values listed below.
     *
     *     vol
     *      |
     * volA |____
     *      |    \
     * volB |     \___
     *      |___________ time to expiry
     *         A   B
     */
    uint shockVolA;
    uint shockVolPointA;
    uint shockVolB;
    uint shockVolPointB;
    // Static percentage shock to the current spot price for calls
    uint callSpotPriceShock;
    // Static percentage shock to the current spot price for puts
    uint putSpotPriceShock;
  }

  ///////////////////
  // Cache storage //
  ///////////////////
  struct GlobalCache {
    uint minUpdatedAt;
    uint minUpdatedAtPrice;
    uint maxUpdatedAtPrice;
    uint maxSkewVariance;
    uint maxIvVariance;
    NetGreeks netGreeks;
  }

  struct OptionBoardCache {
    uint id;
    uint[] strikes;
    uint expiry;
    uint iv;
    NetGreeks netGreeks;
    uint updatedAt;
    uint updatedAtPrice;
    uint maxSkewVariance;
    uint ivVariance;
  }

  struct StrikeCache {
    uint id;
    uint boardId;
    uint strikePrice;
    uint skew;
    StrikeGreeks greeks;
    int callExposure; // long - short
    int putExposure; // long - short
    uint skewVariance; // (GWAVSkew - skew)
  }

  // These are based on GWAVed iv
  struct StrikeGreeks {
    int callDelta;
    int putDelta;
    uint stdVega;
    uint callPrice;
    uint putPrice;
  }

  // These are based on GWAVed iv
  struct NetGreeks {
    int netDelta;
    int netStdVega;
    int netOptionValue;
  }

  ///////////////
  // In-memory //
  ///////////////
  struct TradePricing {
    uint optionPrice;
    int preTradeAmmNetStdVega;
    int postTradeAmmNetStdVega;
    int callDelta;
    uint volTraded;
    uint ivVariance;
    uint vega;
  }

  struct BoardGreeksView {
    NetGreeks boardGreeks;
    uint ivGWAV;
    StrikeGreeks[] strikeGreeks;
    uint[] skewGWAVs;
  }

  
  function getPriceForForceClose(
    IOptionMarket.TradeParameters memory trade,
    IOptionMarket.Strike memory strike,
    uint expiry,
    uint newVol,
    bool isPostCutoff
  ) external view returns (uint optionPrice, uint forceCloseVol);
  
  function getMinCollateral(
    IOptionMarket.OptionType optionType,
    uint strikePrice,
    uint expiry,
    uint spotPrice,
    uint amount
  ) external view returns (uint minCollateral);

  function getShockVol(uint timeToMaturity) external view returns (uint);
  
  function updateBoardCachedGreeks(uint boardId) external;

  function isGlobalCacheStale(uint spotPrice) external view returns (bool);

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

  /////////////////////////////
  // External View functions //
  /////////////////////////////

  /// @notice Get the current cached global netDelta exposure.
  function getGlobalNetDelta() external view returns (int);

  /// @notice Get the current global net option value
  function getGlobalOptionValue() external view returns (int);

  /// @notice Returns the BoardGreeksView struct given a specific boardId
  function getBoardGreeksView(uint boardId) external view returns (BoardGreeksView memory);

  /// @notice Get StrikeCache given a specific strikeId
  function getStrikeCache(uint strikeId) external view returns (StrikeCache memory);

  /// @notice Get OptionBoardCache given a specific boardId
  function getOptionBoardCache(uint boardId) external view returns (OptionBoardCache memory);

  /// @notice Get the global cache
  function getGlobalCache() external view returns (GlobalCache memory);

  /// @notice Returns ivGWAV for a given boardId and GWAV time interval
  function getIvGWAV(uint boardId, uint secondsAgo) external view returns (uint ivGWAV);

  /// @notice Returns skewGWAV for a given strikeId and GWAV time interval
  function getSkewGWAV(uint strikeId, uint secondsAgo) external view returns (uint skewGWAV);

  /// @notice Get the GreekCacheParameters
  function getGreekCacheParams() external view returns (GreekCacheParameters memory);

  /// @notice Get the ForceCloseParamters
  function getForceCloseParams() external view returns (ForceCloseParameters memory);

  /// @notice Get the MinCollateralParamters
  function getMinCollatParams() external view returns (MinCollateralParameters memory);


  ////////////
  // Events //
  ////////////

  event GreekCacheParametersSet(GreekCacheParameters params);
  event ForceCloseParametersSet(ForceCloseParameters params);
  event MinCollateralParametersSet(MinCollateralParameters params);

  event StrikeCacheUpdated(StrikeCache strikeCache);
  event BoardCacheUpdated(OptionBoardCache boardCache);
  event GlobalCacheUpdated(GlobalCache globalCache);

  event BoardCacheRemoved(uint boardId);
  event StrikeCacheRemoved(uint strikeId);
  event BoardIvUpdated(uint boardId, uint newIv, uint globalMaxIvVariance);
  event StrikeSkewUpdated(uint strikeId, uint newSkew, uint globalMaxSkewVariance);

  ////////////
  // Errors //
  ////////////
  // Admin
  error InvalidGreekCacheParameters(address thrower, GreekCacheParameters greekCacheParams);
  error InvalidForceCloseParameters(address thrower, ForceCloseParameters forceCloseParams);
  error InvalidMinCollatParams(address thrower, MinCollateralParameters minCollatParams);

  // Board related
  error BoardStrikeLimitExceeded(address thrower, uint boardId, uint newStrikesLength, uint maxStrikesPerBoard);
  error InvalidBoardId(address thrower, uint boardId);
  error CannotUpdateExpiredBoard(address thrower, uint boardId, uint expiry, uint currentTimestamp);

  // Access
  error OnlyIOptionMarket(address thrower, address caller, address optionMarket);
  error OnlyIOptionMarketPricer(address thrower, address caller, address optionMarketPricer);
}

File 22 of 43 : GWAV.sol
// SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

import "../synthetix/SignedDecimalMath.sol";
import "../synthetix/DecimalMath.sol";
import "./FixedPointMathLib.sol";

/**
 * @title Geometric Moving Average Oracle
 * @author Lyra
 * @dev Instances of stored oracle data, "observations", are collected in the oracle array
 *
 * The GWAV values are calculated from the blockTimestamps and "q" accumulator values of two Observations. When
 * requested the closest observations are scaled to the requested timestamp.
 */
library GWAV {
  using DecimalMath for uint;
  using SignedDecimalMath for int;

  /// @dev Stores all past Observations and the current index
  struct Params {
    Observation[] observations;
    uint index;
  }

  /// @dev An observation holds the cumulative log value of all historic observations (accumulator)
  /// and other relevant fields for computing the next accumulator value.
  /// @dev A pair of oracle Observations is used to deduce the GWAV TWAP
  struct Observation {
    int q; // accumulator value used to compute GWAV
    uint nextVal; // value at the time the observation was made, used to calculate the next q value
    uint blockTimestamp;
  }

  /////////////
  // Setters //
  /////////////

  /**
   * @notice Initialize the oracle array by writing the first Observation.
   * @dev Called once for the lifecycle of the observations array
   * @dev First Observation uses blockTimestamp as the time interval to prevent manipulation of the GWAV immediately
   * after initialization
   * @param self Stores past Observations and the index of the latest Observation
   * @param newVal First observed value for blockTimestamp
   * @param blockTimestamp Timestamp of first Observation
   */
  function _initialize(
    Params storage self,
    uint newVal,
    uint blockTimestamp
  ) internal {
    // if Observation older than blockTimestamp is used for GWAV,
    // _getFirstBefore() will scale the first Observation "q" accordingly
    _initializeWithManualQ(self, FixedPointMathLib.ln((int(newVal))) * int(blockTimestamp), newVal, blockTimestamp);
  }

  /**
   * @notice Writes an oracle Observation to the GWAV array
   * @dev Writable at most once per block. BlockTimestamp must be > last.blockTimestamp
   * @param self Stores past Observations and the index of the latest Observation
   * @param nextVal Value at given blockTimestamp
   * @param blockTimestamp Current blockTimestamp
   */
  function _write(
    Params storage self,
    uint nextVal,
    uint blockTimestamp
  ) internal {
    Observation memory last = self.observations[self.index];

    // Ensure entries are sequential
    if (blockTimestamp < last.blockTimestamp) {
      revert InvalidBlockTimestamp(address(this), blockTimestamp, last.blockTimestamp);
    }

    // early return if we've already written an observation this block
    if (last.blockTimestamp == blockTimestamp) {
      self.observations[self.index].nextVal = nextVal;
      return;
    }
    // No reason to record an entry if it's the same as the last one
    if (last.nextVal == nextVal) return;

    // update accumulator value
    // assumes the market value between the previous and current blockTimstamps was "last.nextVal"
    uint timestampDelta = blockTimestamp - last.blockTimestamp;
    int newQ = last.q + FixedPointMathLib.ln((int(last.nextVal))) * int(timestampDelta);

    // update latest index and store Observation
    uint indexUpdated = (self.index + 1);
    self.observations.push(_transform(newQ, nextVal, blockTimestamp));
    self.index = indexUpdated;
  }

  /////////////
  // Getters //
  /////////////

  /**
   * @notice Calculates the geometric moving average between two Observations A & B. These observations are scaled to
   * the requested timestamps
   * @dev For the current GWAV value, "0" may be passed in for secondsAgo
   * @dev If timestamps A==B, returns the value at A/B.
   * @param self Stores past Observations and the index of the latest Observation
   * @param secondsAgoA Seconds from blockTimestamp to Observation A
   * @param secondsAgoB Seconds from blockTimestamp to Observation B
   */
  function getGWAVForPeriod(
    Params storage self,
    uint secondsAgoA,
    uint secondsAgoB
  ) public view returns (uint) {
    (int q0, uint t0) = queryFirstBeforeAndScale(self, block.timestamp, secondsAgoA);
    (int q1, uint t1) = queryFirstBeforeAndScale(self, block.timestamp, secondsAgoB);

    if (t0 == t1) {
      return uint(FixedPointMathLib.exp(q1 / int(t1)));
    }

    return uint(FixedPointMathLib.exp((q1 - q0) / int(t1 - t0)));
  }

  /**
   * @notice Returns the GWAV accumulator/timestamps values for each "secondsAgo" in the array `secondsAgos[]`
   * @param currentBlockTimestamp Timestamp of current block
   * @param secondsAgos Array of all timestamps for which to export accumulator/timestamp values
   */
  function observe(
    Params storage self,
    uint currentBlockTimestamp,
    uint[] memory secondsAgos
  ) public view returns (int[] memory qCumulatives, uint[] memory timestamps) {
    uint secondsAgosLength = secondsAgos.length;
    qCumulatives = new int[](secondsAgosLength);
    timestamps = new uint[](secondsAgosLength);
    for (uint i = 0; i < secondsAgosLength; ++i) {
      (qCumulatives[i], timestamps[i]) = queryFirstBefore(self, currentBlockTimestamp, secondsAgos[i]);
    }
  }

  //////////////////////////////////////////////////////
  // Querying observation closest to target timestamp //
  //////////////////////////////////////////////////////

  /**
   * @notice Finds the first observation before a timestamp "secondsAgo" from the "currentBlockTimestamp"
   * @dev If target falls between two Observations, the older one is returned
   * @dev See _queryFirstBefore() for edge cases where target lands
   * after the newest Observation or before the oldest Observation
   * @dev Reverts if secondsAgo exceeds the currentBlockTimestamp
   * @param self Stores past Observations and the index of the latest Observation
   * @param currentBlockTimestamp Timestamp of current block
   * @param secondsAgo Seconds from currentBlockTimestamp to target Observation
   */
  function queryFirstBefore(
    Params storage self,
    uint currentBlockTimestamp,
    uint secondsAgo
  ) internal view returns (int qCumulative, uint timestamp) {
    uint target = currentBlockTimestamp - secondsAgo;
    Observation memory beforeOrAt = _queryFirstBefore(self, target);

    return (beforeOrAt.q, beforeOrAt.blockTimestamp);
  }

  function queryFirstBeforeAndScale(
    Params storage self,
    uint currentBlockTimestamp,
    uint secondsAgo
  ) internal view returns (int qCumulative, uint timestamp) {
    uint target = currentBlockTimestamp - secondsAgo;
    Observation memory beforeOrAt = _queryFirstBefore(self, target);

    int timestampDelta = int(target - beforeOrAt.blockTimestamp);

    return (beforeOrAt.q + (FixedPointMathLib.ln(int(beforeOrAt.nextVal)) * timestampDelta), target);
  }

  /**
   * @notice Finds the first observation before the "target" timestamp
   * @dev Checks for trivial scenarios before entering _binarySearch()
   * @dev Assumes _initialize() has been called
   * @param self Stores past Observations and the index of the latest Observation
   * @param target BlockTimestamp of target Observation
   */
  function _queryFirstBefore(Params storage self, uint target) private view returns (Observation memory beforeOrAt) {
    // Case 1: target blockTimestamp is at or after the most recent Observation
    beforeOrAt = self.observations[self.index];
    if (beforeOrAt.blockTimestamp <= target) {
      return (beforeOrAt);
    }

    // Now, set to the oldest observation
    beforeOrAt = self.observations[0];

    // Case 2: target blockTimestamp is older than the oldest Observation
    // The observation is scaled to the target using the nextVal
    if (beforeOrAt.blockTimestamp > target) {
      return _transform((beforeOrAt.q * int(target)) / int(beforeOrAt.blockTimestamp), beforeOrAt.nextVal, target);
    }

    // Case 3: target is within the recorded Observations.
    return self.observations[_binarySearch(self, target)];
  }

  /**
   * @notice Finds closest Observation before target using binary search and returns its index
   * @dev Used when the target is located within the stored observation boundaries
   * e.g. Older than the most recent observation and younger, or the same age as, the oldest observation
   * @return foundIndex Returns the Observation which is older than target (instead of newer)
   * @param self Stores past Observations and the index of the latest Observation
   * @param target BlockTimestamp of target Observation
   */
  function _binarySearch(Params storage self, uint target) internal view returns (uint) {
    uint oldest = 0; // oldest observation
    uint newest = self.index; // newest observation
    uint i;
    while (true) {
      i = (oldest + newest) / 2;
      uint beforeOrAtTimestamp = self.observations[i].blockTimestamp;

      uint atOrAfterTimestamp = self.observations[i + 1].blockTimestamp;
      bool targetAtOrAfter = beforeOrAtTimestamp <= target;

      // check if we've found the answer!
      if (targetAtOrAfter && target <= atOrAfterTimestamp) break;

      if (!targetAtOrAfter) {
        newest = i - 1;
      } else {
        oldest = i + 1;
      }
    }

    return i;
  }

  /////////////
  // Utility //
  /////////////

  /**
   * @notice Creates the first Observation with manual Q accumulator value.
   * @param qVal Initial GWAV accumulator value
   * @param nextVal First observed value for blockTimestamp
   * @param blockTimestamp Timestamp of Observation
   */
  function _initializeWithManualQ(
    Params storage self,
    int qVal,
    uint nextVal,
    uint blockTimestamp
  ) internal {
    self.observations.push(Observation({q: qVal, nextVal: nextVal, blockTimestamp: blockTimestamp}));
  }

  /**
   * @dev Creates an Observation given a GWAV accumulator, latest value, and a blockTimestamp
   */
  function _transform(
    int newQ,
    uint nextVal,
    uint blockTimestamp
  ) private pure returns (Observation memory) {
    return Observation({q: newQ, nextVal: nextVal, blockTimestamp: blockTimestamp});
  }

  ////////////
  // Errors //
  ////////////
  error InvalidBlockTimestamp(address thrower, uint timestamp, uint lastObservedTimestamp);
}

File 23 of 43 : BlackScholes.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

// Libraries
import "../synthetix/SignedDecimalMath.sol";
import "../synthetix/DecimalMath.sol";
import "./FixedPointMathLib.sol";

/**
 * @title BlackScholes
 * @author Lyra
 * @dev Contract to compute the black scholes price of options. Where the unit is unspecified, it should be treated as a
 * PRECISE_DECIMAL, which has 1e27 units of precision. The default decimal matches the ethereum standard of 1e18 units
 * of precision.
 */
library BlackScholes {
  using DecimalMath for uint;
  using SignedDecimalMath for int;

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

  /**
   * @param timeToExpirySec Number of seconds to the expiry of the option
   * @param volatilityDecimal Implied volatility over the period til expiry as a percentage
   * @param spotDecimal The current price of the base asset
   * @param strikePriceDecimal The strikePrice price of the option
   * @param rateDecimal The percentage risk free rate + carry cost
   */
  struct BlackScholesInputs {
    uint timeToExpirySec;
    uint volatilityDecimal;
    uint spotDecimal;
    uint strikePriceDecimal;
    int rateDecimal;
  }

  uint private constant SECONDS_PER_YEAR = 31536000;
  /// @dev Internally this library uses 27 decimals of precision
  uint private constant PRECISE_UNIT = 1e27;
  uint private constant SQRT_TWOPI = 2506628274631000502415765285;
  /// @dev Below this value, return 0
  int private constant MIN_CDF_STD_DIST_INPUT = (int(PRECISE_UNIT) * -45) / 10; // -4.5
  /// @dev Above this value, return 1
  int private constant MAX_CDF_STD_DIST_INPUT = int(PRECISE_UNIT) * 10;
  /// @dev Value to use to avoid any division by 0 or values near 0
  uint private constant MIN_T_ANNUALISED = PRECISE_UNIT / SECONDS_PER_YEAR; // 1 second
  uint private constant MIN_VOLATILITY = PRECISE_UNIT / 10000; // 0.001%
  uint private constant VEGA_STANDARDISATION_MIN_DAYS = 7 days;
  /// @dev Magic numbers for normal CDF
  uint private constant SPLIT = 7071067811865470000000000000;
  uint private constant N0 = 220206867912376000000000000000;
  uint private constant N1 = 221213596169931000000000000000;
  uint private constant N2 = 112079291497871000000000000000;
  uint private constant N3 = 33912866078383000000000000000;
  uint private constant N4 = 6373962203531650000000000000;
  uint private constant N5 = 700383064443688000000000000;
  uint private constant N6 = 35262496599891100000000000;
  uint private constant M0 = 440413735824752000000000000000;
  uint private constant M1 = 793826512519948000000000000000;
  uint private constant M2 = 637333633378831000000000000000;
  uint private constant M3 = 296564248779674000000000000000;
  uint private constant M4 = 86780732202946100000000000000;
  uint private constant M5 = 16064177579207000000000000000;
  uint private constant M6 = 1755667163182640000000000000;
  uint private constant M7 = 88388347648318400000000000;

  /////////////////////////////////////
  // Option Pricing public functions //
  /////////////////////////////////////

  /**
   * @dev Returns call and put prices for options with given parameters.
   */
  function optionPrices(BlackScholesInputs memory bsInput) public pure returns (uint call, uint put) {
    uint tAnnualised = _annualise(bsInput.timeToExpirySec);
    uint spotPrecise = bsInput.spotDecimal.decimalToPreciseDecimal();
    uint strikePricePrecise = bsInput.strikePriceDecimal.decimalToPreciseDecimal();
    int ratePrecise = bsInput.rateDecimal.decimalToPreciseDecimal();
    (int d1, int d2) = _d1d2(
      tAnnualised,
      bsInput.volatilityDecimal.decimalToPreciseDecimal(),
      spotPrecise,
      strikePricePrecise,
      ratePrecise
    );
    (call, put) = _optionPrices(tAnnualised, spotPrecise, strikePricePrecise, ratePrecise, d1, d2);
    return (call.preciseDecimalToDecimal(), put.preciseDecimalToDecimal());
  }

  /**
   * @dev Returns call/put prices and delta/stdVega for options with given parameters.
   */
  function pricesDeltaStdVega(BlackScholesInputs memory bsInput) public pure returns (PricesDeltaStdVega memory) {
    uint tAnnualised = _annualise(bsInput.timeToExpirySec);
    uint spotPrecise = bsInput.spotDecimal.decimalToPreciseDecimal();

    (int d1, int d2) = _d1d2(
      tAnnualised,
      bsInput.volatilityDecimal.decimalToPreciseDecimal(),
      spotPrecise,
      bsInput.strikePriceDecimal.decimalToPreciseDecimal(),
      bsInput.rateDecimal.decimalToPreciseDecimal()
    );
    (uint callPrice, uint putPrice) = _optionPrices(
      tAnnualised,
      spotPrecise,
      bsInput.strikePriceDecimal.decimalToPreciseDecimal(),
      bsInput.rateDecimal.decimalToPreciseDecimal(),
      d1,
      d2
    );
    (uint vegaPrecise, uint stdVegaPrecise) = _standardVega(d1, spotPrecise, bsInput.timeToExpirySec);
    (int callDelta, int putDelta) = _delta(d1);

    return
      PricesDeltaStdVega(
        callPrice.preciseDecimalToDecimal(),
        putPrice.preciseDecimalToDecimal(),
        callDelta.preciseDecimalToDecimal(),
        putDelta.preciseDecimalToDecimal(),
        vegaPrecise.preciseDecimalToDecimal(),
        stdVegaPrecise.preciseDecimalToDecimal()
      );
  }

  /**
   * @dev Returns call delta given parameters.
   */

  function delta(BlackScholesInputs memory bsInput) public pure returns (int callDeltaDecimal, int putDeltaDecimal) {
    uint tAnnualised = _annualise(bsInput.timeToExpirySec);
    uint spotPrecise = bsInput.spotDecimal.decimalToPreciseDecimal();

    (int d1, ) = _d1d2(
      tAnnualised,
      bsInput.volatilityDecimal.decimalToPreciseDecimal(),
      spotPrecise,
      bsInput.strikePriceDecimal.decimalToPreciseDecimal(),
      bsInput.rateDecimal.decimalToPreciseDecimal()
    );

    (int callDelta, int putDelta) = _delta(d1);
    return (callDelta.preciseDecimalToDecimal(), putDelta.preciseDecimalToDecimal());
  }

  /**
   * @dev Returns non-normalized vega given parameters. Quoted in cents.
   */
  function vega(BlackScholesInputs memory bsInput) public pure returns (uint vegaDecimal) {
    uint tAnnualised = _annualise(bsInput.timeToExpirySec);
    uint spotPrecise = bsInput.spotDecimal.decimalToPreciseDecimal();

    (int d1, ) = _d1d2(
      tAnnualised,
      bsInput.volatilityDecimal.decimalToPreciseDecimal(),
      spotPrecise,
      bsInput.strikePriceDecimal.decimalToPreciseDecimal(),
      bsInput.rateDecimal.decimalToPreciseDecimal()
    );
    return _vega(tAnnualised, spotPrecise, d1).preciseDecimalToDecimal();
  }

  //////////////////////
  // Computing Greeks //
  //////////////////////

  /**
   * @dev Returns internal coefficients of the Black-Scholes call price formula, d1 and d2.
   * @param tAnnualised Number of years to expiry
   * @param volatility Implied volatility over the period til expiry as a percentage
   * @param spot The current price of the base asset
   * @param strikePrice The strikePrice price of the option
   * @param rate The percentage risk free rate + carry cost
   */
  function _d1d2(
    uint tAnnualised,
    uint volatility,
    uint spot,
    uint strikePrice,
    int rate
  ) internal pure returns (int d1, int d2) {
    // Set minimum values for tAnnualised and volatility to not break computation in extreme scenarios
    // These values will result in option prices reflecting only the difference in stock/strikePrice, which is expected.
    // This should be caught before calling this function, however the function shouldn't break if the values are 0.
    tAnnualised = tAnnualised < MIN_T_ANNUALISED ? MIN_T_ANNUALISED : tAnnualised;
    volatility = volatility < MIN_VOLATILITY ? MIN_VOLATILITY : volatility;

    int vtSqrt = int(volatility.multiplyDecimalRoundPrecise(_sqrtPrecise(tAnnualised)));
    int log = FixedPointMathLib.lnPrecise(int(spot.divideDecimalRoundPrecise(strikePrice)));
    int v2t = (int(volatility.multiplyDecimalRoundPrecise(volatility) / 2) + rate).multiplyDecimalRoundPrecise(
      int(tAnnualised)
    );
    d1 = (log + v2t).divideDecimalRoundPrecise(vtSqrt);
    d2 = d1 - vtSqrt;
  }

  /**
   * @dev Internal coefficients of the Black-Scholes call price formula.
   * @param tAnnualised Number of years to expiry
   * @param spot The current price of the base asset
   * @param strikePrice The strikePrice price of the option
   * @param rate The percentage risk free rate + carry cost
   * @param d1 Internal coefficient of Black-Scholes
   * @param d2 Internal coefficient of Black-Scholes
   */
  function _optionPrices(
    uint tAnnualised,
    uint spot,
    uint strikePrice,
    int rate,
    int d1,
    int d2
  ) internal pure returns (uint call, uint put) {
    uint strikePricePV = strikePrice.multiplyDecimalRoundPrecise(
      FixedPointMathLib.expPrecise(int(-rate.multiplyDecimalRoundPrecise(int(tAnnualised))))
    );
    uint spotNd1 = spot.multiplyDecimalRoundPrecise(_stdNormalCDF(d1));
    uint strikePriceNd2 = strikePricePV.multiplyDecimalRoundPrecise(_stdNormalCDF(d2));

    // We clamp to zero if the minuend is less than the subtrahend
    // In some scenarios it may be better to compute put price instead and derive call from it depending on which way
    // around is more precise.
    call = strikePriceNd2 <= spotNd1 ? spotNd1 - strikePriceNd2 : 0;
    put = call + strikePricePV;
    put = spot <= put ? put - spot : 0;
  }

  /*
   * Greeks
   */

  /**
   * @dev Returns the option's delta value
   * @param d1 Internal coefficient of Black-Scholes
   */
  function _delta(int d1) internal pure returns (int callDelta, int putDelta) {
    callDelta = int(_stdNormalCDF(d1));
    putDelta = callDelta - int(PRECISE_UNIT);
  }

  /**
   * @dev Returns the option's vega value based on d1. Quoted in cents.
   *
   * @param d1 Internal coefficient of Black-Scholes
   * @param tAnnualised Number of years to expiry
   * @param spot The current price of the base asset
   */
  function _vega(
    uint tAnnualised,
    uint spot,
    int d1
  ) internal pure returns (uint) {
    return _sqrtPrecise(tAnnualised).multiplyDecimalRoundPrecise(_stdNormal(d1).multiplyDecimalRoundPrecise(spot));
  }

  /**
   * @dev Returns the option's vega value with expiry modified to be at least VEGA_STANDARDISATION_MIN_DAYS
   * @param d1 Internal coefficient of Black-Scholes
   * @param spot The current price of the base asset
   * @param timeToExpirySec Number of seconds to expiry
   */
  function _standardVega(
    int d1,
    uint spot,
    uint timeToExpirySec
  ) internal pure returns (uint, uint) {
    uint tAnnualised = _annualise(timeToExpirySec);
    uint normalisationFactor = _getVegaNormalisationFactorPrecise(timeToExpirySec);
    uint vegaPrecise = _vega(tAnnualised, spot, d1);
    return (vegaPrecise, vegaPrecise.multiplyDecimalRoundPrecise(normalisationFactor));
  }

  function _getVegaNormalisationFactorPrecise(uint timeToExpirySec) internal pure returns (uint) {
    timeToExpirySec = timeToExpirySec < VEGA_STANDARDISATION_MIN_DAYS ? VEGA_STANDARDISATION_MIN_DAYS : timeToExpirySec;
    uint daysToExpiry = timeToExpirySec / 1 days;
    uint thirty = 30 * PRECISE_UNIT;
    return _sqrtPrecise(thirty / daysToExpiry) / 100;
  }

  /////////////////////
  // Math Operations //
  /////////////////////

  /**
   * @dev Compute the absolute value of `val`.
   *
   * @param val The number to absolute value.
   */
  function _abs(int val) internal pure returns (uint) {
    return uint(val < 0 ? -val : val);
  }

  /// @notice Calculates the square root of x, rounding down (borrowed from https://github.com/paulrberg/prb-math)
  /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
  /// @param x The uint256 number for which to calculate the square root.
  /// @return result The result as an uint256.
  function _sqrt(uint x) internal pure returns (uint result) {
    if (x == 0) {
      return 0;
    }

    // Calculate the square root of the perfect square of a power of two that is the closest to x.
    uint xAux = uint(x);
    result = 1;
    if (xAux >= 0x100000000000000000000000000000000) {
      xAux >>= 128;
      result <<= 64;
    }
    if (xAux >= 0x10000000000000000) {
      xAux >>= 64;
      result <<= 32;
    }
    if (xAux >= 0x100000000) {
      xAux >>= 32;
      result <<= 16;
    }
    if (xAux >= 0x10000) {
      xAux >>= 16;
      result <<= 8;
    }
    if (xAux >= 0x100) {
      xAux >>= 8;
      result <<= 4;
    }
    if (xAux >= 0x10) {
      xAux >>= 4;
      result <<= 2;
    }
    if (xAux >= 0x8) {
      result <<= 1;
    }

    // The operations can never overflow because the result is max 2^127 when it enters this block.
    unchecked {
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1;
      result = (result + x / result) >> 1; // Seven iterations should be enough
      uint roundedDownResult = x / result;
      return result >= roundedDownResult ? roundedDownResult : result;
    }
  }

  /**
   * @dev Returns the square root of the value using Newton's method.
   */
  function _sqrtPrecise(uint x) internal pure returns (uint) {
    // Add in an extra unit factor for the square root to gobble;
    // otherwise, sqrt(x * UNIT) = sqrt(x) * sqrt(UNIT)
    return _sqrt(x * PRECISE_UNIT);
  }

  /**
   * @dev The standard normal distribution of the value.
   */
  function _stdNormal(int x) internal pure returns (uint) {
    return
      FixedPointMathLib.expPrecise(int(-x.multiplyDecimalRoundPrecise(x / 2))).divideDecimalRoundPrecise(SQRT_TWOPI);
  }

  /**
   * @dev The standard normal cumulative distribution of the value.
   * borrowed from a C++ implementation https://stackoverflow.com/a/23119456
   */
  function _stdNormalCDF(int x) public pure returns (uint) {
    uint z = _abs(x);
    int c;

    if (z <= 37 * PRECISE_UNIT) {
      uint e = FixedPointMathLib.expPrecise(-int(z.multiplyDecimalRoundPrecise(z / 2)));
      if (z < SPLIT) {
        c = int(
          (_stdNormalCDFNumerator(z).divideDecimalRoundPrecise(_stdNormalCDFDenom(z)).multiplyDecimalRoundPrecise(e))
        );
      } else {
        uint f = (z +
          PRECISE_UNIT.divideDecimalRoundPrecise(
            z +
              (2 * PRECISE_UNIT).divideDecimalRoundPrecise(
                z +
                  (3 * PRECISE_UNIT).divideDecimalRoundPrecise(
                    z + (4 * PRECISE_UNIT).divideDecimalRoundPrecise(z + ((PRECISE_UNIT * 13) / 20))
                  )
              )
          ));
        c = int(e.divideDecimalRoundPrecise(f.multiplyDecimalRoundPrecise(SQRT_TWOPI)));
      }
    }
    return uint((x <= 0 ? c : (int(PRECISE_UNIT) - c)));
  }

  /**
   * @dev Helper for _stdNormalCDF
   */
  function _stdNormalCDFNumerator(uint z) internal pure returns (uint) {
    uint numeratorInner = ((((((N6 * z) / PRECISE_UNIT + N5) * z) / PRECISE_UNIT + N4) * z) / PRECISE_UNIT + N3);
    return (((((numeratorInner * z) / PRECISE_UNIT + N2) * z) / PRECISE_UNIT + N1) * z) / PRECISE_UNIT + N0;
  }

  /**
   * @dev Helper for _stdNormalCDF
   */
  function _stdNormalCDFDenom(uint z) internal pure returns (uint) {
    uint denominatorInner = ((((((M7 * z) / PRECISE_UNIT + M6) * z) / PRECISE_UNIT + M5) * z) / PRECISE_UNIT + M4);
    return
      (((((((denominatorInner * z) / PRECISE_UNIT + M3) * z) / PRECISE_UNIT + M2) * z) / PRECISE_UNIT + M1) * z) /
      PRECISE_UNIT +
      M0;
  }

  /**
   * @dev Converts an integer number of seconds to a fractional number of years.
   */
  function _annualise(uint secs) internal pure returns (uint yearFraction) {
    return secs.divideDecimalRoundPrecise(SECONDS_PER_YEAR);
  }
}

File 24 of 43 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^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() {
        _transferOwnership(_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 {
        _transferOwnership(address(0));
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 25 of 43 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

File 26 of 43 : IOptionToken.sol
//SPDX-License-Identifier: ISC
pragma solidity 0.8.9;

// Interfaces
import "./IOptionMarket.sol";
import "./ISynthetixAdapter.sol";
import "./IOptionGreekCache.sol";
import "openzeppelin-contracts-4.4.1/token/ERC721/IERC721.sol";

// For full documentation refer to @lyrafinance/protocol/contracts/OptionToken.sol";
interface IOptionToken is IERC721 {

  enum PositionState {
    EMPTY,
    ACTIVE,
    CLOSED,
    LIQUIDATED,
    SETTLED,
    MERGED
  }

  enum PositionUpdatedType {
    OPENED,
    ADJUSTED,
    CLOSED,
    SPLIT_FROM,
    SPLIT_INTO,
    MERGED,
    MERGED_INTO,
    SETTLED,
    LIQUIDATED,
    TRANSFER
  }

  struct OptionPosition {
    uint positionId;
    uint strikeId;
    IOptionMarket.OptionType optionType;
    uint amount;
    uint collateral;
    PositionState state;
  }


  struct PartialCollateralParameters {
    // Percent of collateral used for penalty (amm + sm + liquidator fees)
    uint penaltyRatio;
    // Percent of penalty used for amm fees
    uint liquidatorFeeRatio;
    // Percent of penalty used for SM fees
    uint smFeeRatio;
    // Minimal value of quote that is used to charge a fee
    uint minLiquidationFee;
  }

  struct PositionWithOwner {
    uint positionId;
    uint strikeId;
    IOptionMarket.OptionType optionType;
    uint amount;
    uint collateral;
    PositionState state;
    address owner;
  }

  struct LiquidationFees {
    uint returnCollateral; // quote || base
    uint lpPremiums; // quote || base
    uint lpFee; // quote || base
    uint liquidatorFee; // quote || base
    uint smFee; // quote || base
    uint insolventAmount; // quote
  }

  function positions(uint positionId) external view returns (OptionPosition memory);

  function nextId() external view returns (uint);

  function partialCollatParams() external view returns (PartialCollateralParameters memory);

  function baseURI() external view returns (string memory);

  function canLiquidate(
    OptionPosition memory position,
    uint expiry,
    uint strikePrice,
    uint spotPrice
  ) external view returns (bool);

  function getLiquidationFees(
    uint gwavPremium, // quote || base
    uint userPositionCollateral, // quote || base
    uint convertedMinLiquidationFee, // quote || base
    uint insolvencyMultiplier // 1 for quote || spotPrice for base
  ) external view returns (LiquidationFees memory liquidationFees);

  ///////////////
  // Transfers //
  ///////////////

  function split(
    uint positionId,
    uint newAmount,
    uint newCollateral,
    address recipient
  ) external returns (uint newPositionId);

  function merge(uint[] memory positionIds) external;

  //////////
  // View //
  //////////


  /// @dev Returns the PositionState of a given positionId
  function getPositionState(uint positionId) external view returns (PositionState);

  /// @dev Returns an OptionPosition struct of a given positionId
  function getOptionPosition(uint positionId) external view returns (OptionPosition memory);

  /// @dev Returns an array of OptionPosition structs given an array of positionIds
  function getOptionPositions(uint[] memory positionIds) external view returns (OptionPosition[] memory);

  /// @dev Returns a PositionWithOwner struct of a given positionId (same as OptionPosition but with owner)
  function getPositionWithOwner(uint positionId) external view returns (PositionWithOwner memory);

  /// @dev Returns an array of PositionWithOwner structs given an array of positionIds
  function getPositionsWithOwner(uint[] memory positionIds) external view returns (PositionWithOwner[] memory);

  /// @notice Returns an array of OptionPosition structs owned by a given address
  /// @dev Meant to be used offchain as it can run out of gas
  function getOwnerPositions(address target) external view returns (OptionPosition[] memory);

  /// @dev returns PartialCollateralParameters struct
  function getPartialCollatParams() external view returns (PartialCollateralParameters memory);

  ////////////
  // Events //
  ///////////

  /**
   * @dev Emitted when the URI is modified
   */
  event URISet(string URI);

  /**
   * @dev Emitted when partial collateral parameters are modified
   */
  event PartialCollateralParamsSet(PartialCollateralParameters partialCollateralParams);

  /**
   * @dev Emitted when a position is minted, adjusted, burned, merged or split.
   */
  event PositionUpdated(
    uint indexed positionId,
    address indexed owner,
    PositionUpdatedType indexed updatedType,
    OptionPosition position,
    uint timestamp
  );

  ////////////
  // Errors //
  ////////////

  // Admin
  error InvalidPartialCollateralParameters(address thrower, PartialCollateralParameters partialCollatParams);

  // Adjusting
  error AdjustmentResultsInMinimumCollateralNotBeingMet(address thrower, OptionPosition position, uint spotPrice);
  error CannotClosePositionZero(address thrower);
  error CannotOpenZeroAmount(address thrower);
  error CannotAdjustInvalidPosition(
    address thrower,
    uint positionId,
    bool invalidPositionId,
    bool positionInactive,
    bool strikeMismatch,
    bool optionTypeMismatch
  );
  error OnlyOwnerCanAdjustPosition(address thrower, uint positionId, address trader, address owner);
  error FullyClosingWithNonZeroSetCollateral(address thrower, uint positionId, uint setCollateralTo);
  error AddingCollateralToInvalidPosition(
    address thrower,
    uint positionId,
    bool invalidPositionId,
    bool positionInactive,
    bool isShort
  );

  // Liquidation
  error PositionNotLiquidatable(address thrower, OptionPosition position, uint spotPrice);

  // Splitting
  error SplittingUnapprovedPosition(address thrower, address caller, uint positionId);
  error InvalidSplitAmount(address thrower, uint originalPositionAmount, uint splitAmount);
  error ResultingOriginalPositionLiquidatable(address thrower, OptionPosition position, uint spotPrice);
  error ResultingNewPositionLiquidatable(address thrower, OptionPosition position, uint spotPrice);

  // Merging
  error MustMergeTwoOrMorePositions(address thrower);
  error MergingUnapprovedPosition(address thrower, address caller, uint positionId);
  error PositionMismatchWhenMerging(
    address thrower,
    OptionPosition firstPosition,
    OptionPosition nextPosition,
    bool ownerMismatch,
    bool strikeMismatch,
    bool optionTypeMismatch,
    bool duplicatePositionId
  );

  // Access
  error StrikeIsSettled(address thrower, uint strikeId);
  error OnlyOptionMarket(address thrower, address caller, address optionMarket);
  error OnlyShortCollateral(address thrower, address caller, address shortCollateral);
}

File 27 of 43 : IShortCollateral.sol
//SPDX-License-Identifier: ISC

pragma solidity 0.8.9;

// Interfaces
import "./IOptionMarket.sol";
import "./IOptionToken.sol";

// For full documentation refer to @lyrafinance/protocol/contracts/ShortCollateral.sol";

interface IShortCollateral {
  // The amount the SC underpaid the LP due to insolvency.
  // The SC will take this much less from the LP when settling insolvent positions.
  function LPBaseExcess() external view returns (uint);
  function LPQuoteExcess() external view returns (uint);

  /////////////////////////
  // Position Settlement //
  /////////////////////////

  function settleOptions(uint[] memory positionIds) external;

  ////////////
  // Events //
  ////////////

  /// @dev Emitted when a board is settled
  event BoardSettlementCollateralSent(
    uint amountBaseSent,
    uint amountQuoteSent,
    uint lpBaseInsolvency,
    uint lpQuoteInsolvency,
    uint LPBaseExcess,
    uint LPQuoteExcess
  );

  /**
   * @dev Emitted when an Option is settled.
   */
  event PositionSettled(
    uint indexed positionId,
    address indexed settler,
    address indexed optionOwner,
    uint strikePrice,
    uint priceAtExpiry,
    IOptionMarket.OptionType optionType,
    uint amount,
    uint settlementAmount,
    uint insolventAmount
  );

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

  event BaseExchangedAndQuoteSent(address indexed recipient, uint amountBase, uint quoteReceived);

  ////////////
  // Errors //
  ////////////

  // Collateral transfers
  error OutOfQuoteCollateralForTransfer(address thrower, uint balance, uint amount);
  error OutOfBaseCollateralForTransfer(address thrower, uint balance, uint amount);
  error OutOfBaseCollateralForExchangeAndTransfer(address thrower, uint balance, uint amount);

  // Token transfers
  error BaseTransferFailed(address thrower, address from, address to, uint amount);
  error QuoteTransferFailed(address thrower, address from, address to, uint amount);

  // Access
  error BoardMustBeSettled(address thrower, IOptionToken.PositionWithOwner position);
  error OnlyOptionMarket(address thrower, address caller, address optionMarket);
}

File 28 of 43 : ICurve.sol
//SPDX-License-Identifier: ISC
pragma solidity ^0.8.9;

interface ICurve {
  function exchange_with_best_rate(
    address _from,
    address _to,
    uint _amount,
    uint _expected,
    address _receiver
  ) external payable returns (uint amountOut);

  function exchange_underlying(
    int128 _from,
    int128 _to,
    uint _amount,
    uint _expected
  ) external payable returns (uint amountOut);

  function get_best_rate(
    address _from,
    address _to,
    uint _amount
  ) external view returns (address pool, uint amountOut);
}

File 29 of 43 : IGWAVOracle.sol
//SPDX-License-Identifier:ISC

pragma solidity 0.8.9;

// For full documentation refer to @lyrafinance/protocol/contracts/periphery/GWAVOracle.sol";

interface IGWAVOracle {
  function ivGWAV(uint boardId, uint secondsAgo) external view returns (uint);

  function skewGWAV(uint strikeId, uint secondsAgo) external view returns (uint);

  function volGWAV(uint strikeId, uint secondsAgo) external view returns (uint);

  function deltaGWAV(uint strikeId, uint secondsAgo) external view returns (int callDelta);

  function vegaGWAV(uint strikeId, uint secondsAgo) external view returns (uint vega);

  function optionPriceGWAV(uint strikeId, uint secondsAgo) external view returns (uint callPrice, uint putPrice);

}

File 30 of 43 : ILyraRegistry.sol
//SPDX-License-Identifier:ISC

pragma solidity 0.8.9;

import "openzeppelin-contracts-4.4.1/token/ERC20/IERC20.sol";

// For full documentation refer to @lyrafinance/protocol/contracts/periphery/LyraRegistry.sol";
/// @dev inputs/returns that contain Lyra contracts replaced with addresses (as opposed to LyraRegistry.sol)
///      so that interacting contracts are not required to import Lyra contracts 
interface ILyraRegistry {
  struct OptionMarketAddresses {
    address liquidityPool;
    address liquidityToken;
    address greekCache;
    address optionMarket;
    address optionMarketPricer;
    address optionToken;
    address poolHedger;
    address shortCollateral;
    address gwavOracle;
    IERC20 quoteAsset;
    IERC20 baseAsset;
  }

  function optionMarkets() external view returns (address[] memory);

  function marketAddress(address market) external view returns (OptionMarketAddresses memory);

  function globalAddresses(bytes32 name) external view returns (address);

  function getMarketAddresses(address optionMarket) external view returns (OptionMarketAddresses memory);

  function getGlobalAddress(bytes32 contractName) external view returns (address globalContract);

  event GlobalAddressUpdated(bytes32 indexed name, address addr);

  event MarketUpdated(address indexed optionMarket, OptionMarketAddresses market);

  event MarketRemoved(address indexed market);

  error RemovingInvalidMarket(address thrower, address market);

  error NonExistentMarket(address optionMarket);

  error NonExistentGlobalContract(bytes32 contractName);
}

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

pragma solidity ^0.8.9;

/**
 * @title SignedDecimalMath
 * @author Lyra
 * @dev Modified synthetix SafeSignedDecimalMath to include internal arithmetic underflow/overflow.
 * @dev https://docs.synthetix.io/contracts/source/libraries/safedecimalmath
 */
library SignedDecimalMath {
  /* 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;
  }

  /**
   * @dev Rounds an input with an extra zero of precision, returning the result without the extra zero.
   * Half increments round away from zero; positive numbers at a half increment are rounded up,
   * while negative such numbers are rounded down. This behaviour is designed to be consistent with the
   * unsigned version of this library (SafeDecimalMath).
   */
  function _roundDividingByTen(int valueTimesTen) private pure returns (int) {
    int increment;
    if (valueTimesTen % 10 >= 5) {
      increment = 10;
    } else if (valueTimesTen % 10 <= -5) {
      increment = -10;
    }
    return (valueTimesTen + increment) / 10;
  }

  /**
   * @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 * 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 * y) / (precisionUnit / 10);
    return _roundDividingByTen(quotientTimesTen);
  }

  /**
   * @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 * UNIT) / 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 * (precisionUnit * 10)) / y;
    return _roundDividingByTen(resultTimesTen);
  }

  /**
   * @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 * 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);
    return _roundDividingByTen(quotientTimesTen);
  }
}

File 32 of 43 : FixedPointMathLib.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.9;

// Slightly modified version of:
// - https://github.com/recmo/experiment-solexp/blob/605738f3ed72d6c67a414e992be58262fbc9bb80/src/FixedPointMathLib.sol
library FixedPointMathLib {
  /// @dev Computes ln(x) for a 1e27 fixed point. Loses 9 last significant digits of precision.
  function lnPrecise(int x) internal pure returns (int r) {
    return ln(x / 1e9) * 1e9;
  }

  /// @dev Computes e ^ x for a 1e27 fixed point. Loses 9 last significant digits of precision.
  function expPrecise(int x) internal pure returns (uint r) {
    return exp(x / 1e9) * 1e9;
  }

  // Computes ln(x) in 1e18 fixed point.
  // Reverts if x is negative or zero.
  // Consumes 670 gas.
  function ln(int x) internal pure returns (int r) {
    unchecked {
      if (x < 1) {
        if (x < 0) revert LnNegativeUndefined();
        revert Overflow();
      }

      // We want to convert x from 10**18 fixed point to 2**96 fixed point.
      // We do this by multiplying by 2**96 / 10**18.
      // But since ln(x * C) = ln(x) + ln(C), we can simply do nothing here
      // and add ln(2**96 / 10**18) at the end.

      // Reduce range of x to (1, 2) * 2**96
      // ln(2^k * x) = k * ln(2) + ln(x)
      // Note: inlining ilog2 saves 8 gas.
      int k = int(ilog2(uint(x))) - 96;
      x <<= uint(159 - k);
      x = int(uint(x) >> 159);

      // Evaluate using a (8, 8)-term rational approximation
      // p is made monic, we will multiply by a scale factor later
      int p = x + 3273285459638523848632254066296;
      p = ((p * x) >> 96) + 24828157081833163892658089445524;
      p = ((p * x) >> 96) + 43456485725739037958740375743393;
      p = ((p * x) >> 96) - 11111509109440967052023855526967;
      p = ((p * x) >> 96) - 45023709667254063763336534515857;
      p = ((p * x) >> 96) - 14706773417378608786704636184526;
      p = p * x - (795164235651350426258249787498 << 96);
      //emit log_named_int("p", p);
      // We leave p in 2**192 basis so we don't need to scale it back up for the division.
      // q is monic by convention
      int q = x + 5573035233440673466300451813936;
      q = ((q * x) >> 96) + 71694874799317883764090561454958;
      q = ((q * x) >> 96) + 283447036172924575727196451306956;
      q = ((q * x) >> 96) + 401686690394027663651624208769553;
      q = ((q * x) >> 96) + 204048457590392012362485061816622;
      q = ((q * x) >> 96) + 31853899698501571402653359427138;
      q = ((q * x) >> 96) + 909429971244387300277376558375;
      assembly {
        // Div in assembly because solidity adds a zero check despite the `unchecked`.
        // The q polynomial is known not to have zeros in the domain. (All roots are complex)
        // No scaling required because p is already 2**96 too large.
        r := sdiv(p, q)
      }
      // r is in the range (0, 0.125) * 2**96

      // Finalization, we need to
      // * multiply by the scale factor s = 5.549…
      // * add ln(2**96 / 10**18)
      // * add k * ln(2)
      // * multiply by 10**18 / 2**96 = 5**18 >> 78
      // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
      r *= 1677202110996718588342820967067443963516166;
      // add ln(2) * k * 5e18 * 2**192
      r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
      // add ln(2**96 / 10**18) * 5e18 * 2**192
      r += 600920179829731861736702779321621459595472258049074101567377883020018308;
      // base conversion: mul 2**18 / 2**192
      r >>= 174;
    }
  }

  // Integer log2
  // @returns floor(log2(x)) if x is nonzero, otherwise 0. This is the same
  //          as the location of the highest set bit.
  // Consumes 232 gas. This could have been an 3 gas EVM opcode though.
  function ilog2(uint x) internal pure returns (uint r) {
    assembly {
      r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
      r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
      r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
      r := or(r, shl(4, lt(0xffff, shr(r, x))))
      r := or(r, shl(3, lt(0xff, shr(r, x))))
      r := or(r, shl(2, lt(0xf, shr(r, x))))
      r := or(r, shl(1, lt(0x3, shr(r, x))))
      r := or(r, lt(0x1, shr(r, x)))
    }
  }

  // Computes e^x in 1e18 fixed point.
  function exp(int x) internal pure returns (uint r) {
    unchecked {
      // Input x is in fixed point format, with scale factor 1/1e18.

      // When the result is < 0.5 we return zero. This happens when
      // x <= floor(log(0.5e18) * 1e18) ~ -42e18
      if (x <= -42139678854452767551) {
        return 0;
      }

      // When the result is > (2**255 - 1) / 1e18 we can not represent it
      // as an int256. This happens when x >= floor(log((2**255 -1) / 1e18) * 1e18) ~ 135.
      if (x >= 135305999368893231589) revert ExpOverflow();

      // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
      // for more intermediate precision and a binary basis. This base conversion
      // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
      x = (x << 78) / 5**18;

      // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers of two
      // such that exp(x) = exp(x') * 2**k, where k is an integer.
      // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
      int k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
      x = x - k * 54916777467707473351141471128;
      // k is in the range [-61, 195].

      // Evaluate using a (6, 7)-term rational approximation
      // p is made monic, we will multiply by a scale factor later
      int p = x + 2772001395605857295435445496992;
      p = ((p * x) >> 96) + 44335888930127919016834873520032;
      p = ((p * x) >> 96) + 398888492587501845352592340339721;
      p = ((p * x) >> 96) + 1993839819670624470859228494792842;
      p = p * x + (4385272521454847904632057985693276 << 96);
      // We leave p in 2**192 basis so we don't need to scale it back up for the division.
      // Evaluate using using Knuth's scheme from p. 491.
      int z = x + 750530180792738023273180420736;
      z = ((z * x) >> 96) + 32788456221302202726307501949080;
      int w = x - 2218138959503481824038194425854;
      w = ((w * z) >> 96) + 892943633302991980437332862907700;
      int q = z + w - 78174809823045304726920794422040;
      q = ((q * w) >> 96) + 4203224763890128580604056984195872;
      assembly {
        // Div in assembly because solidity adds a zero check despite the `unchecked`.
        // The q polynomial is known not to have zeros in the domain. (All roots are complex)
        // No scaling required because p is already 2**96 too large.
        r := sdiv(p, q)
      }
      // r should be in the range (0.09, 0.25) * 2**96.

      // We now need to multiply r by
      //  * the scale factor s = ~6.031367120...,
      //  * the 2**k factor from the range reduction, and
      //  * the 1e18 / 2**96 factor for base converison.
      // We do all of this at once, with an intermediate result in 2**213 basis
      // so the final right shift is always by a positive amount.
      r = (uint(r) * 3822833074963236453042738258902158003155416615667) >> uint(195 - k);
    }
  }

  error Overflow();
  error ExpOverflow();
  error LnNegativeUndefined();
}

File 33 of 43 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^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 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) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 34 of 43 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

File 35 of 43 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.9;

import "./AbstractOwned.sol";

/**
 * @title Owned
 * @author Synthetix
 * @dev Slightly modified Synthetix owned contract, so that first owner is msg.sender
 * @dev https://docs.synthetix.io/contracts/source/contracts/owned
 */
contract Owned is AbstractOwned {
  constructor() {
    owner = msg.sender;
    emit OwnerChanged(address(0), msg.sender);
  }
}

File 37 of 43 : IFeeCounter.sol
//SPDX-License-Identifier: ISC
pragma solidity ^0.8.9;

interface IFeeCounter {
  function trackFee(
    address market,
    address trader,
    uint amount,
    uint totalCost,
    uint totalFee
  ) external;
}

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

pragma solidity ^0.8.9;

/**
 * @title Owned
 * @author Synthetix
 * @dev Synthetix owned contract without constructor and custom errors
 * @dev https://docs.synthetix.io/contracts/source/contracts/owned
 */
abstract contract AbstractOwned {
  address public owner;
  address public nominatedOwner;

  function nominateNewOwner(address _owner) external onlyOwner {
    nominatedOwner = _owner;
    emit OwnerNominated(_owner);
  }

  function acceptOwnership() external {
    if (msg.sender != nominatedOwner) {
      revert OnlyNominatedOwner(address(this), msg.sender, nominatedOwner);
    }
    emit OwnerChanged(owner, nominatedOwner);
    owner = nominatedOwner;
    nominatedOwner = address(0);
  }

  modifier onlyOwner() {
    _onlyOwner();
    _;
  }

  function _onlyOwner() private view {
    if (msg.sender != owner) {
      revert OnlyOwner(address(this), msg.sender, owner);
    }
  }

  event OwnerNominated(address newOwner);
  event OwnerChanged(address oldOwner, address newOwner);

  ////////////
  // Errors //
  ////////////
  error OnlyOwner(address thrower, address caller, address owner);
  error OnlyNominatedOwner(address thrower, address caller, address nominatedOwner);
}

File 39 of 43 : ICrossDomainMessenger.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;
pragma experimental ABIEncoderV2;

/**
 * @title ICrossDomainMessenger
 */
interface ICrossDomainMessenger {
    /**********
     * Events *
     **********/

    event SentMessage(bytes message);
    event RelayedMessage(bytes32 msgHash);
    event FailedRelayedMessage(bytes32 msgHash);

    event TransactionEnqueued(
        address indexed _l1TxOrigin,
        address indexed _target,
        uint256 _gasLimit,
        bytes _data,
        uint256 indexed _queueIndex,
        uint256 _timestamp
    );

    event QueueBatchAppended(
        uint256 _startingQueueIndex,
        uint256 _numQueueElements,
        uint256 _totalElements
    );

    event SequencerBatchAppended(
        uint256 _startingQueueIndex,
        uint256 _numQueueElements,
        uint256 _totalElements
    );

    event TransactionBatchAppended(
        uint256 indexed _batchIndex,
        bytes32 _batchRoot,
        uint256 _batchSize,
        uint256 _prevTotalElements,
        bytes _extraData
    );

    /********************
     * View Functions *
     ********************/

    function receivedMessages(bytes32 messageHash) external view returns (bool);

    function sentMessages(bytes32 messageHash) external view returns (bool);

    function targetMessengerAddress() external view returns (address);

    function messageNonce() external view returns (uint256);

    function xDomainMessageSender() external view returns (address);

    /********************
     * Public Functions *
     ********************/

    /**
     * Sets the target messenger address.
     * @param _targetMessengerAddress New messenger address.
     */
    function setTargetMessengerAddress(address _targetMessengerAddress)
        external;

    /**
     * Sends a cross domain message to the target messenger.
     * @param _target Target contract address.
     * @param _message Message to send to the target.
     * @param _gasLimit Gas limit for the provided message.
     */
    function sendMessage(
        address _target,
        bytes memory _message,
        uint32 _gasLimit
    ) external;
}

File 40 of 43 : IUniswapSwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.4;
pragma abicoder v2;

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface IUniswapSwapRouter {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params)
        external
        payable
        returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params)
        external
        payable
        returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params)
        external
        payable
        returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params)
        external
        payable
        returns (uint256 amountIn);
}

File 41 of 43 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

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

File 42 of 43 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(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");

        (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");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 43 of 43 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "@lyrafinance/protocol/contracts/libraries/BlackScholes.sol": {
      "BlackScholes": "0xe97831964bf41c564edf6629f818ed36c85fd520"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_wantTokenL2","type":"address"},{"internalType":"address","name":"_positionHandlerL1","type":"address"},{"internalType":"address","name":"_lyraOptionMarket","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_governance","type":"address"},{"internalType":"address","name":"_socketRegistry","type":"address"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"thrower","type":"address"},{"internalType":"uint256","name":"baseExpected","type":"uint256"},{"internalType":"uint256","name":"baseReceived","type":"uint256"}],"name":"ExchangerBaseReceivedTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"thrower","type":"address"},{"internalType":"uint256","name":"quoteExpected","type":"uint256"},{"internalType":"uint256","name":"quoteReceived","type":"uint256"}],"name":"ExchangerQuoteReceivedTooLow","type":"error"},{"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":"oldGovernance","type":"address"},{"indexed":true,"internalType":"address","name":"newGovernance","type":"address"}],"name":"UpdatedGovernance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldKeeper","type":"address"},{"indexed":true,"internalType":"address","name":"newKeeper","type":"address"}],"name":"UpdatedKeeper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldSlippage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSlippage","type":"uint256"}],"name":"UpdatedSlippage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRegistry","type":"address"},{"indexed":true,"internalType":"address","name":"newRegistry","type":"address"}],"name":"UpdatedSocketRegistry","type":"event"},{"inputs":[],"name":"L2CrossDomainMessenger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LYRA_ETH_OPTIONS_MARKET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NORMALIZATION_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_FEE","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_positionInWantToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"toSettle","type":"bool"}],"name":"closePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentPosition","outputs":[{"internalType":"uint256","name":"strikeId","type":"uint256"},{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"enum LyraAdapter.OptionType","name":"optionType","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"optionsPurchased","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curveSwap","outputs":[{"internalType":"contract ICurve","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCounter","outputs":[{"internalType":"contract BasicFeeCounter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"greekCache","outputs":[{"internalType":"contract IOptionGreekCache","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gwavOracle","outputs":[{"internalType":"contract IGWAVOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isCurrentPositionActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityPool","outputs":[{"internalType":"contract ILiquidityPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lyraOptionMarket","outputs":[{"internalType":"contract IOptionMarket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lyraRegistry","outputs":[{"internalType":"contract ILyraRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lyraSynthetixAdapter","outputs":[{"internalType":"contract ISynthetixAdapter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messageSender","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"strikeId","type":"uint256"},{"internalType":"bool","name":"isCall","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"updateExistingPosition","type":"bool"}],"name":"openPosition","outputs":[{"components":[{"internalType":"uint256","name":"positionId","type":"uint256"},{"internalType":"uint256","name":"totalCost","type":"uint256"},{"internalType":"uint256","name":"totalFee","type":"uint256"}],"internalType":"struct LyraAdapter.TradeResult","name":"tradeResult","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"optimismMessenger","outputs":[{"internalType":"contract ICrossDomainMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionMarket","outputs":[{"internalType":"contract IOptionMarket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionPricer","outputs":[{"internalType":"contract IOptionMarketPricer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionToken","outputs":[{"internalType":"contract IOptionToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGovernance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionHandlerL1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionInWantToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteAsset","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sUSD","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sUSDAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGovernance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_keeper","type":"address"}],"name":"setKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lyraRegistry","type":"address"},{"internalType":"address","name":"_optionMarket","type":"address"},{"internalType":"address","name":"_curveSwap","type":"address"},{"internalType":"address","name":"_feeCounter","type":"address"}],"name":"setLyraAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"setSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_socketRegistry","type":"address"}],"name":"setSocketRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shortCollateral","outputs":[{"internalType":"contract IShortCollateral","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"slippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"socketRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapRouter","outputs":[{"internalType":"contract IUniswapSwapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateDelegateApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wantTokenL2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address","name":"_socketRegistry","type":"address"},{"internalType":"bytes","name":"socketData","type":"bytes"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60a0604052601480547342000000000000000000000000000000000000076001600160a01b031991821681179092556015805490911690911790553480156200004757600080fd5b5060405162005a1a38038062005a1a8339810160408190526200006a9162000a03565b62000075336200039d565b604051623f970360e91b81527029aca72a2422aa24ac2fa0a220a82a22a960791b600482015273f5a0442d4753ca1ea36427ec071aa5e786da591690637f2e06009060240160206040518083038186803b158015620000d357600080fd5b505afa158015620000e8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200010e919062000aa2565b6001600160a01b03908116608052601780546001600160a01b03199081168a841617909155601880548216898416179055601a805482168784161790556019805482168584161790556016839055601b80548216868416179055600e805490911691871691909117905560175460405163095ea7b360e01b815273e592427a0aece92de3edee1f18e0157c05861564600482015260001960248201526001600160a01b039091169063095ea7b390604401602060405180830381600087803b158015620001da57600080fd5b505af1158015620001ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000215919062000ac9565b5060405163095ea7b360e01b815273e592427a0aece92de3edee1f18e0157c0586156460048201526000196024820152738c6f28f2f1a3c87f0f938b96d27520d9751ec8d99063095ea7b390604401602060405180830381600087803b1580156200027f57600080fd5b505af115801562000294573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002ba919062000ac9565b506000604051620002cb90620009ca565b604051809103906000f080158015620002e8573d6000803e3d6000fd5b5060405163e6a1292b60e01b8152306004820152600160248201529091506001600160a01b0382169063e6a1292b90604401600060405180830381600087803b1580156200033557600080fd5b505af11580156200034a573d6000803e3d6000fd5b505050506200038f73f5a0442d4753ca1ea36427ec071aa5e786da59168773a5407eae9ba41422680e2e00537571bcc53efbfd84620003ed60201b620013321760201c565b505050505050505062000c18565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000546001600160a01b031633146200044c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640160405180910390fd5b600a546001600160a01b031615620004ec57600a5460035460405163095ea7b360e01b81526001600160a01b0391821660048201526000602482015291169063095ea7b390604401602060405180830381600087803b158015620004af57600080fd5b505af1158015620004c4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004ea919062000ac9565b505b600b546001600160a01b0316156200058c57600b5460035460405163095ea7b360e01b81526001600160a01b0391821660048201526000602482015291169063095ea7b390604401602060405180830381600087803b1580156200054f57600080fd5b505af115801562000564573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200058a919062000ac9565b505b600380546001600160a01b038581166001600160a01b031992831617909255600180549287169290911682179055604051623f970360e91b81527029aca72a2422aa24ac2fa0a220a82a22a960791b6004820152637f2e06009060240160206040518083038186803b1580156200060257600080fd5b505afa15801562000617573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200063d919062000aa2565b600280546001600160a01b0319166001600160a01b03929092169190911790556200066762000890565b600c80546001600160a01b038085166001600160a01b031992831617909255600d8054848416921691909117905560025460408051633535c97b60e01b815290519190921691633535c97b916004808301926020929190829003018186803b158015620006d357600080fd5b505afa158015620006e8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200070e919062000aa2565b60025460405163447fbc6360e01b81526001600160a01b03918216600482015291169063447fbc6390602401600060405180830381600087803b1580156200075557600080fd5b505af11580156200076a573d6000803e3d6000fd5b5050600a5460035460405163095ea7b360e01b81526001600160a01b03918216600482015260001960248201529116925063095ea7b39150604401602060405180830381600087803b158015620007c057600080fd5b505af1158015620007d5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620007fb919062000ac9565b50600b5460035460405163095ea7b360e01b81526001600160a01b039182166004820152600019602482015291169063095ea7b390604401602060405180830381600087803b1580156200084e57600080fd5b505af115801562000863573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000889919062000ac9565b5050505050565b60015460035460405163c4158a5160e01b81526001600160a01b039182166004820152600092919091169063c4158a51906024016101606040518083038186803b158015620008de57600080fd5b505afa158015620008f3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000919919062000b25565b8051600580546001600160a01b03199081166001600160a01b03938416179091556040830151600980548316918416919091179055608083015160088054831691841691909117905560a083015160048054831691841691909117905560e0830151600680548316918416919091179055610100830151600780548316918416919091179055610120830151600a8054831691841691909117905561014090920151600b8054909316911617905550565b61057280620054a883390190565b6001600160a01b0381168114620009ee57600080fd5b50565b8051620009fe81620009d8565b919050565b600080600080600080600060e0888a03121562000a1f57600080fd5b875162000a2c81620009d8565b602089015190975062000a3f81620009d8565b604089015190965062000a5281620009d8565b606089015190955062000a6581620009d8565b608089015190945062000a7881620009d8565b60a089015190935062000a8b81620009d8565b8092505060c0880151905092959891949750929550565b60006020828403121562000ab557600080fd5b815162000ac281620009d8565b9392505050565b60006020828403121562000adc57600080fd5b8151801515811462000ac257600080fd5b60405161016081016001600160401b038111828210171562000b1f57634e487b7160e01b600052604160045260246000fd5b60405290565b6000610160828403121562000b3957600080fd5b62000b4362000aed565b62000b4e83620009f1565b815262000b5e60208401620009f1565b602082015262000b7160408401620009f1565b604082015262000b8460608401620009f1565b606082015262000b9760808401620009f1565b608082015262000baa60a08401620009f1565b60a082015262000bbd60c08401620009f1565b60c082015262000bd060e08401620009f1565b60e082015261010062000be5818501620009f1565b9082015261012062000bf9848201620009f1565b9082015261014062000c0d848201620009f1565b908201529392505050565b60805161486d62000c3b600039600081816102ed0152612299015261486d6000f3fe6080604052600436106102ad5760003560e01c8063999d5c6911610165578063d0e30db0116100cc578063ee9e0e6a11610085578063ee9e0e6a14610844578063f0fa55a914610864578063f2fde38b14610884578063f302e4bd146108a4578063f39c38a0146108c0578063fd967f47146108e0578063fdf262b7146108f657600080fd5b8063d0e30db01461077f578063d534d63614610794578063d67bdd25146107cf578063d8fca1ac146107e4578063db23d81814610804578063ea32afae1461082457600080fd5b8063b5ca94a01161011e578063b5ca94a0146106ea578063bb4a9f881461070a578063ca41d8df1461072a578063cae1c5821461073f578063cdf456e11461075f578063d0592736146105d457600080fd5b8063999d5c6914610616578063a167722814610640578063a65be9ba1461066a578063ab033ea91461068a578063aced1661146106aa578063b3d32010146106ca57600080fd5b806362b2b5f011610214578063748747e6116101cd578063748747e61461053f578063870dcaeb1461055f5780638b860e651461057f5780638da5cb5b1461059f5780638dc7d733146105b45780639324cac7146105d457806394f6abfc146105f657600080fd5b806362b2b5f01461047a578063665a11ca1461049a5780636671b6da146104ba578063715018a6146104da578063735de9f7146104ef57806373d9fffb1461051757600080fd5b806338b740541161026657806338b74054146103bc5780633e032a3b146103dc5780635307b3f21461040057806357949815146104255780635aa6e67514610445578063619a84cc1461046557600080fd5b806301681a62146102b9578063029308f8146102db578063238efcbc14610325578063242056971461033a57806328aa5b901461035a5780632bab754b1461039c57600080fd5b366102b457005b600080fd5b3480156102c557600080fd5b506102d96102d4366004613a21565b610916565b005b3480156102e757600080fd5b5061030f7f000000000000000000000000000000000000000000000000000000000000000081565b60405161031c9190613a45565b60405180910390f35b34801561033157600080fd5b506102d9610a42565b34801561034657600080fd5b5060155461030f906001600160a01b031681565b34801561036657600080fd5b5061037a610375366004613a67565b610af8565b604080518251815260208084015190820152918101519082015260600161031c565b3480156103a857600080fd5b5060045461030f906001600160a01b031681565b3480156103c857600080fd5b5060095461030f906001600160a01b031681565b3480156103e857600080fd5b506103f260165481565b60405190815260200161031c565b34801561040c57600080fd5b50610415610b7a565b604051901515815260200161031c565b34801561043157600080fd5b50600d5461030f906001600160a01b031681565b34801561045157600080fd5b50601b5461030f906001600160a01b031681565b34801561047157600080fd5b506102d9610b89565b34801561048657600080fd5b506102d9610495366004613ab1565b610ca3565b3480156104a657600080fd5b5060055461030f906001600160a01b031681565b3480156104c657600080fd5b506102d96104d5366004613b39565b610fb8565b3480156104e657600080fd5b506102d9611029565b3480156104fb57600080fd5b5061030f73e592427a0aece92de3edee1f18e0157c0586156481565b34801561052357600080fd5b5061030f731d42a98848e022908069c2c545ae44cc78509bc881565b34801561054b57600080fd5b506102d961055a366004613a21565b611064565b34801561056b57600080fd5b5060065461030f906001600160a01b031681565b34801561058b57600080fd5b5060145461030f906001600160a01b031681565b3480156105ab57600080fd5b5061030f6110ea565b3480156105c057600080fd5b5060185461030f906001600160a01b031681565b3480156105e057600080fd5b5061030f60008051602061481883398151915281565b34801561060257600080fd5b506102d9610611366004613a21565b6110f9565b34801561062257600080fd5b5061062b61117f565b6040805192835260208301919091520161031c565b34801561064c57600080fd5b50610656610bb881565b60405162ffffff909116815260200161031c565b34801561067657600080fd5b5060075461030f906001600160a01b031681565b34801561069657600080fd5b506102d96106a5366004613a21565b6112e6565b3480156106b657600080fd5b50601a5461030f906001600160a01b031681565b3480156106d657600080fd5b5060175461030f906001600160a01b031681565b3480156106f657600080fd5b506102d9610705366004613b56565b611332565b34801561071657600080fd5b5060035461030f906001600160a01b031681565b34801561073657600080fd5b506103f2611791565b34801561074b57600080fd5b5060085461030f906001600160a01b031681565b34801561076b57600080fd5b50600b5461030f906001600160a01b031681565b34801561078b57600080fd5b506102d9611904565b3480156107a057600080fd5b50600f546010546011546012546013546107be94939260ff16919085565b60405161031c959493929190613bdb565b3480156107db57600080fd5b5061030f611a95565b3480156107f057600080fd5b50600c5461030f906001600160a01b031681565b34801561081057600080fd5b50600e5461030f906001600160a01b031681565b34801561083057600080fd5b5060195461030f906001600160a01b031681565b34801561085057600080fd5b5060015461030f906001600160a01b031681565b34801561087057600080fd5b506102d961087f366004613c0e565b611b12565b34801561089057600080fd5b506102d961089f366004613a21565b611b7d565b3480156108b057600080fd5b506103f2670de0b6b3a764000081565b3480156108cc57600080fd5b50601c5461030f906001600160a01b031681565b3480156108ec57600080fd5b506103f261271081565b34801561090257600080fd5b50600a5461030f906001600160a01b031681565b601b546001600160a01b031633146109495760405162461bcd60e51b815260040161094090613c27565b60405180910390fd5b6040516370a0823160e01b81526001600160a01b0382169063a9059cbb90339083906370a082319061097f903090600401613a45565b60206040518083038186803b15801561099757600080fd5b505afa1580156109ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109cf9190613c50565b6040518363ffffffff1660e01b81526004016109ec929190613c69565b602060405180830381600087803b158015610a0657600080fd5b505af1158015610a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3e9190613c8d565b5050565b601c546001600160a01b03163314610a955760405162461bcd60e51b81526020600482015260166024820152754e4f545f50454e44494e475f474f5645524e414e434560501b6044820152606401610940565b601c54601b546040516001600160a01b0392831692909116907fff0b32b909f3fb702fe6ac1f682adcca675b9dfaa03ad8f46b4b17c4058a93fc90600090a3601c54601b80546001600160a01b0319166001600160a01b03909216919091179055565b610b00613852565b6014546001600160a01b031633148015610b3457506018546001600160a01b0316610b29611a95565b6001600160a01b0316145b80610b495750601a546001600160a01b031633145b610b655760405162461bcd60e51b815260040161094090613caa565b610b7185858585611c1a565b95945050505050565b6000610b84611fde565b905090565b33610b926110ea565b6001600160a01b031614610bb85760405162461bcd60e51b815260040161094090613cd3565b600260009054906101000a90046001600160a01b03166001600160a01b0316633535c97b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610c0657600080fd5b505afa158015610c1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3e9190613d13565b60025460405163447fbc6360e01b81526001600160a01b039283169263447fbc6392610c6f92911690600401613a45565b600060405180830381600087803b158015610c8957600080fd5b505af1158015610c9d573d6000803e3d6000fd5b50505050565b6014546001600160a01b031633148015610cd757506018546001600160a01b0316610ccc611a95565b6001600160a01b0316145b80610cec5750601a546001600160a01b031633145b610d085760405162461bcd60e51b815260040161094090613caa565b6040516370a0823160e01b8152600090600080516020614818833981519152906370a0823190610d3c903090600401613a45565b60206040518083038186803b158015610d5457600080fd5b505afa158015610d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d8c9190613c50565b1115610e1f576040516370a0823160e01b8152610e1f90600090600080516020614818833981519152906370a0823190610dca903090600401613a45565b60206040518083038186803b158015610de257600080fd5b505afa158015610df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1a9190613c50565b612086565b6017546040516370a0823160e01b81526001600160a01b0390911690632e1a7d4d9082906370a0823190610e57903090600401613a45565b60206040518083038186803b158015610e6f57600080fd5b505afa158015610e83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ea79190613c50565b6040518263ffffffff1660e01b8152600401610ec591815260200190565b600060405180830381600087803b158015610edf57600080fd5b505af1158015610ef3573d6000803e3d6000fd5b5050505083471015610f3b5760405162461bcd60e51b81526020600482015260116024820152704e4f545f454e4f5547485f544f4b454e5360781b6044820152606401610940565b8315610c9d576019546001600160a01b03848116911614610f915760405162461bcd60e51b815260206004820152601060248201526f494e56414c49445f524547495354525960801b6044820152606401610940565b601754601954601854610c9d926001600160a01b03908116928116911687600187876124f0565b6014546001600160a01b031633148015610fec57506018546001600160a01b0316610fe1611a95565b6001600160a01b0316145b806110015750601a546001600160a01b031633145b61101d5760405162461bcd60e51b815260040161094090613caa565b611026816125a2565b50565b336110326110ea565b6001600160a01b0316146110585760405162461bcd60e51b815260040161094090613cd3565b6110626000612727565b565b601b546001600160a01b0316331461108e5760405162461bcd60e51b815260040161094090613c27565b601a546040516001600160a01b038084169216907f60ed9ffad04b70bf58c43b18d1f0e54642250116c1137ac1cc4831449124350890600090a3601a80546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b031690565b601b546001600160a01b031633146111235760405162461bcd60e51b815260040161094090613c27565b6019546040516001600160a01b038084169216907fd38cd7a28764cf6ebbaf7ee1ccf8f3b825cfbd5d1b1cd08af390afc71d309dcc90600090a3601980546001600160a01b0319166001600160a01b0392909216919091179055565b600080600061118c611791565b600254604051630fb75cc960e31b81529192506000916001600160a01b0390911690637dbae648906111d690731d42a98848e022908069c2c545ae44cc78509bc890600401613a45565b60206040518083038186803b1580156111ee57600080fd5b505afa158015611202573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112269190613c50565b6017546040516370a0823160e01b815291925047916001600160a01b03909116906370a082319061125b903090600401613a45565b60206040518083038186803b15801561127357600080fd5b505afa158015611287573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ab9190613c50565b826112be670de0b6b3a764000086613d46565b6112c89190613d65565b6112d29190613d87565b6112dc9190613d87565b9443945092505050565b601b546001600160a01b031633146113105760405162461bcd60e51b815260040161094090613c27565b601c80546001600160a01b0319166001600160a01b0392909216919091179055565b3361133b6110ea565b6001600160a01b0316146113615760405162461bcd60e51b815260040161094090613cd3565b600a546001600160a01b0316156113fd57600a5460035460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926113a992911690600090600401613c69565b602060405180830381600087803b1580156113c357600080fd5b505af11580156113d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113fb9190613c8d565b505b600b546001600160a01b03161561149957600b5460035460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261144592911690600090600401613c69565b602060405180830381600087803b15801561145f57600080fd5b505af1158015611473573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114979190613c8d565b505b600380546001600160a01b038581166001600160a01b031992831617909255600180549287169290911682179055604051623f970360e91b81527029aca72a2422aa24ac2fa0a220a82a22a960791b6004820152637f2e06009060240160206040518083038186803b15801561150e57600080fd5b505afa158015611522573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115469190613d13565b600280546001600160a01b0319166001600160a01b039290921691909117905561156e612777565b600c80546001600160a01b038085166001600160a01b031992831617909255600d8054848416921691909117905560025460408051633535c97b60e01b815290519190921691633535c97b916004808301926020929190829003018186803b1580156115d957600080fd5b505afa1580156115ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116119190613d13565b60025460405163447fbc6360e01b81526001600160a01b039283169263447fbc639261164292911690600401613a45565b600060405180830381600087803b15801561165c57600080fd5b505af1158015611670573d6000803e3d6000fd5b5050600a5460035460405163095ea7b360e01b81526001600160a01b03928316945063095ea7b393506116ad929091169060001990600401613c69565b602060405180830381600087803b1580156116c757600080fd5b505af11580156116db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116ff9190613c8d565b50600b5460035460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926117389291169060001990600401613c69565b602060405180830381600087803b15801561175257600080fd5b505af1158015611766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178a9190613c8d565b5050505050565b600061179b611fde565b15611883576000806117b3600f60000154603c6128b1565b60135491935091506000906117ed908260115460ff1660048111156117da576117da613ba7565b146117e557836117e7565b845b90612943565b6040516370a0823160e01b8152909150600080516020614818833981519152906370a0823190611821903090600401613a45565b60206040518083038186803b15801561183957600080fd5b505afa15801561184d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118719190613c50565b61187b9082613d87565b935050505090565b6040516370a0823160e01b8152600080516020614818833981519152906370a08231906118b4903090600401613a45565b60206040518083038186803b1580156118cc57600080fd5b505afa1580156118e0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b849190613c50565b601a546001600160a01b0316331461194c5760405162461bcd60e51b815260206004820152600b60248201526a27a7262cafa5a2a2a822a960a91b6044820152606401610940565b60004711806119d957506017546040516370a0823160e01b81526000916001600160a01b0316906370a0823190611987903090600401613a45565b60206040518083038186803b15801561199f57600080fd5b505afa1580156119b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d79190613c50565b115b6119f55760405162461bcd60e51b815260040161094090613d9f565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0476040518263ffffffff1660e01b81526004016000604051808303818588803b158015611a4557600080fd5b505af1158015611a59573d6000803e3d6000fd5b50506017546040516370a0823160e01b81526110629450600193506001600160a01b0390911691506370a0823190610dca903090600401613a45565b60155460408051636e296e4560e01b815290516000926001600160a01b031691636e296e45916004808301926020929190829003018186803b158015611ada57600080fd5b505afa158015611aee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b849190613d13565b601b546001600160a01b03163314611b3c5760405162461bcd60e51b815260040161094090613c27565b60165460408051918252602082018390527f282c65286b5ce3db38b8527661b948d366f1642008bb1f38bfe820cad1003a42910160405180910390a1601655565b33611b866110ea565b6001600160a01b031614611bac5760405162461bcd60e51b815260040161094090613cd3565b6001600160a01b038116611c115760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610940565b61102681612727565b611c22613852565b6000611c2c613873565b6040516370a0823160e01b8152600090600080516020614818833981519152906370a0823190611c60903090600401613a45565b60206040518083038186803b158015611c7857600080fd5b505afa158015611c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb09190613c50565b905060008111611cef5760405162461bcd60e51b815260206004820152600a6024820152694e4f5f42414c414e434560b01b6044820152606401610940565b600e5460405163095ea7b360e01b81526000805160206148188339815191529163095ea7b391611d2d916001600160a01b0316908590600401613c69565b602060405180830381600087803b158015611d4757600080fd5b505af1158015611d5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d7f9190613c8d565b508415611e2b57601154600f546010546040516370a0823160e01b815260ff9093169550611e249286908a90600080516020614818833981519152906370a0823190611dcf903090600401613a45565b60206040518083038186803b158015611de757600080fd5b505afa158015611dfb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1f9190613c50565b61296e565b9150611e81565b86611e37576001611e3a565b60005b9250611e7e88600085896000805160206148188339815191526001600160a01b03166370a08231306040518263ffffffff1660e01b8152600401611dcf9190613a45565b91505b611e8a826129de565b6040516370a0823160e01b8152909450600090600080516020614818833981519152906370a0823190611ec1903090600401613a45565b60206040518083038186803b158015611ed957600080fd5b505afa158015611eed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f119190613c50565b611f1b9083613dcd565b6040805160a0810182528b8152875160208201529192508101856004811115611f4657611f46613ba7565b815260200187611f57576000611f5b565b6012545b611f659084613d87565b815260200187611f76576000611f7a565b6013545b611f84908a613d87565b90528051600f908155602082015160105560408201516011805460ff19166001836004811115611fb657611fb6613ba7565b0217905550606082015181600301556080820151816004015590505050505050949350505050565b60408051600180825281830190925260009182919060208083019080368337019050509050600f600101548160008151811061201c5761201c613dfa565b602002602001018181525050600061203382612b59565b905060008160008151811061204a5761204a613dfa565b602002602001015190506001600581111561206757612067613ba7565b8160a00151600581111561207d5761207d613ba7565b14935050505090565b600081116120c75760405162461bcd60e51b815260206004820152600e60248201526d1253959053125117d05353d5539560921b6044820152606401610940565b6000826120e257600080516020614818833981519152612153565b306001600160a01b031663b3d320106040518163ffffffff1660e01b815260040160206040518083038186803b15801561211b57600080fd5b505afa15801561212f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121539190613d13565b90506000836121d257306001600160a01b031663b3d320106040518163ffffffff1660e01b815260040160206040518083038186803b15801561219557600080fd5b505afa1580156121a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121cd9190613d13565b6121e2565b6000805160206148188339815191525b905082826001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016122119190613a45565b60206040518083038186803b15801561222957600080fd5b505afa15801561223d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122619190613c50565b101561227f5760405162461bcd60e51b815260040161094090613d9f565b604051630fb75cc960e31b81526000906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690637dbae648906122e290731d42a98848e022908069c2c545ae44cc78509bc890600401613a45565b60206040518083038186803b1580156122fa57600080fd5b505afa15801561230e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123329190613c50565b905060008561235d578161234e670de0b6b3a764000087613d46565b6123589190613d65565b61237a565b670de0b6b3a76400006123708387613d46565b61237a9190613d65565b90506000604051806101000160405280866001600160a01b03168152602001856001600160a01b03168152602001610bb862ffffff168152602001306001600160a01b031681526020014281526020018781526020016127106016546127106123e39190613dcd565b6123ed9086613d46565b6123f79190613d65565b815260006020918201526040805163414bf38960e01b815283516001600160a01b03908116600483015292840151831660248201529083015162ffffff1660448201526060830151821660648201526080830151608482015260a083015160a482015260c083015160c482015260e083015190911660e482015290915073e592427a0aece92de3edee1f18e0157c058615649063414bf3899061010401602060405180830381600087803b1580156124ae57600080fd5b505af11580156124c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124e69190613c50565b5050505050505050565b6124fd8282858a89612dbe565b6000866001600160a01b031685848460405161251a929190613e10565b60006040518083038185875af1925050503d8060008114612557576040519150601f19603f3d011682016040523d82523d6000602084013e61255c565b606091505b50509050806124e65760405162461bcd60e51b815260206004820152601260248201527111905253115117d4d3d0d2d15517d0d0531360721b6044820152606401610940565b6125aa611fde565b6125eb5760405162461bcd60e51b81526020600482015260126024820152712727afa0a1aa24ab22afa827a9a4aa24a7a760711b6044820152606401610940565b600181151514156126a45760408051600180825281830190925260009160208083019080368337019050509050600f600101548160008151811061263157612631613dfa565b602090810291909101015260065460405163bc0d6c5760e01b81526001600160a01b039091169063bc0d6c579061266c908490600401613e20565b600060405180830381600087803b15801561268657600080fd5b505af115801561269a573d6000803e3d6000fd5b50505050506126d7565b600f546010546011546013546000936126c9939092909160ff9091169060001961296e565b90506126d481612e7f565b50505b506040805160a0810182526000808252602082018190526002928201839052606082018190526080909101819052600f81905560108190556011805460ff19169092179091556012819055601355565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60015460035460405163c4158a5160e01b81526000926001600160a01b039081169263c4158a51926127af9290911690600401613a45565b6101606040518083038186803b1580156127c857600080fd5b505afa1580156127dc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128009190613f8c565b8051600580546001600160a01b03199081166001600160a01b03938416179091556040830151600980548316918416919091179055608083015160088054831691841691909117905560a083015160048054831691841691909117905560e0830151600680548316918416919091179055610100830151600780548316918416919091179055610120830151600a8054831691841691909117905561014090920151600b8054909316911617905550565b60075460405163ca4ca66760e01b8152600481018490526024810183905260009182916001600160a01b039091169063ca4ca66790604401604080518083038186803b15801561290057600080fd5b505afa158015612914573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129389190614066565b915091509250929050565b60006129516012600a61416e565b61295b8385613d46565b6129659190613d65565b90505b92915050565b612976613873565b604051806101200160405280878152602001868152602001600381526020018560048111156129a7576129a7613ba7565b81526020018481526020016000815260200160008152602001838152602001306001600160a01b0316815250905095945050505050565b6129e6613852565b6003546000906001600160a01b03166237f2bc612a0285612ec3565b6040518263ffffffff1660e01b8152600401612a1e919061417a565b606060405180830381600087803b158015612a3857600080fd5b505af1158015612a4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a7091906141de565b6101008401519091506001600160a01b031615612b3357600d546003546101008501516001600160a01b039283169263a4ed5781921690612ab087612ec3565b60800151602086015160408088015190516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015260448401919091526064830152608482015260a401600060405180830381600087803b158015612b1a57600080fd5b505af1158015612b2e573d6000803e3d6000fd5b505050505b604080516060810182528251815260208084015190820152918101519082015292915050565b600480546040516323d3547760e01b81526060926000926001600160a01b0316916323d3547791612b8c91879101613e20565b60006040518083038186803b158015612ba457600080fd5b505afa158015612bb8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612be0919081019061425c565b80519091506000816001600160401b03811115612bff57612bff613de4565b604051908082528060200260200182016040528015612c3857816020015b612c256138d9565b815260200190600190039081612c1d5790505b50905060005b82811015612db5576040518060c00160405280858381518110612c6357612c63613dfa565b6020026020010151600001518152602001858381518110612c8657612c86613dfa565b6020026020010151602001518152602001858381518110612ca957612ca9613dfa565b6020026020010151604001516004811115612cc657612cc6613ba7565b6004811115612cd757612cd7613ba7565b6004811115612ce857612ce8613ba7565b8152602001858381518110612cff57612cff613dfa565b6020026020010151606001518152602001858381518110612d2257612d22613dfa565b6020026020010151608001518152602001858381518110612d4557612d45613dfa565b602002602001015160a001516005811115612d6257612d62613ba7565b6005811115612d7357612d73613ba7565b6005811115612d8457612d84613ba7565b815250828281518110612d9957612d99613dfa565b602002602001018190525080612dae90614358565b9050612c3e565b50949350505050565b612dc6613930565b612dd08686612f5f565b905083816020015114612e175760405162461bcd60e51b815260206004820152600f60248201526e1253959053125117d0d21052539251608a1b6044820152606401610940565b816001600160a01b031681600001516001600160a01b031614612e775760405162461bcd60e51b8152602060048201526018602482015277494e56414c49445f52454345495645525f4144445245535360401b6044820152606401610940565b505050505050565b612e87613852565b8151612e9290612f81565b158015612ea757508151612ea590613031565b155b15612eb557612968826130c9565b612968826130ee565b919050565b612ecb6139b6565b60405180610100016040528083600001518152602001836020015181526020018360400151815260200183606001516004811115612f0b57612f0b613ba7565b6004811115612f1c57612f1c613ba7565b6004811115612f2d57612f2d613ba7565b8152602001836080015181526020018360a0015181526020018360c0015181526020018360e001518152509050919050565b612f67613930565b612f748260048186614373565b8101906129659190614462565b600080612f8c613113565b60408051600180825281830190925291925060009190602080830190803683370190505090508381600081518110612fc657612fc6613dfa565b6020026020010181815250506000612fdd8261332b565b600081518110612fef57612fef613dfa565b602002602001015190508260600151601260ff16600a61300f919061416e565b613019919061451b565b811380610b7157508260600151811295945050505050565b60008061303c613113565b6040805160018082528183019092529192506000919060208083019080368337019050509050838160008151811061307657613076613dfa565b602002602001018181525050600061308d82613486565b60008151811061309f5761309f613dfa565b6020026020010151905082608001514282602001516130be9190613dcd565b111595945050505050565b6130d1613852565b6003546000906001600160a01b03166392b4632c612a0285612ec3565b6130f6613852565b6003546000906001600160a01b031663e4e83e3d612a0285612ec3565b61314c6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6008546040805163da53763160e01b815290516000926001600160a01b03169163da53763191600480830192610120929190829003018186803b15801561319257600080fd5b505afa1580156131a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ca91906145d3565b90506000600860009054906101000a90046001600160a01b03166001600160a01b031663ffae4b366040518163ffffffff1660e01b81526004016101806040518083038186803b15801561321d57600080fd5b505afa158015613231573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061325591906145f0565b90506040518060c001604052808360e0015181526020018361010001518152602001600960009054906101000a90046001600160a01b03166001600160a01b031663f369f2ac6040518163ffffffff1660e01b81526004016101406040518083038186803b1580156132c657600080fd5b505afa1580156132da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132fe9190614687565b61012001518152602001826000015181526020018260400151815260200182602001518152509250505090565b8051606090806001600160401b0381111561334857613348613de4565b604051908082528060200260200182016040528015613371578160200160208202803683370190505b50915060005b8181101561347f5760006133a385838151811061339657613396613dfa565b602002602001015161362b565b60408051634698f9d560e11b81528251600482015260208301516024820152908201516044820152606082015160648201526080820151608482015290915073e97831964bf41c564edf6629f818ed36c85fd52090638d31f3aa9060a401604080518083038186803b15801561341857600080fd5b505af415801561342c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134509190614066565b5084838151811061346357613463613dfa565b60209081029190910101525061347881614358565b9050613377565b5050919050565b8051606090806001600160401b038111156134a3576134a3613de4565b60405190808252806020026020018201604052801561350657816020015b6134f36040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816134c15790505b50915060005b8181101561347f57600354845160009182916001600160a01b039091169063c4c4a0d09088908690811061354257613542613dfa565b60200260200101516040518263ffffffff1660e01b815260040161356891815260200190565b60006040518083038186803b15801561358057600080fd5b505afa158015613594573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526135bc919081019061470e565b915091506040518060a0016040528083600001518152602001826020015181526020018360200151815260200183604001518152602001826040015181525085848151811061360d5761360d613dfa565b602002602001018190525050508061362490614358565b905061350c565b61365d6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b600354604051630c4c4a0d60e41b81526004810184905260009182916001600160a01b039091169063c4c4a0d09060240160006040518083038186803b1580156136a657600080fd5b505afa1580156136ba573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136e2919081019061470e565b915091506040518060a001604052804283602001516137019190613dcd565b81526020016137218460400151846040015161294390919063ffffffff16565b8152600254600354604051630fb75cc960e31b81526020909301926001600160a01b0392831692637dbae6489261375d92911690600401613a45565b60206040518083038186803b15801561377557600080fd5b505afa158015613789573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137ad9190613c50565b815260200183602001518152602001600960009054906101000a90046001600160a01b03166001600160a01b031663f369f2ac6040518163ffffffff1660e01b81526004016101406040518083038186803b15801561380b57600080fd5b505afa15801561381f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138439190614687565b61012001519052949350505050565b60405180606001604052806000815260200160008152602001600081525090565b604051806101200160405280600081526020016000815260200160008152602001600060048111156138a7576138a7613ba7565b81526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681525090565b6040518060c0016040528060008152602001600081526020016000600481111561390557613905613ba7565b815260200160008152602001600081526020016000600581111561392b5761392b613ba7565b905290565b6040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200161398d6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001606081525090565b815260408051608081018252600080825260208281018290529282015260608082015291015290565b604051806101000160405280600081526020016000815260200160008152602001600060048111156139ea576139ea613ba7565b8152602001600081526020016000815260200160008152602001600081525090565b6001600160a01b038116811461102657600080fd5b600060208284031215613a3357600080fd5b8135613a3e81613a0c565b9392505050565b6001600160a01b0391909116815260200190565b801515811461102657600080fd5b60008060008060808587031215613a7d57600080fd5b843593506020850135613a8f81613a59565b9250604085013591506060850135613aa681613a59565b939692955090935050565b60008060008060608587031215613ac757600080fd5b843593506020850135613ad981613a0c565b925060408501356001600160401b0380821115613af557600080fd5b818701915087601f830112613b0957600080fd5b813581811115613b1857600080fd5b886020828501011115613b2a57600080fd5b95989497505060200194505050565b600060208284031215613b4b57600080fd5b8135613a3e81613a59565b60008060008060808587031215613b6c57600080fd5b8435613b7781613a0c565b93506020850135613b8781613a0c565b92506040850135613b9781613a0c565b91506060850135613aa681613a0c565b634e487b7160e01b600052602160045260246000fd5b6005811061102657634e487b7160e01b600052602160045260246000fd5b8581526020810185905260a08101613bf285613bbd565b8460408301528360608301528260808301529695505050505050565b600060208284031215613c2057600080fd5b5035919050565b6020808252600f908201526e4f4e4c595f474f5645524e414e434560881b604082015260600190565b600060208284031215613c6257600080fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b8051612ebe81613a59565b600060208284031215613c9f57600080fd5b8151613a3e81613a59565b6020808252600f908201526e13d3931657d055551213d492569151608a1b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b8051612ebe81613a0c565b600060208284031215613d2557600080fd5b8151613a3e81613a0c565b634e487b7160e01b600052601160045260246000fd5b6000816000190483118215151615613d6057613d60613d30565b500290565b600082613d8257634e487b7160e01b600052601260045260246000fd5b500490565b60008219821115613d9a57613d9a613d30565b500190565b602080825260149082015273494e53554646494349454e545f42414c414e434560601b604082015260600190565b600082821015613ddf57613ddf613d30565b500390565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8183823760009101908152919050565b6020808252825182820181905260009190848201906040850190845b81811015613e5857835183529284019291840191600101613e3c565b50909695505050505050565b60405161016081016001600160401b0381118282101715613e8757613e87613de4565b60405290565b60405160c081016001600160401b0381118282101715613e8757613e87613de4565b604051608081016001600160401b0381118282101715613e8757613e87613de4565b60405160a081016001600160401b0381118282101715613e8757613e87613de4565b60405161012081016001600160401b0381118282101715613e8757613e87613de4565b60405161018081016001600160401b0381118282101715613e8757613e87613de4565b60405161014081016001600160401b0381118282101715613e8757613e87613de4565b604051601f8201601f191681016001600160401b0381118282101715613f8457613f84613de4565b604052919050565b60006101608284031215613f9f57600080fd5b613fa7613e64565b613fb083613d08565b8152613fbe60208401613d08565b6020820152613fcf60408401613d08565b6040820152613fe060608401613d08565b6060820152613ff160808401613d08565b608082015261400260a08401613d08565b60a082015261401360c08401613d08565b60c082015261402460e08401613d08565b60e0820152610100614037818501613d08565b90820152610120614049848201613d08565b9082015261014061405b848201613d08565b908201529392505050565b6000806040838503121561407957600080fd5b505080516020909101519092909150565b600181815b808511156140c55781600019048211156140ab576140ab613d30565b808516156140b857918102915b93841c939080029061408f565b509250929050565b6000826140dc57506001612968565b816140e957506000612968565b81600181146140ff576002811461410957614125565b6001915050612968565b60ff84111561411a5761411a613d30565b50506001821b612968565b5060208310610133831016604e8410600b8410161715614148575081810a612968565b614152838361408a565b806000190482111561416657614166613d30565b029392505050565b600061296583836140cd565b60006101008201905082518252602083015160208301526040830151604083015260608301516141a981613bbd565b806060840152506080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b6000606082840312156141f057600080fd5b604051606081018181106001600160401b038211171561421257614212613de4565b80604052508251815260208301516020820152604083015160408201528091505092915050565b60006001600160401b0382111561425257614252613de4565b5060051b60200190565b6000602080838503121561426f57600080fd5b82516001600160401b0381111561428557600080fd5b8301601f8101851361429657600080fd5b80516142a96142a482614239565b613f5c565b81815260c091820283018401918482019190888411156142c857600080fd5b938501935b8385101561434c5780858a0312156142e55760008081fd5b6142ed613e8d565b8551815286860151878201526040808701516005811061430d5760008081fd5b90820152606086810151908201526080808701519082015260a080870151600681106143395760008081fd5b90820152835293840193918501916142cd565b50979650505050505050565b600060001982141561436c5761436c613d30565b5060010190565b6000808585111561438357600080fd5b8386111561439057600080fd5b5050820193919092039150565b6000608082840312156143af57600080fd5b6143b7613eaf565b9050813581526020808301358183015260408301356143d581613a0c565b604083015260608301356001600160401b03808211156143f457600080fd5b818501915085601f83011261440857600080fd5b81358181111561441a5761441a613de4565b61442c601f8201601f19168501613f5c565b9150808252868482850101111561444257600080fd5b808484018584013760008482840101525080606085015250505092915050565b60006020828403121561447457600080fd5b81356001600160401b038082111561448b57600080fd5b9083019060a0828603121561449f57600080fd5b6144a7613ed1565b82356144b281613a0c565b8082525060208301356020820152604083013560408201526060830135828111156144dc57600080fd5b6144e88782860161439d565b60608301525060808301358281111561450057600080fd5b61450c8782860161439d565b60808301525095945050505050565b60008083128015600160ff1b85018412161561453957614539613d30565b6001600160ff1b038401831381161561455457614554613d30565b50500390565b6000610120828403121561456d57600080fd5b614575613ef3565b9050815181526020820151602082015260408201516040820152606082015160608201526080820151608082015260a082015160a082015260c082015160c082015260e082015160e082015261010080830151818301525092915050565b600061012082840312156145e657600080fd5b612965838361455a565b6000610180828403121561460357600080fd5b61460b613f16565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e082015261010080840151818301525061012080840151818301525061014080840151818301525061016061405b818501613c82565b6000610140828403121561469a57600080fd5b6146a2613f39565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152506101208084015181830152508091505092915050565b600080610140838503121561472257600080fd5b61472c848461455a565b91506101208301516001600160401b038082111561474957600080fd5b9084019060a0828703121561475d57600080fd5b614765613ed1565b825181526020808401518183015260408401516040830152606084015161478b81613a59565b60608301526080840151838111156147a257600080fd5b80850194505087601f8501126147b757600080fd5b835192506147c76142a484614239565b83815260059390931b840181019281810190898511156147e657600080fd5b948201945b84861015614804578551825294820194908201906147eb565b608084015250949790965094505050505056fe0000000000000000000000008c6f28f2f1a3c87f0f938b96d27520d9751ec8d9a26469706673582212201c1691fdfb5d742a8bcce80602a20d588bc69e3df0d88b4957b172899593107f64736f6c63430008090033608060405234801561001057600080fd5b50600080546001600160a01b0319163390811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a16105048061006e6000396000f3fe608060405234801561001057600080fd5b50600436106100785760003560e01c80631627540c1461007d57806353a47bb71461009257806379ba5097146100c25780637e673ead146100ca5780638da5cb5b14610103578063a3dd457914610116578063a4ed578114610149578063e6a1292b1461015c575b600080fd5b61009061008b3660046103a7565b61016f565b005b6001546100a5906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100906101cb565b6100f56100d83660046103c9565b600360209081526000928352604080842090915290825290205481565b6040519081526020016100b9565b6000546100a5906001600160a01b031681565b6101396101243660046103a7565b60026020526000908152604090205460ff1681565b60405190151581526020016100b9565b6100906101573660046103fc565b610282565b61009061016a366004610449565b61031a565b61017761034d565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6001546001600160a01b03163314610210576001546040516312d9f3db60e31b815261020791309133916001600160a01b031690600401610485565b60405180910390fd5b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b3360009081526002602052604090205460ff166102d75760405162461bcd60e51b81526020600482015260136024820152723737ba103a393ab9ba32b21031b7bab73a32b960691b6044820152606401610207565b6001600160a01b0380861660009081526003602090815260408083209388168352929052908120805483929061030e9084906104a8565b90915550505050505050565b61032261034d565b6001600160a01b03919091166000908152600260205260409020805460ff1916911515919091179055565b6000546001600160a01b031633146103895760005460405163035785f360e31b815261020791309133916001600160a01b031690600401610485565b565b80356001600160a01b03811681146103a257600080fd5b919050565b6000602082840312156103b957600080fd5b6103c28261038b565b9392505050565b600080604083850312156103dc57600080fd5b6103e58361038b565b91506103f36020840161038b565b90509250929050565b600080600080600060a0868803121561041457600080fd5b61041d8661038b565b945061042b6020870161038b565b94979496505050506040830135926060810135926080909101359150565b6000806040838503121561045c57600080fd5b6104658361038b565b91506020830135801515811461047a57600080fd5b809150509250929050565b6001600160a01b0393841681529183166020830152909116604082015260600190565b600082198211156104c957634e487b7160e01b600052601160045260246000fd5b50019056fea2646970667358221220805bec12f669fce49721b87eb0c0a86b677eec75b82f0ec352872f56ef6c772764736f6c6343000809003300000000000000000000000042000000000000000000000000000000000000060000000000000000000000003a32becc41115b4aa8bd8614301aebb9bb11258b0000000000000000000000001d42a98848e022908069c2c545ae44cc78509bc8000000000000000000000000ae75b29ade678372d77a8b41225654138a7e6ff10000000000000000000000008698160f8d8b4910351c7fadaabea902d7c21283000000000000000000000000c30141b657f4216252dc59af2e7cdb9d8792e1b00000000000000000000000000000000000000000000000000000000000000064

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

00000000000000000000000042000000000000000000000000000000000000060000000000000000000000003a32becc41115b4aa8bd8614301aebb9bb11258b0000000000000000000000001d42a98848e022908069c2c545ae44cc78509bc8000000000000000000000000ae75b29ade678372d77a8b41225654138a7e6ff10000000000000000000000008698160f8d8b4910351c7fadaabea902d7c21283000000000000000000000000c30141b657f4216252dc59af2e7cdb9d8792e1b00000000000000000000000000000000000000000000000000000000000000064

-----Decoded View---------------
Arg [0] : _wantTokenL2 (address): 0x4200000000000000000000000000000000000006
Arg [1] : _positionHandlerL1 (address): 0x3a32becc41115b4aa8bd8614301aebb9bb11258b
Arg [2] : _lyraOptionMarket (address): 0x1d42a98848e022908069c2c545ae44cc78509bc8
Arg [3] : _keeper (address): 0xae75b29ade678372d77a8b41225654138a7e6ff1
Arg [4] : _governance (address): 0x8698160f8d8b4910351c7fadaabea902d7c21283
Arg [5] : _socketRegistry (address): 0xc30141b657f4216252dc59af2e7cdb9d8792e1b0
Arg [6] : _slippage (uint256): 100

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000004200000000000000000000000000000000000006
Arg [1] : 0000000000000000000000003a32becc41115b4aa8bd8614301aebb9bb11258b
Arg [2] : 0000000000000000000000001d42a98848e022908069c2c545ae44cc78509bc8
Arg [3] : 000000000000000000000000ae75b29ade678372d77a8b41225654138a7e6ff1
Arg [4] : 0000000000000000000000008698160f8d8b4910351c7fadaabea902d7c21283
Arg [5] : 000000000000000000000000c30141b657f4216252dc59af2e7cdb9d8792e1b0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000064


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