ETH Price: $3,317.48 (+0.29%)
Gas: 26 Gwei

Contract

0xe1D00F9BAFea5aA40A2192aF12B68AF3D390aFE2
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x614a2061156520472022-10-01 8:14:23668 days ago1664612063IN
 Create: VaultLifecycleTreasury
0 ETH0.023152935.57758242

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VaultLifecycleTreasury

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 16 : VaultLifecycleTreasury.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 {DateTime} from "./DateTime.sol";
import {
    IOtokenFactory,
    IOtoken,
    IController,
    GammaTypes
} from "../interfaces/GammaInterface.sol";
import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol";
import {IGnosisAuction} from "../interfaces/IGnosisAuction.sol";
import {SupportsNonCompliantERC20} from "./SupportsNonCompliantERC20.sol";

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

    struct CloseParams {
        address OTOKEN_FACTORY;
        address USDC;
        address currentOption;
        uint256 delay;
        uint16 lastStrikeOverrideRound;
        uint256 overriddenStrikePrice;
        uint256 period;
    }

    /**
     * @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
     * @param _period is the period between each option sales
     */
    struct InitParams {
        address _owner;
        address _keeper;
        address _feeRecipient;
        uint256 _managementFee;
        uint256 _performanceFee;
        string _tokenName;
        string _tokenSymbol;
        address _optionsPremiumPricer;
        address _strikeSelection;
        uint32 _premiumDiscount;
        uint256 _auctionDuration;
        uint256 _period;
        uint256 _maxDepositors;
        uint256 _minDeposit;
    }

    /**
     * @notice Sets the next option the vault will be shorting, and calculates its premium for the auction
     * @param strikeSelection is the address of the contract with strike selection logic
     * @param optionsPremiumPricer is the address of the contract with the
       black-scholes premium calculation logic
     * @param premiumDiscount is the vault's discount applied to the premium
     * @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 premium is the premium 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(
        address strikeSelection,
        address optionsPremiumPricer,
        uint256 premiumDiscount,
        CloseParams calldata closeParams,
        Vault.VaultParams storage vaultParams,
        Vault.VaultState storage vaultState
    )
        external
        returns (
            address otokenAddress,
            uint256 premium,
            uint256 strikePrice,
            uint256 delta
        )
    {
        uint256 expiry;

        // uninitialized state
        if (closeParams.currentOption == address(0)) {
            expiry = getNextExpiry(block.timestamp, closeParams.period);
        } else {
            expiry = getNextExpiry(
                IOtoken(closeParams.currentOption).expiryTimestamp(),
                closeParams.period
            );
        }

        IStrikeSelection selection = IStrikeSelection(strikeSelection);

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

        (strikePrice, delta) = closeParams.lastStrikeOverrideRound ==
            vaultState.round
            ? (closeParams.overriddenStrikePrice, 0)
            : 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
        );

        // get the black scholes premium of the option
        premium = GnosisAuction.getOTokenPremiumInStables(
            otokenAddress,
            optionsPremiumPricer,
            premiumDiscount
        );

        require(premium > 0, "!premium");

        return (otokenAddress, premium, 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 currentShareSupply is the supply of the shares invoked with totalSupply()
     * @param asset is the address of the vault's asset
     * @param decimals is the decimals of the asset
     * @param lastQueuedWithdrawAmount is the amount queued for withdrawals from last round
     * @param managementFee is the management fee percent to charge on the AUM
     */
    struct RolloverParams {
        uint256 decimals;
        uint256 totalBalance;
        uint256 currentShareSupply;
        uint256 lastQueuedWithdrawAmount;
        uint256 managementFee;
    }

    /**
     * @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 managementFeeInAsset is the amount of management 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 managementFeeInAsset
        )
    {
        uint256 currentBalance = params.totalBalance;
        uint256 pendingAmount = vaultState.totalPending;
        uint256 queuedWithdrawShares = vaultState.queuedWithdrawShares;

        uint256 balanceForVaultFees;
        {
            uint256 pricePerShareBeforeFee =
                ShareMath.pricePerShare(
                    params.currentShareSupply,
                    currentBalance,
                    pendingAmount,
                    params.decimals
                );

            uint256 queuedWithdrawBeforeFee =
                params.currentShareSupply > 0
                    ? ShareMath.sharesToAsset(
                        queuedWithdrawShares,
                        pricePerShareBeforeFee,
                        params.decimals
                    )
                    : 0;

            // Deduct the difference between the newly scheduled withdrawals
            // and the older withdrawals
            // so we can charge them fees before they leave
            uint256 withdrawAmountDiff =
                queuedWithdrawBeforeFee > params.lastQueuedWithdrawAmount
                    ? queuedWithdrawBeforeFee.sub(
                        params.lastQueuedWithdrawAmount
                    )
                    : 0;

            balanceForVaultFees = currentBalance
                .sub(queuedWithdrawBeforeFee)
                .add(withdrawAmountDiff);
        }

        managementFeeInAsset = getManagementFee(
            balanceForVaultFees,
            vaultState.totalPending,
            params.managementFee
        );

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

        {
            newPricePerShare = ShareMath.pricePerShare(
                params.currentShareSupply,
                currentBalance,
                pendingAmount,
                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
            );

            uint256 newSupply = params.currentShareSupply.add(mintShares);

            queuedWithdrawAmount = newSupply > 0
                ? ShareMath.sharesToAsset(
                    queuedWithdrawShares,
                    newPricePerShare,
                    params.decimals
                )
                : 0;
        }

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

    /**
     * @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 management fee for this week's round
     * @param currentBalance is the balance of funds held on the vault after closing short
     * @param pendingAmount is the pending deposit amount
     * @param managementFeePercent is the management fee pct.
     * @return managementFeeInAsset is the management fee
     */
    function getManagementFee(
        uint256 currentBalance,
        uint256 pendingAmount,
        uint256 managementFeePercent
    ) internal pure returns (uint256 managementFeeInAsset) {
        // 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 _managementFeeInAsset;

        // Always charge management fee regardless of whether the vault is
        // making a profit from the previous options sale
        _managementFeeInAsset = managementFeePercent > 0
            ? lockedBalanceSansPending.mul(managementFeePercent).div(
                100 * Vault.FEE_MULTIPLIER
            )
            : 0;

        return _managementFeeInAsset;
    }

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

    /**
     * @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 Verify the constructor params satisfy requirements
     * @param _initParams is the initialization parameter including owner, keeper, etc.
     * @param _vaultParams is the struct with vault general data
     */
    function verifyInitializerParams(
        InitParams calldata _initParams,
        Vault.VaultParams calldata _vaultParams,
        uint256 _min_auction_duration
    ) external pure {
        require(_initParams._owner != address(0), "!_owner");
        require(_initParams._keeper != address(0), "!_keeper");
        require(_initParams._feeRecipient != address(0), "!_feeRecipient");
        require(
            _initParams._performanceFee < 100 * Vault.FEE_MULTIPLIER,
            "performanceFee >= 100%"
        );
        require(
            _initParams._managementFee < 100 * Vault.FEE_MULTIPLIER,
            "managementFee >= 100%"
        );
        require(bytes(_initParams._tokenName).length > 0, "!_tokenName");
        require(bytes(_initParams._tokenSymbol).length > 0, "!_tokenSymbol");
        require(
            (_initParams._period == 7) ||
                (_initParams._period == 14) ||
                (_initParams._period == 30) ||
                (_initParams._period == 90) ||
                (_initParams._period == 180),
            "!_period"
        );
        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"
        );
        require(_initParams._maxDepositors > 0, "!_maxDepositors");
        require(_initParams._minDeposit > 0, "!_minDeposit");

        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 options expiry timestamp, this function should be called
     when there is sufficient guard to ensure valid period
     * @param timestamp is the expiry timestamp of the current option
     * @param period is no. of days in between option sales. Available periods are: 
     * 7(1w), 14(2w), 30(1m), 90(3m), 180(6m)
     */
    function getNextExpiry(uint256 timestamp, uint256 period)
        internal
        pure
        returns (uint256 nextExpiry)
    {
        if (period == 7) {
            nextExpiry = DateTime.getNextFriday(timestamp);
            nextExpiry = nextExpiry <= timestamp
                ? nextExpiry + 1 weeks
                : nextExpiry;
        } else if (period == 14) {
            nextExpiry = DateTime.getNextFriday(timestamp);
            nextExpiry = nextExpiry <= timestamp
                ? nextExpiry + 2 weeks
                : nextExpiry;
        } else if (period == 30) {
            nextExpiry = DateTime.getMonthLastFriday(timestamp);
            nextExpiry = nextExpiry <= timestamp
                ? DateTime.getMonthLastFriday(nextExpiry + 1 weeks)
                : nextExpiry;
        } else if (period == 90) {
            nextExpiry = DateTime.getQuarterLastFriday(timestamp);
            nextExpiry = nextExpiry <= timestamp
                ? DateTime.getQuarterLastFriday(nextExpiry + 1 weeks)
                : nextExpiry;
        } else if (period == 180) {
            nextExpiry = DateTime.getBiannualLastFriday(timestamp);
            nextExpiry = nextExpiry <= timestamp
                ? DateTime.getBiannualLastFriday(nextExpiry + 1 weeks)
                : nextExpiry;
        }

        nextExpiry = nextExpiry - (nextExpiry % (24 hours)) + (8 hours);
    }
}

File 2 of 16 : 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 16 : 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 16 : 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 5 of 16 : 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 6 of 16 : 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 7 of 16 : 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 8 of 16 : DateTime.sol
// SPDX-License-Identifier: MIT
// Source: https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
// ----------------------------------------------------------------------------
// BokkyPooBah's DateTime Library v1.01
// ----------------------------------------------------------------------------

pragma solidity =0.8.4;

library DateTime {
    uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
    uint256 constant SECONDS_PER_HOUR = 60 * 60;
    uint256 constant SECONDS_PER_MINUTE = 60;
    int256 constant OFFSET19700101 = 2440588;

    uint256 constant DOW_MON = 1;
    uint256 constant DOW_TUE = 2;
    uint256 constant DOW_WED = 3;
    uint256 constant DOW_THU = 4;
    uint256 constant DOW_FRI = 5;
    uint256 constant DOW_SAT = 6;
    uint256 constant DOW_SUN = 7;

    // ------------------------------------------------------------------------
    // Calculate the number of days from 1970/01/01 to year/month/day using
    // the date conversion algorithm from
    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php
    // and subtracting the offset 2440588 so that 1970/01/01 is day 0
    //
    // days = day
    //      - 32075
    //      + 1461 * (year + 4800 + (month - 14) / 12) / 4
    //      + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
    //      - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
    //      - offset
    // ------------------------------------------------------------------------
    function _daysFromDate(
        uint256 year,
        uint256 month,
        uint256 day
    ) internal pure returns (uint256 _days) {
        require(year >= 1970);
        int256 _year = int256(year);
        int256 _month = int256(month);
        int256 _day = int256(day);

        int256 __days =
            _day -
                32075 +
                (1461 * (_year + 4800 + (_month - 14) / 12)) /
                4 +
                (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
                12 -
                (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
                4 -
                OFFSET19700101;

        _days = uint256(__days);
    }

    // ------------------------------------------------------------------------
    // Calculate year/month/day from the number of days since 1970/01/01 using
    // the date conversion algorithm from
    //   http://aa.usno.navy.mil/faq/docs/JD_Formula.php
    // and adding the offset 2440588 so that 1970/01/01 is day 0
    //
    // int L = days + 68569 + offset
    // int N = 4 * L / 146097
    // L = L - (146097 * N + 3) / 4
    // year = 4000 * (L + 1) / 1461001
    // L = L - 1461 * year / 4 + 31
    // month = 80 * L / 2447
    // dd = L - 2447 * month / 80
    // L = month / 11
    // month = month + 2 - 12 * L
    // year = 100 * (N - 49) + year + L
    // ------------------------------------------------------------------------
    function _daysToDate(uint256 _days)
        internal
        pure
        returns (
            uint256 year,
            uint256 month,
            uint256 day
        )
    {
        int256 __days = int256(_days);

        int256 L = __days + 68569 + OFFSET19700101;
        int256 N = (4 * L) / 146097;
        L = L - (146097 * N + 3) / 4;
        int256 _year = (4000 * (L + 1)) / 1461001;
        L = L - (1461 * _year) / 4 + 31;
        int256 _month = (80 * L) / 2447;
        int256 _day = L - (2447 * _month) / 80;
        L = _month / 11;
        _month = _month + 2 - 12 * L;
        _year = 100 * (N - 49) + _year + L;

        year = uint256(_year);
        month = uint256(_month);
        day = uint256(_day);
    }

    function isLeapYear(uint256 timestamp)
        internal
        pure
        returns (bool leapYear)
    {
        (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
        leapYear = _isLeapYear(year);
    }

    function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
        leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
    }

    function getDaysInMonth(uint256 timestamp)
        internal
        pure
        returns (uint256 daysInMonth)
    {
        (uint256 year, uint256 month, ) =
            _daysToDate(timestamp / SECONDS_PER_DAY);
        daysInMonth = _getDaysInMonth(year, month);
    }

    function _getDaysInMonth(uint256 year, uint256 month)
        internal
        pure
        returns (uint256 daysInMonth)
    {
        if (
            month == 1 ||
            month == 3 ||
            month == 5 ||
            month == 7 ||
            month == 8 ||
            month == 10 ||
            month == 12
        ) {
            daysInMonth = 31;
        } else if (month != 2) {
            daysInMonth = 30;
        } else {
            daysInMonth = _isLeapYear(year) ? 29 : 28;
        }
    }

    // 1 = Monday, 7 = Sunday
    function getDayOfWeek(uint256 timestamp)
        internal
        pure
        returns (uint256 dayOfWeek)
    {
        uint256 _days = timestamp / SECONDS_PER_DAY;
        dayOfWeek = ((_days + 3) % 7) + 1;
    }

    function getYear(uint256 timestamp) internal pure returns (uint256 year) {
        (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }

    function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
        (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }

    function getDay(uint256 timestamp) internal pure returns (uint256 day) {
        (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
    }

    function timestampFromDate(
        uint256 year,
        uint256 month,
        uint256 day
    ) internal pure returns (uint256 timestamp) {
        timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
    }

    /**
     * @notice Gets the Friday of the same week
     * @param timestamp is the given date and time
     * @return the Friday of the same week in unix time
     */
    function getThisWeekFriday(uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        return timestamp + 5 days - getDayOfWeek(timestamp) * 1 days;
    }

    /**
     * @notice Gets the next friday after the given date and time
     * @param timestamp is the given date and time
     * @return the next friday after the given date and time
     */
    function getNextFriday(uint256 timestamp) internal pure returns (uint256) {
        uint256 friday = getThisWeekFriday(timestamp);
        return friday >= timestamp ? friday : friday + 1 weeks;
    }

    /**
     * @notice Gets the last day of the month
     * @param timestamp is the given date and time
     * @return the last day of the same month in unix time
     */
    function getLastDayOfMonth(uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        return
            timestampFromDate(getYear(timestamp), getMonth(timestamp) + 1, 1) -
            1 days;
    }

    /**
     * @notice Gets the last Friday of the month
     * @param timestamp is the given date and time
     * @return the last Friday of the same month in unix time
     */
    function getMonthLastFriday(uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        uint256 lastDay = getLastDayOfMonth(timestamp);
        uint256 friday = getThisWeekFriday(lastDay);

        return friday > lastDay ? friday - 1 weeks : friday;
    }

    /**
     * @notice Gets the last Friday of the quarter
     * @param timestamp is the given date and time
     * @return the last Friday of the quarter in unix time
     */
    function getQuarterLastFriday(uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        uint256 month = getMonth(timestamp);
        uint256 quarterMonth =
            (month <= 3) ? 3 : (month <= 6) ? 6 : (month <= 9) ? 9 : 12;

        uint256 quarterDate =
            timestampFromDate(getYear(timestamp), quarterMonth, 1);

        return getMonthLastFriday(quarterDate);
    }

    /**
     * @notice Gets the last Friday of the half-year
     * @param timestamp is the given date and time
     * @return the last friday of the half-year
     */
    function getBiannualLastFriday(uint256 timestamp)
        internal
        pure
        returns (uint256)
    {
        uint256 month = getMonth(timestamp);
        uint256 biannualMonth = (month <= 6) ? 6 : 12;

        uint256 biannualDate =
            timestampFromDate(getYear(timestamp), biannualMonth, 1);

        return getMonthLastFriday(biannualDate);
    }
}

File 9 of 16 : 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 10 of 16 : 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 11 of 16 : 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 12 of 16 : 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 13 of 16 : 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 14 of 16 : 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 16 : 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 16 of 16 : 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);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"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":"uint256","name":"_period","type":"uint256"},{"internalType":"uint256","name":"_maxDepositors","type":"uint256"},{"internalType":"uint256","name":"_minDeposit","type":"uint256"}],"internalType":"struct VaultLifecycleTreasury.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"},{"internalType":"uint256","name":"_min_auction_duration","type":"uint256"}],"name":"verifyInitializerParams","outputs":[],"stateMutability":"pure","type":"function"}]

614a2061003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100a85760003560e01c806384d831a91161007057806384d831a9146101a757806395a170d7146101ba578063d8dfd0fe146101ff578063de8e34181461021f578063eebc5ff41461023f57600080fd5b806307cc4239146100ad5780632904c239146100cf57806358ffbb3d146101025780635a90235d14610122578063686fb2f21461016c575b600080fd5b8180156100b957600080fd5b506100cd6100c8366004613fb6565b61025f565b005b8180156100db57600080fd5b506100ef6100ea366004613ebb565b61026f565b6040519081526020015b60405180910390f35b81801561010e57600080fd5b506100ef61011d366004613e39565b6107d9565b81801561012e57600080fd5b5061014261013d366004613f0b565b610d6e565b604080516001600160a01b03909516855260208501939093529183015260608201526080016100f9565b61017f61017a366004614068565b610fb3565b604080519586526020860194909452928401919091526060830152608082015260a0016100f9565b6100cd6101b536600461400c565b6110e5565b8180156101c657600080fd5b506101da6101d5366004613ffa565b6117ae565b60408051938452602084019290925267ffffffffffffffff16908201526060016100f9565b81801561020b57600080fd5b506100ef61021a366004613e01565b6117c9565b81801561022b57600080fd5b506100ef61023a366004613e71565b611b9d565b81801561024b57600080fd5b506100ef61025a366004613f9b565b611ee9565b61026a838383611ef4565b505050565b604051636553690d60e11b8152306004820152600090859082906102fa906001906001600160a01b0385169063caa6d21a9060240160206040518083038186803b1580156102bc57600080fd5b505afa1580156102d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f491906141ff565b90612077565b905060008590506000816001600160a01b031663aabaecd66040518163ffffffff1660e01b815260040160206040518083038186803b15801561033c57600080fd5b505afa158015610350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103749190613e1d565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b157600080fd5b505afa1580156103c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e991906142bd565b60ff1690506000836001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b15801561042957600080fd5b505afa15801561043d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104619190613f7b565b15610528576105216104f961047784600a6146f9565b61048290600a614796565b866001600160a01b031663c52987cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104bb57600080fd5b505afa1580156104cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f391906141ff565b90612083565b61051b670de0b6b3a76400006104f36105146008600a614796565b8d90612083565b9061208f565b9050610564565b5086600882111561056457600061054083600861209b565b61054b90600a614796565b9050808211156105625761055f898261208f565b91505b505b826105796001600160a01b0382168c8b6120a7565b60408051600380825260808201909252600091816020015b610599613cb4565b815260200190600190039081610591579050506040805161010081019091529091508060008152602001306001600160a01b03168152602001306001600160a01b0316815260200160006001600160a01b031681526020018881526020016000815260200160008152602001604051806020016040528060008152508152508160008151811061063957634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060058152602001306001600160a01b03168152602001306001600160a01b03168152602001866001600160a01b031681526020018881526020018b81526020016000815260200160405180602001604052806000815250815250816001815181106106cc57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060018152602001306001600160a01b03168152602001306001600160a01b031681526020018c6001600160a01b0316815260200188815260200184815260200160008152602001604051806020016040528060008152508152508160028151811061075f57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0389169063b617f0c69061079690849060040161447d565b600060405180830381600087803b1580156107b057600080fd5b505af11580156107c4573d6000803e3d6000fd5b50949f9e505050505050505050505050505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038416906370a082319060240160206040518083038186803b15801561081d57600080fd5b505afa158015610831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085591906141ff565b9050600081116108a15760405162461bcd60e51b815260206004820152601260248201527127379037aa37b5b2b739903a3790313ab93760711b60448201526064015b60405180910390fd5b604051636553690d60e11b815230600482015284906000906001600160a01b0383169063caa6d21a9060240160206040518083038186803b1580156108e557600080fd5b505afa1580156108f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091d91906141ff565b60405163d99d13f560e01b8152306004820152602481018290529091506000906001600160a01b0384169063d99d13f59060440160006040518083038186803b15801561096957600080fd5b505afa15801561097d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109a5919081019061409e565b8051519091506109e25760405162461bcd60e51b8152602060048201526008602482015267139bc81cda1bdc9d60c21b6044820152606401610898565b60008160400151600081518110610a0957634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a082319060240160206040518083038186803b158015610a5957600080fd5b505afa158015610a6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9191906141ff565b6040805160028082526060820190925291925060009190816020015b610ab5613cb4565b815260200190600190039081610aad5790505060408051610100810190915290915080600281523060208201819052604082015285518051606090920191600090610b1057634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03168152602001868152602001888152602001600081526020016040518060200160405280600081525081525081600081518110610b6d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060068152602001306001600160a01b03168152602001306001600160a01b03168152602001846001600160a01b03168152602001868152602001610c2e8660600151600081518110610be757634e487b7160e01b600052603260045260246000fd5b602002602001015161051b8b8960a00151600081518110610c1857634e487b7160e01b600052603260045260246000fd5b602002602001015161208390919063ffffffff16565b8152602001600081526020016040518060200160405280600081525081525081600181518110610c6e57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0387169063b617f0c690610ca590849060040161447d565b600060405180830381600087803b158015610cbf57600080fd5b505af1158015610cd3573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03861691506370a082319060240160206040518083038186803b158015610d1957600080fd5b505afa158015610d2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5191906141ff565b9050610d5d818461209b565b985050505050505050505b92915050565b60008080808080610d8560608a0160408b01613e01565b6001600160a01b03161415610da957610da2428960c001356120e3565b9050610e39565b610e36610dbc60608a0160408b01613e01565b6001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b158015610df457600080fd5b505afa158015610e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2c91906141ff565b8960c001356120e3565b90505b8654600188015487548d9260ff8116926001600160a01b039081169262010000909204169061ffff16610e7260a08e0160808f016141dd565b61ffff1614610eff5760405163e436b89160e01b81526004810186905283151560248201526001600160a01b0385169063e436b89190604401604080518083038186803b158015610ec257600080fd5b505afa158015610ed6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efa9190614217565b610f07565b8b60a0013560005b909750955086610f485760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b6044820152606401610898565b610f578c8c84848b8a89612215565b9850610f64898f8f6123a9565b975060008811610fa15760405162461bcd60e51b8152602060048201526008602482015267217072656d69756d60c01b6044820152606401610898565b50505050509650965096509692505050565b600182015460009081908190819081906020870135906001600160801b0380821691600160801b9004168380610ff060408c013586868e35612624565b90506000808c6040013511611006576000611012565b61101284838e3561265a565b905060008c606001358211611028576000611036565b6110368260608f013561209b565b9050611046816102f4898561209b565b60018f015490945061106b93508492506001600160801b0316905060808d01356126c4565b9450611077848661209b565b935061108a60408b013585858d35612624565b965061109883888c3561270c565b955060006110aa60408c013588612077565b9050600081116110bb5760006110c7565b6110c783898d3561265a565b98506110d59050848961209b565b9850505050509295509295909350565b60006110f46020850185613e01565b6001600160a01b031614156111355760405162461bcd60e51b815260206004820152600760248201526610afb7bbb732b960c91b6044820152606401610898565b60006111476040850160208601613e01565b6001600160a01b031614156111895760405162461bcd60e51b815260206004820152600860248201526710afb5b2b2b832b960c11b6044820152606401610898565b600061119b6060850160408601613e01565b6001600160a01b031614156111e35760405162461bcd60e51b815260206004820152600e60248201526d0857d99959549958da5c1a595b9d60921b6044820152606401610898565b6111f1620f424060646148c1565b83608001351061123c5760405162461bcd60e51b8152602060048201526016602482015275706572666f726d616e6365466565203e3d203130302560501b6044820152606401610898565b61124a620f424060646148c1565b8360600135106112945760405162461bcd60e51b81526020600482015260156024820152746d616e6167656d656e74466565203e3d203130302560581b6044820152606401610898565b60006112a360a08501856145ee565b9050116112e05760405162461bcd60e51b815260206004820152600b60248201526a215f746f6b656e4e616d6560a81b6044820152606401610898565b60006112ef60c08501856145ee565b90501161132e5760405162461bcd60e51b815260206004820152600d60248201526c0857dd1bdad95b94de5b589bdb609a1b6044820152606401610898565b826101600135600714806113475750826101600135600e145b806113575750826101600135601e145b806113675750826101600135605a145b80611377575082610160013560b4145b6113ae5760405162461bcd60e51b81526020600482015260086024820152670857dc195c9a5bd960c21b6044820152606401610898565b60006113c1610100850160e08601613e01565b6001600160a01b031614156114115760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b6044820152606401610898565b600061142561012085016101008601613e01565b6001600160a01b031614156114705760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b6044820152606401610898565b60006114846101408501610120860161423a565b63ffffffff161180156114b8575061149e600a60646148c1565b6114b06101408501610120860161423a565b63ffffffff16105b6114f85760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b6044820152606401610898565b8083610140013510156115415760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b6044820152606401610898565b6000836101800135116115885760405162461bcd60e51b815260206004820152600f60248201526e215f6d61784465706f7369746f727360881b6044820152606401610898565b6000836101a00135116115cc5760405162461bcd60e51b815260206004820152600c60248201526b0857db5a5b91195c1bdcda5d60a21b6044820152606401610898565b60006115de6060840160408501613e01565b6001600160a01b0316141561161e5760405162461bcd60e51b815260206004820152600660248201526508585cdcd95d60d21b6044820152606401610898565b60006116306080840160608501613e01565b6001600160a01b031614156116755760405162461bcd60e51b815260206004820152600b60248201526a21756e6465726c79696e6760a81b6044820152606401610898565b600061168760a084016080850161425e565b66ffffffffffffff16116116ce5760405162461bcd60e51b815260206004820152600e60248201526d216d696e696d756d537570706c7960901b6044820152606401610898565b60006116e060c0840160a085016141b6565b6001600160681b03161161171f5760405162461bcd60e51b8152600401610898906020808252600490820152630216361760e41b604082015260600190565b61172f60a083016080840161425e565b66ffffffffffffff1661174860c0840160a085016141b6565b6001600160681b03161161026a5760405162461bcd60e51b815260206004820152602760248201527f6361702068617320746f20626520686967686572207468616e206d696e696d756044820152666d537570706c7960c81b6064820152608401610898565b60008060006117bc8461276f565b9250925092509193909250565b604051636553690d60e11b8152306004820152600090829082906001600160a01b0383169063caa6d21a9060240160206040518083038186803b15801561180f57600080fd5b505afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906141ff565b60405163d99d13f560e01b8152306004820152602481018290529091506000906001600160a01b0384169063d99d13f59060440160006040518083038186803b15801561189357600080fd5b505afa1580156118a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118cf919081019061409e565b80515190915061190c5760405162461bcd60e51b8152602060048201526008602482015267139bc81cda1bdc9d60c21b6044820152606401610898565b6000816040015160008151811061193357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060006001600160a01b0316816001600160a01b031614156119645750600095945050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a082319060240160206040518083038186803b1580156119a657600080fd5b505afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de91906141ff565b60408051600180825281830190925291925060009190816020015b611a01613cb4565b8152602001906001900390816119f9579050506040805161010081019091529091508060078152602001306001600160a01b03168152602001306001600160a01b0316815260200160006001600160a01b0316815260200186815260200160008152602001600081526020016040518060200160405280600081525081525081600081518110611aa157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0387169063b617f0c690611ad890849060040161447d565b600060405180830381600087803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03861691506370a082319060240160206040518083038186803b158015611b4c57600080fd5b505afa158015611b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8491906141ff565b9050611b90818461209b565b9998505050505050505050565b6040516370a0823160e01b8152306004820152600090849082906001600160a01b038616906370a082319060240160206040518083038186803b158015611be357600080fd5b505afa158015611bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1b91906141ff565b60405163565eea1960e01b81526001600160a01b038781166004830152602482018390529192509083169063565eea199060440160206040518083038186803b158015611c6757600080fd5b505afa158015611c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9f91906141ff565b611cae57600092505050611ee2565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a082319060240160206040518083038186803b158015611cf057600080fd5b505afa158015611d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2891906141ff565b60408051600180825281830190925291925060009190816020015b611d4b613cb4565b815260200190600190039081611d4357905050604080516101008101909152909150806008815260200160006001600160a01b03168152602001306001600160a01b03168152602001886001600160a01b0316815260200160008152602001848152602001600081526020016040518060200160405280600081525081525081600081518110611deb57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0385169063b617f0c690611e2290849060040161447d565b600060405180830381600087803b158015611e3c57600080fd5b505af1158015611e50573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03891691506370a082319060240160206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ece91906141ff565b9050611eda818461209b565b955050505050505b9392505050565b6000610d6882612b0b565b6000611f2b611f096060860160408701614285565b611f1960408701602088016142de565b611f2660208801886142de565b612e59565b60408051600180825281830190925291925060009190602080830190803683370190505090508181600081518110611f7357634e487b7160e01b600052603260045260246000fd5b602002602001018181525050836001600160a01b0316637882deaf846001600160a01b031663432833a66040518163ffffffff1660e01b815260040160206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fff91906141ff565b836040518363ffffffff1660e01b815260040161201d92919061456e565b6040805180830381600087803b15801561203657600080fd5b505af115801561204a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206e9190614217565b50505050505050565b6000611ee282846146f9565b6000611ee282846148c1565b6000611ee2828461473f565b6000611ee2828461491f565b6001600160a01b03831673dac17f958d2ee523a2206206994597c13d831ec714156120d8576120d883836000612e9c565b61026a838383612e9c565b6000816007141561211b576120f783612fe2565b9050828111156121075780612114565b6121148162093a806146f9565b90506121f2565b81600e141561214a5761212d83612fe2565b90508281111561213d5780612114565b61211481621275006146f9565b81601e14156121815761215c8361300a565b90508281111561216c5780612114565b61211461217c8262093a806146f9565b61300a565b81605a14156121b8576121938361303f565b9050828111156121a35780612114565b6121146121b38262093a806146f9565b61303f565b8160b414156121f2576121ca836130b1565b9050828111156121da57806121ef565b6121ef6121ea8262093a806146f9565b6130b1565b90505b6121ff6201518082614966565b612209908261491f565b611ee2906170806146f9565b60008061222560208a018a613e01565b905060006001600160a01b03821663115470548961224960408e0160208f01613e01565b8a8a8a8a6040518763ffffffff1660e01b815260040161226e969594939291906143bf565b60206040518083038186803b15801561228657600080fd5b505afa15801561229a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122be9190613e1d565b90506001600160a01b038116156122d857915061239e9050565b6000826001600160a01b031663c09746308a8d60200160208101906122fd9190613e01565b8b8b8b8b6040518763ffffffff1660e01b8152600401612322969594939291906143bf565b602060405180830381600087803b15801561233c57600080fd5b505af1158015612350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123749190613e1d565b9050612399818b8a8e602001602081019061238f9190613e01565b8f606001356130e2565b925050505b979650505050505050565b60008084905060008490506000816001600160a01b03166330a75871846001600160a01b031663c52987cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123fe57600080fd5b505afa158015612412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243691906141ff565b856001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561246f57600080fd5b505afa158015612483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a791906141ff565b866001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b1580156124e057600080fd5b505afa1580156124f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125189190613f7b565b6040516001600160e01b031960e086901b168152600481019390935260248301919091521515604482015260640160206040518083038186803b15801561255e57600080fd5b505afa158015612572573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259691906141ff565b90506125b16125a7600a60646148c1565b61051b8388612083565b90506001600160601b0381111561261a5760405162461bcd60e51b815260206004820152602760248201527f6f7074696f6e5072656d69756d203e20747970652875696e74393629206d61786044820152662076616c75652160c81b6064820152608401610898565b9695505050505050565b60008061263283600a614796565b905060008611612642578061261a565b61261a8661051b612653888861209b565b8490612083565b6000600183116126a45760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610898565b6126bc6126b283600a614796565b61051b8686612083565b949350505050565b6000808385116126d55760006126df565b6126df858561209b565b905060008084116126f157600061261a565b61261a612702620f424060646148c1565b61051b8487612083565b6000600183116127565760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610898565b6126bc8361051b61276885600a614796565b8790612083565b600080806127936127816064806148c1565b61051b60a087013560c0880135612083565b92506127d16127a76060860135600a614796565b61051b60e0870135816127bf60608a01356008612077565b6127ca90600a614796565b8890612083565b91506001600160601b038311156128365760405162461bcd60e51b8152602060048201526024808201527f73656c6c416d6f756e74203e20747970652875696e74393629206d61782076616044820152636c75652160e01b6064820152608401610898565b6001600160601b038211156128995760405162461bcd60e51b815260206004820152602360248201527f627579416d6f756e74203e20747970652875696e74393629206d61782076616c60448201526275652160e81b6064820152608401610898565b6128cd6128ac6040860160208701613e01565b846128bd6060880160408901613e01565b6001600160a01b03169190612e9c565b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848360008151811061295757634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160601b031690816001600160601b031681525050858260008151811061299957634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160601b031690816001600160601b031681525050600160001b816000815181106129df57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508660200160208101906129fe9190613e01565b6001600160a01b031663d225269c88608001358585856040518563ffffffff1660e01b8152600401612a339493929190614587565b602060405180830381600087803b158015612a4d57600080fd5b505af1158015612a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8591906142a1565b9350612a9961012088016101008901613e01565b6001600160a01b0316612aaf6020890189613e01565b6040805160808b01358152602081018a90529081018890526001600160a01b0391909116907fa227b5eceb2bd983c0ed9dd34c971be74cd4f386b289d4415602a1fbcf2cb53b9060600160405180910390a35050509193909250565b600080612b23612b1e6020850185613e01565b61350e565b905060008111612b6a5760405162461bcd60e51b8152602060048201526012602482015271139bc81bdd1bdad95b9cc81d1bc81cd95b1b60721b6044820152606401610898565b612c10612b7d6040850160208601613e01565b612b8a6020860186613e01565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b158015612bcb57600080fd5b505afa158015612bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0391906141ff565b6128bd6020870187613e01565b6000612c2e612c24836402540be400612083565b85608001356135f6565b90506012846060013511612c6557612c60612c4e6012606087013561209b565b612c5990600a614796565b829061208f565b612c89565b612c89612c776060860135601261209b565b612c8290600a614796565b8290612083565b90506001600160601b03811115612d085760405162461bcd60e51b815260206004820152603a60248201527f6f7074696f6e5072656d69756d202a206f546f6b656e53656c6c416d6f756e7460448201527f203e20747970652875696e74393629206d61782076616c7565210000000000006064820152608401610898565b6000612d184260a0870135612077565b9050612d2a6040860160208701613e01565b6001600160a01b0316630a4cd6c9612d456020880188613e01565b612d556060890160408a01613e01565b60408051602081018252600080825291516001600160e01b031960e087901b168152612d94949392889283928c928c92600192829182916004016143f8565b602060405180830381600087803b158015612dae57600080fd5b505af1158015612dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de691906141ff565b935033612df96060870160408801613e01565b6001600160a01b0316612e0f6020880188613e01565b6001600160a01b03167f95ad3b10488285d6307fda297f633faaf2a0d713c08ebe5f49c1b9255b01d29e87604051612e4991815260200190565b60405180910390a4505050919050565b60006001600160601b038216612e926bffffffffffffffffffffffff60601b606086901b166001600160c01b031960c088901b166146f9565b6126bc91906146f9565b801580612f255750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b158015612eeb57600080fd5b505afa158015612eff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2391906141ff565b155b612f905760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610898565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261026a908490613630565b600080612fee83613702565b905082811015610d68576130058162093a806146f9565b611ee2565b60008061301683613731565b9050600061302382613702565b905081811161303257806126bc565b6126bc62093a808261491f565b60008061304b8361375e565b90506000600382111561307f57600682111561307857600982111561307157600c613082565b6009613082565b6006613082565b60035b60ff169050600061309d6130958661377d565b836001613798565b90506130a88161300a565b95945050505050565b6000806130bd8361375e565b9050600060068211156130d157600c613082565b506006600061309d6130958661377d565b6001600160a01b0385166131295760405162461bcd60e51b815260206004820152600e60248201526d216f746f6b656e4164647265737360901b6044820152606401610898565b8354604080516379e13a5360e11b81529051879260ff161515916001600160a01b0384169163f3c274a691600480820192602092909190829003018186803b15801561317457600080fd5b505afa158015613188573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ac9190613f7b565b1515146131eb5760405162461bcd60e51b815260206004820152600d60248201526c0a8f2e0ca40dad2e6dac2e8c6d609b1b6044820152606401610898565b600185015460408051631c56369f60e21b815290516001600160a01b0392831692841691637158da7c916004808301926020929190829003018186803b15801561323457600080fd5b505afa158015613248573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061326c9190613e1d565b6001600160a01b0316146132ba5760405162461bcd60e51b815260206004820152601560248201527415dc9bdb99c81d5b99195c9b1e5a5b99d05cdcd95d605a1b6044820152606401610898565b836001600160a01b0316816001600160a01b031663aabaecd66040518163ffffffff1660e01b815260040160206040518083038186803b1580156132fd57600080fd5b505afa158015613311573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133359190613e1d565b6001600160a01b0316146133835760405162461bcd60e51b815260206004820152601560248201527415dc9bdb99c818dbdb1b185d195c985b105cdcd95d605a1b6044820152606401610898565b826001600160a01b0316816001600160a01b03166317d69bc86040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c657600080fd5b505afa1580156133da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fe9190613e1d565b6001600160a01b03161461344a5760405162461bcd60e51b8152602060048201526013602482015272737472696b65417373657420213d205553444360681b6044820152606401610898565b60006134564284612077565b905080826001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561349257600080fd5b505afa1580156134a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134ca91906141ff565b101561206e5760405162461bcd60e51b8152602060048201526013602482015272457870697279206265666f72652064656c617960681b6044820152606401610898565b6040516370a0823160e01b815230600482015260009081906001600160a01b038416906370a082319060240160206040518083038186803b15801561355257600080fd5b505afa158015613566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061358a91906141ff565b90506001600160601b03811115610d685760405162461bcd60e51b815260206004820152602a60248201527f6f546f6b656e53656c6c416d6f756e74203e20747970652875696e74393629206044820152696d61782076616c75652160b01b6064820152608401610898565b6000670de0b6b3a764000061362661360e85856137b3565b6136216002670de0b6b3a764000061473f565b61381a565b611ee2919061473f565b6000613685826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661386f9092919063ffffffff16565b80519091501561026a57808060200190518101906136a39190613f7b565b61026a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610898565b600061370d8261387e565b61371a90620151806148c1565b61372783620697806146f9565b610d68919061491f565b6000620151806137276137438461377d565b61374c8561375e565b6137579060016146f9565b6001613798565b6000613775613770620151808461473f565b6138b2565b509392505050565b600061378f613770620151808461473f565b50909392505050565b6000620151806137a9858585613a26565b6126bc91906148c1565b60008115806137d7575082826137c981836148c1565b92506137d5908361473f565b145b610d685760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b6044820152606401610898565b60008261382783826146f9565b9150811015610d685760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b6044820152606401610898565b60606126bc8484600085613b63565b60008061388e620151808461473f565b9050600761389d8260036146f9565b6138a79190614966565b611ee29060016146f9565b60008080838162253d8c6138c98362010bd96146b8565b6138d391906146b8565b9050600062023ab16138e683600461483e565b6138f09190614711565b905060046139018262023ab161483e565b61390c9060036146b8565b6139169190614711565b61392090836148e0565b9150600062164b096139338460016146b8565b61393f90610fa061483e565b6139499190614711565b90506004613959826105b561483e565b6139639190614711565b61396d90846148e0565b61397890601f6146b8565b9250600061098f61398a85605061483e565b6139949190614711565b9050600060506139a68361098f61483e565b6139b09190614711565b6139ba90866148e0565b90506139c7600b83614711565b94506139d485600c61483e565b6139df8360026146b8565b6139e991906148e0565b915084836139f86031876148e0565b613a0390606461483e565b613a0d91906146b8565b613a1791906146b8565b9a919950975095505050505050565b60006107b2841015613a3757600080fd5b838383600062253d8c60046064600c613a51600e886148e0565b613a5b9190614711565b613a67886113246146b8565b613a7191906146b8565b613a7b9190614711565b613a8690600361483e565b613a909190614711565b600c80613a9e600e886148e0565b613aa89190614711565b613ab390600c61483e565b613abe6002886148e0565b613ac891906148e0565b613ad49061016f61483e565b613ade9190614711565b6004600c613aed600e896148e0565b613af79190614711565b613b03896112c06146b8565b613b0d91906146b8565b613b19906105b561483e565b613b239190614711565b613b2f617d4b876148e0565b613b3991906146b8565b613b4391906146b8565b613b4d91906148e0565b613b5791906148e0565b98975050505050505050565b606082471015613bc45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610898565b843b613c125760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610898565b600080866001600160a01b03168587604051613c2e91906143a3565b60006040518083038185875af1925050503d8060008114613c6b576040519150601f19603f3d011682016040523d82523d6000602084013e613c70565b606091505b509150915061239e82828660608315613c8a575081611ee2565b825115613c9a5782518084602001fd5b8160405162461bcd60e51b8152600401610898919061455b565b604080516101008101909152806000815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001606081525090565b600082601f830112613d25578081fd5b81516020613d3a613d3583614694565b614663565b80838252828201915082860187848660051b8901011115613d59578586fd5b855b85811015613d80578151613d6e816149bc565b84529284019290840190600101613d5b565b5090979650505050505050565b600082601f830112613d9d578081fd5b81516020613dad613d3583614694565b80838252828201915082860187848660051b8901011115613dcc578586fd5b855b85811015613d8057815184529284019290840190600101613dce565b600060c08284031215613dfb578081fd5b50919050565b600060208284031215613e12578081fd5b8135611ee2816149bc565b600060208284031215613e2e578081fd5b8151611ee2816149bc565b60008060408385031215613e4b578081fd5b8235613e56816149bc565b91506020830135613e66816149bc565b809150509250929050565b600080600060608486031215613e85578081fd5b8335613e90816149bc565b92506020840135613ea0816149bc565b91506040840135613eb0816149bc565b809150509250925092565b60008060008060808587031215613ed0578182fd5b8435613edb816149bc565b93506020850135613eeb816149bc565b92506040850135613efb816149bc565b9396929550929360600135925050565b600080600080600080868803610180811215613f25578485fd5b8735613f30816149bc565b96506020880135613f40816149bc565b95506040880135945060e0605f1982011215613f5a578283fd5b50606087019250610140870135915061016087013590509295509295509295565b600060208284031215613f8c578081fd5b81518015158114611ee2578182fd5b600060c08284031215613fac578081fd5b611ee28383613dea565b600080600083850360a0811215613fcb578182fd5b6060811215613fd8578182fd5b508392506060840135613fea816149bc565b91506080840135613eb0816149bc565b60006101208284031215613dfb578081fd5b60008060006101008486031215614021578081fd5b833567ffffffffffffffff811115614037578182fd5b84016101c08187031215614049578182fd5b92506140588560208601613dea565b915060e084013590509250925092565b60008082840360c081121561407b578283fd5b8335925060a0601f1982011215614090578182fd5b506020830190509250929050565b6000602082840312156140af578081fd5b815167ffffffffffffffff808211156140c6578283fd5b9083019060c082860312156140d9578283fd5b6140e161463a565b8251828111156140ef578485fd5b6140fb87828601613d15565b82525060208301518281111561410f578485fd5b61411b87828601613d15565b602083015250604083015182811115614132578485fd5b61413e87828601613d15565b604083015250606083015182811115614155578485fd5b61416187828601613d8d565b606083015250608083015182811115614178578485fd5b61418487828601613d8d565b60808301525060a08301518281111561419b578485fd5b6141a787828601613d8d565b60a08301525095945050505050565b6000602082840312156141c7578081fd5b81356001600160681b0381168114611ee2578182fd5b6000602082840312156141ee578081fd5b813561ffff81168114611ee2578182fd5b600060208284031215614210578081fd5b5051919050565b60008060408385031215614229578182fd5b505080516020909101519092909150565b60006020828403121561424b578081fd5b813563ffffffff81168114611ee2578182fd5b60006020828403121561426f578081fd5b813566ffffffffffffff81168114611ee2578182fd5b600060208284031215614296578081fd5b8135611ee2816149d4565b6000602082840312156142b2578081fd5b8151611ee2816149d4565b6000602082840312156142ce578081fd5b815160ff81168114611ee2578182fd5b6000602082840312156142ef578081fd5b81356001600160601b0381168114611ee2578182fd5b6000815180845260208085019450808401835b8381101561433457815187529582019590820190600101614318565b509495945050505050565b6000815180845260208085019450808401835b838110156143345781516001600160601b031687529582019590820190600101614352565b6000815180845261438f816020860160208601614936565b601f01601f19169290920160200192915050565b600082516143b5818460208701614936565b9190910192915050565b6001600160a01b03968716815294861660208601529290941660408401526060830152608082019290925290151560a082015260c00190565b6001600160a01b038c811682528b81166020830152604082018b9052606082018a90526001600160601b038981166080840152881660a083015260c0820187905260e082018690528415156101008301528316610120820152610160610140820181905260009061446b83820185614377565b9e9d5050505050505050505050505050565b60006020808301818452808551808352604092508286019150828160051b870101848801865b8381101561454d57603f1989840301855281516101008151600b81106144d757634e487b7160e01b8b52602160045260248bfd5b8552818901516001600160a01b039081168a87015288830151811689870152606080840151909116908601526080808301519086015260a0808301519086015260c0808301519086015260e09182015191850181905261453981860183614377565b9689019694505050908601906001016144a3565b509098975050505050505050565b602081526000611ee26020830184614377565b8281526040602082015260006126bc6040830184614305565b84815260a0602082015260006145a060a083018661433f565b82810360408401526145b2818661433f565b905082810360608401526145c68185614305565b83810360809094019390935250506002815261060f60f31b6020820152604001949350505050565b6000808335601e19843603018112614604578283fd5b83018035915067ffffffffffffffff82111561461e578283fd5b60200191503681900382131561463357600080fd5b9250929050565b60405160c0810167ffffffffffffffff8111828210171561465d5761465d6149a6565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561468c5761468c6149a6565b604052919050565b600067ffffffffffffffff8211156146ae576146ae6149a6565b5060051b60200190565b600080821280156001600160ff1b03849003851316156146da576146da61497a565b600160ff1b83900384128116156146f3576146f361497a565b50500190565b6000821982111561470c5761470c61497a565b500190565b60008261472057614720614990565b600160ff1b82146000198414161561473a5761473a61497a565b500590565b60008261474e5761474e614990565b500490565b600181815b8085111561478e5781600019048211156147745761477461497a565b8085161561478157918102915b93841c9390800290614758565b509250929050565b6000611ee283836000826147ac57506001610d68565b816147b957506000610d68565b81600181146147cf57600281146147d9576147f5565b6001915050610d68565b60ff8411156147ea576147ea61497a565b50506001821b610d68565b5060208310610133831016604e8410600b8410161715614818575081810a610d68565b6148228383614753565b80600019048211156148365761483661497a565b029392505050565b60006001600160ff1b03818413828413808216868404861116156148645761486461497a565b600160ff1b848712828116878305891216156148825761488261497a565b85871292508782058712848416161561489d5761489d61497a565b878505871281841616156148b3576148b361497a565b505050929093029392505050565b60008160001904831182151516156148db576148db61497a565b500290565b60008083128015600160ff1b8501841216156148fe576148fe61497a565b6001600160ff1b03840183138116156149195761491961497a565b50500390565b6000828210156149315761493161497a565b500390565b60005b83811015614951578181015183820152602001614939565b83811115614960576000848401525b50505050565b60008261497557614975614990565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146149d157600080fd5b50565b67ffffffffffffffff811681146149d157600080fdfea2646970667358221220b95c20f4c0d0bb193e394828202857e8f6d4228ea2ec08f64b51a549981e37f564736f6c63430008040033

Deployed Bytecode

0x73e1d00f9bafea5aa40a2192af12b68af3d390afe230146080604052600436106100a85760003560e01c806384d831a91161007057806384d831a9146101a757806395a170d7146101ba578063d8dfd0fe146101ff578063de8e34181461021f578063eebc5ff41461023f57600080fd5b806307cc4239146100ad5780632904c239146100cf57806358ffbb3d146101025780635a90235d14610122578063686fb2f21461016c575b600080fd5b8180156100b957600080fd5b506100cd6100c8366004613fb6565b61025f565b005b8180156100db57600080fd5b506100ef6100ea366004613ebb565b61026f565b6040519081526020015b60405180910390f35b81801561010e57600080fd5b506100ef61011d366004613e39565b6107d9565b81801561012e57600080fd5b5061014261013d366004613f0b565b610d6e565b604080516001600160a01b03909516855260208501939093529183015260608201526080016100f9565b61017f61017a366004614068565b610fb3565b604080519586526020860194909452928401919091526060830152608082015260a0016100f9565b6100cd6101b536600461400c565b6110e5565b8180156101c657600080fd5b506101da6101d5366004613ffa565b6117ae565b60408051938452602084019290925267ffffffffffffffff16908201526060016100f9565b81801561020b57600080fd5b506100ef61021a366004613e01565b6117c9565b81801561022b57600080fd5b506100ef61023a366004613e71565b611b9d565b81801561024b57600080fd5b506100ef61025a366004613f9b565b611ee9565b61026a838383611ef4565b505050565b604051636553690d60e11b8152306004820152600090859082906102fa906001906001600160a01b0385169063caa6d21a9060240160206040518083038186803b1580156102bc57600080fd5b505afa1580156102d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f491906141ff565b90612077565b905060008590506000816001600160a01b031663aabaecd66040518163ffffffff1660e01b815260040160206040518083038186803b15801561033c57600080fd5b505afa158015610350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103749190613e1d565b90506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103b157600080fd5b505afa1580156103c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103e991906142bd565b60ff1690506000836001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b15801561042957600080fd5b505afa15801561043d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104619190613f7b565b15610528576105216104f961047784600a6146f9565b61048290600a614796565b866001600160a01b031663c52987cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104bb57600080fd5b505afa1580156104cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104f391906141ff565b90612083565b61051b670de0b6b3a76400006104f36105146008600a614796565b8d90612083565b9061208f565b9050610564565b5086600882111561056457600061054083600861209b565b61054b90600a614796565b9050808211156105625761055f898261208f565b91505b505b826105796001600160a01b0382168c8b6120a7565b60408051600380825260808201909252600091816020015b610599613cb4565b815260200190600190039081610591579050506040805161010081019091529091508060008152602001306001600160a01b03168152602001306001600160a01b0316815260200160006001600160a01b031681526020018881526020016000815260200160008152602001604051806020016040528060008152508152508160008151811061063957634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060058152602001306001600160a01b03168152602001306001600160a01b03168152602001866001600160a01b031681526020018881526020018b81526020016000815260200160405180602001604052806000815250815250816001815181106106cc57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060018152602001306001600160a01b03168152602001306001600160a01b031681526020018c6001600160a01b0316815260200188815260200184815260200160008152602001604051806020016040528060008152508152508160028151811061075f57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0389169063b617f0c69061079690849060040161447d565b600060405180830381600087803b1580156107b057600080fd5b505af11580156107c4573d6000803e3d6000fd5b50949f9e505050505050505050505050505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038416906370a082319060240160206040518083038186803b15801561081d57600080fd5b505afa158015610831573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085591906141ff565b9050600081116108a15760405162461bcd60e51b815260206004820152601260248201527127379037aa37b5b2b739903a3790313ab93760711b60448201526064015b60405180910390fd5b604051636553690d60e11b815230600482015284906000906001600160a01b0383169063caa6d21a9060240160206040518083038186803b1580156108e557600080fd5b505afa1580156108f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061091d91906141ff565b60405163d99d13f560e01b8152306004820152602481018290529091506000906001600160a01b0384169063d99d13f59060440160006040518083038186803b15801561096957600080fd5b505afa15801561097d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109a5919081019061409e565b8051519091506109e25760405162461bcd60e51b8152602060048201526008602482015267139bc81cda1bdc9d60c21b6044820152606401610898565b60008160400151600081518110610a0957634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a082319060240160206040518083038186803b158015610a5957600080fd5b505afa158015610a6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a9191906141ff565b6040805160028082526060820190925291925060009190816020015b610ab5613cb4565b815260200190600190039081610aad5790505060408051610100810190915290915080600281523060208201819052604082015285518051606090920191600090610b1057634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03168152602001868152602001888152602001600081526020016040518060200160405280600081525081525081600081518110610b6d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526040805161010081019091528060068152602001306001600160a01b03168152602001306001600160a01b03168152602001846001600160a01b03168152602001868152602001610c2e8660600151600081518110610be757634e487b7160e01b600052603260045260246000fd5b602002602001015161051b8b8960a00151600081518110610c1857634e487b7160e01b600052603260045260246000fd5b602002602001015161208390919063ffffffff16565b8152602001600081526020016040518060200160405280600081525081525081600181518110610c6e57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0387169063b617f0c690610ca590849060040161447d565b600060405180830381600087803b158015610cbf57600080fd5b505af1158015610cd3573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03861691506370a082319060240160206040518083038186803b158015610d1957600080fd5b505afa158015610d2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5191906141ff565b9050610d5d818461209b565b985050505050505050505b92915050565b60008080808080610d8560608a0160408b01613e01565b6001600160a01b03161415610da957610da2428960c001356120e3565b9050610e39565b610e36610dbc60608a0160408b01613e01565b6001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b158015610df457600080fd5b505afa158015610e08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2c91906141ff565b8960c001356120e3565b90505b8654600188015487548d9260ff8116926001600160a01b039081169262010000909204169061ffff16610e7260a08e0160808f016141dd565b61ffff1614610eff5760405163e436b89160e01b81526004810186905283151560248201526001600160a01b0385169063e436b89190604401604080518083038186803b158015610ec257600080fd5b505afa158015610ed6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610efa9190614217565b610f07565b8b60a0013560005b909750955086610f485760405162461bcd60e51b815260206004820152600c60248201526b21737472696b65507269636560a01b6044820152606401610898565b610f578c8c84848b8a89612215565b9850610f64898f8f6123a9565b975060008811610fa15760405162461bcd60e51b8152602060048201526008602482015267217072656d69756d60c01b6044820152606401610898565b50505050509650965096509692505050565b600182015460009081908190819081906020870135906001600160801b0380821691600160801b9004168380610ff060408c013586868e35612624565b90506000808c6040013511611006576000611012565b61101284838e3561265a565b905060008c606001358211611028576000611036565b6110368260608f013561209b565b9050611046816102f4898561209b565b60018f015490945061106b93508492506001600160801b0316905060808d01356126c4565b9450611077848661209b565b935061108a60408b013585858d35612624565b965061109883888c3561270c565b955060006110aa60408c013588612077565b9050600081116110bb5760006110c7565b6110c783898d3561265a565b98506110d59050848961209b565b9850505050509295509295909350565b60006110f46020850185613e01565b6001600160a01b031614156111355760405162461bcd60e51b815260206004820152600760248201526610afb7bbb732b960c91b6044820152606401610898565b60006111476040850160208601613e01565b6001600160a01b031614156111895760405162461bcd60e51b815260206004820152600860248201526710afb5b2b2b832b960c11b6044820152606401610898565b600061119b6060850160408601613e01565b6001600160a01b031614156111e35760405162461bcd60e51b815260206004820152600e60248201526d0857d99959549958da5c1a595b9d60921b6044820152606401610898565b6111f1620f424060646148c1565b83608001351061123c5760405162461bcd60e51b8152602060048201526016602482015275706572666f726d616e6365466565203e3d203130302560501b6044820152606401610898565b61124a620f424060646148c1565b8360600135106112945760405162461bcd60e51b81526020600482015260156024820152746d616e6167656d656e74466565203e3d203130302560581b6044820152606401610898565b60006112a360a08501856145ee565b9050116112e05760405162461bcd60e51b815260206004820152600b60248201526a215f746f6b656e4e616d6560a81b6044820152606401610898565b60006112ef60c08501856145ee565b90501161132e5760405162461bcd60e51b815260206004820152600d60248201526c0857dd1bdad95b94de5b589bdb609a1b6044820152606401610898565b826101600135600714806113475750826101600135600e145b806113575750826101600135601e145b806113675750826101600135605a145b80611377575082610160013560b4145b6113ae5760405162461bcd60e51b81526020600482015260086024820152670857dc195c9a5bd960c21b6044820152606401610898565b60006113c1610100850160e08601613e01565b6001600160a01b031614156114115760405162461bcd60e51b815260206004820152601660248201527510afb7b83a34b7b739a83932b6b4bab6a83934b1b2b960511b6044820152606401610898565b600061142561012085016101008601613e01565b6001600160a01b031614156114705760405162461bcd60e51b815260206004820152601160248201527010afb9ba3934b5b2a9b2b632b1ba34b7b760791b6044820152606401610898565b60006114846101408501610120860161423a565b63ffffffff161180156114b8575061149e600a60646148c1565b6114b06101408501610120860161423a565b63ffffffff16105b6114f85760405162461bcd60e51b81526020600482015260116024820152700857dc1c995b5a5d5b511a5cd8dbdd5b9d607a1b6044820152606401610898565b8083610140013510156115415760405162461bcd60e51b815260206004820152601160248201527010afb0bab1ba34b7b7223ab930ba34b7b760791b6044820152606401610898565b6000836101800135116115885760405162461bcd60e51b815260206004820152600f60248201526e215f6d61784465706f7369746f727360881b6044820152606401610898565b6000836101a00135116115cc5760405162461bcd60e51b815260206004820152600c60248201526b0857db5a5b91195c1bdcda5d60a21b6044820152606401610898565b60006115de6060840160408501613e01565b6001600160a01b0316141561161e5760405162461bcd60e51b815260206004820152600660248201526508585cdcd95d60d21b6044820152606401610898565b60006116306080840160608501613e01565b6001600160a01b031614156116755760405162461bcd60e51b815260206004820152600b60248201526a21756e6465726c79696e6760a81b6044820152606401610898565b600061168760a084016080850161425e565b66ffffffffffffff16116116ce5760405162461bcd60e51b815260206004820152600e60248201526d216d696e696d756d537570706c7960901b6044820152606401610898565b60006116e060c0840160a085016141b6565b6001600160681b03161161171f5760405162461bcd60e51b8152600401610898906020808252600490820152630216361760e41b604082015260600190565b61172f60a083016080840161425e565b66ffffffffffffff1661174860c0840160a085016141b6565b6001600160681b03161161026a5760405162461bcd60e51b815260206004820152602760248201527f6361702068617320746f20626520686967686572207468616e206d696e696d756044820152666d537570706c7960c81b6064820152608401610898565b60008060006117bc8461276f565b9250925092509193909250565b604051636553690d60e11b8152306004820152600090829082906001600160a01b0383169063caa6d21a9060240160206040518083038186803b15801561180f57600080fd5b505afa158015611823573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061184791906141ff565b60405163d99d13f560e01b8152306004820152602481018290529091506000906001600160a01b0384169063d99d13f59060440160006040518083038186803b15801561189357600080fd5b505afa1580156118a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526118cf919081019061409e565b80515190915061190c5760405162461bcd60e51b8152602060048201526008602482015267139bc81cda1bdc9d60c21b6044820152606401610898565b6000816040015160008151811061193357634e487b7160e01b600052603260045260246000fd5b6020026020010151905060006001600160a01b0316816001600160a01b031614156119645750600095945050505050565b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a082319060240160206040518083038186803b1580156119a657600080fd5b505afa1580156119ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119de91906141ff565b60408051600180825281830190925291925060009190816020015b611a01613cb4565b8152602001906001900390816119f9579050506040805161010081019091529091508060078152602001306001600160a01b03168152602001306001600160a01b0316815260200160006001600160a01b0316815260200186815260200160008152602001600081526020016040518060200160405280600081525081525081600081518110611aa157634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0387169063b617f0c690611ad890849060040161447d565b600060405180830381600087803b158015611af257600080fd5b505af1158015611b06573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03861691506370a082319060240160206040518083038186803b158015611b4c57600080fd5b505afa158015611b60573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8491906141ff565b9050611b90818461209b565b9998505050505050505050565b6040516370a0823160e01b8152306004820152600090849082906001600160a01b038616906370a082319060240160206040518083038186803b158015611be357600080fd5b505afa158015611bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1b91906141ff565b60405163565eea1960e01b81526001600160a01b038781166004830152602482018390529192509083169063565eea199060440160206040518083038186803b158015611c6757600080fd5b505afa158015611c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c9f91906141ff565b611cae57600092505050611ee2565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a082319060240160206040518083038186803b158015611cf057600080fd5b505afa158015611d04573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d2891906141ff565b60408051600180825281830190925291925060009190816020015b611d4b613cb4565b815260200190600190039081611d4357905050604080516101008101909152909150806008815260200160006001600160a01b03168152602001306001600160a01b03168152602001886001600160a01b0316815260200160008152602001848152602001600081526020016040518060200160405280600081525081525081600081518110611deb57634e487b7160e01b600052603260045260246000fd5b6020908102919091010152604051635b0bf86360e11b81526001600160a01b0385169063b617f0c690611e2290849060040161447d565b600060405180830381600087803b158015611e3c57600080fd5b505af1158015611e50573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092506001600160a01b03891691506370a082319060240160206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ece91906141ff565b9050611eda818461209b565b955050505050505b9392505050565b6000610d6882612b0b565b6000611f2b611f096060860160408701614285565b611f1960408701602088016142de565b611f2660208801886142de565b612e59565b60408051600180825281830190925291925060009190602080830190803683370190505090508181600081518110611f7357634e487b7160e01b600052603260045260246000fd5b602002602001018181525050836001600160a01b0316637882deaf846001600160a01b031663432833a66040518163ffffffff1660e01b815260040160206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fff91906141ff565b836040518363ffffffff1660e01b815260040161201d92919061456e565b6040805180830381600087803b15801561203657600080fd5b505af115801561204a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061206e9190614217565b50505050505050565b6000611ee282846146f9565b6000611ee282846148c1565b6000611ee2828461473f565b6000611ee2828461491f565b6001600160a01b03831673dac17f958d2ee523a2206206994597c13d831ec714156120d8576120d883836000612e9c565b61026a838383612e9c565b6000816007141561211b576120f783612fe2565b9050828111156121075780612114565b6121148162093a806146f9565b90506121f2565b81600e141561214a5761212d83612fe2565b90508281111561213d5780612114565b61211481621275006146f9565b81601e14156121815761215c8361300a565b90508281111561216c5780612114565b61211461217c8262093a806146f9565b61300a565b81605a14156121b8576121938361303f565b9050828111156121a35780612114565b6121146121b38262093a806146f9565b61303f565b8160b414156121f2576121ca836130b1565b9050828111156121da57806121ef565b6121ef6121ea8262093a806146f9565b6130b1565b90505b6121ff6201518082614966565b612209908261491f565b611ee2906170806146f9565b60008061222560208a018a613e01565b905060006001600160a01b03821663115470548961224960408e0160208f01613e01565b8a8a8a8a6040518763ffffffff1660e01b815260040161226e969594939291906143bf565b60206040518083038186803b15801561228657600080fd5b505afa15801561229a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122be9190613e1d565b90506001600160a01b038116156122d857915061239e9050565b6000826001600160a01b031663c09746308a8d60200160208101906122fd9190613e01565b8b8b8b8b6040518763ffffffff1660e01b8152600401612322969594939291906143bf565b602060405180830381600087803b15801561233c57600080fd5b505af1158015612350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123749190613e1d565b9050612399818b8a8e602001602081019061238f9190613e01565b8f606001356130e2565b925050505b979650505050505050565b60008084905060008490506000816001600160a01b03166330a75871846001600160a01b031663c52987cf6040518163ffffffff1660e01b815260040160206040518083038186803b1580156123fe57600080fd5b505afa158015612412573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061243691906141ff565b856001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561246f57600080fd5b505afa158015612483573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a791906141ff565b866001600160a01b031663f3c274a66040518163ffffffff1660e01b815260040160206040518083038186803b1580156124e057600080fd5b505afa1580156124f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125189190613f7b565b6040516001600160e01b031960e086901b168152600481019390935260248301919091521515604482015260640160206040518083038186803b15801561255e57600080fd5b505afa158015612572573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061259691906141ff565b90506125b16125a7600a60646148c1565b61051b8388612083565b90506001600160601b0381111561261a5760405162461bcd60e51b815260206004820152602760248201527f6f7074696f6e5072656d69756d203e20747970652875696e74393629206d61786044820152662076616c75652160c81b6064820152608401610898565b9695505050505050565b60008061263283600a614796565b905060008611612642578061261a565b61261a8661051b612653888861209b565b8490612083565b6000600183116126a45760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610898565b6126bc6126b283600a614796565b61051b8686612083565b949350505050565b6000808385116126d55760006126df565b6126df858561209b565b905060008084116126f157600061261a565b61261a612702620f424060646148c1565b61051b8487612083565b6000600183116127565760405162461bcd60e51b8152602060048201526015602482015274496e76616c6964206173736574506572536861726560581b6044820152606401610898565b6126bc8361051b61276885600a614796565b8790612083565b600080806127936127816064806148c1565b61051b60a087013560c0880135612083565b92506127d16127a76060860135600a614796565b61051b60e0870135816127bf60608a01356008612077565b6127ca90600a614796565b8890612083565b91506001600160601b038311156128365760405162461bcd60e51b8152602060048201526024808201527f73656c6c416d6f756e74203e20747970652875696e74393629206d61782076616044820152636c75652160e01b6064820152608401610898565b6001600160601b038211156128995760405162461bcd60e51b815260206004820152602360248201527f627579416d6f756e74203e20747970652875696e74393629206d61782076616c60448201526275652160e81b6064820152608401610898565b6128cd6128ac6040860160208701613e01565b846128bd6060880160408901613e01565b6001600160a01b03169190612e9c565b6040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337019050509050848360008151811061295757634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160601b031690816001600160601b031681525050858260008151811061299957634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160601b031690816001600160601b031681525050600160001b816000815181106129df57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250508660200160208101906129fe9190613e01565b6001600160a01b031663d225269c88608001358585856040518563ffffffff1660e01b8152600401612a339493929190614587565b602060405180830381600087803b158015612a4d57600080fd5b505af1158015612a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8591906142a1565b9350612a9961012088016101008901613e01565b6001600160a01b0316612aaf6020890189613e01565b6040805160808b01358152602081018a90529081018890526001600160a01b0391909116907fa227b5eceb2bd983c0ed9dd34c971be74cd4f386b289d4415602a1fbcf2cb53b9060600160405180910390a35050509193909250565b600080612b23612b1e6020850185613e01565b61350e565b905060008111612b6a5760405162461bcd60e51b8152602060048201526012602482015271139bc81bdd1bdad95b9cc81d1bc81cd95b1b60721b6044820152606401610898565b612c10612b7d6040850160208601613e01565b612b8a6020860186613e01565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b158015612bcb57600080fd5b505afa158015612bdf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0391906141ff565b6128bd6020870187613e01565b6000612c2e612c24836402540be400612083565b85608001356135f6565b90506012846060013511612c6557612c60612c4e6012606087013561209b565b612c5990600a614796565b829061208f565b612c89565b612c89612c776060860135601261209b565b612c8290600a614796565b8290612083565b90506001600160601b03811115612d085760405162461bcd60e51b815260206004820152603a60248201527f6f7074696f6e5072656d69756d202a206f546f6b656e53656c6c416d6f756e7460448201527f203e20747970652875696e74393629206d61782076616c7565210000000000006064820152608401610898565b6000612d184260a0870135612077565b9050612d2a6040860160208701613e01565b6001600160a01b0316630a4cd6c9612d456020880188613e01565b612d556060890160408a01613e01565b60408051602081018252600080825291516001600160e01b031960e087901b168152612d94949392889283928c928c92600192829182916004016143f8565b602060405180830381600087803b158015612dae57600080fd5b505af1158015612dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de691906141ff565b935033612df96060870160408801613e01565b6001600160a01b0316612e0f6020880188613e01565b6001600160a01b03167f95ad3b10488285d6307fda297f633faaf2a0d713c08ebe5f49c1b9255b01d29e87604051612e4991815260200190565b60405180910390a4505050919050565b60006001600160601b038216612e926bffffffffffffffffffffffff60601b606086901b166001600160c01b031960c088901b166146f9565b6126bc91906146f9565b801580612f255750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b158015612eeb57600080fd5b505afa158015612eff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f2391906141ff565b155b612f905760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610898565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261026a908490613630565b600080612fee83613702565b905082811015610d68576130058162093a806146f9565b611ee2565b60008061301683613731565b9050600061302382613702565b905081811161303257806126bc565b6126bc62093a808261491f565b60008061304b8361375e565b90506000600382111561307f57600682111561307857600982111561307157600c613082565b6009613082565b6006613082565b60035b60ff169050600061309d6130958661377d565b836001613798565b90506130a88161300a565b95945050505050565b6000806130bd8361375e565b9050600060068211156130d157600c613082565b506006600061309d6130958661377d565b6001600160a01b0385166131295760405162461bcd60e51b815260206004820152600e60248201526d216f746f6b656e4164647265737360901b6044820152606401610898565b8354604080516379e13a5360e11b81529051879260ff161515916001600160a01b0384169163f3c274a691600480820192602092909190829003018186803b15801561317457600080fd5b505afa158015613188573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ac9190613f7b565b1515146131eb5760405162461bcd60e51b815260206004820152600d60248201526c0a8f2e0ca40dad2e6dac2e8c6d609b1b6044820152606401610898565b600185015460408051631c56369f60e21b815290516001600160a01b0392831692841691637158da7c916004808301926020929190829003018186803b15801561323457600080fd5b505afa158015613248573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061326c9190613e1d565b6001600160a01b0316146132ba5760405162461bcd60e51b815260206004820152601560248201527415dc9bdb99c81d5b99195c9b1e5a5b99d05cdcd95d605a1b6044820152606401610898565b836001600160a01b0316816001600160a01b031663aabaecd66040518163ffffffff1660e01b815260040160206040518083038186803b1580156132fd57600080fd5b505afa158015613311573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133359190613e1d565b6001600160a01b0316146133835760405162461bcd60e51b815260206004820152601560248201527415dc9bdb99c818dbdb1b185d195c985b105cdcd95d605a1b6044820152606401610898565b826001600160a01b0316816001600160a01b03166317d69bc86040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c657600080fd5b505afa1580156133da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fe9190613e1d565b6001600160a01b03161461344a5760405162461bcd60e51b8152602060048201526013602482015272737472696b65417373657420213d205553444360681b6044820152606401610898565b60006134564284612077565b905080826001600160a01b031663ade6e2aa6040518163ffffffff1660e01b815260040160206040518083038186803b15801561349257600080fd5b505afa1580156134a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134ca91906141ff565b101561206e5760405162461bcd60e51b8152602060048201526013602482015272457870697279206265666f72652064656c617960681b6044820152606401610898565b6040516370a0823160e01b815230600482015260009081906001600160a01b038416906370a082319060240160206040518083038186803b15801561355257600080fd5b505afa158015613566573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061358a91906141ff565b90506001600160601b03811115610d685760405162461bcd60e51b815260206004820152602a60248201527f6f546f6b656e53656c6c416d6f756e74203e20747970652875696e74393629206044820152696d61782076616c75652160b01b6064820152608401610898565b6000670de0b6b3a764000061362661360e85856137b3565b6136216002670de0b6b3a764000061473f565b61381a565b611ee2919061473f565b6000613685826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661386f9092919063ffffffff16565b80519091501561026a57808060200190518101906136a39190613f7b565b61026a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610898565b600061370d8261387e565b61371a90620151806148c1565b61372783620697806146f9565b610d68919061491f565b6000620151806137276137438461377d565b61374c8561375e565b6137579060016146f9565b6001613798565b6000613775613770620151808461473f565b6138b2565b509392505050565b600061378f613770620151808461473f565b50909392505050565b6000620151806137a9858585613a26565b6126bc91906148c1565b60008115806137d7575082826137c981836148c1565b92506137d5908361473f565b145b610d685760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b6044820152606401610898565b60008261382783826146f9565b9150811015610d685760405162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b6044820152606401610898565b60606126bc8484600085613b63565b60008061388e620151808461473f565b9050600761389d8260036146f9565b6138a79190614966565b611ee29060016146f9565b60008080838162253d8c6138c98362010bd96146b8565b6138d391906146b8565b9050600062023ab16138e683600461483e565b6138f09190614711565b905060046139018262023ab161483e565b61390c9060036146b8565b6139169190614711565b61392090836148e0565b9150600062164b096139338460016146b8565b61393f90610fa061483e565b6139499190614711565b90506004613959826105b561483e565b6139639190614711565b61396d90846148e0565b61397890601f6146b8565b9250600061098f61398a85605061483e565b6139949190614711565b9050600060506139a68361098f61483e565b6139b09190614711565b6139ba90866148e0565b90506139c7600b83614711565b94506139d485600c61483e565b6139df8360026146b8565b6139e991906148e0565b915084836139f86031876148e0565b613a0390606461483e565b613a0d91906146b8565b613a1791906146b8565b9a919950975095505050505050565b60006107b2841015613a3757600080fd5b838383600062253d8c60046064600c613a51600e886148e0565b613a5b9190614711565b613a67886113246146b8565b613a7191906146b8565b613a7b9190614711565b613a8690600361483e565b613a909190614711565b600c80613a9e600e886148e0565b613aa89190614711565b613ab390600c61483e565b613abe6002886148e0565b613ac891906148e0565b613ad49061016f61483e565b613ade9190614711565b6004600c613aed600e896148e0565b613af79190614711565b613b03896112c06146b8565b613b0d91906146b8565b613b19906105b561483e565b613b239190614711565b613b2f617d4b876148e0565b613b3991906146b8565b613b4391906146b8565b613b4d91906148e0565b613b5791906148e0565b98975050505050505050565b606082471015613bc45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610898565b843b613c125760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610898565b600080866001600160a01b03168587604051613c2e91906143a3565b60006040518083038185875af1925050503d8060008114613c6b576040519150601f19603f3d011682016040523d82523d6000602084013e613c70565b606091505b509150915061239e82828660608315613c8a575081611ee2565b825115613c9a5782518084602001fd5b8160405162461bcd60e51b8152600401610898919061455b565b604080516101008101909152806000815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001606081525090565b600082601f830112613d25578081fd5b81516020613d3a613d3583614694565b614663565b80838252828201915082860187848660051b8901011115613d59578586fd5b855b85811015613d80578151613d6e816149bc565b84529284019290840190600101613d5b565b5090979650505050505050565b600082601f830112613d9d578081fd5b81516020613dad613d3583614694565b80838252828201915082860187848660051b8901011115613dcc578586fd5b855b85811015613d8057815184529284019290840190600101613dce565b600060c08284031215613dfb578081fd5b50919050565b600060208284031215613e12578081fd5b8135611ee2816149bc565b600060208284031215613e2e578081fd5b8151611ee2816149bc565b60008060408385031215613e4b578081fd5b8235613e56816149bc565b91506020830135613e66816149bc565b809150509250929050565b600080600060608486031215613e85578081fd5b8335613e90816149bc565b92506020840135613ea0816149bc565b91506040840135613eb0816149bc565b809150509250925092565b60008060008060808587031215613ed0578182fd5b8435613edb816149bc565b93506020850135613eeb816149bc565b92506040850135613efb816149bc565b9396929550929360600135925050565b600080600080600080868803610180811215613f25578485fd5b8735613f30816149bc565b96506020880135613f40816149bc565b95506040880135945060e0605f1982011215613f5a578283fd5b50606087019250610140870135915061016087013590509295509295509295565b600060208284031215613f8c578081fd5b81518015158114611ee2578182fd5b600060c08284031215613fac578081fd5b611ee28383613dea565b600080600083850360a0811215613fcb578182fd5b6060811215613fd8578182fd5b508392506060840135613fea816149bc565b91506080840135613eb0816149bc565b60006101208284031215613dfb578081fd5b60008060006101008486031215614021578081fd5b833567ffffffffffffffff811115614037578182fd5b84016101c08187031215614049578182fd5b92506140588560208601613dea565b915060e084013590509250925092565b60008082840360c081121561407b578283fd5b8335925060a0601f1982011215614090578182fd5b506020830190509250929050565b6000602082840312156140af578081fd5b815167ffffffffffffffff808211156140c6578283fd5b9083019060c082860312156140d9578283fd5b6140e161463a565b8251828111156140ef578485fd5b6140fb87828601613d15565b82525060208301518281111561410f578485fd5b61411b87828601613d15565b602083015250604083015182811115614132578485fd5b61413e87828601613d15565b604083015250606083015182811115614155578485fd5b61416187828601613d8d565b606083015250608083015182811115614178578485fd5b61418487828601613d8d565b60808301525060a08301518281111561419b578485fd5b6141a787828601613d8d565b60a08301525095945050505050565b6000602082840312156141c7578081fd5b81356001600160681b0381168114611ee2578182fd5b6000602082840312156141ee578081fd5b813561ffff81168114611ee2578182fd5b600060208284031215614210578081fd5b5051919050565b60008060408385031215614229578182fd5b505080516020909101519092909150565b60006020828403121561424b578081fd5b813563ffffffff81168114611ee2578182fd5b60006020828403121561426f578081fd5b813566ffffffffffffff81168114611ee2578182fd5b600060208284031215614296578081fd5b8135611ee2816149d4565b6000602082840312156142b2578081fd5b8151611ee2816149d4565b6000602082840312156142ce578081fd5b815160ff81168114611ee2578182fd5b6000602082840312156142ef578081fd5b81356001600160601b0381168114611ee2578182fd5b6000815180845260208085019450808401835b8381101561433457815187529582019590820190600101614318565b509495945050505050565b6000815180845260208085019450808401835b838110156143345781516001600160601b031687529582019590820190600101614352565b6000815180845261438f816020860160208601614936565b601f01601f19169290920160200192915050565b600082516143b5818460208701614936565b9190910192915050565b6001600160a01b03968716815294861660208601529290941660408401526060830152608082019290925290151560a082015260c00190565b6001600160a01b038c811682528b81166020830152604082018b9052606082018a90526001600160601b038981166080840152881660a083015260c0820187905260e082018690528415156101008301528316610120820152610160610140820181905260009061446b83820185614377565b9e9d5050505050505050505050505050565b60006020808301818452808551808352604092508286019150828160051b870101848801865b8381101561454d57603f1989840301855281516101008151600b81106144d757634e487b7160e01b8b52602160045260248bfd5b8552818901516001600160a01b039081168a87015288830151811689870152606080840151909116908601526080808301519086015260a0808301519086015260c0808301519086015260e09182015191850181905261453981860183614377565b9689019694505050908601906001016144a3565b509098975050505050505050565b602081526000611ee26020830184614377565b8281526040602082015260006126bc6040830184614305565b84815260a0602082015260006145a060a083018661433f565b82810360408401526145b2818661433f565b905082810360608401526145c68185614305565b83810360809094019390935250506002815261060f60f31b6020820152604001949350505050565b6000808335601e19843603018112614604578283fd5b83018035915067ffffffffffffffff82111561461e578283fd5b60200191503681900382131561463357600080fd5b9250929050565b60405160c0810167ffffffffffffffff8111828210171561465d5761465d6149a6565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561468c5761468c6149a6565b604052919050565b600067ffffffffffffffff8211156146ae576146ae6149a6565b5060051b60200190565b600080821280156001600160ff1b03849003851316156146da576146da61497a565b600160ff1b83900384128116156146f3576146f361497a565b50500190565b6000821982111561470c5761470c61497a565b500190565b60008261472057614720614990565b600160ff1b82146000198414161561473a5761473a61497a565b500590565b60008261474e5761474e614990565b500490565b600181815b8085111561478e5781600019048211156147745761477461497a565b8085161561478157918102915b93841c9390800290614758565b509250929050565b6000611ee283836000826147ac57506001610d68565b816147b957506000610d68565b81600181146147cf57600281146147d9576147f5565b6001915050610d68565b60ff8411156147ea576147ea61497a565b50506001821b610d68565b5060208310610133831016604e8410600b8410161715614818575081810a610d68565b6148228383614753565b80600019048211156148365761483661497a565b029392505050565b60006001600160ff1b03818413828413808216868404861116156148645761486461497a565b600160ff1b848712828116878305891216156148825761488261497a565b85871292508782058712848416161561489d5761489d61497a565b878505871281841616156148b3576148b361497a565b505050929093029392505050565b60008160001904831182151516156148db576148db61497a565b500290565b60008083128015600160ff1b8501841216156148fe576148fe61497a565b6001600160ff1b03840183138116156149195761491961497a565b50500390565b6000828210156149315761493161497a565b500390565b60005b83811015614951578181015183820152602001614939565b83811115614960576000848401525b50505050565b60008261497557614975614990565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146149d157600080fd5b50565b67ffffffffffffffff811681146149d157600080fdfea2646970667358221220b95c20f4c0d0bb193e394828202857e8f6d4228ea2ec08f64b51a549981e37f564736f6c63430008040033

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.