ETH Price: $3,302.63 (-2.04%)

Contract

0x91e9e99AC7C39d5c057F83ef44136dFB1e7adD7d

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Mint Allowed161605442024-09-20 7:11:43123 days ago1726816303IN
0x91e9e99A...B1e7adD7d
0 ETH0.000006980.25311454
Transfer Allowed161124252024-09-18 14:49:29124 days ago1726670969IN
0x91e9e99A...B1e7adD7d
0 ETH0.000009660.34613474
Mint Allowed160994542024-09-18 3:57:14125 days ago1726631834IN
0x91e9e99A...B1e7adD7d
0 ETH0.000006980.25327189
Mint Allowed160898772024-09-17 19:54:59125 days ago1726602899IN
0x91e9e99A...B1e7adD7d
0 ETH0.000009010.32665612
Transfer Allowed160777512024-09-17 9:45:37126 days ago1726566337IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004520.16212424
Transfer Allowed160687792024-09-17 2:14:56126 days ago1726539296IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004080.14626092
Transfer Allowed160352472024-09-15 22:11:53127 days ago1726438313IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005480.19628753
Transfer Allowed160247632024-09-15 13:25:12127 days ago1726406712IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004450.15937003
Mint Allowed160137662024-09-15 4:11:44128 days ago1726373504IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004480.16260109
Transfer Allowed160113782024-09-15 2:11:18128 days ago1726366278IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004450.15954531
Mint Allowed160020802024-09-14 18:22:43128 days ago1726338163IN
0x91e9e99A...B1e7adD7d
0 ETH0.000006130.22239613
Transfer Allowed159854232024-09-14 4:23:53129 days ago1726287833IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005750.20608027
Transfer Allowed159658152024-09-13 11:58:24130 days ago1726228704IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004210.15086044
Transfer Allowed159654762024-09-13 11:41:32130 days ago1726227692IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005470.1960023
Mint Allowed159531702024-09-13 1:22:29130 days ago1726190549IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005180.18798161
Transfer Allowed159494742024-09-12 22:16:27130 days ago1726179387IN
0x91e9e99A...B1e7adD7d
0 ETH0.000003930.140878
Transfer Allowed159464012024-09-12 19:41:47130 days ago1726170107IN
0x91e9e99A...B1e7adD7d
0 ETH0.00000770.2759934
Transfer Allowed158885632024-09-10 19:13:56132 days ago1725995636IN
0x91e9e99A...B1e7adD7d
0 ETH0.000007430.26643293
Transfer Allowed158792592024-09-10 11:26:30133 days ago1725967590IN
0x91e9e99A...B1e7adD7d
0 ETH0.000007670.27494063
Transfer Allowed158630682024-09-09 21:51:31133 days ago1725918691IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005410.19382056
Transfer Allowed158508822024-09-09 11:40:13134 days ago1725882013IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005670.20321889
Transfer Allowed158427242024-09-09 4:50:23134 days ago1725857423IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005410.19392061
Transfer Allowed158297592024-09-08 17:59:05134 days ago1725818345IN
0x91e9e99A...B1e7adD7d
0 ETH0.000004850.17382851
Transfer Allowed158252892024-09-08 14:14:35134 days ago1725804875IN
0x91e9e99A...B1e7adD7d
0 ETH0.000005360.19220608
Mint Allowed158151462024-09-08 5:44:28135 days ago1725774268IN
0x91e9e99A...B1e7adD7d
0 ETH0.00000390.14154895
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Comptroller

Compiler Version
v0.8.4+commit.c7e474f2

Optimization Enabled:
Yes with 10 runs

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

import "./libraries/ErrorReporter.sol";
import "./libraries/ExponentialNoError.sol";
import "./interfaces/IComptroller.sol";
import "./ComptrollerStorage.sol";

interface I0vix {
    function transfer(address, uint256) external;

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

interface IUnitroller {
    function admin() external view returns (address);

    function _acceptImplementation() external returns (uint256);
}

/**
 * @title Comptroller Contract
 * @author 0VIX Protocol
 * @notice Based on Compound's Comptroller with some changes inspired by BENQi.fi
 */
contract Comptroller is
    ComptrollerV7Storage,
    ComptrollerErrorReporter,
    ExponentialNoError
{
    /// @notice Emitted when an admin modifies a reward updater
    event RewardUpdaterModified(address _rewardUpdater);

    /// @notice Emitted when an admin supports a market
    event MarketListed(IOToken oToken);

    /// @notice Emitted when market autoCollaterize flag is set
    event MarketAutoCollateralized(bool isAutoCollateralized);

    /// @notice Emitted when an account enters a market
    event MarketEntered(IOToken oToken, address account);

    /// @notice Emitted when an account exits a market
    event MarketExited(IOToken oToken, address account);

    /// @notice Emitted when close factor is changed by admin
    event NewCloseFactor(
        uint256 oldCloseFactorMantissa,
        uint256 newCloseFactorMantissa
    );

    /// @notice Emitted when a collateral factor is changed by admin
    event NewCollateralFactor(
        IOToken oToken,
        uint256 oldCollateralFactorMantissa,
        uint256 newCollateralFactorMantissa
    );

    /// @notice Emitted when liquidation incentive is changed by admin
    event NewLiquidationIncentive(
        uint256 oldLiquidationIncentiveMantissa,
        uint256 newLiquidationIncentiveMantissa
    );

    /// @notice Emitted when price oracle is changed
    event NewPriceOracle(
        PriceOracle oldPriceOracle,
        PriceOracle newPriceOracle
    );

    /// @notice Emitted when pause guardian is changed
    event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian);

    /// @notice Emitted when an action is paused globally
    event ActionPausedGlobally(string action, bool pauseState);

    /// @notice Emitted when an action is paused on a market
    event ActionPaused(IOToken oToken, string action, bool pauseState);

    /// @notice Emitted when a new borrow-side Reward speed is calculated for a market
    event RewardBorrowSpeedUpdated(IOToken indexed oToken, uint256 newSpeed);

    /// @notice Emitted when a new supply-side Reward speed is calculated for a market
    event RewardSupplySpeedUpdated(IOToken indexed oToken, uint256 newSpeed);

    /// @notice Emitted when a new Reward speed is set for a contributor
    event ContributorRewardSpeedUpdated(
        address indexed contributor,
        uint256 newSpeed
    );

    /// @notice Emitted when VIX is distributed to a supplier
    event DistributedSupplierReward(
        IOToken indexed oToken,
        address indexed supplier,
        uint256 tokenDelta,
        uint256 tokenSupplyIndex
    );

    /// @notice Emitted when VIX is distributed to a borrower
    event DistributedBorrowerReward(
        IOToken indexed oToken,
        address indexed borrower,
        uint256 tokenDelta,
        uint256 tokenBorrowIndex
    );

    /// @notice Emitted when borrow cap for a oToken is changed
    event NewBorrowCap(IOToken indexed oToken, uint256 newBorrowCap);

    /// @notice Emitted when borrow cap guardian is changed
    event NewBorrowCapGuardian(
        address oldBorrowCapGuardian,
        address newBorrowCapGuardian
    );

    /// @notice Emitted when VIX is granted by admin
    event VixGranted(address recipient, uint256 amount);

    /// @notice Emitted when VIX rewards are being claimed for a user
    event VixClaimed(address recipient, uint256 amount);

    /// @notice Emitted when VIX address changes
    event VixAddressSet(address vix);
    /// @notice Emitted when boostmanager address changes
    event BoostManagerSet(address boostmanager);

    bool public constant override isComptroller = true;

    /// @notice The initial Reward index for a market
    uint224 public constant marketInitialIndex = 1e36;

    // closeFactorMantissa must be strictly greater than this value
    uint256 internal constant closeFactorMinMantissa = 0.05e18; // 0.05

    // closeFactorMantissa must not exceed this value
    uint256 internal constant closeFactorMaxMantissa = 0.9e18; // 0.9

    // No collateralFactorMantissa may exceed this value
    uint256 internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9

    address public vixAddress;
    address public rewardUpdater;

    modifier onlyAdmin() {
        require(msg.sender == admin);
        _;
    }

    constructor() {
        admin = msg.sender;
    }

    /*** Assets You Are In ***/

    /**
     * @notice Returns the assets an account has entered
     * @param account The address of the account to pull assets for
     * @return A dynamic list with the assets the account has entered
     */
    function getAssetsIn(address account)
        external
        view
        returns (IOToken[] memory)
    {
        return accountAssets[account];
    }

    /**
     * @notice Returns whether the given token is listed market
     * @param oToken The oToken to check
     * @return True if is market, otherwise false.
     */
    function isMarket(address oToken) external view override returns (bool) {
        return markets[oToken].isListed;
    }

    /**
     * @notice Returns whether the given account is entered in the given asset
     * @param account The address of the account to check
     * @param oToken The oToken to check
     * @return True if the account is in the asset, otherwise false.
     */
    function checkMembership(address account, IOToken oToken)
        external
        view
        returns (bool)
    {
        return accountMembership[address(oToken)][account];
    }

    /**
     * @notice Add assets to be included in account liquidity calculation
     * @param oTokens The list of addresses of the oToken markets to be enabled
     * @return Success indicator for whether each corresponding market was entered
     */
    function enterMarkets(address[] memory oTokens)
        public
        override
        returns (uint256[] memory)
    {
        uint256 len = oTokens.length;

        uint256[] memory results = new uint256[](len);
        for (uint256 i = 0; i < len; i++) {
            results[i] = uint256(
                addToMarketInternal(IOToken(oTokens[i]), msg.sender)
            );
        }

        return results;
    }

    /**
     * @notice Add the market to the borrower's "assets in" for liquidity calculations
     * @param oToken The market to enter
     * @param borrower The address of the account to modify
     * @return Success indicator for whether the market was entered
     */
    function addToMarketInternal(IOToken oToken, address borrower)
        internal
        returns (Error)
    {
        if (!markets[address(oToken)].isListed) {
            // market is not listed, cannot join
            return Error.MARKET_NOT_LISTED;
        }

        if (accountMembership[address(oToken)][borrower]) {
            // already joined
            return Error.NO_ERROR;
        }

        // survived the gauntlet, add to list
        // NOTE: we store these somewhat redundantly as a significant optimization
        //  this avoids having to iterate through the list for the most common use cases
        //  that is, only when we need to perform liquidity checks
        //  and not whenever we want to check if an account is in a particular market
        accountMembership[address(oToken)][borrower] = true;
        accountAssets[borrower].push(oToken);

        emit MarketEntered(oToken, borrower);

        return Error.NO_ERROR;
    }

    /**
     * @notice Removes asset from sender's account liquidity calculation
     * @dev Sender must not have an outstanding borrow balance in the asset,
     *  or be providing necessary collateral for an outstanding borrow.
     * @param oTokenAddress The address of the asset to be removed
     * @return Whether or not the account successfully exited the market
     */
    function exitMarket(address oTokenAddress)
        external
        override
        returns (uint256)
    {
        IOToken oToken = IOToken(oTokenAddress);
        /* Get sender tokensHeld and amountOwed underlying from the oToken */
        (uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = oToken
            .getAccountSnapshot(msg.sender);
        require(oErr == 0, "exitMarket: getAccountSnapshot failed"); // semi-opaque error code

        /* Fail if the sender has a borrow balance */
        if (amountOwed != 0) {
            return
                fail(
                    Error.NONZERO_BORROW_BALANCE,
                    FailureInfo.EXIT_MARKET_BALANCE_OWED
                );
        }

        /* Fail if the sender is not permitted to redeem all of their tokens */
        uint256 allowed = redeemAllowedInternal(
            oTokenAddress,
            msg.sender,
            tokensHeld
        );
        if (allowed != 0) {
            return
                failOpaque(
                    Error.REJECTION,
                    FailureInfo.EXIT_MARKET_REJECTION,
                    allowed
                );
        }

        /* Return true if the sender is not already ‘in’ the market */
        if (!accountMembership[address(oToken)][msg.sender]) {
            return uint256(Error.NO_ERROR);
        }

        /* Set oToken account membership to false */
        delete accountMembership[address(oToken)][msg.sender];

        /* Delete oToken from the account’s list of assets */
        // load into memory for faster iteration
        IOToken[] memory userAssetList = accountAssets[msg.sender];
        uint256 len = userAssetList.length;
        uint256 assetIndex = len;
        for (uint256 i = 0; i < len; i++) {
            if (userAssetList[i] == oToken) {
                assetIndex = i;
                break;
            }
        }

        // We *must* have found the asset in the list or our redundant data structure is broken
        assert(assetIndex < len);

        // copy last item in list to location of item to be removed, reduce length by 1
        IOToken[] storage storedList = accountAssets[msg.sender];
        storedList[assetIndex] = storedList[storedList.length - 1];
        storedList.pop();

        emit MarketExited(oToken, msg.sender);

        return uint256(Error.NO_ERROR);
    }

    /*** Policy Hooks ***/

    /**
     * @notice Checks if the account should be allowed to mint tokens in the given market
     * @param oToken The market to verify the mint against
     * @param minter The account which would get the minted tokens
     * @param mintAmount The amount of underlying being supplied to the market in exchange for tokens
     * @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
     */
    function mintAllowed(
        address oToken,
        address minter,
        uint256 mintAmount
    ) external override returns (uint256) {
        // Pausing is a very serious situation - we revert to sound the alarms
        mintAmount; // not used yet
        require(!guardianPaused[oToken].mint, "mint is paused");

        if (!markets[oToken].isListed) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        // Sets an asset automatically as collateral if the user has no
        // oToken (on first deposit) and if the asset allows auto-collateralization
        if (
            IOToken(oToken).balanceOf(minter) == 0 &&
            markets[oToken].autoCollaterize
        ) {
            addToMarketInternal(IOToken(oToken), minter);
        }

        updateAndDistributeSupplierRewardsForToken(oToken, minter);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates mint and reverts on rejection. May emit logs.
     * @param oToken Asset being minted
     * @param minter The address minting the tokens
     * @param actualMintAmount The amount of the underlying asset being minted
     * @param mintTokens The number of tokens being minted
     */
    function mintVerify(
        address oToken,
        address minter,
        uint256 actualMintAmount,
        uint256 mintTokens
    ) external override {
        // Shh - currently unused
        oToken;
        minter;
        actualMintAmount;
        mintTokens;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /**
     * @notice Checks if the account should be allowed to redeem tokens in the given market
     * @param oToken The market to verify the redeem against
     * @param redeemer The account which would redeem the tokens
     * @param redeemTokens The number of oTokens to exchange for the underlying asset in the market
     * @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
     */
    function redeemAllowed(
        address oToken,
        address redeemer,
        uint256 redeemTokens
    ) external override returns (uint256) {
        uint256 allowed = redeemAllowedInternal(oToken, redeemer, redeemTokens);
        if (allowed != uint256(Error.NO_ERROR)) {
            return allowed;
        }

        updateAndDistributeSupplierRewardsForToken(oToken, redeemer);

        return uint256(Error.NO_ERROR);
    }

    function redeemAllowedInternal(
        address oToken,
        address redeemer,
        uint256 redeemTokens
    ) internal view returns (uint256) {
        if (!markets[oToken].isListed) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        /* If the redeemer is not 'in' the market, then we can bypass the liquidity check */
        if (!accountMembership[oToken][redeemer]) {
            return uint256(Error.NO_ERROR);
        }

        /* Otherwise, perform a hypothetical liquidity check to guard against shortfall */
        (
            Error err,
            ,
            uint256 shortfall
        ) = getHypotheticalAccountLiquidityInternal(
                redeemer,
                IOToken(oToken),
                redeemTokens,
                0
            );
        if (err != Error.NO_ERROR) {
            return uint256(err);
        }
        if (shortfall > 0) {
            return uint256(Error.INSUFFICIENT_LIQUIDITY);
        }

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates redeem and reverts on rejection. May emit logs.
     * @param oToken Asset being redeemed
     * @param redeemer The address redeeming the tokens
     * @param redeemAmount The amount of the underlying asset being redeemed
     * @param redeemTokens The number of tokens being redeemed
     */
    function redeemVerify(
        address oToken,
        address redeemer,
        uint256 redeemAmount,
        uint256 redeemTokens
    ) external pure override {
        // Shh - currently unused
        oToken;
        redeemer;

        // Require tokens is zero or amount is also zero
        require(redeemTokens != 0 || redeemAmount == 0, "redeemTokens zero");
    }

    /**
     * @notice Checks if the account should be allowed to borrow the underlying asset of the given market
     * @param oToken The market to verify the borrow against
     * @param borrower The account which would borrow the asset
     * @param borrowAmount The amount of underlying the account would borrow
     * @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
     */
    function borrowAllowed(
        address oToken,
        address borrower,
        uint256 borrowAmount
    ) external override returns (uint256) {
        // Pausing is a very serious situation - we revert to sound the alarms
        require(!guardianPaused[oToken].borrow, "borrow is paused");
        if (!markets[oToken].isListed) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        if (!accountMembership[oToken][borrower]) {
            // only oTokens may call borrowAllowed if borrower not in market
            require(msg.sender == oToken, "sender must be oToken");

            // attempt to add borrower to the market
            Error err = addToMarketInternal(IOToken(msg.sender), borrower);
            if (err != Error.NO_ERROR) {
                return uint256(err);
            }

            // it should be impossible to break the important invariant
            assert(accountMembership[oToken][borrower]);
        }

        if (oracle.getUnderlyingPrice(IOToken(oToken)) == 0) {
            return uint256(Error.PRICE_ERROR);
        }

        uint256 borrowCap = borrowCaps[oToken];
        // Borrow cap of 0 corresponds to unlimited borrowing
        if (borrowCap != 0) {
            require(
                (IOToken(oToken).totalBorrows() + borrowAmount) < borrowCap,
                "borrow cap reached"
            );
        }

        (
            Error err,
            ,
            uint256 shortfall
        ) = getHypotheticalAccountLiquidityInternal(
                borrower,
                IOToken(oToken),
                0,
                borrowAmount
            );

        if (err != Error.NO_ERROR) {
            return uint256(err);
        }
        if (shortfall > 0) {
            return uint256(Error.INSUFFICIENT_LIQUIDITY);
        }

        // Keep the flywheel moving
        updateAndDistributeBorrowerRewardsForToken(oToken, borrower);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates borrow and reverts on rejection. May emit logs.
     * @param oToken Asset whose underlying is being borrowed
     * @param borrower The address borrowing the underlying
     * @param borrowAmount The amount of the underlying asset requested to borrow
     */
    function borrowVerify(
        address oToken,
        address borrower,
        uint256 borrowAmount
    ) external override {
        // Shh - currently unused
        oToken;
        borrower;
        borrowAmount;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /**
     * @notice Checks if the account should be allowed to repay a borrow in the given market
     * @param oToken The market to verify the repay against
     * @param payer The account which would repay the asset
     * @param borrower The account which would borrowed the asset
     * @param repayAmount The amount of the underlying asset the account would repay
     * @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
     */
    function repayBorrowAllowed(
        address oToken,
        address payer,
        address borrower,
        uint256 repayAmount
    ) external override returns (uint256) {
        // Shh - currently unused
        payer;
        borrower;
        repayAmount;

        if (!markets[oToken].isListed) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        // Keep the flywheel moving
        updateAndDistributeBorrowerRewardsForToken(oToken, borrower);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates repayBorrow and reverts on rejection. May emit logs.
     * @param oToken Asset being repaid
     * @param payer The address repaying the borrow
     * @param borrower The address of the borrower
     * @param actualRepayAmount The amount of underlying being repaid
     */
    function repayBorrowVerify(
        address oToken,
        address payer,
        address borrower,
        uint256 actualRepayAmount,
        uint256 borrowerIndex
    ) external {
        // Shh - currently unused
        oToken;
        payer;
        borrower;
        actualRepayAmount;
        borrowerIndex;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /**
     * @notice Checks if the liquidation should be allowed to occur
     * @param oTokenBorrowed Asset which was borrowed by the borrower
     * @param oTokenCollateral Asset which was used as collateral and will be seized
     * @param liquidator The address repaying the borrow and seizing the collateral
     * @param borrower The address of the borrower
     * @param repayAmount The amount of underlying being repaid
     */
    function liquidateBorrowAllowed(
        address oTokenBorrowed,
        address oTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount
    ) external view override returns (uint256) {
        // Shh - currently unused
        liquidator;

        if (
            !markets[oTokenBorrowed].isListed ||
            !markets[oTokenCollateral].isListed
        ) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        uint256 borrowBalance = IOToken(oTokenBorrowed).borrowBalanceStored(
            borrower
        );

        /* allow accounts to be liquidated if the market is deprecated */
        if (isDeprecated(IOToken(oTokenBorrowed))) {
            require(
                borrowBalance >= repayAmount,
                "Can not repay more than the total borrow"
            );
        } else {
            /* The borrower must have shortfall in order to be liquidatable */
            (
                Error err,
                ,
                uint256 shortfall
            ) = getHypotheticalAccountLiquidityInternal(
                    borrower,
                    IOToken(address(0)),
                    0,
                    0
                );

            if (err != Error.NO_ERROR) {
                return uint256(err);
            }

            if (shortfall == 0) {
                return uint256(Error.INSUFFICIENT_SHORTFALL);
            }

            /* The liquidator may not repay more than what is allowed by the closeFactor */
            uint256 maxClose = mul_ScalarTruncate(
                Exp({mantissa: closeFactorMantissa}),
                borrowBalance
            );
            if (repayAmount > maxClose) {
                return uint256(Error.TOO_MUCH_REPAY);
            }
        }
        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates liquidateBorrow and reverts on rejection. May emit logs.
     * @param oTokenBorrowed Asset which was borrowed by the borrower
     * @param oTokenCollateral Asset which was used as collateral and will be seized
     * @param liquidator The address repaying the borrow and seizing the collateral
     * @param borrower The address of the borrower
     * @param actualRepayAmount The amount of underlying being repaid
     */
    function liquidateBorrowVerify(
        address oTokenBorrowed,
        address oTokenCollateral,
        address liquidator,
        address borrower,
        uint256 actualRepayAmount,
        uint256 seizeTokens
    ) external {
        // Shh - currently unused
        oTokenBorrowed;
        oTokenCollateral;
        liquidator;
        borrower;
        actualRepayAmount;
        seizeTokens;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /**
     * @notice Checks if the seizing of assets should be allowed to occur
     * @param oTokenCollateral Asset which was used as collateral and will be seized
     * @param oTokenBorrowed Asset which was borrowed by the borrower
     * @param liquidator The address repaying the borrow and seizing the collateral
     * @param borrower The address of the borrower
     * @param seizeTokens The number of collateral tokens to seize
     */
    function seizeAllowed(
        address oTokenCollateral,
        address oTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external override returns (uint256) {
        // Pausing is a very serious situation - we revert to sound the alarms
        require(!seizeGuardianPaused, "seize is paused");

        // Shh - currently unused
        seizeTokens;

        if (
            !markets[oTokenCollateral].isListed ||
            !markets[oTokenBorrowed].isListed
        ) {
            return uint256(Error.MARKET_NOT_LISTED);
        }

        if (
            IOToken(oTokenCollateral).comptroller() !=
            IOToken(oTokenBorrowed).comptroller()
        ) {
            return uint256(Error.COMPTROLLER_MISMATCH);
        }

        // Keep the flywheel moving
        updateRewardSupplyIndex(oTokenCollateral);
        distributeSupplierReward(oTokenCollateral, borrower);
        distributeSupplierReward(oTokenCollateral, liquidator);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates seize and reverts on rejection. May emit logs.
     * @param oTokenCollateral Asset which was used as collateral and will be seized
     * @param oTokenBorrowed Asset which was borrowed by the borrower
     * @param liquidator The address repaying the borrow and seizing the collateral
     * @param borrower The address of the borrower
     * @param seizeTokens The number of collateral tokens to seize
     */
    function seizeVerify(
        address oTokenCollateral,
        address oTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external override {
        // Shh - currently unused
        oTokenCollateral;
        oTokenBorrowed;
        liquidator;
        borrower;
        seizeTokens;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /**
     * @notice Checks if the account should be allowed to transfer tokens in the given market
     * @param oToken The market to verify the transfer against
     * @param src The account which sources the tokens
     * @param dst The account which receives the tokens
     * @param transferTokens The number of oTokens to transfer
     * @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
     */
    function transferAllowed(
        address oToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external override returns (uint256) {
        // Pausing is a very serious situation - we revert to sound the alarms
        require(!transferGuardianPaused, "transfer is paused");

        // Currently the only consideration is whether or not
        //  the src is allowed to redeem this many tokens
        uint256 allowed = redeemAllowedInternal(oToken, src, transferTokens);
        if (allowed != uint256(Error.NO_ERROR)) {
            return allowed;
        }

        // Keep the flywheel moving
        updateRewardSupplyIndex(oToken);
        distributeSupplierReward(oToken, src);
        distributeSupplierReward(oToken, dst);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Validates transfer and reverts on rejection. May emit logs.
     * @param oToken Asset being transferred
     * @param src The account which sources the tokens
     * @param dst The account which receives the tokens
     * @param transferTokens The number of oTokens to transfer
     */
    function transferVerify(
        address oToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external override {
        // Shh - currently unused
        oToken;
        src;
        dst;
        transferTokens;

        // Shh - we don't ever want this hook to be marked pure
        if (false) {
            maxAssets = maxAssets;
        }
    }

    /*** Liquidity/Liquidation Calculations ***/

    /**
     * @dev Local vars for avoiding stack-depth limits in calculating account liquidity.
     *  Note that `oTokenBalance` is the number of oTokens the account owns in the market,
     *  whereas `borrowBalance` is the amount of underlying that the account has borrowed.
     */
    struct AccountLiquidityLocalVars {
        uint256 sumCollateral;
        uint256 sumBorrowPlusEffects;
        uint256 oTokenBalance;
        uint256 borrowBalance;
        uint256 exchangeRateMantissa;
        uint256 oraclePriceMantissa;
        Exp collateralFactor;
        Exp exchangeRate;
        Exp oraclePrice;
        Exp tokensToDenom;
    }

    /**
     * @notice Determine the current account liquidity wrt collateral requirements
     * @return (possible error code (semi-opaque),
                account liquidity in excess of collateral requirements,
     *          account shortfall below collateral requirements)
     */
    function getAccountLiquidity(address account)
        public
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        (
            Error err,
            uint256 liquidity,
            uint256 shortfall
        ) = getHypotheticalAccountLiquidityInternal(
                account,
                IOToken(address(0)),
                0,
                0
            );

        return (uint256(err), liquidity, shortfall);
    }

    /**
     * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
     * @param oTokenModify The market to hypothetically redeem/borrow in
     * @param account The account to determine liquidity for
     * @param redeemTokens The number of tokens to hypothetically redeem
     * @param borrowAmount The amount of underlying to hypothetically borrow
     * @return (possible error code (semi-opaque),
                hypothetical account liquidity in excess of collateral requirements,
     *          hypothetical account shortfall below collateral requirements)
     */
    function getHypotheticalAccountLiquidity(
        address account,
        address oTokenModify,
        uint256 redeemTokens,
        uint256 borrowAmount
    )
        public
        view
        returns (
            uint256,
            uint256,
            uint256
        )
    {
        (
            Error err,
            uint256 liquidity,
            uint256 shortfall
        ) = getHypotheticalAccountLiquidityInternal(
                account,
                IOToken(oTokenModify),
                redeemTokens,
                borrowAmount
            );
        return (uint256(err), liquidity, shortfall);
    }

    /**
     * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
     * @param oTokenModify The market to hypothetically redeem/borrow in
     * @param account The account to determine liquidity for
     * @param redeemTokens The number of tokens to hypothetically redeem
     * @param borrowAmount The amount of underlying to hypothetically borrow
     * @dev Note that we calculate the exchangeRateStored for each collateral oToken using stored data,
     *  without calculating accumulated interest.
     * @return (possible error code,
                hypothetical account liquidity in excess of collateral requirements,
     *          hypothetical account shortfall below collateral requirements)
     */
    function getHypotheticalAccountLiquidityInternal(
        address account,
        IOToken oTokenModify,
        uint256 redeemTokens,
        uint256 borrowAmount
    )
        internal
        view
        returns (
            Error,
            uint256,
            uint256
        )
    {
        AccountLiquidityLocalVars memory vars; // Holds all our calculation results
        uint256 oErr;

        // For each asset the account is in
        IOToken[] memory assets = accountAssets[account];
        for (uint256 i = 0; i < assets.length; i++) {
            IOToken asset = assets[i];

            // Read the balances and exchange rate from the oToken
            (
                oErr,
                vars.oTokenBalance,
                vars.borrowBalance,
                vars.exchangeRateMantissa
            ) = asset.getAccountSnapshot(account);
            if (oErr != 0) {
                // semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgrades
                return (Error.SNAPSHOT_ERROR, 0, 0);
            }
            vars.collateralFactor = Exp({
                mantissa: markets[address(asset)].collateralFactorMantissa
            });
            vars.exchangeRate = Exp({mantissa: vars.exchangeRateMantissa});

            // Get the normalized price of the asset
            vars.oraclePriceMantissa = oracle.getUnderlyingPrice(asset);
            if (vars.oraclePriceMantissa == 0) {
                return (Error.PRICE_ERROR, 0, 0);
            }
            vars.oraclePrice = Exp({mantissa: vars.oraclePriceMantissa});

            // Pre-compute a conversion factor from tokens -> ether (normalized price value)
            vars.tokensToDenom = mul_(
                mul_(vars.collateralFactor, vars.exchangeRate),
                vars.oraclePrice
            );

            // sumCollateral += tokensToDenom * oTokenBalance
            vars.sumCollateral = mul_ScalarTruncateAddUInt(
                vars.tokensToDenom,
                vars.oTokenBalance,
                vars.sumCollateral
            );

            // sumBorrowPlusEffects += oraclePrice * borrowBalance
            vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
                vars.oraclePrice,
                vars.borrowBalance,
                vars.sumBorrowPlusEffects
            );

            // Calculate effects of interacting with oTokenModify
            if (asset == oTokenModify) {
                // redeem effect
                // sumBorrowPlusEffects += tokensToDenom * redeemTokens
                vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
                    vars.tokensToDenom,
                    redeemTokens,
                    vars.sumBorrowPlusEffects
                );

                // borrow effect
                // sumBorrowPlusEffects += oraclePrice * borrowAmount
                vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
                    vars.oraclePrice,
                    borrowAmount,
                    vars.sumBorrowPlusEffects
                );
            }
        }

        // These are safe, as the underflow condition is checked first
        unchecked {
            if (vars.sumCollateral > vars.sumBorrowPlusEffects) {
                return (
                    Error.NO_ERROR,
                    vars.sumCollateral - vars.sumBorrowPlusEffects,
                    0
                );
            } else {
                return (
                    Error.NO_ERROR,
                    0,
                    vars.sumBorrowPlusEffects - vars.sumCollateral
                );
            }
        }
    }

    /**
     * @notice Calculate number of tokens of collateral asset to seize given an underlying amount
     * @dev Used in liquidation (called in oToken.liquidateBorrowFresh)
     * @param oTokenBorrowed The address of the borrowed oToken
     * @param oTokenCollateral The address of the collateral oToken
     * @param actualRepayAmount The amount of oTokenBorrowed underlying to convert into oTokenCollateral tokens
     * @return (errorCode, number of oTokenCollateral tokens to be seized in a liquidation)
     */
    function liquidateCalculateSeizeTokens(
        address oTokenBorrowed,
        address oTokenCollateral,
        uint256 actualRepayAmount
    ) external view override returns (uint256, uint256) {
        /* Read oracle prices for borrowed and collateral markets */
        uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(
            IOToken(oTokenBorrowed)
        );
        uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(
            IOToken(oTokenCollateral)
        );

        if (priceBorrowedMantissa == 0 || priceCollateralMantissa == 0) {
            return (uint256(Error.PRICE_ERROR), 0);
        }

        /*
         * Get the exchange rate and calculate the number of collateral tokens to seize:
         *  seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral
         *  seizeTokens = seizeAmount / exchangeRate
         *   = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)
         */
        uint256 exchangeRateMantissa = IOToken(oTokenCollateral)
            .exchangeRateStored(); // Note: reverts on error

        Exp memory numerator = mul_(
            Exp({mantissa: liquidationIncentiveMantissa}),
            Exp({mantissa: priceBorrowedMantissa})
        );
        Exp memory denominator = mul_(
            Exp({mantissa: priceCollateralMantissa}),
            Exp({mantissa: exchangeRateMantissa})
        );
        Exp memory ratio = div_(numerator, denominator);

        uint256 seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);

        return (uint256(Error.NO_ERROR), seizeTokens);
    }

    /*** Admin Functions ***/

    /**
     * @notice Sets a new price oracle for the comptroller
     * @dev Admin function to set a new price oracle
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setPriceOracle(PriceOracle newOracle) public returns (uint256) {
        // Check caller is admin
        if (msg.sender != admin) {
            return
                fail(
                    Error.UNAUTHORIZED,
                    FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK
                );
        }

        // Track the old oracle for the comptroller
        PriceOracle oldOracle = oracle;

        // Set comptroller's oracle to newOracle
        oracle = newOracle;

        // Emit NewPriceOracle(oldOracle, newOracle)
        emit NewPriceOracle(oldOracle, newOracle);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Sets the closeFactor used when liquidating borrows
     * @dev Admin function to set closeFactor
     * @param newCloseFactorMantissa New close factor, scaled by 1e18
     * @return uint 0=success, otherwise a failure
     */
    function _setCloseFactor(uint256 newCloseFactorMantissa)
        external
        onlyAdmin
        returns (uint256)
    {
        uint256 oldCloseFactorMantissa = closeFactorMantissa;
        closeFactorMantissa = newCloseFactorMantissa;
        emit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa);

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Sets the collateralFactor for a market
     * @dev Admin function to set per-market collateralFactor
     * @param oToken The market to set the factor on
     * @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18
     * @return uint 0=success, otherwise a failure. (See ErrorReporter for details)
     */
    function _setCollateralFactor(
        IOToken oToken,
        uint256 newCollateralFactorMantissa
    ) external returns (uint256) {
        // Check caller is admin
        if (msg.sender != admin) {
            return
                fail(
                    Error.UNAUTHORIZED,
                    FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK
                );
        }

        // Verify market is listed
        Market storage market = markets[address(oToken)];
        if (!market.isListed) {
            return
                fail(
                    Error.MARKET_NOT_LISTED,
                    FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS
                );
        }

        Exp memory newCollateralFactorExp = Exp({
            mantissa: newCollateralFactorMantissa
        });

        // Check collateral factor <= 0.9
        Exp memory highLimit = Exp({mantissa: collateralFactorMaxMantissa});
        if (lessThanExp(highLimit, newCollateralFactorExp)) {
            return
                fail(
                    Error.INVALID_COLLATERAL_FACTOR,
                    FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION
                );
        }

        // If collateral factor != 0, fail if price == 0
        if (
            newCollateralFactorMantissa != 0 &&
            oracle.getUnderlyingPrice(oToken) == 0
        ) {
            return
                fail(
                    Error.PRICE_ERROR,
                    FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE
                );
        }

        // Set market's collateral factor to new collateral factor, remember old value
        uint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;
        market.collateralFactorMantissa = newCollateralFactorMantissa;

        // Emit event with asset, old collateral factor, and new collateral factor
        emit NewCollateralFactor(
            oToken,
            oldCollateralFactorMantissa,
            newCollateralFactorMantissa
        );

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Sets liquidationIncentive
     * @dev Admin function to set liquidationIncentive
     * @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18
     * @return uint 0=success, otherwise a failure. (See ErrorReporter for details)
     */
    function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa)
        external
        returns (uint256)
    {
        // Check caller is admin
        if (msg.sender != admin) {
            return
                fail(
                    Error.UNAUTHORIZED,
                    FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK
                );
        }

        // Save current value for use in log
        uint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;

        // Set liquidation incentive to new incentive
        liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;

        // Emit event with old incentive, new incentive
        emit NewLiquidationIncentive(
            oldLiquidationIncentiveMantissa,
            newLiquidationIncentiveMantissa
        );

        return uint256(Error.NO_ERROR);
    }

    /**
     * @notice Add the market to the markets mapping and set it as listed
     * @dev Admin function to set isListed and add support for the market
     * @param oToken The address of the market (token) to list
     * @param _autoCollaterize Boolean value representing whether the market should have auto-collateralisation enabled
     * @return uint 0=success, otherwise a failure. (See enum Error for details)
     */
    function _supportMarket(IOToken oToken, bool _autoCollaterize)
        external
        returns (uint256)
    {
        if (msg.sender != admin) {
            return
                fail(
                    Error.UNAUTHORIZED,
                    FailureInfo.SUPPORT_MARKET_OWNER_CHECK
                );
        }

        if (markets[address(oToken)].isListed) {
            return
                fail(
                    Error.MARKET_ALREADY_LISTED,
                    FailureInfo.SUPPORT_MARKET_EXISTS
                );
        }

        oToken.isOToken(); // Sanity check to make sure its really a IOToken

        markets[address(oToken)] = Market({
            isListed: true,
            autoCollaterize: _autoCollaterize,
            collateralFactorMantissa: 0
        });

        emit MarketAutoCollateralized(_autoCollaterize);
        
        for (uint i = 0; i < allMarkets.length; i ++) {
            require(allMarkets[i] != IOToken(oToken), "market already added");
        }

        allMarkets.push(oToken);
        _initializeMarket(address(oToken));

        emit MarketListed(oToken);

        return uint256(Error.NO_ERROR);
    }

    function _initializeMarket(address oToken) internal {
        uint32 timestamp = safe32(getTimestamp());

        MarketState storage supState = supplyState[oToken];
        MarketState storage borState = borrowState[oToken];

        /*
         * Update market state indices
         */
        if (supState.index == 0) {
            // Initialize supply state index with default value
            supState.index = marketInitialIndex;
        }

        if (borState.index == 0) {
            // Initialize borrow state index with default value
            borState.index = marketInitialIndex;
        }

        /*
         * Update market state timestamps
         */
        supState.timestamp = borState.timestamp = timestamp;
    }

    /**
     * @notice Set the given borrow caps for the given oToken markets. Borrowing that brings total borrows to or above borrow cap will revert.
     * @dev Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing.
     * @param oTokens The addresses of the markets (tokens) to change the borrow caps for
     * @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing.
     */
    function _setMarketBorrowCaps(
        IOToken[] calldata oTokens,
        uint256[] calldata newBorrowCaps
    ) external {
        require(
            msg.sender == admin || msg.sender == borrowCapGuardian,
            "only admin or borrowCapGuardian"
        );

        uint256 numMarkets = oTokens.length;
        uint256 numBorrowCaps = newBorrowCaps.length;

        require(
            numMarkets != 0 && numMarkets == numBorrowCaps,
            "invalid input"
        );

        for (uint256 i = 0; i < numMarkets; i++) {
            borrowCaps[address(oTokens[i])] = newBorrowCaps[i];
            emit NewBorrowCap(oTokens[i], newBorrowCaps[i]);
        }
    }

    /**
     * @notice Admin function to change the Borrow Cap Guardian
     * @param newBorrowCapGuardian The address of the new Borrow Cap Guardian
     */
    function _setBorrowCapGuardian(address newBorrowCapGuardian)
        external
        onlyAdmin
    {
        // Save current value for inclusion in log
        address oldBorrowCapGuardian = borrowCapGuardian;

        // Store borrowCapGuardian with value newBorrowCapGuardian
        borrowCapGuardian = newBorrowCapGuardian;

        // Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian)
        emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian);
    }

    /**
     * @notice Admin function to change the Pause Guardian
     * @param newPauseGuardian The address of the new Pause Guardian
     * @return uint 0=success, otherwise a failure. (See enum Error for details)
     */
    function _setPauseGuardian(address newPauseGuardian)
        public
        returns (uint256)
    {
        if (msg.sender != admin) {
            return
                fail(
                    Error.UNAUTHORIZED,
                    FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK
                );
        }

        // Save current value for inclusion in log
        address oldPauseGuardian = pauseGuardian;

        // Store pauseGuardian with value newPauseGuardian
        pauseGuardian = newPauseGuardian;

        // Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian)
        emit NewPauseGuardian(oldPauseGuardian, pauseGuardian);

        return uint256(Error.NO_ERROR);
    }

    function onlyAdminOrGuardian() internal view {
        require(
            msg.sender == admin || msg.sender == pauseGuardian,
            "only pause guardian and admin"
        );
    }

    function _setMintPaused(IOToken oToken, bool state) public returns (bool) {
        require(
            markets[address(oToken)].isListed,
            "cannot pause: market not listed"
        );
        onlyAdminOrGuardian();
        require(msg.sender == admin || state, "only admin can unpause");

        guardianPaused[address(oToken)].mint = state;
        emit ActionPaused(oToken, "Mint", state);
        return state;
    }

    function _setBorrowPaused(IOToken oToken, bool state)
        public
        returns (bool)
    {
        require(
            markets[address(oToken)].isListed,
            "cannot pause: market not listed"
        );
        onlyAdminOrGuardian();
        require(msg.sender == admin || state, "only admin can unpause");

        guardianPaused[address(oToken)].borrow = state;
        emit ActionPaused(oToken, "Borrow", state);
        return state;
    }

    function _setTransferPaused(bool state) public returns (bool) {
        onlyAdminOrGuardian();
        require(msg.sender == admin || state, "only admin can unpause");

        transferGuardianPaused = state;
        emit ActionPausedGlobally("Transfer", state);
        return state;
    }

    function _setSeizePaused(bool state) public returns (bool) {
        onlyAdminOrGuardian();
        require(msg.sender == admin || state, "only admin can unpause");

        seizeGuardianPaused = state;
        emit ActionPausedGlobally("Seize", state);
        return state;
    }

    function _become(IUnitroller unitroller) public {
        require(
            msg.sender == unitroller.admin(),
            "only unitroller admin can _become"
        );
        require(
            unitroller._acceptImplementation() == 0,
            "change not authorized"
        );
    }

    /**
     * @notice Checks caller is admin, or this contract is becoming the new implementation
     */
    function adminOrInitializing() internal view returns (bool) {
        return msg.sender == admin || msg.sender == comptrollerImplementation;
    }

    /*** VIX Distribution ***/

    /**
     * @notice Set Reward speed for a single market
     * @param oToken The market whose Reward speed to update
     * @param supplySpeed New supply-side Reward speed for market
     * @param borrowSpeed New borrow-side Reward speed for market
     */
    function setRewardSpeedInternal(
        IOToken oToken,
        uint256 supplySpeed,
        uint256 borrowSpeed
    ) internal {
        require(markets[address(oToken)].isListed, "0VIX market is not listed");

        if (rewardSupplySpeeds[address(oToken)] != supplySpeed) {
            // Supply speed updated so let's update supply state to ensure that
            //  1. Reward accrued properly for the old speed, and
            //  2. Reward accrued at the new speed starts after this block.
            updateRewardSupplyIndex(address(oToken));

            // Update speed and emit event
            rewardSupplySpeeds[address(oToken)] = supplySpeed;
            emit RewardSupplySpeedUpdated(oToken, supplySpeed);
        }

        if (rewardBorrowSpeeds[address(oToken)] != borrowSpeed) {
            // Borrow speed updated so let's update borrow state to ensure that
            //  1. Reward accrued properly for the old speed, and
            //  2. Reward accrued at the new speed starts after this block.
            Exp memory borrowIndex = Exp({mantissa: oToken.borrowIndex()});
            updateRewardBorrowIndex(address(oToken), borrowIndex);

            // Update speed and emit event
            rewardBorrowSpeeds[address(oToken)] = borrowSpeed;
            emit RewardBorrowSpeedUpdated(oToken, borrowSpeed);
        }
    }

    function updateAndDistributeSupplierRewardsForToken(
        address oToken,
        address account
    ) public override {
        updateRewardSupplyIndex(oToken);
        distributeSupplierReward(oToken, account);
    }

    function updateAndDistributeBorrowerRewardsForToken(
        address oToken,
        address borrower
    ) public override {
        Exp memory marketBorrowIndex = Exp({
            mantissa: IOToken(oToken).borrowIndex()
        });
        updateRewardBorrowIndex(oToken, marketBorrowIndex);
        distributeBorrowerReward(oToken, borrower, marketBorrowIndex);
    }

    /**
     * @notice Accrue Reward to the market by updating the supply index
     * @param oToken The market whose supply index to update
     * @dev Index is a cumulative sum of the Reward per oToken accrued.
     */
    function updateRewardSupplyIndex(address oToken) internal {
        MarketState storage supplyState = supplyState[oToken];
        uint256 supplySpeed = rewardSupplySpeeds[oToken];
        uint32 timestamp = safe32(getTimestamp());
        uint256 deltaTimestamps = uint256(timestamp) -
            uint256(supplyState.timestamp);
        if (deltaTimestamps > 0) {
            if (supplySpeed > 0) {
                uint256 supplyTokens = address(boostManager) == address(0)
                    ? IOToken(oToken).totalSupply()
                    : boostManager.boostedTotalSupply(oToken);
                uint256 rewardAccrued = deltaTimestamps * supplySpeed;
                Double memory ratio = supplyTokens > 0
                    ? fraction(rewardAccrued, supplyTokens)
                    : Double({mantissa: 0});
                supplyState.index = safe224(
                    add_(Double({mantissa: supplyState.index}), ratio).mantissa
                );
            }
            supplyState.timestamp = timestamp;
        }
    }

    /**
     * @notice Accrue Reward to the market by updating the borrow index
     * @param oToken The market whose borrow index to update
     * @dev Index is a cumulative sum of the Reward per oToken accrued.
     */
    function updateRewardBorrowIndex(
        address oToken,
        Exp memory marketBorrowIndex
    ) internal {
        MarketState storage borrowState = borrowState[oToken];
        uint256 borrowSpeed = rewardBorrowSpeeds[oToken];
        uint32 timestamp = safe32(getTimestamp());
        uint256 deltaTimestamps = uint256(timestamp) -
            uint256(borrowState.timestamp);
        if (deltaTimestamps > 0) {
            if (borrowSpeed > 0) {
                uint256 borrowAmount = div_(
                    address(boostManager) == address(0)
                        ? IOToken(oToken).totalBorrows()
                        : boostManager.boostedTotalBorrows(oToken),
                    marketBorrowIndex
                );
                uint256 rewardAccrued = deltaTimestamps * borrowSpeed;
                Double memory ratio = borrowAmount > 0
                    ? fraction(rewardAccrued, borrowAmount)
                    : Double({mantissa: 0});
                borrowState.index = safe224(
                    add_(Double({mantissa: borrowState.index}), ratio).mantissa
                );
            }
            borrowState.timestamp = timestamp;
        }
    }

    /**
     * @notice Calculate Reward accrued by a supplier
     * @param oToken The market in which the supplier is interacting
     * @param supplier The address of the supplier to distribute Reward to
     */
    function distributeSupplierReward(address oToken, address supplier)
        internal
    {
        // TODO: Don't distribute supplier Reward if the user is not in the supplier market.
        // This check should be as gas efficient as possible as distributeSupplierReward is called in many places.
        // - We really don't want to call an external contract as that's quite expensive.

        uint256 supplyIndex = supplyState[oToken].index;
        uint256 supplierIndex = rewardSupplierIndex[oToken][supplier];

        // Update supplier's index to the current index since we are distributing accrued VIX
        rewardSupplierIndex[oToken][supplier] = supplyIndex;

        if (supplierIndex == 0 && supplyIndex >= 0) {
            return;
        }

        // Calculate change in the cumulative sum of the Reward per oToken accrued
        Double memory deltaIndex = Double({
            mantissa: supplyIndex - supplierIndex
        });

        uint256 supplierTokens = address(boostManager) == address(0)
            ? IOToken(oToken).balanceOf(supplier)
            : boostManager.boostedSupplyBalanceOf(oToken, supplier);

        if (supplyIndex != supplierIndex) {
            // Calculate Reward accrued: oTokenAmount * accruedPerOToken
            uint256 supplierDelta = mul_(supplierTokens, deltaIndex);

            uint256 supplierAccrued = rewardAccrued[supplier] + supplierDelta;
            rewardAccrued[supplier] = supplierAccrued;

            emit DistributedSupplierReward(
                IOToken(oToken),
                supplier,
                supplierDelta,
                supplyIndex
            );
        }
    }

    /**
     * @notice Calculate Reward accrued by a borrower
     * @dev Borrowers will not begin to accrue until after the first interaction with the protocol.
     * @param oToken The market in which the borrower is interacting
     * @param borrower The address of the borrower to distribute Reward to
     */
    function distributeBorrowerReward(
        address oToken,
        address borrower,
        Exp memory marketBorrowIndex
    ) internal {
        // TODO: Don't distribute supplier Reward if the user is not in the borrower market.
        // This check should be as gas efficient as possible as distributeBorrowerReward is called in many places.
        // - We really don't want to call an external contract as that's quite expensive.

        uint256 borrowIndex = borrowState[oToken].index;
        uint256 borrowerIndex = rewardBorrowerIndex[oToken][borrower];

        // Update borrowers's index to the current index since we are distributing accrued VIX
        rewardBorrowerIndex[oToken][borrower] = borrowIndex;

        if (borrowerIndex == 0 && borrowIndex >= 0) {
            return;
        }

        // Calculate change in the cumulative sum of the Reward per borrowed unit accrued
        Double memory deltaIndex = Double({
            mantissa: borrowIndex - borrowerIndex
        });

        if (borrowIndex != borrowerIndex) {
            uint256 borrowerAmount = div_(
                address(boostManager) == address(0)
                    ? IOToken(oToken).borrowBalanceStored(borrower)
                    : boostManager.boostedBorrowBalanceOf(oToken, borrower),
                marketBorrowIndex
            );

            // Calculate Reward accrued: oTokenAmount * accruedPerBorrowedUnit
            uint256 borrowerDelta = mul_(borrowerAmount, deltaIndex);

            uint256 borrowerAccrued = rewardAccrued[borrower] + borrowerDelta;
            rewardAccrued[borrower] = borrowerAccrued;

            emit DistributedBorrowerReward(
                IOToken(oToken),
                borrower,
                borrowerDelta,
                borrowIndex
            );
        }
    }

    /**
     * @notice Calculate additional accrued Reward for a contributor since last accrual
     * @param contributor The address to calculate contributor rewards for
     */
    function updateContributorRewards(address contributor) public {
        uint256 rewardSpeed = rewardContributorSpeeds[contributor];
        uint256 timestamp = getTimestamp();
        uint256 deltaTimestamps = timestamp - lastContributorTimestamp[contributor];
        if (deltaTimestamps > 0 && rewardSpeed > 0) {
            uint256 newAccrued = deltaTimestamps * rewardSpeed;
            uint256 contributorAccrued = rewardAccrued[contributor] +
                newAccrued;

            rewardAccrued[contributor] = contributorAccrued;
            lastContributorTimestamp[contributor] = timestamp;
        }
    }

    /**
     * @notice Claim all the reward accrued by holder in all markets
     * @param holder The address to claim Reward for
     */

    function claimReward(address holder) public returns (uint256) {
        for (uint256 i = 0; i < allMarkets.length; i++) {
            IOToken oToken = allMarkets[i];
            require(markets[address(oToken)].isListed, "market must be listed");
            updateAndDistributeBorrowerRewardsForToken(address(oToken), holder);
            updateAndDistributeSupplierRewardsForToken(address(oToken), holder);
        }
        uint256 totalReward = rewardAccrued[holder];
        rewardAccrued[holder] = grantRewardInternal(holder, totalReward);
        return totalReward;
    }

    /**
     * @notice Claim all the reward accrued by holder in the specified markets
     * @param holder The address to claim Reward for
     * @param oTokens The list of markets to claim Reward in
     */
    function claimRewards(address holder, IOToken[] memory oTokens) public {
        // todo: undo _
        address[] memory holders = new address[](1);
        holders[0] = holder;
        claimRewards(holders, oTokens, true, true);
    }

    /**
     * @notice Claim all reward accrued by the holders
     * @param holders The addresses to claim Reward for
     * @param oTokens The list of markets to claim Reward in
     * @param borrowers Whether or not to claim Reward earned by borrowing
     * @param suppliers Whether or not to claim Reward earned by supplying
     */
    function claimRewards(
        address[] memory holders,
        IOToken[] memory oTokens,
        bool borrowers,
        bool suppliers
    ) public {
        for (uint256 i = 0; i < oTokens.length; i++) {
            IOToken oToken = oTokens[i];
            require(markets[address(oToken)].isListed, "market must be listed");
            if (borrowers) {
                Exp memory borrowIndex = Exp({mantissa: oToken.borrowIndex()});
                updateRewardBorrowIndex(address(oToken), borrowIndex);
                for (uint256 j = 0; j < holders.length; j++) {
                    distributeBorrowerReward(
                        address(oToken),
                        holders[j],
                        borrowIndex
                    );
                }
            }
            if (suppliers) {
                updateRewardSupplyIndex(address(oToken));
                for (uint256 j = 0; j < holders.length; j++) {
                    distributeSupplierReward(address(oToken), holders[j]);
                }
            }
        }
        for (uint256 j = 0; j < holders.length; j++) {
            rewardAccrued[holders[j]] = grantRewardInternal(
                holders[j],
                rewardAccrued[holders[j]]
            );
        }
    }

    /**
     * @notice Transfer Reward to the user
     * @dev Note: If there is not enough VIX, we do not perform the transfer all.
     * @param user The address of the user to transfer Reward to
     * @param amount The amount of Reward to (possibly) transfer
     * @return The amount of Reward which was NOT transferred to the user
     */
    function grantRewardInternal(address user, uint256 amount)
        internal
        returns (uint256)
    {
        I0vix vix = I0vix(getVixAddress());
        if (address(vix) != address(0)) {
            uint256 rewardRemaining = vix.balanceOf(address(this));
            if (amount > 0 && amount <= rewardRemaining) {
                vix.transfer(user, amount);
                emit VixClaimed(user, amount);
                return 0;
            }
        }

        return amount;
    }

    /*** VIX Distribution Admin ***/

    /**
     * @notice Transfer Reward to the recipient
     * @dev Note: If there is not enough VIX, we do not perform the transfer all.
     * @param recipient The address of the recipient to transfer Reward to
     * @param amount The amount of Reward to (possibly) transfer
     */
    function _grantReward(address recipient, uint256 amount) public {
        require(adminOrInitializing(), "only admin can grant reward");
        uint256 amountLeft = grantRewardInternal(recipient, amount);
        require(amountLeft == 0, "insufficient token for grant");
        emit VixGranted(recipient, amount);
    }

    /**
     * @notice Set Reward borrow and supply speeds for the specified markets.
     * @param oTokens The markets whose Reward speed to update.
     * @param supplySpeeds New supply-side Reward speed for the corresponding market.
     * @param borrowSpeeds New borrow-side Reward speed for the corresponding market.
     */
    function _setRewardSpeeds(
        address[] memory oTokens,
        uint256[] memory supplySpeeds,
        uint256[] memory borrowSpeeds
    ) public override {
        require(
            msg.sender == admin || msg.sender == rewardUpdater,
            "only admin can set reward speed"
        );

        uint256 numTokens = oTokens.length;
        require(
            numTokens == supplySpeeds.length &&
                numTokens == borrowSpeeds.length,
            "Comptroller::_setRewardSpeeds invalid input"
        );

        for (uint256 i = 0; i < numTokens; ++i) {
            setRewardSpeedInternal(
                IOToken(oTokens[i]),
                supplySpeeds[i],
                borrowSpeeds[i]
            );
        }
    }

    /**
     * @notice Set Reward speed for a single contributor
     * @param contributor The contributor whose Reward speed to update
     * @param rewardSpeed New Reward speed for contributor
     */
    function _setContributorRewardSpeed(
        address contributor,
        uint256 rewardSpeed
    ) public {
        require(
            msg.sender == admin || msg.sender == rewardUpdater,
            "only admin can set reward speed"
        );

        // note that Reward speed could be set to 0 to halt liquidity rewards for a contributor
        updateContributorRewards(contributor);
        if (rewardSpeed == 0) {
            // release storage
            delete lastContributorTimestamp[contributor];
        } else {
            lastContributorTimestamp[contributor] = getTimestamp();
        }
        rewardContributorSpeeds[contributor] = rewardSpeed;

        emit ContributorRewardSpeedUpdated(contributor, rewardSpeed);
    }

    /**
     * @notice Return all of the markets
     * @dev The automatic getter may be used to access an individual market.
     * @return The list of market addresses
     */
    function getAllMarkets() public view override returns (IOToken[] memory) {
        return allMarkets;
    }

    /**
     * @notice Returns true if the given oToken market has been deprecated
     * @dev All borrows in a deprecated oToken market can be immediately liquidated
     * @param oToken The market to check if deprecated
     */
    function isDeprecated(IOToken oToken) public view returns (bool) {
        return
            markets[address(oToken)].collateralFactorMantissa == 0 &&
            guardianPaused[address(oToken)].borrow &&
            oToken.reserveFactorMantissa() == 1e18;
    }

    function getTimestamp() public view returns (uint256) {
        return block.timestamp;
    }

    /**
     * @notice Return the address of the 0VIX token
     * @return The address of VIX
     */
    function getVixAddress() public view returns (address) {
        return vixAddress;
    }

    /**
     * @notice Set the 0VIX token address
     */
    function setVixAddress(address newVixAddress) public onlyAdmin {
        require(newVixAddress != address(0), "no zero address allowed");
        require(vixAddress == address(0), "VIX already set");
        vixAddress = newVixAddress;
        emit VixAddressSet(newVixAddress);
    }

    /**
     * @notice Set the booster manager address
     */
    function setBoostManager(address newBoostManager) public onlyAdmin {
        require(newBoostManager != address(0), "no zero address allowed");
        require(address(boostManager) == address(0), "VIX already set");
        boostManager = IBoostManager(newBoostManager);
        emit BoostManagerSet(newBoostManager);
    }

    function getBoostManager() external view override returns (address) {
        return address(boostManager);
    }

    function setRewardUpdater(address _rewardUpdater) public onlyAdmin {
        require(_rewardUpdater != address(0), "no zero address allowed");
        rewardUpdater = _rewardUpdater;
        emit RewardUpdaterModified(_rewardUpdater);
    }

    function setAutoCollaterize(address market, bool flag) external onlyAdmin {
        markets[market].autoCollaterize = flag;
        emit MarketAutoCollateralized(flag);
    }

    /**
     * @notice payable function needed to receive NATIVE
     */
    receive() external payable {}
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 3 of 13 : ComptrollerStorage.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

import "./otokens/interfaces/IOToken.sol";
import "./PriceOracle.sol";
import "./vote-escrow/interfaces/IBoostManager.sol";

import "./interfaces/IComptroller.sol";
import "./UnitrollerAdminStorage.sol";

abstract contract ComptrollerV1Storage is IComptroller, UnitrollerAdminStorage  {
    /**
     * @notice Oracle which gives the price of any given asset
     */
    PriceOracle public override oracle;

    /**
     * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
     */
    uint public closeFactorMantissa;

    /**
     * @notice Multiplier representing the discount on collateral that a liquidator receives
     */
    uint public liquidationIncentiveMantissa;

    /**
     * @notice Max number of assets a single account can participate in (borrow or use as collateral)
     */
    uint public maxAssets;

    /**
     * @notice Per-account mapping of "assets you are in", capped by maxAssets
     */
    mapping(address => IOToken[]) public accountAssets;

    /// @notice Per-market mapping of "accounts in this asset"
    mapping(address => mapping(address => bool)) public accountMembership;

}

abstract contract ComptrollerV2Storage is ComptrollerV1Storage {
    struct Market {
        /// @notice Whether or not this market is listed
        bool isListed;
        // markets marked with autoCollaterize are automatically set as collateral for the user at the first mint
        bool autoCollaterize;
        /**
         * @notice Multiplier representing the most one can borrow against their collateral in this market.
         *  For instance, 0.9 to allow borrowing 90% of collateral value.
         *  Must be between 0 and 1, and stored as a mantissa.
         */
        uint collateralFactorMantissa;
    }

    /**
     * @notice Official mapping of oTokens -> Market metadata
     * @dev Used e.g. to determine if a market is supported
     */
    mapping(address => Market) public markets;


    /**
     * @notice The Pause Guardian can pause certain actions as a safety mechanism.
     *  Actions which allow users to remove their own assets cannot be paused.
     *  Liquidation / seizing / transfer can only be paused globally, not by market.
     */
    address public pauseGuardian;
    bool public _mintGuardianPaused;
    bool public _borrowGuardianPaused;
    bool public transferGuardianPaused;
    bool public seizeGuardianPaused;
    
    struct PauseData {
        bool mint;
        bool borrow;
    }

    mapping(address => PauseData) public guardianPaused;
}

abstract contract ComptrollerV3Storage is ComptrollerV2Storage {
    struct MarketState {
        /// @notice The market's last updated tokenBorrowIndex or tokenSupplyIndex
        uint224 index;

        /// @notice The timestamp the index was last updated at
        uint32 timestamp;
    }

    /// @notice A list of all markets
    IOToken[] public allMarkets;

    /// @notice The rate at which the flywheel distributes VIX, per second
    uint public compRate;

    /// @notice The portion of compRate that each market currently receives
    mapping(address => uint) public rewardSpeeds;

    /// @notice The 0VIX market supply state for each market
    mapping(address => MarketState) public supplyState;

    /// @notice The 0VIX market borrow state for each market
    mapping(address => MarketState) public borrowState;

    /// @notice The 0VIX borrow index for each market for each supplier as of the last time they accrued VIX
    mapping(address => mapping(address => uint)) public rewardSupplierIndex;

    /// @notice The 0VIX borrow index for each market for each borrower as of the last time they accrued VIX
    mapping(address => mapping(address => uint)) public rewardBorrowerIndex;

    /// @notice The VIX accrued but not yet transferred to each user
    mapping(address => uint) public rewardAccrued;
}

abstract contract ComptrollerV4Storage is ComptrollerV3Storage {
    // @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market.
    address public borrowCapGuardian;

    // @notice Borrow caps enforced by borrowAllowed for each oToken address. Defaults to zero which corresponds to unlimited borrowing.
    mapping(address => uint) public borrowCaps;
}

abstract contract ComptrollerV5Storage is ComptrollerV4Storage {
    /// @notice The portion of VIX that each contributor receives per second
    mapping(address => uint) public rewardContributorSpeeds;

    /// @notice Last timestamp at which a contributor's VIX rewards have been allocated
    mapping(address => uint) public lastContributorTimestamp;
}

abstract contract ComptrollerV6Storage is ComptrollerV5Storage {
    /// @notice The rate at which VIX is distributed to the corresponding borrow market (per second)
    mapping(address => uint) public rewardBorrowSpeeds;

    /// @notice The rate at which VIX is distributed to the corresponding supply market (per second)
    mapping(address => uint) public rewardSupplySpeeds;
}

abstract contract ComptrollerV7Storage is ComptrollerV6Storage {
    /// @notice Accounting storage mapping account addresses to how much VIX they owe the protocol.
    mapping(address => uint) public rewardReceivable;

    IBoostManager public boostManager;
}

File 4 of 13 : IInterestRateModel.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

/**
  * @title 0VIX's IInterestRateModel Interface
  * @author 0VIX
  */
interface IInterestRateModel {
    /// @notice Indicator that this is an InterestRateModel contract (for inspection)
    function isInterestRateModel() external view returns(bool);

    /**
      * @notice Calculates the current borrow interest rate per timestmp
      * @param cash The total amount of cash the market has
      * @param borrows The total amount of borrows the market has outstanding
      * @param reserves The total amount of reserves the market has
      * @return The borrow rate per timestmp (as a percentage, and scaled by 1e18)
      */
    function getBorrowRate(uint cash, uint borrows, uint reserves) external view returns (uint);

    /**
      * @notice Calculates the current supply interest rate per timestmp
      * @param cash The total amount of cash the market has
      * @param borrows The total amount of borrows the market has outstanding
      * @param reserves The total amount of reserves the market has
      * @param reserveFactorMantissa The current reserve factor the market has
      * @return The supply rate per timestmp (as a percentage, and scaled by 1e18)
      */
    function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external view returns (uint);

}

File 5 of 13 : IComptroller.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

import "../otokens/interfaces/IOToken.sol";
import "../PriceOracle.sol";

interface IComptroller {
    /// @notice Indicator that this is a Comptroller contract (for inspection)
    function isComptroller() external view returns(bool);

    /*** Assets You Are In ***/

    function enterMarkets(address[] calldata oTokens) external returns (uint[] memory);
    function exitMarket(address oToken) external returns (uint);

    /*** Policy Hooks ***/

    function mintAllowed(address oToken, address minter, uint mintAmount) external returns (uint);
    function mintVerify(address oToken, address minter, uint mintAmount, uint mintTokens) external;

    function redeemAllowed(address oToken, address redeemer, uint redeemTokens) external returns (uint);
    function redeemVerify(address oToken, address redeemer, uint redeemAmount, uint redeemTokens) external;

    function borrowAllowed(address oToken, address borrower, uint borrowAmount) external returns (uint);
    function borrowVerify(address oToken, address borrower, uint borrowAmount) external;

    function repayBorrowAllowed(
        address oToken,
        address payer,
        address borrower,
        uint repayAmount) external returns (uint);

    function liquidateBorrowAllowed(
        address oTokenBorrowed,
        address oTokenCollateral,
        address liquidator,
        address borrower,
        uint repayAmount) external returns (uint);

    function seizeAllowed(
        address oTokenCollateral,
        address oTokenBorrowed,
        address liquidator,
        address borrower,
        uint seizeTokens) external returns (uint);
        
    function seizeVerify(
        address oTokenCollateral,
        address oTokenBorrowed,
        address liquidator,
        address borrower,
        uint seizeTokens) external;

    function transferAllowed(address oToken, address src, address dst, uint transferTokens) external returns (uint);
    function transferVerify(address oToken, address src, address dst, uint transferTokens) external;

    /*** Liquidity/Liquidation Calculations ***/

    function liquidateCalculateSeizeTokens(
        address oTokenBorrowed,
        address oTokenCollateral,
        uint repayAmount) external view returns (uint, uint);



    function isMarket(address market) external view returns(bool);
    function getBoostManager() external view returns(address);
    function getAllMarkets() external view returns(IOToken[] memory);
    function oracle() external view returns(PriceOracle);

    function updateAndDistributeSupplierRewardsForToken(
        address oToken,
        address account
    ) external;

    function updateAndDistributeBorrowerRewardsForToken(
        address oToken,
        address borrower
    ) external;

    function _setRewardSpeeds(
        address[] memory oTokens,
        uint256[] memory supplySpeeds,
        uint256[] memory borrowSpeeds
    ) external;
}

File 6 of 13 : ErrorReporter.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

contract ComptrollerErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        COMPTROLLER_MISMATCH,
        INSUFFICIENT_SHORTFALL,
        INSUFFICIENT_LIQUIDITY,
        INVALID_CLOSE_FACTOR,
        INVALID_COLLATERAL_FACTOR,
        INVALID_LIQUIDATION_INCENTIVE,
        MARKET_NOT_ENTERED, // no longer possible
        MARKET_NOT_LISTED,
        MARKET_ALREADY_LISTED,
        MATH_ERROR,
        NONZERO_BORROW_BALANCE,
        PRICE_ERROR,
        REJECTION,
        SNAPSHOT_ERROR,
        TOO_MANY_ASSETS,
        TOO_MUCH_REPAY
    }

    enum FailureInfo {
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
        EXIT_MARKET_BALANCE_OWED,
        EXIT_MARKET_REJECTION,
        SET_CLOSE_FACTOR_OWNER_CHECK,
        SET_CLOSE_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_OWNER_CHECK,
        SET_COLLATERAL_FACTOR_NO_EXISTS,
        SET_COLLATERAL_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
        SET_IMPLEMENTATION_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_VALIDATION,
        SET_MAX_ASSETS_OWNER_CHECK,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
        SET_PRICE_ORACLE_OWNER_CHECK,
        SUPPORT_MARKET_EXISTS,
        SUPPORT_MARKET_OWNER_CHECK,
        SET_PAUSE_GUARDIAN_OWNER_CHECK
    }

    /**
      * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
      * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
      **/
    event Failure(uint error, uint info, uint detail);

    /**
      * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
      */
    function fail(Error err, FailureInfo info) internal returns (uint) {
        emit Failure(uint(err), uint(info), 0);

        return uint(err);
    }

    /**
      * @dev use this when reporting an opaque error from an upgradeable collaborator contract
      */
    function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
        emit Failure(uint(err), uint(info), opaqueError);

        return uint(err);
    }
}

contract TokenErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        BAD_INPUT,
        COMPTROLLER_REJECTION,
        COMPTROLLER_CALCULATION_ERROR,
        INTEREST_RATE_MODEL_ERROR,
        INVALID_ACCOUNT_PAIR,
        INVALID_CLOSE_AMOUNT_REQUESTED,
        INVALID_COLLATERAL_FACTOR,
        MATH_ERROR,
        MARKET_NOT_FRESH,
        MARKET_NOT_LISTED,
        TOKEN_INSUFFICIENT_ALLOWANCE,
        TOKEN_INSUFFICIENT_BALANCE,
        TOKEN_INSUFFICIENT_CASH,
        TOKEN_TRANSFER_IN_FAILED,
        TOKEN_TRANSFER_OUT_FAILED
    }

    /*
     * Note: FailureInfo (but not Error) is kept in alphabetical order
     *       This is because FailureInfo grows significantly faster, and
     *       the order of Error has some meaning, while the order of FailureInfo
     *       is entirely arbitrary.
     */
    enum FailureInfo {
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED,
        ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED,
        ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED,
        ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
        BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        BORROW_ACCRUE_INTEREST_FAILED,
        BORROW_CASH_NOT_AVAILABLE,
        BORROW_FRESHNESS_CHECK,
        BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
        BORROW_MARKET_NOT_LISTED,
        BORROW_COMPTROLLER_REJECTION,
        LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
        LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
        LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
        LIQUIDATE_COMPTROLLER_REJECTION,
        LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED,
        LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX,
        LIQUIDATE_CLOSE_AMOUNT_IS_ZERO,
        LIQUIDATE_FRESHNESS_CHECK,
        LIQUIDATE_LIQUIDATOR_IS_BORROWER,
        LIQUIDATE_REPAY_BORROW_FRESH_FAILED,
        LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED,
        LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
        LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
        LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
        LIQUIDATE_SEIZE_TOO_MUCH,
        MINT_ACCRUE_INTEREST_FAILED,
        MINT_COMPTROLLER_REJECTION,
        MINT_EXCHANGE_CALCULATION_FAILED,
        MINT_EXCHANGE_RATE_READ_FAILED,
        MINT_FRESHNESS_CHECK,
        MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
        MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        MINT_TRANSFER_IN_FAILED,
        MINT_TRANSFER_IN_NOT_POSSIBLE,
        REDEEM_ACCRUE_INTEREST_FAILED,
        REDEEM_COMPTROLLER_REJECTION,
        REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED,
        REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED,
        REDEEM_EXCHANGE_RATE_READ_FAILED,
        REDEEM_FRESHNESS_CHECK,
        REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
        REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
        REDEEM_TRANSFER_OUT_NOT_POSSIBLE,
        REDUCE_RESERVES_ACCRUE_INTEREST_FAILED,
        REDUCE_RESERVES_ADMIN_CHECK,
        REDUCE_RESERVES_CASH_NOT_AVAILABLE,
        REDUCE_RESERVES_FRESH_CHECK,
        REDUCE_RESERVES_VALIDATION,
        REPAY_BEHALF_ACCRUE_INTEREST_FAILED,
        REPAY_BORROW_ACCRUE_INTEREST_FAILED,
        REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_COMPTROLLER_REJECTION,
        REPAY_BORROW_FRESHNESS_CHECK,
        REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
        REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
        SET_COLLATERAL_FACTOR_OWNER_CHECK,
        SET_COLLATERAL_FACTOR_VALIDATION,
        SET_COMPTROLLER_OWNER_CHECK,
        SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED,
        SET_INTEREST_RATE_MODEL_FRESH_CHECK,
        SET_INTEREST_RATE_MODEL_OWNER_CHECK,
        SET_MAX_ASSETS_OWNER_CHECK,
        SET_ORACLE_MARKET_NOT_LISTED,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED,
        SET_RESERVE_FACTOR_ADMIN_CHECK,
        SET_RESERVE_FACTOR_FRESH_CHECK,
        SET_RESERVE_FACTOR_BOUNDS_CHECK,
        TRANSFER_COMPTROLLER_REJECTION,
        TRANSFER_NOT_ALLOWED,
        TRANSFER_NOT_ENOUGH,
        TRANSFER_TOO_MUCH,
        ADD_RESERVES_ACCRUE_INTEREST_FAILED,
        ADD_RESERVES_FRESH_CHECK,
        ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE,
        SET_PROTOCOL_SEIZE_SHARE_ACCRUE_INTEREST_FAILED,
        SET_PROTOCOL_SEIZE_SHARE_OWNER_CHECK,
        SET_PROTOCOL_SEIZE_SHARE_FRESH_CHECK
    }

    /**
      * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
      * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
      **/
    event Failure(uint error, uint info, uint detail);

    /**
      * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
      */
    function fail(Error err, FailureInfo info) internal returns (uint) {
        emit Failure(uint(err), uint(info), 0);

        return uint(err);
    }

    /**
      * @dev use this when reporting an opaque error from an upgradeable collaborator contract
      */
    function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
        emit Failure(uint(err), uint(info), opaqueError);

        return uint(err);
    }
}

File 7 of 13 : ExponentialNoError.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

/**
 * @title Exponential module for storing fixed-precision decimals
 * @author 0VIX
 * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
 *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
 *         `Exp({mantissa: 5100000000000000000})`.
 */
contract ExponentialNoError {
    uint constant expScale = 1e18;
    uint constant doubleScale = 1e36;
    uint constant halfExpScale = expScale/2;
    uint constant mantissaOne = expScale;

    struct Exp {
        uint mantissa;
    }

    struct Double {
        uint mantissa;
    }

    /**
     * @dev Truncates the given exp to a whole number value.
     *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
     */
    function truncate(Exp memory exp) pure internal returns (uint) {
        // Note: We are not using careful math here as we're performing a division that cannot fail
        return exp.mantissa / expScale;
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) {
        return truncate(mul_(a, scalar));
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) {
        return truncate(mul_(a, scalar)) + addend;
    }

    /**
     * @dev Checks if first Exp is less than second Exp.
     */
    function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa < right.mantissa;
    }

    /**
     * @dev Checks if left Exp <= right Exp.
     */
    function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa <= right.mantissa;
    }

    /**
     * @dev Checks if left Exp > right Exp.
     */
    function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
        return left.mantissa > right.mantissa;
    }

    /**
     * @dev returns true if Exp is exactly zero
     */
    function isZeroExp(Exp memory value) pure internal returns (bool) {
        return value.mantissa == 0;
    }

    function safe224(uint n) pure internal returns (uint224) {
        require(n <= type(uint224).max, "safe224 overflow");
        return uint224(n);
    }

    function safe32(uint n) pure internal returns (uint32) {
        require(n <= type(uint32).max, "safe32 overflow");
        return uint32(n);
    }

    function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: a.mantissa + b.mantissa});
    }

    function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: a.mantissa + b.mantissa});
    }

    function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint c) {
        unchecked {
            require((c = a + b ) >= a, errorMessage);
        }
    }

    function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: a.mantissa - b.mantissa});
    }

    function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: a.mantissa - b.mantissa});
    }

    function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint c) {
        unchecked {
            require((c = a - b) <= a, errorMessage);
        }
    }

    function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: (a.mantissa * b.mantissa) / expScale});
    }

    function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
        return Exp({mantissa: a.mantissa * b});
    }

    function mul_(uint a, Exp memory b) pure internal returns (uint) {
        return (a * b.mantissa) / expScale;
    }

    function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: (a.mantissa * b.mantissa) / doubleScale});
    }

    function mul_(Double memory a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: a.mantissa * b});
    }

    function mul_(uint a, Double memory b) pure internal returns (uint) {
        return (a * b.mantissa) / doubleScale;
    }

    function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint c) {
        unchecked {
            require(a == 0 || (c = a * b) / a == b, errorMessage);
        }
    }

    function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
        return Exp({mantissa: (a.mantissa * expScale) / b.mantissa});
    }

    function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
        return Exp({mantissa: a.mantissa / b});
    }

    function div_(uint a, Exp memory b) pure internal returns (uint) {
        return (a * expScale) / b.mantissa;
    }

    function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
        return Double({mantissa: (a.mantissa * doubleScale) / b.mantissa});
    }

    function div_(Double memory a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: a.mantissa / b});
    }

    function div_(uint a, Double memory b) pure internal returns (uint) {
        return (a * doubleScale) / b.mantissa;
    }

    function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    function fraction(uint a, uint b) pure internal returns (Double memory) {
        return Double({mantissa: (a * doubleScale) / b});
    }
}

File 8 of 13 : IEIP20.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

/**
 * @title ERC 20 Token Standard Interface
 *  https://eips.ethereum.org/EIPS/eip-20
 */
interface IEIP20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    /**
      * @notice Get the total number of tokens in circulation
      * @return The supply of tokens
      */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return balance The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
      * @notice Transfer `amount` tokens from `msg.sender` to `dst`
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return success Whether or not the transfer succeeded
      */
    function transfer(address dst, uint256 amount) external returns (bool success);

    /**
      * @notice Transfer `amount` tokens from `src` to `dst`
      * @param src The address of the source account
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      * @return success Whether or not the transfer succeeded
      */
    function transferFrom(address src, address dst, uint256 amount) external returns (bool success);

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved (-1 means infinite)
      * @return success Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return remaining The number of tokens allowed to be spent (-1 means infinite)
      */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

File 9 of 13 : IEIP20NonStandard.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

/**
 * @title IEIP20NonStandard
 * @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
 *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */
interface IEIP20NonStandard {

    /**
     * @notice Get the total number of tokens in circulation
     * @return The supply of tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return balance The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
      * @notice Transfer `amount` tokens from `msg.sender` to `dst`
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      */
    function transfer(address dst, uint256 amount) external;

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
      * @notice Transfer `amount` tokens from `src` to `dst`
      * @param src The address of the source account
      * @param dst The address of the destination account
      * @param amount The number of tokens to transfer
      */
    function transferFrom(address src, address dst, uint256 amount) external;

    /**
      * @notice Approve `spender` to transfer up to `amount` from `src`
      * @dev This will overwrite the approval amount for `spender`
      *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
      * @param spender The address of the account which may transfer tokens
      * @param amount The number of tokens that are approved
      * @return success Whether or not the approval succeeded
      */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
      * @notice Get the current allowance from `owner` for `spender`
      * @param owner The address of the account which owns the tokens to be spent
      * @param spender The address of the account which may transfer tokens
      * @return remaining The number of tokens allowed to be spent
      */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

File 10 of 13 : IOToken.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

import "../../interfaces/IComptroller.sol";
import "../../interest-rate-models/interfaces/IInterestRateModel.sol";
import "./IEIP20NonStandard.sol";
import "./IEIP20.sol";

interface IOToken is IEIP20{
    /**
     * @notice Indicator that this is a OToken contract (for inspection)
     */
    function isOToken() external view returns(bool);


    /*** Market Events ***/

    /**
     * @notice Event emitted when interest is accrued
     */
    event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows);

    /**
     * @notice Event emitted when tokens are minted
     */
    event Mint(address minter, uint mintAmount, uint mintTokens);

    /**
     * @notice Event emitted when tokens are redeemed
     */
    event Redeem(address redeemer, uint redeemAmount, uint redeemTokens);

    /**
     * @notice Event emitted when underlying is borrowed
     */
    event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows);

    /**
     * @notice Event emitted when a borrow is repaid
     */
    event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows);

    /**
     * @notice Event emitted when a borrow is liquidated
     */
    event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address oTokenCollateral, uint seizeTokens);


    /*** Admin Events ***/

    /**
     * @notice Event emitted when pendingAdmin is changed
     */
    event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);

    /**
     * @notice Event emitted when pendingAdmin is accepted, which means admin is updated
     */
    event NewAdmin(address oldAdmin, address newAdmin);

    /**
     * @notice Event emitted when comptroller is changed
     */
    event NewComptroller(IComptroller oldComptroller, IComptroller newComptroller);

    /**
     * @notice Event emitted when interestRateModel is changed
     */
    event NewMarketInterestRateModel(IInterestRateModel oldInterestRateModel, IInterestRateModel newInterestRateModel);

    /**
     * @notice Event emitted when the reserve factor is changed
     */
    event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa);

    /**
     * @notice Event emitted when the protocol seize share is changed
     */
    event NewProtocolSeizeShare(uint oldProtocolSeizeShareMantissa, uint newProtocolSeizeShareMantissa);

    /**
     * @notice Event emitted when the reserves are added
     */
    event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves);

    /**
     * @notice Event emitted when the reserves are reduced
     */
    event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves);

    function accrualBlockTimestamp() external returns(uint256);

    /*** User Interface ***/
    function balanceOfUnderlying(address owner) external returns (uint);
    function getAccountSnapshot(address account) external view returns (uint, uint, uint, uint);
    function borrowRatePerTimestamp() external view returns (uint);
    function supplyRatePerTimestamp() external view returns (uint);
    function totalBorrowsCurrent() external returns (uint);
    function borrowBalanceCurrent(address account) external returns (uint);
    function borrowBalanceStored(address account) external view returns (uint);
    function exchangeRateCurrent() external returns (uint);
    function exchangeRateStored() external view returns (uint);
    function getCash() external view returns (uint);
    function accrueInterest() external returns (uint);
    function seize(address liquidator, address borrower, uint seizeTokens) external returns (uint);

    function totalBorrows() external view returns(uint);
    function comptroller() external view returns(IComptroller);
    function borrowIndex() external view returns(uint);
    function reserveFactorMantissa() external view returns(uint);


    /*** Admin Functions ***/

    function _setPendingAdmin(address payable newPendingAdmin) external returns (uint);
    function _acceptAdmin() external returns (uint);
    function _setComptroller(IComptroller newComptroller) external returns (uint);
    function _setReserveFactor(uint newReserveFactorMantissa) external returns (uint);
    function _reduceReserves(uint reduceAmount) external returns (uint);
    function _setInterestRateModel(IInterestRateModel newInterestRateModel) external returns (uint);
    function _setProtocolSeizeShare(uint newProtocolSeizeShareMantissa) external returns (uint);
}

File 11 of 13 : PriceOracle.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

import "./otokens/interfaces/IOToken.sol";

abstract contract PriceOracle {
    /// @notice Indicator that this is a PriceOracle contract (for inspection)
    bool public constant isPriceOracle = true;

    /**
      * @notice Get the underlying price of a oToken asset
      * @param oToken The oToken to get the underlying price of
      * @return The underlying asset price mantissa (scaled by 1e18).
      *  Zero means the price is unavailable.
      */
    function getUnderlyingPrice(IOToken oToken) external virtual view returns (uint);
}

File 12 of 13 : UnitrollerAdminStorage.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

abstract contract UnitrollerAdminStorage {
    /**
    * @notice Administrator for this contract
    */
    address public admin;

    /**
    * @notice Pending administrator for this contract
    */
    address public pendingAdmin;

    /**
    * @notice Active brains of Unitroller
    */
    address public comptrollerImplementation;

    /**
    * @notice Pending brains of Unitroller
    */
    address public pendingComptrollerImplementation;
}

File 13 of 13 : IBoostManager.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

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

interface IBoostManager {
    function updateBoostBasis(address user)
        external
        returns (bool);

    function updateBoostSupplyBalances(
        address market,
        address user,
        uint256 oldBalance,
        uint256 newBalance
    ) external;

    function updateBoostBorrowBalances(
        address market,
        address user,
        uint256 oldBalance,
        uint256 newBalance
    ) external;

    function boostedSupplyBalanceOf(address market, address user)
        external
        view
        returns (uint256);

    function boostedBorrowBalanceOf(address market, address user)
        external
        view
        returns (uint256);

    function boostedTotalSupply(address market) external view returns (uint256);

    function boostedTotalBorrows(address market)
        external
        view
        returns (uint256);

    function setAuthorized(address addr, bool flag) external;

    function setVeOVIX(IERC20 ve) external;

    function isAuthorized(address addr) external view returns (bool);
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"string","name":"action","type":"string"},{"indexed":false,"internalType":"bool","name":"pauseState","type":"bool"}],"name":"ActionPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"action","type":"string"},{"indexed":false,"internalType":"bool","name":"pauseState","type":"bool"}],"name":"ActionPausedGlobally","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"boostmanager","type":"address"}],"name":"BoostManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSpeed","type":"uint256"}],"name":"ContributorRewardSpeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenBorrowIndex","type":"uint256"}],"name":"DistributedBorrowerReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":true,"internalType":"address","name":"supplier","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenDelta","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenSupplyIndex","type":"uint256"}],"name":"DistributedSupplierReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"error","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"info","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"detail","type":"uint256"}],"name":"Failure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isAutoCollateralized","type":"bool"}],"name":"MarketAutoCollateralized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MarketEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MarketExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOToken","name":"oToken","type":"address"}],"name":"MarketListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newBorrowCap","type":"uint256"}],"name":"NewBorrowCap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldBorrowCapGuardian","type":"address"},{"indexed":false,"internalType":"address","name":"newBorrowCapGuardian","type":"address"}],"name":"NewBorrowCapGuardian","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldCloseFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCloseFactorMantissa","type":"uint256"}],"name":"NewCloseFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldCollateralFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCollateralFactorMantissa","type":"uint256"}],"name":"NewCollateralFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLiquidationIncentiveMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLiquidationIncentiveMantissa","type":"uint256"}],"name":"NewLiquidationIncentive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPauseGuardian","type":"address"},{"indexed":false,"internalType":"address","name":"newPauseGuardian","type":"address"}],"name":"NewPauseGuardian","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract PriceOracle","name":"oldPriceOracle","type":"address"},{"indexed":false,"internalType":"contract PriceOracle","name":"newPriceOracle","type":"address"}],"name":"NewPriceOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSpeed","type":"uint256"}],"name":"RewardBorrowSpeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IOToken","name":"oToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"newSpeed","type":"uint256"}],"name":"RewardSupplySpeedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_rewardUpdater","type":"address"}],"name":"RewardUpdaterModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vix","type":"address"}],"name":"VixAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VixClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VixGranted","type":"event"},{"inputs":[{"internalType":"contract IUnitroller","name":"unitroller","type":"address"}],"name":"_become","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_borrowGuardianPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"_grantReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_mintGuardianPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newBorrowCapGuardian","type":"address"}],"name":"_setBorrowCapGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOToken","name":"oToken","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"_setBorrowPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCloseFactorMantissa","type":"uint256"}],"name":"_setCloseFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOToken","name":"oToken","type":"address"},{"internalType":"uint256","name":"newCollateralFactorMantissa","type":"uint256"}],"name":"_setCollateralFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"},{"internalType":"uint256","name":"rewardSpeed","type":"uint256"}],"name":"_setContributorRewardSpeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newLiquidationIncentiveMantissa","type":"uint256"}],"name":"_setLiquidationIncentive","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOToken[]","name":"oTokens","type":"address[]"},{"internalType":"uint256[]","name":"newBorrowCaps","type":"uint256[]"}],"name":"_setMarketBorrowCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOToken","name":"oToken","type":"address"},{"internalType":"bool","name":"state","type":"bool"}],"name":"_setMintPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPauseGuardian","type":"address"}],"name":"_setPauseGuardian","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PriceOracle","name":"newOracle","type":"address"}],"name":"_setPriceOracle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"oTokens","type":"address[]"},{"internalType":"uint256[]","name":"supplySpeeds","type":"uint256[]"},{"internalType":"uint256[]","name":"borrowSpeeds","type":"uint256[]"}],"name":"_setRewardSpeeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"_setSeizePaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"_setTransferPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOToken","name":"oToken","type":"address"},{"internalType":"bool","name":"_autoCollaterize","type":"bool"}],"name":"_supportMarket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"accountAssets","outputs":[{"internalType":"contract IOToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"accountMembership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allMarkets","outputs":[{"internalType":"contract IOToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostManager","outputs":[{"internalType":"contract IBoostManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrowAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowCapGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowCaps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrowVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IOToken","name":"oToken","type":"address"}],"name":"checkMembership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"claimReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"},{"internalType":"contract IOToken[]","name":"oTokens","type":"address[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"holders","type":"address[]"},{"internalType":"contract IOToken[]","name":"oTokens","type":"address[]"},{"internalType":"bool","name":"borrowers","type":"bool"},{"internalType":"bool","name":"suppliers","type":"bool"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptrollerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"oTokens","type":"address[]"}],"name":"enterMarkets","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenAddress","type":"address"}],"name":"exitMarket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllMarkets","outputs":[{"internalType":"contract IOToken[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAssetsIn","outputs":[{"internalType":"contract IOToken[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBoostManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"oTokenModify","type":"address"},{"internalType":"uint256","name":"redeemTokens","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"getHypotheticalAccountLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVixAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"guardianPaused","outputs":[{"internalType":"bool","name":"mint","type":"bool"},{"internalType":"bool","name":"borrow","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isComptroller","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IOToken","name":"oToken","type":"address"}],"name":"isDeprecated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"}],"name":"isMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastContributorTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenBorrowed","type":"address"},{"internalType":"address","name":"oTokenCollateral","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"liquidateBorrowAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenBorrowed","type":"address"},{"internalType":"address","name":"oTokenCollateral","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"actualRepayAmount","type":"uint256"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"liquidateBorrowVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenBorrowed","type":"address"},{"internalType":"address","name":"oTokenCollateral","type":"address"},{"internalType":"uint256","name":"actualRepayAmount","type":"uint256"}],"name":"liquidateCalculateSeizeTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationIncentiveMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketInitialIndex","outputs":[{"internalType":"uint224","name":"","type":"uint224"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"bool","name":"isListed","type":"bool"},{"internalType":"bool","name":"autoCollaterize","type":"bool"},{"internalType":"uint256","name":"collateralFactorMantissa","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mintAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"actualMintAmount","type":"uint256"},{"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"mintVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract PriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingComptrollerImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"redeemer","type":"address"},{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeemAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"redeemer","type":"address"},{"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeemVerify","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"payer","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"payer","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"actualRepayAmount","type":"uint256"},{"internalType":"uint256","name":"borrowerIndex","type":"uint256"}],"name":"repayBorrowVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardBorrowSpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewardBorrowerIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardContributorSpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardReceivable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardSpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewardSupplierIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardSupplySpeeds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardUpdater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenCollateral","type":"address"},{"internalType":"address","name":"oTokenBorrowed","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seizeAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"seizeGuardianPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oTokenCollateral","type":"address"},{"internalType":"address","name":"oTokenBorrowed","type":"address"},{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seizeVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"bool","name":"flag","type":"bool"}],"name":"setAutoCollaterize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newBoostManager","type":"address"}],"name":"setBoostManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardUpdater","type":"address"}],"name":"setRewardUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newVixAddress","type":"address"}],"name":"setVixAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyState","outputs":[{"internalType":"uint224","name":"index","type":"uint224"},{"internalType":"uint32","name":"timestamp","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"transferTokens","type":"uint256"}],"name":"transferAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferGuardianPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"transferTokens","type":"uint256"}],"name":"transferVerify","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"borrower","type":"address"}],"name":"updateAndDistributeBorrowerRewardsForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oToken","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"updateAndDistributeSupplierRewardsForToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"updateContributorRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vixAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561001057600080fd5b50600080546001600160a01b03191633179055615d1180620000336000396000f3fe6080604052600436106104035760003560e01c80636a56947e116102115780636a56947e14610ab25780636b1e85c514610acd5780636d35bf9114610aed5780636ec934da14610b085780637388dcff14610b41578063741b252514610b565780637dc0d1d014610b7657806385e7069414610b9657806387f7630314610bd05780638b3f9c3b14610bf15780638e8f294b14610c115780638ebf636414610c6f5780638fc67c2614610c8f578063929fe9a114610caf57806394543c1514610cf957806394b2294b14610d195780639ace3ada14610d2f578063a7b1986c14610d6a578063a8a5564a14610d8a578063aa5b3e7f14610dc2578063aa90075414610de2578063abfceffc14610df8578063ac0b0bb714610e25578063b0772d0b14610e46578063bb82aa5e14610e5b578063bdcdc25814610e7b578063c299823814610e9b578063c47a852614610ec8578063c488847b14610f0d578063ca7d5a1514610f3b578063d02f735114610f5b578063d03a27c514610f7b578063d279c19114610f9b578063da3d454c14610fbb578063daacd11814610fdb578063dce1544914611008578063dcfbc0c714611028578063e4028eee14611048578063e6653f3d14611068578063e875544614611089578063eabe7d911461109f578063ede4edd0146110bf578063ee0905cd146110df578063f851a440146110ff578063ff278d2d1461111f57600080fd5b80627e3c461461040f5780627e3dd21461044f5780630346ef2a146104745780630dd1cc881461049657806312e1e8c4146104c3578063188ec356146104e357806318c882a5146104f65780631d504dc6146105165780631ededc91146105365780632026ffa31461055857806321af456914610578578063239a4237146105a557806324008a62146105c557806324a3d622146105e5578063267822471461060557806328bd3c85146106255780632c1d6c56146106525780632d70db781461067f578063317b0b771461069f57806336301f1f146106bf578063391957d7146106f75780633b593a45146107175780633bcf7ec1146107445780633c94786f146107645780633cb61d4d1461078557806341c728b9146107a5578063447a1366146107c657806347ef3b3b146108165780634a584432146108395780634aacbabc146108665780634ada90af146108845780634e79238f1461089a5780634ef4c3e1146108c95780634fd42e17146108e957806351dff9891461090957806352d84d1e1461092957806354eb76fa1461094957806355ee1fe1146109695780635c778605146109895780635ec88c79146109a95780635f5af1aa146109c95780635fc7e71e146109e9578063607ef6c114610a09578063670e8afd14610a295780636760c07514610a4957600080fd5b3661040a57005b600080fd5b34801561041b57600080fd5b5061043c61042a3660046153b4565b60146020526000908152604090205481565b6040519081526020015b60405180910390f35b34801561045b57600080fd5b50610464600181565b6040519015158152602001610446565b34801561048057600080fd5b5061049461048f3660046156af565b61114c565b005b3480156104a257600080fd5b5061043c6104b13660046153b4565b60186020526000908152604090205481565b3480156104cf57600080fd5b506104946104de3660046153ec565b61123c565b3480156104ef57600080fd5b504261043c565b34801561050257600080fd5b50610464610511366004615670565b6112d3565b34801561052257600080fd5b506104946105313660046153b4565b6113c6565b34801561054257600080fd5b50610494610551366004615544565b5050505050565b34801561056457600080fd5b50610494610573366004615623565b61155c565b34801561058457600080fd5b50601554610598906001600160a01b031681565b6040516104469190615919565b3480156105b157600080fd5b506104946105c03660046153b4565b6115cd565b3480156105d157600080fd5b5061043c6105e03660046154f4565b611689565b3480156105f157600080fd5b50600b54610598906001600160a01b031681565b34801561061157600080fd5b50600154610598906001600160a01b031681565b34801561063157600080fd5b5061043c6106403660046153b4565b601b6020526000908152604090205481565b34801561065e57600080fd5b5061043c61066d3660046153b4565b60196020526000908152604090205481565b34801561068b57600080fd5b5061046461069a36600461587c565b6116c7565b3480156106ab57600080fd5b5061043c6106ba3660046158b4565b611768565b3480156106cb57600080fd5b5061043c6106da3660046153ec565b601260209081526000928352604080842090915290825290205481565b34801561070357600080fd5b506104946107123660046153b4565b6117cb565b34801561072357600080fd5b5061043c6107323660046153b4565b601a6020526000908152604090205481565b34801561075057600080fd5b5061046461075f366004615670565b611841565b34801561077057600080fd5b50600b5461046490600160a01b900460ff1681565b34801561079157600080fd5b50601e54610598906001600160a01b031681565b3480156107b157600080fd5b506104946107c03660046155de565b50505050565b3480156107d257600080fd5b506107ff6107e13660046153b4565b600c6020526000908152604090205460ff8082169161010090041682565b604080519215158352901515602083015201610446565b34801561082257600080fd5b50610494610831366004615487565b505050505050565b34801561084557600080fd5b5061043c6108543660046153b4565b60166020526000908152604090205481565b34801561087257600080fd5b50601c546001600160a01b0316610598565b34801561089057600080fd5b5061043c60065481565b3480156108a657600080fd5b506108ba6108b53660046155de565b61191d565b60405161044693929190615b1a565b3480156108d557600080fd5b5061043c6108e436600461559e565b61196b565b3480156108f557600080fd5b5061043c6109043660046158b4565b611abd565b34801561091557600080fd5b506104946109243660046155de565b611b16565b34801561093557600080fd5b506105986109443660046158b4565b611b62565b34801561095557600080fd5b5061049461096436600461570c565b611b8c565b34801561097557600080fd5b5061043c6109843660046153b4565b611e47565b34801561099557600080fd5b506104946109a436600461559e565b505050565b3480156109b557600080fd5b506108ba6109c43660046153b4565b611eb9565b3480156109d557600080fd5b5061043c6109e43660046153b4565b611f02565b3480156109f557600080fd5b5061043c610a04366004615424565b611f73565b348015610a1557600080fd5b50610494610a24366004615814565b612174565b348015610a3557600080fd5b50610494610a443660046153ec565b61236c565b348015610a5557600080fd5b50610a8e610a643660046153b4565b6010602052600090815260409020546001600160e01b03811690600160e01b900463ffffffff1682565b604080516001600160e01b03909316835263ffffffff909116602083015201610446565b348015610abe57600080fd5b506104946107c03660046154f4565b348015610ad957600080fd5b50610494610ae83660046153b4565b612383565b348015610af957600080fd5b50610494610551366004615424565b348015610b1457600080fd5b50610464610b233660046153b4565b6001600160a01b03166000908152600a602052604090205460ff1690565b348015610b4d57600080fd5b50610598612434565b348015610b6257600080fd5b50610494610b713660046153b4565b612443565b348015610b8257600080fd5b50600454610598906001600160a01b031681565b348015610ba257600080fd5b50610bb86a0c097ce7bc90715b34b9f160241b81565b6040516001600160e01b039091168152602001610446565b348015610bdc57600080fd5b50600b5461046490600160b01b900460ff1681565b348015610bfd57600080fd5b50610494610c0c366004615791565b6124ef565b348015610c1d57600080fd5b50610c52610c2c3660046153b4565b600a602052600090815260409020805460019091015460ff808316926101009004169083565b604080519315158452911515602084015290820152606001610446565b348015610c7b57600080fd5b50610464610c8a36600461587c565b61263b565b348015610c9b57600080fd5b50610494610caa3660046153b4565b6126d3565b348015610cbb57600080fd5b50610464610cca36600461569d565b6001600160a01b0380821660009081526009602090815260408083209386168352929052205460ff1692915050565b348015610d0557600080fd5b50610464610d143660046153b4565b61275b565b348015610d2557600080fd5b5061043c60075481565b348015610d3b57600080fd5b50610464610d4a3660046153ec565b600960209081526000928352604080842090915290825290205460ff1681565b348015610d7657600080fd5b50610494610d85366004615670565b612829565b348015610d9657600080fd5b5061043c610da53660046153ec565b601360209081526000928352604080842090915290825290205481565b348015610dce57600080fd5b50601d54610598906001600160a01b031681565b348015610dee57600080fd5b5061043c600e5481565b348015610e0457600080fd5b50610e18610e133660046153b4565b61288d565b6040516104469190615960565b348015610e3157600080fd5b50600b5461046490600160b81b900460ff1681565b348015610e5257600080fd5b50610e18612903565b348015610e6757600080fd5b50600254610598906001600160a01b031681565b348015610e8757600080fd5b5061043c610e963660046154f4565b612965565b348015610ea757600080fd5b50610ebb610eb63660046156da565b6129fc565b60405161044691906159ad565b348015610ed457600080fd5b50610a8e610ee33660046153b4565b6011602052600090815260409020546001600160e01b03811690600160e01b900463ffffffff1682565b348015610f1957600080fd5b50610f2d610f2836600461559e565b612af5565b604051610446929190615b0c565b348015610f4757600080fd5b5061043c610f56366004615670565b612d1c565b348015610f6757600080fd5b5061043c610f76366004615424565b612f8c565b348015610f8757600080fd5b50601c54610598906001600160a01b031681565b348015610fa757600080fd5b5061043c610fb63660046153b4565b61313b565b348015610fc757600080fd5b5061043c610fd636600461559e565b61321f565b348015610fe757600080fd5b5061043c610ff63660046153b4565b60176020526000908152604090205481565b34801561101457600080fd5b506105986110233660046156af565b6135bb565b34801561103457600080fd5b50600354610598906001600160a01b031681565b34801561105457600080fd5b5061043c6110633660046156af565b6135f3565b34801561107457600080fd5b50600b5461046490600160a81b900460ff1681565b34801561109557600080fd5b5061043c60055481565b3480156110ab57600080fd5b5061043c6110ba36600461559e565b61378d565b3480156110cb57600080fd5b5061043c6110da3660046153b4565b6137bf565b3480156110eb57600080fd5b506104946110fa3660046156af565b613b6c565b34801561110b57600080fd5b50600054610598906001600160a01b031681565b34801561112b57600080fd5b5061043c61113a3660046153b4565b600f6020526000908152604090205481565b611154613c4b565b6111a35760405162461bcd60e51b815260206004820152601b60248201527a1bdb9b1e4818591b5a5b8818d85b8819dc985b9d081c995dd85c99602a1b60448201526064015b60405180910390fd5b60006111af8383613c74565b905080156111fe5760405162461bcd60e51b815260206004820152601c60248201527b1a5b9cdd59999a58da595b9d081d1bdad95b88199bdc8819dc985b9d60221b604482015260640161119a565b7f16be5776def5b9fc98f7848c5fe8811ac9e187f5d864466387d38c58c399ea5d838360405161122f929190615947565b60405180910390a1505050565b60006040518060200160405280846001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561128257600080fd5b505afa158015611296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ba91906158cc565b905290506112c88382613dd4565b6109a4838383613ff2565b6001600160a01b0382166000908152600a602052604081205460ff1661130b5760405162461bcd60e51b815260040161119a90615a9e565b61131361422f565b6000546001600160a01b03163314806113295750815b6113455760405162461bcd60e51b815260040161119a90615a3f565b6001600160a01b0383166000818152600c6020908152604091829020805461ff001916610100871515908102919091179091558251938452606091840182905260069184019190915265426f72726f7760d01b608084015290820152600080516020615cbc8339815191529060a0015b60405180910390a150805b92915050565b806001600160a01b031663f851a4406040518163ffffffff1660e01b815260040160206040518083038186803b1580156113ff57600080fd5b505afa158015611413573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143791906153d0565b6001600160a01b0316336001600160a01b0316146114a15760405162461bcd60e51b815260206004820152602160248201527f6f6e6c7920756e6974726f6c6c65722061646d696e2063616e205f6265636f6d6044820152606560f81b606482015260840161119a565b806001600160a01b031663c1e803346040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156114dc57600080fd5b505af11580156114f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151491906158cc565b156115595760405162461bcd60e51b815260206004820152601560248201527418da185b99d9481b9bdd08185d5d1a1bdc9a5e9959605a1b604482015260640161119a565b50565b6040805160018082528183019092526000916020808301908036833701905050905082816000815181106115a057634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250506109a48183600180611b8c565b6000546001600160a01b031633146115e457600080fd5b6001600160a01b03811661160a5760405162461bcd60e51b815260040161119a906159e5565b601c546001600160a01b0316156116335760405162461bcd60e51b815260040161119a90615a16565b601c80546001600160a01b0319166001600160a01b0383161790556040517fd879a30bb65908f7b6db545675d2415361dc65a59feb404d5f1282ec615c91d89061167e908390615919565b60405180910390a150565b6001600160a01b0384166000908152600a602052604081205460ff166116b1575060096116bf565b6116bb858461123c565b5060005b949350505050565b60006116d161422f565b6000546001600160a01b03163314806116e75750815b6117035760405162461bcd60e51b815260040161119a90615a3f565b600b8054831515600160b81b0260ff60b81b19909116179055604051600080516020615c7c8339815191529061175c9084906040808252600590820152645365697a6560d81b6060820152901515602082015260800190565b60405180910390a15090565b600080546001600160a01b0316331461178057600080fd5b60058054908390556040517f3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9906117ba9083908690615b0c565b60405180910390a160009392505050565b6000546001600160a01b031633146117e257600080fd5b601580546001600160a01b038381166001600160a01b03198316179092556040519116907feda98690e518e9a05f8ec6837663e188211b2da8f4906648b323f2c1d4434e2990611835908390859061592d565b60405180910390a15050565b6001600160a01b0382166000908152600a602052604081205460ff166118795760405162461bcd60e51b815260040161119a90615a9e565b61188161422f565b6000546001600160a01b03163314806118975750815b6118b35760405162461bcd60e51b815260040161119a90615a3f565b6001600160a01b0383166000818152600c6020908152604091829020805460ff19168615159081179091558251938452606091840182905260049184019190915263135a5b9d60e21b608084015290820152600080516020615cbc8339815191529060a0016113b5565b6000806000806000806119328a8a8a8a6142a0565b92509250925082601181111561195857634e487b7160e01b600052602160045260246000fd5b95509093509150505b9450945094915050565b6001600160a01b0383166000908152600c602052604081205460ff16156119c55760405162461bcd60e51b815260206004820152600e60248201526d1b5a5b9d081a5cc81c185d5cd95960921b604482015260640161119a565b6001600160a01b0384166000908152600a602052604090205460ff166119ef5760095b9050611ab6565b6040516370a0823160e01b81526001600160a01b038516906370a0823190611a1b908690600401615919565b60206040518083038186803b158015611a3357600080fd5b505afa158015611a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a6b91906158cc565b158015611a9557506001600160a01b0384166000908152600a6020526040902054610100900460ff165b15611aa657611aa484846145e3565b505b611ab0848461236c565b60005b90505b9392505050565b600080546001600160a01b03163314611adc576113c06001600b6146db565b60068054908390556040517faeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316906117ba9083908690615b0c565b80151580611b22575081155b6107c05760405162461bcd60e51b815260206004820152601160248201527072656465656d546f6b656e73207a65726f60781b604482015260640161119a565b600d8181548110611b7257600080fd5b6000918252602090912001546001600160a01b0316905081565b60005b8351811015611d5a576000848281518110611bba57634e487b7160e01b600052603260045260246000fd5b6020908102919091018101516001600160a01b0381166000908152600a90925260409091205490915060ff16611c025760405162461bcd60e51b815260040161119a90615a6f565b8315611ce75760006040518060200160405280836001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611c4e57600080fd5b505afa158015611c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8691906158cc565b90529050611c948282613dd4565b60005b8751811015611ce457611cd283898381518110611cc457634e487b7160e01b600052603260045260246000fd5b602002602001015184613ff2565b80611cdc81615bf1565b915050611c97565b50505b8215611d4757611cf681614765565b60005b8651811015611d4557611d3382888381518110611d2657634e487b7160e01b600052603260045260246000fd5b6020026020010151614972565b80611d3d81615bf1565b915050611cf9565b505b5080611d5281615bf1565b915050611b8f565b5060005b845181101561055157611de4858281518110611d8a57634e487b7160e01b600052603260045260246000fd5b602002602001015160146000888581518110611db657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054613c74565b60146000878481518110611e0857634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055508080611e3f90615bf1565b915050611d5e565b600080546001600160a01b03163314611e66576113c0600160106146db565b600480546001600160a01b038481166001600160a01b03198316179092556040519116907fd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22906117ba908390869061592d565b600080600080600080611ed08760008060006142a0565b925092509250826011811115611ef657634e487b7160e01b600052602160045260246000fd5b97919650945092505050565b600080546001600160a01b03163314611f21576113c0600160136146db565b600b80546001600160a01b038481166001600160a01b0319831681179093556040519116917f0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e916117ba91849161592d565b6001600160a01b0385166000908152600a602052604081205460ff161580611fb457506001600160a01b0385166000908152600a602052604090205460ff16155b15611fc35760095b905061216b565b6040516395dd919360e01b81526000906001600160a01b038816906395dd919390611ff2908790600401615919565b60206040518083038186803b15801561200a57600080fd5b505afa15801561201e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204291906158cc565b905061204d8761275b565b156120b857828110156120b35760405162461bcd60e51b815260206004820152602860248201527f43616e206e6f74207265706179206d6f7265207468616e2074686520746f74616044820152676c20626f72726f7760c01b606482015260840161119a565b612165565b6000806120c98660008060006142a0565b919350909150600090508260118111156120f357634e487b7160e01b600052602160045260246000fd5b146121225781601181111561211857634e487b7160e01b600052602160045260246000fd5b935050505061216b565b8061212e576003612118565b600061214a604051806020016040528060055481525085614ba4565b90508086111561216157601194505050505061216b565b5050505b60009150505b95945050505050565b6000546001600160a01b031633148061219757506015546001600160a01b031633145b6121e35760405162461bcd60e51b815260206004820152601f60248201527f6f6e6c792061646d696e206f7220626f72726f77436170477561726469616e00604482015260640161119a565b828181158015906121f357508082145b61222f5760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081a5b9c1d5d609a1b604482015260640161119a565b60005b828110156123635784848281811061225a57634e487b7160e01b600052603260045260246000fd5b905060200201356016600089898581811061228557634e487b7160e01b600052603260045260246000fd5b905060200201602081019061229a91906153b4565b6001600160a01b031681526020810191909152604001600020558686828181106122d457634e487b7160e01b600052603260045260246000fd5b90506020020160208101906122e991906153b4565b6001600160a01b03167f6f1951b2aad10f3fc81b86d91105b413a5b3f847a34bbc5ce1904201b14438f686868481811061233357634e487b7160e01b600052603260045260246000fd5b9050602002013560405161234991815260200190565b60405180910390a28061235b81615bf1565b915050612232565b50505050505050565b61237582614765565b61237f8282614972565b5050565b6000546001600160a01b0316331461239a57600080fd5b6001600160a01b0381166123c05760405162461bcd60e51b815260040161119a906159e5565b601d546001600160a01b0316156123e95760405162461bcd60e51b815260040161119a90615a16565b601d80546001600160a01b0319166001600160a01b0383161790556040517f0bd1fe217455fa5501f840301b8930a48a691061ad7c3864ac856f2e9adcdcc49061167e908390615919565b601d546001600160a01b031690565b6001600160a01b0381166000908152601760209081526040808320546018909252822054909142916124759083615bda565b90506000811180156124875750600083115b156107c05760006124988483615bbb565b6001600160a01b038616600090815260146020526040812054919250906124c0908390615b83565b6001600160a01b0387166000908152601460209081526040808320939093556018905220849055505050505050565b6000546001600160a01b03163314806125125750601e546001600160a01b031633145b61252e5760405162461bcd60e51b815260040161119a90615ad5565b82518251811480156125405750815181145b6125a05760405162461bcd60e51b815260206004820152602b60248201527f436f6d7074726f6c6c65723a3a5f73657452657761726453706565647320696e60448201526a1d985b1a59081a5b9c1d5d60aa1b606482015260840161119a565b60005b818110156105515761262b8582815181106125ce57634e487b7160e01b600052603260045260246000fd5b60200260200101518583815181106125f657634e487b7160e01b600052603260045260246000fd5b602002602001015185848151811061261e57634e487b7160e01b600052603260045260246000fd5b6020026020010151614bb8565b61263481615bf1565b90506125a3565b600061264561422f565b6000546001600160a01b031633148061265b5750815b6126775760405162461bcd60e51b815260040161119a90615a3f565b600b8054831515600160b01b0260ff60b01b19909116179055604051600080516020615c7c8339815191529061175c9084906040808252600890820152672a3930b739b332b960c11b6060820152901515602082015260800190565b6000546001600160a01b031633146126ea57600080fd5b6001600160a01b0381166127105760405162461bcd60e51b815260040161119a906159e5565b601e80546001600160a01b0319166001600160a01b0383161790556040517fbea39469363910457d9c890c8148cf384c00faf61147f6e40a39ddafa26fc2099061167e908390615919565b6001600160a01b0381166000908152600a60205260408120600101541580156127a157506001600160a01b0382166000908152600c6020526040902054610100900460ff165b80156113c05750816001600160a01b031663173b99046040518163ffffffff1660e01b815260040160206040518083038186803b1580156127e157600080fd5b505afa1580156127f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061281991906158cc565b670de0b6b3a76400001492915050565b6000546001600160a01b0316331461284057600080fd5b6001600160a01b0382166000908152600a60205260409081902080548315156101000261ff001990911617905551600080516020615c9c8339815191529061183590831515815260200190565b6001600160a01b0381166000908152600860209081526040918290208054835181840281018401909452808452606093928301828280156128f757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116128d9575b50505050509050919050565b6060600d80548060200260200160405190810160405280929190818152602001828054801561295b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161293d575b5050505050905090565b600b54600090600160b01b900460ff16156129b75760405162461bcd60e51b81526020600482015260126024820152711d1c985b9cd9995c881a5cc81c185d5cd95960721b604482015260640161119a565b60006129c4868685614da0565b905080156129d35790506116bf565b6129dc86614765565b6129e68686614972565b6129f08685614972565b60009695505050505050565b80516060906000816001600160401b03811115612a2957634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612a52578160200160208202803683370190505b50905060005b82811015612aed57612a91858281518110612a8357634e487b7160e01b600052603260045260246000fd5b6020026020010151336145e3565b6011811115612ab057634e487b7160e01b600052602160045260246000fd5b828281518110612ad057634e487b7160e01b600052603260045260246000fd5b602090810291909101015280612ae581615bf1565b915050612a58565b509392505050565b6004805460405163fc57d4df60e01b8152600092839283926001600160a01b039091169163fc57d4df91612b2b918a9101615919565b60206040518083038186803b158015612b4357600080fd5b505afa158015612b57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7b91906158cc565b6004805460405163fc57d4df60e01b81529293506000926001600160a01b039091169163fc57d4df91612bb0918a9101615919565b60206040518083038186803b158015612bc857600080fd5b505afa158015612bdc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0091906158cc565b9050811580612c0d575080155b15612c2157600d6000935093505050612d14565b6000866001600160a01b031663182df0f56040518163ffffffff1660e01b815260040160206040518083038186803b158015612c5c57600080fd5b505afa158015612c70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9491906158cc565b90506000612cc06040518060200160405280600654815250604051806020016040528087815250614e70565b90506000612cea604051806020016040528086815250604051806020016040528086815250614e70565b90506000612cf88383614eb3565b90506000612d06828b614ba4565b600099509750505050505050505b935093915050565b600080546001600160a01b03163314612d4257612d3b600160126146db565b90506113c0565b6001600160a01b0383166000908152600a602052604090205460ff1615612d6f57612d3b600a60116146db565b826001600160a01b03166397de9d116040518163ffffffff1660e01b815260040160206040518083038186803b158015612da857600080fd5b505afa158015612dbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de09190615898565b50604080516060810182526001808252841515602080840182815260008587018181526001600160a01b038b168252600a84529087902095518654925161ffff1990931690151561ff00191617610100921515929092029190911785555193909201929092559151908152600080516020615c9c833981519152910160405180910390a160005b600d54811015612f0857836001600160a01b0316600d8281548110612e9c57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03161415612ef65760405162461bcd60e51b81526020600482015260146024820152731b585c9ad95d08185b1c9958591e48185919195960621b604482015260640161119a565b80612f0081615bf1565b915050612e67565b50600d80546001810182556000919091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b0319166001600160a01b038516179055612f5d83614ee3565b7fcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f836040516117ba9190615919565b600b54600090600160b81b900460ff1615612fdb5760405162461bcd60e51b815260206004820152600f60248201526e1cd95a5e99481a5cc81c185d5cd959608a1b604482015260640161119a565b6001600160a01b0386166000908152600a602052604090205460ff16158061301c57506001600160a01b0385166000908152600a602052604090205460ff16155b15613028576009611fbc565b846001600160a01b0316635fe3b5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561306157600080fd5b505afa158015613075573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309991906153d0565b6001600160a01b0316866001600160a01b0316635fe3b5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156130db57600080fd5b505afa1580156130ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311391906153d0565b6001600160a01b031614613128576002611fbc565b61313186614765565b6129e68684614972565b6000805b600d548110156131da576000600d828154811061316c57634e487b7160e01b600052603260045260246000fd5b60009182526020808320909101546001600160a01b0316808352600a90915260409091205490915060ff166131b35760405162461bcd60e51b815260040161119a90615a6f565b6131bd818561123c565b6131c7818561236c565b50806131d281615bf1565b91505061313f565b506001600160a01b0382166000908152601460205260409020546131fe8382613c74565b6001600160a01b039093166000908152601460205260409020929092555090565b6001600160a01b0383166000908152600c6020526040812054610100900460ff16156132805760405162461bcd60e51b815260206004820152601060248201526f189bdc9c9bddc81a5cc81c185d5cd95960821b604482015260640161119a565b6001600160a01b0384166000908152600a602052604090205460ff166132a75760096119e8565b6001600160a01b0380851660009081526009602090815260408083209387168352929052205460ff166133c657336001600160a01b038516146133245760405162461bcd60e51b815260206004820152601560248201527439b2b73232b91036bab9ba1031329037aa37b5b2b760591b604482015260640161119a565b600061333033856145e3565b9050600081601181111561335457634e487b7160e01b600052602160045260246000fd5b146133815780601181111561337957634e487b7160e01b600052602160045260246000fd5b915050611ab6565b6001600160a01b0380861660009081526009602090815260408083209388168352929052205460ff166133c457634e487b7160e01b600052600160045260246000fd5b505b6004805460405163fc57d4df60e01b81526001600160a01b039091169163fc57d4df916133f591889101615919565b60206040518083038186803b15801561340d57600080fd5b505afa158015613421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061344591906158cc565b61345057600d6119e8565b6001600160a01b038416600090815260166020526040902054801561352e578083866001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b1580156134aa57600080fd5b505afa1580156134be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134e291906158cc565b6134ec9190615b83565b1061352e5760405162461bcd60e51b8152602060048201526012602482015271189bdc9c9bddc818d85c081c995858da195960721b604482015260640161119a565b60008061353e86886000886142a0565b9193509091506000905082601181111561356857634e487b7160e01b600052602160045260246000fd5b146135975781601181111561358d57634e487b7160e01b600052602160045260246000fd5b9350505050611ab6565b80156135a457600461358d565b6135ae878761123c565b6000979650505050505050565b600860205281600052604060002081815481106135d757600080fd5b6000918252602090912001546001600160a01b03169150829050565b600080546001600160a01b0316331461361257612d3b600160066146db565b6001600160a01b0383166000908152600a60205260409020805460ff166136475761363f600960076146db565b9150506113c0565b60408051602080820183528582528251908101909252670c7d713b49da000082529061367581835190511090565b1561369057613686600660086146db565b93505050506113c0565b841580159061371b57506004805460405163fc57d4df60e01b81526001600160a01b039091169163fc57d4df916136c9918a9101615919565b60206040518083038186803b1580156136e157600080fd5b505afa1580156136f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371991906158cc565b155b1561372c57613686600d60096146db565b60018301805490869055604080516001600160a01b0389168152602081018390529081018790527f70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc59060600160405180910390a16000979650505050505050565b60008061379b858585614da0565b905080156137aa579050611ab6565b6137b4858561236c565b600095945050505050565b6000808290506000806000836001600160a01b031663c37f68e2336040518263ffffffff1660e01b81526004016137f69190615919565b60806040518083038186803b15801561380e57600080fd5b505afa158015613822573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061384691906158e4565b50925092509250826000146138ab5760405162461bcd60e51b815260206004820152602560248201527f657869744d61726b65743a206765744163636f756e74536e617073686f742066604482015264185a5b195960da1b606482015260840161119a565b80156138c7576138bd600c60026146db565b9695505050505050565b60006138d4873385614da0565b905080156138f4576138e9600e600383614f9f565b979650505050505050565b6001600160a01b038516600090815260096020908152604080832033845290915290205460ff166139265760006138e9565b6001600160a01b03851660009081526009602090815260408083203384528252808320805460ff1916905560088252808320805482518185028101850190935280835291929091908301828280156139a757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613989575b5050835193945083925060009150505b82811015613a1a57886001600160a01b03168482815181106139e957634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03161415613a0857809150613a1a565b80613a1281615bf1565b9150506139b7565b50818110613a3857634e487b7160e01b600052600160045260246000fd5b33600090815260086020526040902080548190613a5790600190615bda565b81548110613a7557634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b0316818381548110613ab357634e487b7160e01b600052603260045260246000fd5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080805480613aff57634e487b7160e01b600052603160045260246000fd5b600082815260209020810160001990810180546001600160a01b03191690550190556040517fe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d90613b53908b90339061592d565b60405180910390a160009b9a5050505050505050505050565b6000546001600160a01b0316331480613b8f5750601e546001600160a01b031633145b613bab5760405162461bcd60e51b815260040161119a90615ad5565b613bb482612443565b80613bd7576001600160a01b038216600090815260186020526040812055613bf2565b426001600160a01b0383166000908152601860205260409020555b6001600160a01b03821660008181526017602052604090819020839055517f85eecf30a97533335aac0dc394c47a6600a5107038a0c967102478d9fe568dab90613c3f9084815260200190565b60405180910390a25050565b600080546001600160a01b0316331480613c6f57506002546001600160a01b031633145b905090565b600080613c7f612434565b90506001600160a01b03811615613dcc576040516370a0823160e01b81526000906001600160a01b038316906370a0823190613cbf903090600401615919565b60206040518083038186803b158015613cd757600080fd5b505afa158015613ceb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d0f91906158cc565b9050600084118015613d215750808411155b15613dca5760405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb90613d549088908890600401615947565b600060405180830381600087803b158015613d6e57600080fd5b505af1158015613d82573d6000803e3d6000fd5b505050507fdc38f4aaaf6d37c4ae151051029aff3215faa3c2aceabbed1c30c5252f139af18585604051613db7929190615947565b60405180910390a16000925050506113c0565b505b509092915050565b6001600160a01b038216600090815260116020908152604080832060199092528220549091613e0242615028565b8354909150600090613e259063ffffffff600160e01b9091048116908416615bda565b90508015610831578215613fcd57601c54600090613f47906001600160a01b031615613ed057601c546040516301cd4a6960e11b81526001600160a01b039091169063039a94d290613e7b908b90600401615919565b60206040518083038186803b158015613e9357600080fd5b505afa158015613ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ecb91906158cc565b613f41565b876001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b158015613f0957600080fd5b505afa158015613f1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4191906158cc565b87615074565b90506000613f558584615bbb565b90506000808311613f755760405180602001604052806000815250613f7f565b613f7f8284615095565b604080516020810190915288546001600160e01b03168152909150613fae90613fa890836150ca565b516150f3565b87546001600160e01b0319166001600160e01b03919091161787555050505b835463ffffffff8316600160e01b026001600160e01b03909116178455505050505050565b6001600160a01b0383811660009081526011602090815260408083205460138352818420948716845293909152902080546001600160e01b0390921690819055908015801561403f575060015b1561404b575050505050565b6000604051806020016040528083856140649190615bda565b9052905082821461083157601c54600090614190906001600160a01b03161561410e57601c54604051632777dd6960e11b81526001600160a01b0390911690634eefbad2906140b9908b908b9060040161592d565b60206040518083038186803b1580156140d157600080fd5b505afa1580156140e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061410991906158cc565b61418a565b6040516395dd919360e01b81526001600160a01b038916906395dd91939061413a908a90600401615919565b60206040518083038186803b15801561415257600080fd5b505afa158015614166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418a91906158cc565b86615074565b9050600061419e828461513f565b6001600160a01b038816600090815260146020526040812054919250906141c6908390615b83565b6001600160a01b03808a16600081815260146020526040908190208490555192935091908b16907f140436893bf94456b060bcba4ad537b98c6bea5fb2f5badef8904e98bbd78bd79061421c9086908b90615b0c565b60405180910390a3505050505050505050565b6000546001600160a01b03163314806142525750600b546001600160a01b031633145b61429e5760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920706175736520677561726469616e20616e642061646d696e000000604482015260640161119a565b565b60008060006142ad6151b3565b6001600160a01b03881660009081526008602090815260408083208054825181850281018501909352808352849383018282801561431457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116142f6575b5050505050905060005b81518110156145a457600082828151811061434957634e487b7160e01b600052603260045260246000fd5b60200260200101519050806001600160a01b031663c37f68e28d6040518263ffffffff1660e01b815260040161437f9190615919565b60806040518083038186803b15801561439757600080fd5b505afa1580156143ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143cf91906158e4565b608089015260608801526040870152935083156143fb57600f6000809750975097505050505050611961565b60408051602080820183526001600160a01b038085166000908152600a835284902060010154835260c0890192909252825190810183526080880151815260e088015260048054925163fc57d4df60e01b8152929091169163fc57d4df9161446591859101615919565b60206040518083038186803b15801561447d57600080fd5b505afa158015614491573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144b591906158cc565b60a086018190526144d557600d6000809750975097505050505050611961565b604080516020810190915260a0860151815261010086015260c085015160e086015161450f9161450491614e70565b866101000151614e70565b61012086018190526040860151865161452992919061515e565b85526101008501516060860151602087015161454692919061515e565b60208601526001600160a01b03818116908c161415614591576145738561012001518b876020015161515e565b6020860181905261010086015161458b918b9061515e565b60208601525b508061459c81615bf1565b91505061431e565b506020830151835111156145ca5750506020810151905160009450039150829050611961565b5050805160209091015160009450849350039050611961565b6001600160a01b0382166000908152600a602052604081205460ff1661460b575060096113c0565b6001600160a01b0380841660009081526009602090815260408083209386168352929052205460ff1615614641575060006113c0565b6001600160a01b0380841660008181526009602090815260408083209487168352938152838220805460ff191660019081179091556008825284832080549182018155835291200180546001600160a01b0319169091179055517f3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5906146ca908590859061592d565b60405180910390a150600092915050565b6000600080516020615c5c83398151915283601181111561470c57634e487b7160e01b600052602160045260246000fd5b83601381111561472c57634e487b7160e01b600052602160045260246000fd5b600060405161473d93929190615b1a565b60405180910390a1826011811115611ab657634e487b7160e01b600052602160045260246000fd5b6001600160a01b0381166000908152601060209081526040808320601a909252822054909161479342615028565b83549091506000906147b69063ffffffff600160e01b9091048116908416615bda565b9050801561055157821561494e57601c546000906001600160a01b03161561485d57601c54604051631e1932fb60e01b81526001600160a01b0390911690631e1932fb90614808908990600401615919565b60206040518083038186803b15801561482057600080fd5b505afa158015614834573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061485891906158cc565b6148ce565b856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561489657600080fd5b505afa1580156148aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148ce91906158cc565b905060006148dc8584615bbb565b905060008083116148fc5760405180602001604052806000815250614906565b6149068284615095565b604080516020810190915288546001600160e01b0316815290915061492f90613fa890836150ca565b87546001600160e01b0319166001600160e01b03919091161787555050505b835463ffffffff8316600160e01b026001600160e01b039091161784555050505050565b6001600160a01b0382811660009081526010602090815260408083205460128352818420948616845293909152902080546001600160e01b039092169081905590801580156149bf575060015b156149ca5750505050565b6000604051806020016040528083856149e39190615bda565b9052601c549091506000906001600160a01b031615614a8357601c546040516301693b2560e41b81526001600160a01b0390911690631693b25090614a2e908990899060040161592d565b60206040518083038186803b158015614a4657600080fd5b505afa158015614a5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a7e91906158cc565b614aff565b6040516370a0823160e01b81526001600160a01b038716906370a0823190614aaf908890600401615919565b60206040518083038186803b158015614ac757600080fd5b505afa158015614adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614aff91906158cc565b9050828414610831576000614b14828461513f565b6001600160a01b03871660009081526014602052604081205491925090614b3c908390615b83565b6001600160a01b03808916600081815260146020526040908190208490555192935091908a16907f1b63b3b859428434d99569695315fa35918c5a2bce714acb147f4c3019808bdd90614b929086908b90615b0c565b60405180910390a35050505050505050565b6000611ab6614bb38484615178565b61519b565b6001600160a01b0383166000908152600a602052604090205460ff16614c1c5760405162461bcd60e51b81526020600482015260196024820152780c159256081b585c9ad95d081a5cc81b9bdd081b1a5cdd1959603a1b604482015260640161119a565b6001600160a01b0383166000908152601a60205260409020548214614c9a57614c4483614765565b6001600160a01b0383166000818152601a602052604090819020849055517ff720735fa76f84bc32ace1df447c03b6acef6e4b238ff71722ebaa93f9c1cdc090614c919085815260200190565b60405180910390a25b6001600160a01b03831660009081526019602052604090205481146109a45760006040518060200160405280856001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015614cff57600080fd5b505afa158015614d13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614d3791906158cc565b90529050614d458482613dd4565b6001600160a01b03841660008181526019602052604090819020849055517ff386f4abe5d857960f167a80b4dbb954055f412561afb755f97406c675be98c390614d929085815260200190565b60405180910390a250505050565b6001600160a01b0383166000908152600a602052604081205460ff16614dc75760096119e8565b6001600160a01b0380851660009081526009602090815260408083209387168352929052205460ff16614dfb5760006119e8565b600080614e0b85878660006142a0565b91935090915060009050826011811115614e3557634e487b7160e01b600052602160045260246000fd5b14614e6357816011811115614e5a57634e487b7160e01b600052602160045260246000fd5b92505050611ab6565b80156129f0576004614e5a565b614e7861521d565b6040518060200160405280670de0b6b3a764000084600001518660000151614ea09190615bbb565b614eaa9190615b9b565b90529392505050565b614ebb61521d565b60405180602001604052808360000151670de0b6b3a76400008660000151614ea09190615bbb565b6000614eee42615028565b6001600160a01b03831660009081526010602090815260408083206011909252909120815492935090916001600160e01b0316614f445781546001600160e01b0319166a0c097ce7bc90715b34b9f160241b1782555b80546001600160e01b0316614f725780546001600160e01b0319166a0c097ce7bc90715b34b9f160241b1781555b805463ffffffff909316600160e01b026001600160e01b0393841681179091558154909216909117905550565b6000600080516020615c5c833981519152846011811115614fd057634e487b7160e01b600052602160045260246000fd5b846013811115614ff057634e487b7160e01b600052602160045260246000fd5b8460405161500093929190615b1a565b60405180910390a1836011811115611ab357634e487b7160e01b600052602160045260246000fd5b600063ffffffff8211156150705760405162461bcd60e51b815260206004820152600f60248201526e736166653332206f766572666c6f7760881b604482015260640161119a565b5090565b805160009061508b670de0b6b3a764000085615bbb565b611ab69190615b9b565b6040805160208101909152600081526040518060200160405280836a0c097ce7bc90715b34b9f160241b86614ea09190615bbb565b6040805160208101909152600081526040805160208101909152825184518291614eaa91615b83565b60006001600160e01b038211156150705760405162461bcd60e51b815260206004820152601060248201526f73616665323234206f766572666c6f7760801b604482015260640161119a565b80516000906a0c097ce7bc90715b34b9f160241b9061508b9085615bbb565b60008161516e614bb38686615178565b611ab39190615b83565b61518061521d565b6040518060200160405280838560000151614eaa9190615bbb565b80516000906113c090670de0b6b3a764000090615b9b565b6040518061014001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016151f161521d565b81526020016151fe61521d565b815260200161520b61521d565b815260200161521861521d565b905290565b6040518060200160405280600081525090565b600082601f830112615240578081fd5b8135602061525561525083615b60565b615b30565b80838252828201915082860187848660051b8901011115615274578586fd5b855b8581101561529b57813561528981615c38565b84529284019290840190600101615276565b5090979650505050505050565b60008083601f8401126152b9578182fd5b5081356001600160401b038111156152cf578182fd5b6020830191508360208260051b85010111156152ea57600080fd5b9250929050565b600082601f830112615301578081fd5b8135602061531161525083615b60565b80838252828201915082860187848660051b8901011115615330578586fd5b855b8581101561529b57813561534581615c38565b84529284019290840190600101615332565b600082601f830112615367578081fd5b8135602061537761525083615b60565b80838252828201915082860187848660051b8901011115615396578586fd5b855b8581101561529b57813584529284019290840190600101615398565b6000602082840312156153c5578081fd5b8135611ab681615c38565b6000602082840312156153e1578081fd5b8151611ab681615c38565b600080604083850312156153fe578081fd5b823561540981615c38565b9150602083013561541981615c38565b809150509250929050565b600080600080600060a0868803121561543b578081fd5b853561544681615c38565b9450602086013561545681615c38565b9350604086013561546681615c38565b9250606086013561547681615c38565b949793965091946080013592915050565b60008060008060008060c0878903121561549f578081fd5b86356154aa81615c38565b955060208701356154ba81615c38565b945060408701356154ca81615c38565b935060608701356154da81615c38565b9598949750929560808101359460a0909101359350915050565b60008060008060808587031215615509578182fd5b843561551481615c38565b9350602085013561552481615c38565b9250604085013561553481615c38565b9396929550929360600135925050565b600080600080600060a0868803121561555b578283fd5b853561556681615c38565b9450602086013561557681615c38565b9350604086013561558681615c38565b94979396509394606081013594506080013592915050565b6000806000606084860312156155b2578081fd5b83356155bd81615c38565b925060208401356155cd81615c38565b929592945050506040919091013590565b600080600080608085870312156155f3578182fd5b84356155fe81615c38565b9350602085013561560e81615c38565b93969395505050506040820135916060013590565b60008060408385031215615635578182fd5b823561564081615c38565b915060208301356001600160401b0381111561565a578182fd5b615666858286016152f1565b9150509250929050565b60008060408385031215615682578182fd5b823561568d81615c38565b9150602083013561541981615c4d565b600080604083850312156153fe578182fd5b600080604083850312156156c1578182fd5b82356156cc81615c38565b946020939093013593505050565b6000602082840312156156eb578081fd5b81356001600160401b03811115615700578182fd5b6116bf84828501615230565b60008060008060808587031215615721578182fd5b84356001600160401b0380821115615737578384fd5b61574388838901615230565b95506020870135915080821115615758578384fd5b50615765878288016152f1565b935050604085013561577681615c4d565b9150606085013561578681615c4d565b939692955090935050565b6000806000606084860312156157a5578081fd5b83356001600160401b03808211156157bb578283fd5b6157c787838801615230565b945060208601359150808211156157dc578283fd5b6157e887838801615357565b935060408601359150808211156157fd578283fd5b5061580a86828701615357565b9150509250925092565b60008060008060408587031215615829578182fd5b84356001600160401b038082111561583f578384fd5b61584b888389016152a8565b90965094506020870135915080821115615863578384fd5b50615870878288016152a8565b95989497509550505050565b60006020828403121561588d578081fd5b8135611ab681615c4d565b6000602082840312156158a9578081fd5b8151611ab681615c4d565b6000602082840312156158c5578081fd5b5035919050565b6000602082840312156158dd578081fd5b5051919050565b600080600080608085870312156158f9578182fd5b505082516020840151604085015160609095015191969095509092509050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156159a15783516001600160a01b03168352928401929184019160010161597c565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156159a1578351835292840192918401916001016159c9565b6020808252601790820152761b9bc81e995c9bc81859191c995cdcc8185b1b1bddd959604a1b604082015260600190565b6020808252600f908201526e15925608185b1c9958591e481cd95d608a1b604082015260600190565b6020808252601690820152756f6e6c792061646d696e2063616e20756e706175736560501b604082015260600190565b6020808252601590820152741b585c9ad95d081b5d5cdd081899481b1a5cdd1959605a1b604082015260600190565b6020808252601f908201527f63616e6e6f742070617573653a206d61726b6574206e6f74206c697374656400604082015260600190565b6020808252601f908201527f6f6e6c792061646d696e2063616e207365742072657761726420737065656400604082015260600190565b918252602082015260400190565b9283526020830191909152604082015260600190565b604051601f8201601f191681016001600160401b0381118282101715615b5857615b58615c22565b604052919050565b60006001600160401b03821115615b7957615b79615c22565b5060051b60200190565b60008219821115615b9657615b96615c0c565b500190565b600082615bb657634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615615bd557615bd5615c0c565b500290565b600082821015615bec57615bec615c0c565b500390565b6000600019821415615c0557615c05615c0c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461155957600080fd5b801515811461155957600080fdfe45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa027ee2b767de43e8b48d1666d8c451a273fbf857e760fda6e95d0d582f68534bc31abd22c3c8d5f4796be5f21843042b53e07dcda713c78eb2751d9bd12aaba8671aec636243f9709bb0007ae15e9afb8150ab01716d75fd7573be5cc096e03b0a2646970667358221220ffd80756bd14a8457538acc2832870150b9bcc272fc0e30abfd22dc328b5fe8564736f6c63430008040033

Deployed Bytecode

0x6080604052600436106104035760003560e01c80636a56947e116102115780636a56947e14610ab25780636b1e85c514610acd5780636d35bf9114610aed5780636ec934da14610b085780637388dcff14610b41578063741b252514610b565780637dc0d1d014610b7657806385e7069414610b9657806387f7630314610bd05780638b3f9c3b14610bf15780638e8f294b14610c115780638ebf636414610c6f5780638fc67c2614610c8f578063929fe9a114610caf57806394543c1514610cf957806394b2294b14610d195780639ace3ada14610d2f578063a7b1986c14610d6a578063a8a5564a14610d8a578063aa5b3e7f14610dc2578063aa90075414610de2578063abfceffc14610df8578063ac0b0bb714610e25578063b0772d0b14610e46578063bb82aa5e14610e5b578063bdcdc25814610e7b578063c299823814610e9b578063c47a852614610ec8578063c488847b14610f0d578063ca7d5a1514610f3b578063d02f735114610f5b578063d03a27c514610f7b578063d279c19114610f9b578063da3d454c14610fbb578063daacd11814610fdb578063dce1544914611008578063dcfbc0c714611028578063e4028eee14611048578063e6653f3d14611068578063e875544614611089578063eabe7d911461109f578063ede4edd0146110bf578063ee0905cd146110df578063f851a440146110ff578063ff278d2d1461111f57600080fd5b80627e3c461461040f5780627e3dd21461044f5780630346ef2a146104745780630dd1cc881461049657806312e1e8c4146104c3578063188ec356146104e357806318c882a5146104f65780631d504dc6146105165780631ededc91146105365780632026ffa31461055857806321af456914610578578063239a4237146105a557806324008a62146105c557806324a3d622146105e5578063267822471461060557806328bd3c85146106255780632c1d6c56146106525780632d70db781461067f578063317b0b771461069f57806336301f1f146106bf578063391957d7146106f75780633b593a45146107175780633bcf7ec1146107445780633c94786f146107645780633cb61d4d1461078557806341c728b9146107a5578063447a1366146107c657806347ef3b3b146108165780634a584432146108395780634aacbabc146108665780634ada90af146108845780634e79238f1461089a5780634ef4c3e1146108c95780634fd42e17146108e957806351dff9891461090957806352d84d1e1461092957806354eb76fa1461094957806355ee1fe1146109695780635c778605146109895780635ec88c79146109a95780635f5af1aa146109c95780635fc7e71e146109e9578063607ef6c114610a09578063670e8afd14610a295780636760c07514610a4957600080fd5b3661040a57005b600080fd5b34801561041b57600080fd5b5061043c61042a3660046153b4565b60146020526000908152604090205481565b6040519081526020015b60405180910390f35b34801561045b57600080fd5b50610464600181565b6040519015158152602001610446565b34801561048057600080fd5b5061049461048f3660046156af565b61114c565b005b3480156104a257600080fd5b5061043c6104b13660046153b4565b60186020526000908152604090205481565b3480156104cf57600080fd5b506104946104de3660046153ec565b61123c565b3480156104ef57600080fd5b504261043c565b34801561050257600080fd5b50610464610511366004615670565b6112d3565b34801561052257600080fd5b506104946105313660046153b4565b6113c6565b34801561054257600080fd5b50610494610551366004615544565b5050505050565b34801561056457600080fd5b50610494610573366004615623565b61155c565b34801561058457600080fd5b50601554610598906001600160a01b031681565b6040516104469190615919565b3480156105b157600080fd5b506104946105c03660046153b4565b6115cd565b3480156105d157600080fd5b5061043c6105e03660046154f4565b611689565b3480156105f157600080fd5b50600b54610598906001600160a01b031681565b34801561061157600080fd5b50600154610598906001600160a01b031681565b34801561063157600080fd5b5061043c6106403660046153b4565b601b6020526000908152604090205481565b34801561065e57600080fd5b5061043c61066d3660046153b4565b60196020526000908152604090205481565b34801561068b57600080fd5b5061046461069a36600461587c565b6116c7565b3480156106ab57600080fd5b5061043c6106ba3660046158b4565b611768565b3480156106cb57600080fd5b5061043c6106da3660046153ec565b601260209081526000928352604080842090915290825290205481565b34801561070357600080fd5b506104946107123660046153b4565b6117cb565b34801561072357600080fd5b5061043c6107323660046153b4565b601a6020526000908152604090205481565b34801561075057600080fd5b5061046461075f366004615670565b611841565b34801561077057600080fd5b50600b5461046490600160a01b900460ff1681565b34801561079157600080fd5b50601e54610598906001600160a01b031681565b3480156107b157600080fd5b506104946107c03660046155de565b50505050565b3480156107d257600080fd5b506107ff6107e13660046153b4565b600c6020526000908152604090205460ff8082169161010090041682565b604080519215158352901515602083015201610446565b34801561082257600080fd5b50610494610831366004615487565b505050505050565b34801561084557600080fd5b5061043c6108543660046153b4565b60166020526000908152604090205481565b34801561087257600080fd5b50601c546001600160a01b0316610598565b34801561089057600080fd5b5061043c60065481565b3480156108a657600080fd5b506108ba6108b53660046155de565b61191d565b60405161044693929190615b1a565b3480156108d557600080fd5b5061043c6108e436600461559e565b61196b565b3480156108f557600080fd5b5061043c6109043660046158b4565b611abd565b34801561091557600080fd5b506104946109243660046155de565b611b16565b34801561093557600080fd5b506105986109443660046158b4565b611b62565b34801561095557600080fd5b5061049461096436600461570c565b611b8c565b34801561097557600080fd5b5061043c6109843660046153b4565b611e47565b34801561099557600080fd5b506104946109a436600461559e565b505050565b3480156109b557600080fd5b506108ba6109c43660046153b4565b611eb9565b3480156109d557600080fd5b5061043c6109e43660046153b4565b611f02565b3480156109f557600080fd5b5061043c610a04366004615424565b611f73565b348015610a1557600080fd5b50610494610a24366004615814565b612174565b348015610a3557600080fd5b50610494610a443660046153ec565b61236c565b348015610a5557600080fd5b50610a8e610a643660046153b4565b6010602052600090815260409020546001600160e01b03811690600160e01b900463ffffffff1682565b604080516001600160e01b03909316835263ffffffff909116602083015201610446565b348015610abe57600080fd5b506104946107c03660046154f4565b348015610ad957600080fd5b50610494610ae83660046153b4565b612383565b348015610af957600080fd5b50610494610551366004615424565b348015610b1457600080fd5b50610464610b233660046153b4565b6001600160a01b03166000908152600a602052604090205460ff1690565b348015610b4d57600080fd5b50610598612434565b348015610b6257600080fd5b50610494610b713660046153b4565b612443565b348015610b8257600080fd5b50600454610598906001600160a01b031681565b348015610ba257600080fd5b50610bb86a0c097ce7bc90715b34b9f160241b81565b6040516001600160e01b039091168152602001610446565b348015610bdc57600080fd5b50600b5461046490600160b01b900460ff1681565b348015610bfd57600080fd5b50610494610c0c366004615791565b6124ef565b348015610c1d57600080fd5b50610c52610c2c3660046153b4565b600a602052600090815260409020805460019091015460ff808316926101009004169083565b604080519315158452911515602084015290820152606001610446565b348015610c7b57600080fd5b50610464610c8a36600461587c565b61263b565b348015610c9b57600080fd5b50610494610caa3660046153b4565b6126d3565b348015610cbb57600080fd5b50610464610cca36600461569d565b6001600160a01b0380821660009081526009602090815260408083209386168352929052205460ff1692915050565b348015610d0557600080fd5b50610464610d143660046153b4565b61275b565b348015610d2557600080fd5b5061043c60075481565b348015610d3b57600080fd5b50610464610d4a3660046153ec565b600960209081526000928352604080842090915290825290205460ff1681565b348015610d7657600080fd5b50610494610d85366004615670565b612829565b348015610d9657600080fd5b5061043c610da53660046153ec565b601360209081526000928352604080842090915290825290205481565b348015610dce57600080fd5b50601d54610598906001600160a01b031681565b348015610dee57600080fd5b5061043c600e5481565b348015610e0457600080fd5b50610e18610e133660046153b4565b61288d565b6040516104469190615960565b348015610e3157600080fd5b50600b5461046490600160b81b900460ff1681565b348015610e5257600080fd5b50610e18612903565b348015610e6757600080fd5b50600254610598906001600160a01b031681565b348015610e8757600080fd5b5061043c610e963660046154f4565b612965565b348015610ea757600080fd5b50610ebb610eb63660046156da565b6129fc565b60405161044691906159ad565b348015610ed457600080fd5b50610a8e610ee33660046153b4565b6011602052600090815260409020546001600160e01b03811690600160e01b900463ffffffff1682565b348015610f1957600080fd5b50610f2d610f2836600461559e565b612af5565b604051610446929190615b0c565b348015610f4757600080fd5b5061043c610f56366004615670565b612d1c565b348015610f6757600080fd5b5061043c610f76366004615424565b612f8c565b348015610f8757600080fd5b50601c54610598906001600160a01b031681565b348015610fa757600080fd5b5061043c610fb63660046153b4565b61313b565b348015610fc757600080fd5b5061043c610fd636600461559e565b61321f565b348015610fe757600080fd5b5061043c610ff63660046153b4565b60176020526000908152604090205481565b34801561101457600080fd5b506105986110233660046156af565b6135bb565b34801561103457600080fd5b50600354610598906001600160a01b031681565b34801561105457600080fd5b5061043c6110633660046156af565b6135f3565b34801561107457600080fd5b50600b5461046490600160a81b900460ff1681565b34801561109557600080fd5b5061043c60055481565b3480156110ab57600080fd5b5061043c6110ba36600461559e565b61378d565b3480156110cb57600080fd5b5061043c6110da3660046153b4565b6137bf565b3480156110eb57600080fd5b506104946110fa3660046156af565b613b6c565b34801561110b57600080fd5b50600054610598906001600160a01b031681565b34801561112b57600080fd5b5061043c61113a3660046153b4565b600f6020526000908152604090205481565b611154613c4b565b6111a35760405162461bcd60e51b815260206004820152601b60248201527a1bdb9b1e4818591b5a5b8818d85b8819dc985b9d081c995dd85c99602a1b60448201526064015b60405180910390fd5b60006111af8383613c74565b905080156111fe5760405162461bcd60e51b815260206004820152601c60248201527b1a5b9cdd59999a58da595b9d081d1bdad95b88199bdc8819dc985b9d60221b604482015260640161119a565b7f16be5776def5b9fc98f7848c5fe8811ac9e187f5d864466387d38c58c399ea5d838360405161122f929190615947565b60405180910390a1505050565b60006040518060200160405280846001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561128257600080fd5b505afa158015611296573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ba91906158cc565b905290506112c88382613dd4565b6109a4838383613ff2565b6001600160a01b0382166000908152600a602052604081205460ff1661130b5760405162461bcd60e51b815260040161119a90615a9e565b61131361422f565b6000546001600160a01b03163314806113295750815b6113455760405162461bcd60e51b815260040161119a90615a3f565b6001600160a01b0383166000818152600c6020908152604091829020805461ff001916610100871515908102919091179091558251938452606091840182905260069184019190915265426f72726f7760d01b608084015290820152600080516020615cbc8339815191529060a0015b60405180910390a150805b92915050565b806001600160a01b031663f851a4406040518163ffffffff1660e01b815260040160206040518083038186803b1580156113ff57600080fd5b505afa158015611413573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143791906153d0565b6001600160a01b0316336001600160a01b0316146114a15760405162461bcd60e51b815260206004820152602160248201527f6f6e6c7920756e6974726f6c6c65722061646d696e2063616e205f6265636f6d6044820152606560f81b606482015260840161119a565b806001600160a01b031663c1e803346040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156114dc57600080fd5b505af11580156114f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151491906158cc565b156115595760405162461bcd60e51b815260206004820152601560248201527418da185b99d9481b9bdd08185d5d1a1bdc9a5e9959605a1b604482015260640161119a565b50565b6040805160018082528183019092526000916020808301908036833701905050905082816000815181106115a057634e487b7160e01b600052603260045260246000fd5b60200260200101906001600160a01b031690816001600160a01b0316815250506109a48183600180611b8c565b6000546001600160a01b031633146115e457600080fd5b6001600160a01b03811661160a5760405162461bcd60e51b815260040161119a906159e5565b601c546001600160a01b0316156116335760405162461bcd60e51b815260040161119a90615a16565b601c80546001600160a01b0319166001600160a01b0383161790556040517fd879a30bb65908f7b6db545675d2415361dc65a59feb404d5f1282ec615c91d89061167e908390615919565b60405180910390a150565b6001600160a01b0384166000908152600a602052604081205460ff166116b1575060096116bf565b6116bb858461123c565b5060005b949350505050565b60006116d161422f565b6000546001600160a01b03163314806116e75750815b6117035760405162461bcd60e51b815260040161119a90615a3f565b600b8054831515600160b81b0260ff60b81b19909116179055604051600080516020615c7c8339815191529061175c9084906040808252600590820152645365697a6560d81b6060820152901515602082015260800190565b60405180910390a15090565b600080546001600160a01b0316331461178057600080fd5b60058054908390556040517f3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9906117ba9083908690615b0c565b60405180910390a160009392505050565b6000546001600160a01b031633146117e257600080fd5b601580546001600160a01b038381166001600160a01b03198316179092556040519116907feda98690e518e9a05f8ec6837663e188211b2da8f4906648b323f2c1d4434e2990611835908390859061592d565b60405180910390a15050565b6001600160a01b0382166000908152600a602052604081205460ff166118795760405162461bcd60e51b815260040161119a90615a9e565b61188161422f565b6000546001600160a01b03163314806118975750815b6118b35760405162461bcd60e51b815260040161119a90615a3f565b6001600160a01b0383166000818152600c6020908152604091829020805460ff19168615159081179091558251938452606091840182905260049184019190915263135a5b9d60e21b608084015290820152600080516020615cbc8339815191529060a0016113b5565b6000806000806000806119328a8a8a8a6142a0565b92509250925082601181111561195857634e487b7160e01b600052602160045260246000fd5b95509093509150505b9450945094915050565b6001600160a01b0383166000908152600c602052604081205460ff16156119c55760405162461bcd60e51b815260206004820152600e60248201526d1b5a5b9d081a5cc81c185d5cd95960921b604482015260640161119a565b6001600160a01b0384166000908152600a602052604090205460ff166119ef5760095b9050611ab6565b6040516370a0823160e01b81526001600160a01b038516906370a0823190611a1b908690600401615919565b60206040518083038186803b158015611a3357600080fd5b505afa158015611a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a6b91906158cc565b158015611a9557506001600160a01b0384166000908152600a6020526040902054610100900460ff165b15611aa657611aa484846145e3565b505b611ab0848461236c565b60005b90505b9392505050565b600080546001600160a01b03163314611adc576113c06001600b6146db565b60068054908390556040517faeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316906117ba9083908690615b0c565b80151580611b22575081155b6107c05760405162461bcd60e51b815260206004820152601160248201527072656465656d546f6b656e73207a65726f60781b604482015260640161119a565b600d8181548110611b7257600080fd5b6000918252602090912001546001600160a01b0316905081565b60005b8351811015611d5a576000848281518110611bba57634e487b7160e01b600052603260045260246000fd5b6020908102919091018101516001600160a01b0381166000908152600a90925260409091205490915060ff16611c025760405162461bcd60e51b815260040161119a90615a6f565b8315611ce75760006040518060200160405280836001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611c4e57600080fd5b505afa158015611c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8691906158cc565b90529050611c948282613dd4565b60005b8751811015611ce457611cd283898381518110611cc457634e487b7160e01b600052603260045260246000fd5b602002602001015184613ff2565b80611cdc81615bf1565b915050611c97565b50505b8215611d4757611cf681614765565b60005b8651811015611d4557611d3382888381518110611d2657634e487b7160e01b600052603260045260246000fd5b6020026020010151614972565b80611d3d81615bf1565b915050611cf9565b505b5080611d5281615bf1565b915050611b8f565b5060005b845181101561055157611de4858281518110611d8a57634e487b7160e01b600052603260045260246000fd5b602002602001015160146000888581518110611db657634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054613c74565b60146000878481518110611e0857634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055508080611e3f90615bf1565b915050611d5e565b600080546001600160a01b03163314611e66576113c0600160106146db565b600480546001600160a01b038481166001600160a01b03198316179092556040519116907fd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e22906117ba908390869061592d565b600080600080600080611ed08760008060006142a0565b925092509250826011811115611ef657634e487b7160e01b600052602160045260246000fd5b97919650945092505050565b600080546001600160a01b03163314611f21576113c0600160136146db565b600b80546001600160a01b038481166001600160a01b0319831681179093556040519116917f0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e916117ba91849161592d565b6001600160a01b0385166000908152600a602052604081205460ff161580611fb457506001600160a01b0385166000908152600a602052604090205460ff16155b15611fc35760095b905061216b565b6040516395dd919360e01b81526000906001600160a01b038816906395dd919390611ff2908790600401615919565b60206040518083038186803b15801561200a57600080fd5b505afa15801561201e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061204291906158cc565b905061204d8761275b565b156120b857828110156120b35760405162461bcd60e51b815260206004820152602860248201527f43616e206e6f74207265706179206d6f7265207468616e2074686520746f74616044820152676c20626f72726f7760c01b606482015260840161119a565b612165565b6000806120c98660008060006142a0565b919350909150600090508260118111156120f357634e487b7160e01b600052602160045260246000fd5b146121225781601181111561211857634e487b7160e01b600052602160045260246000fd5b935050505061216b565b8061212e576003612118565b600061214a604051806020016040528060055481525085614ba4565b90508086111561216157601194505050505061216b565b5050505b60009150505b95945050505050565b6000546001600160a01b031633148061219757506015546001600160a01b031633145b6121e35760405162461bcd60e51b815260206004820152601f60248201527f6f6e6c792061646d696e206f7220626f72726f77436170477561726469616e00604482015260640161119a565b828181158015906121f357508082145b61222f5760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081a5b9c1d5d609a1b604482015260640161119a565b60005b828110156123635784848281811061225a57634e487b7160e01b600052603260045260246000fd5b905060200201356016600089898581811061228557634e487b7160e01b600052603260045260246000fd5b905060200201602081019061229a91906153b4565b6001600160a01b031681526020810191909152604001600020558686828181106122d457634e487b7160e01b600052603260045260246000fd5b90506020020160208101906122e991906153b4565b6001600160a01b03167f6f1951b2aad10f3fc81b86d91105b413a5b3f847a34bbc5ce1904201b14438f686868481811061233357634e487b7160e01b600052603260045260246000fd5b9050602002013560405161234991815260200190565b60405180910390a28061235b81615bf1565b915050612232565b50505050505050565b61237582614765565b61237f8282614972565b5050565b6000546001600160a01b0316331461239a57600080fd5b6001600160a01b0381166123c05760405162461bcd60e51b815260040161119a906159e5565b601d546001600160a01b0316156123e95760405162461bcd60e51b815260040161119a90615a16565b601d80546001600160a01b0319166001600160a01b0383161790556040517f0bd1fe217455fa5501f840301b8930a48a691061ad7c3864ac856f2e9adcdcc49061167e908390615919565b601d546001600160a01b031690565b6001600160a01b0381166000908152601760209081526040808320546018909252822054909142916124759083615bda565b90506000811180156124875750600083115b156107c05760006124988483615bbb565b6001600160a01b038616600090815260146020526040812054919250906124c0908390615b83565b6001600160a01b0387166000908152601460209081526040808320939093556018905220849055505050505050565b6000546001600160a01b03163314806125125750601e546001600160a01b031633145b61252e5760405162461bcd60e51b815260040161119a90615ad5565b82518251811480156125405750815181145b6125a05760405162461bcd60e51b815260206004820152602b60248201527f436f6d7074726f6c6c65723a3a5f73657452657761726453706565647320696e60448201526a1d985b1a59081a5b9c1d5d60aa1b606482015260840161119a565b60005b818110156105515761262b8582815181106125ce57634e487b7160e01b600052603260045260246000fd5b60200260200101518583815181106125f657634e487b7160e01b600052603260045260246000fd5b602002602001015185848151811061261e57634e487b7160e01b600052603260045260246000fd5b6020026020010151614bb8565b61263481615bf1565b90506125a3565b600061264561422f565b6000546001600160a01b031633148061265b5750815b6126775760405162461bcd60e51b815260040161119a90615a3f565b600b8054831515600160b01b0260ff60b01b19909116179055604051600080516020615c7c8339815191529061175c9084906040808252600890820152672a3930b739b332b960c11b6060820152901515602082015260800190565b6000546001600160a01b031633146126ea57600080fd5b6001600160a01b0381166127105760405162461bcd60e51b815260040161119a906159e5565b601e80546001600160a01b0319166001600160a01b0383161790556040517fbea39469363910457d9c890c8148cf384c00faf61147f6e40a39ddafa26fc2099061167e908390615919565b6001600160a01b0381166000908152600a60205260408120600101541580156127a157506001600160a01b0382166000908152600c6020526040902054610100900460ff165b80156113c05750816001600160a01b031663173b99046040518163ffffffff1660e01b815260040160206040518083038186803b1580156127e157600080fd5b505afa1580156127f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061281991906158cc565b670de0b6b3a76400001492915050565b6000546001600160a01b0316331461284057600080fd5b6001600160a01b0382166000908152600a60205260409081902080548315156101000261ff001990911617905551600080516020615c9c8339815191529061183590831515815260200190565b6001600160a01b0381166000908152600860209081526040918290208054835181840281018401909452808452606093928301828280156128f757602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116128d9575b50505050509050919050565b6060600d80548060200260200160405190810160405280929190818152602001828054801561295b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161293d575b5050505050905090565b600b54600090600160b01b900460ff16156129b75760405162461bcd60e51b81526020600482015260126024820152711d1c985b9cd9995c881a5cc81c185d5cd95960721b604482015260640161119a565b60006129c4868685614da0565b905080156129d35790506116bf565b6129dc86614765565b6129e68686614972565b6129f08685614972565b60009695505050505050565b80516060906000816001600160401b03811115612a2957634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612a52578160200160208202803683370190505b50905060005b82811015612aed57612a91858281518110612a8357634e487b7160e01b600052603260045260246000fd5b6020026020010151336145e3565b6011811115612ab057634e487b7160e01b600052602160045260246000fd5b828281518110612ad057634e487b7160e01b600052603260045260246000fd5b602090810291909101015280612ae581615bf1565b915050612a58565b509392505050565b6004805460405163fc57d4df60e01b8152600092839283926001600160a01b039091169163fc57d4df91612b2b918a9101615919565b60206040518083038186803b158015612b4357600080fd5b505afa158015612b57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b7b91906158cc565b6004805460405163fc57d4df60e01b81529293506000926001600160a01b039091169163fc57d4df91612bb0918a9101615919565b60206040518083038186803b158015612bc857600080fd5b505afa158015612bdc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c0091906158cc565b9050811580612c0d575080155b15612c2157600d6000935093505050612d14565b6000866001600160a01b031663182df0f56040518163ffffffff1660e01b815260040160206040518083038186803b158015612c5c57600080fd5b505afa158015612c70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9491906158cc565b90506000612cc06040518060200160405280600654815250604051806020016040528087815250614e70565b90506000612cea604051806020016040528086815250604051806020016040528086815250614e70565b90506000612cf88383614eb3565b90506000612d06828b614ba4565b600099509750505050505050505b935093915050565b600080546001600160a01b03163314612d4257612d3b600160126146db565b90506113c0565b6001600160a01b0383166000908152600a602052604090205460ff1615612d6f57612d3b600a60116146db565b826001600160a01b03166397de9d116040518163ffffffff1660e01b815260040160206040518083038186803b158015612da857600080fd5b505afa158015612dbc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612de09190615898565b50604080516060810182526001808252841515602080840182815260008587018181526001600160a01b038b168252600a84529087902095518654925161ffff1990931690151561ff00191617610100921515929092029190911785555193909201929092559151908152600080516020615c9c833981519152910160405180910390a160005b600d54811015612f0857836001600160a01b0316600d8281548110612e9c57634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03161415612ef65760405162461bcd60e51b81526020600482015260146024820152731b585c9ad95d08185b1c9958591e48185919195960621b604482015260640161119a565b80612f0081615bf1565b915050612e67565b50600d80546001810182556000919091527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50180546001600160a01b0319166001600160a01b038516179055612f5d83614ee3565b7fcf583bb0c569eb967f806b11601c4cb93c10310485c67add5f8362c2f212321f836040516117ba9190615919565b600b54600090600160b81b900460ff1615612fdb5760405162461bcd60e51b815260206004820152600f60248201526e1cd95a5e99481a5cc81c185d5cd959608a1b604482015260640161119a565b6001600160a01b0386166000908152600a602052604090205460ff16158061301c57506001600160a01b0385166000908152600a602052604090205460ff16155b15613028576009611fbc565b846001600160a01b0316635fe3b5676040518163ffffffff1660e01b815260040160206040518083038186803b15801561306157600080fd5b505afa158015613075573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061309991906153d0565b6001600160a01b0316866001600160a01b0316635fe3b5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156130db57600080fd5b505afa1580156130ef573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061311391906153d0565b6001600160a01b031614613128576002611fbc565b61313186614765565b6129e68684614972565b6000805b600d548110156131da576000600d828154811061316c57634e487b7160e01b600052603260045260246000fd5b60009182526020808320909101546001600160a01b0316808352600a90915260409091205490915060ff166131b35760405162461bcd60e51b815260040161119a90615a6f565b6131bd818561123c565b6131c7818561236c565b50806131d281615bf1565b91505061313f565b506001600160a01b0382166000908152601460205260409020546131fe8382613c74565b6001600160a01b039093166000908152601460205260409020929092555090565b6001600160a01b0383166000908152600c6020526040812054610100900460ff16156132805760405162461bcd60e51b815260206004820152601060248201526f189bdc9c9bddc81a5cc81c185d5cd95960821b604482015260640161119a565b6001600160a01b0384166000908152600a602052604090205460ff166132a75760096119e8565b6001600160a01b0380851660009081526009602090815260408083209387168352929052205460ff166133c657336001600160a01b038516146133245760405162461bcd60e51b815260206004820152601560248201527439b2b73232b91036bab9ba1031329037aa37b5b2b760591b604482015260640161119a565b600061333033856145e3565b9050600081601181111561335457634e487b7160e01b600052602160045260246000fd5b146133815780601181111561337957634e487b7160e01b600052602160045260246000fd5b915050611ab6565b6001600160a01b0380861660009081526009602090815260408083209388168352929052205460ff166133c457634e487b7160e01b600052600160045260246000fd5b505b6004805460405163fc57d4df60e01b81526001600160a01b039091169163fc57d4df916133f591889101615919565b60206040518083038186803b15801561340d57600080fd5b505afa158015613421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061344591906158cc565b61345057600d6119e8565b6001600160a01b038416600090815260166020526040902054801561352e578083866001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b1580156134aa57600080fd5b505afa1580156134be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134e291906158cc565b6134ec9190615b83565b1061352e5760405162461bcd60e51b8152602060048201526012602482015271189bdc9c9bddc818d85c081c995858da195960721b604482015260640161119a565b60008061353e86886000886142a0565b9193509091506000905082601181111561356857634e487b7160e01b600052602160045260246000fd5b146135975781601181111561358d57634e487b7160e01b600052602160045260246000fd5b9350505050611ab6565b80156135a457600461358d565b6135ae878761123c565b6000979650505050505050565b600860205281600052604060002081815481106135d757600080fd5b6000918252602090912001546001600160a01b03169150829050565b600080546001600160a01b0316331461361257612d3b600160066146db565b6001600160a01b0383166000908152600a60205260409020805460ff166136475761363f600960076146db565b9150506113c0565b60408051602080820183528582528251908101909252670c7d713b49da000082529061367581835190511090565b1561369057613686600660086146db565b93505050506113c0565b841580159061371b57506004805460405163fc57d4df60e01b81526001600160a01b039091169163fc57d4df916136c9918a9101615919565b60206040518083038186803b1580156136e157600080fd5b505afa1580156136f5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061371991906158cc565b155b1561372c57613686600d60096146db565b60018301805490869055604080516001600160a01b0389168152602081018390529081018790527f70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc59060600160405180910390a16000979650505050505050565b60008061379b858585614da0565b905080156137aa579050611ab6565b6137b4858561236c565b600095945050505050565b6000808290506000806000836001600160a01b031663c37f68e2336040518263ffffffff1660e01b81526004016137f69190615919565b60806040518083038186803b15801561380e57600080fd5b505afa158015613822573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061384691906158e4565b50925092509250826000146138ab5760405162461bcd60e51b815260206004820152602560248201527f657869744d61726b65743a206765744163636f756e74536e617073686f742066604482015264185a5b195960da1b606482015260840161119a565b80156138c7576138bd600c60026146db565b9695505050505050565b60006138d4873385614da0565b905080156138f4576138e9600e600383614f9f565b979650505050505050565b6001600160a01b038516600090815260096020908152604080832033845290915290205460ff166139265760006138e9565b6001600160a01b03851660009081526009602090815260408083203384528252808320805460ff1916905560088252808320805482518185028101850190935280835291929091908301828280156139a757602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613989575b5050835193945083925060009150505b82811015613a1a57886001600160a01b03168482815181106139e957634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03161415613a0857809150613a1a565b80613a1281615bf1565b9150506139b7565b50818110613a3857634e487b7160e01b600052600160045260246000fd5b33600090815260086020526040902080548190613a5790600190615bda565b81548110613a7557634e487b7160e01b600052603260045260246000fd5b9060005260206000200160009054906101000a90046001600160a01b0316818381548110613ab357634e487b7160e01b600052603260045260246000fd5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555080805480613aff57634e487b7160e01b600052603160045260246000fd5b600082815260209020810160001990810180546001600160a01b03191690550190556040517fe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d90613b53908b90339061592d565b60405180910390a160009b9a5050505050505050505050565b6000546001600160a01b0316331480613b8f5750601e546001600160a01b031633145b613bab5760405162461bcd60e51b815260040161119a90615ad5565b613bb482612443565b80613bd7576001600160a01b038216600090815260186020526040812055613bf2565b426001600160a01b0383166000908152601860205260409020555b6001600160a01b03821660008181526017602052604090819020839055517f85eecf30a97533335aac0dc394c47a6600a5107038a0c967102478d9fe568dab90613c3f9084815260200190565b60405180910390a25050565b600080546001600160a01b0316331480613c6f57506002546001600160a01b031633145b905090565b600080613c7f612434565b90506001600160a01b03811615613dcc576040516370a0823160e01b81526000906001600160a01b038316906370a0823190613cbf903090600401615919565b60206040518083038186803b158015613cd757600080fd5b505afa158015613ceb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d0f91906158cc565b9050600084118015613d215750808411155b15613dca5760405163a9059cbb60e01b81526001600160a01b0383169063a9059cbb90613d549088908890600401615947565b600060405180830381600087803b158015613d6e57600080fd5b505af1158015613d82573d6000803e3d6000fd5b505050507fdc38f4aaaf6d37c4ae151051029aff3215faa3c2aceabbed1c30c5252f139af18585604051613db7929190615947565b60405180910390a16000925050506113c0565b505b509092915050565b6001600160a01b038216600090815260116020908152604080832060199092528220549091613e0242615028565b8354909150600090613e259063ffffffff600160e01b9091048116908416615bda565b90508015610831578215613fcd57601c54600090613f47906001600160a01b031615613ed057601c546040516301cd4a6960e11b81526001600160a01b039091169063039a94d290613e7b908b90600401615919565b60206040518083038186803b158015613e9357600080fd5b505afa158015613ea7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ecb91906158cc565b613f41565b876001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b158015613f0957600080fd5b505afa158015613f1d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f4191906158cc565b87615074565b90506000613f558584615bbb565b90506000808311613f755760405180602001604052806000815250613f7f565b613f7f8284615095565b604080516020810190915288546001600160e01b03168152909150613fae90613fa890836150ca565b516150f3565b87546001600160e01b0319166001600160e01b03919091161787555050505b835463ffffffff8316600160e01b026001600160e01b03909116178455505050505050565b6001600160a01b0383811660009081526011602090815260408083205460138352818420948716845293909152902080546001600160e01b0390921690819055908015801561403f575060015b1561404b575050505050565b6000604051806020016040528083856140649190615bda565b9052905082821461083157601c54600090614190906001600160a01b03161561410e57601c54604051632777dd6960e11b81526001600160a01b0390911690634eefbad2906140b9908b908b9060040161592d565b60206040518083038186803b1580156140d157600080fd5b505afa1580156140e5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061410991906158cc565b61418a565b6040516395dd919360e01b81526001600160a01b038916906395dd91939061413a908a90600401615919565b60206040518083038186803b15801561415257600080fd5b505afa158015614166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418a91906158cc565b86615074565b9050600061419e828461513f565b6001600160a01b038816600090815260146020526040812054919250906141c6908390615b83565b6001600160a01b03808a16600081815260146020526040908190208490555192935091908b16907f140436893bf94456b060bcba4ad537b98c6bea5fb2f5badef8904e98bbd78bd79061421c9086908b90615b0c565b60405180910390a3505050505050505050565b6000546001600160a01b03163314806142525750600b546001600160a01b031633145b61429e5760405162461bcd60e51b815260206004820152601d60248201527f6f6e6c7920706175736520677561726469616e20616e642061646d696e000000604482015260640161119a565b565b60008060006142ad6151b3565b6001600160a01b03881660009081526008602090815260408083208054825181850281018501909352808352849383018282801561431457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116142f6575b5050505050905060005b81518110156145a457600082828151811061434957634e487b7160e01b600052603260045260246000fd5b60200260200101519050806001600160a01b031663c37f68e28d6040518263ffffffff1660e01b815260040161437f9190615919565b60806040518083038186803b15801561439757600080fd5b505afa1580156143ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143cf91906158e4565b608089015260608801526040870152935083156143fb57600f6000809750975097505050505050611961565b60408051602080820183526001600160a01b038085166000908152600a835284902060010154835260c0890192909252825190810183526080880151815260e088015260048054925163fc57d4df60e01b8152929091169163fc57d4df9161446591859101615919565b60206040518083038186803b15801561447d57600080fd5b505afa158015614491573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144b591906158cc565b60a086018190526144d557600d6000809750975097505050505050611961565b604080516020810190915260a0860151815261010086015260c085015160e086015161450f9161450491614e70565b866101000151614e70565b61012086018190526040860151865161452992919061515e565b85526101008501516060860151602087015161454692919061515e565b60208601526001600160a01b03818116908c161415614591576145738561012001518b876020015161515e565b6020860181905261010086015161458b918b9061515e565b60208601525b508061459c81615bf1565b91505061431e565b506020830151835111156145ca5750506020810151905160009450039150829050611961565b5050805160209091015160009450849350039050611961565b6001600160a01b0382166000908152600a602052604081205460ff1661460b575060096113c0565b6001600160a01b0380841660009081526009602090815260408083209386168352929052205460ff1615614641575060006113c0565b6001600160a01b0380841660008181526009602090815260408083209487168352938152838220805460ff191660019081179091556008825284832080549182018155835291200180546001600160a01b0319169091179055517f3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5906146ca908590859061592d565b60405180910390a150600092915050565b6000600080516020615c5c83398151915283601181111561470c57634e487b7160e01b600052602160045260246000fd5b83601381111561472c57634e487b7160e01b600052602160045260246000fd5b600060405161473d93929190615b1a565b60405180910390a1826011811115611ab657634e487b7160e01b600052602160045260246000fd5b6001600160a01b0381166000908152601060209081526040808320601a909252822054909161479342615028565b83549091506000906147b69063ffffffff600160e01b9091048116908416615bda565b9050801561055157821561494e57601c546000906001600160a01b03161561485d57601c54604051631e1932fb60e01b81526001600160a01b0390911690631e1932fb90614808908990600401615919565b60206040518083038186803b15801561482057600080fd5b505afa158015614834573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061485891906158cc565b6148ce565b856001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561489657600080fd5b505afa1580156148aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148ce91906158cc565b905060006148dc8584615bbb565b905060008083116148fc5760405180602001604052806000815250614906565b6149068284615095565b604080516020810190915288546001600160e01b0316815290915061492f90613fa890836150ca565b87546001600160e01b0319166001600160e01b03919091161787555050505b835463ffffffff8316600160e01b026001600160e01b039091161784555050505050565b6001600160a01b0382811660009081526010602090815260408083205460128352818420948616845293909152902080546001600160e01b039092169081905590801580156149bf575060015b156149ca5750505050565b6000604051806020016040528083856149e39190615bda565b9052601c549091506000906001600160a01b031615614a8357601c546040516301693b2560e41b81526001600160a01b0390911690631693b25090614a2e908990899060040161592d565b60206040518083038186803b158015614a4657600080fd5b505afa158015614a5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614a7e91906158cc565b614aff565b6040516370a0823160e01b81526001600160a01b038716906370a0823190614aaf908890600401615919565b60206040518083038186803b158015614ac757600080fd5b505afa158015614adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614aff91906158cc565b9050828414610831576000614b14828461513f565b6001600160a01b03871660009081526014602052604081205491925090614b3c908390615b83565b6001600160a01b03808916600081815260146020526040908190208490555192935091908a16907f1b63b3b859428434d99569695315fa35918c5a2bce714acb147f4c3019808bdd90614b929086908b90615b0c565b60405180910390a35050505050505050565b6000611ab6614bb38484615178565b61519b565b6001600160a01b0383166000908152600a602052604090205460ff16614c1c5760405162461bcd60e51b81526020600482015260196024820152780c159256081b585c9ad95d081a5cc81b9bdd081b1a5cdd1959603a1b604482015260640161119a565b6001600160a01b0383166000908152601a60205260409020548214614c9a57614c4483614765565b6001600160a01b0383166000818152601a602052604090819020849055517ff720735fa76f84bc32ace1df447c03b6acef6e4b238ff71722ebaa93f9c1cdc090614c919085815260200190565b60405180910390a25b6001600160a01b03831660009081526019602052604090205481146109a45760006040518060200160405280856001600160a01b031663aa5af0fd6040518163ffffffff1660e01b815260040160206040518083038186803b158015614cff57600080fd5b505afa158015614d13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614d3791906158cc565b90529050614d458482613dd4565b6001600160a01b03841660008181526019602052604090819020849055517ff386f4abe5d857960f167a80b4dbb954055f412561afb755f97406c675be98c390614d929085815260200190565b60405180910390a250505050565b6001600160a01b0383166000908152600a602052604081205460ff16614dc75760096119e8565b6001600160a01b0380851660009081526009602090815260408083209387168352929052205460ff16614dfb5760006119e8565b600080614e0b85878660006142a0565b91935090915060009050826011811115614e3557634e487b7160e01b600052602160045260246000fd5b14614e6357816011811115614e5a57634e487b7160e01b600052602160045260246000fd5b92505050611ab6565b80156129f0576004614e5a565b614e7861521d565b6040518060200160405280670de0b6b3a764000084600001518660000151614ea09190615bbb565b614eaa9190615b9b565b90529392505050565b614ebb61521d565b60405180602001604052808360000151670de0b6b3a76400008660000151614ea09190615bbb565b6000614eee42615028565b6001600160a01b03831660009081526010602090815260408083206011909252909120815492935090916001600160e01b0316614f445781546001600160e01b0319166a0c097ce7bc90715b34b9f160241b1782555b80546001600160e01b0316614f725780546001600160e01b0319166a0c097ce7bc90715b34b9f160241b1781555b805463ffffffff909316600160e01b026001600160e01b0393841681179091558154909216909117905550565b6000600080516020615c5c833981519152846011811115614fd057634e487b7160e01b600052602160045260246000fd5b846013811115614ff057634e487b7160e01b600052602160045260246000fd5b8460405161500093929190615b1a565b60405180910390a1836011811115611ab357634e487b7160e01b600052602160045260246000fd5b600063ffffffff8211156150705760405162461bcd60e51b815260206004820152600f60248201526e736166653332206f766572666c6f7760881b604482015260640161119a565b5090565b805160009061508b670de0b6b3a764000085615bbb565b611ab69190615b9b565b6040805160208101909152600081526040518060200160405280836a0c097ce7bc90715b34b9f160241b86614ea09190615bbb565b6040805160208101909152600081526040805160208101909152825184518291614eaa91615b83565b60006001600160e01b038211156150705760405162461bcd60e51b815260206004820152601060248201526f73616665323234206f766572666c6f7760801b604482015260640161119a565b80516000906a0c097ce7bc90715b34b9f160241b9061508b9085615bbb565b60008161516e614bb38686615178565b611ab39190615b83565b61518061521d565b6040518060200160405280838560000151614eaa9190615bbb565b80516000906113c090670de0b6b3a764000090615b9b565b6040518061014001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016151f161521d565b81526020016151fe61521d565b815260200161520b61521d565b815260200161521861521d565b905290565b6040518060200160405280600081525090565b600082601f830112615240578081fd5b8135602061525561525083615b60565b615b30565b80838252828201915082860187848660051b8901011115615274578586fd5b855b8581101561529b57813561528981615c38565b84529284019290840190600101615276565b5090979650505050505050565b60008083601f8401126152b9578182fd5b5081356001600160401b038111156152cf578182fd5b6020830191508360208260051b85010111156152ea57600080fd5b9250929050565b600082601f830112615301578081fd5b8135602061531161525083615b60565b80838252828201915082860187848660051b8901011115615330578586fd5b855b8581101561529b57813561534581615c38565b84529284019290840190600101615332565b600082601f830112615367578081fd5b8135602061537761525083615b60565b80838252828201915082860187848660051b8901011115615396578586fd5b855b8581101561529b57813584529284019290840190600101615398565b6000602082840312156153c5578081fd5b8135611ab681615c38565b6000602082840312156153e1578081fd5b8151611ab681615c38565b600080604083850312156153fe578081fd5b823561540981615c38565b9150602083013561541981615c38565b809150509250929050565b600080600080600060a0868803121561543b578081fd5b853561544681615c38565b9450602086013561545681615c38565b9350604086013561546681615c38565b9250606086013561547681615c38565b949793965091946080013592915050565b60008060008060008060c0878903121561549f578081fd5b86356154aa81615c38565b955060208701356154ba81615c38565b945060408701356154ca81615c38565b935060608701356154da81615c38565b9598949750929560808101359460a0909101359350915050565b60008060008060808587031215615509578182fd5b843561551481615c38565b9350602085013561552481615c38565b9250604085013561553481615c38565b9396929550929360600135925050565b600080600080600060a0868803121561555b578283fd5b853561556681615c38565b9450602086013561557681615c38565b9350604086013561558681615c38565b94979396509394606081013594506080013592915050565b6000806000606084860312156155b2578081fd5b83356155bd81615c38565b925060208401356155cd81615c38565b929592945050506040919091013590565b600080600080608085870312156155f3578182fd5b84356155fe81615c38565b9350602085013561560e81615c38565b93969395505050506040820135916060013590565b60008060408385031215615635578182fd5b823561564081615c38565b915060208301356001600160401b0381111561565a578182fd5b615666858286016152f1565b9150509250929050565b60008060408385031215615682578182fd5b823561568d81615c38565b9150602083013561541981615c4d565b600080604083850312156153fe578182fd5b600080604083850312156156c1578182fd5b82356156cc81615c38565b946020939093013593505050565b6000602082840312156156eb578081fd5b81356001600160401b03811115615700578182fd5b6116bf84828501615230565b60008060008060808587031215615721578182fd5b84356001600160401b0380821115615737578384fd5b61574388838901615230565b95506020870135915080821115615758578384fd5b50615765878288016152f1565b935050604085013561577681615c4d565b9150606085013561578681615c4d565b939692955090935050565b6000806000606084860312156157a5578081fd5b83356001600160401b03808211156157bb578283fd5b6157c787838801615230565b945060208601359150808211156157dc578283fd5b6157e887838801615357565b935060408601359150808211156157fd578283fd5b5061580a86828701615357565b9150509250925092565b60008060008060408587031215615829578182fd5b84356001600160401b038082111561583f578384fd5b61584b888389016152a8565b90965094506020870135915080821115615863578384fd5b50615870878288016152a8565b95989497509550505050565b60006020828403121561588d578081fd5b8135611ab681615c4d565b6000602082840312156158a9578081fd5b8151611ab681615c4d565b6000602082840312156158c5578081fd5b5035919050565b6000602082840312156158dd578081fd5b5051919050565b600080600080608085870312156158f9578182fd5b505082516020840151604085015160609095015191969095509092509050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03929092168252602082015260400190565b6020808252825182820181905260009190848201906040850190845b818110156159a15783516001600160a01b03168352928401929184019160010161597c565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156159a1578351835292840192918401916001016159c9565b6020808252601790820152761b9bc81e995c9bc81859191c995cdcc8185b1b1bddd959604a1b604082015260600190565b6020808252600f908201526e15925608185b1c9958591e481cd95d608a1b604082015260600190565b6020808252601690820152756f6e6c792061646d696e2063616e20756e706175736560501b604082015260600190565b6020808252601590820152741b585c9ad95d081b5d5cdd081899481b1a5cdd1959605a1b604082015260600190565b6020808252601f908201527f63616e6e6f742070617573653a206d61726b6574206e6f74206c697374656400604082015260600190565b6020808252601f908201527f6f6e6c792061646d696e2063616e207365742072657761726420737065656400604082015260600190565b918252602082015260400190565b9283526020830191909152604082015260600190565b604051601f8201601f191681016001600160401b0381118282101715615b5857615b58615c22565b604052919050565b60006001600160401b03821115615b7957615b79615c22565b5060051b60200190565b60008219821115615b9657615b96615c0c565b500190565b600082615bb657634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615615bd557615bd5615c0c565b500290565b600082821015615bec57615bec615c0c565b500390565b6000600019821415615c0557615c05615c0c565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461155957600080fd5b801515811461155957600080fdfe45b96fe442630264581b197e84bbada861235052c5a1aadfff9ea4e40a969aa027ee2b767de43e8b48d1666d8c451a273fbf857e760fda6e95d0d582f68534bc31abd22c3c8d5f4796be5f21843042b53e07dcda713c78eb2751d9bd12aaba8671aec636243f9709bb0007ae15e9afb8150ab01716d75fd7573be5cc096e03b0a2646970667358221220ffd80756bd14a8457538acc2832870150b9bcc272fc0e30abfd22dc328b5fe8564736f6c63430008040033

Block Transaction Gas Used Reward
view all blocks sequenced

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
[ 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.