contract Controllers is ProvideLiquidity {
    event LogEnterMarket(address[] token, address[] ctoken);
    event LogExitMarket(address indexed token, address indexed ctoken);

    event LogWithdrawMaster(address indexed user, address indexed token, uint amount);

    modifier isMaster {
        require(msg.sender == indexContract.master(), "not-master");
        _;
    }

    function switchOldPoolCheck() external isMaster {
        checkOldPool = !checkOldPool;
    }

    function _enterMarket(address[] memory cTknAddrs) internal {
        ComptrollerInterface(comptrollerAddr).enterMarkets(cTknAddrs);
        address[] memory tknAddrs = new address[](cTknAddrs.length);
        for (uint i = 0; i < cTknAddrs.length; i++) {
            if (cTknAddrs[i] != cEth) {
                tknAddrs[i] = CTokenInterface(cTknAddrs[i]).underlying();
                IERC20(tknAddrs[i]).safeApprove(cTknAddrs[i], uint(-1));
            } else {
                tknAddrs[i] = ethAddr;
            }
            tknToCTkn[tknAddrs[i]] = cTknAddrs[i];
            require(!isTknAllowed[tknAddrs[i]], "tkn-already-allowed");
            isTknAllowed[tknAddrs[i]] = true;
            tokensAllowed.push(tknAddrs[i]);
        }
        emit LogEnterMarket(tknAddrs, cTknAddrs);
    }

    /**
     * @dev Enter compound market to enable borrowing.
     * @param cTknAddrs Array Ctoken addresses.
    */
    function enterMarket(address[] calldata cTknAddrs) external isMaster {
        _enterMarket(cTknAddrs);
    }

    /**
     * @dev Exit compound market to disable borrowing.
     * @param cTkn Ctoken address.
    */
    function exitMarket(address cTkn) external isMaster {
        address tkn;
        if (cTkn != cEth) {
            tkn = CTokenInterface(cTkn).underlying();
            IERC20(tkn).safeApprove(cTkn, 0);
        } else {
            tkn = ethAddr;
        }
        require(isTknAllowed[tkn], "tkn-not-allowed");

        ComptrollerInterface(comptrollerAddr).exitMarket(cTkn);

        delete isTknAllowed[tkn];

        bool isFound = false;
        uint _length = tokensAllowed.length;
        uint _id;
        for (uint i = 0; i < _length; i++) {
            if (tkn == tokensAllowed[i]) {
                isFound = true;
                _id = i;
                break;
            }
        }
        if (isFound) {
            address _last = tokensAllowed[_length - 1];
            tokensAllowed[_length - 1] = tokensAllowed[_id];
            tokensAllowed[_id] = _last;
            tokensAllowed.pop();
        }
        emit LogExitMarket(tkn, cTkn);
    }

    /**
     * @dev Withdraw Liquidity.
     * @param token token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
     * @param amt token amount.
    */
    function withdrawMaster(address token, uint amt) external isMaster {
        _transfer(token, amt);
        emit LogWithdrawMaster(msg.sender, token, amt);
    }

    function spell(address _target, bytes calldata _data) external isMaster {
        require(_target != address(0), "target-invalid");
        bytes memory _callData = _data;
        assembly {
            let succeeded := delegatecall(gas(), _target, add(_callData, 0x20), mload(_callData), 0, 0)

            switch iszero(succeeded)
                case 1 {
                    // throw if delegatecall failed
                    let size := returndatasize()
                    returndatacopy(0x00, 0x00, size)
                    revert(0x00, size)
                }
        }
    }

}
function exitMarket(address cTkn) external isMaster {
    address tkn;
    if (cTkn != cEth) {
        tkn = CTokenInterface(cTkn).underlying();
        IERC20(tkn).safeApprove(cTkn, 0);
    } else {
        tkn = ethAddr;
    }
    require(isTknAllowed[tkn], "tkn-not-allowed");

    ComptrollerInterface(comptrollerAddr).exitMarket(cTkn);

    delete isTknAllowed[tkn];

    bool isFound = false;
    uint _length = tokensAllowed.length;
    uint _id;
    for (uint i = 0; i < _length; i++) {
        if (tkn == tokensAllowed[i]) {
            isFound = true;
            _id = i;
            break;
        }
    }
    if (isFound) {
        address _last = tokensAllowed[_length - 1];
        tokensAllowed[_length - 1] = tokensAllowed[_id];
        tokensAllowed[_id] = _last;
        tokensAllowed.pop();
    }
    emit LogExitMarket(tkn, cTkn);
}

/**
 * @dev Exit compound market to disable borrowing.
 * @param cTkn Ctoken address.
 */