ETH Price: $3,006.06 (+3.12%)
Gas: 2 Gwei

Contract

0xbEba74374e7cf9362FdEDD0b9E2Bc4a04549E4ad
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x61014060149720342022-06-16 7:06:20751 days ago1655363180IN
 Create: RibbonThetaVault
0 ETH0.11167821.23075265

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RibbonThetaVault

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 29 : RibbonThetaVault.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

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

import {GnosisAuction} from "../../libraries/GnosisAuction.sol";
import {
    RibbonThetaVaultStorage
} from "../../storage/RibbonThetaVaultStorage.sol";
import {Vault} from "../../libraries/Vault.sol";
import {VaultLifecycle} from "../../libraries/VaultLifecycle.sol";
import {ShareMath} from "../../libraries/ShareMath.sol";
import {ILiquidityGauge} from "../../interfaces/ILiquidityGauge.sol";
import {IVaultPauser} from "../../interfaces/IVaultPauser.sol";
import {RibbonVault} from "./base/RibbonVault.sol";

/**
 * UPGRADEABILITY: Since we use the upgradeable proxy pattern, we must observe
 * the inheritance chain closely.
 * Any changes/appends in storage variable needs to happen in RibbonThetaVaultStorage.
 * RibbonThetaVault should not inherit from any other contract aside from RibbonVault, RibbonThetaVaultStorage
 */
contract RibbonThetaVault is RibbonVault, RibbonThetaVaultStorage {
    using SafeERC20 for IERC20;
    using SafeMath for uint256;
    using ShareMath for Vault.DepositReceipt;

    /************************************************
     *  IMMUTABLES & CONSTANTS
     ***********************************************/

    /// @notice oTokenFactory is the factory contract used to spawn otokens. Used to lookup otokens.
    address public immutable OTOKEN_FACTORY;

    // The minimum duration for an option auction.
    uint256 private constant MIN_AUCTION_DURATION = 5 minutes;

    /************************************************
     *  EVENTS
     ***********************************************/

    event OpenShort(
        address indexed options,
        uint256 depositAmount,
        address indexed manager
    );

    event CloseShort(
        address indexed options,
        uint256 withdrawAmount,
        address indexed manager
    );

    event NewOptionStrikeSelected(uint256 strikePrice, uint256 delta);

    event PremiumDiscountSet(
        uint256 premiumDiscount,
        uint256 newPremiumDiscount
    );

    event AuctionDurationSet(
        uint256 auctionDuration,
        uint256 newAuctionDuration
    );

    event InstantWithdraw(
        address indexed account,
        uint256 amount,
        uint256 round
    );

    event InitiateGnosisAuction(
        address indexed auctioningToken,
        address indexed biddingToken,
        uint256 auctionCounter,
        address indexed manager
    );

    /************************************************
     *  STRUCTS
     ***********************************************/

    /**
     * @notice Initialization parameters for the vault.
     * @param _owner is the owner of the vault with critical permissions
     * @param _feeRecipient is the address to recieve vault performance and management fees
     * @param _managementFee is the management fee pct.
     * @param _performanceFee is the perfomance fee pct.
     * @param _tokenName is the name of the token
     * @param _tokenSymbol is the symbol of the token
     * @param _optionsPremiumPricer is the address of the contract with the
       black-scholes premium calculation logic
     * @param _strikeSelection is the address of the contract with strike selection logic
     * @param _premiumDiscount is the vault's discount applied to the premium
     * @param _auctionDuration is the duration of the gnosis auction
     */
    struct InitParams {
        address _owner;
        address _keeper;
        address _feeRecipient;
        uint256 _managementFee;
        uint256 _performanceFee;
        string _tokenName;
        string _tokenSymbol;
        address _optionsPremiumPricer;
        address _strikeSelection;
        uint32 _premiumDiscount;
        uint256 _auctionDuration;
    }

    /************************************************
     *  CONSTRUCTOR & INITIALIZATION
     ***********************************************/

    /**
     * @notice Initializes the contract with immutable variables
     * @param _weth is the Wrapped Ether contract
     * @param _usdc is the USDC contract
     * @param _oTokenFactory is the contract address for minting new opyn option types (strikes, asset, expiry)
     * @param _gammaController is the contract address for opyn actions
     * @param _marginPool is the contract address for providing collateral to opyn
     * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions
     */
    constructor(
        address _weth,
        address _usdc,
        address _oTokenFactory,
        address _gammaController,
        address _marginPool,
        address _gnosisEasyAuction
    )
        RibbonVault(
            _weth,
            _usdc,
            _gammaController,
            _marginPool,
            _gnosisEasyAuction
        )
    {
        require(_oTokenFactory != address(0), "!_oTokenFactory");
        OTOKEN_FACTORY = _oTokenFactory;
    }

    /**
     * @notice Initializes the OptionVault contract with storage variables.
     * @param _initParams is the struct with vault initialization parameters
     * @param _vaultParams is the struct with vault general data
     */
    function initialize(
        InitParams calldata _initParams,
        Vault.VaultParams calldata _vaultParams
    ) external initializer {
        baseInitialize(
            _initParams._owner,
            _initParams._keeper,
            _initParams._feeRecipient,
            _initParams._managementFee,
            _initParams._performanceFee,
            _initParams._tokenName,
            _initParams._tokenSymbol,
            _vaultParams
        );
        require(
            _initParams._optionsPremiumPricer != address(0),
            "!_optionsPremiumPricer"
        );
        require(
            _initParams._strikeSelection != address(0),
            "!_strikeSelection"
        );
        require(
            _initParams._premiumDiscount > 0 &&
                _initParams._premiumDiscount <
                100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,
            "!_premiumDiscount"
        );
        require(
            _initParams._auctionDuration >= MIN_AUCTION_DURATION,
            "!_auctionDuration"
        );
        optionsPremiumPricer = _initParams._optionsPremiumPricer;
        strikeSelection = _initParams._strikeSelection;
        premiumDiscount = _initParams._premiumDiscount;
        auctionDuration = _initParams._auctionDuration;
    }

    /************************************************
     *  SETTERS
     ***********************************************/

    /**
     * @notice Sets the new discount on premiums for options we are selling
     * @param newPremiumDiscount is the premium discount
     */
    function setPremiumDiscount(uint256 newPremiumDiscount)
        external
        onlyKeeper
    {
        require(
            newPremiumDiscount > 0 &&
                newPremiumDiscount <= 100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER,
            "Invalid discount"
        );

        emit PremiumDiscountSet(premiumDiscount, newPremiumDiscount);

        premiumDiscount = newPremiumDiscount;
    }

    /**
     * @notice Sets the new auction duration
     * @param newAuctionDuration is the auction duration
     */
    function setAuctionDuration(uint256 newAuctionDuration) external onlyOwner {
        require(
            newAuctionDuration >= MIN_AUCTION_DURATION,
            "Invalid auction duration"
        );

        emit AuctionDurationSet(auctionDuration, newAuctionDuration);

        auctionDuration = newAuctionDuration;
    }

    /**
     * @notice Sets the new strike selection contract
     * @param newStrikeSelection is the address of the new strike selection contract
     */
    function setStrikeSelection(address newStrikeSelection) external onlyOwner {
        require(newStrikeSelection != address(0), "!newStrikeSelection");
        strikeSelection = newStrikeSelection;
    }

    /**
     * @notice Sets the new options premium pricer contract
     * @param newOptionsPremiumPricer is the address of the new strike selection contract
     */
    function setOptionsPremiumPricer(address newOptionsPremiumPricer)
        external
        onlyOwner
    {
        require(
            newOptionsPremiumPricer != address(0),
            "!newOptionsPremiumPricer"
        );
        optionsPremiumPricer = newOptionsPremiumPricer;
    }

    /**
     * @notice Optionality to set strike price manually
     * @param strikePrice is the strike price of the new oTokens (decimals = 8)
     */
    function setStrikePrice(uint128 strikePrice) external onlyOwner {
        require(strikePrice > 0, "!strikePrice");
        overriddenStrikePrice = strikePrice;
        lastStrikeOverrideRound = vaultState.round;
    }

    /**
     * @notice Sets the new liquidityGauge contract for this vault
     * @param newLiquidityGauge is the address of the new liquidityGauge contract
     */
    function setLiquidityGauge(address newLiquidityGauge) external onlyOwner {
        liquidityGauge = newLiquidityGauge;
    }

    /**
     * @notice Sets the new optionsPurchaseQueue contract for this vault
     * @param newOptionsPurchaseQueue is the address of the new optionsPurchaseQueue contract
     */
    function setOptionsPurchaseQueue(address newOptionsPurchaseQueue)
        external
        onlyOwner
    {
        optionsPurchaseQueue = newOptionsPurchaseQueue;
    }

    /**
     * @notice Sets oToken Premium
     * @param minPrice is the new oToken Premium in the units of 10**18
     */
    function setMinPrice(uint256 minPrice) external onlyKeeper {
        require(minPrice > 0, "!minPrice");
        currentOtokenPremium = minPrice;
    }

    /**
     * @notice Sets the new Vault Pauser contract for this vault
     * @param newVaultPauser is the address of the new vaultPauser contract
     */
    function setVaultPauser(address newVaultPauser) external onlyOwner {
        vaultPauser = newVaultPauser;
    }

    /************************************************
     *  VAULT OPERATIONS
     ***********************************************/

    /**
     * @notice Withdraws the assets on the vault using the outstanding `DepositReceipt.amount`
     * @param amount is the amount to withdraw
     */
    function withdrawInstantly(uint256 amount) external nonReentrant {
        Vault.DepositReceipt storage depositReceipt =
            depositReceipts[msg.sender];

        uint256 currentRound = vaultState.round;
        require(amount > 0, "!amount");
        require(depositReceipt.round == currentRound, "Invalid round");

        uint256 receiptAmount = depositReceipt.amount;
        require(receiptAmount >= amount, "Exceed amount");

        // Subtraction underflow checks already ensure it is smaller than uint104
        depositReceipt.amount = uint104(receiptAmount.sub(amount));
        vaultState.totalPending = uint128(
            uint256(vaultState.totalPending).sub(amount)
        );

        emit InstantWithdraw(msg.sender, amount, currentRound);

        transferAsset(msg.sender, amount);
    }

    /**
     * @notice Initiates a withdrawal that can be processed once the round completes
     * @param numShares is the number of shares to withdraw
     */
    function initiateWithdraw(uint256 numShares) external nonReentrant {
        _initiateWithdraw(numShares);
        currentQueuedWithdrawShares = currentQueuedWithdrawShares.add(
            numShares
        );
    }

    /**
     * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round
     */
    function completeWithdraw() external nonReentrant {
        uint256 withdrawAmount = _completeWithdraw();
        lastQueuedWithdrawAmount = uint128(
            uint256(lastQueuedWithdrawAmount).sub(withdrawAmount)
        );
    }

    /**
     * @notice Stakes a users vault shares
     * @param numShares is the number of shares to stake
     */
    function stake(uint256 numShares) external nonReentrant {
        address _liquidityGauge = liquidityGauge;
        require(_liquidityGauge != address(0)); // Removed revert msgs due to contract size limit
        require(numShares > 0);
        uint256 heldByAccount = balanceOf(msg.sender);
        if (heldByAccount < numShares) {
            _redeem(numShares.sub(heldByAccount), false);
        }
        _transfer(msg.sender, address(this), numShares);
        _approve(address(this), _liquidityGauge, numShares);
        ILiquidityGauge(_liquidityGauge).deposit(numShares, msg.sender, false);
    }

    /**
     * @notice Sets the next option the vault will be shorting, and closes the existing short.
     *         This allows all the users to withdraw if the next option is malicious.
     */
    function commitAndClose() external nonReentrant {
        address oldOption = optionState.currentOption;

        VaultLifecycle.CloseParams memory closeParams =
            VaultLifecycle.CloseParams({
                OTOKEN_FACTORY: OTOKEN_FACTORY,
                USDC: USDC,
                currentOption: oldOption,
                delay: DELAY,
                lastStrikeOverrideRound: lastStrikeOverrideRound,
                overriddenStrikePrice: overriddenStrikePrice,
                strikeSelection: strikeSelection,
                optionsPremiumPricer: optionsPremiumPricer,
                premiumDiscount: premiumDiscount
            });

        (address otokenAddress, uint256 strikePrice, uint256 delta) =
            VaultLifecycle.commitAndClose(closeParams, vaultParams, vaultState);

        emit NewOptionStrikeSelected(strikePrice, delta);

        optionState.nextOption = otokenAddress;

        uint256 nextOptionReady = block.timestamp.add(DELAY);
        require(
            nextOptionReady <= type(uint32).max,
            "Overflow nextOptionReady"
        );
        optionState.nextOptionReadyAt = uint32(nextOptionReady);

        _closeShort(oldOption);
    }

    /**
     * @notice Closes the existing short position for the vault.
     */
    function _closeShort(address oldOption) private {
        uint256 lockedAmount = vaultState.lockedAmount;
        if (oldOption != address(0)) {
            vaultState.lastLockedAmount = uint104(lockedAmount);
        }
        vaultState.lockedAmount = 0;

        optionState.currentOption = address(0);

        if (oldOption != address(0)) {
            uint256 withdrawAmount =
                VaultLifecycle.settleShort(GAMMA_CONTROLLER);
            emit CloseShort(oldOption, withdrawAmount, msg.sender);
        }
    }

    /**
     * @notice Rolls the vault's funds into a new short position.
     */
    function rollToNextOption() external onlyKeeper nonReentrant {
        uint256 currQueuedWithdrawShares = currentQueuedWithdrawShares;

        (
            address newOption,
            uint256 lockedBalance,
            uint256 queuedWithdrawAmount
        ) =
            _rollToNextOption(
                lastQueuedWithdrawAmount,
                currQueuedWithdrawShares
            );

        lastQueuedWithdrawAmount = queuedWithdrawAmount;

        uint256 newQueuedWithdrawShares =
            uint256(vaultState.queuedWithdrawShares).add(
                currQueuedWithdrawShares
            );
        ShareMath.assertUint128(newQueuedWithdrawShares);
        vaultState.queuedWithdrawShares = uint128(newQueuedWithdrawShares);

        currentQueuedWithdrawShares = 0;

        ShareMath.assertUint104(lockedBalance);
        vaultState.lockedAmount = uint104(lockedBalance);

        emit OpenShort(newOption, lockedBalance, msg.sender);

        uint256 optionsMintAmount =
            VaultLifecycle.createShort(
                GAMMA_CONTROLLER,
                MARGIN_POOL,
                newOption,
                lockedBalance
            );

        VaultLifecycle.allocateOptions(
            optionsPurchaseQueue,
            newOption,
            optionsMintAmount,
            VaultLifecycle.QUEUE_OPTION_ALLOCATION
        );

        _startAuction();
    }

    /**
     * @notice Initiate the gnosis auction.
     */
    function startAuction() external onlyKeeper nonReentrant {
        _startAuction();
    }

    function _startAuction() private {
        GnosisAuction.AuctionDetails memory auctionDetails;

        address currentOtoken = optionState.currentOption;

        auctionDetails.oTokenAddress = currentOtoken;
        auctionDetails.gnosisEasyAuction = GNOSIS_EASY_AUCTION;
        auctionDetails.asset = vaultParams.asset;
        auctionDetails.assetDecimals = vaultParams.decimals;
        auctionDetails.oTokenPremium = currentOtokenPremium;
        auctionDetails.duration = auctionDuration;

        optionAuctionID = VaultLifecycle.startAuction(auctionDetails);
    }

    /**
     * @notice Sell the allocated options to the purchase queue post auction settlement
     */
    function sellOptionsToQueue() external onlyKeeper nonReentrant {
        VaultLifecycle.sellOptionsToQueue(
            optionsPurchaseQueue,
            GNOSIS_EASY_AUCTION,
            optionAuctionID
        );
    }

    /**
     * @notice Burn the remaining oTokens left over from gnosis auction.
     */
    function burnRemainingOTokens() external onlyKeeper nonReentrant {
        uint256 unlockedAssetAmount =
            VaultLifecycle.burnOtokens(
                GAMMA_CONTROLLER,
                optionState.currentOption
            );

        vaultState.lockedAmount = uint104(
            uint256(vaultState.lockedAmount).sub(unlockedAssetAmount)
        );
    }

    /**
     * @notice Recovery function that returns an ERC20 token to the recipient
     * @param token is the ERC20 token to recover from the vault
     * @param recipient is the recipient of the recovered tokens
     */
    function recoverTokens(address token, address recipient)
        external
        onlyOwner
    {
        require(token != vaultParams.asset, "Vault asset not recoverable");
        require(token != address(this), "Vault share not recoverable");
        require(recipient != address(this), "Recipient cannot be vault");

        IERC20(token).safeTransfer(
            recipient,
            IERC20(token).balanceOf(address(this))
        );
    }

    /**
     * @notice pause a user's vault position
     */
    function pausePosition() external {
        address _vaultPauserAddress = vaultPauser;
        require(_vaultPauserAddress != address(0)); // Removed revert msgs due to contract size limit
        _redeem(0, true);
        uint256 heldByAccount = balanceOf(msg.sender);
        _approve(msg.sender, _vaultPauserAddress, heldByAccount);
        IVaultPauser(_vaultPauserAddress).pausePosition(
            msg.sender,
            heldByAccount
        );
    }
}

File 2 of 29 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

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

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

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

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

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

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

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

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

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

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 4 of 29 : SafeERC20.sol
// SPDX-License-Identifier: MIT

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 5 of 29 : GnosisAuction.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    SafeERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {DSMath} from "../vendor/DSMath.sol";
import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol";
import {IOtoken} from "../interfaces/GammaInterface.sol";
import {IOptionsPremiumPricer} from "../interfaces/IRibbon.sol";
import {Vault} from "./Vault.sol";
import {IRibbonThetaVault} from "../interfaces/IRibbonThetaVault.sol";

library GnosisAuction {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    event InitiateGnosisAuction(
        address indexed auctioningToken,
        address indexed biddingToken,
        uint256 auctionCounter,
        address indexed manager
    );

    event PlaceAuctionBid(
        uint256 auctionId,
        address indexed auctioningToken,
        uint256 sellAmount,
        uint256 buyAmount,
        address indexed bidder
    );

    struct AuctionDetails {
        address oTokenAddress;
        address gnosisEasyAuction;
        address asset;
        uint256 assetDecimals;
        uint256 oTokenPremium;
        uint256 duration;
    }

    struct BidDetails {
        address oTokenAddress;
        address gnosisEasyAuction;
        address asset;
        uint256 assetDecimals;
        uint256 auctionId;
        uint256 lockedBalance;
        uint256 optionAllocation;
        uint256 optionPremium;
        address bidder;
    }

    function startAuction(AuctionDetails calldata auctionDetails)
        internal
        returns (uint256 auctionID)
    {
        uint256 oTokenSellAmount =
            getOTokenSellAmount(auctionDetails.oTokenAddress);
        require(oTokenSellAmount > 0, "No otokens to sell");

        IERC20(auctionDetails.oTokenAddress).safeApprove(
            auctionDetails.gnosisEasyAuction,
            IERC20(auctionDetails.oTokenAddress).balanceOf(address(this))
        );

        // minBidAmount is total oTokens to sell * premium per oToken
        // shift decimals to correspond to decimals of USDC for puts
        // and underlying for calls
        uint256 minBidAmount =
            DSMath.wmul(
                oTokenSellAmount.mul(10**10),
                auctionDetails.oTokenPremium
            );

        minBidAmount = auctionDetails.assetDecimals > 18
            ? minBidAmount.mul(10**(auctionDetails.assetDecimals.sub(18)))
            : minBidAmount.div(
                10**(uint256(18).sub(auctionDetails.assetDecimals))
            );

        require(
            minBidAmount <= type(uint96).max,
            "optionPremium * oTokenSellAmount > type(uint96) max value!"
        );

        uint256 auctionEnd = block.timestamp.add(auctionDetails.duration);

        auctionID = IGnosisAuction(auctionDetails.gnosisEasyAuction)
            .initiateAuction(
            // address of oToken we minted and are selling
            auctionDetails.oTokenAddress,
            // address of asset we want in exchange for oTokens. Should match vault `asset`
            auctionDetails.asset,
            // orders can be cancelled at any time during the auction
            auctionEnd,
            // order will last for `duration`
            auctionEnd,
            // we are selling all of the otokens minus a fee taken by gnosis
            uint96(oTokenSellAmount),
            // the minimum we are willing to sell all the oTokens for. A discount is applied on black-scholes price
            uint96(minBidAmount),
            // the minimum bidding amount must be 1 * 10 ** -assetDecimals
            1,
            // the min funding threshold
            0,
            // no atomic closure
            false,
            // access manager contract
            address(0),
            // bytes for storing info like a whitelist for who can bid
            bytes("")
        );

        emit InitiateGnosisAuction(
            auctionDetails.oTokenAddress,
            auctionDetails.asset,
            auctionID,
            msg.sender
        );
    }

    function placeBid(BidDetails calldata bidDetails)
        internal
        returns (
            uint256 sellAmount,
            uint256 buyAmount,
            uint64 userId
        )
    {
        // calculate how much to allocate
        sellAmount = bidDetails
            .lockedBalance
            .mul(bidDetails.optionAllocation)
            .div(100 * Vault.OPTION_ALLOCATION_MULTIPLIER);

        // divide the `asset` sellAmount by the target premium per oToken to
        // get the number of oTokens to buy (8 decimals)
        buyAmount = sellAmount
            .mul(10**(bidDetails.assetDecimals.add(Vault.OTOKEN_DECIMALS)))
            .div(bidDetails.optionPremium)
            .div(10**bidDetails.assetDecimals);

        require(
            sellAmount <= type(uint96).max,
            "sellAmount > type(uint96) max value!"
        );
        require(
            buyAmount <= type(uint96).max,
            "buyAmount > type(uint96) max value!"
        );

        // approve that amount
        IERC20(bidDetails.asset).safeApprove(
            bidDetails.gnosisEasyAuction,
            sellAmount
        );

        uint96[] memory _minBuyAmounts = new uint96[](1);
        uint96[] memory _sellAmounts = new uint96[](1);
        bytes32[] memory _prevSellOrders = new bytes32[](1);
        _minBuyAmounts[0] = uint96(buyAmount);
        _sellAmounts[0] = uint96(sellAmount);
        _prevSellOrders[
            0
        ] = 0x0000000000000000000000000000000000000000000000000000000000000001;

        // place sell order with that amount
        userId = IGnosisAuction(bidDetails.gnosisEasyAuction).placeSellOrders(
            bidDetails.auctionId,
            _minBuyAmounts,
            _sellAmounts,
            _prevSellOrders,
            "0x"
        );

        emit PlaceAuctionBid(
            bidDetails.auctionId,
            bidDetails.oTokenAddress,
            sellAmount,
            buyAmount,
            bidDetails.bidder
        );

        return (sellAmount, buyAmount, userId);
    }

    function claimAuctionOtokens(
        Vault.AuctionSellOrder calldata auctionSellOrder,
        address gnosisEasyAuction,
        address counterpartyThetaVault
    ) internal {
        bytes32 order =
            encodeOrder(
                auctionSellOrder.userId,
                auctionSellOrder.buyAmount,
                auctionSellOrder.sellAmount
            );
        bytes32[] memory orders = new bytes32[](1);
        orders[0] = order;
        IGnosisAuction(gnosisEasyAuction).claimFromParticipantOrder(
            IRibbonThetaVault(counterpartyThetaVault).optionAuctionID(),
            orders
        );
    }

    function getOTokenSellAmount(address oTokenAddress)
        internal
        view
        returns (uint256)
    {
        // We take our current oToken balance. That will be our sell amount
        // but otokens will be transferred to gnosis.
        uint256 oTokenSellAmount =
            IERC20(oTokenAddress).balanceOf(address(this));

        require(
            oTokenSellAmount <= type(uint96).max,
            "oTokenSellAmount > type(uint96) max value!"
        );

        return oTokenSellAmount;
    }

    function getOTokenPremiumInStables(
        address oTokenAddress,
        address optionsPremiumPricer,
        uint256 premiumDiscount
    ) internal view returns (uint256) {
        IOtoken newOToken = IOtoken(oTokenAddress);
        IOptionsPremiumPricer premiumPricer =
            IOptionsPremiumPricer(optionsPremiumPricer);

        // Apply black-scholes formula (from rvol library) to option given its features
        // and get price for 100 contracts denominated USDC for both call and put options
        uint256 optionPremium =
            premiumPricer.getPremiumInStables(
                newOToken.strikePrice(),
                newOToken.expiryTimestamp(),
                newOToken.isPut()
            );

        // Apply a discount to incentivize arbitraguers
        optionPremium = optionPremium.mul(premiumDiscount).div(
            100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER
        );

        require(
            optionPremium <= type(uint96).max,
            "optionPremium > type(uint96) max value!"
        );

        return optionPremium;
    }

    function encodeOrder(
        uint64 userId,
        uint96 buyAmount,
        uint96 sellAmount
    ) internal pure returns (bytes32) {
        return
            bytes32(
                (uint256(userId) << 192) +
                    (uint256(buyAmount) << 96) +
                    uint256(sellAmount)
            );
    }
}

File 6 of 29 : RibbonThetaVaultStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

abstract contract RibbonThetaVaultStorageV1 {
    // Logic contract used to price options
    address public optionsPremiumPricer;
    // Logic contract used to select strike prices
    address public strikeSelection;
    // Premium discount on options we are selling (thousandths place: 000 - 999)
    uint256 public premiumDiscount;
    // Current oToken premium
    uint256 public currentOtokenPremium;
    // Last round id at which the strike was manually overridden
    uint16 public lastStrikeOverrideRound;
    // Price last overridden strike set to
    uint256 public overriddenStrikePrice;
    // Auction duration
    uint256 public auctionDuration;
    // Auction id of current option
    uint256 public optionAuctionID;
}

abstract contract RibbonThetaVaultStorageV2 {
    // Amount locked for scheduled withdrawals last week;
    uint256 public lastQueuedWithdrawAmount;
}

abstract contract RibbonThetaVaultStorageV3 {
    // DEPRECATED: Auction will be denominated in USDC if true
    bool private _isUsdcAuction;
    // DEPRECATED: Path for swaps
    bytes private _swapPath;
}

abstract contract RibbonThetaVaultStorageV4 {
    // LiquidityGauge contract for the vault
    address public liquidityGauge;
}

abstract contract RibbonThetaVaultStorageV5 {
    // OptionsPurchaseQueue contract for selling options
    address public optionsPurchaseQueue;
}

abstract contract RibbonThetaVaultStorageV6 {
    // Queued withdraw shares for the current round
    uint256 public currentQueuedWithdrawShares;
}

abstract contract RibbonThetaVaultStorageV7 {
    // Vault Pauser Contract for the vault
    address public vaultPauser;
}

// We are following Compound's method of upgrading new contract implementations
// When we need to add new storage variables, we create a new version of RibbonThetaVaultStorage
// e.g. RibbonThetaVaultStorage<versionNumber>, so finally it would look like
// contract RibbonThetaVaultStorage is RibbonThetaVaultStorageV1, RibbonThetaVaultStorageV2
abstract contract RibbonThetaVaultStorage is
    RibbonThetaVaultStorageV1,
    RibbonThetaVaultStorageV2,
    RibbonThetaVaultStorageV3,
    RibbonThetaVaultStorageV4,
    RibbonThetaVaultStorageV5,
    RibbonThetaVaultStorageV6,
    RibbonThetaVaultStorageV7
{

}

File 7 of 29 : Vault.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

library Vault {
    /************************************************
     *  IMMUTABLES & CONSTANTS
     ***********************************************/

    // Fees are 6-decimal places. For example: 20 * 10**6 = 20%
    uint256 internal constant FEE_MULTIPLIER = 10**6;

    // Premium discount has 1-decimal place. For example: 80 * 10**1 = 80%. Which represents a 20% discount.
    uint256 internal constant PREMIUM_DISCOUNT_MULTIPLIER = 10;

    // Otokens have 8 decimal places.
    uint256 internal constant OTOKEN_DECIMALS = 8;

    // Percentage of funds allocated to options is 2 decimal places. 10 * 10**2 = 10%
    uint256 internal constant OPTION_ALLOCATION_MULTIPLIER = 10**2;

    // Placeholder uint value to prevent cold writes
    uint256 internal constant PLACEHOLDER_UINT = 1;

    struct VaultParams {
        // Option type the vault is selling
        bool isPut;
        // Token decimals for vault shares
        uint8 decimals;
        // Asset used in Theta / Delta Vault
        address asset;
        // Underlying asset of the options sold by vault
        address underlying;
        // Minimum supply of the vault shares issued, for ETH it's 10**10
        uint56 minimumSupply;
        // Vault cap
        uint104 cap;
    }

    struct OptionState {
        // Option that the vault is shorting / longing in the next cycle
        address nextOption;
        // Option that the vault is currently shorting / longing
        address currentOption;
        // The timestamp when the `nextOption` can be used by the vault
        uint32 nextOptionReadyAt;
    }

    struct VaultState {
        // 32 byte slot 1
        //  Current round number. `round` represents the number of `period`s elapsed.
        uint16 round;
        // Amount that is currently locked for selling options
        uint104 lockedAmount;
        // Amount that was locked for selling options in the previous round
        // used for calculating performance fee deduction
        uint104 lastLockedAmount;
        // 32 byte slot 2
        // Stores the total tally of how much of `asset` there is
        // to be used to mint rTHETA tokens
        uint128 totalPending;
        // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)
        uint128 queuedWithdrawShares;
    }

    struct DepositReceipt {
        // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.
        uint16 round;
        // Deposit amount, max 20,282,409,603,651 or 20 trillion ETH deposit
        uint104 amount;
        // Unredeemed shares balance
        uint128 unredeemedShares;
    }

    struct Withdrawal {
        // Maximum of 65535 rounds. Assuming 1 round is 7 days, maximum is 1256 years.
        uint16 round;
        // Number of shares withdrawn
        uint128 shares;
    }

    struct AuctionSellOrder {
        // Amount of `asset` token offered in auction
        uint96 sellAmount;
        // Amount of oToken requested in auction
        uint96 buyAmount;
        // User Id of delta vault in latest gnosis auction
        uint64 userId;
    }
}

File 8 of 29 : VaultLifecycle.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Vault} from "./Vault.sol";
import {ShareMath} from "./ShareMath.sol";
import {IStrikeSelection} from "../interfaces/IRibbon.sol";
import {GnosisAuction} from "./GnosisAuction.sol";
import {
    IOtokenFactory,
    IOtoken,
    IController,
    GammaTypes
} from "../interfaces/GammaInterface.sol";
import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol";
import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol";
import {IOptionsPurchaseQueue} from "../interfaces/IOptionsPurchaseQueue.sol";
import {SupportsNonCompliantERC20} from "./SupportsNonCompliantERC20.sol";
import {IOptionsPremiumPricer} from "../interfaces/IRibbon.sol";

library VaultLifecycle {
    using SafeMath for uint256;
    using SupportsNonCompliantERC20 for IERC20;

    struct CloseParams {
        address OTOKEN_FACTORY;
        address USDC;
        address currentOption;
        uint256 delay;
        uint16 lastStrikeOverrideRound;
        uint256 overriddenStrikePrice;
        address strikeSelection;
        address optionsPremiumPricer;
        uint256 premiumDiscount;
    }

    /// @notice Default maximum option allocation for the queue (50%)
    uint256 internal constant QUEUE_OPTION_ALLOCATION = 5000;

    /**
     * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction
     * @param closeParams is the struct with details on previous option and strike selection details
     * @param vaultParams is the struct with vault general data
     * @param vaultState is the struct with vault accounting state
     * @return otokenAddress is the address of the new option
     * @return strikePrice is the strike price of the new option
     * @return delta is the delta of the new option
     */
    function commitAndClose(
        CloseParams calldata closeParams,
        Vault.VaultParams storage vaultParams,
        Vault.VaultState storage vaultState
    )
        external
        returns (
            address otokenAddress,
            uint256 strikePrice,
            uint256 delta
        )
    {
        uint256 expiry = getNextExpiry(closeParams.currentOption);

        IStrikeSelection selection =
            IStrikeSelection(closeParams.strikeSelection);

        bool isPut = vaultParams.isPut;
        address underlying = vaultParams.underlying;
        address asset = vaultParams.asset;

        (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==
            vaultState.round
            ? (closeParams.overriddenStrikePrice, selection.delta())
            : selection.getStrikePrice(expiry, isPut);

        require(strikePrice != 0, "!strikePrice");

        // retrieve address if option already exists, or deploy it
        otokenAddress = getOrDeployOtoken(
            closeParams,
            vaultParams,
            underlying,
            asset,
            strikePrice,
            expiry,
            isPut
        );

        return (otokenAddress, strikePrice, delta);
    }

    /**
     * @notice Verify the otoken has the correct parameters to prevent vulnerability to opyn contract changes
     * @param otokenAddress is the address of the otoken
     * @param vaultParams is the struct with vault general data
     * @param collateralAsset is the address of the collateral asset
     * @param USDC is the address of usdc
     * @param delay is the delay between commitAndClose and rollToNextOption
     */
    function verifyOtoken(
        address otokenAddress,
        Vault.VaultParams storage vaultParams,
        address collateralAsset,
        address USDC,
        uint256 delay
    ) private view {
        require(otokenAddress != address(0), "!otokenAddress");

        IOtoken otoken = IOtoken(otokenAddress);
        require(otoken.isPut() == vaultParams.isPut, "Type mismatch");
        require(
            otoken.underlyingAsset() == vaultParams.underlying,
            "Wrong underlyingAsset"
        );
        require(
            otoken.collateralAsset() == collateralAsset,
            "Wrong collateralAsset"
        );

        // we just assume all options use USDC as the strike
        require(otoken.strikeAsset() == USDC, "strikeAsset != USDC");

        uint256 readyAt = block.timestamp.add(delay);
        require(otoken.expiryTimestamp() >= readyAt, "Expiry before delay");
    }

    /**
     * @param decimals is the decimals of the asset
     * @param totalBalance is the vaults total balance of the asset
     * @param currentShareSupply is the supply of the shares invoked with totalSupply()
     * @param lastQueuedWithdrawAmount is the total amount queued for withdrawals
     * @param performanceFee is the perf fee percent to charge on premiums
     * @param managementFee is the management fee percent to charge on the AUM
     * @param currentQueuedWithdrawShares is amount of queued withdrawals from the current round
     */
    struct RolloverParams {
        uint256 decimals;
        uint256 totalBalance;
        uint256 currentShareSupply;
        uint256 lastQueuedWithdrawAmount;
        uint256 performanceFee;
        uint256 managementFee;
        uint256 currentQueuedWithdrawShares;
    }

    /**
     * @notice Calculate the shares to mint, new price per share, and
      amount of funds to re-allocate as collateral for the new round
     * @param vaultState is the storage variable vaultState passed from RibbonVault
     * @param params is the rollover parameters passed to compute the next state
     * @return newLockedAmount is the amount of funds to allocate for the new round
     * @return queuedWithdrawAmount is the amount of funds set aside for withdrawal
     * @return newPricePerShare is the price per share of the new round
     * @return mintShares is the amount of shares to mint from deposits
     * @return performanceFeeInAsset is the performance fee charged by vault
     * @return totalVaultFee is the total amount of fee charged by vault
     */
    function rollover(
        Vault.VaultState storage vaultState,
        RolloverParams calldata params
    )
        external
        view
        returns (
            uint256 newLockedAmount,
            uint256 queuedWithdrawAmount,
            uint256 newPricePerShare,
            uint256 mintShares,
            uint256 performanceFeeInAsset,
            uint256 totalVaultFee
        )
    {
        uint256 currentBalance = params.totalBalance;
        uint256 pendingAmount = vaultState.totalPending;
        // Total amount of queued withdrawal shares from previous rounds (doesn't include the current round)
        uint256 lastQueuedWithdrawShares = vaultState.queuedWithdrawShares;

        // Deduct older queued withdraws so we don't charge fees on them
        uint256 balanceForVaultFees =
            currentBalance.sub(params.lastQueuedWithdrawAmount);

        {
            (performanceFeeInAsset, , totalVaultFee) = VaultLifecycle
                .getVaultFees(
                balanceForVaultFees,
                vaultState.lastLockedAmount,
                vaultState.totalPending,
                params.performanceFee,
                params.managementFee
            );
        }

        // Take into account the fee
        // so we can calculate the newPricePerShare
        currentBalance = currentBalance.sub(totalVaultFee);

        {
            newPricePerShare = ShareMath.pricePerShare(
                params.currentShareSupply.sub(lastQueuedWithdrawShares),
                currentBalance.sub(params.lastQueuedWithdrawAmount),
                pendingAmount,
                params.decimals
            );

            queuedWithdrawAmount = params.lastQueuedWithdrawAmount.add(
                ShareMath.sharesToAsset(
                    params.currentQueuedWithdrawShares,
                    newPricePerShare,
                    params.decimals
                )
            );

            // After closing the short, if the options expire in-the-money
            // vault pricePerShare would go down because vault's asset balance decreased.
            // This ensures that the newly-minted shares do not take on the loss.
            mintShares = ShareMath.assetToShares(
                pendingAmount,
                newPricePerShare,
                params.decimals
            );
        }

        return (
            currentBalance.sub(queuedWithdrawAmount), // new locked balance subtracts the queued withdrawals
            queuedWithdrawAmount,
            newPricePerShare,
            mintShares,
            performanceFeeInAsset,
            totalVaultFee
        );
    }

    /**
     * @notice Creates the actual Opyn short position by depositing collateral and minting otokens
     * @param gammaController is the address of the opyn controller contract
     * @param marginPool is the address of the opyn margin contract which holds the collateral
     * @param oTokenAddress is the address of the otoken to mint
     * @param depositAmount is the amount of collateral to deposit
     * @return the otoken mint amount
     */
    function createShort(
        address gammaController,
        address marginPool,
        address oTokenAddress,
        uint256 depositAmount
    ) external returns (uint256) {
        IController controller = IController(gammaController);
        uint256 newVaultID =
            (controller.getAccountVaultCounter(address(this))).add(1);

        // An otoken's collateralAsset is the vault's `asset`
        // So in the context of performing Opyn short operations we call them collateralAsset
        IOtoken oToken = IOtoken(oTokenAddress);
        address collateralAsset = oToken.collateralAsset();

        uint256 collateralDecimals =
            uint256(IERC20Detailed(collateralAsset).decimals());
        uint256 mintAmount;

        if (oToken.isPut()) {
            // For minting puts, there will be instances where the full depositAmount will not be used for minting.
            // This is because of an issue with precision.
            //
            // For ETH put options, we are calculating the mintAmount (10**8 decimals) using
            // the depositAmount (10**18 decimals), which will result in truncation of decimals when scaling down.
            // As a result, there will be tiny amounts of dust left behind in the Opyn vault when minting put otokens.
            //
            // For simplicity's sake, we do not refund the dust back to the address(this) on minting otokens.
            // We retain the dust in the vault so the calling contract can withdraw the
            // actual locked amount + dust at settlement.
            //
            // To test this behavior, we can console.log
            // MarginCalculatorInterface(0x7A48d10f372b3D7c60f6c9770B91398e4ccfd3C7).getExcessCollateral(vault)
            // to see how much dust (or excess collateral) is left behind.
            mintAmount = depositAmount
                .mul(10**Vault.OTOKEN_DECIMALS)
                .mul(10**18) // we use 10**18 to give extra precision
                .div(oToken.strikePrice().mul(10**(10 + collateralDecimals)));
        } else {
            mintAmount = depositAmount;

            if (collateralDecimals > 8) {
                uint256 scaleBy = 10**(collateralDecimals.sub(8)); // oTokens have 8 decimals
                if (mintAmount > scaleBy) {
                    mintAmount = depositAmount.div(scaleBy); // scale down from 10**18 to 10**8
                }
            }
        }

        // double approve to fix non-compliant ERC20s
        IERC20 collateralToken = IERC20(collateralAsset);
        collateralToken.safeApproveNonCompliant(marginPool, depositAmount);

        IController.ActionArgs[] memory actions =
            new IController.ActionArgs[](3);

        actions[0] = IController.ActionArgs(
            IController.ActionType.OpenVault,
            address(this), // owner
            address(this), // receiver
            address(0), // asset, otoken
            newVaultID, // vaultId
            0, // amount
            0, //index
            "" //data
        );

        actions[1] = IController.ActionArgs(
            IController.ActionType.DepositCollateral,
            address(this), // owner
            address(this), // address to transfer from
            collateralAsset, // deposited asset
            newVaultID, // vaultId
            depositAmount, // amount
            0, //index
            "" //data
        );

        actions[2] = IController.ActionArgs(
            IController.ActionType.MintShortOption,
            address(this), // owner
            address(this), // address to transfer to
            oTokenAddress, // option address
            newVaultID, // vaultId
            mintAmount, // amount
            0, //index
            "" //data
        );

        controller.operate(actions);

        return mintAmount;
    }

    /**
     * @notice Close the existing short otoken position. Currently this implementation is simple.
     * It closes the most recent vault opened by the contract. This assumes that the contract will
     * only have a single vault open at any given time. Since calling `_closeShort` deletes vaults by
     calling SettleVault action, this assumption should hold.
     * @param gammaController is the address of the opyn controller contract
     * @return amount of collateral redeemed from the vault
     */
    function settleShort(address gammaController) external returns (uint256) {
        IController controller = IController(gammaController);

        // gets the currently active vault ID
        uint256 vaultID = controller.getAccountVaultCounter(address(this));

        GammaTypes.Vault memory vault =
            controller.getVault(address(this), vaultID);

        require(vault.shortOtokens.length > 0, "No short");

        // An otoken's collateralAsset is the vault's `asset`
        // So in the context of performing Opyn short operations we call them collateralAsset
        IERC20 collateralToken = IERC20(vault.collateralAssets[0]);

        // The short position has been previously closed, or all the otokens have been burned.
        // So we return early.
        if (address(collateralToken) == address(0)) {
            return 0;
        }

        // This is equivalent to doing IERC20(vault.asset).balanceOf(address(this))
        uint256 startCollateralBalance =
            collateralToken.balanceOf(address(this));

        // If it is after expiry, we need to settle the short position using the normal way
        // Delete the vault and withdraw all remaining collateral from the vault
        IController.ActionArgs[] memory actions =
            new IController.ActionArgs[](1);

        actions[0] = IController.ActionArgs(
            IController.ActionType.SettleVault,
            address(this), // owner
            address(this), // address to transfer to
            address(0), // not used
            vaultID, // vaultId
            0, // not used
            0, // not used
            "" // not used
        );

        controller.operate(actions);

        uint256 endCollateralBalance = collateralToken.balanceOf(address(this));

        return endCollateralBalance.sub(startCollateralBalance);
    }

    /**
     * @notice Exercises the ITM option using existing long otoken position. Currently this implementation is simple.
     * It calls the `Redeem` action to claim the payout.
     * @param gammaController is the address of the opyn controller contract
     * @param oldOption is the address of the old option
     * @param asset is the address of the vault's asset
     * @return amount of asset received by exercising the option
     */
    function settleLong(
        address gammaController,
        address oldOption,
        address asset
    ) external returns (uint256) {
        IController controller = IController(gammaController);

        uint256 oldOptionBalance = IERC20(oldOption).balanceOf(address(this));

        if (controller.getPayout(oldOption, oldOptionBalance) == 0) {
            return 0;
        }

        uint256 startAssetBalance = IERC20(asset).balanceOf(address(this));

        // If it is after expiry, we need to redeem the profits
        IController.ActionArgs[] memory actions =
            new IController.ActionArgs[](1);

        actions[0] = IController.ActionArgs(
            IController.ActionType.Redeem,
            address(0), // not used
            address(this), // address to send profits to
            oldOption, // address of otoken
            0, // not used
            oldOptionBalance, // otoken balance
            0, // not used
            "" // not used
        );

        controller.operate(actions);

        uint256 endAssetBalance = IERC20(asset).balanceOf(address(this));

        return endAssetBalance.sub(startAssetBalance);
    }

    /**
     * @notice Burn the remaining oTokens left over from auction. Currently this implementation is simple.
     * It burns oTokens from the most recent vault opened by the contract. This assumes that the contract will
     * only have a single vault open at any given time.
     * @param gammaController is the address of the opyn controller contract
     * @param currentOption is the address of the current option
     * @return amount of collateral redeemed by burning otokens
     */
    function burnOtokens(address gammaController, address currentOption)
        external
        returns (uint256)
    {
        uint256 numOTokensToBurn =
            IERC20(currentOption).balanceOf(address(this));

        require(numOTokensToBurn > 0, "No oTokens to burn");

        IController controller = IController(gammaController);

        // gets the currently active vault ID
        uint256 vaultID = controller.getAccountVaultCounter(address(this));

        GammaTypes.Vault memory vault =
            controller.getVault(address(this), vaultID);

        require(vault.shortOtokens.length > 0, "No short");

        IERC20 collateralToken = IERC20(vault.collateralAssets[0]);

        uint256 startCollateralBalance =
            collateralToken.balanceOf(address(this));

        // Burning `amount` of oTokens from the ribbon vault,
        // then withdrawing the corresponding collateral amount from the vault
        IController.ActionArgs[] memory actions =
            new IController.ActionArgs[](2);

        actions[0] = IController.ActionArgs(
            IController.ActionType.BurnShortOption,
            address(this), // owner
            address(this), // address to transfer from
            address(vault.shortOtokens[0]), // otoken address
            vaultID, // vaultId
            numOTokensToBurn, // amount
            0, //index
            "" //data
        );

        actions[1] = IController.ActionArgs(
            IController.ActionType.WithdrawCollateral,
            address(this), // owner
            address(this), // address to transfer to
            address(collateralToken), // withdrawn asset
            vaultID, // vaultId
            vault.collateralAmounts[0].mul(numOTokensToBurn).div(
                vault.shortAmounts[0]
            ), // amount
            0, //index
            "" //data
        );

        controller.operate(actions);

        uint256 endCollateralBalance = collateralToken.balanceOf(address(this));

        return endCollateralBalance.sub(startCollateralBalance);
    }

    /**
     * @notice Calculates the performance and management fee for this week's round
     * @param currentBalance is the balance of funds held on the vault after closing short
     * @param lastLockedAmount is the amount of funds locked from the previous round
     * @param pendingAmount is the pending deposit amount
     * @param performanceFeePercent is the performance fee pct.
     * @param managementFeePercent is the management fee pct.
     * @return performanceFeeInAsset is the performance fee
     * @return managementFeeInAsset is the management fee
     * @return vaultFee is the total fees
     */
    function getVaultFees(
        uint256 currentBalance,
        uint256 lastLockedAmount,
        uint256 pendingAmount,
        uint256 performanceFeePercent,
        uint256 managementFeePercent
    )
        internal
        pure
        returns (
            uint256 performanceFeeInAsset,
            uint256 managementFeeInAsset,
            uint256 vaultFee
        )
    {
        // At the first round, currentBalance=0, pendingAmount>0
        // so we just do not charge anything on the first round
        uint256 lockedBalanceSansPending =
            currentBalance > pendingAmount
                ? currentBalance.sub(pendingAmount)
                : 0;

        uint256 _performanceFeeInAsset;
        uint256 _managementFeeInAsset;
        uint256 _vaultFee;

        // Take performance fee and management fee ONLY if difference between
        // last week and this week's vault deposits, taking into account pending
        // deposits and withdrawals, is positive. If it is negative, last week's
        // option expired ITM past breakeven, and the vault took a loss so we
        // do not collect performance fee for last week
        if (lockedBalanceSansPending > lastLockedAmount) {
            _performanceFeeInAsset = performanceFeePercent > 0
                ? lockedBalanceSansPending
                    .sub(lastLockedAmount)
                    .mul(performanceFeePercent)
                    .div(100 * Vault.FEE_MULTIPLIER)
                : 0;
            _managementFeeInAsset = managementFeePercent > 0
                ? lockedBalanceSansPending.mul(managementFeePercent).div(
                    100 * Vault.FEE_MULTIPLIER
                )
                : 0;

            _vaultFee = _performanceFeeInAsset.add(_managementFeeInAsset);
        }

        return (_performanceFeeInAsset, _managementFeeInAsset, _vaultFee);
    }

    /**
     * @notice Either retrieves the option token if it already exists, or deploy it
     * @param closeParams is the struct with details on previous option and strike selection details
     * @param vaultParams is the struct with vault general data
     * @param underlying is the address of the underlying asset of the option
     * @param collateralAsset is the address of the collateral asset of the option
     * @param strikePrice is the strike price of the option
     * @param expiry is the expiry timestamp of the option
     * @param isPut is whether the option is a put
     * @return the address of the option
     */
    function getOrDeployOtoken(
        CloseParams calldata closeParams,
        Vault.VaultParams storage vaultParams,
        address underlying,
        address collateralAsset,
        uint256 strikePrice,
        uint256 expiry,
        bool isPut
    ) internal returns (address) {
        IOtokenFactory factory = IOtokenFactory(closeParams.OTOKEN_FACTORY);

        address otokenFromFactory =
            factory.getOtoken(
                underlying,
                closeParams.USDC,
                collateralAsset,
                strikePrice,
                expiry,
                isPut
            );

        if (otokenFromFactory != address(0)) {
            return otokenFromFactory;
        }

        address otoken =
            factory.createOtoken(
                underlying,
                closeParams.USDC,
                collateralAsset,
                strikePrice,
                expiry,
                isPut
            );

        verifyOtoken(
            otoken,
            vaultParams,
            collateralAsset,
            closeParams.USDC,
            closeParams.delay
        );

        return otoken;
    }

    function getOTokenPremium(
        address oTokenAddress,
        address optionsPremiumPricer,
        uint256 premiumDiscount
    ) external view returns (uint256) {
        return
            _getOTokenPremium(
                oTokenAddress,
                optionsPremiumPricer,
                premiumDiscount
            );
    }

    function _getOTokenPremium(
        address oTokenAddress,
        address optionsPremiumPricer,
        uint256 premiumDiscount
    ) internal view returns (uint256) {
        IOtoken newOToken = IOtoken(oTokenAddress);
        IOptionsPremiumPricer premiumPricer =
            IOptionsPremiumPricer(optionsPremiumPricer);

        // Apply black-scholes formula (from rvol library) to option given its features
        // and get price for 100 contracts denominated in the underlying asset for call option
        // and USDC for put option
        uint256 optionPremium =
            premiumPricer.getPremium(
                newOToken.strikePrice(),
                newOToken.expiryTimestamp(),
                newOToken.isPut()
            );

        // Apply a discount to incentivize arbitraguers
        optionPremium = optionPremium.mul(premiumDiscount).div(
            100 * Vault.PREMIUM_DISCOUNT_MULTIPLIER
        );

        require(
            optionPremium <= type(uint96).max,
            "optionPremium > type(uint96) max value!"
        );
        require(optionPremium > 0, "!optionPremium");

        return optionPremium;
    }

    /**
     * @notice Starts the gnosis auction
     * @param auctionDetails is the struct with all the custom parameters of the auction
     * @return the auction id of the newly created auction
     */
    function startAuction(GnosisAuction.AuctionDetails calldata auctionDetails)
        external
        returns (uint256)
    {
        return GnosisAuction.startAuction(auctionDetails);
    }

    /**
     * @notice Settles the gnosis auction
     * @param gnosisEasyAuction is the contract address of Gnosis easy auction protocol
     * @param auctionID is the auction ID of the gnosis easy auction
     */
    function settleAuction(address gnosisEasyAuction, uint256 auctionID)
        internal
    {
        IGnosisAuction(gnosisEasyAuction).settleAuction(auctionID);
    }

    /**
     * @notice Places a bid in an auction
     * @param bidDetails is the struct with all the details of the
      bid including the auction's id and how much to bid
     */
    function placeBid(GnosisAuction.BidDetails calldata bidDetails)
        external
        returns (
            uint256 sellAmount,
            uint256 buyAmount,
            uint64 userId
        )
    {
        return GnosisAuction.placeBid(bidDetails);
    }

    /**
     * @notice Claims the oTokens belonging to the vault
     * @param auctionSellOrder is the sell order of the bid
     * @param gnosisEasyAuction is the address of the gnosis auction contract
     holding custody to the funds
     * @param counterpartyThetaVault is the address of the counterparty theta
     vault of this delta vault
     */
    function claimAuctionOtokens(
        Vault.AuctionSellOrder calldata auctionSellOrder,
        address gnosisEasyAuction,
        address counterpartyThetaVault
    ) external {
        GnosisAuction.claimAuctionOtokens(
            auctionSellOrder,
            gnosisEasyAuction,
            counterpartyThetaVault
        );
    }

    /**
     * @notice Allocates the vault's minted options to the OptionsPurchaseQueue contract
     * @dev Skipped if the optionsPurchaseQueue doesn't exist
     * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract
     * @param option is the minted option
     * @param optionsAmount is the amount of options minted
     * @param optionAllocation is the maximum % of options to allocate towards the purchase queue (will only allocate
     *  up to the amount that is on the queue)
     * @return allocatedOptions is the amount of options that ended up getting allocated to the OptionsPurchaseQueue
     */
    function allocateOptions(
        address optionsPurchaseQueue,
        address option,
        uint256 optionsAmount,
        uint256 optionAllocation
    ) external returns (uint256 allocatedOptions) {
        // Skip if optionsPurchaseQueue is address(0)
        if (optionsPurchaseQueue != address(0)) {
            allocatedOptions = optionsAmount.mul(optionAllocation).div(
                100 * Vault.OPTION_ALLOCATION_MULTIPLIER
            );
            allocatedOptions = IOptionsPurchaseQueue(optionsPurchaseQueue)
                .getOptionsAllocation(address(this), allocatedOptions);

            if (allocatedOptions != 0) {
                IERC20(option).approve(optionsPurchaseQueue, allocatedOptions);
                IOptionsPurchaseQueue(optionsPurchaseQueue).allocateOptions(
                    allocatedOptions
                );
            }
        }

        return allocatedOptions;
    }

    /**
     * @notice Sell the allocated options to the purchase queue post auction settlement
     * @dev Reverts if the auction hasn't settled yet
     * @param optionsPurchaseQueue is the OptionsPurchaseQueue contract
     * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract
     * @return totalPremiums Total premiums earnt by the vault
     */
    function sellOptionsToQueue(
        address optionsPurchaseQueue,
        address gnosisEasyAuction,
        uint256 optionAuctionID
    ) external returns (uint256) {
        uint256 settlementPrice =
            getAuctionSettlementPrice(gnosisEasyAuction, optionAuctionID);
        require(settlementPrice != 0, "!settlementPrice");

        return
            IOptionsPurchaseQueue(optionsPurchaseQueue).sellToBuyers(
                settlementPrice
            );
    }

    /**
     * @notice Gets the settlement price of a settled auction
     * @param gnosisEasyAuction The address of the Gnosis Easy Auction contract
     * @return settlementPrice Auction settlement price
     */
    function getAuctionSettlementPrice(
        address gnosisEasyAuction,
        uint256 optionAuctionID
    ) public view returns (uint256) {
        bytes32 clearingPriceOrder =
            IGnosisAuction(gnosisEasyAuction)
                .auctionData(optionAuctionID)
                .clearingPriceOrder;

        if (clearingPriceOrder == bytes32(0)) {
            // Current auction hasn't settled yet
            return 0;
        } else {
            // We decode the clearingPriceOrder to find the auction settlement price
            // settlementPrice = clearingPriceOrder.sellAmount / clearingPriceOrder.buyAmount
            return
                (10**Vault.OTOKEN_DECIMALS)
                    .mul(
                    uint96(uint256(clearingPriceOrder)) // sellAmount
                )
                    .div(
                    uint96(uint256(clearingPriceOrder) >> 96) // buyAmount
                );
        }
    }

    /**
     * @notice Verify the constructor params satisfy requirements
     * @param owner is the owner of the vault with critical permissions
     * @param feeRecipient is the address to recieve vault performance and management fees
     * @param performanceFee is the perfomance fee pct.
     * @param tokenName is the name of the token
     * @param tokenSymbol is the symbol of the token
     * @param _vaultParams is the struct with vault general data
     */
    function verifyInitializerParams(
        address owner,
        address keeper,
        address feeRecipient,
        uint256 performanceFee,
        uint256 managementFee,
        string calldata tokenName,
        string calldata tokenSymbol,
        Vault.VaultParams calldata _vaultParams
    ) external pure {
        require(owner != address(0), "!owner");
        require(keeper != address(0), "!keeper");
        require(feeRecipient != address(0), "!feeRecipient");
        require(
            performanceFee < 100 * Vault.FEE_MULTIPLIER,
            "performanceFee >= 100%"
        );
        require(
            managementFee < 100 * Vault.FEE_MULTIPLIER,
            "managementFee >= 100%"
        );
        require(bytes(tokenName).length > 0, "!tokenName");
        require(bytes(tokenSymbol).length > 0, "!tokenSymbol");

        require(_vaultParams.asset != address(0), "!asset");
        require(_vaultParams.underlying != address(0), "!underlying");
        require(_vaultParams.minimumSupply > 0, "!minimumSupply");
        require(_vaultParams.cap > 0, "!cap");
        require(
            _vaultParams.cap > _vaultParams.minimumSupply,
            "cap has to be higher than minimumSupply"
        );
    }

    /**
     * @notice Gets the next option expiry timestamp
     * @param currentOption is the otoken address that the vault is currently writing
     */
    function getNextExpiry(address currentOption)
        internal
        view
        returns (uint256)
    {
        // uninitialized state
        if (currentOption == address(0)) {
            return getNextFriday(block.timestamp);
        }
        uint256 currentExpiry = IOtoken(currentOption).expiryTimestamp();

        // After options expiry if no options are written for >1 week
        // We need to give the ability continue writing options
        if (block.timestamp > currentExpiry + 7 days) {
            return getNextFriday(block.timestamp);
        }
        return getNextFriday(currentExpiry);
    }

    /**
     * @notice Gets the next options expiry timestamp
     * @param timestamp is the expiry timestamp of the current option
     * Reference: https://codereview.stackexchange.com/a/33532
     * Examples:
     * getNextFriday(week 1 thursday) -> week 1 friday
     * getNextFriday(week 1 friday) -> week 2 friday
     * getNextFriday(week 1 saturday) -> week 2 friday
     */
    function getNextFriday(uint256 timestamp) internal pure returns (uint256) {
        // dayOfWeek = 0 (sunday) - 6 (saturday)
        uint256 dayOfWeek = ((timestamp / 1 days) + 4) % 7;
        uint256 nextFriday = timestamp + ((7 + 5 - dayOfWeek) % 7) * 1 days;
        uint256 friday8am = nextFriday - (nextFriday % (24 hours)) + (8 hours);

        // If the passed timestamp is day=Friday hour>8am, we simply increment it by a week to next Friday
        if (timestamp >= friday8am) {
            friday8am += 7 days;
        }
        return friday8am;
    }
}

File 9 of 29 : ShareMath.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {Vault} from "./Vault.sol";

library ShareMath {
    using SafeMath for uint256;

    uint256 internal constant PLACEHOLDER_UINT = 1;

    function assetToShares(
        uint256 assetAmount,
        uint256 assetPerShare,
        uint256 decimals
    ) internal pure returns (uint256) {
        // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet
        // which should never happen.
        // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.
        require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare");

        return assetAmount.mul(10**decimals).div(assetPerShare);
    }

    function sharesToAsset(
        uint256 shares,
        uint256 assetPerShare,
        uint256 decimals
    ) internal pure returns (uint256) {
        // If this throws, it means that vault's roundPricePerShare[currentRound] has not been set yet
        // which should never happen.
        // Has to be larger than 1 because `1` is used in `initRoundPricePerShares` to prevent cold writes.
        require(assetPerShare > PLACEHOLDER_UINT, "Invalid assetPerShare");

        return shares.mul(assetPerShare).div(10**decimals);
    }

    /**
     * @notice Returns the shares unredeemed by the user given their DepositReceipt
     * @param depositReceipt is the user's deposit receipt
     * @param currentRound is the `round` stored on the vault
     * @param assetPerShare is the price in asset per share
     * @param decimals is the number of decimals the asset/shares use
     * @return unredeemedShares is the user's virtual balance of shares that are owed
     */
    function getSharesFromReceipt(
        Vault.DepositReceipt memory depositReceipt,
        uint256 currentRound,
        uint256 assetPerShare,
        uint256 decimals
    ) internal pure returns (uint256 unredeemedShares) {
        if (depositReceipt.round > 0 && depositReceipt.round < currentRound) {
            uint256 sharesFromRound =
                assetToShares(depositReceipt.amount, assetPerShare, decimals);

            return
                uint256(depositReceipt.unredeemedShares).add(sharesFromRound);
        }
        return depositReceipt.unredeemedShares;
    }

    function pricePerShare(
        uint256 totalSupply,
        uint256 totalBalance,
        uint256 pendingAmount,
        uint256 decimals
    ) internal pure returns (uint256) {
        uint256 singleShare = 10**decimals;
        return
            totalSupply > 0
                ? singleShare.mul(totalBalance.sub(pendingAmount)).div(
                    totalSupply
                )
                : singleShare;
    }

    /************************************************
     *  HELPERS
     ***********************************************/

    function assertUint104(uint256 num) internal pure {
        require(num <= type(uint104).max, "Overflow uint104");
    }

    function assertUint128(uint256 num) internal pure {
        require(num <= type(uint128).max, "Overflow uint128");
    }
}

File 10 of 29 : ILiquidityGauge.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

interface ILiquidityGauge {
    function balanceOf(address) external view returns (uint256);

    function deposit(
        uint256 _value,
        address _addr,
        bool _claim_rewards
    ) external;

    function withdraw(uint256 _value) external;
}

File 11 of 29 : IVaultPauser.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

interface IVaultPauser {
    /// @notice pause vault position of an account with max amount
    /// @param _account the address of user
    /// @param _amount amount of shares
    function pausePosition(address _account, uint256 _amount) external;

    /// @notice resume vault position of an account with max amount
    /// @param _vaultAddress the address of vault
    function resumePosition(address _vaultAddress) external;
}

File 12 of 29 : RibbonVault.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    SafeERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {
    ReentrancyGuardUpgradeable
} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {
    OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
    ERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

import {Vault} from "../../../libraries/Vault.sol";
import {VaultLifecycle} from "../../../libraries/VaultLifecycle.sol";
import {ShareMath} from "../../../libraries/ShareMath.sol";
import {IWETH} from "../../../interfaces/IWETH.sol";

contract RibbonVault is
    ReentrancyGuardUpgradeable,
    OwnableUpgradeable,
    ERC20Upgradeable
{
    using SafeERC20 for IERC20;
    using SafeMath for uint256;
    using ShareMath for Vault.DepositReceipt;

    /************************************************
     *  NON UPGRADEABLE STORAGE
     ***********************************************/

    /// @notice Stores the user's pending deposit for the round
    mapping(address => Vault.DepositReceipt) public depositReceipts;

    /// @notice On every round's close, the pricePerShare value of an rTHETA token is stored
    /// This is used to determine the number of shares to be returned
    /// to a user with their DepositReceipt.depositAmount
    mapping(uint256 => uint256) public roundPricePerShare;

    /// @notice Stores pending user withdrawals
    mapping(address => Vault.Withdrawal) public withdrawals;

    /// @notice Vault's parameters like cap, decimals
    Vault.VaultParams public vaultParams;

    /// @notice Vault's lifecycle state like round and locked amounts
    Vault.VaultState public vaultState;

    /// @notice Vault's state of the options sold and the timelocked option
    Vault.OptionState public optionState;

    /// @notice Fee recipient for the performance and management fees
    address public feeRecipient;

    /// @notice role in charge of weekly vault operations such as rollToNextOption and burnRemainingOTokens
    // no access to critical vault changes
    address public keeper;

    /// @notice Performance fee charged on premiums earned in rollToNextOption. Only charged when there is no loss.
    uint256 public performanceFee;

    /// @notice Management fee charged on entire AUM in rollToNextOption. Only charged when there is no loss.
    uint256 public managementFee;

    // Gap is left to avoid storage collisions. Though RibbonVault is not upgradeable, we add this as a safety measure.
    uint256[30] private ____gap;

    // *IMPORTANT* NO NEW STORAGE VARIABLES SHOULD BE ADDED HERE
    // This is to prevent storage collisions. All storage variables should be appended to RibbonThetaVaultStorage
    // or RibbonDeltaVaultStorage instead. Read this documentation to learn more:
    // https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#modifying-your-contracts

    /************************************************
     *  IMMUTABLES & CONSTANTS
     ***********************************************/

    /// @notice WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    address public immutable WETH;

    /// @notice USDC 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    address public immutable USDC;

    /// @notice Deprecated: 15 minute timelock between commitAndClose and rollToNexOption.
    uint256 public constant DELAY = 0;

    /// @notice 7 day period between each options sale.
    uint256 public constant PERIOD = 7 days;

    // Number of weeks per year = 52.142857 weeks * FEE_MULTIPLIER = 52142857
    // Dividing by weeks per year requires doing num.mul(FEE_MULTIPLIER).div(WEEKS_PER_YEAR)
    uint256 private constant WEEKS_PER_YEAR = 52142857;

    // GAMMA_CONTROLLER is the top-level contract in Gamma protocol
    // which allows users to perform multiple actions on their vaults
    // and positions https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/Controller.sol
    address public immutable GAMMA_CONTROLLER;

    // MARGIN_POOL is Gamma protocol's collateral pool.
    // Needed to approve collateral.safeTransferFrom for minting otokens.
    // https://github.com/opynfinance/GammaProtocol/blob/master/contracts/core/MarginPool.sol
    address public immutable MARGIN_POOL;

    // GNOSIS_EASY_AUCTION is Gnosis protocol's contract for initiating auctions and placing bids
    // https://github.com/gnosis/ido-contracts/blob/main/contracts/EasyAuction.sol
    address public immutable GNOSIS_EASY_AUCTION;

    /************************************************
     *  EVENTS
     ***********************************************/

    event Deposit(address indexed account, uint256 amount, uint256 round);

    event InitiateWithdraw(
        address indexed account,
        uint256 shares,
        uint256 round
    );

    event Redeem(address indexed account, uint256 share, uint256 round);

    event ManagementFeeSet(uint256 managementFee, uint256 newManagementFee);

    event PerformanceFeeSet(uint256 performanceFee, uint256 newPerformanceFee);

    event CapSet(uint256 oldCap, uint256 newCap);

    event Withdraw(address indexed account, uint256 amount, uint256 shares);

    event CollectVaultFees(
        uint256 performanceFee,
        uint256 vaultFee,
        uint256 round,
        address indexed feeRecipient
    );

    /************************************************
     *  CONSTRUCTOR & INITIALIZATION
     ***********************************************/

    /**
     * @notice Initializes the contract with immutable variables
     * @param _weth is the Wrapped Ether contract
     * @param _usdc is the USDC contract
     * @param _gammaController is the contract address for opyn actions
     * @param _marginPool is the contract address for providing collateral to opyn
     * @param _gnosisEasyAuction is the contract address that facilitates gnosis auctions
     */
    constructor(
        address _weth,
        address _usdc,
        address _gammaController,
        address _marginPool,
        address _gnosisEasyAuction
    ) {
        require(_weth != address(0), "!_weth");
        require(_usdc != address(0), "!_usdc");
        require(_gnosisEasyAuction != address(0), "!_gnosisEasyAuction");
        require(_gammaController != address(0), "!_gammaController");
        require(_marginPool != address(0), "!_marginPool");

        WETH = _weth;
        USDC = _usdc;
        GAMMA_CONTROLLER = _gammaController;
        MARGIN_POOL = _marginPool;
        GNOSIS_EASY_AUCTION = _gnosisEasyAuction;
    }

    /**
     * @notice Initializes the OptionVault contract with storage variables.
     */
    function baseInitialize(
        address _owner,
        address _keeper,
        address _feeRecipient,
        uint256 _managementFee,
        uint256 _performanceFee,
        string memory _tokenName,
        string memory _tokenSymbol,
        Vault.VaultParams calldata _vaultParams
    ) internal initializer {
        VaultLifecycle.verifyInitializerParams(
            _owner,
            _keeper,
            _feeRecipient,
            _performanceFee,
            _managementFee,
            _tokenName,
            _tokenSymbol,
            _vaultParams
        );

        __ReentrancyGuard_init();
        __ERC20_init(_tokenName, _tokenSymbol);
        __Ownable_init();
        transferOwnership(_owner);

        keeper = _keeper;

        feeRecipient = _feeRecipient;
        performanceFee = _performanceFee;
        managementFee = _managementFee.mul(Vault.FEE_MULTIPLIER).div(
            WEEKS_PER_YEAR
        );
        vaultParams = _vaultParams;

        uint256 assetBalance =
            IERC20(vaultParams.asset).balanceOf(address(this));
        ShareMath.assertUint104(assetBalance);
        vaultState.lastLockedAmount = uint104(assetBalance);

        vaultState.round = 1;
    }

    /**
     * @dev Throws if called by any account other than the keeper.
     */
    modifier onlyKeeper() {
        require(msg.sender == keeper, "!keeper");
        _;
    }

    /************************************************
     *  SETTERS
     ***********************************************/

    /**
     * @notice Sets the new keeper
     * @param newKeeper is the address of the new keeper
     */
    function setNewKeeper(address newKeeper) external onlyOwner {
        require(newKeeper != address(0), "!newKeeper");
        keeper = newKeeper;
    }

    /**
     * @notice Sets the new fee recipient
     * @param newFeeRecipient is the address of the new fee recipient
     */
    function setFeeRecipient(address newFeeRecipient) external onlyOwner {
        require(newFeeRecipient != address(0), "!newFeeRecipient");
        require(newFeeRecipient != feeRecipient, "Must be new feeRecipient");
        feeRecipient = newFeeRecipient;
    }

    /**
     * @notice Sets the management fee for the vault
     * @param newManagementFee is the management fee (6 decimals). ex: 2 * 10 ** 6 = 2%
     */
    function setManagementFee(uint256 newManagementFee) external onlyOwner {
        require(
            newManagementFee < 100 * Vault.FEE_MULTIPLIER,
            "Invalid management fee"
        );

        // We are dividing annualized management fee by num weeks in a year
        uint256 tmpManagementFee =
            newManagementFee.mul(Vault.FEE_MULTIPLIER).div(WEEKS_PER_YEAR);

        emit ManagementFeeSet(managementFee, newManagementFee);

        managementFee = tmpManagementFee;
    }

    /**
     * @notice Sets the performance fee for the vault
     * @param newPerformanceFee is the performance fee (6 decimals). ex: 20 * 10 ** 6 = 20%
     */
    function setPerformanceFee(uint256 newPerformanceFee) external onlyOwner {
        require(
            newPerformanceFee < 100 * Vault.FEE_MULTIPLIER,
            "Invalid performance fee"
        );

        emit PerformanceFeeSet(performanceFee, newPerformanceFee);

        performanceFee = newPerformanceFee;
    }

    /**
     * @notice Sets a new cap for deposits
     * @param newCap is the new cap for deposits
     */
    function setCap(uint256 newCap) external onlyOwner {
        require(newCap > 0, "!newCap");
        ShareMath.assertUint104(newCap);
        emit CapSet(vaultParams.cap, newCap);
        vaultParams.cap = uint104(newCap);
    }

    /************************************************
     *  DEPOSIT & WITHDRAWALS
     ***********************************************/

    /**
     * @notice Deposits ETH into the contract and mint vault shares. Reverts if the asset is not WETH.
     */
    function depositETH() external payable nonReentrant {
        require(vaultParams.asset == WETH, "!WETH");
        require(msg.value > 0, "!value");

        _depositFor(msg.value, msg.sender);

        IWETH(WETH).deposit{value: msg.value}();
    }

    /**
     * @notice Deposits the `asset` from msg.sender.
     * @param amount is the amount of `asset` to deposit
     */
    function deposit(uint256 amount) external nonReentrant {
        require(amount > 0, "!amount");

        _depositFor(amount, msg.sender);

        // An approve() by the msg.sender is required beforehand
        IERC20(vaultParams.asset).safeTransferFrom(
            msg.sender,
            address(this),
            amount
        );
    }

    /**
     * @notice Deposits the `asset` from msg.sender added to `creditor`'s deposit.
     * @notice Used for vault -> vault deposits on the user's behalf
     * @param amount is the amount of `asset` to deposit
     * @param creditor is the address that can claim/withdraw deposited amount
     */
    function depositFor(uint256 amount, address creditor)
        external
        nonReentrant
    {
        require(amount > 0, "!amount");
        require(creditor != address(0));

        _depositFor(amount, creditor);

        // An approve() by the msg.sender is required beforehand
        IERC20(vaultParams.asset).safeTransferFrom(
            msg.sender,
            address(this),
            amount
        );
    }

    /**
     * @notice Mints the vault shares to the creditor
     * @param amount is the amount of `asset` deposited
     * @param creditor is the address to receieve the deposit
     */
    function _depositFor(uint256 amount, address creditor) private {
        uint256 currentRound = vaultState.round;
        uint256 totalWithDepositedAmount = totalBalance().add(amount);

        require(totalWithDepositedAmount <= vaultParams.cap, "Exceed cap");
        require(
            totalWithDepositedAmount >= vaultParams.minimumSupply,
            "Insufficient balance"
        );

        emit Deposit(creditor, amount, currentRound);

        Vault.DepositReceipt memory depositReceipt = depositReceipts[creditor];

        // If we have an unprocessed pending deposit from the previous rounds, we have to process it.
        uint256 unredeemedShares =
            depositReceipt.getSharesFromReceipt(
                currentRound,
                roundPricePerShare[depositReceipt.round],
                vaultParams.decimals
            );

        uint256 depositAmount = amount;

        // If we have a pending deposit in the current round, we add on to the pending deposit
        if (currentRound == depositReceipt.round) {
            uint256 newAmount = uint256(depositReceipt.amount).add(amount);
            depositAmount = newAmount;
        }

        ShareMath.assertUint104(depositAmount);

        depositReceipts[creditor] = Vault.DepositReceipt({
            round: uint16(currentRound),
            amount: uint104(depositAmount),
            unredeemedShares: uint128(unredeemedShares)
        });

        uint256 newTotalPending = uint256(vaultState.totalPending).add(amount);
        ShareMath.assertUint128(newTotalPending);

        vaultState.totalPending = uint128(newTotalPending);
    }

    /**
     * @notice Initiates a withdrawal that can be processed once the round completes
     * @param numShares is the number of shares to withdraw
     */
    function _initiateWithdraw(uint256 numShares) internal {
        require(numShares > 0, "!numShares");

        // We do a max redeem before initiating a withdrawal
        // But we check if they must first have unredeemed shares
        if (
            depositReceipts[msg.sender].amount > 0 ||
            depositReceipts[msg.sender].unredeemedShares > 0
        ) {
            _redeem(0, true);
        }

        // This caches the `round` variable used in shareBalances
        uint256 currentRound = vaultState.round;
        Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];

        bool withdrawalIsSameRound = withdrawal.round == currentRound;

        emit InitiateWithdraw(msg.sender, numShares, currentRound);

        uint256 existingShares = uint256(withdrawal.shares);

        uint256 withdrawalShares;
        if (withdrawalIsSameRound) {
            withdrawalShares = existingShares.add(numShares);
        } else {
            require(existingShares == 0, "Existing withdraw");
            withdrawalShares = numShares;
            withdrawals[msg.sender].round = uint16(currentRound);
        }

        ShareMath.assertUint128(withdrawalShares);
        withdrawals[msg.sender].shares = uint128(withdrawalShares);

        _transfer(msg.sender, address(this), numShares);
    }

    /**
     * @notice Completes a scheduled withdrawal from a past round. Uses finalized pps for the round
     * @return withdrawAmount the current withdrawal amount
     */
    function _completeWithdraw() internal returns (uint256) {
        Vault.Withdrawal storage withdrawal = withdrawals[msg.sender];

        uint256 withdrawalShares = withdrawal.shares;
        uint256 withdrawalRound = withdrawal.round;

        // This checks if there is a withdrawal
        require(withdrawalShares > 0, "Not initiated");

        require(withdrawalRound < vaultState.round, "Round not closed");

        // We leave the round number as non-zero to save on gas for subsequent writes
        withdrawals[msg.sender].shares = 0;
        vaultState.queuedWithdrawShares = uint128(
            uint256(vaultState.queuedWithdrawShares).sub(withdrawalShares)
        );

        uint256 withdrawAmount =
            ShareMath.sharesToAsset(
                withdrawalShares,
                roundPricePerShare[withdrawalRound],
                vaultParams.decimals
            );

        emit Withdraw(msg.sender, withdrawAmount, withdrawalShares);

        _burn(address(this), withdrawalShares);

        require(withdrawAmount > 0, "!withdrawAmount");
        transferAsset(msg.sender, withdrawAmount);

        return withdrawAmount;
    }

    /**
     * @notice Redeems shares that are owed to the account
     * @param numShares is the number of shares to redeem
     */
    function redeem(uint256 numShares) external nonReentrant {
        require(numShares > 0, "!numShares");
        _redeem(numShares, false);
    }

    /**
     * @notice Redeems the entire unredeemedShares balance that is owed to the account
     */
    function maxRedeem() external nonReentrant {
        _redeem(0, true);
    }

    /**
     * @notice Redeems shares that are owed to the account
     * @param numShares is the number of shares to redeem, could be 0 when isMax=true
     * @param isMax is flag for when callers do a max redemption
     */
    function _redeem(uint256 numShares, bool isMax) internal {
        Vault.DepositReceipt memory depositReceipt =
            depositReceipts[msg.sender];

        // This handles the null case when depositReceipt.round = 0
        // Because we start with round = 1 at `initialize`
        uint256 currentRound = vaultState.round;

        uint256 unredeemedShares =
            depositReceipt.getSharesFromReceipt(
                currentRound,
                roundPricePerShare[depositReceipt.round],
                vaultParams.decimals
            );

        numShares = isMax ? unredeemedShares : numShares;
        if (numShares == 0) {
            return;
        }
        require(numShares <= unredeemedShares, "Exceeds available");

        // If we have a depositReceipt on the same round, BUT we have some unredeemed shares
        // we debit from the unredeemedShares, but leave the amount field intact
        // If the round has past, with no new deposits, we just zero it out for new deposits.
        if (depositReceipt.round < currentRound) {
            depositReceipts[msg.sender].amount = 0;
        }

        ShareMath.assertUint128(numShares);
        depositReceipts[msg.sender].unredeemedShares = uint128(
            unredeemedShares.sub(numShares)
        );

        emit Redeem(msg.sender, numShares, depositReceipt.round);

        _transfer(address(this), msg.sender, numShares);
    }

    /************************************************
     *  VAULT OPERATIONS
     ***********************************************/

    /**
     * @notice Helper function that helps to save gas for writing values into the roundPricePerShare map.
     *         Writing `1` into the map makes subsequent writes warm, reducing the gas from 20k to 5k.
     *         Having 1 initialized beforehand will not be an issue as long as we round down share calculations to 0.
     * @param numRounds is the number of rounds to initialize in the map
     */
    function initRounds(uint256 numRounds) external nonReentrant {
        require(numRounds > 0, "!numRounds");

        uint256 _round = vaultState.round;
        for (uint256 i = 0; i < numRounds; i++) {
            uint256 index = _round + i;
            require(roundPricePerShare[index] == 0, "Initialized"); // AVOID OVERWRITING ACTUAL VALUES
            roundPricePerShare[index] = ShareMath.PLACEHOLDER_UINT;
        }
    }

    /**
     * @notice Helper function that performs most administrative tasks
     * such as setting next option, minting new shares, getting vault fees, etc.
     * @param lastQueuedWithdrawAmount is old queued withdraw amount
     * @param currentQueuedWithdrawShares is the queued withdraw shares for the current round
     * @return newOption is the new option address
     * @return lockedBalance is the new balance used to calculate next option purchase size or collateral size
     * @return queuedWithdrawAmount is the new queued withdraw amount for this round
     */
    function _rollToNextOption(
        uint256 lastQueuedWithdrawAmount,
        uint256 currentQueuedWithdrawShares
    )
        internal
        returns (
            address newOption,
            uint256 lockedBalance,
            uint256 queuedWithdrawAmount
        )
    {
        require(block.timestamp >= optionState.nextOptionReadyAt, "!ready");

        newOption = optionState.nextOption;
        require(newOption != address(0), "!nextOption");

        address recipient = feeRecipient;
        uint256 mintShares;
        uint256 performanceFeeInAsset;
        uint256 totalVaultFee;
        {
            uint256 newPricePerShare;
            (
                lockedBalance,
                queuedWithdrawAmount,
                newPricePerShare,
                mintShares,
                performanceFeeInAsset,
                totalVaultFee
            ) = VaultLifecycle.rollover(
                vaultState,
                VaultLifecycle.RolloverParams(
                    vaultParams.decimals,
                    IERC20(vaultParams.asset).balanceOf(address(this)),
                    totalSupply(),
                    lastQueuedWithdrawAmount,
                    performanceFee,
                    managementFee,
                    currentQueuedWithdrawShares
                )
            );

            optionState.currentOption = newOption;
            optionState.nextOption = address(0);

            // Finalize the pricePerShare at the end of the round
            uint256 currentRound = vaultState.round;
            roundPricePerShare[currentRound] = newPricePerShare;

            emit CollectVaultFees(
                performanceFeeInAsset,
                totalVaultFee,
                currentRound,
                recipient
            );

            vaultState.totalPending = 0;
            vaultState.round = uint16(currentRound + 1);
        }

        _mint(address(this), mintShares);

        if (totalVaultFee > 0) {
            transferAsset(payable(recipient), totalVaultFee);
        }

        return (newOption, lockedBalance, queuedWithdrawAmount);
    }

    /**
     * @notice Helper function to make either an ETH transfer or ERC20 transfer
     * @param recipient is the receiving address
     * @param amount is the transfer amount
     */
    function transferAsset(address recipient, uint256 amount) internal {
        address asset = vaultParams.asset;
        if (asset == WETH) {
            IWETH(WETH).withdraw(amount);
            (bool success, ) = recipient.call{value: amount}("");
            require(success, "Transfer failed");
            return;
        }
        IERC20(asset).safeTransfer(recipient, amount);
    }

    /************************************************
     *  GETTERS
     ***********************************************/

    /**
     * @notice Returns the asset balance held on the vault for the account
     * @param account is the address to lookup balance for
     * @return the amount of `asset` custodied by the vault for the user
     */
    function accountVaultBalance(address account)
        external
        view
        returns (uint256)
    {
        uint256 _decimals = vaultParams.decimals;
        uint256 assetPerShare =
            ShareMath.pricePerShare(
                totalSupply(),
                totalBalance(),
                vaultState.totalPending,
                _decimals
            );
        return
            ShareMath.sharesToAsset(shares(account), assetPerShare, _decimals);
    }

    /**
     * @notice Getter for returning the account's share balance including unredeemed shares
     * @param account is the account to lookup share balance for
     * @return the share balance
     */
    function shares(address account) public view returns (uint256) {
        (uint256 heldByAccount, uint256 heldByVault) = shareBalances(account);
        return heldByAccount.add(heldByVault);
    }

    /**
     * @notice Getter for returning the account's share balance split between account and vault holdings
     * @param account is the account to lookup share balance for
     * @return heldByAccount is the shares held by account
     * @return heldByVault is the shares held on the vault (unredeemedShares)
     */
    function shareBalances(address account)
        public
        view
        returns (uint256 heldByAccount, uint256 heldByVault)
    {
        Vault.DepositReceipt memory depositReceipt = depositReceipts[account];

        if (depositReceipt.round < ShareMath.PLACEHOLDER_UINT) {
            return (balanceOf(account), 0);
        }

        uint256 unredeemedShares =
            depositReceipt.getSharesFromReceipt(
                vaultState.round,
                roundPricePerShare[depositReceipt.round],
                vaultParams.decimals
            );

        return (balanceOf(account), unredeemedShares);
    }

    /**
     * @notice The price of a unit of share denominated in the `asset`
     */
    function pricePerShare() external view returns (uint256) {
        return
            ShareMath.pricePerShare(
                totalSupply(),
                totalBalance(),
                vaultState.totalPending,
                vaultParams.decimals
            );
    }

    /**
     * @notice Returns the vault's total balance, including the amounts locked into a short position
     * @return total balance of the vault, including the amounts locked in third party protocols
     */
    function totalBalance() public view returns (uint256) {
        return
            uint256(vaultState.lockedAmount).add(
                IERC20(vaultParams.asset).balanceOf(address(this))
            );
    }

    /**
     * @notice Returns the token decimals
     */
    function decimals() public view override returns (uint8) {
        return vaultParams.decimals;
    }

    function cap() external view returns (uint256) {
        return vaultParams.cap;
    }

    function nextOptionReadyAt() external view returns (uint256) {
        return optionState.nextOptionReadyAt;
    }

    function currentOption() external view returns (address) {
        return optionState.currentOption;
    }

    function nextOption() external view returns (address) {
        return optionState.nextOption;
    }

    function totalPending() external view returns (uint256) {
        return vaultState.totalPending;
    }
}

File 13 of 29 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 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 14 of 29 : DSMath.sol
// SPDX-License-Identifier: MIT

/// math.sol -- mixin for inline numerical wizardry

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >0.4.13;

library DSMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }

    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x - y) <= x, "ds-math-sub-underflow");
    }

    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x <= y ? x : y;
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x >= y ? x : y;
    }

    function imin(int256 x, int256 y) internal pure returns (int256 z) {
        return x <= y ? x : y;
    }

    function imax(int256 x, int256 y) internal pure returns (int256 z) {
        return x >= y ? x : y;
    }

    uint256 constant WAD = 10**18;
    uint256 constant RAY = 10**27;

    //rounds to zero if x*y < WAD / 2
    function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }

    //rounds to zero if x*y < WAD / 2
    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }

    //rounds to zero if x*y < WAD / 2
    function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, WAD), y / 2) / y;
    }

    //rounds to zero if x*y < RAY / 2
    function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, RAY), y / 2) / y;
    }

    // This famous algorithm is called "exponentiation by squaring"
    // and calculates x^n with x as fixed-point and n as regular unsigned.
    //
    // It's O(log n), instead of O(n) for naive repeated multiplication.
    //
    // These facts are why it works:
    //
    //  If n is even, then x^n = (x^2)^(n/2).
    //  If n is odd,  then x^n = x * x^(n-1),
    //   and applying the equation for even x gives
    //    x^n = x * (x^2)^((n-1) / 2).
    //
    //  Also, EVM division is flooring and
    //    floor[(n-1) / 2] = floor[n / 2].
    //
    function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
        z = n % 2 != 0 ? x : RAY;

        for (n /= 2; n != 0; n /= 2) {
            x = rmul(x, x);

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }
}

File 15 of 29 : IGnosisAuction.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

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

library AuctionType {
    struct AuctionData {
        IERC20 auctioningToken;
        IERC20 biddingToken;
        uint256 orderCancellationEndDate;
        uint256 auctionEndDate;
        bytes32 initialAuctionOrder;
        uint256 minimumBiddingAmountPerOrder;
        uint256 interimSumBidAmount;
        bytes32 interimOrder;
        bytes32 clearingPriceOrder;
        uint96 volumeClearingPriceOrder;
        bool minFundingThresholdNotReached;
        bool isAtomicClosureAllowed;
        uint256 feeNumerator;
        uint256 minFundingThreshold;
    }
}

interface IGnosisAuction {
    function initiateAuction(
        address _auctioningToken,
        address _biddingToken,
        uint256 orderCancellationEndDate,
        uint256 auctionEndDate,
        uint96 _auctionedSellAmount,
        uint96 _minBuyAmount,
        uint256 minimumBiddingAmountPerOrder,
        uint256 minFundingThreshold,
        bool isAtomicClosureAllowed,
        address accessManagerContract,
        bytes memory accessManagerContractData
    ) external returns (uint256);

    function auctionCounter() external view returns (uint256);

    function auctionData(uint256 auctionId)
        external
        view
        returns (AuctionType.AuctionData memory);

    function auctionAccessManager(uint256 auctionId)
        external
        view
        returns (address);

    function auctionAccessData(uint256 auctionId)
        external
        view
        returns (bytes memory);

    function FEE_DENOMINATOR() external view returns (uint256);

    function feeNumerator() external view returns (uint256);

    function settleAuction(uint256 auctionId) external returns (bytes32);

    function placeSellOrders(
        uint256 auctionId,
        uint96[] memory _minBuyAmounts,
        uint96[] memory _sellAmounts,
        bytes32[] memory _prevSellOrders,
        bytes calldata allowListCallData
    ) external returns (uint64);

    function claimFromParticipantOrder(
        uint256 auctionId,
        bytes32[] memory orders
    ) external returns (uint256, uint256);
}

File 16 of 29 : GammaInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

library GammaTypes {
    // vault is a struct of 6 arrays that describe a position a user has, a user can have multiple vaults.
    struct Vault {
        // addresses of oTokens a user has shorted (i.e. written) against this vault
        address[] shortOtokens;
        // addresses of oTokens a user has bought and deposited in this vault
        // user can be long oTokens without opening a vault (e.g. by buying on a DEX)
        // generally, long oTokens will be 'deposited' in vaults to act as collateral
        // in order to write oTokens against (i.e. in spreads)
        address[] longOtokens;
        // addresses of other ERC-20s a user has deposited as collateral in this vault
        address[] collateralAssets;
        // quantity of oTokens minted/written for each oToken address in shortOtokens
        uint256[] shortAmounts;
        // quantity of oTokens owned and held in the vault for each oToken address in longOtokens
        uint256[] longAmounts;
        // quantity of ERC-20 deposited as collateral in the vault for each ERC-20 address in collateralAssets
        uint256[] collateralAmounts;
    }
}

interface IOtoken {
    function underlyingAsset() external view returns (address);

    function strikeAsset() external view returns (address);

    function collateralAsset() external view returns (address);

    function strikePrice() external view returns (uint256);

    function expiryTimestamp() external view returns (uint256);

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

interface IOtokenFactory {
    function getOtoken(
        address _underlyingAsset,
        address _strikeAsset,
        address _collateralAsset,
        uint256 _strikePrice,
        uint256 _expiry,
        bool _isPut
    ) external view returns (address);

    function createOtoken(
        address _underlyingAsset,
        address _strikeAsset,
        address _collateralAsset,
        uint256 _strikePrice,
        uint256 _expiry,
        bool _isPut
    ) external returns (address);

    function getTargetOtokenAddress(
        address _underlyingAsset,
        address _strikeAsset,
        address _collateralAsset,
        uint256 _strikePrice,
        uint256 _expiry,
        bool _isPut
    ) external view returns (address);

    event OtokenCreated(
        address tokenAddress,
        address creator,
        address indexed underlying,
        address indexed strike,
        address indexed collateral,
        uint256 strikePrice,
        uint256 expiry,
        bool isPut
    );
}

interface IController {
    // possible actions that can be performed
    enum ActionType {
        OpenVault,
        MintShortOption,
        BurnShortOption,
        DepositLongOption,
        WithdrawLongOption,
        DepositCollateral,
        WithdrawCollateral,
        SettleVault,
        Redeem,
        Call,
        Liquidate
    }

    struct ActionArgs {
        // type of action that is being performed on the system
        ActionType actionType;
        // address of the account owner
        address owner;
        // address which we move assets from or to (depending on the action type)
        address secondAddress;
        // asset that is to be transfered
        address asset;
        // index of the vault that is to be modified (if any)
        uint256 vaultId;
        // amount of asset that is to be transfered
        uint256 amount;
        // each vault can hold multiple short / long / collateral assets
        // but we are restricting the scope to only 1 of each in this version
        // in future versions this would be the index of the short / long / collateral asset that needs to be modified
        uint256 index;
        // any other data that needs to be passed in for arbitrary function calls
        bytes data;
    }

    struct RedeemArgs {
        // address to which we pay out the oToken proceeds
        address receiver;
        // oToken that is to be redeemed
        address otoken;
        // amount of oTokens that is to be redeemed
        uint256 amount;
    }

    function getPayout(address _otoken, uint256 _amount)
        external
        view
        returns (uint256);

    function operate(ActionArgs[] calldata _actions) external;

    function getAccountVaultCounter(address owner)
        external
        view
        returns (uint256);

    function oracle() external view returns (address);

    function getVault(address _owner, uint256 _vaultId)
        external
        view
        returns (GammaTypes.Vault memory);

    function getProceed(address _owner, uint256 _vaultId)
        external
        view
        returns (uint256);

    function isSettlementAllowed(
        address _underlying,
        address _strike,
        address _collateral,
        uint256 _expiry
    ) external view returns (bool);
}

interface IOracle {
    function setAssetPricer(address _asset, address _pricer) external;

    function updateAssetPricer(address _asset, address _pricer) external;

    function getPrice(address _asset) external view returns (uint256);
}

File 17 of 29 : IRibbon.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;
import {Vault} from "../libraries/Vault.sol";

interface IRibbonVault {
    function deposit(uint256 amount) external;

    function depositETH() external payable;

    function cap() external view returns (uint256);

    function depositFor(uint256 amount, address creditor) external;

    function vaultParams() external view returns (Vault.VaultParams memory);
}

interface IStrikeSelection {
    function getStrikePrice(uint256 expiryTimestamp, bool isPut)
        external
        view
        returns (uint256, uint256);

    function delta() external view returns (uint256);
}

interface IOptionsPremiumPricer {
    function getPremium(
        uint256 strikePrice,
        uint256 timeToExpiry,
        bool isPut
    ) external view returns (uint256);

    function getPremiumInStables(
        uint256 strikePrice,
        uint256 timeToExpiry,
        bool isPut
    ) external view returns (uint256);

    function getOptionDelta(
        uint256 spotPrice,
        uint256 strikePrice,
        uint256 volatility,
        uint256 expiryTimestamp
    ) external view returns (uint256 delta);

    function getUnderlyingPrice() external view returns (uint256);

    function priceOracle() external view returns (address);

    function volatilityOracle() external view returns (address);

    function optionId() external view returns (bytes32);
}

File 18 of 29 : IRibbonThetaVault.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

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

interface IRibbonThetaVault {
    function currentOption() external view returns (address);

    function nextOption() external view returns (address);

    function vaultParams() external view returns (Vault.VaultParams memory);

    function vaultState() external view returns (Vault.VaultState memory);

    function optionState() external view returns (Vault.OptionState memory);

    function optionAuctionID() external view returns (uint256);

    function pricePerShare() external view returns (uint256);

    function roundPricePerShare(uint256) external view returns (uint256);

    function depositFor(uint256 amount, address creditor) external;

    function initiateWithdraw(uint256 numShares) external;

    function completeWithdraw() external;

    function maxRedeem() external;

    function depositYieldTokenFor(uint256 amount, address creditor) external;

    function symbol() external view returns (string calldata);
}

File 19 of 29 : IERC20Detailed.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

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

interface IERC20Detailed is IERC20 {
    function decimals() external view returns (uint8);

    function symbol() external view returns (string calldata);

    function name() external view returns (string calldata);
}

File 20 of 29 : IOptionsPurchaseQueue.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IOptionsPurchaseQueue {
    /**
     * @dev Contains purchase request info
     * @param optionsAmount Amount of options to purchase
     * @param premiums Total premiums the buyer is spending to purchase the options (optionsAmount * ceilingPrice)
     *  We need to track the premiums here since the ceilingPrice could change between the time the purchase was
     *  requested and when the options are sold
     * @param buyer The buyer requesting this purchase
     */
    struct Purchase {
        uint128 optionsAmount; // Slot 0
        uint128 premiums;
        address buyer; // Slot 1
    }

    function purchases(address, uint256)
        external
        view
        returns (
            uint128,
            uint128,
            address
        );

    function totalOptionsAmount(address) external view returns (uint256);

    function vaultAllocatedOptions(address) external view returns (uint256);

    function whitelistedBuyer(address) external view returns (bool);

    function minPurchaseAmount(address) external view returns (uint256);

    function ceilingPrice(address) external view returns (uint256);

    function getPurchases(address vault)
        external
        view
        returns (Purchase[] memory);

    function getPremiums(address vault, uint256 optionsAmount)
        external
        view
        returns (uint256);

    function getOptionsAllocation(address vault, uint256 allocatedOptions)
        external
        view
        returns (uint256);

    function requestPurchase(address vault, uint256 optionsAmount)
        external
        returns (uint256);

    function allocateOptions(uint256 allocatedOptions)
        external
        returns (uint256);

    function sellToBuyers(uint256 settlementPrice) external returns (uint256);

    function cancelAllPurchases(address vault) external;

    function addWhitelist(address buyer) external;

    function removeWhitelist(address buyer) external;

    function setCeilingPrice(address vault, uint256 price) external;

    function setMinPurchaseAmount(address vault, uint256 amount) external;
}

File 21 of 29 : SupportsNonCompliantERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    SafeERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * This library supports ERC20s that have quirks in their behavior.
 * One such ERC20 is USDT, which requires allowance to be 0 before calling approve.
 * We plan to update this library with ERC20s that display such idiosyncratic behavior.
 */
library SupportsNonCompliantERC20 {
    address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;

    function safeApproveNonCompliant(
        IERC20 token,
        address spender,
        uint256 amount
    ) internal {
        if (address(token) == USDT) {
            SafeERC20.safeApprove(token, spender, 0);
        }
        SafeERC20.safeApprove(token, spender, amount);
    }
}

File 22 of 29 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal initializer {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal initializer {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
    uint256[49] private __gap;
}

File 23 of 29 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal initializer {
        _setOwner(_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 {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
    uint256[49] private __gap;
}

File 24 of 29 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
        __Context_init_unchained();
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
    uint256[45] private __gap;
}

File 25 of 29 : IWETH.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;

interface IWETH {
    function deposit() external payable;

    function withdraw(uint256) external;

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function decimals() external view returns (uint256);
}

File 26 of 29 : Initializable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        require(_initializing || !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }
}

File 27 of 29 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal initializer {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal initializer {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private __gap;
}

File 28 of 29 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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 29 of 29 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @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": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "contracts/libraries/VaultLifecycle.sol": {
      "VaultLifecycle": "0x25af99ff07575ac55fb3c2d1e6b920ba3bf67b40"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_usdc","type":"address"},{"internalType":"address","name":"_oTokenFactory","type":"address"},{"internalType":"address","name":"_gammaController","type":"address"},{"internalType":"address","name":"_marginPool","type":"address"},{"internalType":"address","name":"_gnosisEasyAuction","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"auctionDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newAuctionDuration","type":"uint256"}],"name":"AuctionDurationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"CapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"options","type":"address"},{"indexed":false,"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"CloseShort","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"CollectVaultFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auctioningToken","type":"address"},{"indexed":true,"internalType":"address","name":"biddingToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"auctionCounter","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"InitiateGnosisAuction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"InitiateWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"InstantWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"managementFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"ManagementFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"strikePrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"delta","type":"uint256"}],"name":"NewOptionStrikeSelected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"options","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"OpenShort","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"performanceFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"PerformanceFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"premiumDiscount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPremiumDiscount","type":"uint256"}],"name":"PremiumDiscountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAMMA_CONTROLLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GNOSIS_EASY_AUCTION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MARGIN_POOL","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OTOKEN_FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"accountVaultBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"auctionDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnRemainingOTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"commitAndClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completeWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentOption","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentOtokenPremium","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentQueuedWithdrawShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"creditor","type":"address"}],"name":"depositFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositReceipts","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint104","name":"amount","type":"uint104"},{"internalType":"uint128","name":"unredeemedShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numRounds","type":"uint256"}],"name":"initRounds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint256","name":"_managementFee","type":"uint256"},{"internalType":"uint256","name":"_performanceFee","type":"uint256"},{"internalType":"string","name":"_tokenName","type":"string"},{"internalType":"string","name":"_tokenSymbol","type":"string"},{"internalType":"address","name":"_optionsPremiumPricer","type":"address"},{"internalType":"address","name":"_strikeSelection","type":"address"},{"internalType":"uint32","name":"_premiumDiscount","type":"uint32"},{"internalType":"uint256","name":"_auctionDuration","type":"uint256"}],"internalType":"struct RibbonThetaVault.InitParams","name":"_initParams","type":"tuple"},{"components":[{"internalType":"bool","name":"isPut","type":"bool"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint56","name":"minimumSupply","type":"uint56"},{"internalType":"uint104","name":"cap","type":"uint104"}],"internalType":"struct Vault.VaultParams","name":"_vaultParams","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"initiateWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastQueuedWithdrawAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastStrikeOverrideRound","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityGauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managementFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextOption","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextOptionReadyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionAuctionID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionState","outputs":[{"internalType":"address","name":"nextOption","type":"address"},{"internalType":"address","name":"currentOption","type":"address"},{"internalType":"uint32","name":"nextOptionReadyAt","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionsPremiumPricer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optionsPurchaseQueue","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"overriddenStrikePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"performanceFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"premiumDiscount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"recoverTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollToNextOption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"roundPricePerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sellOptionsToQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newAuctionDuration","type":"uint256"}],"name":"setAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLiquidityGauge","type":"address"}],"name":"setLiquidityGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newManagementFee","type":"uint256"}],"name":"setManagementFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minPrice","type":"uint256"}],"name":"setMinPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newKeeper","type":"address"}],"name":"setNewKeeper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOptionsPremiumPricer","type":"address"}],"name":"setOptionsPremiumPricer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOptionsPurchaseQueue","type":"address"}],"name":"setOptionsPurchaseQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPerformanceFee","type":"uint256"}],"name":"setPerformanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPremiumDiscount","type":"uint256"}],"name":"setPremiumDiscount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"strikePrice","type":"uint128"}],"name":"setStrikePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newStrikeSelection","type":"address"}],"name":"setStrikeSelection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newVaultPauser","type":"address"}],"name":"setVaultPauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"shareBalances","outputs":[{"internalType":"uint256","name":"heldByAccount","type":"uint256"},{"internalType":"uint256","name":"heldByVault","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numShares","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strikeSelection","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPending","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultParams","outputs":[{"internalType":"bool","name":"isPut","type":"bool"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint56","name":"minimumSupply","type":"uint56"},{"internalType":"uint104","name":"cap","type":"uint104"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultPauser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultState","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint104","name":"lockedAmount","type":"uint104"},{"internalType":"uint104","name":"lastLockedAmount","type":"uint104"},{"internalType":"uint128","name":"totalPending","type":"uint128"},{"internalType":"uint128","name":"queuedWithdrawShares","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawInstantly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawals","outputs":[{"internalType":"uint16","name":"round","type":"uint16"},{"internalType":"uint128","name":"shares","type":"uint128"}],"stateMutability":"view","type":"function"}]

6101406040523480156200001257600080fd5b506040516200619838038062006198833981016040819052620000359162000261565b85858484846001600160a01b0385166200007f5760405162461bcd60e51b8152602060048201526006602482015265042beeecae8d60d31b60448201526064015b60405180910390fd5b6001600160a01b038416620000c05760405162461bcd60e51b8152602060048201526006602482015265215f7573646360d01b604482015260640162000076565b6001600160a01b038116620001185760405162461bcd60e51b815260206004820152601360248201527f215f676e6f7369734561737941756374696f6e00000000000000000000000000604482015260640162000076565b6001600160a01b038316620001645760405162461bcd60e51b815260206004820152601160248201527010afb3b0b6b6b0a1b7b73a3937b63632b960791b604482015260640162000076565b6001600160a01b038216620001ab5760405162461bcd60e51b815260206004820152600c60248201526b0857db585c99da5b941bdbdb60a21b604482015260640162000076565b6001600160601b0319606095861b811660805293851b841660a05291841b831660c052831b821660e05290911b16610100526001600160a01b038416620002275760405162461bcd60e51b815260206004820152600f60248201526e215f6f546f6b656e466163746f727960881b604482015260640162000076565b50505060601b6001600160601b0319166101205250620002e19050565b80516001600160a01b03811681146200025c57600080fd5b919050565b60008060008060008060c087890312156200027a578182fd5b620002858762000244565b9550620002956020880162000244565b9450620002a56040880162000244565b9350620002b56060880162000244565b9250620002c56080880162000244565b9150620002d560a0880162000244565b90509295509295509295565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c6101205160601c615e0162000397600039600081816110000152612e67015260008181610a2f015281816120b20152613f4b01526000818161094d015261294e0152600081816105ad01528181611f4b0152818161292601526148ae015260008181610bc00152612e8c015260008181610ea001528181613137015281816131e3015281816137ef015261382c0152615e016000f3fe6080604052600436106104c05760003560e01c806387153eb111610276578063b4d1d7951161014f578063e73c63d5116100c1578063f656ba5111610085578063f656ba51146110db578063f756fa2114611110578063f957a06714611125578063f9a0be6814611145578063fba7dc6114611166578063fe56e2321461118757600080fd5b8063e73c63d514611068578063e74b981b1461107e578063ea00b1ca1461109e578063f2fde38b146110b3578063f6326fb3146110d357600080fd5b8063d13f1b3e11610113578063d13f1b3e14610f78578063d164cc1514610f98578063d5f2638214610fb8578063db006a7514610fce578063db43e86214610fee578063dd62ed3e1461102257600080fd5b8063b4d1d79514610ef7578063b6b55f2514610f0e578063b9f8092b14610f2e578063ca59409414610f43578063ce7c2ac214610f5857600080fd5b8063a285c9e8116101e8578063a6f7f5d6116101ac578063a6f7f5d614610e38578063a9059cbb14610e4e578063aced166114610e6e578063ad5c464814610e8e578063ad7a672f14610ec2578063afa6626414610ed757600080fd5b8063a285c9e814610d98578063a2db9d8314610dba578063a457c2d714610dd8578063a497e67414610df8578063a694fc3a14610e1857600080fd5b8063947061b51161023a578063947061b514610c1e57806395d89b4114610c3f57806399530b0614610c545780639be43daa14610c695780639fcc2d7514610c89578063a083ff1714610d3557600080fd5b806387153eb114610b6b5780638778878214610b9857806389a3027114610bae5780638b10cc7c14610be25780638da5cb5b14610c0057600080fd5b806347786d37116103a85780636719b2ee1161031a57806370897b23116102de57806370897b2314610a6657806370a0823114610a86578063715018a614610abc5780637a9262a214610ad15780637e108d5214610b3557806383536ff314610b5557600080fd5b80636719b2ee1461096f57806369b41170146109f35780636b64c76914610a085780636c10f66c14610a1d5780636f31ab3414610a5157600080fd5b8063573f0d6e1161036c578063573f0d6e146108b157806358358207146108d15780635ea8cd12146108f1578063600a2cfb14610911578063611b034d14610926578063650cce8a1461093b57600080fd5b806347786d37146108245780634ac88cb6146108445780634b2431d914610864578063503c70aa1461087b57806355489bb21461089157600080fd5b806330630da4116104415780633ec143d3116104055780633ec143d3146107625780633f23bb73146107905780633f90916a146107b0578063432833a6146107ce5780634603c0aa146107e4578063469048401461080457600080fd5b806330630da4146106ba578063313ce567146106da578063355274ea1461070457806336efd16f14610722578063395093511461074257600080fd5b806318160ddd1161048857806318160ddd146105865780631a92f6c21461059b57806323b872dd146105e75780632728f333146106075780632775d01c1461069a57600080fd5b8063048bf085146104c5578063056097ac146104e757806306fdde0314610507578063095ea7b3146105325780630cbf54c814610562575b600080fd5b3480156104d157600080fd5b506104e56104e036600461548a565b6111a7565b005b3480156104f357600080fd5b506104e56105023660046154a6565b6111fd565b34801561051357600080fd5b5061051c6113cf565b6040516105299190615821565b60405180910390f35b34801561053e57600080fd5b5061055261054d36600461551e565b611461565b6040519015158152602001610529565b34801561056e57600080fd5b5061057860fb5481565b604051908152602001610529565b34801561059257600080fd5b50609954610578565b3480156105a757600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610529565b3480156105f357600080fd5b506105526106023660046154de565b611478565b34801561061357600080fd5b5060cf5460d0546106559161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a001610529565b3480156106a657600080fd5b506104e56106b536600461561e565b611524565b3480156106c657600080fd5b506104e56106d536600461548a565b6116c7565b3480156106e657600080fd5b5060cc54610100900460ff1660405160ff9091168152602001610529565b34801561071057600080fd5b5060ce546001600160681b0316610578565b34801561072e57600080fd5b506104e561073d36600461564e565b61175f565b34801561074e57600080fd5b5061055261075d36600461551e565b6117e7565b34801561076e57600080fd5b5060f95461077d9061ffff1681565b60405161ffff9091168152602001610529565b34801561079c57600080fd5b506105786107ab36600461548a565b611823565b3480156107bc57600080fd5b5060d0546001600160801b0316610578565b3480156107da57600080fd5b5061057860fc5481565b3480156107f057600080fd5b506104e56107ff36600461548a565b611876565b34801561081057600080fd5b5060d3546105cf906001600160a01b031681565b34801561083057600080fd5b506104e561083f36600461561e565b611918565b34801561085057600080fd5b506104e561085f36600461548a565b6119f3565b34801561087057600080fd5b506105786101025481565b34801561088757600080fd5b5061057860fd5481565b34801561089d57600080fd5b506104e56108ac3660046155f7565b611a40565b3480156108bd57600080fd5b506104e56108cc36600461548a565b611ad8565b3480156108dd57600080fd5b506104e56108ec36600461559b565b611b25565b3480156108fd57600080fd5b506104e561090c36600461561e565b611e74565b34801561091d57600080fd5b506104e5611edf565b34801561093257600080fd5b506104e561203a565b34801561094757600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b34801561097b57600080fd5b506109c261098a36600461548a565b60c96020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b031690820152606001610529565b3480156109ff57600080fd5b50610578600081565b348015610a1457600080fd5b506104e5612154565b348015610a2957600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610a5d57600080fd5b506104e56121b4565b348015610a7257600080fd5b506104e5610a8136600461561e565b6121eb565b348015610a9257600080fd5b50610578610aa136600461548a565b6001600160a01b031660009081526097602052604090205490565b348015610ac857600080fd5b506104e56122b2565b348015610add57600080fd5b50610b13610aec36600461548a565b60cb6020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b03909116602083015201610529565b348015610b4157600080fd5b506104e5610b5036600461561e565b6122e8565b348015610b6157600080fd5b5061057860f75481565b348015610b7757600080fd5b50610578610b8636600461561e565b60ca6020526000908152604090205481565b348015610ba457600080fd5b5061057860d55481565b348015610bba57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610bee57600080fd5b5060d2546001600160a01b03166105cf565b348015610c0c57600080fd5b506065546001600160a01b03166105cf565b348015610c2a57600080fd5b50610103546105cf906001600160a01b031681565b348015610c4b57600080fd5b5061051c612332565b348015610c6057600080fd5b50610578612341565b348015610c7557600080fd5b506104e5610c8436600461561e565b61237b565b348015610c9557600080fd5b5060cc5460cd5460ce54610ce59260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c001610529565b348015610d4157600080fd5b5060d15460d254610d6c916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff1690820152606001610529565b348015610da457600080fd5b5060d254600160a01b900463ffffffff16610578565b348015610dc657600080fd5b5060d1546001600160a01b03166105cf565b348015610de457600080fd5b50610552610df336600461551e565b612475565b348015610e0457600080fd5b506104e5610e1336600461561e565b61250e565b348015610e2457600080fd5b506104e5610e3336600461561e565b6125cb565b348015610e4457600080fd5b5061057860d65481565b348015610e5a57600080fd5b50610552610e6936600461551e565b6126c9565b348015610e7a57600080fd5b5060d4546105cf906001600160a01b031681565b348015610e9a57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b348015610ece57600080fd5b506105786126d6565b348015610ee357600080fd5b5060f5546105cf906001600160a01b031681565b348015610f0357600080fd5b5061057862093a8081565b348015610f1a57600080fd5b506104e5610f2936600461561e565b612775565b348015610f3a57600080fd5b506104e56127e2565b348015610f4f57600080fd5b506104e5612ab3565b348015610f6457600080fd5b50610578610f7336600461548a565b612b56565b348015610f8457600080fd5b506104e5610f9336600461561e565b612b73565b348015610fa457600080fd5b506104e5610fb336600461548a565b612c38565b348015610fc457600080fd5b5061057860f85481565b348015610fda57600080fd5b506104e5610fe936600461561e565b612cc7565b348015610ffa57600080fd5b506105cf7f000000000000000000000000000000000000000000000000000000000000000081565b34801561102e57600080fd5b5061057861103d3660046154a6565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b34801561107457600080fd5b5061057860fa5481565b34801561108a57600080fd5b506104e561109936600461548a565b612d34565b3480156110aa57600080fd5b506104e5612e27565b3480156110bf57600080fd5b506104e56110ce36600461548a565b61306f565b6104e561310a565b3480156110e757600080fd5b506110fb6110f636600461548a565b61325b565b60408051928352602083019190915201610529565b34801561111c57600080fd5b506104e561333e565b34801561113157600080fd5b5060f6546105cf906001600160a01b031681565b34801561115157600080fd5b50610100546105cf906001600160a01b031681565b34801561117257600080fd5b50610101546105cf906001600160a01b031681565b34801561119357600080fd5b506104e56111a236600461561e565b613393565b6065546001600160a01b031633146111da5760405162461bcd60e51b81526004016111d1906158a3565b60405180910390fd5b61010080546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146112275760405162461bcd60e51b81526004016111d1906158a3565b60cc546001600160a01b038381166201000090920416141561128b5760405162461bcd60e51b815260206004820152601b60248201527f5661756c74206173736574206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b0382163014156112e45760405162461bcd60e51b815260206004820152601b60248201527f5661756c74207368617265206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b03811630141561133d5760405162461bcd60e51b815260206004820152601960248201527f526563697069656e742063616e6e6f74206265207661756c740000000000000060448201526064016111d1565b6040516370a0823160e01b81523060048201526113cb9082906001600160a01b038516906370a082319060240160206040518083038186803b15801561138257600080fd5b505afa158015611396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ba9190615636565b6001600160a01b0385169190613474565b5050565b6060609a80546113de90615bcb565b80601f016020809104026020016040519081016040528092919081815260200182805461140a90615bcb565b80156114575780601f1061142c57610100808354040283529160200191611457565b820191906000526020600020905b81548152906001019060200180831161143a57829003601f168201915b5050505050905090565b600061146e3384846134d7565b5060015b92915050565b60006114858484846135fc565b6001600160a01b03841660009081526098602090815260408083203384529091529020548281101561150a5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084016111d1565b61151785338584036134d7565b60019150505b9392505050565b600260015414156115475760405162461bcd60e51b81526004016111d1906158f9565b600260015533600090815260c96020526040902060cf5461ffff168261157f5760405162461bcd60e51b81526004016111d1906158d8565b815461ffff1681146115c35760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b60448201526064016111d1565b81546201000090046001600160681b0316838110156116145760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b60448201526064016111d1565b61161e81856137cc565b83546001600160681b0391909116620100000262010000600160781b031990911617835560d054611658906001600160801b0316856137cc565b60d080546001600160801b0319166001600160801b0392909216919091179055604080518581526020810184905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a26116bd33856137d8565b5050600180555050565b6065546001600160a01b031633146116f15760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b03811661173d5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b60448201526064016111d1565b60f680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156117825760405162461bcd60e51b81526004016111d1906158f9565b6002600155816117a45760405162461bcd60e51b81526004016111d1906158d8565b6001600160a01b0381166117b757600080fd5b6117c18282613939565b60cc546117df906201000090046001600160a01b0316333085613bcb565b505060018055565b3360008181526098602090815260408083206001600160a01b0387168452909152812054909161146e91859061181e908690615a46565b6134d7565b60cc5460009060ff610100909104168161185961183f60995490565b6118476126d6565b60d0546001600160801b031685613c03565b905061186e61186785612b56565b8284613c43565b949350505050565b6065546001600160a01b031633146118a05760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166118f65760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d507269636572000000000000000060448201526064016111d1565b60f580546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146119425760405162461bcd60e51b81526004016111d1906158a3565b6000811161197c5760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b60448201526064016111d1565b61198581613ca5565b60ce54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160ce80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b6065546001600160a01b03163314611a1d5760405162461bcd60e51b81526004016111d1906158a3565b61010180546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b03163314611a6a5760405162461bcd60e51b81526004016111d1906158a3565b6000816001600160801b031611611ab25760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b60448201526064016111d1565b6001600160801b031660fa5560cf5460f9805461ffff191661ffff909216919091179055565b6065546001600160a01b03163314611b025760405162461bcd60e51b81526004016111d1906158a3565b61010380546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff1680611b3e575060005460ff16155b611b5a5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015611b7c576000805461ffff19166101011790555b611c41611b8c602085018561548a565b611b9c604086016020870161548a565b611bac606087016040880161548a565b60608701356080880135611bc360a08a018a6159fa565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c059250505060c08b018b6159fa565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250613cef915050565b6000611c54610100850160e0860161548a565b6001600160a01b03161415611ca45760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b60448201526064016111d1565b6000611cb86101208501610100860161548a565b6001600160a01b03161415611d035760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b60448201526064016111d1565b6000611d17610140850161012086016156bb565b63ffffffff16118015611d4b5750611d31600a6064615b69565b611d43610140850161012086016156bb565b63ffffffff16105b611d8b5760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b60448201526064016111d1565b61012c8361014001351015611dd65760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b60448201526064016111d1565b611de7610100840160e0850161548a565b60f580546001600160a01b0319166001600160a01b0392909216919091179055611e196101208401610100850161548a565b60f680546001600160a01b0319166001600160a01b0392909216919091179055611e4b610140840161012085016156bb565b63ffffffff1660f75561014083013560fb558015611e6f576000805461ff00191690555b505050565b60d4546001600160a01b03163314611e9e5760405162461bcd60e51b81526004016111d190615834565b60008111611eda5760405162461bcd60e51b8152602060048201526009602482015268216d696e507269636560b81b60448201526064016111d1565b60f855565b60d4546001600160a01b03163314611f095760405162461bcd60e51b81526004016111d190615834565b60026001541415611f2c5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d2546040516358ffbb3d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015290911660248201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b40906358ffbb3d9060440160206040518083038186803b158015611fb357600080fd5b505af4158015611fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611feb9190615636565b60cf5490915061200a906201000090046001600160681b0316826137cc565b60cf80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d4546001600160a01b031633146120645760405162461bcd60e51b81526004016111d190615834565b600260015414156120875760405162461bcd60e51b81526004016111d1906158f9565b60026001556101015460fc54604051632288d73760e11b81526001600160a01b0392831660048201527f0000000000000000000000000000000000000000000000000000000000000000909216602483015260448201527325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090634511ae6e9060640160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214d9190615636565b5060018055565b60d4546001600160a01b0316331461217e5760405162461bcd60e51b81526004016111d190615834565b600260015414156121a15760405162461bcd60e51b81526004016111d1906158f9565b60026001556121ae613f2e565b60018055565b600260015414156121d75760405162461bcd60e51b81526004016111d1906158f9565b60026001819055506121ae60006001614061565b6065546001600160a01b031633146122155760405162461bcd60e51b81526004016111d1906158a3565b612223620f42406064615b69565b81106122715760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e63652066656500000000000000000060448201526064016111d1565b60d55460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d555565b6065546001600160a01b031633146122dc5760405162461bcd60e51b81526004016111d1906158a3565b6122e66000614210565b565b6002600154141561230b5760405162461bcd60e51b81526004016111d1906158f9565b600260015561231981614262565b610102546123279082614429565b610102555060018055565b6060609b80546113de90615bcb565b600061237661234f60995490565b6123576126d6565b60d05460cc546001600160801b0390911690610100900460ff16613c03565b905090565b6002600154141561239e5760405162461bcd60e51b81526004016111d1906158f9565b6002600155806123dd5760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b60448201526064016111d1565b60cf5461ffff1660005b8281101561246c5760006123fb8284615a46565b600081815260ca6020526040902054909150156124485760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b60448201526064016111d1565b600090815260ca60205260409020600190558061246481615c06565b9150506123e7565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156124f75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016111d1565b61250433858584036134d7565b5060019392505050565b6065546001600160a01b031633146125385760405162461bcd60e51b81526004016111d1906158a3565b61012c81101561258a5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e000000000000000060448201526064016111d1565b60fb5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160fb55565b600260015414156125ee5760405162461bcd60e51b81526004016111d1906158f9565b6002600155610100546001600160a01b03168061260a57600080fd5b6000821161261757600080fd5b33600090815260976020526040902054828110156126435761264361263c84836137cc565b6000614061565b61264e3330856135fc565b6126593083856134d7565b6040516383df674760e01b815260048101849052336024820152600060448201526001600160a01b038316906383df674790606401600060405180830381600087803b1580156126a857600080fd5b505af11580156126bc573d6000803e3d6000fd5b5050600180555050505050565b600061146e3384846135fc565b60cc546040516370a0823160e01b815230600482015260009161237691620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561272557600080fd5b505afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190615636565b60cf546201000090046001600160681b031690614429565b600260015414156127985760405162461bcd60e51b81526004016111d1906158f9565b6002600155806127ba5760405162461bcd60e51b81526004016111d1906158d8565b6127c48133613939565b60cc5461214d906201000090046001600160a01b0316333084613bcb565b60d4546001600160a01b0316331461280c5760405162461bcd60e51b81526004016111d190615834565b6002600154141561282f5760405162461bcd60e51b81526004016111d1906158f9565b60026001556101025460fd546000908190819061284c9085614435565b60fd81905560d054929550909350915060009061287990600160801b90046001600160801b031686614429565b9050612884816147d0565b60d080546001600160801b03808416600160801b0291161790556000610102556128ad83613ca5565b60cf805462010000600160781b031916620100006001600160681b0386160217905560405183815233906001600160a01b038616907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f00000000000000000000000000000000000000000000000000000000000000008116602483015285166044820152606481018490526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090632904c2399060840160206040518083038186803b1580156129bc57600080fd5b505af41580156129d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f49190615636565b61010154604051636648c73d60e11b81526001600160a01b03918216600482015290871660248201526044810182905261138860648201529091507325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063cc918e7a9060840160206040518083038186803b158015612a6657600080fd5b505af4158015612a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9e9190615636565b50612aa7613f2e565b50506001805550505050565b610103546001600160a01b031680612aca57600080fd5b612ad660006001614061565b3360008181526097602052604090205490612af29083836134d7565b60405163c9c2d4f560e01b8152336004820152602481018290526001600160a01b0383169063c9c2d4f590604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505050565b6000806000612b648461325b565b909250905061186e8282614429565b60d4546001600160a01b03163314612b9d5760405162461bcd60e51b81526004016111d190615834565b600081118015612bb85750612bb4600a6064615b69565b8111155b612bf75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b60448201526064016111d1565b60f75460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160f755565b6065546001600160a01b03163314612c625760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612ca55760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b60448201526064016111d1565b60d480546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612cea5760405162461bcd60e51b81526004016111d1906158f9565b600260015580612d295760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b61214d816000614061565b6065546001600160a01b03163314612d5e5760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612da75760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b60448201526064016111d1565b60d3546001600160a01b0382811691161415612e055760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e74000000000000000060448201526064016111d1565b60d380546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612e4a5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d25460408051610120810182526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811682527f00000000000000000000000000000000000000000000000000000000000000008116602083015292831681830181905260006060830181905260f95461ffff16608084015260fa5460a084015260f654851660c084015260f55490941660e083015260f754610100830152915163f39ad79560e01b815291929091819081907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063f39ad79590612f3a90879060cc9060cf90600401615930565b60606040518083038186803b158015612f5257600080fd5b505af4158015612f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8a9190615549565b604080518381526020810183905293965091945092507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb0910160405180910390a160d180546001600160a01b0319166001600160a01b0385161790556000612ff24282614429565b905063ffffffff8111156130485760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e5265616479000000000000000060448201526064016111d1565b60d2805463ffffffff60a01b1916600160a01b63ffffffff841602179055612aa78661481a565b6065546001600160a01b031633146130995760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166130fe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016111d1565b61310781614210565b50565b6002600154141561312d5760405162461bcd60e51b81526004016111d1906158f9565b600260015560cc547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811662010000909204161461319e5760405162461bcd60e51b8152602060048201526005602482015264042ae8aa8960db1b60448201526064016111d1565b600034116131d75760405162461bcd60e51b81526020600482015260066024820152652176616c756560d01b60448201526064016111d1565b6131e13433613939565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323c57600080fd5b505af1158015613250573d6000803e3d6000fd5b505060018055505050565b6001600160a01b038116600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b0316918101919091528291600111156132dd575050506001600160a01b031660009081526097602052604081205491565b60cf54815161ffff908116600090815260ca602052604081205460cc54919361331393869391169190610100900460ff1661498c565b9050613334856001600160a01b031660009081526097602052604090205490565b9590945092505050565b600260015414156133615760405162461bcd60e51b81526004016111d1906158f9565b600260015560006133706149fd565b60fd5490915061338090826137cc565b6001600160801b031660fd555060018055565b6065546001600160a01b031633146133bd5760405162461bcd60e51b81526004016111d1906158a3565b6133cb620f42406064615b69565b81106134125760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b60448201526064016111d1565b600061342f63031ba30961342984620f4240614bba565b90614bc6565b60d65460408051918252602082018590529192507f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a160d65550565b6040516001600160a01b038316602482015260448101829052611e6f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bd2565b6001600160a01b0383166135395760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016111d1565b6001600160a01b03821661359a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016111d1565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166136605760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016111d1565b6001600160a01b0382166136c25760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016111d1565b6001600160a01b0383166000908152609760205260409020548181101561373a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016111d1565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290613771908490615a46565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516137bd91815260200190565b60405180910390a35b50505050565b600061151d8284615b88565b60cc546001600160a01b03620100009091048116907f00000000000000000000000000000000000000000000000000000000000000001681141561392557604051632e1a7d4d60e01b8152600481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561387857600080fd5b505af115801561388c573d6000803e3d6000fd5b505050506000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146138dd576040519150601f19603f3d011682016040523d82523d6000602084013e6138e2565b606091505b50509050806137c65760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016111d1565b611e6f6001600160a01b0382168484613474565b60cf5461ffff1660006139548461394e6126d6565b90614429565b60ce549091506001600160681b031681111561399f5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b60448201526064016111d1565b60cd54600160a01b900466ffffffffffffff168110156139f85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016111d1565b60408051858152602081018490526001600160a01b038516917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a26001600160a01b038316600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031683860152600160781b9091046001600160801b031682840152845260ca90925282205460cc54919291613ab7918491879190610100900460ff1661498c565b8251909150869061ffff16851415613ae8576020830151600090613ae4906001600160681b031689614429565b9150505b613af181613ca5565b6040805160608101825261ffff80881682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038e16600090815260c990935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560d054613b97911689614429565b9050613ba2816147d0565b60d080546001600160801b0319166001600160801b039290921691909117905550505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526137c69085906323b872dd60e01b906084016134a0565b600080613c1183600a615ac1565b905060008611613c215780613c39565b613c3986613429613c3288886137cc565b8490614bba565b9695505050505050565b600060018311613c8d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e613c9b83600a615ac1565b6134298686614bba565b6001600160681b038111156131075760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b60448201526064016111d1565b600054610100900460ff1680613d08575060005460ff16155b613d245760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015613d46576000805461ffff19166101011790555b60405163c72733f760e01b81527325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063c72733f790613d8b908c908c908c908b908d908c908c908c90600401615727565b60006040518083038186803b158015613da357600080fd5b505af4158015613db7573d6000803e3d6000fd5b50505050613dc3614ca4565b613dcd8484614d17565b613dd5614d96565b613dde8961306f565b60d480546001600160a01b03808b166001600160a01b03199283161790925560d38054928a169290911691909117905560d5859055613e2863031ba30961342988620f4240614bba565b60d6558160cc613e388282615c5e565b505060cc546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a082319060240160206040518083038186803b158015613e8457600080fd5b505afa158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615636565b9050613ec781613ca5565b60cf805461ffff196001600160681b03909316600160781b02929092167fffffffff00000000000000000000000000ffffffffffffffffffffffffff00009092169190911760011790558015613f23576000805461ff00191690555b505050505050505050565b6040805160c08101825260d2546001600160a01b039081168083527f000000000000000000000000000000000000000000000000000000000000000082166020840190815260cc54620100008104841685870190815260ff610100909204919091166060860190815260f8546080870190815260fb5460a088019081529751633baf17fd60e21b8152875187166004820152935186166024850152915190941660448301529251606482015291516084830152925160a48201529091907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063eebc5ff49060c40160206040518083038186803b15801561402257600080fd5b505af4158015614036573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405a9190615636565b60fc555050565b33600090815260c9602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cf5491865260ca9094529184205460cc54919492909316926140dc9185918591610100900460ff1661498c565b9050836140e957846140eb565b805b9450846140f9575050505050565b8085111561413d5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b60448201526064016111d1565b825161ffff1682111561416a5733600090815260c960205260409020805462010000600160781b03191690555b614173856147d0565b61417d81866137cc565b33600081815260c960205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916141f69189825261ffff16602082015260400190565b60405180910390a26142093033876135fc565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000811161429f5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b33600090815260c960205260409020546201000090046001600160681b03161515806142e9575033600090815260c96020526040902054600160781b90046001600160801b031615155b156142fa576142fa60006001614061565b60cf5433600081815260cb60209081526040918290208054835187815261ffff96871693810184905292959194911685149290917f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a281546201000090046001600160801b0316600082156143835761437c8287614429565b90506143e6565b81156143c55760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b60448201526064016111d1565b5033600090815260cb60205260409020805461ffff191661ffff8616179055845b6143ef816147d0565b33600081815260cb60205260409020805462010000600160901b031916620100006001600160801b03851602179055612b4e9030886135fc565b600061151d8284615a46565b60d25460009081908190600160a01b900463ffffffff164210156144845760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b60448201526064016111d1565b60d1546001600160a01b03169250826144cd5760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b60448201526064016111d1565b60d3546040805160e08101825260cc54610100810460ff16825291516370a0823160e01b81523060048201526001600160a01b03938416936000938493849384937325af99ff07575ac55fb3c2d1e6b920ba3bf67b409363633c14269360cf939092602084019262010000900416906370a082319060240160206040518083038186803b15801561455d57600080fd5b505afa158015614571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145959190615636565b81526020016145a360995490565b81526020018e815260200160d554815260200160d65481526020018d8152506040518363ffffffff1660e01b815260040161463092919060006101008201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e08301529392505050565b60c06040518083038186803b15801561464857600080fd5b505af415801561465c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146809190615672565b809750819850829950839650849b50859c505050505050508760d160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060d160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060cf60000160009054906101000a900461ffff1661ffff1690508160ca600083815260200190815260200160002081905550856001600160a01b03167f0a242f7ecaf711036ca770774ceffae28e60ef042ac113ddd187f2631db0c00685858460405161476f939291909283526020830191909152604082015260600190565b60405180910390a260d080546001600160801b0319169055614792816001615a46565b60cf805461ffff191661ffff92909216919091179055506147b590503084614dfd565b80156147c5576147c584826137d8565b505050509250925092565b6001600160801b038111156131075760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b60448201526064016111d1565b60cf546201000090046001600160681b03166001600160a01b038216156148665760cf80546cffffffffffffffffffffffffff60781b1916600160781b6001600160681b038416021790555b60cf805462010000600160781b031916905560d280546001600160a01b03191690556001600160a01b038216156113cb57604051636c6fe87f60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063d8dfd0fe9060240160206040518083038186803b15801561490d57600080fd5b505af4158015614921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149459190615636565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f7836040516135ef91815260200190565b835160009061ffff16158015906149a75750845161ffff1684115b156149e95760006149c686602001516001600160681b03168585614edc565b60408701519091506149e1906001600160801b031682614429565b91505061186e565b50505050604001516001600160801b031690565b33600090815260cb6020526040812080546001600160801b03620100008204169061ffff1681614a5f5760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b60448201526064016111d1565b60cf5461ffff168110614aa75760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b60448201526064016111d1565b33600090815260cb60205260409020805462010000600160901b031916905560d054614ae390600160801b90046001600160801b0316836137cc565b60d080546001600160801b03928316600160801b029216919091179055600081815260ca602052604081205460cc54614b2691859160ff61010090910416613c43565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a2614b6e3084614f3f565b60008111614bb05760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b60448201526064016111d1565b61186e33826137d8565b600061151d8284615b69565b600061151d8284615a5e565b6000614c27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661508d9092919063ffffffff16565b805190915015611e6f5780806020019051810190614c45919061557f565b611e6f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016111d1565b600054610100900460ff1680614cbd575060005460ff16155b614cd95760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614cfb576000805461ffff19166101011790555b614d0361509c565b8015613107576000805461ff001916905550565b600054610100900460ff1680614d30575060005460ff16155b614d4c5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d6e576000805461ffff19166101011790555b614d7661510b565b614d808383615175565b8015611e6f576000805461ff0019169055505050565b600054610100900460ff1680614daf575060005460ff16155b614dcb5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614ded576000805461ffff19166101011790555b614df561510b565b614d0361520a565b6001600160a01b038216614e535760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016111d1565b8060996000828254614e659190615a46565b90915550506001600160a01b03821660009081526097602052604081208054839290614e92908490615a46565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614f265760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e83613429614f3885600a615ac1565b8790614bba565b6001600160a01b038216614f9f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016111d1565b6001600160a01b038216600090815260976020526040902054818110156150135760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016111d1565b6001600160a01b0383166000908152609760205260408120838303905560998054849290615042908490615b88565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b606061186e848460008561526a565b600054610100900460ff16806150b5575060005460ff16155b6150d15760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156150f3576000805461ffff19166101011790555b600180558015613107576000805461ff001916905550565b600054610100900460ff1680615124575060005460ff16155b6151405760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d03576000805461ffff19166101011790558015613107576000805461ff001916905550565b600054610100900460ff168061518e575060005460ff16155b6151aa5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156151cc576000805461ffff19166101011790555b82516151df90609a9060208601906153cb565b5081516151f390609b9060208501906153cb565b508015611e6f576000805461ff0019169055505050565b600054610100900460ff1680615223575060005460ff16155b61523f5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015615261576000805461ffff19166101011790555b614d0333614210565b6060824710156152cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016111d1565b843b6153195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016111d1565b600080866001600160a01b03168587604051615335919061570b565b60006040518083038185875af1925050503d8060008114615372576040519150601f19603f3d011682016040523d82523d6000602084013e615377565b606091505b5091509150615387828286615392565b979650505050505050565b606083156153a157508161151d565b8251156153b15782518084602001fd5b8160405162461bcd60e51b81526004016111d19190615821565b8280546153d790615bcb565b90600052602060002090601f0160209004810192826153f9576000855561543f565b82601f1061541257805160ff191683800117855561543f565b8280016001018555821561543f579182015b8281111561543f578251825591602001919060010190615424565b5061544b92915061544f565b5090565b5b8082111561544b5760008155600101615450565b803561546f81615d6f565b919050565b803561546f81615d92565b803561546f81615da7565b60006020828403121561549b578081fd5b813561151d81615d6f565b600080604083850312156154b8578081fd5b82356154c381615d6f565b915060208301356154d381615d6f565b809150509250929050565b6000806000606084860312156154f2578081fd5b83356154fd81615d6f565b9250602084013561550d81615d6f565b929592945050506040919091013590565b60008060408385031215615530578182fd5b823561553b81615d6f565b946020939093013593505050565b60008060006060848603121561555d578283fd5b835161556881615d6f565b602085015160409095015190969495509392505050565b600060208284031215615590578081fd5b815161151d81615d84565b60008082840360e08112156155ae578283fd5b833567ffffffffffffffff8111156155c4578384fd5b840161016081870312156155d6578384fd5b925060c0601f19820112156155e9578182fd5b506020830190509250929050565b600060208284031215615608578081fd5b81356001600160801b038116811461151d578182fd5b60006020828403121561562f578081fd5b5035919050565b600060208284031215615647578081fd5b5051919050565b60008060408385031215615660578182fd5b8235915060208301356154d381615d6f565b60008060008060008060c0878903121561568a578182fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6000602082840312156156cc578081fd5b813563ffffffff8116811461151d578182fd5b600081518084526156f7816020860160208601615b9f565b601f01601f19169290920160200192915050565b6000825161571d818460208701615b9f565b9190910192915050565b60006101a060018060a01b03808c168452808b166020850152808a1660408501528860608501528760808501528160a0850152615766828501886156df565b915083820360c085015261577a82876156df565b92508435915061578982615d84565b90151560e08401526020840135906157a082615dbc565b60ff8216610100850152604085013591506157ba82615d6f565b166101208301526157cd60608401615464565b6001600160a01b03166101408301526157e86080840161547f565b66ffffffffffffff1661016083015261580360a08401615474565b6001600160681b038116610180840152509998505050505050505050565b60208152600061151d60208301846156df565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526608585b5bdd5b9d60ca1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b83516001600160a01b0316815260208085015161016083019161595d908401826001600160a01b03169052565b50604085015161597860408401826001600160a01b03169052565b50606085015160608301526080850151615998608084018261ffff169052565b5060a085015160a083015260c08501516159bd60c08401826001600160a01b03169052565b5060e08501516159d860e08401826001600160a01b03169052565b5061010094850151948201949094526101208101929092526101409091015290565b6000808335601e19843603018112615a10578283fd5b83018035915067ffffffffffffffff821115615a2a578283fd5b602001915036819003821315615a3f57600080fd5b9250929050565b60008219821115615a5957615a59615c21565b500190565b600082615a7957634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115615ab9578160001904821115615a9f57615a9f615c21565b80851615615aac57918102915b93841c9390800290615a83565b509250929050565b600061151d8383600082615ad757506001611472565b81615ae457506000611472565b8160018114615afa5760028114615b0457615b20565b6001915050611472565b60ff841115615b1557615b15615c21565b50506001821b611472565b5060208310610133831016604e8410600b8410161715615b43575081810a611472565b615b4d8383615a7e565b8060001904821115615b6157615b61615c21565b029392505050565b6000816000190483118215151615615b8357615b83615c21565b500290565b600082821015615b9a57615b9a615c21565b500390565b60005b83811015615bba578181015183820152602001615ba2565b838111156137c65750506000910152565b600181811c90821680615bdf57607f821691505b60208210811415615c0057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c1a57615c1a615c21565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6000813561147281615d6f565b6000813561147281615d92565b6000813561147281615da7565b8135615c6981615d84565b815460ff19811691151560ff1691821783556020840135615c8981615dbc565b61ff008160081b169050808361ffff198416171784556040850135615cad81615d6f565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d05615ce860608501615c37565b82546001600160a01b0319166001600160a01b0391909116178255565b615d3e615d1460808501615c51565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b506113cb615d4e60a08401615c44565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b038116811461310757600080fd5b801515811461310757600080fd5b6001600160681b038116811461310757600080fd5b66ffffffffffffff8116811461310757600080fd5b60ff8116811461310757600080fdfea26469706673582212205fbcaa8781187325d25b31e13bacef69edce6157041a37dc50e9cf290afbc57364736f6c63430008040033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc720000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101

Deployed Bytecode

0x6080604052600436106104c05760003560e01c806387153eb111610276578063b4d1d7951161014f578063e73c63d5116100c1578063f656ba5111610085578063f656ba51146110db578063f756fa2114611110578063f957a06714611125578063f9a0be6814611145578063fba7dc6114611166578063fe56e2321461118757600080fd5b8063e73c63d514611068578063e74b981b1461107e578063ea00b1ca1461109e578063f2fde38b146110b3578063f6326fb3146110d357600080fd5b8063d13f1b3e11610113578063d13f1b3e14610f78578063d164cc1514610f98578063d5f2638214610fb8578063db006a7514610fce578063db43e86214610fee578063dd62ed3e1461102257600080fd5b8063b4d1d79514610ef7578063b6b55f2514610f0e578063b9f8092b14610f2e578063ca59409414610f43578063ce7c2ac214610f5857600080fd5b8063a285c9e8116101e8578063a6f7f5d6116101ac578063a6f7f5d614610e38578063a9059cbb14610e4e578063aced166114610e6e578063ad5c464814610e8e578063ad7a672f14610ec2578063afa6626414610ed757600080fd5b8063a285c9e814610d98578063a2db9d8314610dba578063a457c2d714610dd8578063a497e67414610df8578063a694fc3a14610e1857600080fd5b8063947061b51161023a578063947061b514610c1e57806395d89b4114610c3f57806399530b0614610c545780639be43daa14610c695780639fcc2d7514610c89578063a083ff1714610d3557600080fd5b806387153eb114610b6b5780638778878214610b9857806389a3027114610bae5780638b10cc7c14610be25780638da5cb5b14610c0057600080fd5b806347786d37116103a85780636719b2ee1161031a57806370897b23116102de57806370897b2314610a6657806370a0823114610a86578063715018a614610abc5780637a9262a214610ad15780637e108d5214610b3557806383536ff314610b5557600080fd5b80636719b2ee1461096f57806369b41170146109f35780636b64c76914610a085780636c10f66c14610a1d5780636f31ab3414610a5157600080fd5b8063573f0d6e1161036c578063573f0d6e146108b157806358358207146108d15780635ea8cd12146108f1578063600a2cfb14610911578063611b034d14610926578063650cce8a1461093b57600080fd5b806347786d37146108245780634ac88cb6146108445780634b2431d914610864578063503c70aa1461087b57806355489bb21461089157600080fd5b806330630da4116104415780633ec143d3116104055780633ec143d3146107625780633f23bb73146107905780633f90916a146107b0578063432833a6146107ce5780634603c0aa146107e4578063469048401461080457600080fd5b806330630da4146106ba578063313ce567146106da578063355274ea1461070457806336efd16f14610722578063395093511461074257600080fd5b806318160ddd1161048857806318160ddd146105865780631a92f6c21461059b57806323b872dd146105e75780632728f333146106075780632775d01c1461069a57600080fd5b8063048bf085146104c5578063056097ac146104e757806306fdde0314610507578063095ea7b3146105325780630cbf54c814610562575b600080fd5b3480156104d157600080fd5b506104e56104e036600461548a565b6111a7565b005b3480156104f357600080fd5b506104e56105023660046154a6565b6111fd565b34801561051357600080fd5b5061051c6113cf565b6040516105299190615821565b60405180910390f35b34801561053e57600080fd5b5061055261054d36600461551e565b611461565b6040519015158152602001610529565b34801561056e57600080fd5b5061057860fb5481565b604051908152602001610529565b34801561059257600080fd5b50609954610578565b3480156105a757600080fd5b506105cf7f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc7281565b6040516001600160a01b039091168152602001610529565b3480156105f357600080fd5b506105526106023660046154de565b611478565b34801561061357600080fd5b5060cf5460d0546106559161ffff8116916001600160681b03620100008304811692600160781b900416906001600160801b0380821691600160801b90041685565b6040805161ffff90961686526001600160681b03948516602087015293909216928401929092526001600160801b03918216606084015216608082015260a001610529565b3480156106a657600080fd5b506104e56106b536600461561e565b611524565b3480156106c657600080fd5b506104e56106d536600461548a565b6116c7565b3480156106e657600080fd5b5060cc54610100900460ff1660405160ff9091168152602001610529565b34801561071057600080fd5b5060ce546001600160681b0316610578565b34801561072e57600080fd5b506104e561073d36600461564e565b61175f565b34801561074e57600080fd5b5061055261075d36600461551e565b6117e7565b34801561076e57600080fd5b5060f95461077d9061ffff1681565b60405161ffff9091168152602001610529565b34801561079c57600080fd5b506105786107ab36600461548a565b611823565b3480156107bc57600080fd5b5060d0546001600160801b0316610578565b3480156107da57600080fd5b5061057860fc5481565b3480156107f057600080fd5b506104e56107ff36600461548a565b611876565b34801561081057600080fd5b5060d3546105cf906001600160a01b031681565b34801561083057600080fd5b506104e561083f36600461561e565b611918565b34801561085057600080fd5b506104e561085f36600461548a565b6119f3565b34801561087057600080fd5b506105786101025481565b34801561088757600080fd5b5061057860fd5481565b34801561089d57600080fd5b506104e56108ac3660046155f7565b611a40565b3480156108bd57600080fd5b506104e56108cc36600461548a565b611ad8565b3480156108dd57600080fd5b506104e56108ec36600461559b565b611b25565b3480156108fd57600080fd5b506104e561090c36600461561e565b611e74565b34801561091d57600080fd5b506104e5611edf565b34801561093257600080fd5b506104e561203a565b34801561094757600080fd5b506105cf7f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef81565b34801561097b57600080fd5b506109c261098a36600461548a565b60c96020526000908152604090205461ffff8116906201000081046001600160681b031690600160781b90046001600160801b031683565b6040805161ffff90941684526001600160681b0390921660208401526001600160801b031690820152606001610529565b3480156109ff57600080fd5b50610578600081565b348015610a1457600080fd5b506104e5612154565b348015610a2957600080fd5b506105cf7f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10181565b348015610a5d57600080fd5b506104e56121b4565b348015610a7257600080fd5b506104e5610a8136600461561e565b6121eb565b348015610a9257600080fd5b50610578610aa136600461548a565b6001600160a01b031660009081526097602052604090205490565b348015610ac857600080fd5b506104e56122b2565b348015610add57600080fd5b50610b13610aec36600461548a565b60cb6020526000908152604090205461ffff8116906201000090046001600160801b031682565b6040805161ffff90931683526001600160801b03909116602083015201610529565b348015610b4157600080fd5b506104e5610b5036600461561e565b6122e8565b348015610b6157600080fd5b5061057860f75481565b348015610b7757600080fd5b50610578610b8636600461561e565b60ca6020526000908152604090205481565b348015610ba457600080fd5b5061057860d55481565b348015610bba57600080fd5b506105cf7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b348015610bee57600080fd5b5060d2546001600160a01b03166105cf565b348015610c0c57600080fd5b506065546001600160a01b03166105cf565b348015610c2a57600080fd5b50610103546105cf906001600160a01b031681565b348015610c4b57600080fd5b5061051c612332565b348015610c6057600080fd5b50610578612341565b348015610c7557600080fd5b506104e5610c8436600461561e565b61237b565b348015610c9557600080fd5b5060cc5460cd5460ce54610ce59260ff808216936101008304909116926001600160a01b036201000090930483169282169166ffffffffffffff600160a01b90910416906001600160681b031686565b60408051961515875260ff90951660208701526001600160a01b03938416948601949094529116606084015266ffffffffffffff1660808301526001600160681b031660a082015260c001610529565b348015610d4157600080fd5b5060d15460d254610d6c916001600160a01b039081169190811690600160a01b900463ffffffff1683565b604080516001600160a01b03948516815293909216602084015263ffffffff1690820152606001610529565b348015610da457600080fd5b5060d254600160a01b900463ffffffff16610578565b348015610dc657600080fd5b5060d1546001600160a01b03166105cf565b348015610de457600080fd5b50610552610df336600461551e565b612475565b348015610e0457600080fd5b506104e5610e1336600461561e565b61250e565b348015610e2457600080fd5b506104e5610e3336600461561e565b6125cb565b348015610e4457600080fd5b5061057860d65481565b348015610e5a57600080fd5b50610552610e6936600461551e565b6126c9565b348015610e7a57600080fd5b5060d4546105cf906001600160a01b031681565b348015610e9a57600080fd5b506105cf7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b348015610ece57600080fd5b506105786126d6565b348015610ee357600080fd5b5060f5546105cf906001600160a01b031681565b348015610f0357600080fd5b5061057862093a8081565b348015610f1a57600080fd5b506104e5610f2936600461561e565b612775565b348015610f3a57600080fd5b506104e56127e2565b348015610f4f57600080fd5b506104e5612ab3565b348015610f6457600080fd5b50610578610f7336600461548a565b612b56565b348015610f8457600080fd5b506104e5610f9336600461561e565b612b73565b348015610fa457600080fd5b506104e5610fb336600461548a565b612c38565b348015610fc457600080fd5b5061057860f85481565b348015610fda57600080fd5b506104e5610fe936600461561e565b612cc7565b348015610ffa57600080fd5b506105cf7f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e81565b34801561102e57600080fd5b5061057861103d3660046154a6565b6001600160a01b03918216600090815260986020908152604080832093909416825291909152205490565b34801561107457600080fd5b5061057860fa5481565b34801561108a57600080fd5b506104e561109936600461548a565b612d34565b3480156110aa57600080fd5b506104e5612e27565b3480156110bf57600080fd5b506104e56110ce36600461548a565b61306f565b6104e561310a565b3480156110e757600080fd5b506110fb6110f636600461548a565b61325b565b60408051928352602083019190915201610529565b34801561111c57600080fd5b506104e561333e565b34801561113157600080fd5b5060f6546105cf906001600160a01b031681565b34801561115157600080fd5b50610100546105cf906001600160a01b031681565b34801561117257600080fd5b50610101546105cf906001600160a01b031681565b34801561119357600080fd5b506104e56111a236600461561e565b613393565b6065546001600160a01b031633146111da5760405162461bcd60e51b81526004016111d1906158a3565b60405180910390fd5b61010080546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146112275760405162461bcd60e51b81526004016111d1906158a3565b60cc546001600160a01b038381166201000090920416141561128b5760405162461bcd60e51b815260206004820152601b60248201527f5661756c74206173736574206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b0382163014156112e45760405162461bcd60e51b815260206004820152601b60248201527f5661756c74207368617265206e6f74207265636f76657261626c65000000000060448201526064016111d1565b6001600160a01b03811630141561133d5760405162461bcd60e51b815260206004820152601960248201527f526563697069656e742063616e6e6f74206265207661756c740000000000000060448201526064016111d1565b6040516370a0823160e01b81523060048201526113cb9082906001600160a01b038516906370a082319060240160206040518083038186803b15801561138257600080fd5b505afa158015611396573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ba9190615636565b6001600160a01b0385169190613474565b5050565b6060609a80546113de90615bcb565b80601f016020809104026020016040519081016040528092919081815260200182805461140a90615bcb565b80156114575780601f1061142c57610100808354040283529160200191611457565b820191906000526020600020905b81548152906001019060200180831161143a57829003601f168201915b5050505050905090565b600061146e3384846134d7565b5060015b92915050565b60006114858484846135fc565b6001600160a01b03841660009081526098602090815260408083203384529091529020548281101561150a5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b60648201526084016111d1565b61151785338584036134d7565b60019150505b9392505050565b600260015414156115475760405162461bcd60e51b81526004016111d1906158f9565b600260015533600090815260c96020526040902060cf5461ffff168261157f5760405162461bcd60e51b81526004016111d1906158d8565b815461ffff1681146115c35760405162461bcd60e51b815260206004820152600d60248201526c125b9d985b1a59081c9bdd5b99609a1b60448201526064016111d1565b81546201000090046001600160681b0316838110156116145760405162461bcd60e51b815260206004820152600d60248201526c115e18d9595908185b5bdd5b9d609a1b60448201526064016111d1565b61161e81856137cc565b83546001600160681b0391909116620100000262010000600160781b031990911617835560d054611658906001600160801b0316856137cc565b60d080546001600160801b0319166001600160801b0392909216919091179055604080518581526020810184905233917fab2daf3c146ca6416cbccd2a86ed2ba995e171ef6319df14a38aef01403a9c96910160405180910390a26116bd33856137d8565b5050600180555050565b6065546001600160a01b031633146116f15760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b03811661173d5760405162461bcd60e51b815260206004820152601360248201527210b732bba9ba3934b5b2a9b2b632b1ba34b7b760691b60448201526064016111d1565b60f680546001600160a01b0319166001600160a01b0392909216919091179055565b600260015414156117825760405162461bcd60e51b81526004016111d1906158f9565b6002600155816117a45760405162461bcd60e51b81526004016111d1906158d8565b6001600160a01b0381166117b757600080fd5b6117c18282613939565b60cc546117df906201000090046001600160a01b0316333085613bcb565b505060018055565b3360008181526098602090815260408083206001600160a01b0387168452909152812054909161146e91859061181e908690615a46565b6134d7565b60cc5460009060ff610100909104168161185961183f60995490565b6118476126d6565b60d0546001600160801b031685613c03565b905061186e61186785612b56565b8284613c43565b949350505050565b6065546001600160a01b031633146118a05760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166118f65760405162461bcd60e51b815260206004820152601860248201527f216e65774f7074696f6e735072656d69756d507269636572000000000000000060448201526064016111d1565b60f580546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b031633146119425760405162461bcd60e51b81526004016111d1906158a3565b6000811161197c5760405162461bcd60e51b81526020600482015260076024820152660216e65774361760cc1b60448201526064016111d1565b61198581613ca5565b60ce54604080516001600160681b039092168252602082018390527f5f86edbb9d92228a9edc3f0ebc0f001bda1ea345ac7335e0eeef3504b31d1a1c910160405180910390a160ce80546cffffffffffffffffffffffffff19166001600160681b0392909216919091179055565b6065546001600160a01b03163314611a1d5760405162461bcd60e51b81526004016111d1906158a3565b61010180546001600160a01b0319166001600160a01b0392909216919091179055565b6065546001600160a01b03163314611a6a5760405162461bcd60e51b81526004016111d1906158a3565b6000816001600160801b031611611ab25760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b60448201526064016111d1565b6001600160801b031660fa5560cf5460f9805461ffff191661ffff909216919091179055565b6065546001600160a01b03163314611b025760405162461bcd60e51b81526004016111d1906158a3565b61010380546001600160a01b0319166001600160a01b0392909216919091179055565b600054610100900460ff1680611b3e575060005460ff16155b611b5a5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015611b7c576000805461ffff19166101011790555b611c41611b8c602085018561548a565b611b9c604086016020870161548a565b611bac606087016040880161548a565b60608701356080880135611bc360a08a018a6159fa565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611c059250505060c08b018b6159fa565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508c9250613cef915050565b6000611c54610100850160e0860161548a565b6001600160a01b03161415611ca45760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b60448201526064016111d1565b6000611cb86101208501610100860161548a565b6001600160a01b03161415611d035760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b60448201526064016111d1565b6000611d17610140850161012086016156bb565b63ffffffff16118015611d4b5750611d31600a6064615b69565b611d43610140850161012086016156bb565b63ffffffff16105b611d8b5760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b60448201526064016111d1565b61012c8361014001351015611dd65760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b60448201526064016111d1565b611de7610100840160e0850161548a565b60f580546001600160a01b0319166001600160a01b0392909216919091179055611e196101208401610100850161548a565b60f680546001600160a01b0319166001600160a01b0392909216919091179055611e4b610140840161012085016156bb565b63ffffffff1660f75561014083013560fb558015611e6f576000805461ff00191690555b505050565b60d4546001600160a01b03163314611e9e5760405162461bcd60e51b81526004016111d190615834565b60008111611eda5760405162461bcd60e51b8152602060048201526009602482015268216d696e507269636560b81b60448201526064016111d1565b60f855565b60d4546001600160a01b03163314611f095760405162461bcd60e51b81526004016111d190615834565b60026001541415611f2c5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d2546040516358ffbb3d60e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc728116600483015290911660248201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b40906358ffbb3d9060440160206040518083038186803b158015611fb357600080fd5b505af4158015611fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611feb9190615636565b60cf5490915061200a906201000090046001600160681b0316826137cc565b60cf80546001600160681b0392909216620100000262010000600160781b03199092169190911790555060018055565b60d4546001600160a01b031633146120645760405162461bcd60e51b81526004016111d190615834565b600260015414156120875760405162461bcd60e51b81526004016111d1906158f9565b60026001556101015460fc54604051632288d73760e11b81526001600160a01b0392831660048201527f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101909216602483015260448201527325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090634511ae6e9060640160206040518083038186803b15801561211557600080fd5b505af4158015612129573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214d9190615636565b5060018055565b60d4546001600160a01b0316331461217e5760405162461bcd60e51b81526004016111d190615834565b600260015414156121a15760405162461bcd60e51b81526004016111d1906158f9565b60026001556121ae613f2e565b60018055565b600260015414156121d75760405162461bcd60e51b81526004016111d1906158f9565b60026001819055506121ae60006001614061565b6065546001600160a01b031633146122155760405162461bcd60e51b81526004016111d1906158a3565b612223620f42406064615b69565b81106122715760405162461bcd60e51b815260206004820152601760248201527f496e76616c696420706572666f726d616e63652066656500000000000000000060448201526064016111d1565b60d55460408051918252602082018390527f24867dfb6fcb9970a07be21024956524abe7a1837faa903ff0e99aaa40cf893e910160405180910390a160d555565b6065546001600160a01b031633146122dc5760405162461bcd60e51b81526004016111d1906158a3565b6122e66000614210565b565b6002600154141561230b5760405162461bcd60e51b81526004016111d1906158f9565b600260015561231981614262565b610102546123279082614429565b610102555060018055565b6060609b80546113de90615bcb565b600061237661234f60995490565b6123576126d6565b60d05460cc546001600160801b0390911690610100900460ff16613c03565b905090565b6002600154141561239e5760405162461bcd60e51b81526004016111d1906158f9565b6002600155806123dd5760405162461bcd60e51b815260206004820152600a602482015269216e756d526f756e647360b01b60448201526064016111d1565b60cf5461ffff1660005b8281101561246c5760006123fb8284615a46565b600081815260ca6020526040902054909150156124485760405162461bcd60e51b815260206004820152600b60248201526a125b9a5d1a585b1a5e995960aa1b60448201526064016111d1565b600090815260ca60205260409020600190558061246481615c06565b9150506123e7565b50506001805550565b3360009081526098602090815260408083206001600160a01b0386168452909152812054828110156124f75760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084016111d1565b61250433858584036134d7565b5060019392505050565b6065546001600160a01b031633146125385760405162461bcd60e51b81526004016111d1906158a3565b61012c81101561258a5760405162461bcd60e51b815260206004820152601860248201527f496e76616c69642061756374696f6e206475726174696f6e000000000000000060448201526064016111d1565b60fb5460408051918252602082018390527f5acd982e2240ed224a69aa03dab039d3797c108e4b5f288cd7dd6ca181b275f3910160405180910390a160fb55565b600260015414156125ee5760405162461bcd60e51b81526004016111d1906158f9565b6002600155610100546001600160a01b03168061260a57600080fd5b6000821161261757600080fd5b33600090815260976020526040902054828110156126435761264361263c84836137cc565b6000614061565b61264e3330856135fc565b6126593083856134d7565b6040516383df674760e01b815260048101849052336024820152600060448201526001600160a01b038316906383df674790606401600060405180830381600087803b1580156126a857600080fd5b505af11580156126bc573d6000803e3d6000fd5b5050600180555050505050565b600061146e3384846135fc565b60cc546040516370a0823160e01b815230600482015260009161237691620100009091046001600160a01b0316906370a082319060240160206040518083038186803b15801561272557600080fd5b505afa158015612739573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275d9190615636565b60cf546201000090046001600160681b031690614429565b600260015414156127985760405162461bcd60e51b81526004016111d1906158f9565b6002600155806127ba5760405162461bcd60e51b81526004016111d1906158d8565b6127c48133613939565b60cc5461214d906201000090046001600160a01b0316333084613bcb565b60d4546001600160a01b0316331461280c5760405162461bcd60e51b81526004016111d190615834565b6002600154141561282f5760405162461bcd60e51b81526004016111d1906158f9565b60026001556101025460fd546000908190819061284c9085614435565b60fd81905560d054929550909350915060009061287990600160801b90046001600160801b031686614429565b9050612884816147d0565b60d080546001600160801b03808416600160801b0291161790556000610102556128ad83613ca5565b60cf805462010000600160781b031916620100006001600160681b0386160217905560405183815233906001600160a01b038616907f045c558fdce4714c5816d53820d27420f4cd860892df203fe636384d8d19aa019060200160405180910390a3604051632904c23960e01b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc72811660048301527f0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef8116602483015285166044820152606481018490526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b4090632904c2399060840160206040518083038186803b1580156129bc57600080fd5b505af41580156129d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129f49190615636565b61010154604051636648c73d60e11b81526001600160a01b03918216600482015290871660248201526044810182905261138860648201529091507325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063cc918e7a9060840160206040518083038186803b158015612a6657600080fd5b505af4158015612a7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9e9190615636565b50612aa7613f2e565b50506001805550505050565b610103546001600160a01b031680612aca57600080fd5b612ad660006001614061565b3360008181526097602052604090205490612af29083836134d7565b60405163c9c2d4f560e01b8152336004820152602481018290526001600160a01b0383169063c9c2d4f590604401600060405180830381600087803b158015612b3a57600080fd5b505af1158015612b4e573d6000803e3d6000fd5b505050505050565b6000806000612b648461325b565b909250905061186e8282614429565b60d4546001600160a01b03163314612b9d5760405162461bcd60e51b81526004016111d190615834565b600081118015612bb85750612bb4600a6064615b69565b8111155b612bf75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908191a5cd8dbdd5b9d60821b60448201526064016111d1565b60f75460408051918252602082018390527f4cd657fde404967d63a338fb06c5c98751a9df57dfae6dc333a432faf8a5f656910160405180910390a160f755565b6065546001600160a01b03163314612c625760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612ca55760405162461bcd60e51b815260206004820152600a60248201526910b732bba5b2b2b832b960b11b60448201526064016111d1565b60d480546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612cea5760405162461bcd60e51b81526004016111d1906158f9565b600260015580612d295760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b61214d816000614061565b6065546001600160a01b03163314612d5e5760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b038116612da75760405162461bcd60e51b815260206004820152601060248201526f085b995dd19959549958da5c1a595b9d60821b60448201526064016111d1565b60d3546001600160a01b0382811691161415612e055760405162461bcd60e51b815260206004820152601860248201527f4d757374206265206e657720666565526563697069656e74000000000000000060448201526064016111d1565b60d380546001600160a01b0319166001600160a01b0392909216919091179055565b60026001541415612e4a5760405162461bcd60e51b81526004016111d1906158f9565b600260015560d25460408051610120810182526001600160a01b037f0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e811682527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116602083015292831681830181905260006060830181905260f95461ffff16608084015260fa5460a084015260f654851660c084015260f55490941660e083015260f754610100830152915163f39ad79560e01b815291929091819081907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063f39ad79590612f3a90879060cc9060cf90600401615930565b60606040518083038186803b158015612f5257600080fd5b505af4158015612f66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f8a9190615549565b604080518381526020810183905293965091945092507fa217999b1c125c2a996f712c5f26a28addad7167bd8a67d5bd5b2a751148abb0910160405180910390a160d180546001600160a01b0319166001600160a01b0385161790556000612ff24282614429565b905063ffffffff8111156130485760405162461bcd60e51b815260206004820152601860248201527f4f766572666c6f77206e6578744f7074696f6e5265616479000000000000000060448201526064016111d1565b60d2805463ffffffff60a01b1916600160a01b63ffffffff841602179055612aa78661481a565b6065546001600160a01b031633146130995760405162461bcd60e51b81526004016111d1906158a3565b6001600160a01b0381166130fe5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016111d1565b61310781614210565b50565b6002600154141561312d5760405162461bcd60e51b81526004016111d1906158f9565b600260015560cc547f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0390811662010000909204161461319e5760405162461bcd60e51b8152602060048201526005602482015264042ae8aa8960db1b60448201526064016111d1565b600034116131d75760405162461bcd60e51b81526020600482015260066024820152652176616c756560d01b60448201526064016111d1565b6131e13433613939565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561323c57600080fd5b505af1158015613250573d6000803e3d6000fd5b505060018055505050565b6001600160a01b038116600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031694830194909452600160781b90046001600160801b0316918101919091528291600111156132dd575050506001600160a01b031660009081526097602052604081205491565b60cf54815161ffff908116600090815260ca602052604081205460cc54919361331393869391169190610100900460ff1661498c565b9050613334856001600160a01b031660009081526097602052604090205490565b9590945092505050565b600260015414156133615760405162461bcd60e51b81526004016111d1906158f9565b600260015560006133706149fd565b60fd5490915061338090826137cc565b6001600160801b031660fd555060018055565b6065546001600160a01b031633146133bd5760405162461bcd60e51b81526004016111d1906158a3565b6133cb620f42406064615b69565b81106134125760405162461bcd60e51b8152602060048201526016602482015275496e76616c6964206d616e6167656d656e742066656560501b60448201526064016111d1565b600061342f63031ba30961342984620f4240614bba565b90614bc6565b60d65460408051918252602082018590529192507f4e874b007ab14f7e263baefd44951834c8266f4f224d1092e49e9c254354cc54910160405180910390a160d65550565b6040516001600160a01b038316602482015260448101829052611e6f90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614bd2565b6001600160a01b0383166135395760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b60648201526084016111d1565b6001600160a01b03821661359a5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b60648201526084016111d1565b6001600160a01b0383811660008181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6001600160a01b0383166136605760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b60648201526084016111d1565b6001600160a01b0382166136c25760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b60648201526084016111d1565b6001600160a01b0383166000908152609760205260409020548181101561373a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b60648201526084016111d1565b6001600160a01b03808516600090815260976020526040808220858503905591851681529081208054849290613771908490615a46565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516137bd91815260200190565b60405180910390a35b50505050565b600061151d8284615b88565b60cc546001600160a01b03620100009091048116907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21681141561392557604051632e1a7d4d60e01b8152600481018390527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d90602401600060405180830381600087803b15801561387857600080fd5b505af115801561388c573d6000803e3d6000fd5b505050506000836001600160a01b03168360405160006040518083038185875af1925050503d80600081146138dd576040519150601f19603f3d011682016040523d82523d6000602084013e6138e2565b606091505b50509050806137c65760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016111d1565b611e6f6001600160a01b0382168484613474565b60cf5461ffff1660006139548461394e6126d6565b90614429565b60ce549091506001600160681b031681111561399f5760405162461bcd60e51b815260206004820152600a6024820152690457863656564206361760b41b60448201526064016111d1565b60cd54600160a01b900466ffffffffffffff168110156139f85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b60448201526064016111d1565b60408051858152602081018490526001600160a01b038516917f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a15910160405180910390a26001600160a01b038316600090815260c9602090815260408083208151606081018352905461ffff81168083526201000082046001600160681b031683860152600160781b9091046001600160801b031682840152845260ca90925282205460cc54919291613ab7918491879190610100900460ff1661498c565b8251909150869061ffff16851415613ae8576020830151600090613ae4906001600160681b031689614429565b9150505b613af181613ca5565b6040805160608101825261ffff80881682526001600160681b0380851660208085019182526001600160801b038089168688019081526001600160a01b038e16600090815260c990935296822095518654935197518216600160781b02600160781b600160f81b03199890951662010000026effffffffffffffffffffffffffffff1990941695169490941791909117949094161790915560d054613b97911689614429565b9050613ba2816147d0565b60d080546001600160801b0319166001600160801b039290921691909117905550505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526137c69085906323b872dd60e01b906084016134a0565b600080613c1183600a615ac1565b905060008611613c215780613c39565b613c3986613429613c3288886137cc565b8490614bba565b9695505050505050565b600060018311613c8d5760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e613c9b83600a615ac1565b6134298686614bba565b6001600160681b038111156131075760405162461bcd60e51b815260206004820152601060248201526f13dd995c999b1bddc81d5a5b9d0c4c0d60821b60448201526064016111d1565b600054610100900460ff1680613d08575060005460ff16155b613d245760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015613d46576000805461ffff19166101011790555b60405163c72733f760e01b81527325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063c72733f790613d8b908c908c908c908b908d908c908c908c90600401615727565b60006040518083038186803b158015613da357600080fd5b505af4158015613db7573d6000803e3d6000fd5b50505050613dc3614ca4565b613dcd8484614d17565b613dd5614d96565b613dde8961306f565b60d480546001600160a01b03808b166001600160a01b03199283161790925560d38054928a169290911691909117905560d5859055613e2863031ba30961342988620f4240614bba565b60d6558160cc613e388282615c5e565b505060cc546040516370a0823160e01b81523060048201526000916201000090046001600160a01b0316906370a082319060240160206040518083038186803b158015613e8457600080fd5b505afa158015613e98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ebc9190615636565b9050613ec781613ca5565b60cf805461ffff196001600160681b03909316600160781b02929092167fffffffff00000000000000000000000000ffffffffffffffffffffffffff00009092169190911760011790558015613f23576000805461ff00191690555b505050505050505050565b6040805160c08101825260d2546001600160a01b039081168083527f0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d10182166020840190815260cc54620100008104841685870190815260ff610100909204919091166060860190815260f8546080870190815260fb5460a088019081529751633baf17fd60e21b8152875187166004820152935186166024850152915190941660448301529251606482015291516084830152925160a48201529091907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063eebc5ff49060c40160206040518083038186803b15801561402257600080fd5b505af4158015614036573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061405a9190615636565b60fc555050565b33600090815260c9602090815260408083208151606081018352905461ffff8082168084526201000083046001600160681b031684870152600160781b9092046001600160801b03168385015260cf5491865260ca9094529184205460cc54919492909316926140dc9185918591610100900460ff1661498c565b9050836140e957846140eb565b805b9450846140f9575050505050565b8085111561413d5760405162461bcd60e51b81526020600482015260116024820152704578636565647320617661696c61626c6560781b60448201526064016111d1565b825161ffff1682111561416a5733600090815260c960205260409020805462010000600160781b03191690555b614173856147d0565b61417d81866137cc565b33600081815260c960205260409081902080546001600160801b0394909416600160781b02600160781b600160f81b0319909416939093179092558451915190917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929916141f69189825261ffff16602082015260400190565b60405180910390a26142093033876135fc565b5050505050565b606580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000811161429f5760405162461bcd60e51b815260206004820152600a602482015269216e756d53686172657360b01b60448201526064016111d1565b33600090815260c960205260409020546201000090046001600160681b03161515806142e9575033600090815260c96020526040902054600160781b90046001600160801b031615155b156142fa576142fa60006001614061565b60cf5433600081815260cb60209081526040918290208054835187815261ffff96871693810184905292959194911685149290917f0c53c82ad07e2d592d88ece3b066777dd60f1118e2a081b380efc4358f0d9e2a910160405180910390a281546201000090046001600160801b0316600082156143835761437c8287614429565b90506143e6565b81156143c55760405162461bcd60e51b81526020600482015260116024820152704578697374696e6720776974686472617760781b60448201526064016111d1565b5033600090815260cb60205260409020805461ffff191661ffff8616179055845b6143ef816147d0565b33600081815260cb60205260409020805462010000600160901b031916620100006001600160801b03851602179055612b4e9030886135fc565b600061151d8284615a46565b60d25460009081908190600160a01b900463ffffffff164210156144845760405162461bcd60e51b815260206004820152600660248201526521726561647960d01b60448201526064016111d1565b60d1546001600160a01b03169250826144cd5760405162461bcd60e51b815260206004820152600b60248201526a10b732bc3a27b83a34b7b760a91b60448201526064016111d1565b60d3546040805160e08101825260cc54610100810460ff16825291516370a0823160e01b81523060048201526001600160a01b03938416936000938493849384937325af99ff07575ac55fb3c2d1e6b920ba3bf67b409363633c14269360cf939092602084019262010000900416906370a082319060240160206040518083038186803b15801561455d57600080fd5b505afa158015614571573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145959190615636565b81526020016145a360995490565b81526020018e815260200160d554815260200160d65481526020018d8152506040518363ffffffff1660e01b815260040161463092919060006101008201905083825282516020830152602083015160408301526040830151606083015260608301516080830152608083015160a083015260a083015160c083015260c083015160e08301529392505050565b60c06040518083038186803b15801561464857600080fd5b505af415801561465c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146809190615672565b809750819850829950839650849b50859c505050505050508760d160010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060d160000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600060cf60000160009054906101000a900461ffff1661ffff1690508160ca600083815260200190815260200160002081905550856001600160a01b03167f0a242f7ecaf711036ca770774ceffae28e60ef042ac113ddd187f2631db0c00685858460405161476f939291909283526020830191909152604082015260600190565b60405180910390a260d080546001600160801b0319169055614792816001615a46565b60cf805461ffff191661ffff92909216919091179055506147b590503084614dfd565b80156147c5576147c584826137d8565b505050509250925092565b6001600160801b038111156131075760405162461bcd60e51b815260206004820152601060248201526f09eeccae4ccd8deee40ead2dce86264760831b60448201526064016111d1565b60cf546201000090046001600160681b03166001600160a01b038216156148665760cf80546cffffffffffffffffffffffffff60781b1916600160781b6001600160681b038416021790555b60cf805462010000600160781b031916905560d280546001600160a01b03191690556001600160a01b038216156113cb57604051636c6fe87f60e11b81526001600160a01b037f0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc721660048201526000907325af99ff07575ac55fb3c2d1e6b920ba3bf67b409063d8dfd0fe9060240160206040518083038186803b15801561490d57600080fd5b505af4158015614921573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149459190615636565b9050336001600160a01b0316836001600160a01b03167f7e830f7c1771deb1bdb35c4a7e6051bbac32b376f7f4e4976b8618b0b11997f7836040516135ef91815260200190565b835160009061ffff16158015906149a75750845161ffff1684115b156149e95760006149c686602001516001600160681b03168585614edc565b60408701519091506149e1906001600160801b031682614429565b91505061186e565b50505050604001516001600160801b031690565b33600090815260cb6020526040812080546001600160801b03620100008204169061ffff1681614a5f5760405162461bcd60e51b815260206004820152600d60248201526c139bdd081a5b9a5d1a585d1959609a1b60448201526064016111d1565b60cf5461ffff168110614aa75760405162461bcd60e51b815260206004820152601060248201526f149bdd5b99081b9bdd0818db1bdcd95960821b60448201526064016111d1565b33600090815260cb60205260409020805462010000600160901b031916905560d054614ae390600160801b90046001600160801b0316836137cc565b60d080546001600160801b03928316600160801b029216919091179055600081815260ca602052604081205460cc54614b2691859160ff61010090910416613c43565b604080518281526020810186905291925033917ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b568910160405180910390a2614b6e3084614f3f565b60008111614bb05760405162461bcd60e51b815260206004820152600f60248201526e085dda5d1a191c985dd05b5bdd5b9d608a1b60448201526064016111d1565b61186e33826137d8565b600061151d8284615b69565b600061151d8284615a5e565b6000614c27826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661508d9092919063ffffffff16565b805190915015611e6f5780806020019051810190614c45919061557f565b611e6f5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016111d1565b600054610100900460ff1680614cbd575060005460ff16155b614cd95760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614cfb576000805461ffff19166101011790555b614d0361509c565b8015613107576000805461ff001916905550565b600054610100900460ff1680614d30575060005460ff16155b614d4c5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d6e576000805461ffff19166101011790555b614d7661510b565b614d808383615175565b8015611e6f576000805461ff0019169055505050565b600054610100900460ff1680614daf575060005460ff16155b614dcb5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614ded576000805461ffff19166101011790555b614df561510b565b614d0361520a565b6001600160a01b038216614e535760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016111d1565b8060996000828254614e659190615a46565b90915550506001600160a01b03821660009081526097602052604081208054839290614e92908490615a46565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600060018311614f265760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b60448201526064016111d1565b61186e83613429614f3885600a615ac1565b8790614bba565b6001600160a01b038216614f9f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b60648201526084016111d1565b6001600160a01b038216600090815260976020526040902054818110156150135760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b60648201526084016111d1565b6001600160a01b0383166000908152609760205260408120838303905560998054849290615042908490615b88565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b606061186e848460008561526a565b600054610100900460ff16806150b5575060005460ff16155b6150d15760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156150f3576000805461ffff19166101011790555b600180558015613107576000805461ff001916905550565b600054610100900460ff1680615124575060005460ff16155b6151405760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015614d03576000805461ffff19166101011790558015613107576000805461ff001916905550565b600054610100900460ff168061518e575060005460ff16155b6151aa5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff161580156151cc576000805461ffff19166101011790555b82516151df90609a9060208601906153cb565b5081516151f390609b9060208501906153cb565b508015611e6f576000805461ff0019169055505050565b600054610100900460ff1680615223575060005460ff16155b61523f5760405162461bcd60e51b81526004016111d190615855565b600054610100900460ff16158015615261576000805461ffff19166101011790555b614d0333614210565b6060824710156152cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016111d1565b843b6153195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016111d1565b600080866001600160a01b03168587604051615335919061570b565b60006040518083038185875af1925050503d8060008114615372576040519150601f19603f3d011682016040523d82523d6000602084013e615377565b606091505b5091509150615387828286615392565b979650505050505050565b606083156153a157508161151d565b8251156153b15782518084602001fd5b8160405162461bcd60e51b81526004016111d19190615821565b8280546153d790615bcb565b90600052602060002090601f0160209004810192826153f9576000855561543f565b82601f1061541257805160ff191683800117855561543f565b8280016001018555821561543f579182015b8281111561543f578251825591602001919060010190615424565b5061544b92915061544f565b5090565b5b8082111561544b5760008155600101615450565b803561546f81615d6f565b919050565b803561546f81615d92565b803561546f81615da7565b60006020828403121561549b578081fd5b813561151d81615d6f565b600080604083850312156154b8578081fd5b82356154c381615d6f565b915060208301356154d381615d6f565b809150509250929050565b6000806000606084860312156154f2578081fd5b83356154fd81615d6f565b9250602084013561550d81615d6f565b929592945050506040919091013590565b60008060408385031215615530578182fd5b823561553b81615d6f565b946020939093013593505050565b60008060006060848603121561555d578283fd5b835161556881615d6f565b602085015160409095015190969495509392505050565b600060208284031215615590578081fd5b815161151d81615d84565b60008082840360e08112156155ae578283fd5b833567ffffffffffffffff8111156155c4578384fd5b840161016081870312156155d6578384fd5b925060c0601f19820112156155e9578182fd5b506020830190509250929050565b600060208284031215615608578081fd5b81356001600160801b038116811461151d578182fd5b60006020828403121561562f578081fd5b5035919050565b600060208284031215615647578081fd5b5051919050565b60008060408385031215615660578182fd5b8235915060208301356154d381615d6f565b60008060008060008060c0878903121561568a578182fd5b865195506020870151945060408701519350606087015192506080870151915060a087015190509295509295509295565b6000602082840312156156cc578081fd5b813563ffffffff8116811461151d578182fd5b600081518084526156f7816020860160208601615b9f565b601f01601f19169290920160200192915050565b6000825161571d818460208701615b9f565b9190910192915050565b60006101a060018060a01b03808c168452808b166020850152808a1660408501528860608501528760808501528160a0850152615766828501886156df565b915083820360c085015261577a82876156df565b92508435915061578982615d84565b90151560e08401526020840135906157a082615dbc565b60ff8216610100850152604085013591506157ba82615d6f565b166101208301526157cd60608401615464565b6001600160a01b03166101408301526157e86080840161547f565b66ffffffffffffff1661016083015261580360a08401615474565b6001600160681b038116610180840152509998505050505050505050565b60208152600061151d60208301846156df565b60208082526007908201526610b5b2b2b832b960c91b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526007908201526608585b5bdd5b9d60ca1b604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b83516001600160a01b0316815260208085015161016083019161595d908401826001600160a01b03169052565b50604085015161597860408401826001600160a01b03169052565b50606085015160608301526080850151615998608084018261ffff169052565b5060a085015160a083015260c08501516159bd60c08401826001600160a01b03169052565b5060e08501516159d860e08401826001600160a01b03169052565b5061010094850151948201949094526101208101929092526101409091015290565b6000808335601e19843603018112615a10578283fd5b83018035915067ffffffffffffffff821115615a2a578283fd5b602001915036819003821315615a3f57600080fd5b9250929050565b60008219821115615a5957615a59615c21565b500190565b600082615a7957634e487b7160e01b81526012600452602481fd5b500490565b600181815b80851115615ab9578160001904821115615a9f57615a9f615c21565b80851615615aac57918102915b93841c9390800290615a83565b509250929050565b600061151d8383600082615ad757506001611472565b81615ae457506000611472565b8160018114615afa5760028114615b0457615b20565b6001915050611472565b60ff841115615b1557615b15615c21565b50506001821b611472565b5060208310610133831016604e8410600b8410161715615b43575081810a611472565b615b4d8383615a7e565b8060001904821115615b6157615b61615c21565b029392505050565b6000816000190483118215151615615b8357615b83615c21565b500290565b600082821015615b9a57615b9a615c21565b500390565b60005b83811015615bba578181015183820152602001615ba2565b838111156137c65750506000910152565b600181811c90821680615bdf57607f821691505b60208210811415615c0057634e487b7160e01b600052602260045260246000fd5b50919050565b6000600019821415615c1a57615c1a615c21565b5060010190565b634e487b7160e01b600052601160045260246000fd5b6000813561147281615d6f565b6000813561147281615d92565b6000813561147281615da7565b8135615c6981615d84565b815460ff19811691151560ff1691821783556020840135615c8981615dbc565b61ff008160081b169050808361ffff198416171784556040850135615cad81615d6f565b6001600160b01b0319929092169092179190911760109190911b62010000600160b01b031617815560018101615d05615ce860608501615c37565b82546001600160a01b0319166001600160a01b0391909116178255565b615d3e615d1460808501615c51565b82805466ffffffffffffff60a01b191660a09290921b66ffffffffffffff60a01b16919091179055565b506113cb615d4e60a08401615c44565b600283016001600160681b0382166001600160681b03198254161781555050565b6001600160a01b038116811461310757600080fd5b801515811461310757600080fd5b6001600160681b038116811461310757600080fd5b66ffffffffffffff8116811461310757600080fd5b60ff8116811461310757600080fdfea26469706673582212205fbcaa8781187325d25b31e13bacef69edce6157041a37dc50e9cf290afbc57364736f6c63430008040033

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

000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc720000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101

-----Decoded View---------------
Arg [0] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [1] : _usdc (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _oTokenFactory (address): 0x7C06792Af1632E77cb27a558Dc0885338F4Bdf8E
Arg [3] : _gammaController (address): 0x4ccc2339F87F6c59c6893E1A678c2266cA58dC72
Arg [4] : _marginPool (address): 0x5934807cC0654d46755eBd2848840b616256C6Ef
Arg [5] : _gnosisEasyAuction (address): 0x0b7fFc1f4AD541A4Ed16b40D8c37f0929158D101

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 0000000000000000000000007c06792af1632e77cb27a558dc0885338f4bdf8e
Arg [3] : 0000000000000000000000004ccc2339f87f6c59c6893e1a678c2266ca58dc72
Arg [4] : 0000000000000000000000005934807cc0654d46755ebd2848840b616256c6ef
Arg [5] : 0000000000000000000000000b7ffc1f4ad541a4ed16b40d8c37f0929158d101


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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