Contract 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f872

 
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x4064d4ad8db2db95501ac483902c3048f0e98a9179e867734fe635de5f10e990Initialize2391022021-09-01 11:23:07523 days 11 hrs agodForce: Deployer 2 IN  0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH0.000939057167 ETH
0x8996816e1389a2860346ca7441945b43823df6f8d5737261a277cd1849e6f9870x608060402391002021-09-01 11:23:07523 days 11 hrs agodForce: Deployer 2 IN  Create: Controller0 ETH0.18271354663 ETH
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xf87218562d214d3f9cb85b81639fd2765eec5d0c56dbff99dc44da3f455e80fd78822142022-03-14 9:53:54329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xf87218562d214d3f9cb85b81639fd2765eec5d0c56dbff99dc44da3f455e80fd78822142022-03-14 9:53:54329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xf87218562d214d3f9cb85b81639fd2765eec5d0c56dbff99dc44da3f455e80fd78822142022-03-14 9:53:54329 days 13 hrs ago dForce: iUSDT Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xf87218562d214d3f9cb85b81639fd2765eec5d0c56dbff99dc44da3f455e80fd78822142022-03-14 9:53:54329 days 13 hrs ago dForce: iUSDT Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x1112486a52108669742d088ad85913d1b178a25714196b4146af7a5cafdc278f78821152022-03-14 9:50:07329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x1112486a52108669742d088ad85913d1b178a25714196b4146af7a5cafdc278f78821152022-03-14 9:50:07329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x1112486a52108669742d088ad85913d1b178a25714196b4146af7a5cafdc278f78821152022-03-14 9:50:07329 days 13 hrs ago dForce: iUSDC Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x1112486a52108669742d088ad85913d1b178a25714196b4146af7a5cafdc278f78821152022-03-14 9:50:07329 days 13 hrs ago dForce: iUSDC Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x487d7032e80346197a56c15dc9c2f7f8f13b0a87ecf2f6174bac29c50c2520b978820892022-03-14 9:48:51329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x487d7032e80346197a56c15dc9c2f7f8f13b0a87ecf2f6174bac29c50c2520b978820892022-03-14 9:48:51329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x487d7032e80346197a56c15dc9c2f7f8f13b0a87ecf2f6174bac29c50c2520b978820892022-03-14 9:48:51329 days 13 hrs ago 0xee338313f022caee84034253174fa562495dcc15 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x487d7032e80346197a56c15dc9c2f7f8f13b0a87ecf2f6174bac29c50c2520b978820892022-03-14 9:48:51329 days 13 hrs ago 0xee338313f022caee84034253174fa562495dcc15 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xa3ffa6619b492c1d3a0b41ef8c1166d038bb5783e1e4cfed01e925e2edc0af7278819252022-03-14 9:43:32329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xa3ffa6619b492c1d3a0b41ef8c1166d038bb5783e1e4cfed01e925e2edc0af7278819252022-03-14 9:43:32329 days 13 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xa3ffa6619b492c1d3a0b41ef8c1166d038bb5783e1e4cfed01e925e2edc0af7278819252022-03-14 9:43:32329 days 13 hrs ago 0xee338313f022caee84034253174fa562495dcc15 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xa3ffa6619b492c1d3a0b41ef8c1166d038bb5783e1e4cfed01e925e2edc0af7278819252022-03-14 9:43:32329 days 13 hrs ago 0xee338313f022caee84034253174fa562495dcc15 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x0565e8f9626d3e967c4397af2b2b6c507070e7cd0131f10440b9a31a7027d09578794392022-03-14 8:14:57329 days 14 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x0565e8f9626d3e967c4397af2b2b6c507070e7cd0131f10440b9a31a7027d09578794392022-03-14 8:14:57329 days 14 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x0565e8f9626d3e967c4397af2b2b6c507070e7cd0131f10440b9a31a7027d09578794392022-03-14 8:14:57329 days 14 hrs ago 0xd3204e4189becd9cd957046a8e4a643437ee0acc 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x0565e8f9626d3e967c4397af2b2b6c507070e7cd0131f10440b9a31a7027d09578794392022-03-14 8:14:57329 days 14 hrs ago 0xd3204e4189becd9cd957046a8e4a643437ee0acc 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x67668a8b18a402c813d822acc7fbba30b7e1e9ac34664e247bf7839a80878f5778785102022-03-14 7:46:08329 days 15 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x67668a8b18a402c813d822acc7fbba30b7e1e9ac34664e247bf7839a80878f5778785102022-03-14 7:46:08329 days 15 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x67668a8b18a402c813d822acc7fbba30b7e1e9ac34664e247bf7839a80878f5778785102022-03-14 7:46:08329 days 15 hrs ago dForce: iUSDT Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0x67668a8b18a402c813d822acc7fbba30b7e1e9ac34664e247bf7839a80878f5778785102022-03-14 7:46:08329 days 15 hrs ago dForce: iUSDT Token 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
0xd4bd8a618211caf9c0d658f862f2b329ff055eb100262ca217b10e2a7b0997de78785002022-03-14 7:44:03329 days 15 hrs ago 0xf45e2ae152384d50d4e9b08b8a1f65f0d96786c3 0xbfb0b7caec2a133f5661b7ffb3f40b1cfa99f8720 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Controller

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 14 : Controller.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol";

import "./interface/IControllerInterface.sol";
import "./interface/IPriceOracle.sol";
import "./interface/IiToken.sol";
import "./interface/IRewardDistributor.sol";

import "./library/Initializable.sol";
import "./library/Ownable.sol";
import "./library/SafeRatioMath.sol";

/**
 * @title dForce's lending controller Contract
 * @author dForce
 */
contract Controller is Initializable, Ownable, IControllerInterface {
    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
    using SafeRatioMath for uint256;
    using SafeMathUpgradeable for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    /// @dev EnumerableSet of all iTokens
    EnumerableSetUpgradeable.AddressSet internal iTokens;

    struct Market {
        /*
         *  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 in [0, 0.9], and stored as a mantissa.
         */
        uint256 collateralFactorMantissa;
        /*
         *  Multiplier representing the most one can borrow the asset.
         *  For instance, 0.5 to allow borrowing this asset 50% * collateral value * collateralFactor.
         *  When calculating equity, 0.5 with 100 borrow balance will produce 200 borrow value
         *  Must be between (0, 1], and stored as a mantissa.
         */
        uint256 borrowFactorMantissa;
        /*
         *  The borrow capacity of the asset, will be checked in beforeBorrow()
         *  -1 means there is no limit on the capacity
         *  0 means the asset can not be borrowed any more
         */
        uint256 borrowCapacity;
        /*
         *  The supply capacity of the asset, will be checked in beforeMint()
         *  -1 means there is no limit on the capacity
         *  0 means the asset can not be supplied any more
         */
        uint256 supplyCapacity;
        // Whether market's mint is paused
        bool mintPaused;
        // Whether market's redeem is paused
        bool redeemPaused;
        // Whether market's borrow is paused
        bool borrowPaused;
    }

    /// @notice Mapping of iTokens to corresponding markets
    mapping(address => Market) public markets;

    struct AccountData {
        // Account's collateral assets
        EnumerableSetUpgradeable.AddressSet collaterals;
        // Account's borrowed assets
        EnumerableSetUpgradeable.AddressSet borrowed;
    }

    /// @dev Mapping of accounts' data, including collateral and borrowed assets
    mapping(address => AccountData) internal accountsData;

    /**
     * @notice Oracle to query the price of a given asset
     */
    address public priceOracle;

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

    // 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

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

    // liquidationIncentiveMantissa must be no less than this value
    uint256 internal constant liquidationIncentiveMinMantissa = 1.0e18; // 1.0

    // liquidationIncentiveMantissa must be no greater than this value
    uint256 internal constant liquidationIncentiveMaxMantissa = 1.5e18; // 1.5

    // collateralFactorMantissa must not exceed this value
    uint256 internal constant collateralFactorMaxMantissa = 1e18; // 1.0

    // borrowFactorMantissa must not exceed this value
    uint256 internal constant borrowFactorMaxMantissa = 1e18; // 1.0

    /**
     * @notice Guardian who can pause mint/borrow/liquidate/transfer in case of emergency
     */
    address public pauseGuardian;

    /// @notice whether global transfer is paused
    bool public transferPaused;

    /// @notice whether global seize is paused
    bool public seizePaused;

    /**
     * @notice the address of reward distributor
     */
    address public rewardDistributor;

    /**
     * @dev Check if called by owner or pauseGuardian, and only owner can unpause
     */
    modifier checkPauser(bool _paused) {
        require(
            msg.sender == owner || (msg.sender == pauseGuardian && _paused),
            "Only owner and guardian can pause and only owner can unpause"
        );

        _;
    }

    /**
     * @notice Initializes the contract.
     */
    function initialize() external initializer {
        __Ownable_init();
    }

    /*********************************/
    /******** Security Check *********/
    /*********************************/

    /**
     * @notice Ensure this is a Controller contract.
     */
    function isController() external view override returns (bool) {
        return true;
    }

    /*********************************/
    /******** Admin Operations *******/
    /*********************************/

    /**
     * @notice Admin function to add iToken into supported markets
     * Checks if the iToken already exsits
     * Will `revert()` if any check fails
     * @param _iToken The _iToken to add
     * @param _collateralFactor The _collateralFactor of _iToken
     * @param _borrowFactor The _borrowFactor of _iToken
     * @param _supplyCapacity The _supplyCapacity of _iToken
     * @param _distributionFactor The _distributionFactor of _iToken
     */
    function _addMarket(
        address _iToken,
        uint256 _collateralFactor,
        uint256 _borrowFactor,
        uint256 _supplyCapacity,
        uint256 _borrowCapacity,
        uint256 _distributionFactor
    ) external override onlyOwner {
        require(IiToken(_iToken).isSupported(), "Token is not supported");

        // Market must not have been listed, EnumerableSet.add() will return false if it exsits
        require(iTokens.add(_iToken), "Token has already been listed");

        require(
            _collateralFactor <= collateralFactorMaxMantissa,
            "Collateral factor invalid"
        );

        require(
            _borrowFactor > 0 && _borrowFactor <= borrowFactorMaxMantissa,
            "Borrow factor invalid"
        );

        // Its value will be taken into account when calculate account equity
        // Check if the price is available for the calculation
        require(
            IPriceOracle(priceOracle).getUnderlyingPrice(_iToken) != 0,
            "Underlying price is unavailable"
        );

        markets[_iToken] = Market({
            collateralFactorMantissa: _collateralFactor,
            borrowFactorMantissa: _borrowFactor,
            borrowCapacity: _borrowCapacity,
            supplyCapacity: _supplyCapacity,
            mintPaused: false,
            redeemPaused: false,
            borrowPaused: false
        });

        IRewardDistributor(rewardDistributor)._addRecipient(
            _iToken,
            _distributionFactor
        );

        emit MarketAdded(
            _iToken,
            _collateralFactor,
            _borrowFactor,
            _supplyCapacity,
            _borrowCapacity,
            _distributionFactor
        );
    }

    /**
     * @notice Sets price oracle
     * @dev Admin function to set price oracle
     * @param _newOracle New oracle contract
     */
    function _setPriceOracle(address _newOracle) external override onlyOwner {
        address _oldOracle = priceOracle;
        require(
            _newOracle != address(0) && _newOracle != _oldOracle,
            "Oracle address invalid"
        );
        priceOracle = _newOracle;
        emit NewPriceOracle(_oldOracle, _newOracle);
    }

    /**
     * @notice Sets the closeFactor used when liquidating borrows
     * @dev Admin function to set closeFactor
     * @param _newCloseFactorMantissa New close factor, scaled by 1e18
     */
    function _setCloseFactor(uint256 _newCloseFactorMantissa)
        external
        override
        onlyOwner
    {
        require(
            _newCloseFactorMantissa >= closeFactorMinMantissa &&
                _newCloseFactorMantissa <= closeFactorMaxMantissa,
            "Close factor invalid"
        );

        uint256 _oldCloseFactorMantissa = closeFactorMantissa;
        closeFactorMantissa = _newCloseFactorMantissa;
        emit NewCloseFactor(_oldCloseFactorMantissa, _newCloseFactorMantissa);
    }

    /**
     * @notice Sets liquidationIncentive
     * @dev Admin function to set liquidationIncentive
     * @param _newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18
     */
    function _setLiquidationIncentive(uint256 _newLiquidationIncentiveMantissa)
        external
        override
        onlyOwner
    {
        require(
            _newLiquidationIncentiveMantissa >=
                liquidationIncentiveMinMantissa &&
                _newLiquidationIncentiveMantissa <=
                liquidationIncentiveMaxMantissa,
            "Liquidation incentive invalid"
        );

        uint256 _oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;
        liquidationIncentiveMantissa = _newLiquidationIncentiveMantissa;

        emit NewLiquidationIncentive(
            _oldLiquidationIncentiveMantissa,
            _newLiquidationIncentiveMantissa
        );
    }

    /**
     * @notice Sets the collateralFactor for a iToken
     * @dev Admin function to set collateralFactor for a iToken
     * @param _iToken The token to set the factor on
     * @param _newCollateralFactorMantissa The new collateral factor, scaled by 1e18
     */
    function _setCollateralFactor(
        address _iToken,
        uint256 _newCollateralFactorMantissa
    ) external override onlyOwner {
        _checkiTokenListed(_iToken);

        require(
            _newCollateralFactorMantissa <= collateralFactorMaxMantissa,
            "Collateral factor invalid"
        );

        // Its value will be taken into account when calculate account equity
        // Check if the price is available for the calculation
        require(
            IPriceOracle(priceOracle).getUnderlyingPrice(_iToken) != 0,
            "Failed to set collateral factor, underlying price is unavailable"
        );

        Market storage _market = markets[_iToken];
        uint256 _oldCollateralFactorMantissa = _market.collateralFactorMantissa;
        _market.collateralFactorMantissa = _newCollateralFactorMantissa;

        emit NewCollateralFactor(
            _iToken,
            _oldCollateralFactorMantissa,
            _newCollateralFactorMantissa
        );
    }

    /**
     * @notice Sets the borrowFactor for a iToken
     * @dev Admin function to set borrowFactor for a iToken
     * @param _iToken The token to set the factor on
     * @param _newBorrowFactorMantissa The new borrow factor, scaled by 1e18
     */
    function _setBorrowFactor(address _iToken, uint256 _newBorrowFactorMantissa)
        external
        override
        onlyOwner
    {
        _checkiTokenListed(_iToken);

        require(
            _newBorrowFactorMantissa > 0 &&
                _newBorrowFactorMantissa <= borrowFactorMaxMantissa,
            "Borrow factor invalid"
        );

        // Its value will be taken into account when calculate account equity
        // Check if the price is available for the calculation
        require(
            IPriceOracle(priceOracle).getUnderlyingPrice(_iToken) != 0,
            "Failed to set borrow factor, underlying price is unavailable"
        );

        Market storage _market = markets[_iToken];
        uint256 _oldBorrowFactorMantissa = _market.borrowFactorMantissa;
        _market.borrowFactorMantissa = _newBorrowFactorMantissa;

        emit NewBorrowFactor(
            _iToken,
            _oldBorrowFactorMantissa,
            _newBorrowFactorMantissa
        );
    }

    /**
     * @notice Sets the borrowCapacity for a iToken
     * @dev Admin function to set borrowCapacity for a iToken
     * @param _iToken The token to set the capacity on
     * @param _newBorrowCapacity The new borrow capacity
     */
    function _setBorrowCapacity(address _iToken, uint256 _newBorrowCapacity)
        external
        override
        onlyOwner
    {
        _checkiTokenListed(_iToken);

        Market storage _market = markets[_iToken];
        uint256 oldBorrowCapacity = _market.borrowCapacity;
        _market.borrowCapacity = _newBorrowCapacity;

        emit NewBorrowCapacity(_iToken, oldBorrowCapacity, _newBorrowCapacity);
    }

    /**
     * @notice Sets the supplyCapacity for a iToken
     * @dev Admin function to set supplyCapacity for a iToken
     * @param _iToken The token to set the capacity on
     * @param _newSupplyCapacity The new supply capacity
     */
    function _setSupplyCapacity(address _iToken, uint256 _newSupplyCapacity)
        external
        override
        onlyOwner
    {
        _checkiTokenListed(_iToken);

        Market storage _market = markets[_iToken];
        uint256 oldSupplyCapacity = _market.supplyCapacity;
        _market.supplyCapacity = _newSupplyCapacity;

        emit NewSupplyCapacity(_iToken, oldSupplyCapacity, _newSupplyCapacity);
    }

    /**
     * @notice Sets the pauseGuardian
     * @dev Admin function to set pauseGuardian
     * @param _newPauseGuardian The new pause guardian
     */
    function _setPauseGuardian(address _newPauseGuardian)
        external
        override
        onlyOwner
    {
        address _oldPauseGuardian = pauseGuardian;

        require(
            _newPauseGuardian != address(0) &&
                _newPauseGuardian != _oldPauseGuardian,
            "Pause guardian address invalid"
        );

        pauseGuardian = _newPauseGuardian;

        emit NewPauseGuardian(_oldPauseGuardian, _newPauseGuardian);
    }

    /**
     * @notice pause/unpause mint() for all iTokens
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setAllMintPaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        EnumerableSetUpgradeable.AddressSet storage _iTokens = iTokens;
        uint256 _len = _iTokens.length();

        for (uint256 i = 0; i < _len; i++) {
            _setMintPausedInternal(_iTokens.at(i), _paused);
        }
    }

    /**
     * @notice pause/unpause mint() for the iToken
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _iToken The iToken to pause/unpause
     * @param _paused whether to pause or unpause
     */
    function _setMintPaused(address _iToken, bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _checkiTokenListed(_iToken);

        _setMintPausedInternal(_iToken, _paused);
    }

    function _setMintPausedInternal(address _iToken, bool _paused) internal {
        markets[_iToken].mintPaused = _paused;
        emit MintPaused(_iToken, _paused);
    }

    /**
     * @notice pause/unpause redeem() for all iTokens
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setAllRedeemPaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        EnumerableSetUpgradeable.AddressSet storage _iTokens = iTokens;
        uint256 _len = _iTokens.length();

        for (uint256 i = 0; i < _len; i++) {
            _setRedeemPausedInternal(_iTokens.at(i), _paused);
        }
    }

    /**
     * @notice pause/unpause redeem() for the iToken
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _iToken The iToken to pause/unpause
     * @param _paused whether to pause or unpause
     */
    function _setRedeemPaused(address _iToken, bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _checkiTokenListed(_iToken);

        _setRedeemPausedInternal(_iToken, _paused);
    }

    function _setRedeemPausedInternal(address _iToken, bool _paused) internal {
        markets[_iToken].redeemPaused = _paused;
        emit RedeemPaused(_iToken, _paused);
    }

    /**
     * @notice pause/unpause borrow() for all iTokens
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setAllBorrowPaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        EnumerableSetUpgradeable.AddressSet storage _iTokens = iTokens;
        uint256 _len = _iTokens.length();

        for (uint256 i = 0; i < _len; i++) {
            _setBorrowPausedInternal(_iTokens.at(i), _paused);
        }
    }

    /**
     * @notice pause/unpause borrow() for the iToken
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _iToken The iToken to pause/unpause
     * @param _paused whether to pause or unpause
     */
    function _setBorrowPaused(address _iToken, bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _checkiTokenListed(_iToken);

        _setBorrowPausedInternal(_iToken, _paused);
    }

    function _setBorrowPausedInternal(address _iToken, bool _paused) internal {
        markets[_iToken].borrowPaused = _paused;
        emit BorrowPaused(_iToken, _paused);
    }

    /**
     * @notice pause/unpause global transfer()
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setTransferPaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _setTransferPausedInternal(_paused);
    }

    function _setTransferPausedInternal(bool _paused) internal {
        transferPaused = _paused;
        emit TransferPaused(_paused);
    }

    /**
     * @notice pause/unpause global seize()
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setSeizePaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _setSeizePausedInternal(_paused);
    }

    function _setSeizePausedInternal(bool _paused) internal {
        seizePaused = _paused;
        emit SeizePaused(_paused);
    }

    /**
     * @notice pause/unpause all actions iToken, including mint/redeem/borrow
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setiTokenPaused(address _iToken, bool _paused)
        external
        override
        checkPauser(_paused)
    {
        _checkiTokenListed(_iToken);

        _setiTokenPausedInternal(_iToken, _paused);
    }

    function _setiTokenPausedInternal(address _iToken, bool _paused) internal {
        Market storage _market = markets[_iToken];

        _market.mintPaused = _paused;
        emit MintPaused(_iToken, _paused);

        _market.redeemPaused = _paused;
        emit RedeemPaused(_iToken, _paused);

        _market.borrowPaused = _paused;
        emit BorrowPaused(_iToken, _paused);
    }

    /**
     * @notice pause/unpause entire protocol, including mint/redeem/borrow/seize/transfer
     * @dev Admin function, only owner and pauseGuardian can call this
     * @param _paused whether to pause or unpause
     */
    function _setProtocolPaused(bool _paused)
        external
        override
        checkPauser(_paused)
    {
        EnumerableSetUpgradeable.AddressSet storage _iTokens = iTokens;
        uint256 _len = _iTokens.length();

        for (uint256 i = 0; i < _len; i++) {
            address _iToken = _iTokens.at(i);

            _setiTokenPausedInternal(_iToken, _paused);
        }

        _setTransferPausedInternal(_paused);
        _setSeizePausedInternal(_paused);
    }

    /**
     * @notice Sets Reward Distributor
     * @dev Admin function to set reward distributor
     * @param _newRewardDistributor new reward distributor
     */
    function _setRewardDistributor(address _newRewardDistributor)
        external
        override
        onlyOwner
    {
        address _oldRewardDistributor = rewardDistributor;
        require(
            _newRewardDistributor != address(0) &&
                _newRewardDistributor != _oldRewardDistributor,
            "Reward Distributor address invalid"
        );

        rewardDistributor = _newRewardDistributor;
        emit NewRewardDistributor(_oldRewardDistributor, _newRewardDistributor);
    }

    /*********************************/
    /******** Poclicy Hooks **********/
    /*********************************/

    /**
     * @notice Hook function before iToken `mint()`
     * Checks if the account should be allowed to mint the given iToken
     * Will `revert()` if any check fails
     * @param _iToken The iToken to check the mint against
     * @param _minter The account which would get the minted tokens
     * @param _mintAmount The amount of underlying being minted to iToken
     */
    function beforeMint(
        address _iToken,
        address _minter,
        uint256 _mintAmount
    ) external override {
        _checkiTokenListed(_iToken);

        Market storage _market = markets[_iToken];
        require(!_market.mintPaused, "Token mint has been paused");

        // Check the iToken's supply capacity, -1 means no limit
        uint256 _totalSupplyUnderlying =
            IERC20Upgradeable(_iToken).totalSupply().rmul(
                IiToken(_iToken).exchangeRateStored()
            );
        require(
            _totalSupplyUnderlying.add(_mintAmount) <= _market.supplyCapacity,
            "Token supply capacity reached"
        );

        // Update the Reward Distribution Supply state and distribute reward to suppplier
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            false
        );
        IRewardDistributor(rewardDistributor).updateReward(
            _iToken,
            _minter,
            false
        );
    }

    /**
     * @notice Hook function after iToken `mint()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken being minted
     * @param _minter The account which would get the minted tokens
     * @param _mintAmount The amount of underlying being minted to iToken
     * @param _mintedAmount The amount of iToken being minted
     */
    function afterMint(
        address _iToken,
        address _minter,
        uint256 _mintAmount,
        uint256 _mintedAmount
    ) external override {
        _iToken;
        _minter;
        _mintAmount;
        _mintedAmount;
    }

    /**
     * @notice Hook function before iToken `redeem()`
     * Checks if the account should be allowed to redeem the given iToken
     * Will `revert()` if any check fails
     * @param _iToken The iToken to check the redeem against
     * @param _redeemer The account which would redeem iToken
     * @param _redeemAmount The amount of iToken to redeem
     */
    function beforeRedeem(
        address _iToken,
        address _redeemer,
        uint256 _redeemAmount
    ) external override {
        // _redeemAllowed below will check whether _iToken is listed

        require(!markets[_iToken].redeemPaused, "Token redeem has been paused");

        _redeemAllowed(_iToken, _redeemer, _redeemAmount);

        // Update the Reward Distribution Supply state and distribute reward to suppplier
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            false
        );
        IRewardDistributor(rewardDistributor).updateReward(
            _iToken,
            _redeemer,
            false
        );
    }

    /**
     * @notice Hook function after iToken `redeem()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken being redeemed
     * @param _redeemer The account which redeemed iToken
     * @param _redeemAmount  The amount of iToken being redeemed
     * @param _redeemedUnderlying The amount of underlying being redeemed
     */
    function afterRedeem(
        address _iToken,
        address _redeemer,
        uint256 _redeemAmount,
        uint256 _redeemedUnderlying
    ) external override {
        _iToken;
        _redeemer;
        _redeemAmount;
        _redeemedUnderlying;
    }

    /**
     * @notice Hook function before iToken `borrow()`
     * Checks if the account should be allowed to borrow the given iToken
     * Will `revert()` if any check fails
     * @param _iToken The iToken to check the borrow against
     * @param _borrower The account which would borrow iToken
     * @param _borrowAmount The amount of underlying to borrow
     */
    function beforeBorrow(
        address _iToken,
        address _borrower,
        uint256 _borrowAmount
    ) external override {
        _checkiTokenListed(_iToken);

        Market storage _market = markets[_iToken];
        require(!_market.borrowPaused, "Token borrow has been paused");

        if (!hasBorrowed(_borrower, _iToken)) {
            // Unlike collaterals, borrowed asset can only be added by iToken,
            // rather than enabled by user directly.
            require(msg.sender == _iToken, "sender must be iToken");

            // Have checked _iToken is listed, just add it
            _addToBorrowed(_borrower, _iToken);
        }

        // Check borrower's equity
        (, uint256 _shortfall, , ) =
            calcAccountEquityWithEffect(_borrower, _iToken, 0, _borrowAmount);

        require(_shortfall == 0, "Account has some shortfall");

        // Check the iToken's borrow capacity, -1 means no limit
        uint256 _totalBorrows = IiToken(_iToken).totalBorrows();
        require(
            _totalBorrows.add(_borrowAmount) <= _market.borrowCapacity,
            "Token borrow capacity reached"
        );

        // Update the Reward Distribution Borrow state and distribute reward to borrower
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            true
        );
        IRewardDistributor(rewardDistributor).updateReward(
            _iToken,
            _borrower,
            true
        );
    }

    /**
     * @notice Hook function after iToken `borrow()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken being borrewd
     * @param _borrower The account which borrowed iToken
     * @param _borrowedAmount  The amount of underlying being borrowed
     */
    function afterBorrow(
        address _iToken,
        address _borrower,
        uint256 _borrowedAmount
    ) external override {
        _iToken;
        _borrower;
        _borrowedAmount;
    }

    /**
     * @notice Hook function before iToken `repayBorrow()`
     * Checks if the account should be allowed to repay the given iToken
     * for the borrower. Will `revert()` if any check fails
     * @param _iToken The iToken to verify the repay against
     * @param _payer The account which would repay iToken
     * @param _borrower The account which has borrowed
     * @param _repayAmount The amount of underlying to repay
     */
    function beforeRepayBorrow(
        address _iToken,
        address _payer,
        address _borrower,
        uint256 _repayAmount
    ) external override {
        _checkiTokenListed(_iToken);

        // Update the Reward Distribution Borrow state and distribute reward to borrower
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            true
        );
        IRewardDistributor(rewardDistributor).updateReward(
            _iToken,
            _borrower,
            true
        );

        _payer;
        _repayAmount;
    }

    /**
     * @notice Hook function after iToken `repayBorrow()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken being repaid
     * @param _payer The account which would repay
     * @param _borrower The account which has borrowed
     * @param _repayAmount  The amount of underlying being repaied
     */
    function afterRepayBorrow(
        address _iToken,
        address _payer,
        address _borrower,
        uint256 _repayAmount
    ) external override {
        _checkiTokenListed(_iToken);

        // Remove _iToken from borrowed list if new borrow balance is 0
        if (IiToken(_iToken).borrowBalanceStored(_borrower) == 0) {
            // Only allow called by iToken as we are going to remove this token from borrower's borrowed list
            require(msg.sender == _iToken, "sender must be iToken");

            // Have checked _iToken is listed, just remove it
            _removeFromBorrowed(_borrower, _iToken);
        }

        _payer;
        _repayAmount;
    }

    /**
     * @notice Hook function before iToken `liquidateBorrow()`
     * Checks if the account should be allowed to liquidate the given iToken
     * for the borrower. Will `revert()` if any check fails
     * @param _iTokenBorrowed The iToken was borrowed
     * @param _iTokenCollateral The collateral iToken to be liqudate with
     * @param _liquidator The account which would repay the borrowed iToken
     * @param _borrower The account which has borrowed
     * @param _repayAmount The amount of underlying to repay
     */
    function beforeLiquidateBorrow(
        address _iTokenBorrowed,
        address _iTokenCollateral,
        address _liquidator,
        address _borrower,
        uint256 _repayAmount
    ) external override {
        // Tokens must have been listed
        require(
            iTokens.contains(_iTokenBorrowed) &&
                iTokens.contains(_iTokenCollateral),
            "Tokens have not been listed"
        );

        (, uint256 _shortfall, , ) = calcAccountEquity(_borrower);

        require(_shortfall > 0, "Account does not have shortfall");

        // Only allowed to repay the borrow balance's close factor
        uint256 _borrowBalance =
            IiToken(_iTokenBorrowed).borrowBalanceStored(_borrower);
        uint256 _maxRepay = _borrowBalance.rmul(closeFactorMantissa);

        require(_repayAmount <= _maxRepay, "Repay exceeds max repay allowed");

        _liquidator;
    }

    /**
     * @notice Hook function after iToken `liquidateBorrow()`
     * Will `revert()` if any operation fails
     * @param _iTokenBorrowed The iToken was borrowed
     * @param _iTokenCollateral The collateral iToken to be seized
     * @param _liquidator The account which would repay and seize
     * @param _borrower The account which has borrowed
     * @param _repaidAmount  The amount of underlying being repaied
     * @param _seizedAmount  The amount of collateral being seized
     */
    function afterLiquidateBorrow(
        address _iTokenBorrowed,
        address _iTokenCollateral,
        address _liquidator,
        address _borrower,
        uint256 _repaidAmount,
        uint256 _seizedAmount
    ) external override {
        _iTokenBorrowed;
        _iTokenCollateral;
        _liquidator;
        _borrower;
        _repaidAmount;
        _seizedAmount;

        // Unlike repayBorrow, liquidateBorrow does not allow to repay all borrow balance
        // No need to check whether should remove from borrowed asset list
    }

    /**
     * @notice Hook function before iToken `seize()`
     * Checks if the liquidator should be allowed to seize the collateral iToken
     * Will `revert()` if any check fails
     * @param _iTokenCollateral The collateral iToken to be seize
     * @param _iTokenBorrowed The iToken was borrowed
     * @param _liquidator The account which has repaid the borrowed iToken
     * @param _borrower The account which has borrowed
     * @param _seizeAmount The amount of collateral iToken to seize
     */
    function beforeSeize(
        address _iTokenCollateral,
        address _iTokenBorrowed,
        address _liquidator,
        address _borrower,
        uint256 _seizeAmount
    ) external override {
        require(!seizePaused, "Seize has been paused");

        // Markets must have been listed
        require(
            iTokens.contains(_iTokenBorrowed) &&
                iTokens.contains(_iTokenCollateral),
            "Tokens have not been listed"
        );

        // Sanity Check the controllers
        require(
            IiToken(_iTokenBorrowed).controller() ==
                IiToken(_iTokenCollateral).controller(),
            "Controller mismatch between Borrowed and Collateral"
        );

        // Update the Reward Distribution Supply state on collateral
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iTokenCollateral,
            false
        );

        // Update reward of liquidator and borrower on collateral
        IRewardDistributor(rewardDistributor).updateReward(
            _iTokenCollateral,
            _liquidator,
            false
        );
        IRewardDistributor(rewardDistributor).updateReward(
            _iTokenCollateral,
            _borrower,
            false
        );

        _seizeAmount;
    }

    /**
     * @notice Hook function after iToken `seize()`
     * Will `revert()` if any operation fails
     * @param _iTokenCollateral The collateral iToken to be seized
     * @param _iTokenBorrowed The iToken was borrowed
     * @param _liquidator The account which has repaid and seized
     * @param _borrower The account which has borrowed
     * @param _seizedAmount  The amount of collateral being seized
     */
    function afterSeize(
        address _iTokenCollateral,
        address _iTokenBorrowed,
        address _liquidator,
        address _borrower,
        uint256 _seizedAmount
    ) external override {
        _iTokenBorrowed;
        _iTokenCollateral;
        _liquidator;
        _borrower;
        _seizedAmount;
    }

    /**
     * @notice Hook function before iToken `transfer()`
     * Checks if the transfer should be allowed
     * Will `revert()` if any check fails
     * @param _iToken The iToken to be transfered
     * @param _from The account to be transfered from
     * @param _to The account to be transfered to
     * @param _amount The amount to be transfered
     */
    function beforeTransfer(
        address _iToken,
        address _from,
        address _to,
        uint256 _amount
    ) external override {
        // _redeemAllowed below will check whether _iToken is listed

        require(!transferPaused, "Transfer has been paused");

        // Check account equity with this amount to decide whether the transfer is allowed
        _redeemAllowed(_iToken, _from, _amount);

        // Update the Reward Distribution supply state
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            false
        );

        // Update reward of from and to
        IRewardDistributor(rewardDistributor).updateReward(
            _iToken,
            _from,
            false
        );
        IRewardDistributor(rewardDistributor).updateReward(_iToken, _to, false);
    }

    /**
     * @notice Hook function after iToken `transfer()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken was transfered
     * @param _from The account was transfer from
     * @param _to The account was transfer to
     * @param _amount  The amount was transfered
     */
    function afterTransfer(
        address _iToken,
        address _from,
        address _to,
        uint256 _amount
    ) external override {
        _iToken;
        _from;
        _to;
        _amount;
    }

    /**
     * @notice Hook function before iToken `flashloan()`
     * Checks if the flashloan should be allowed
     * Will `revert()` if any check fails
     * @param _iToken The iToken to be flashloaned
     * @param _to The account flashloaned transfer to
     * @param _amount The amount to be flashloaned
     */
    function beforeFlashloan(
        address _iToken,
        address _to,
        uint256 _amount
    ) external override {
        // Flashloan share the same pause state with borrow
        require(!markets[_iToken].borrowPaused, "Token borrow has been paused");

        _checkiTokenListed(_iToken);

        _to;
        _amount;

        // Update the Reward Distribution Borrow state
        IRewardDistributor(rewardDistributor).updateDistributionState(
            _iToken,
            true
        );
    }

    /**
     * @notice Hook function after iToken `flashloan()`
     * Will `revert()` if any operation fails
     * @param _iToken The iToken was flashloaned
     * @param _to The account flashloan transfer to
     * @param _amount  The amount was flashloaned
     */
    function afterFlashloan(
        address _iToken,
        address _to,
        uint256 _amount
    ) external override {
        _iToken;
        _to;
        _amount;
    }

    /*********************************/
    /***** Internal  Functions *******/
    /*********************************/

    function _redeemAllowed(
        address _iToken,
        address _redeemer,
        uint256 _amount
    ) private view {
        _checkiTokenListed(_iToken);

        // No need to check liquidity if _redeemer has not used _iToken as collateral
        if (!accountsData[_redeemer].collaterals.contains(_iToken)) {
            return;
        }

        (, uint256 _shortfall, , ) =
            calcAccountEquityWithEffect(_redeemer, _iToken, _amount, 0);

        require(_shortfall == 0, "Account has some shortfall");
    }

    /**
     * @dev Check if _iToken is listed
     */
    function _checkiTokenListed(address _iToken) private view {
        require(iTokens.contains(_iToken), "Token has not been listed");
    }

    /*********************************/
    /** Account equity calculation ***/
    /*********************************/

    /**
     * @notice Calculates current account equity
     * @param _account The account to query equity of
     * @return account equity, shortfall, collateral value, borrowed value.
     */
    function calcAccountEquity(address _account)
        public
        view
        override
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        return calcAccountEquityWithEffect(_account, address(0), 0, 0);
    }

    /**
     * @dev Local vars for avoiding stack-depth limits in calculating account liquidity.
     *  Note that `iTokenBalance` is the number of iTokens the account owns in the collateral,
     *  whereas `borrowBalance` is the amount of underlying that the account has borrowed.
     */
    struct AccountEquityLocalVars {
        uint256 sumCollateral;
        uint256 sumBorrowed;
        uint256 iTokenBalance;
        uint256 borrowBalance;
        uint256 exchangeRateMantissa;
        uint256 underlyingPrice;
        uint256 collateralValue;
        uint256 borrowValue;
    }

    /**
     * @notice Calculates current account equity plus some token and amount to effect
     * @param _account The account to query equity of
     * @param _tokenToEffect The token address to add some additional redeeem/borrow
     * @param _redeemAmount The additional amount to redeem
     * @param _borrowAmount The additional amount to borrow
     * @return account euqity, shortfall, collateral value, borrowed value plus the effect.
     */
    function calcAccountEquityWithEffect(
        address _account,
        address _tokenToEffect,
        uint256 _redeemAmount,
        uint256 _borrowAmount
    )
        internal
        view
        virtual
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        )
    {
        AccountEquityLocalVars memory _local;
        AccountData storage _accountData = accountsData[_account];

        // Calculate value of all collaterals
        // collateralValuePerToken = underlyingPrice * exchangeRate * collateralFactor
        // collateralValue = balance * collateralValuePerToken
        // sumCollateral += collateralValue
        uint256 _len = _accountData.collaterals.length();
        for (uint256 i = 0; i < _len; i++) {
            IiToken _token = IiToken(_accountData.collaterals.at(i));

            _local.iTokenBalance = IERC20Upgradeable(address(_token)).balanceOf(
                _account
            );
            _local.exchangeRateMantissa = _token.exchangeRateStored();

            if (_tokenToEffect == address(_token) && _redeemAmount > 0) {
                _local.iTokenBalance = _local.iTokenBalance.sub(_redeemAmount);
            }

            _local.underlyingPrice = IPriceOracle(priceOracle)
                .getUnderlyingPrice(address(_token));

            require(
                _local.underlyingPrice != 0,
                "Invalid price to calculate account equity"
            );

            _local.collateralValue = _local
                .iTokenBalance
                .mul(_local.underlyingPrice)
                .rmul(_local.exchangeRateMantissa)
                .rmul(markets[address(_token)].collateralFactorMantissa);

            _local.sumCollateral = _local.sumCollateral.add(
                _local.collateralValue
            );
        }

        // Calculate all borrowed value
        // borrowValue = underlyingPrice * underlyingBorrowed / borrowFactor
        // sumBorrowed += borrowValue
        _len = _accountData.borrowed.length();
        for (uint256 i = 0; i < _len; i++) {
            IiToken _token = IiToken(_accountData.borrowed.at(i));

            _local.borrowBalance = _token.borrowBalanceStored(_account);

            if (_tokenToEffect == address(_token) && _borrowAmount > 0) {
                _local.borrowBalance = _local.borrowBalance.add(_borrowAmount);
            }

            _local.underlyingPrice = IPriceOracle(priceOracle)
                .getUnderlyingPrice(address(_token));

            require(
                _local.underlyingPrice != 0,
                "Invalid price to calculate account equity"
            );

            // borrowFactorMantissa can not be set to 0
            _local.borrowValue = _local
                .borrowBalance
                .mul(_local.underlyingPrice)
                .rdiv(markets[address(_token)].borrowFactorMantissa);

            _local.sumBorrowed = _local.sumBorrowed.add(_local.borrowValue);
        }

        // Should never underflow
        return
            _local.sumCollateral > _local.sumBorrowed
                ? (
                    _local.sumCollateral - _local.sumBorrowed,
                    uint256(0),
                    _local.sumCollateral,
                    _local.sumBorrowed
                )
                : (
                    uint256(0),
                    _local.sumBorrowed - _local.sumCollateral,
                    _local.sumCollateral,
                    _local.sumBorrowed
                );
    }

    /**
     * @notice Calculate amount of collateral iToken to seize after repaying an underlying amount
     * @dev Used in liquidation
     * @param _iTokenBorrowed The iToken was borrowed
     * @param _iTokenCollateral The collateral iToken to be seized
     * @param _actualRepayAmount The amount of underlying token liquidator has repaied
     * @return _seizedTokenCollateral amount of iTokenCollateral tokens to be seized
     */
    function liquidateCalculateSeizeTokens(
        address _iTokenBorrowed,
        address _iTokenCollateral,
        uint256 _actualRepayAmount
    ) external view virtual override returns (uint256 _seizedTokenCollateral) {
        /* Read oracle prices for borrowed and collateral assets */
        uint256 _priceBorrowed =
            IPriceOracle(priceOracle).getUnderlyingPrice(_iTokenBorrowed);
        uint256 _priceCollateral =
            IPriceOracle(priceOracle).getUnderlyingPrice(_iTokenCollateral);
        require(
            _priceBorrowed != 0 && _priceCollateral != 0,
            "Borrowed or Collateral asset price is invalid"
        );

        uint256 _valueRepayPlusIncentive =
            _actualRepayAmount.mul(_priceBorrowed).rmul(
                liquidationIncentiveMantissa
            );

        // Use stored value here as it is view function
        uint256 _exchangeRateMantissa =
            IiToken(_iTokenCollateral).exchangeRateStored();

        // seizedTokenCollateral = valueRepayPlusIncentive / valuePerTokenCollateral
        // valuePerTokenCollateral = exchangeRateMantissa * priceCollateral
        _seizedTokenCollateral = _valueRepayPlusIncentive
            .rdiv(_exchangeRateMantissa)
            .div(_priceCollateral);
    }

    /*********************************/
    /*** Account Markets Operation ***/
    /*********************************/

    /**
     * @notice Returns the markets list the account has entered
     * @param _account The address of the account to query
     * @return _accountCollaterals The markets list the account has entered
     */
    function getEnteredMarkets(address _account)
        external
        view
        override
        returns (address[] memory _accountCollaterals)
    {
        AccountData storage _accountData = accountsData[_account];

        uint256 _len = _accountData.collaterals.length();
        _accountCollaterals = new address[](_len);
        for (uint256 i = 0; i < _len; i++) {
            _accountCollaterals[i] = _accountData.collaterals.at(i);
        }
    }

    /**
     * @notice Add markets to `msg.sender`'s markets list for liquidity calculations
     * @param _iTokens The list of addresses of the iToken markets to be entered
     * @return _results Success indicator for whether each corresponding market was entered
     */
    function enterMarkets(address[] calldata _iTokens)
        external
        override
        returns (bool[] memory _results)
    {
        uint256 _len = _iTokens.length;

        _results = new bool[](_len);
        for (uint256 i = 0; i < _len; i++) {
            _results[i] = _enterMarket(_iTokens[i], msg.sender);
        }
    }

    /**
     * @notice Add the market to the account's markets list for liquidity calculations
     * @param _iToken The market to enter
     * @param _account The address of the account to modify
     * @return True if entered successfully, false for non-listed market or other errors
     */
    function _enterMarket(address _iToken, address _account)
        internal
        returns (bool)
    {
        // Market not listed, skip it
        if (!iTokens.contains(_iToken)) {
            return false;
        }

        // add() will return false if iToken is in account's market list
        if (accountsData[_account].collaterals.add(_iToken)) {
            emit MarketEntered(_iToken, _account);
        }

        return true;
    }

    /**
     * @notice Only expect to be called by iToken contract.
     * @dev Add the market to the account's markets list for liquidity calculations
     * @param _account The address of the account to modify
     */
    function enterMarketFromiToken(address _account)
        external
        override
    {
        require(
            _enterMarket(msg.sender, _account),
            "enterMarketFromiToken: Only can be called by a supported iToken!"
        );
    }

    /**
     * @notice Returns whether the given account has entered the market
     * @param _account The address of the account to check
     * @param _iToken The iToken to check against
     * @return True if the account has entered the market, otherwise false.
     */
    function hasEnteredMarket(address _account, address _iToken)
        external
        view
        override
        returns (bool)
    {
        return accountsData[_account].collaterals.contains(_iToken);
    }

    /**
     * @notice Remove markets from `msg.sender`'s collaterals for liquidity calculations
     * @param _iTokens The list of addresses of the iToken to exit
     * @return _results Success indicators for whether each corresponding market was exited
     */
    function exitMarkets(address[] calldata _iTokens)
        external
        override
        returns (bool[] memory _results)
    {
        uint256 _len = _iTokens.length;
        _results = new bool[](_len);
        for (uint256 i = 0; i < _len; i++) {
            _results[i] = _exitMarket(_iTokens[i], msg.sender);
        }
    }

    /**
     * @notice Remove the market to the account's markets list for liquidity calculations
     * @param _iToken The market to exit
     * @param _account The address of the account to modify
     * @return True if exit successfully, false for non-listed market or other errors
     */
    function _exitMarket(address _iToken, address _account)
        internal
        returns (bool)
    {
        // Market not listed, skip it
        if (!iTokens.contains(_iToken)) {
            return true;
        }

        // Account has not entered this market, skip it
        if (!accountsData[_account].collaterals.contains(_iToken)) {
            return true;
        }

        // Get the iToken balance
        uint256 _balance = IERC20Upgradeable(_iToken).balanceOf(_account);

        // Check account's equity if all balance are redeemed
        // which means iToken can be removed from collaterals
        _redeemAllowed(_iToken, _account, _balance);

        // Have checked account has entered market before
        accountsData[_account].collaterals.remove(_iToken);

        emit MarketExited(_iToken, _account);

        return true;
    }

    /**
     * @notice Returns the asset list the account has borrowed
     * @param _account The address of the account to query
     * @return _borrowedAssets The asset list the account has borrowed
     */
    function getBorrowedAssets(address _account)
        external
        view
        override
        returns (address[] memory _borrowedAssets)
    {
        AccountData storage _accountData = accountsData[_account];

        uint256 _len = _accountData.borrowed.length();
        _borrowedAssets = new address[](_len);
        for (uint256 i = 0; i < _len; i++) {
            _borrowedAssets[i] = _accountData.borrowed.at(i);
        }
    }

    /**
     * @notice Add the market to the account's borrowed list for equity calculations
     * @param _iToken The iToken of underlying to borrow
     * @param _account The address of the account to modify
     */
    function _addToBorrowed(address _account, address _iToken) internal {
        // add() will return false if iToken is in account's market list
        if (accountsData[_account].borrowed.add(_iToken)) {
            emit BorrowedAdded(_iToken, _account);
        }
    }

    /**
     * @notice Returns whether the given account has borrowed the given iToken
     * @param _account The address of the account to check
     * @param _iToken The iToken to check against
     * @return True if the account has borrowed the iToken, otherwise false.
     */
    function hasBorrowed(address _account, address _iToken)
        public
        view
        override
        returns (bool)
    {
        return accountsData[_account].borrowed.contains(_iToken);
    }

    /**
     * @notice Remove the iToken from the account's borrowed list
     * @param _iToken The iToken to remove
     * @param _account The address of the account to modify
     */
    function _removeFromBorrowed(address _account, address _iToken) internal {
        // remove() will return false if iToken does not exist in account's borrowed list
        if (accountsData[_account].borrowed.remove(_iToken)) {
            emit BorrowedRemoved(_iToken, _account);
        }
    }

    /*********************************/
    /****** General Information ******/
    /*********************************/

    /**
     * @notice Return all of the iTokens
     * @return _alliTokens The list of iToken addresses
     */
    function getAlliTokens()
        public
        view
        override
        returns (address[] memory _alliTokens)
    {
        EnumerableSetUpgradeable.AddressSet storage _iTokens = iTokens;

        uint256 _len = _iTokens.length();
        _alliTokens = new address[](_len);
        for (uint256 i = 0; i < _len; i++) {
            _alliTokens[i] = _iTokens.at(i);
        }
    }

    /**
     * @notice Check whether a iToken is listed in controller
     * @param _iToken The iToken to check for
     * @return true if the iToken is listed otherwise false
     */
    function hasiToken(address _iToken) public view override returns (bool) {
        return iTokens.contains(_iToken);
    }
}

File 2 of 14 : SafeERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.sol";

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

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

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

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

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

    function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

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

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

File 3 of 14 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

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

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

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

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

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

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

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

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

File 4 of 14 : EnumerableSetUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSetUpgradeable {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(value)));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(value)));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(value)));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint256(_at(set._inner, index)));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

File 5 of 14 : IControllerInterface.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IControllerAdminInterface {
    /// @notice Emitted when an admin supports a market
    event MarketAdded(
        address iToken,
        uint256 collateralFactor,
        uint256 borrowFactor,
        uint256 supplyCapacity,
        uint256 borrowCapacity,
        uint256 distributionFactor
    );

    function _addMarket(
        address _iToken,
        uint256 _collateralFactor,
        uint256 _borrowFactor,
        uint256 _supplyCapacity,
        uint256 _borrowCapacity,
        uint256 _distributionFactor
    ) external;

    /// @notice Emitted when new price oracle is set
    event NewPriceOracle(address oldPriceOracle, address newPriceOracle);

    function _setPriceOracle(address newOracle) external;

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

    function _setCloseFactor(uint256 newCloseFactorMantissa) external;

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

    function _setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa)
        external;

    /// @notice Emitted when iToken's collateral factor is changed by admin
    event NewCollateralFactor(
        address iToken,
        uint256 oldCollateralFactorMantissa,
        uint256 newCollateralFactorMantissa
    );

    function _setCollateralFactor(
        address iToken,
        uint256 newCollateralFactorMantissa
    ) external;

    /// @notice Emitted when iToken's borrow factor is changed by admin
    event NewBorrowFactor(
        address iToken,
        uint256 oldBorrowFactorMantissa,
        uint256 newBorrowFactorMantissa
    );

    function _setBorrowFactor(address iToken, uint256 newBorrowFactorMantissa)
        external;

    /// @notice Emitted when iToken's borrow capacity is changed by admin
    event NewBorrowCapacity(
        address iToken,
        uint256 oldBorrowCapacity,
        uint256 newBorrowCapacity
    );

    function _setBorrowCapacity(address iToken, uint256 newBorrowCapacity)
        external;

    /// @notice Emitted when iToken's supply capacity is changed by admin
    event NewSupplyCapacity(
        address iToken,
        uint256 oldSupplyCapacity,
        uint256 newSupplyCapacity
    );

    function _setSupplyCapacity(address iToken, uint256 newSupplyCapacity)
        external;

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

    function _setPauseGuardian(address newPauseGuardian) external;

    /// @notice Emitted when mint is paused/unpaused by admin or pause guardian
    event MintPaused(address iToken, bool paused);

    function _setMintPaused(address iToken, bool paused) external;

    function _setAllMintPaused(bool paused) external;

    /// @notice Emitted when redeem is paused/unpaused by admin or pause guardian
    event RedeemPaused(address iToken, bool paused);

    function _setRedeemPaused(address iToken, bool paused) external;

    function _setAllRedeemPaused(bool paused) external;

    /// @notice Emitted when borrow is paused/unpaused by admin or pause guardian
    event BorrowPaused(address iToken, bool paused);

    function _setBorrowPaused(address iToken, bool paused) external;

    function _setAllBorrowPaused(bool paused) external;

    /// @notice Emitted when transfer is paused/unpaused by admin or pause guardian
    event TransferPaused(bool paused);

    function _setTransferPaused(bool paused) external;

    /// @notice Emitted when seize is paused/unpaused by admin or pause guardian
    event SeizePaused(bool paused);

    function _setSeizePaused(bool paused) external;

    function _setiTokenPaused(address iToken, bool paused) external;

    function _setProtocolPaused(bool paused) external;

    event NewRewardDistributor(
        address oldRewardDistributor,
        address _newRewardDistributor
    );

    function _setRewardDistributor(address _newRewardDistributor) external;
}

interface IControllerPolicyInterface {
    function beforeMint(
        address iToken,
        address account,
        uint256 mintAmount
    ) external;

    function afterMint(
        address iToken,
        address minter,
        uint256 mintAmount,
        uint256 mintedAmount
    ) external;

    function beforeRedeem(
        address iToken,
        address redeemer,
        uint256 redeemAmount
    ) external;

    function afterRedeem(
        address iToken,
        address redeemer,
        uint256 redeemAmount,
        uint256 redeemedAmount
    ) external;

    function beforeBorrow(
        address iToken,
        address borrower,
        uint256 borrowAmount
    ) external;

    function afterBorrow(
        address iToken,
        address borrower,
        uint256 borrowedAmount
    ) external;

    function beforeRepayBorrow(
        address iToken,
        address payer,
        address borrower,
        uint256 repayAmount
    ) external;

    function afterRepayBorrow(
        address iToken,
        address payer,
        address borrower,
        uint256 repayAmount
    ) external;

    function beforeLiquidateBorrow(
        address iTokenBorrowed,
        address iTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount
    ) external;

    function afterLiquidateBorrow(
        address iTokenBorrowed,
        address iTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repaidAmount,
        uint256 seizedAmount
    ) external;

    function beforeSeize(
        address iTokenBorrowed,
        address iTokenCollateral,
        address liquidator,
        address borrower,
        uint256 seizeAmount
    ) external;

    function afterSeize(
        address iTokenBorrowed,
        address iTokenCollateral,
        address liquidator,
        address borrower,
        uint256 seizedAmount
    ) external;

    function beforeTransfer(
        address iToken,
        address from,
        address to,
        uint256 amount
    ) external;

    function afterTransfer(
        address iToken,
        address from,
        address to,
        uint256 amount
    ) external;

    function beforeFlashloan(
        address iToken,
        address to,
        uint256 amount
    ) external;

    function afterFlashloan(
        address iToken,
        address to,
        uint256 amount
    ) external;
}

interface IControllerAccountEquityInterface {
    function calcAccountEquity(address account)
        external
        view
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        );

    function liquidateCalculateSeizeTokens(
        address iTokenBorrowed,
        address iTokenCollateral,
        uint256 actualRepayAmount
    ) external view returns (uint256);
}

interface IControllerAccountInterface {
    function hasEnteredMarket(address account, address iToken)
        external
        view
        returns (bool);

    function getEnteredMarkets(address account)
        external
        view
        returns (address[] memory);

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

    function enterMarkets(address[] calldata iTokens)
        external
        returns (bool[] memory);

        function enterMarketFromiToken(address _account) external;

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

    function exitMarkets(address[] calldata iTokens)
        external
        returns (bool[] memory);

    /// @notice Emitted when an account add a borrow asset
    event BorrowedAdded(address iToken, address account);

    /// @notice Emitted when an account remove a borrow asset
    event BorrowedRemoved(address iToken, address account);

    function hasBorrowed(address account, address iToken)
        external
        view
        returns (bool);

    function getBorrowedAssets(address account)
        external
        view
        returns (address[] memory);
}

interface IControllerInterface is
    IControllerAdminInterface,
    IControllerPolicyInterface,
    IControllerAccountEquityInterface,
    IControllerAccountInterface
{
    /**
     * @notice Security checks when updating the comptroller of a market, always expect to return true.
     */
    function isController() external view returns (bool);

    /**
     * @notice Return all of the iTokens
     * @return The list of iToken addresses
     */
    function getAlliTokens() external view returns (address[] memory);

    /**
     * @notice Check whether a iToken is listed in controller
     * @param _iToken The iToken to check for
     * @return true if the iToken is listed otherwise false
     */
    function hasiToken(address _iToken) external view returns (bool);
}

File 6 of 14 : IPriceOracle.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "./IiToken.sol";

interface IPriceOracle {
    /**
     * @notice Get the underlying price of a iToken asset
     * @param _iToken The iToken to get the underlying price of
     * @return The underlying asset price mantissa (scaled by 1e18).
     *  Zero means the price is unavailable.
     */
    function getUnderlyingPrice(address _iToken)
        external
        view
        returns (uint256);

    /**
     * @notice Get the price of a underlying asset
     * @param _iToken The iToken to get the underlying price of
     * @return The underlying asset price mantissa (scaled by 1e18).
     *  Zero means the price is unavailable and whether the price is valid.
     */
    function getUnderlyingPriceAndStatus(address _iToken)
        external
        view
        returns (uint256, bool);
}

File 7 of 14 : IiToken.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "./IInterestRateModelInterface.sol";
import "./IControllerInterface.sol";

interface IiToken {
    function isSupported() external returns (bool);

    function isiToken() external returns (bool);

    //----------------------------------
    //********* User Interface *********
    //----------------------------------
    function mint(address recipient, uint256 mintAmount) external;

    function mintForSelfAndEnterMarket(uint256 mintAmount) external;

    function redeem(address from, uint256 redeemTokens) external;

    function redeemUnderlying(address from, uint256 redeemAmount) external;

    function borrow(uint256 borrowAmount) external;

    function repayBorrow(uint256 repayAmount) external;

    function repayBorrowBehalf(address borrower, uint256 repayAmount) external;

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address iTokenCollateral
    ) external;

    function flashloan(
        address recipient,
        uint256 loanAmount,
        bytes memory data
    ) external;

    function seize(
        address _liquidator,
        address _borrower,
        uint256 _seizeTokens
    ) external;

    function updateInterest() external returns (bool);

    function controller() external view returns (address);

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function totalBorrowsCurrent() external returns (uint256);

    function totalBorrows() external view returns (uint256);

    function borrowBalanceCurrent(address _user) external returns (uint256);

    function borrowBalanceStored(address _user) external view returns (uint256);

    function borrowIndex() external view returns (uint256);

    function getAccountSnapshot(address _account)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );

    function borrowRatePerBlock() external view returns (uint256);

    function supplyRatePerBlock() external view returns (uint256);

    function getCash() external view returns (uint256);

    //----------------------------------
    //********* Owner Actions **********
    //----------------------------------

    function _setNewReserveRatio(uint256 _newReserveRatio) external;

    function _setNewFlashloanFeeRatio(uint256 _newFlashloanFeeRatio) external;

    function _setNewProtocolFeeRatio(uint256 _newProtocolFeeRatio) external;

    function _setController(IControllerInterface _newController) external;

    function _setInterestRateModel(
        IInterestRateModelInterface _newInterestRateModel
    ) external;

    function _withdrawReserves(uint256 _withdrawAmount) external;
}

File 8 of 14 : IRewardDistributor.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

interface IRewardDistributor {
    function _setRewardToken(address newRewardToken) external;

    /// @notice Emitted reward token address is changed by admin
    event NewRewardToken(address oldRewardToken, address newRewardToken);

    function _addRecipient(address _iToken, uint256 _distributionFactor)
        external;

    event NewRecipient(address iToken, uint256 distributionFactor);

    /// @notice Emitted when mint is paused/unpaused by admin
    event Paused(bool paused);

    function _pause() external;

    function _unpause(uint256 _borrowSpeed, uint256 _supplySpeed) external;

    /// @notice Emitted when Global Distribution speed for both supply and borrow are updated
    event GlobalDistributionSpeedsUpdated(
        uint256 borrowSpeed,
        uint256 supplySpeed
    );

    function _setGlobalDistributionSpeeds(
        uint256 borrowSpeed,
        uint256 supplySpeed
    ) external;

    /// @notice Emitted when iToken's Distribution speed is updated
    event DistributionSpeedsUpdated(
        address iToken,
        uint256 borrowSpeed,
        uint256 supplySpeed
    );

    function updateDistributionSpeed() external;

    /// @notice Emitted when iToken's Distribution factor is changed by admin
    event NewDistributionFactor(
        address iToken,
        uint256 oldDistributionFactorMantissa,
        uint256 newDistributionFactorMantissa
    );

    function _setDistributionFactors(
        address[] calldata iToken,
        uint256[] calldata distributionFactors
    ) external;

    function updateDistributionState(address _iToken, bool _isBorrow) external;

    function updateReward(
        address _iToken,
        address _account,
        bool _isBorrow
    ) external;

    function updateRewardBatch(
        address[] memory _holders,
        address[] memory _iTokens
    ) external;

    function claimReward(address[] memory _holders, address[] memory _iTokens)
        external;

    function claimAllReward(address[] memory _holders) external;

    /// @notice Emitted when reward of amount is distributed into account
    event RewardDistributed(
        address iToken,
        address account,
        uint256 amount,
        uint256 accountIndex
    );
}

File 9 of 14 : Initializable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

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

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

        _;

        _initialized = true;
    }
}

File 10 of 14 : Ownable.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {_setPendingOwner} and {_acceptOwner}.
 */
contract Ownable {
    /**
     * @dev Returns the address of the current owner.
     */
    address payable public owner;

    /**
     * @dev Returns the address of the current pending owner.
     */
    address payable public pendingOwner;

    event NewOwner(address indexed previousOwner, address indexed newOwner);
    event NewPendingOwner(
        address indexed oldPendingOwner,
        address indexed newPendingOwner
    );

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner == msg.sender, "onlyOwner: caller is not the owner");
        _;
    }

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal {
        owner = msg.sender;
        emit NewOwner(address(0), msg.sender);
    }

    /**
     * @notice Base on the inputing parameter `newPendingOwner` to check the exact error reason.
     * @dev Transfer contract control to a new owner. The newPendingOwner must call `_acceptOwner` to finish the transfer.
     * @param newPendingOwner New pending owner.
     */
    function _setPendingOwner(address payable newPendingOwner)
        external
        onlyOwner
    {
        require(
            newPendingOwner != address(0) && newPendingOwner != pendingOwner,
            "_setPendingOwner: New owenr can not be zero address and owner has been set!"
        );

        // Gets current owner.
        address oldPendingOwner = pendingOwner;

        // Sets new pending owner.
        pendingOwner = newPendingOwner;

        emit NewPendingOwner(oldPendingOwner, newPendingOwner);
    }

    /**
     * @dev Accepts the admin rights, but only for pendingOwenr.
     */
    function _acceptOwner() external {
        require(
            msg.sender == pendingOwner,
            "_acceptOwner: Only for pending owner!"
        );

        // Gets current values for events.
        address oldOwner = owner;
        address oldPendingOwner = pendingOwner;

        // Set the new contract owner.
        owner = pendingOwner;

        // Clear the pendingOwner.
        pendingOwner = address(0);

        emit NewOwner(oldOwner, owner);
        emit NewPendingOwner(oldPendingOwner, pendingOwner);
    }

    uint256[50] private __gap;
}

File 11 of 14 : SafeRatioMath.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";

library SafeRatioMath {
    using SafeMathUpgradeable for uint256;

    uint256 private constant BASE = 10**18;
    uint256 private constant DOUBLE = 10**36;

    function divup(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.add(y.sub(1)).div(y);
    }

    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.mul(y).div(BASE);
    }

    function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.mul(BASE).div(y);
    }

    function rdivup(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x.mul(BASE).add(y.sub(1)).div(y);
    }

    function tmul(
        uint256 x,
        uint256 y,
        uint256 z
    ) internal pure returns (uint256 result) {
        result = x.mul(y).mul(z).div(DOUBLE);
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 base
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
                case 0 {
                    switch n
                        case 0 {
                            z := base
                        }
                        default {
                            z := 0
                        }
                }
                default {
                    switch mod(n, 2)
                        case 0 {
                            z := base
                        }
                        default {
                            z := x
                        }
                    let half := div(base, 2) // for rounding.

                    for {
                        n := div(n, 2)
                    } n {
                        n := div(n, 2)
                    } {
                        let xx := mul(x, x)
                        if iszero(eq(div(xx, x), x)) {
                            revert(0, 0)
                        }
                        let xxRound := add(xx, half)
                        if lt(xxRound, xx) {
                            revert(0, 0)
                        }
                        x := div(xxRound, base)
                        if mod(n, 2) {
                            let zx := mul(z, x)
                            if and(
                                iszero(iszero(x)),
                                iszero(eq(div(zx, x), z))
                            ) {
                                revert(0, 0)
                            }
                            let zxRound := add(zx, half)
                            if lt(zxRound, zx) {
                                revert(0, 0)
                            }
                            z := div(zxRound, base)
                        }
                    }
                }
        }
    }
}

File 12 of 14 : SafeMathUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMathUpgradeable {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

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

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

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

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

        return c;
    }

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

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

File 13 of 14 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

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

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

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

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 14 of 14 : IInterestRateModelInterface.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.6.12;

/**
 * @title dForce Lending Protocol's InterestRateModel Interface.
 * @author dForce Team.
 */
interface IInterestRateModelInterface {
    function isInterestRateModel() external view returns (bool);

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

    /**
     * @dev Calculates the current supply interest rate per block.
     * @param cash The total amount of cash the market has.
     * @param borrows The total amount of borrows the market has.
     * @param reserves The total amnount of reserves the market has.
     * @param reserveRatio The current reserve factor the market has.
     * @return The supply rate per block (as a percentage, and scaled by 1e18).
     */
    function getSupplyRate(
        uint256 cash,
        uint256 borrows,
        uint256 reserves,
        uint256 reserveRatio
    ) external view returns (uint256);
}

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

Contract ABI

[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"BorrowPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"BorrowedAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"BorrowedRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supplyCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"distributionFactor","type":"uint256"}],"name":"MarketAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MarketEntered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"MarketExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"MintPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldBorrowCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBorrowCapacity","type":"uint256"}],"name":"NewBorrowCapacity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldBorrowFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBorrowFactorMantissa","type":"uint256"}],"name":"NewBorrowFactor","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":"address","name":"iToken","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":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwner","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":true,"internalType":"address","name":"oldPendingOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"NewPendingOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPriceOracle","type":"address"},{"indexed":false,"internalType":"address","name":"newPriceOracle","type":"address"}],"name":"NewPriceOracle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRewardDistributor","type":"address"},{"indexed":false,"internalType":"address","name":"_newRewardDistributor","type":"address"}],"name":"NewRewardDistributor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldSupplyCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newSupplyCapacity","type":"uint256"}],"name":"NewSupplyCapacity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"iToken","type":"address"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"RedeemPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"SeizePaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"TransferPaused","type":"event"},{"inputs":[],"name":"_acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"uint256","name":"_collateralFactor","type":"uint256"},{"internalType":"uint256","name":"_borrowFactor","type":"uint256"},{"internalType":"uint256","name":"_supplyCapacity","type":"uint256"},{"internalType":"uint256","name":"_borrowCapacity","type":"uint256"},{"internalType":"uint256","name":"_distributionFactor","type":"uint256"}],"name":"_addMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setAllBorrowPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setAllMintPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setAllRedeemPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"uint256","name":"_newBorrowCapacity","type":"uint256"}],"name":"_setBorrowCapacity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"uint256","name":"_newBorrowFactorMantissa","type":"uint256"}],"name":"_setBorrowFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setBorrowPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newCloseFactorMantissa","type":"uint256"}],"name":"_setCloseFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"uint256","name":"_newCollateralFactorMantissa","type":"uint256"}],"name":"_setCollateralFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newLiquidationIncentiveMantissa","type":"uint256"}],"name":"_setLiquidationIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setMintPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newPauseGuardian","type":"address"}],"name":"_setPauseGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingOwner","type":"address"}],"name":"_setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOracle","type":"address"}],"name":"_setPriceOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setProtocolPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setRedeemPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newRewardDistributor","type":"address"}],"name":"_setRewardDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setSeizePaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"uint256","name":"_newSupplyCapacity","type":"uint256"}],"name":"_setSupplyCapacity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setTransferPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"bool","name":"_paused","type":"bool"}],"name":"_setiTokenPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_borrowedAmount","type":"uint256"}],"name":"afterBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"afterFlashloan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iTokenBorrowed","type":"address"},{"internalType":"address","name":"_iTokenCollateral","type":"address"},{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_repaidAmount","type":"uint256"},{"internalType":"uint256","name":"_seizedAmount","type":"uint256"}],"name":"afterLiquidateBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_mintAmount","type":"uint256"},{"internalType":"uint256","name":"_mintedAmount","type":"uint256"}],"name":"afterMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_redeemer","type":"address"},{"internalType":"uint256","name":"_redeemAmount","type":"uint256"},{"internalType":"uint256","name":"_redeemedUnderlying","type":"uint256"}],"name":"afterRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_payer","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_repayAmount","type":"uint256"}],"name":"afterRepayBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iTokenCollateral","type":"address"},{"internalType":"address","name":"_iTokenBorrowed","type":"address"},{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_seizedAmount","type":"uint256"}],"name":"afterSeize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"afterTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_borrowAmount","type":"uint256"}],"name":"beforeBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"beforeFlashloan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iTokenBorrowed","type":"address"},{"internalType":"address","name":"_iTokenCollateral","type":"address"},{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_repayAmount","type":"uint256"}],"name":"beforeLiquidateBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_mintAmount","type":"uint256"}],"name":"beforeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_redeemer","type":"address"},{"internalType":"uint256","name":"_redeemAmount","type":"uint256"}],"name":"beforeRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_payer","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_repayAmount","type":"uint256"}],"name":"beforeRepayBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iTokenCollateral","type":"address"},{"internalType":"address","name":"_iTokenBorrowed","type":"address"},{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_seizeAmount","type":"uint256"}],"name":"beforeSeize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"beforeTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"calcAccountEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"closeFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"enterMarketFromiToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_iTokens","type":"address[]"}],"name":"enterMarkets","outputs":[{"internalType":"bool[]","name":"_results","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_iTokens","type":"address[]"}],"name":"exitMarkets","outputs":[{"internalType":"bool[]","name":"_results","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAlliTokens","outputs":[{"internalType":"address[]","name":"_alliTokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getBorrowedAssets","outputs":[{"internalType":"address[]","name":"_borrowedAssets","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getEnteredMarkets","outputs":[{"internalType":"address[]","name":"_accountCollaterals","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_iToken","type":"address"}],"name":"hasBorrowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_iToken","type":"address"}],"name":"hasEnteredMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_iToken","type":"address"}],"name":"hasiToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_iTokenBorrowed","type":"address"},{"internalType":"address","name":"_iTokenCollateral","type":"address"},{"internalType":"uint256","name":"_actualRepayAmount","type":"uint256"}],"name":"liquidateCalculateSeizeTokens","outputs":[{"internalType":"uint256","name":"_seizedTokenCollateral","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationIncentiveMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"uint256","name":"collateralFactorMantissa","type":"uint256"},{"internalType":"uint256","name":"borrowFactorMantissa","type":"uint256"},{"internalType":"uint256","name":"borrowCapacity","type":"uint256"},{"internalType":"uint256","name":"supplyCapacity","type":"uint256"},{"internalType":"bool","name":"mintPaused","type":"bool"},{"internalType":"bool","name":"redeemPaused","type":"bool"},{"internalType":"bool","name":"borrowPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseGuardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"seizePaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"transferPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

608060405234801561001057600080fd5b50614f66806100206000396000f3fe608060405234801561001057600080fd5b50600436106103af5760003560e01c80638ebf6364116101f4578063d4d484731161011a578063e4028eee116100ad578063f079420a1161007c578063f079420a14610d80578063f1575f0814610dbc578063fb2cb34e14610dea578063fc4d33f914610df2576103af565b8063e4028eee14610d2d578063e715d96a14610d59578063e875544614610d78578063efced38514610b21576103af565b8063de65f41b116100e9578063de65f41b14610997578063e29f7f5e14610cca578063e30c397814610ce9578063e33af48a14610cf1576103af565b8063d4d4847314610c0d578063d5b4436014610c3b578063d79287d714610c67578063dbb32d6814610cab576103af565b8063acc2166a11610192578063c488847b11610161578063c488847b14610aeb578063ca498ad614610b21578063cb6db19c14610b57578063d0af413814610b9d576103af565b8063acc2166a14610a11578063af505ad914610a19578063bd8296c214610a4f578063c299823814610a7b576103af565b8063a09cf78b116101ce578063a09cf78b1461094b578063a2ddeb8514610997578063a8f4e9f6146109d3578063ab1777d214610a09576103af565b80638ebf6364146108da5780639142c34c146108f95780639d8cc5c41461091f576103af565b80634fd42e17116102d95780637160a8701161027757806386b841871161024657806386b84187146108125780638ccb720b146108485780638da5cb5b1461086e5780638e8f294b14610876576103af565b80637160a8701461077257806372b4304a146107a05780638129fc1c146107dc5780638283e3e2146107e4576103af565b80635f5af1aa116102b35780635f5af1aa146106d25780635f7bc006146106f857806360a8a931146107445780636e96dfd71461074c576103af565b80634fd42e171461066957806355ee1fe1146106865780635c254d11146106ac576103af565b80632a6a6065116103515780633bcf7ec1116103205780633bcf7ec1146105df5780634428e8621461060d5780634ada90af146106475780634e1647fb14610661576103af565b80632a6a60651461054e5780632b9553ce1461056d5780632d70db78146105a3578063317b0b77146105c2576103af565b80631637eefd1161038d5780631637eefd146104b857806318c882a5146104f457806324a3d622146105225780632630c12f14610546576103af565b8063061682de146103b4578063075aff36146103fc5780630d7f67a214610472575b600080fd5b6103fa600480360360a08110156103ca57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060800135610dfa565b005b6104226004803603602081101561041257600080fd5b50356001600160a01b0316610fbd565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561045e578181015183820152602001610446565b505050509050019250505060405180910390f35b6103fa600480360360a081101561048857600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060800135611076565b6103fa600480360360808110156104ce57600080fd5b506001600160a01b0381358116916020810135821691604082013516906060013561107d565b6103fa6004803603604081101561050a57600080fd5b506001600160a01b0381351690602001351515611164565b61052a6111e9565b604080516001600160a01b039092168252519081900360200190f35b61052a6111f8565b6103fa6004803603602081101561056457600080fd5b50351515611207565b6103fa6004803603606081101561058357600080fd5b506001600160a01b038135811691602081013590911690604001356112c8565b6103fa600480360360208110156105b957600080fd5b503515156115d5565b6103fa600480360360208110156105d857600080fd5b503561164f565b6103fa600480360360408110156105f557600080fd5b506001600160a01b038135169060200135151561174b565b6106336004803603602081101561062357600080fd5b50356001600160a01b03166117cb565b604080519115158252519081900360200190f35b61064f6117de565b60408051918252519081900360200190f35b6106336117e4565b6103fa6004803603602081101561067f57600080fd5b50356117e9565b6103fa6004803603602081101561069c57600080fd5b50356001600160a01b03166118ef565b6103fa600480360360208110156106c257600080fd5b50356001600160a01b0316611a18565b6103fa600480360360208110156106e857600080fd5b50356001600160a01b0316611b32565b6103fa600480360360c081101561070e57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060808101359060a00135611c62565b610422611c6a565b6103fa6004803603602081101561076257600080fd5b50356001600160a01b0316611d06565b6103fa6004803603604081101561078857600080fd5b506001600160a01b0381351690602001351515611e07565b6103fa600480360360808110156107b657600080fd5b506001600160a01b03813581169160208101358216916040820135169060600135611e87565b6103fa612026565b610633600480360360408110156107fa57600080fd5b506001600160a01b038135811691602001351661207f565b6103fa6004803603606081101561082857600080fd5b506001600160a01b038135811691602081013590911690604001356120ab565b6104226004803603602081101561085e57600080fd5b50356001600160a01b031661234a565b61052a6123f5565b61089c6004803603602081101561088c57600080fd5b50356001600160a01b0316612409565b60408051978852602088019690965286860194909452606086019290925215156080850152151560a0840152151560c0830152519081900360e00190f35b6103fa600480360360208110156108f057600080fd5b5035151561244e565b6103fa6004803603602081101561090f57600080fd5b50356001600160a01b03166124c4565b6103fa6004803603604081101561093557600080fd5b506001600160a01b03813516906020013561250c565b6109716004803603602081101561096157600080fd5b50356001600160a01b03166126e5565b604080519485526020850193909352838301919091526060830152519081900360800190f35b6103fa600480360360808110156109ad57600080fd5b506001600160a01b038135811691602081013590911690604081013590606001356112c2565b6103fa600480360360608110156109e957600080fd5b506001600160a01b03813581169160208101359091169060400135612708565b6106336127fa565b61052a61280a565b6103fa60048036036060811015610a2f57600080fd5b506001600160a01b03813581169160208101359091169060400135612819565b6103fa60048036036040811015610a6557600080fd5b506001600160a01b03813516906020013561295e565b61042260048036036020811015610a9157600080fd5b810190602081018135640100000000811115610aac57600080fd5b820183602082011115610abe57600080fd5b80359060200191846020830284011164010000000083111715610ae057600080fd5b509092509050612a22565b61064f60048036036060811015610b0157600080fd5b506001600160a01b03813581169160208101359091169060400135612ac5565b6103fa60048036036060811015610b3757600080fd5b506001600160a01b038135811691602081013590911690604001356111e4565b6103fa600480360360a0811015610b6d57600080fd5b506001600160a01b0381358116916020810135821691604082013581169160608101359091169060800135612caf565b61042260048036036020811015610bb357600080fd5b810190602081018135640100000000811115610bce57600080fd5b820183602082011115610be057600080fd5b80359060200191846020830284011164010000000083111715610c0257600080fd5b509092509050612fc3565b61063360048036036040811015610c2357600080fd5b506001600160a01b038135811691602001351661305e565b6103fa60048036036040811015610c5157600080fd5b506001600160a01b038135169060200135613080565b6103fa600480360360c0811015610c7d57600080fd5b506001600160a01b038135169060208101359060408101359060608101359060808101359060a00135613144565b6103fa60048036036020811015610cc157600080fd5b503515156135fa565b6103fa60048036036020811015610ce057600080fd5b5035151561369c565b61052a61373e565b6103fa60048036036080811015610d0757600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356112c2565b6103fa60048036036040811015610d4357600080fd5b506001600160a01b03813516906020013561374d565b6103fa60048036036020811015610d6f57600080fd5b50351515613919565b61064f6139bb565b6103fa60048036036080811015610d9657600080fd5b506001600160a01b038135811691602081013582169160408201351690606001356139c1565b6103fa60048036036040811015610dd257600080fd5b506001600160a01b0381351690602001351515613aa6565b610633613b26565b6103fa613b36565b610e05603486613c2d565b8015610e175750610e17603485613c2d565b610e68576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e732068617665206e6f74206265656e206c69737465640000000000604482015290519081900360640190fd5b6000610e73836126e5565b505091505060008111610ecd576040805162461bcd60e51b815260206004820152601f60248201527f4163636f756e7420646f6573206e6f7420686176652073686f727466616c6c00604482015290519081900360640190fd5b6000866001600160a01b03166395dd9193856040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610f1c57600080fd5b505afa158015610f30573d6000803e3d6000fd5b505050506040513d6020811015610f4657600080fd5b5051603954909150600090610f5c908390613c42565b905080841115610fb3576040805162461bcd60e51b815260206004820152601f60248201527f52657061792065786365656473206d617820726570617920616c6c6f77656400604482015290519081900360640190fd5b5050505050505050565b6001600160a01b0381166000908152603760205260408120606091610fe460028301613c5a565b90508067ffffffffffffffff81118015610ffd57600080fd5b50604051908082528060200260200182016040528015611027578160200160208202803683370190505b50925060005b8181101561106e576110426002840182613c65565b84828151811061104e57fe5b6001600160a01b039092166020928302919091019091015260010161102d565b505050919050565b5050505050565b61108684613c71565b603c5460408051636576717f60e11b81526001600160a01b038781166004830152600160248301529151919092169163caece2fe91604480830192600092919082900301818387803b1580156110db57600080fd5b505af11580156110ef573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b0389811660048301528781166024830152600160448301529151919092169350637d3572fd9250606480830192600092919082900301818387803b15801561115057600080fd5b505af1158015610fb3573d6000803e3d6000fd5b600054819061010090046001600160a01b03163314806111965750603b546001600160a01b0316331480156111965750805b6111d15760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6111da83613c71565b6111e48383613ccd565b505050565b603b546001600160a01b031681565b6038546001600160a01b031681565b600054819061010090046001600160a01b03163314806112395750603b546001600160a01b0316331480156112395750805b6112745760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6034600061128182613c5a565b905060005b818110156112af57600061129a8483613c65565b90506112a68187613d3e565b50600101611286565b506112b984613e6b565b6112c284613ebe565b50505050565b6112d183613c71565b6001600160a01b0383166000908152603660205260409020600481015462010000900460ff1615611349576040805162461bcd60e51b815260206004820152601c60248201527f546f6b656e20626f72726f7720686173206265656e2070617573656400000000604482015290519081900360640190fd5b611353838561207f565b6113b657336001600160a01b038516146113ac576040805162461bcd60e51b815260206004820152601560248201527439b2b73232b91036bab9ba1031329034aa37b5b2b760591b604482015290519081900360640190fd5b6113b68385613f11565b60006113c58486600086613f84565b50509150508060001461141f576040805162461bcd60e51b815260206004820152601a60248201527f4163636f756e742068617320736f6d652073686f727466616c6c000000000000604482015290519081900360640190fd5b6000856001600160a01b03166347bd37186040518163ffffffff1660e01b815260040160206040518083038186803b15801561145a57600080fd5b505afa15801561146e573d6000803e3d6000fd5b505050506040513d602081101561148457600080fd5b505160028401549091506114988286614442565b11156114eb576040805162461bcd60e51b815260206004820152601d60248201527f546f6b656e20626f72726f772063617061636974792072656163686564000000604482015290519081900360640190fd5b603c5460408051636576717f60e11b81526001600160a01b038981166004830152600160248301529151919092169163caece2fe91604480830192600092919082900301818387803b15801561154057600080fd5b505af1158015611554573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b038b811660048301528a81166024830152600160448301529151919092169350637d3572fd9250606480830192600092919082900301818387803b1580156115b557600080fd5b505af11580156115c9573d6000803e3d6000fd5b50505050505050505050565b600054819061010090046001600160a01b03163314806116075750603b546001600160a01b0316331480156116075750805b6116425760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b61164b82613ebe565b5050565b60005461010090046001600160a01b0316331461169d5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b66b1a2bc2ec5000081101580156116bc5750670c7d713b49da00008111155b611704576040805162461bcd60e51b815260206004820152601460248201527310db1bdcd948199858dd1bdc881a5b9d985b1a5960621b604482015290519081900360640190fd5b6039805490829055604080518281526020810184905281517f3b9670cf975d26958e754b57098eaa2ac914d8d2a31b83257997b9f346110fd9929181900390910190a15050565b600054819061010090046001600160a01b031633148061177d5750603b546001600160a01b03163314801561177d5750805b6117b85760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6117c183613c71565b6111e4838361449c565b60006117d8603483613c2d565b92915050565b603a5481565b600190565b60005461010090046001600160a01b031633146118375760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b670de0b6b3a7640000811015801561185757506714d1120d7b1600008111155b6118a8576040805162461bcd60e51b815260206004820152601d60248201527f4c69717569646174696f6e20696e63656e7469766520696e76616c6964000000604482015290519081900360640190fd5b603a805490829055604080518281526020810184905281517faeba5a6c40a8ac138134bff1aaa65debf25971188a58804bad717f82f0ec1316929181900390910190a15050565b60005461010090046001600160a01b0316331461193d5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b6038546001600160a01b039081169082161580159061196e5750806001600160a01b0316826001600160a01b031614155b6119b8576040805162461bcd60e51b815260206004820152601660248201527513dc9858db19481859191c995cdcc81a5b9d985b1a5960521b604482015290519081900360640190fd5b603880546001600160a01b0319166001600160a01b03848116918217909255604080519284168352602083019190915280517fd52b2b9b7e9ee655fcb95d2e5b9e0c9f69e7ef2b8e9d2d0ea78402d576d22e229281900390910190a15050565b60005461010090046001600160a01b03163314611a665760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b603c546001600160a01b0390811690821615801590611a975750806001600160a01b0316826001600160a01b031614155b611ad25760405162461bcd60e51b8152600401808060200182810382526022815260200180614e6a6022913960400191505060405180910390fd5b603c80546001600160a01b0319166001600160a01b03848116918217909255604080519284168352602083019190915280517f8ddca872a7a62d68235cff1a03badc845dc3007cfaa6145379f7bf3452ecb9b99281900390910190a15050565b60005461010090046001600160a01b03163314611b805760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b603b546001600160a01b0390811690821615801590611bb15750806001600160a01b0316826001600160a01b031614155b611c02576040805162461bcd60e51b815260206004820152601e60248201527f506175736520677561726469616e206164647265737320696e76616c69640000604482015290519081900360640190fd5b603b80546001600160a01b0319166001600160a01b03848116918217909255604080519284168352602083019190915280517f0613b6ee6a04f0d09f390e4d9318894b9f6ac7fd83897cd8d18896ba579c401e9281900390910190a15050565b505050505050565b606060346000611c7982613c5a565b90508067ffffffffffffffff81118015611c9257600080fd5b50604051908082528060200260200182016040528015611cbc578160200160208202803683370190505b50925060005b81811015611d0057611cd48382613c65565b848281518110611ce057fe5b6001600160a01b0390921660209283029190910190910152600101611cc2565b50505090565b60005461010090046001600160a01b03163314611d545760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b6001600160a01b03811615801590611d7a57506001546001600160a01b03828116911614155b611db55760405162461bcd60e51b815260040180806020018281038252604b815260200180614ce0604b913960600191505060405180910390fd5b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b600054819061010090046001600160a01b0316331480611e395750603b546001600160a01b031633148015611e395750805b611e745760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b611e7d83613c71565b6111e48383614503565b603b54600160a01b900460ff1615611ee6576040805162461bcd60e51b815260206004820152601860248201527f5472616e7366657220686173206265656e207061757365640000000000000000604482015290519081900360640190fd5b611ef1848483614572565b603c5460408051636576717f60e11b81526001600160a01b038781166004830152600060248301819052925193169263caece2fe9260448084019391929182900301818387803b158015611f4457600080fd5b505af1158015611f58573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b0389811660048301528881166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b158015611fb557600080fd5b505af1158015611fc9573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b0389811660048301528781166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b15801561115057600080fd5b60005460ff16156120685760405162461bcd60e51b815260040180806020018281038252602e815260200180614d90602e913960400191505060405180910390fd5b61207061460f565b6000805460ff19166001179055565b6001600160a01b03821660009081526037602052604081206120a49060020183613c2d565b9392505050565b6120b483613c71565b6001600160a01b0383166000908152603660205260409020600481015460ff1615612126576040805162461bcd60e51b815260206004820152601a60248201527f546f6b656e206d696e7420686173206265656e20706175736564000000000000604482015290519081900360640190fd5b6000612202856001600160a01b031663182df0f56040518163ffffffff1660e01b815260040160206040518083038186803b15801561216457600080fd5b505afa158015612178573d6000803e3d6000fd5b505050506040513d602081101561218e57600080fd5b5051604080516318160ddd60e01b815290516001600160a01b038916916318160ddd916004808301926020929190829003018186803b1580156121d057600080fd5b505afa1580156121e4573d6000803e3d6000fd5b505050506040513d60208110156121fa57600080fd5b505190613c42565b60038301549091506122148285614442565b1115612267576040805162461bcd60e51b815260206004820152601d60248201527f546f6b656e20737570706c792063617061636974792072656163686564000000604482015290519081900360640190fd5b603c5460408051636576717f60e11b81526001600160a01b038881166004830152600060248301819052925193169263caece2fe9260448084019391929182900301818387803b1580156122ba57600080fd5b505af11580156122ce573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b038a811660048301528981166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b15801561232b57600080fd5b505af115801561233f573d6000803e3d6000fd5b505050505050505050565b6001600160a01b038116600090815260376020526040812060609161236e82613c5a565b90508067ffffffffffffffff8111801561238757600080fd5b506040519080825280602002602001820160405280156123b1578160200160208202803683370190505b50925060005b8181101561106e576123c98382613c65565b8482815181106123d557fe5b6001600160a01b03909216602092830291909101909101526001016123b7565b60005461010090046001600160a01b031681565b603660205260009081526040902080546001820154600283015460038401546004909401549293919290919060ff808216916101008104821691620100009091041687565b600054819061010090046001600160a01b03163314806124805750603b546001600160a01b0316331480156124805750805b6124bb5760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b61164b82613e6b565b6124ce3382614657565b6125095760405162461bcd60e51b8152600401808060200182810382526040815260200180614ef16040913960400191505060405180910390fd5b50565b60005461010090046001600160a01b0316331461255a5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b61256382613c71565b60008111801561257b5750670de0b6b3a76400008111155b6125c4576040805162461bcd60e51b8152602060048201526015602482015274109bdc9c9bddc8199858dd1bdc881a5b9d985b1a59605a1b604482015290519081900360640190fd5b6038546040805163fc57d4df60e01b81526001600160a01b0385811660048301529151919092169163fc57d4df916024808301926020929190829003018186803b15801561261157600080fd5b505afa158015612625573d6000803e3d6000fd5b505050506040513d602081101561263b57600080fd5b50516126785760405162461bcd60e51b815260040180806020018281038252603c815260200180614ddf603c913960400191505060405180910390fd5b6001600160a01b0382166000818152603660209081526040918290206001810180549086905583519485529184018290528383018590529151919290917fd69d3c48186e96a0af0545cf26e970b49ed3e29a306a37118a5a787bae6b90be9181900360600190a150505050565b6000806000806126f9856000806000613f84565b93509350935093509193509193565b6001600160a01b03831660009081526036602052604090206004015462010000900460ff161561277f576040805162461bcd60e51b815260206004820152601c60248201527f546f6b656e20626f72726f7720686173206265656e2070617573656400000000604482015290519081900360640190fd5b61278883613c71565b603c5460408051636576717f60e11b81526001600160a01b038681166004830152600160248301529151919092169163caece2fe91604480830192600092919082900301818387803b1580156127dd57600080fd5b505af11580156127f1573d6000803e3d6000fd5b50505050505050565b603b54600160a81b900460ff1681565b603c546001600160a01b031681565b6001600160a01b038316600090815260366020526040902060040154610100900460ff161561288f576040805162461bcd60e51b815260206004820152601c60248201527f546f6b656e2072656465656d20686173206265656e2070617573656400000000604482015290519081900360640190fd5b61289a838383614572565b603c5460408051636576717f60e11b81526001600160a01b038681166004830152600060248301819052925193169263caece2fe9260448084019391929182900301818387803b1580156128ed57600080fd5b505af1158015612901573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b0388811660048301528781166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b1580156127dd57600080fd5b60005461010090046001600160a01b031633146129ac5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b6129b582613c71565b6001600160a01b0382166000818152603660209081526040918290206002810180549086905583519485529184018290528383018590529151919290917f6c07964980a680dc12652ccab077a57c65740bf2ebc204a7670438051ee6fa5b9181900360600190a150505050565b6060818067ffffffffffffffff81118015612a3c57600080fd5b50604051908082528060200260200182016040528015612a66578160200160208202803683370190505b50915060005b81811015612abd57612a99858583818110612a8357fe5b905060200201356001600160a01b031633614657565b838281518110612aa557fe5b91151560209283029190910190910152600101612a6c565b505092915050565b6038546040805163fc57d4df60e01b81526001600160a01b03868116600483015291516000938493169163fc57d4df916024808301926020929190829003018186803b158015612b1457600080fd5b505afa158015612b28573d6000803e3d6000fd5b505050506040513d6020811015612b3e57600080fd5b50516038546040805163fc57d4df60e01b81526001600160a01b0388811660048301529151939450600093919092169163fc57d4df916024808301926020929190829003018186803b158015612b9357600080fd5b505afa158015612ba7573d6000803e3d6000fd5b505050506040513d6020811015612bbd57600080fd5b505190508115801590612bcf57508015155b612c0a5760405162461bcd60e51b815260040180806020018281038252602d815260200180614e3d602d913960400191505060405180910390fd5b603a54600090612c2490612c1e87866146e6565b90613c42565b90506000866001600160a01b031663182df0f56040518163ffffffff1660e01b815260040160206040518083038186803b158015612c6157600080fd5b505afa158015612c75573d6000803e3d6000fd5b505050506040513d6020811015612c8b57600080fd5b50519050612ca383612c9d848461473f565b90614757565b98975050505050505050565b603b54600160a81b900460ff1615612d06576040805162461bcd60e51b815260206004820152601560248201527414d95a5e99481a185cc81899595b881c185d5cd959605a1b604482015290519081900360640190fd5b612d11603485613c2d565b8015612d235750612d23603486613c2d565b612d74576040805162461bcd60e51b815260206004820152601b60248201527f546f6b656e732068617665206e6f74206265656e206c69737465640000000000604482015290519081900360640190fd5b846001600160a01b031663f77c47916040518163ffffffff1660e01b815260040160206040518083038186803b158015612dad57600080fd5b505afa158015612dc1573d6000803e3d6000fd5b505050506040513d6020811015612dd757600080fd5b50516040805163f77c479160e01b815290516001600160a01b039283169287169163f77c4791916004808301926020929190829003018186803b158015612e1d57600080fd5b505afa158015612e31573d6000803e3d6000fd5b505050506040513d6020811015612e4757600080fd5b50516001600160a01b031614612e8e5760405162461bcd60e51b8152600401808060200182810382526033815260200180614cad6033913960400191505060405180910390fd5b603c5460408051636576717f60e11b81526001600160a01b038881166004830152600060248301819052925193169263caece2fe9260448084019391929182900301818387803b158015612ee157600080fd5b505af1158015612ef5573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b038a811660048301528881166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b158015612f5257600080fd5b505af1158015612f66573d6000803e3d6000fd5b5050603c5460408051637d3572fd60e01b81526001600160a01b038a811660048301528781166024830152600060448301819052925193169450637d3572fd93506064808201939182900301818387803b15801561232b57600080fd5b6060818067ffffffffffffffff81118015612fdd57600080fd5b50604051908082528060200260200182016040528015613007578160200160208202803683370190505b50915060005b81811015612abd5761303a85858381811061302457fe5b905060200201356001600160a01b031633614799565b83828151811061304657fe5b9115156020928302919091019091015260010161300d565b6001600160a01b03821660009081526037602052604081206120a49083613c2d565b60005461010090046001600160a01b031633146130ce5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b6130d782613c71565b6001600160a01b0382166000818152603660209081526040918290206003810180549086905583519485529184018290528383018590529151919290917f6ffc41eacf6bc473fab68880680a35214210358ff10d45c53cee339f1547f0bb9181900360600190a150505050565b60005461010090046001600160a01b031633146131925760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b856001600160a01b031663744279376040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156131cd57600080fd5b505af11580156131e1573d6000803e3d6000fd5b505050506040513d60208110156131f757600080fd5b5051613243576040805162461bcd60e51b8152602060048201526016602482015275151bdad95b881a5cc81b9bdd081cdd5c1c1bdc9d195960521b604482015290519081900360640190fd5b61324e6034876148da565b61329f576040805162461bcd60e51b815260206004820152601d60248201527f546f6b656e2068617320616c7265616479206265656e206c6973746564000000604482015290519081900360640190fd5b670de0b6b3a76400008511156132f8576040805162461bcd60e51b815260206004820152601960248201527810dbdb1b185d195c985b08199858dd1bdc881a5b9d985b1a59603a1b604482015290519081900360640190fd5b6000841180156133105750670de0b6b3a76400008411155b613359576040805162461bcd60e51b8152602060048201526015602482015274109bdc9c9bddc8199858dd1bdc881a5b9d985b1a59605a1b604482015290519081900360640190fd5b6038546040805163fc57d4df60e01b81526001600160a01b0389811660048301529151919092169163fc57d4df916024808301926020929190829003018186803b1580156133a657600080fd5b505afa1580156133ba573d6000803e3d6000fd5b505050506040513d60208110156133d057600080fd5b5051613423576040805162461bcd60e51b815260206004820152601f60248201527f556e6465726c79696e6720707269636520697320756e617661696c61626c6500604482015290519081900360640190fd5b6040518060e001604052808681526020018581526020018381526020018481526020016000151581526020016000151581526020016000151581525060366000886001600160a01b03166001600160a01b031681526020019081526020016000206000820151816000015560208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a81548160ff02191690831515021790555060a08201518160040160016101000a81548160ff02191690831515021790555060c08201518160040160026101000a81548160ff021916908315150217905550905050603c60009054906101000a90046001600160a01b03166001600160a01b0316635658fec387836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561357b57600080fd5b505af115801561358f573d6000803e3d6000fd5b5050604080516001600160a01b038a16815260208101899052808201889052606081018790526080810186905260a0810185905290517f96f15e56d27d14b82ed736fd9387871d60f721cd59f324699722b4f0d11ee1b193509081900360c0019150a1505050505050565b600054819061010090046001600160a01b031633148061362c5750603b546001600160a01b03163314801561362c5750805b6136675760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6034600061367482613c5a565b905060005b818110156110765761369461368e8483613c65565b8661449c565b600101613679565b600054819061010090046001600160a01b03163314806136ce5750603b546001600160a01b0316331480156136ce5750805b6137095760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6034600061371682613c5a565b905060005b81811015611076576137366137308483613c65565b86614503565b60010161371b565b6001546001600160a01b031681565b60005461010090046001600160a01b0316331461379b5760405162461bcd60e51b8152600401808060200182810382526022815260200180614e1b6022913960400191505060405180910390fd5b6137a482613c71565b670de0b6b3a76400008111156137fd576040805162461bcd60e51b815260206004820152601960248201527810dbdb1b185d195c985b08199858dd1bdc881a5b9d985b1a59603a1b604482015290519081900360640190fd5b6038546040805163fc57d4df60e01b81526001600160a01b0385811660048301529151919092169163fc57d4df916024808301926020929190829003018186803b15801561384a57600080fd5b505afa15801561385e573d6000803e3d6000fd5b505050506040513d602081101561387457600080fd5b50516138b15760405162461bcd60e51b8152600401808060200182810382526040815260200180614e8c6040913960400191505060405180910390fd5b6001600160a01b038216600081815260366020908152604091829020805485825583519485529184018290528383018590529151919290917f70483e6592cd5182d45ac970e05bc62cdcc90e9d8ef2c2dbe686cf383bcd7fc59181900360600190a150505050565b600054819061010090046001600160a01b031633148061394b5750603b546001600160a01b03163314801561394b5750805b6139865760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b6034600061399382613c5a565b905060005b81811015611076576139b36139ad8483613c65565b86613ccd565b600101613998565b60395481565b6139ca84613c71565b836001600160a01b03166395dd9193836040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015613a1757600080fd5b505afa158015613a2b573d6000803e3d6000fd5b505050506040513d6020811015613a4157600080fd5b50516112c257336001600160a01b03851614613a9c576040805162461bcd60e51b815260206004820152601560248201527439b2b73232b91036bab9ba1031329034aa37b5b2b760591b604482015290519081900360640190fd5b6112c282856148ef565b600054819061010090046001600160a01b0316331480613ad85750603b546001600160a01b031633148015613ad85750805b613b135760405162461bcd60e51b815260040180806020018281038252603c815260200180614d54603c913960400191505060405180910390fd5b613b1c83613c71565b6111e48383613d3e565b603b54600160a01b900460ff1681565b6001546001600160a01b03163314613b7f5760405162461bcd60e51b8152600401808060200182810382526025815260200180614ecc6025913960400191505060405180910390fd5b60008054600180546001600160a01b03818116610100818102610100600160a81b03198716178088556001600160a01b031990941690945560405194849004821695909493909204169184917f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236491a36001546040516001600160a01b03918216918316907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b60006120a4836001600160a01b038416614962565b60006120a4670de0b6b3a7640000612c9d85856146e6565b60006117d88261497a565b60006120a4838361497e565b613c7c603482613c2d565b612509576040805162461bcd60e51b815260206004820152601960248201527f546f6b656e20686173206e6f74206265656e206c697374656400000000000000604482015290519081900360640190fd5b6001600160a01b038216600081815260366020908152604091829020600401805462ff00001916620100008615159081029190911790915582519384529083015280517fa501bd5ac3de9924ce0c13576750267130fd835780f1ec6e1ae9fb13ee7465039281900390910190a15050565b6001600160a01b03821660008181526036602090815260409182902060048101805460ff1916861515908117909155835194855291840191909152815190927f48cff1fdfe035051f817c77855362a92f96f941342d41008f2d596f530c4e96b92908290030190a160048101805461ff00191661010084151590810291909117909155604080516001600160a01b0386168152602081019290925280517f3d95d87adcb06de16ac0e322ea97a06e30967fedcc83a6d1d0b9fda60300801a9281900390910190a160048101805462ff000019166201000084151590810291909117909155604080516001600160a01b0386168152602081019290925280517fa501bd5ac3de9924ce0c13576750267130fd835780f1ec6e1ae9fb13ee7465039281900390910190a1505050565b603b8054821515600160a01b810260ff60a01b199092169190911790915560408051918252517f7b92c580e5b53e34f85950f4117143b86c4031ca9568a3cd30f4b3cd5bb95dc89181900360200190a150565b603b8054821515600160a81b810260ff60a81b199092169190911790915560408051918252517fcaf79886b466b41f609cc39fdc375ab5484cc3811570b7faa22fcb2b55516fdf9181900360200190a150565b6001600160a01b0382166000908152603760205260409020613f3690600201826148da565b1561164b57604080516001600160a01b0380841682528416602082015281517f61400a0050615e96f6a4a234e4fc162ab71775345f32f4641cd884601603a455929181900390910190a15050565b600080600080613f92614c45565b6001600160a01b038916600090815260376020526040812090613fb482613c5a565b905060005b81811015614206576000613fcd8483613c65565b9050806001600160a01b03166370a082318e6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561401c57600080fd5b505afa158015614030573d6000803e3d6000fd5b505050506040513d602081101561404657600080fd5b5051604080870191909152805163182df0f560e01b815290516001600160a01b0383169163182df0f5916004808301926020929190829003018186803b15801561408f57600080fd5b505afa1580156140a3573d6000803e3d6000fd5b505050506040513d60208110156140b957600080fd5b505160808601526001600160a01b038c81169082161480156140db575060008b115b156140f55760408501516140ef908c6149e2565b60408601525b6038546040805163fc57d4df60e01b81526001600160a01b0384811660048301529151919092169163fc57d4df916024808301926020929190829003018186803b15801561414257600080fd5b505afa158015614156573d6000803e3d6000fd5b505050506040513d602081101561416c57600080fd5b505160a086018190526141b05760405162461bcd60e51b8152600401808060200182810382526029815260200180614d2b6029913960400191505060405180910390fd5b6001600160a01b0381166000908152603660205260409081902054608087015160a0880151928801516141e993612c1e929183916146e6565b60c0860181905285516141fb91614442565b855250600101613fb9565b5061421382600201613c5a565b905060005b818110156143fd57600061422f6002850183613c65565b9050806001600160a01b03166395dd91938e6040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561427e57600080fd5b505afa158015614292573d6000803e3d6000fd5b505050506040513d60208110156142a857600080fd5b505160608601526001600160a01b038c81169082161480156142ca575060008a115b156142e45760608501516142de908b614442565b60608601525b6038546040805163fc57d4df60e01b81526001600160a01b0384811660048301529151919092169163fc57d4df916024808301926020929190829003018186803b15801561433157600080fd5b505afa158015614345573d6000803e3d6000fd5b505050506040513d602081101561435b57600080fd5b505160a0860181905261439f5760405162461bcd60e51b8152600401808060200182810382526029815260200180614d2b6029913960400191505060405180910390fd5b6001600160a01b03811660009081526036602052604090206001015460a086015160608701516143da92916143d491906146e6565b9061473f565b60e0860181905260208601516143ef91614442565b602086015250600101614218565b50602083015183511161441d57825160208401516000918082039161442d565b6020830151835181810391600091905b929e919d509b50909950975050505050505050565b6000828201838110156120a4576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b038216600081815260366020908152604091829020600401805460ff191685151590811790915582519384529083015280517f48cff1fdfe035051f817c77855362a92f96f941342d41008f2d596f530c4e96b9281900390910190a15050565b6001600160a01b038216600081815260366020908152604091829020600401805461ff0019166101008615159081029190911790915582519384529083015280517f3d95d87adcb06de16ac0e322ea97a06e30967fedcc83a6d1d0b9fda60300801a9281900390910190a15050565b61457b83613c71565b6001600160a01b038216600090815260376020526040902061459d9084613c2d565b6145a6576111e4565b60006145b58385846000613f84565b5050915050806000146112c2576040805162461bcd60e51b815260206004820152601a60248201527f4163636f756e742068617320736f6d652073686f727466616c6c000000000000604482015290519081900360640190fd5b60008054610100600160a81b0319163361010081029190911782556040519091907f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b2364908290a3565b6000614664603484613c2d565b614670575060006117d8565b6001600160a01b038216600090815260376020526040902061469290846148da565b156146dd57604080516001600160a01b0380861682528416602082015281517f3ab23ab0d51cccc0c3085aec51f99228625aa1a922b3a8ca89a26b0f2027a1a5929181900390910190a15b50600192915050565b6000826146f5575060006117d8565b8282028284828161470257fe5b04146120a45760405162461bcd60e51b8152600401808060200182810382526021815260200180614dbe6021913960400191505060405180910390fd5b60006120a482612c9d85670de0b6b3a76400006146e6565b60006120a483836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614a24565b60006147a6603484613c2d565b6147b2575060016117d8565b6001600160a01b03821660009081526037602052604090206147d49084613c2d565b6147e0575060016117d8565b6000836001600160a01b03166370a08231846040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b15801561482f57600080fd5b505afa158015614843573d6000803e3d6000fd5b505050506040513d602081101561485957600080fd5b50519050614868848483614572565b6001600160a01b038316600090815260376020526040902061488a9085614ac6565b50604080516001600160a01b0380871682528516602082015281517fe699a64c18b07ac5b7301aa273f36a2287239eb9501d81950672794afba29a0d929181900390910190a15060019392505050565b60006120a4836001600160a01b038416614adb565b6001600160a01b03821660009081526037602052604090206149149060020182614ac6565b1561164b57604080516001600160a01b0380841682528416602082015281517f6df0340c4ae9990f4b8f3c3e0e5408dcf6ba2c086b92966dfdbbef1f2f052e1a929181900390910190a15050565b60009081526001919091016020526040902054151590565b5490565b815460009082106149c05760405162461bcd60e51b8152600401808060200182810382526022815260200180614c8b6022913960400191505060405180910390fd5b8260000182815481106149cf57fe5b9060005260206000200154905092915050565b60006120a483836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250614b25565b60008183614ab05760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015614a75578181015183820152602001614a5d565b50505050905090810190601f168015614aa25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838581614abc57fe5b0495945050505050565b60006120a4836001600160a01b038416614b7f565b6000614ae78383614962565b614b1d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556117d8565b5060006117d8565b60008184841115614b775760405162461bcd60e51b8152602060048201818152835160248401528351909283926044909101919085019080838360008315614a75578181015183820152602001614a5d565b505050900390565b60008181526001830160205260408120548015614c3b5783546000198083019190810190600090879083908110614bb257fe5b9060005260206000200154905080876000018481548110614bcf57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080614bff57fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506117d8565b60009150506117d8565b6040518061010001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152509056fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473436f6e74726f6c6c6572206d69736d61746368206265747765656e20426f72726f77656420616e6420436f6c6c61746572616c5f73657450656e64696e674f776e65723a204e6577206f77656e722063616e206e6f74206265207a65726f206164647265737320616e64206f776e657220686173206265656e2073657421496e76616c696420707269636520746f2063616c63756c617465206163636f756e74206571756974794f6e6c79206f776e657220616e6420677561726469616e2063616e20706175736520616e64206f6e6c79206f776e65722063616e20756e7061757365496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774661696c656420746f2073657420626f72726f7720666163746f722c20756e6465726c79696e6720707269636520697320756e617661696c61626c656f6e6c794f776e65723a2063616c6c6572206973206e6f7420746865206f776e6572426f72726f776564206f7220436f6c6c61746572616c20617373657420707269636520697320696e76616c6964526577617264204469737472696275746f72206164647265737320696e76616c69644661696c656420746f2073657420636f6c6c61746572616c20666163746f722c20756e6465726c79696e6720707269636520697320756e617661696c61626c655f6163636570744f776e65723a204f6e6c7920666f722070656e64696e67206f776e657221656e7465724d61726b657446726f6d69546f6b656e3a204f6e6c792063616e2062652063616c6c6564206279206120737570706f727465642069546f6b656e21a2646970667358221220e81953e16f374459748653e7caae0da1ae2623baceff193bedcf6c1cd10405f564736f6c634300060c0033

Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.