ETH Price: $2,745.54 (+5.75%)

Contract

0x04dA2043C361113DEeB52A4c3834d877586bbebd
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Stuck F...108394612020-09-11 8:23:551615 days ago1599812635IN
0x04dA2043...7586bbebd
0 ETH0.00485918116.6
Repay For108237292020-09-08 22:23:091618 days ago1599603789IN
0x04dA2043...7586bbebd
0.00553 ETH0.1029862179
Repay For108145342020-09-07 12:32:101619 days ago1599481930IN
0x04dA2043...7586bbebd
0.00756 ETH0.15098097108
Repay For108114492020-09-07 1:04:081620 days ago1599440648IN
0x04dA2043...7586bbebd
0 ETH0.1529788989
Boost For108106272020-09-06 21:58:561620 days ago1599429536IN
0x04dA2043...7586bbebd
0.00623 ETH0.132524289
Boost For108068932020-09-06 8:33:501620 days ago1599381230IN
0x04dA2043...7586bbebd
0.021 ETH0.1789464150
Repay For108033742020-09-05 19:37:391621 days ago1599334659IN
0x04dA2043...7586bbebd
0.02842 ETH0.7203662406
Repay For108032392020-09-05 19:08:341621 days ago1599332914IN
0x04dA2043...7586bbebd
0.0441 ETH0.8222217450.45
Repay For108032382020-09-05 19:08:321621 days ago1599332912IN
0x04dA2043...7586bbebd
0.02205 ETH0.73249025450.45
Repay For108032382020-09-05 19:08:321621 days ago1599332912IN
0x04dA2043...7586bbebd
0.02205 ETH1.09477052450.45
Repay For108032382020-09-05 19:08:321621 days ago1599332912IN
0x04dA2043...7586bbebd
0.02205 ETH0.93572924450.45
Repay For108032382020-09-05 19:08:321621 days ago1599332912IN
0x04dA2043...7586bbebd
0.02205 ETH0.99572377450.45
Repay For108032322020-09-05 19:07:261621 days ago1599332846IN
0x04dA2043...7586bbebd
0.02205 ETH0.70878844381.15
Repay For108030972020-09-05 18:38:131621 days ago1599331093IN
0x04dA2043...7586bbebd
0.0441 ETH0.33675862315
Repay For108030472020-09-05 18:29:021621 days ago1599330542IN
0x04dA2043...7586bbebd
0.04466 ETH0.48180803319
Repay For108029982020-09-05 18:20:311621 days ago1599330031IN
0x04dA2043...7586bbebd
0.04466 ETH0.59306055319
Repay For108028592020-09-05 17:51:341621 days ago1599328294IN
0x04dA2043...7586bbebd
0.04102 ETH0.10040216301
Repay For108016142020-09-05 13:17:061621 days ago1599311826IN
0x04dA2043...7586bbebd
0.05964 ETH0.14785798495
Repay For108015462020-09-05 13:00:201621 days ago1599310820IN
0x04dA2043...7586bbebd
0.028 ETH0.4660504400
Repay For108015132020-09-05 12:54:181621 days ago1599310458IN
0x04dA2043...7586bbebd
0.0539 ETH0.75013326423.5
Repay For108015132020-09-05 12:54:181621 days ago1599310458IN
0x04dA2043...7586bbebd
0.0504 ETH0.72767154435.6
Repay For108014932020-09-05 12:50:531621 days ago1599310253IN
0x04dA2043...7586bbebd
0.02408 ETH0.94000061416.24
Repay For108014932020-09-05 12:50:531621 days ago1599310253IN
0x04dA2043...7586bbebd
0.02408 ETH0.79934063416.24
Repay For108014832020-09-05 12:48:141621 days ago1599310094IN
0x04dA2043...7586bbebd
0.0238 ETH0.79674356411.4
Repay For108012612020-09-05 12:01:581621 days ago1599307318IN
0x04dA2043...7586bbebd
0.04382 ETH0.75397292344.3
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
108237292020-09-08 22:23:091618 days ago1599603789
0x04dA2043...7586bbebd
0.00553 ETH
108145342020-09-07 12:32:101619 days ago1599481930
0x04dA2043...7586bbebd
0.00756 ETH
108106272020-09-06 21:58:561620 days ago1599429536
0x04dA2043...7586bbebd
0.00623 ETH
108068932020-09-06 8:33:501620 days ago1599381230
0x04dA2043...7586bbebd
0.021 ETH
108033742020-09-05 19:37:391621 days ago1599334659
0x04dA2043...7586bbebd
0.02842 ETH
108032392020-09-05 19:08:341621 days ago1599332914
0x04dA2043...7586bbebd
0.0441 ETH
108032382020-09-05 19:08:321621 days ago1599332912
0x04dA2043...7586bbebd
0.02205 ETH
108032382020-09-05 19:08:321621 days ago1599332912
0x04dA2043...7586bbebd
0.02205 ETH
108032382020-09-05 19:08:321621 days ago1599332912
0x04dA2043...7586bbebd
0.02205 ETH
108032382020-09-05 19:08:321621 days ago1599332912
0x04dA2043...7586bbebd
0.02205 ETH
108032322020-09-05 19:07:261621 days ago1599332846
0x04dA2043...7586bbebd
0.02205 ETH
108030972020-09-05 18:38:131621 days ago1599331093
0x04dA2043...7586bbebd
0.0441 ETH
108030472020-09-05 18:29:021621 days ago1599330542
0x04dA2043...7586bbebd
0.04466 ETH
108029982020-09-05 18:20:311621 days ago1599330031
0x04dA2043...7586bbebd
0.04466 ETH
108015462020-09-05 13:00:201621 days ago1599310820
0x04dA2043...7586bbebd
0.028 ETH
108015132020-09-05 12:54:181621 days ago1599310458
0x04dA2043...7586bbebd
0.0539 ETH
108015132020-09-05 12:54:181621 days ago1599310458
0x04dA2043...7586bbebd
0.0504 ETH
108014932020-09-05 12:50:531621 days ago1599310253
0x04dA2043...7586bbebd
0.02408 ETH
108014932020-09-05 12:50:531621 days ago1599310253
0x04dA2043...7586bbebd
0.02408 ETH
108014832020-09-05 12:48:141621 days ago1599310094
0x04dA2043...7586bbebd
0.0238 ETH
108012612020-09-05 12:01:581621 days ago1599307318
0x04dA2043...7586bbebd
0.04382 ETH
108011332020-09-05 11:34:241621 days ago1599305664
0x04dA2043...7586bbebd
0.04284 ETH
108011132020-09-05 11:30:331621 days ago1599305433
0x04dA2043...7586bbebd
0.04032 ETH
107959482020-09-04 16:28:261622 days ago1599236906
0x04dA2043...7586bbebd
0.0434 ETH
107956212020-09-04 15:11:381622 days ago1599232298
0x04dA2043...7586bbebd
0.0392 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CompoundMonitor

Compiler Version
v0.6.10+commit.00c0fcaf

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-08-27
*/

/**
 *Submitted for verification at Etherscan.io on 2020-08-18
*/

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

interface ERC20 {
    function totalSupply() external view returns (uint256 supply);

    function balanceOf(address _owner) external view returns (uint256 balance);

    function transfer(address _to, uint256 _value) external returns (bool success);

    function transferFrom(address _from, address _to, uint256 _value)
        external
        returns (bool success);

    function approve(address _spender, uint256 _value) external returns (bool success);

    function allowance(address _owner, address _spender) external view returns (uint256 remaining);

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

    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
} abstract contract GasTokenInterface is ERC20 {
    function free(uint256 value) public virtual returns (bool success);

    function freeUpTo(uint256 value) public virtual returns (uint256 freed);

    function freeFrom(address from, uint256 value) public virtual returns (bool success);

    function freeFromUpTo(address from, uint256 value) public virtual returns (uint256 freed);
} contract GasBurner {
    // solhint-disable-next-line const-name-snakecase
    GasTokenInterface public constant gasToken = GasTokenInterface(0x0000000000b3F879cb30FE243b4Dfee438691c04);

    modifier burnGas(uint _amount) {
        if (gasToken.balanceOf(address(this)) >= _amount) {
            gasToken.free(_amount);
        }

        _;
    }
} abstract contract DSProxyInterface {

    /// Truffle wont compile if this isn't commented
    // function execute(bytes memory _code, bytes memory _data)
    //     public virtual
    //     payable
    //     returns (address, bytes32);

    function execute(address _target, bytes memory _data) public virtual payable returns (bytes32);

    function setCache(address _cacheAddr) public virtual payable returns (bool);

    function owner() public virtual returns (address);
} library Address {
    function isContract(address account) internal view returns (bool) {
        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != accountHash && codehash != 0x0);
    }

    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");
    }

    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, errorMessage);
    }

    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");
    }

    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");
        return _functionCallWithValue(target, data, value, errorMessage);
    }

    function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
        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);
            }
        }
    }
}



library SafeMath {
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    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;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by 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;
    }

    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}







library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

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

    function safeTransferFrom(ERC20 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.
     */
    function safeApprove(ERC20 token, address spender, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(ERC20 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(ERC20 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));
    }

    function _callOptionalReturn(ERC20 token, bytes memory data) private {

        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");
        }
    }
} contract AdminAuth {

    using SafeERC20 for ERC20;

    address public owner;
    address public admin;

    modifier onlyOwner() {
        require(owner == msg.sender);
        _;
    }

    constructor() public {
        owner = msg.sender;
    }

    /// @notice Admin is set by owner first time, after that admin is super role and has permission to change owner
    /// @param _admin Address of multisig that becomes admin
    function setAdminByOwner(address _admin) public {
        require(msg.sender == owner);
        require(admin == address(0));

        admin = _admin;
    }

    /// @notice Admin is able to set new admin
    /// @param _admin Address of multisig that becomes new admin
    function setAdminByAdmin(address _admin) public {
        require(msg.sender == admin);

        admin = _admin;
    }

    /// @notice Admin is able to change owner
    /// @param _owner Address of new owner
    function setOwnerByAdmin(address _owner) public {
        require(msg.sender == admin);

        owner = _owner;
    }

    /// @notice Destroy the contract
    function kill() public onlyOwner {
        selfdestruct(payable(owner));
    }

    /// @notice  withdraw stuck funds
    function withdrawStuckFunds(address _token, uint _amount) public onlyOwner {
        if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
            payable(owner).transfer(_amount);
        } else {
            ERC20(_token).safeTransfer(owner, _amount);
        }
    }
} /// @title Contract with the actuall DSProxy permission calls the automation operations
contract CompoundMonitorProxy is AdminAuth {

    using SafeERC20 for ERC20;

    uint public CHANGE_PERIOD;
    address public monitor;
    address public newMonitor;
    address public lastMonitor;
    uint public changeRequestedTimestamp;

    mapping(address => bool) public allowed;

    event MonitorChangeInitiated(address oldMonitor, address newMonitor);
    event MonitorChangeCanceled();
    event MonitorChangeFinished(address monitor);
    event MonitorChangeReverted(address monitor);

    // if someone who is allowed become malicious, owner can't be changed
    modifier onlyAllowed() {
        require(allowed[msg.sender] || msg.sender == owner);
        _;
    }

    modifier onlyMonitor() {
        require (msg.sender == monitor);
        _;
    }

    constructor(uint _changePeriod) public {
        CHANGE_PERIOD = _changePeriod * 1 days;
    }

    /// @notice Only monitor contract is able to call execute on users proxy
    /// @param _owner Address of cdp owner (users DSProxy address)
    /// @param _compoundSaverProxy Address of CompoundSaverProxy
    /// @param _data Data to send to CompoundSaverProxy
    function callExecute(address _owner, address _compoundSaverProxy, bytes memory _data) public payable onlyMonitor {
        // execute reverts if calling specific method fails
        DSProxyInterface(_owner).execute{value: msg.value}(_compoundSaverProxy, _data);

        // return if anything left
        if (address(this).balance > 0) {
            msg.sender.transfer(address(this).balance);
        }
    }

    /// @notice Allowed users are able to set Monitor contract without any waiting period first time
    /// @param _monitor Address of Monitor contract
    function setMonitor(address _monitor) public onlyAllowed {
        require(monitor == address(0));
        monitor = _monitor;
    }

    /// @notice Allowed users are able to start procedure for changing monitor
    /// @dev after CHANGE_PERIOD needs to call confirmNewMonitor to actually make a change
    /// @param _newMonitor address of new monitor
    function changeMonitor(address _newMonitor) public onlyAllowed {
        require(changeRequestedTimestamp == 0);

        changeRequestedTimestamp = now;
        lastMonitor = monitor;
        newMonitor = _newMonitor;

        emit MonitorChangeInitiated(lastMonitor, newMonitor);
    }

    /// @notice At any point allowed users are able to cancel monitor change
    function cancelMonitorChange() public onlyAllowed {
        require(changeRequestedTimestamp > 0);

        changeRequestedTimestamp = 0;
        newMonitor = address(0);

        emit MonitorChangeCanceled();
    }

    /// @notice Anyone is able to confirm new monitor after CHANGE_PERIOD if process is started
    function confirmNewMonitor() public onlyAllowed {
        require((changeRequestedTimestamp + CHANGE_PERIOD) < now);
        require(changeRequestedTimestamp != 0);
        require(newMonitor != address(0));

        monitor = newMonitor;
        newMonitor = address(0);
        changeRequestedTimestamp = 0;

        emit MonitorChangeFinished(monitor);
    }

    /// @notice Its possible to revert monitor to last used monitor
    function revertMonitor() public onlyAllowed {
        require(lastMonitor != address(0));

        monitor = lastMonitor;

        emit MonitorChangeReverted(monitor);
    }


    /// @notice Allowed users are able to add new allowed user
    /// @param _user Address of user that will be allowed
    function addAllowed(address _user) public onlyAllowed {
        allowed[_user] = true;
    }

    /// @notice Allowed users are able to remove allowed user
    /// @dev owner is always allowed even if someone tries to remove it from allowed mapping
    /// @param _user Address of allowed user
    function removeAllowed(address _user) public onlyAllowed {
        allowed[_user] = false;
    }

    function setChangePeriod(uint _periodInDays) public onlyAllowed {
        require(_periodInDays * 1 days > CHANGE_PERIOD);

        CHANGE_PERIOD = _periodInDays * 1 days;
    }

    /// @notice In case something is left in contract, owner is able to withdraw it
    /// @param _token address of token to withdraw balance
    function withdrawToken(address _token) public onlyOwner {
        uint balance = ERC20(_token).balanceOf(address(this));
        ERC20(_token).safeTransfer(msg.sender, balance);
    }

    /// @notice In case something is left in contract, owner is able to withdraw it
    function withdrawEth() public onlyOwner {
        uint balance = address(this).balance;
        msg.sender.transfer(balance);
    }
}



/// @title Stores subscription information for Compound automatization
contract CompoundSubscriptions is AdminAuth {

    struct CompoundHolder {
        address user;
        uint128 minRatio;
        uint128 maxRatio;
        uint128 optimalRatioBoost;
        uint128 optimalRatioRepay;
        bool boostEnabled;
    }

    struct SubPosition {
        uint arrPos;
        bool subscribed;
    }

    CompoundHolder[] public subscribers;
    mapping (address => SubPosition) public subscribersPos;

    uint public changeIndex;

    event Subscribed(address indexed user);
    event Unsubscribed(address indexed user);
    event Updated(address indexed user);
    event ParamUpdates(address indexed user, uint128, uint128, uint128, uint128, bool);

    /// @dev Called by the DSProxy contract which owns the Compound position
    /// @notice Adds the users Compound poistion in the list of subscriptions so it can be monitored
    /// @param _minRatio Minimum ratio below which repay is triggered
    /// @param _maxRatio Maximum ratio after which boost is triggered
    /// @param _optimalBoost Ratio amount which boost should target
    /// @param _optimalRepay Ratio amount which repay should target
    /// @param _boostEnabled Boolean determing if boost is enabled
    function subscribe(uint128 _minRatio, uint128 _maxRatio, uint128 _optimalBoost, uint128 _optimalRepay, bool _boostEnabled) external {

        // if boost is not enabled, set max ratio to max uint
        uint128 localMaxRatio = _boostEnabled ? _maxRatio : uint128(-1);
        require(checkParams(_minRatio, localMaxRatio), "Must be correct params");

        SubPosition storage subInfo = subscribersPos[msg.sender];

        CompoundHolder memory subscription = CompoundHolder({
                minRatio: _minRatio,
                maxRatio: localMaxRatio,
                optimalRatioBoost: _optimalBoost,
                optimalRatioRepay: _optimalRepay,
                user: msg.sender,
                boostEnabled: _boostEnabled
            });

        changeIndex++;

        if (subInfo.subscribed) {
            subscribers[subInfo.arrPos] = subscription;

            emit Updated(msg.sender);
            emit ParamUpdates(msg.sender, _minRatio, localMaxRatio, _optimalBoost, _optimalRepay, _boostEnabled);
        } else {
            subscribers.push(subscription);

            subInfo.arrPos = subscribers.length - 1;
            subInfo.subscribed = true;

            emit Subscribed(msg.sender);
        }
    }

    /// @notice Called by the users DSProxy
    /// @dev Owner who subscribed cancels his subscription
    function unsubscribe() external {
        _unsubscribe(msg.sender);
    }

    /// @dev Checks limit if minRatio is bigger than max
    /// @param _minRatio Minimum ratio, bellow which repay can be triggered
    /// @param _maxRatio Maximum ratio, over which boost can be triggered
    /// @return Returns bool if the params are correct
    function checkParams(uint128 _minRatio, uint128 _maxRatio) internal pure returns (bool) {

        if (_minRatio > _maxRatio) {
            return false;
        }

        return true;
    }

    /// @dev Internal method to remove a subscriber from the list
    /// @param _user The actual address that owns the Compound position
    function _unsubscribe(address _user) internal {
        require(subscribers.length > 0, "Must have subscribers in the list");

        SubPosition storage subInfo = subscribersPos[_user];

        require(subInfo.subscribed, "Must first be subscribed");

        address lastOwner = subscribers[subscribers.length - 1].user;

        SubPosition storage subInfo2 = subscribersPos[lastOwner];
        subInfo2.arrPos = subInfo.arrPos;

        subscribers[subInfo.arrPos] = subscribers[subscribers.length - 1];
        subscribers.pop(); // remove last element and reduce arr length

        changeIndex++;
        subInfo.subscribed = false;
        subInfo.arrPos = 0;

        emit Unsubscribed(msg.sender);
    }

    /// @dev Checks if the user is subscribed
    /// @param _user The actual address that owns the Compound position
    /// @return If the user is subscribed
    function isSubscribed(address _user) public view returns (bool) {
        SubPosition storage subInfo = subscribersPos[_user];

        return subInfo.subscribed;
    }

    /// @dev Returns subscribtion information about a user
    /// @param _user The actual address that owns the Compound position
    /// @return Subscription information about the user if exists
    function getHolder(address _user) public view returns (CompoundHolder memory) {
        SubPosition storage subInfo = subscribersPos[_user];

        return subscribers[subInfo.arrPos];
    }

    /// @notice Helper method to return all the subscribed CDPs
    /// @return List of all subscribers
    function getSubscribers() public view returns (CompoundHolder[] memory) {
        return subscribers;
    }

    /// @notice Helper method for the frontend, returns all the subscribed CDPs paginated
    /// @param _page What page of subscribers you want
    /// @param _perPage Number of entries per page
    /// @return List of all subscribers for that page
    function getSubscribersByPage(uint _page, uint _perPage) public view returns (CompoundHolder[] memory) {
        CompoundHolder[] memory holders = new CompoundHolder[](_perPage);

        uint start = _page * _perPage;
        uint end = start + _perPage;

        end = (end > holders.length) ? holders.length : end;

        uint count = 0;
        for (uint i = start; i < end; i++) {
            holders[count] = subscribers[i];
            count++;
        }

        return holders;
    }

    ////////////// ADMIN METHODS ///////////////////

    /// @notice Admin function to unsubscribe a CDP
    /// @param _user The actual address that owns the Compound position
    function unsubscribeByAdmin(address _user) public onlyOwner {
        SubPosition storage subInfo = subscribersPos[_user];

        if (subInfo.subscribed) {
            _unsubscribe(_user);
        }
    }
} contract DSMath {
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x);
    }

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

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

    function div(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return x / y;
    }

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

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

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

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

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

    function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }

    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }

    function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, WAD), y / 2) / y;
    }

    function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(mul(x, RAY), y / 2) / y;
    }

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

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

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }
} contract DefisaverLogger {
    event LogEvent(
        address indexed contractAddress,
        address indexed caller,
        string indexed logName,
        bytes data
    );

    // solhint-disable-next-line func-name-mixedcase
    function Log(address _contract, address _caller, string memory _logName, bytes memory _data)
        public
    {
        emit LogEvent(_contract, _caller, _logName, _data);
    }
} abstract contract CompoundOracleInterface {
    function getUnderlyingPrice(address cToken) external view virtual returns (uint);
} abstract contract ComptrollerInterface {
    function enterMarkets(address[] calldata cTokens) external virtual returns (uint256[] memory);

    function exitMarket(address cToken) external virtual returns (uint256);

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

    function markets(address account) public virtual view returns (bool, uint256);

    function getAccountLiquidity(address account) external virtual view returns (uint256, uint256, uint256);

    function claimComp(address holder) virtual public;

    function oracle() public virtual view returns (address);
} abstract contract CTokenInterface is ERC20 {
    function mint(uint256 mintAmount) external virtual returns (uint256);

    // function mint() external virtual payable;

    function accrueInterest() public virtual returns (uint);

    function redeem(uint256 redeemTokens) external virtual returns (uint256);

    function redeemUnderlying(uint256 redeemAmount) external virtual returns (uint256);

    function borrow(uint256 borrowAmount) external virtual returns (uint256);

    function repayBorrow(uint256 repayAmount) external virtual returns (uint256);

    function repayBorrow() external virtual payable;

    function repayBorrowBehalf(address borrower, uint256 repayAmount) external virtual returns (uint256);

    function repayBorrowBehalf(address borrower) external virtual payable;

    function liquidateBorrow(address borrower, uint256 repayAmount, address cTokenCollateral)
        external virtual
        returns (uint256);

    function liquidateBorrow(address borrower, address cTokenCollateral) external virtual payable;

    function exchangeRateCurrent() external virtual returns (uint256);

    function supplyRatePerBlock() external virtual returns (uint256);

    function borrowRatePerBlock() external virtual returns (uint256);

    function totalReserves() external virtual returns (uint256);

    function reserveFactorMantissa() external virtual returns (uint256);

    function borrowBalanceCurrent(address account) external virtual returns (uint256);

    function totalBorrowsCurrent() external virtual returns (uint256);

    function getCash() external virtual returns (uint256);

    function balanceOfUnderlying(address owner) external virtual returns (uint256);

    function underlying() external virtual returns (address);

    function getAccountSnapshot(address account) external virtual view returns (uint, uint, uint, uint);
} contract CarefulMath {

    /**
     * @dev Possible error codes that we can return
     */
    enum MathError {
        NO_ERROR,
        DIVISION_BY_ZERO,
        INTEGER_OVERFLOW,
        INTEGER_UNDERFLOW
    }

    /**
    * @dev Multiplies two numbers, returns an error on overflow.
    */
    function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (a == 0) {
            return (MathError.NO_ERROR, 0);
        }

        uint c = a * b;

        if (c / a != b) {
            return (MathError.INTEGER_OVERFLOW, 0);
        } else {
            return (MathError.NO_ERROR, c);
        }
    }

    /**
    * @dev Integer division of two numbers, truncating the quotient.
    */
    function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (b == 0) {
            return (MathError.DIVISION_BY_ZERO, 0);
        }

        return (MathError.NO_ERROR, a / b);
    }

    /**
    * @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
    */
    function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
        if (b <= a) {
            return (MathError.NO_ERROR, a - b);
        } else {
            return (MathError.INTEGER_UNDERFLOW, 0);
        }
    }

    /**
    * @dev Adds two numbers, returns an error on overflow.
    */
    function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
        uint c = a + b;

        if (c >= a) {
            return (MathError.NO_ERROR, c);
        } else {
            return (MathError.INTEGER_OVERFLOW, 0);
        }
    }

    /**
    * @dev add a and b and then subtract c
    */
    function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
        (MathError err0, uint sum) = addUInt(a, b);

        if (err0 != MathError.NO_ERROR) {
            return (err0, 0);
        }

        return subUInt(sum, c);
    }
} contract Exponential is CarefulMath {
    uint constant expScale = 1e18;
    uint constant halfExpScale = expScale/2;
    uint constant mantissaOne = expScale;

    struct Exp {
        uint mantissa;
    }

    /**
     * @dev Creates an exponential from numerator and denominator values.
     *      Note: Returns an error if (`num` * 10e18) > MAX_INT,
     *            or if `denom` is zero.
     */
    function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
        (MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        (MathError err1, uint rational) = divUInt(scaledNumerator, denom);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: rational}));
    }

    /**
     * @dev Adds two exponentials, returning a new exponential.
     */
    function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
        (MathError error, uint result) = addUInt(a.mantissa, b.mantissa);

        return (error, Exp({mantissa: result}));
    }

    /**
     * @dev Subtracts two exponentials, returning a new exponential.
     */
    function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
        (MathError error, uint result) = subUInt(a.mantissa, b.mantissa);

        return (error, Exp({mantissa: result}));
    }

    /**
     * @dev Multiply an Exp by a scalar, returning a new Exp.
     */
    function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
        (MathError err0, uint scaledMantissa) = mulUInt(a.mantissa, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: scaledMantissa}));
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return (MathError.NO_ERROR, truncate(product));
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
        (MathError err, Exp memory product) = mulScalar(a, scalar);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return addUInt(truncate(product), addend);
    }

    /**
     * @dev Divide an Exp by a scalar, returning a new Exp.
     */
    function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
        (MathError err0, uint descaledMantissa) = divUInt(a.mantissa, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        return (MathError.NO_ERROR, Exp({mantissa: descaledMantissa}));
    }

    /**
     * @dev Divide a scalar by an Exp, returning a new Exp.
     */
    function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (MathError, Exp memory) {
        /*
          We are doing this as:
          getExp(mulUInt(expScale, scalar), divisor.mantissa)

          How it works:
          Exp = a / b;
          Scalar = s;
          `s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
        */
        (MathError err0, uint numerator) = mulUInt(expScale, scalar);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }
        return getExp(numerator, divisor.mantissa);
    }

    /**
     * @dev Divide a scalar by an Exp, then truncate to return an unsigned integer.
     */
    function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
        (MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
        if (err != MathError.NO_ERROR) {
            return (err, 0);
        }

        return (MathError.NO_ERROR, truncate(fraction));
    }

    /**
     * @dev Multiplies two exponentials, returning a new exponential.
     */
    function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {

        (MathError err0, uint doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
        if (err0 != MathError.NO_ERROR) {
            return (err0, Exp({mantissa: 0}));
        }

        // We add half the scale before dividing so that we get rounding instead of truncation.
        //  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717
        // Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
        (MathError err1, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
        if (err1 != MathError.NO_ERROR) {
            return (err1, Exp({mantissa: 0}));
        }

        (MathError err2, uint product) = divUInt(doubleScaledProductWithHalfScale, expScale);
        // The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.
        assert(err2 == MathError.NO_ERROR);

        return (MathError.NO_ERROR, Exp({mantissa: product}));
    }

    /**
     * @dev Multiplies two exponentials given their mantissas, returning a new exponential.
     */
    function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) {
        return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
    }

    /**
     * @dev Multiplies three exponentials, returning a new exponential.
     */
    function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (MathError, Exp memory) {
        (MathError err, Exp memory ab) = mulExp(a, b);
        if (err != MathError.NO_ERROR) {
            return (err, ab);
        }
        return mulExp(ab, c);
    }

    /**
     * @dev Divides two exponentials, returning a new exponential.
     *     (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
     *  which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
     */
    function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
        return getExp(a.mantissa, b.mantissa);
    }

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

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

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

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

    /**
     * @dev returns true if Exp is exactly zero
     */
    function isZeroExp(Exp memory value) pure internal returns (bool) {
        return value.mantissa == 0;
    }
} contract CompoundSafetyRatio is Exponential, DSMath {
    // solhint-disable-next-line const-name-snakecase
    ComptrollerInterface public constant comp = ComptrollerInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);

    /// @notice Calcualted the ratio of debt / adjusted collateral
    /// @param _user Address of the user
    function getSafetyRatio(address _user) public view returns (uint) {
        // For each asset the account is in
        address[] memory assets = comp.getAssetsIn(_user);
        address oracleAddr = comp.oracle();


        uint sumCollateral = 0;
        uint sumBorrow = 0;

        for (uint i = 0; i < assets.length; i++) {
            address asset = assets[i];

            (, uint cTokenBalance, uint borrowBalance, uint exchangeRateMantissa)
                                        = CTokenInterface(asset).getAccountSnapshot(_user);

            Exp memory oraclePrice;

            if (cTokenBalance != 0 || borrowBalance != 0) {
                oraclePrice = Exp({mantissa: CompoundOracleInterface(oracleAddr).getUnderlyingPrice(asset)});
            }

            // Sum up collateral in Usd
            if (cTokenBalance != 0) {

                (, uint collFactorMantissa) = comp.markets(address(asset));

                Exp memory collateralFactor = Exp({mantissa: collFactorMantissa});
                Exp memory exchangeRate = Exp({mantissa: exchangeRateMantissa});

                (, Exp memory tokensToUsd) = mulExp3(collateralFactor, exchangeRate, oraclePrice);

                (, sumCollateral) = mulScalarTruncateAddUInt(tokensToUsd, cTokenBalance, sumCollateral);
            }

            // Sum up debt in Usd
            if (borrowBalance != 0) {
                (, sumBorrow) = mulScalarTruncateAddUInt(oraclePrice, borrowBalance, sumBorrow);
            }
        }

        if (sumBorrow == 0) return uint(-1);

        uint borrowPowerUsed = (sumBorrow * 10**18) / sumCollateral;
        return wdiv(1e18, borrowPowerUsed);
    }
} abstract contract TokenInterface {
    function allowance(address, address) public virtual returns (uint256);

    function balanceOf(address) public virtual returns (uint256);

    function approve(address, uint256) public virtual;

    function transfer(address, uint256) public virtual returns (bool);

    function transferFrom(address, address, uint256) public virtual returns (bool);

    function deposit() public virtual payable;

    function withdraw(uint256) public virtual;
} interface ExchangeInterfaceV2 {
    function sell(address _srcAddr, address _destAddr, uint _srcAmount) external payable returns (uint);

    function buy(address _srcAddr, address _destAddr, uint _destAmount) external payable returns(uint);

    function getSellRate(address _srcAddr, address _destAddr, uint _srcAmount) external view returns (uint);

    function getBuyRate(address _srcAddr, address _destAddr, uint _srcAmount) external view returns (uint);
} contract ZrxAllowlist is AdminAuth {

    mapping (address => bool) public zrxAllowlist;

    function setAllowlistAddr(address _zrxAddr, bool _state) public onlyOwner {
        zrxAllowlist[_zrxAddr] = _state;
    }

    function isZrxAddr(address _zrxAddr) public view returns (bool) {
        return zrxAllowlist[_zrxAddr];
    }
} contract Discount {
    address public owner;
    mapping(address => CustomServiceFee) public serviceFees;

    uint256 constant MAX_SERVICE_FEE = 400;

    struct CustomServiceFee {
        bool active;
        uint256 amount;
    }

    constructor() public {
        owner = msg.sender;
    }

    function isCustomFeeSet(address _user) public view returns (bool) {
        return serviceFees[_user].active;
    }

    function getCustomServiceFee(address _user) public view returns (uint256) {
        return serviceFees[_user].amount;
    }

    function setServiceFee(address _user, uint256 _fee) public {
        require(msg.sender == owner, "Only owner");
        require(_fee >= MAX_SERVICE_FEE || _fee == 0);

        serviceFees[_user] = CustomServiceFee({active: true, amount: _fee});
    }

    function disableServiceFee(address _user) public {
        require(msg.sender == owner, "Only owner");

        serviceFees[_user] = CustomServiceFee({active: false, amount: 0});
    }
} contract SaverExchangeHelper {

    using SafeERC20 for ERC20;

    address public constant KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    address payable public constant WALLET_ID = 0x322d58b9E75a6918f7e7849AEe0fF09369977e08;
    address public constant DISCOUNT_ADDRESS = 0x1b14E8D511c9A4395425314f849bD737BAF8208F;
    address public constant SAVER_EXCHANGE_REGISTRY = 0x25dd3F51e0C3c3Ff164DDC02A8E4D65Bb9cBB12D;

    address public constant ERC20_PROXY_0X = 0x95E6F48254609A6ee006F7D493c8e5fB97094ceF;
    address public constant ZRX_ALLOWLIST_ADDR = 0x019739e288973F92bDD3c1d87178E206E51fd911;


    function getDecimals(address _token) internal view returns (uint256) {
        if (_token == KYBER_ETH_ADDRESS) return 18;

        return ERC20(_token).decimals();
    }

    function getBalance(address _tokenAddr) internal view returns (uint balance) {
        if (_tokenAddr == KYBER_ETH_ADDRESS) {
            balance = address(this).balance;
        } else {
            balance = ERC20(_tokenAddr).balanceOf(address(this));
        }
    }

    function approve0xProxy(address _tokenAddr, uint _amount) internal {
        if (_tokenAddr != KYBER_ETH_ADDRESS) {
            ERC20(_tokenAddr).safeApprove(address(ERC20_PROXY_0X), _amount);
        }
    }

    function sendLeftover(address _srcAddr, address _destAddr, address payable _to) internal {
        // send back any leftover ether or tokens
        if (address(this).balance > 0) {
            _to.transfer(address(this).balance);
        }

        if (getBalance(_srcAddr) > 0) {
            ERC20(_srcAddr).safeTransfer(_to, getBalance(_srcAddr));
        }

        if (getBalance(_destAddr) > 0) {
            ERC20(_destAddr).safeTransfer(_to, getBalance(_destAddr));
        }
    }

    function sliceUint(bytes memory bs, uint256 start) internal pure returns (uint256) {
        require(bs.length >= start + 32, "slicing out of range");

        uint256 x;
        assembly {
            x := mload(add(bs, add(0x20, start)))
        }

        return x;
    }
} contract SaverExchangeRegistry is AdminAuth {

	mapping(address => bool) private wrappers;

	constructor() public {
		wrappers[0x880A845A85F843a5c67DB2061623c6Fc3bB4c511] = true;
		wrappers[0x4c9B55f2083629A1F7aDa257ae984E03096eCD25] = true;
		wrappers[0x42A9237b872368E1bec4Ca8D26A928D7d39d338C] = true;
	}

	function addWrapper(address _wrapper) public onlyOwner {
		wrappers[_wrapper] = true;
	}

	function removeWrapper(address _wrapper) public onlyOwner {
		wrappers[_wrapper] = false;
	}

	function isWrapper(address _wrapper) public view returns(bool) {
		return wrappers[_wrapper];
	}
}








contract SaverExchangeCore is SaverExchangeHelper, DSMath {

    // first is empty to keep the legacy order in place
    enum ExchangeType { _, OASIS, KYBER, UNISWAP, ZEROX }

    enum ActionType { SELL, BUY }

    struct ExchangeData {
        address srcAddr;
        address destAddr;
        uint srcAmount;
        uint destAmount;
        uint minPrice;
        address wrapper;
        address exchangeAddr;
        bytes callData;
        uint256 price0x;
    }

    /// @notice Internal method that preforms a sell on 0x/on-chain
    /// @dev Usefull for other DFS contract to integrate for exchanging
    /// @param exData Exchange data struct
    /// @return (address, uint) Address of the wrapper used and destAmount
    function _sell(ExchangeData memory exData) internal returns (address, uint) {

        address wrapper;
        uint swapedTokens;
        bool success;
        uint tokensLeft = exData.srcAmount;

        // if selling eth, convert to weth
        if (exData.srcAddr == KYBER_ETH_ADDRESS) {
            exData.srcAddr = ethToWethAddr(exData.srcAddr);
            TokenInterface(WETH_ADDRESS).deposit.value(exData.srcAmount)();
        }

        // Try 0x first and then fallback on specific wrapper
        if (exData.price0x > 0) {
            approve0xProxy(exData.srcAddr, exData.srcAmount);

            (success, swapedTokens, tokensLeft) = takeOrder(exData, address(this).balance, ActionType.SELL);

            if (success) {
                wrapper = exData.exchangeAddr;
            }
        }

        // fallback to desired wrapper if 0x failed
        if (!success) {
            swapedTokens = saverSwap(exData, ActionType.SELL);
            wrapper = exData.wrapper;
        }

        require(getBalance(exData.destAddr) >= wmul(exData.minPrice, exData.srcAmount), "Final amount isn't correct");

        // if anything is left in weth, pull it to user as eth
        if (getBalance(WETH_ADDRESS) > 0) {
            TokenInterface(WETH_ADDRESS).withdraw(
                TokenInterface(WETH_ADDRESS).balanceOf(address(this))
            );
        }            

        return (wrapper, swapedTokens);
    }

    /// @notice Internal method that preforms a buy on 0x/on-chain
    /// @dev Usefull for other DFS contract to integrate for exchanging
    /// @param exData Exchange data struct
    /// @return (address, uint) Address of the wrapper used and srcAmount
    function _buy(ExchangeData memory exData) internal returns (address, uint) {

        address wrapper;
        uint swapedTokens;
        bool success;

        require(exData.destAmount != 0, "Dest amount must be specified");

        // if selling eth, convert to weth
        if (exData.srcAddr == KYBER_ETH_ADDRESS) {
            exData.srcAddr = ethToWethAddr(exData.srcAddr);
            TokenInterface(WETH_ADDRESS).deposit.value(exData.srcAmount)();
        }

        if (exData.price0x > 0) { 
            approve0xProxy(exData.srcAddr, exData.srcAmount);

            (success, swapedTokens,) = takeOrder(exData, address(this).balance, ActionType.BUY);

            if (success) {
                wrapper = exData.exchangeAddr;
            }
        }

        // fallback to desired wrapper if 0x failed
        if (!success) {
            swapedTokens = saverSwap(exData, ActionType.BUY);
            wrapper = exData.wrapper;
        }

        require(getBalance(exData.destAddr) >= exData.destAmount, "Final amount isn't correct");

        // if anything is left in weth, pull it to user as eth
        if (getBalance(WETH_ADDRESS) > 0) {
            TokenInterface(WETH_ADDRESS).withdraw(
                TokenInterface(WETH_ADDRESS).balanceOf(address(this))
            );
        }

        return (wrapper, getBalance(exData.destAddr));
    }

    /// @notice Takes order from 0x and returns bool indicating if it is successful
    /// @param _exData Exchange data
    /// @param _ethAmount Ether fee needed for 0x order
    function takeOrder(
        ExchangeData memory _exData,
        uint256 _ethAmount,
        ActionType _type
    ) private returns (bool success, uint256, uint256) {

        // write in the exact amount we are selling/buing in an order
        if (_type == ActionType.SELL) {
            writeUint256(_exData.callData, 36, _exData.srcAmount);
        } else {
            writeUint256(_exData.callData, 36, _exData.destAmount);
        }

        if (ZrxAllowlist(ZRX_ALLOWLIST_ADDR).isZrxAddr(_exData.exchangeAddr)) {
            (success, ) = _exData.exchangeAddr.call{value: _ethAmount}(_exData.callData);
        } else {
            success = false;
        }

        uint256 tokensSwaped = 0;
        uint256 tokensLeft = _exData.srcAmount;

        if (success) {
            // check to see if any _src tokens are left over after exchange
            tokensLeft = getBalance(_exData.srcAddr);

            // convert weth -> eth if needed
            if (_exData.destAddr == KYBER_ETH_ADDRESS) {
                TokenInterface(WETH_ADDRESS).withdraw(
                    TokenInterface(WETH_ADDRESS).balanceOf(address(this))
                );
            }

            // get the current balance of the swaped tokens
            tokensSwaped = getBalance(_exData.destAddr);
        }

        return (success, tokensSwaped, tokensLeft);
    }

    /// @notice Calls wraper contract for exchage to preform an on-chain swap
    /// @param _exData Exchange data struct
    /// @param _type Type of action SELL|BUY
    /// @return swapedTokens For Sell that the destAmount, for Buy thats the srcAmount
    function saverSwap(ExchangeData memory _exData, ActionType _type) internal returns (uint swapedTokens) {
        require(SaverExchangeRegistry(SAVER_EXCHANGE_REGISTRY).isWrapper(_exData.wrapper), "Wrapper is not valid");

        uint ethValue = 0;

        ERC20(_exData.srcAddr).safeTransfer(_exData.wrapper, _exData.srcAmount);
        
        if (_type == ActionType.SELL) {
            swapedTokens = ExchangeInterfaceV2(_exData.wrapper).
                    sell{value: ethValue}(_exData.srcAddr, _exData.destAddr, _exData.srcAmount);
        } else {
            swapedTokens = ExchangeInterfaceV2(_exData.wrapper).
                    buy{value: ethValue}(_exData.srcAddr, _exData.destAddr, _exData.destAmount);
        }
    }

    function writeUint256(bytes memory _b, uint256 _index, uint _input) internal pure {
        if (_b.length < _index + 32) {
            revert("Incorrent lengt while writting bytes32");
        }

        bytes32 input = bytes32(_input);

        _index += 32;

        // Read the bytes32 from array memory
        assembly {
            mstore(add(_b, _index), input)
        }
    }

    /// @notice Converts Kybers Eth address -> Weth
    /// @param _src Input address
    function ethToWethAddr(address _src) internal pure returns (address) {
        return _src == KYBER_ETH_ADDRESS ? WETH_ADDRESS : _src;
    }

    function packExchangeData(ExchangeData memory _exData) public pure returns(bytes memory) {     
        // splitting in two different bytes and encoding all because of stack too deep in decoding part

        bytes memory part1 = abi.encode(
            _exData.srcAddr,
            _exData.destAddr,
            _exData.srcAmount,
            _exData.destAmount
        );

        bytes memory part2 = abi.encode(
            _exData.minPrice,
            _exData.wrapper,
            _exData.exchangeAddr,
            _exData.callData,
            _exData.price0x
        );


        return abi.encode(part1, part2);
    }

    function unpackExchangeData(bytes memory _data) public pure returns(ExchangeData memory _exData) {
        (
            bytes memory part1,
            bytes memory part2
        ) = abi.decode(_data, (bytes,bytes));

        (
            _exData.srcAddr,
            _exData.destAddr,
            _exData.srcAmount,
            _exData.destAmount
        ) = abi.decode(part1, (address,address,uint256,uint256));
        
        (
            _exData.minPrice,
            _exData.wrapper,
            _exData.exchangeAddr,
            _exData.callData,
            _exData.price0x  
        )
        = abi.decode(part2, (uint256,address,address,bytes,uint256));
    }

    // solhint-disable-next-line no-empty-blocks
    receive() external virtual payable {}
}











/// @title Contract implements logic of calling boost/repay in the automatic system
contract CompoundMonitor is AdminAuth, DSMath, CompoundSafetyRatio, GasBurner {

    using SafeERC20 for ERC20;

    enum Method { Boost, Repay }

    uint public REPAY_GAS_TOKEN = 20;
    uint public BOOST_GAS_TOKEN = 20;

    uint constant public MAX_GAS_PRICE = 500000000000; // 500 gwei

    uint public REPAY_GAS_COST = 2000000;
    uint public BOOST_GAS_COST = 2000000;

    address public constant GAS_TOKEN_INTERFACE_ADDRESS = 0x0000000000b3F879cb30FE243b4Dfee438691c04;
    address public constant DEFISAVER_LOGGER = 0x5c55B921f590a89C1Ebe84dF170E655a82b62126;

    CompoundMonitorProxy public compoundMonitorProxy;
    CompoundSubscriptions public subscriptionsContract;
    address public compoundFlashLoanTakerAddress;

    DefisaverLogger public logger = DefisaverLogger(DEFISAVER_LOGGER);

    /// @dev Addresses that are able to call methods for repay and boost
    mapping(address => bool) public approvedCallers;

    modifier onlyApproved() {
        require(approvedCallers[msg.sender]);
        _;
    }

    /// @param _compoundMonitorProxy Proxy contracts that actually is authorized to call DSProxy
    /// @param _subscriptions Subscriptions contract for Compound positions
    /// @param _compoundFlashLoanTaker Contract that actually performs Repay/Boost
    constructor(address _compoundMonitorProxy, address _subscriptions, address _compoundFlashLoanTaker) public {
        approvedCallers[msg.sender] = true;
        approvedCallers[0x776B4a13093e30B05781F97F6A4565B6aa8BE330] = true;

        compoundMonitorProxy = CompoundMonitorProxy(_compoundMonitorProxy);
        subscriptionsContract = CompoundSubscriptions(_subscriptions);
        compoundFlashLoanTakerAddress = _compoundFlashLoanTaker;
    }

    /// @notice Bots call this method to repay for user when conditions are met
    /// @dev If the contract ownes gas token it will try and use it for gas price reduction
    /// @param _exData Exchange data
    /// @param _cAddresses cTokens addreses and exchange [cCollAddress, cBorrowAddress, exchangeAddress]
    /// @param _user The actual address that owns the Compound position
    function repayFor(
        SaverExchangeCore.ExchangeData memory _exData,
        address[2] memory _cAddresses, // cCollAddress, cBorrowAddress
        address _user
    ) public payable onlyApproved burnGas(REPAY_GAS_TOKEN) {

        (bool isAllowed, uint ratioBefore) = canCall(Method.Repay, _user);
        require(isAllowed); // check if conditions are met

        uint256 gasCost = calcGasCost(REPAY_GAS_COST);

        compoundMonitorProxy.callExecute{value: msg.value}(
            _user,
            compoundFlashLoanTakerAddress,
            abi.encodeWithSignature(
                "repayWithLoan((address,address,uint256,uint256,uint256,address,address,bytes,uint256),address[2],uint256)",
                _exData,
                _cAddresses,
                gasCost
            )
        );

        (bool isGoodRatio, uint ratioAfter) = ratioGoodAfter(Method.Repay, _user);
        require(isGoodRatio); // check if the after result of the actions is good

        returnEth();

        logger.Log(address(this), _user, "AutomaticCompoundRepay", abi.encode(ratioBefore, ratioAfter));
    }

    /// @notice Bots call this method to boost for user when conditions are met
    /// @dev If the contract ownes gas token it will try and use it for gas price reduction
    /// @param _exData Exchange data
    /// @param _cAddresses cTokens addreses and exchange [cCollAddress, cBorrowAddress, exchangeAddress]
    /// @param _user The actual address that owns the Compound position
    function boostFor(
        SaverExchangeCore.ExchangeData memory _exData,
        address[2] memory _cAddresses, // cCollAddress, cBorrowAddress
        address _user
    ) public payable onlyApproved burnGas(BOOST_GAS_TOKEN) {

        (bool isAllowed, uint ratioBefore) = canCall(Method.Boost, _user);
        require(isAllowed); // check if conditions are met

        uint256 gasCost = calcGasCost(BOOST_GAS_COST);

        compoundMonitorProxy.callExecute{value: msg.value}(
            _user,
            compoundFlashLoanTakerAddress,
            abi.encodeWithSignature(
                "boostWithLoan((address,address,uint256,uint256,uint256,address,address,bytes,uint256),address[2],uint256)",
                _exData,
                _cAddresses,
                gasCost
            )
        );


        (bool isGoodRatio, uint ratioAfter) = ratioGoodAfter(Method.Boost, _user);
        require(isGoodRatio);  // check if the after result of the actions is good

        returnEth();

        logger.Log(address(this), _user, "AutomaticCompoundBoost", abi.encode(ratioBefore, ratioAfter));
    }

/******************* INTERNAL METHODS ********************************/
    function returnEth() internal {
        // return if some eth left
        if (address(this).balance > 0) {
            msg.sender.transfer(address(this).balance);
        }
    }

/******************* STATIC METHODS ********************************/

    /// @notice Checks if Boost/Repay could be triggered for the CDP
    /// @dev Called by MCDMonitor to enforce the min/max check
    /// @param _method Type of action to be called
    /// @param _user The actual address that owns the Compound position
    /// @return Boolean if it can be called and the ratio
    function canCall(Method _method, address _user) public view returns(bool, uint) {
        bool subscribed = subscriptionsContract.isSubscribed(_user);
        CompoundSubscriptions.CompoundHolder memory holder = subscriptionsContract.getHolder(_user);

        // check if cdp is subscribed
        if (!subscribed) return (false, 0);

        // check if boost and boost allowed
        if (_method == Method.Boost && !holder.boostEnabled) return (false, 0);

        uint currRatio = getSafetyRatio(_user);

        if (_method == Method.Repay) {
            return (currRatio < holder.minRatio, currRatio);
        } else if (_method == Method.Boost) {
            return (currRatio > holder.maxRatio, currRatio);
        }
    }

    /// @dev After the Boost/Repay check if the ratio doesn't trigger another call
    /// @param _method Type of action to be called
    /// @param _user The actual address that owns the Compound position
    /// @return Boolean if the recent action preformed correctly and the ratio
    function ratioGoodAfter(Method _method, address _user) public view returns(bool, uint) {
        CompoundSubscriptions.CompoundHolder memory holder;

        holder= subscriptionsContract.getHolder(_user);

        uint currRatio = getSafetyRatio(_user);

        if (_method == Method.Repay) {
            return (currRatio < holder.maxRatio, currRatio);
        } else if (_method == Method.Boost) {
            return (currRatio > holder.minRatio, currRatio);
        }
    }

    /// @notice Calculates gas cost (in Eth) of tx
    /// @dev Gas price is limited to MAX_GAS_PRICE to prevent attack of draining user CDP
    /// @param _gasAmount Amount of gas used for the tx
    function calcGasCost(uint _gasAmount) public view returns (uint) {
        uint gasPrice = tx.gasprice <= MAX_GAS_PRICE ? tx.gasprice : MAX_GAS_PRICE;

        return mul(gasPrice, _gasAmount);
    }

/******************* OWNER ONLY OPERATIONS ********************************/

    /// @notice Allows owner to change gas cost for boost operation, but only up to 3 millions
    /// @param _gasCost New gas cost for boost method
    function changeBoostGasCost(uint _gasCost) public onlyOwner {
        require(_gasCost < 3000000);

        BOOST_GAS_COST = _gasCost;
    }

    /// @notice Allows owner to change gas cost for repay operation, but only up to 3 millions
    /// @param _gasCost New gas cost for repay method
    function changeRepayGasCost(uint _gasCost) public onlyOwner {
        require(_gasCost < 3000000);

        REPAY_GAS_COST = _gasCost;
    }

    /// @notice Adds a new bot address which will be able to call repay/boost
    /// @param _caller Bot address
    function addCaller(address _caller) public onlyOwner {
        approvedCallers[_caller] = true;
    }

    /// @notice Removes a bot address so it can't call repay/boost
    /// @param _caller Bot address
    function removeCaller(address _caller) public onlyOwner {
        approvedCallers[_caller] = false;
    }

}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_compoundMonitorProxy","type":"address"},{"internalType":"address","name":"_subscriptions","type":"address"},{"internalType":"address","name":"_compoundFlashLoanTaker","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BOOST_GAS_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BOOST_GAS_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFISAVER_LOGGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAS_TOKEN_INTERFACE_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_GAS_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPAY_GAS_COST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REPAY_GAS_TOKEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"addCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedCallers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"srcAddr","type":"address"},{"internalType":"address","name":"destAddr","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"address","name":"exchangeAddr","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"price0x","type":"uint256"}],"internalType":"struct SaverExchangeCore.ExchangeData","name":"_exData","type":"tuple"},{"internalType":"address[2]","name":"_cAddresses","type":"address[2]"},{"internalType":"address","name":"_user","type":"address"}],"name":"boostFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasAmount","type":"uint256"}],"name":"calcGasCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum CompoundMonitor.Method","name":"_method","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"canCall","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasCost","type":"uint256"}],"name":"changeBoostGasCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gasCost","type":"uint256"}],"name":"changeRepayGasCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"comp","outputs":[{"internalType":"contract ComptrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compoundFlashLoanTakerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compoundMonitorProxy","outputs":[{"internalType":"contract CompoundMonitorProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gasToken","outputs":[{"internalType":"contract GasTokenInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getSafetyRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"logger","outputs":[{"internalType":"contract DefisaverLogger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum CompoundMonitor.Method","name":"_method","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"ratioGoodAfter","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_caller","type":"address"}],"name":"removeCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"srcAddr","type":"address"},{"internalType":"address","name":"destAddr","type":"address"},{"internalType":"uint256","name":"srcAmount","type":"uint256"},{"internalType":"uint256","name":"destAmount","type":"uint256"},{"internalType":"uint256","name":"minPrice","type":"uint256"},{"internalType":"address","name":"wrapper","type":"address"},{"internalType":"address","name":"exchangeAddr","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"price0x","type":"uint256"}],"internalType":"struct SaverExchangeCore.ExchangeData","name":"_exData","type":"tuple"},{"internalType":"address[2]","name":"_cAddresses","type":"address[2]"},{"internalType":"address","name":"_user","type":"address"}],"name":"repayFor","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdminByAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"setAdminByOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwnerByAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"subscriptionsContract","outputs":[{"internalType":"contract CompoundSubscriptions","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawStuckFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405260146002819055600355621e84806004819055600555600980546001600160a01b031916735c55b921f590a89c1ebe84df170e655a82b621261790553480156200004d57600080fd5b506040516200239d3803806200239d83398101604081905262000070916200011b565b60008054336001600160a01b0319918216811783558252600a60205260408220805460ff19908116600190811790925573776b4a13093e30b05781f97f6a4565b6aa8be3309093527f48759b72d37a678826f270ac477a0f7ab04900700868789bbb8817abe75bc7598054909316179091556006805482166001600160a01b039586161790556007805482169385169390931790925560088054909216921691909117905562000187565b60008060006060848603121562000130578283fd5b83516200013d816200016e565b602085015190935062000150816200016e565b604085015190925062000163816200016e565b809150509250925092565b6001600160a01b03811681146200018457600080fd5b50565b61220680620001976000396000f3fe6080604052600436106101d85760003560e01c806379521f0211610102578063c91d59fe11610095578063ec23ef2711610064578063ec23ef27146104bf578063eef21cd2146104d4578063f24ccbfe146104f4578063f851a44014610509576101d8565b8063c91d59fe146102c2578063d98bb5b11461046a578063deca5f881461048a578063e3bbb4f1146104aa576101d8565b8063a71975af116100d1578063a71975af146103dd578063a7304bf71461040a578063a8c903231461042a578063bfc361721461044a576101d8565b806379521f021461038b5780638da5cb5b146103a057806398a3f265146103b5578063a56f9718146103c8576101d8565b806332ac5cd21161017a57806341c0e1b51161014957806341c0e1b5146103215780634d3f199e14610336578063696806c014610356578063747293fb1461036b576101d8565b806332ac5cd2146102c257806336fc603f146102d757806339df1878146102ec5780633a12832214610301576101d8565b806318bf60e1116101b657806318bf60e11461024b5780631e48907b1461026d57806329ad0f361461028d5780632a56f602146102a2576101d8565b806306d5e37e146101dd578063109d0af8146102145780631391989414610236575b600080fd5b3480156101e957600080fd5b506101fd6101f8366004611c1e565b61051e565b60405161020b929190611ff4565b60405180910390f35b34801561022057600080fd5b506102296106ec565b60405161020b9190611ed3565b610249610244366004611ce2565b610704565b005b34801561025757600080fd5b506102606109c9565b60405161020b9190612151565b34801561027957600080fd5b50610249610288366004611abd565b6109cf565b34801561029957600080fd5b50610229610a08565b3480156102ae57600080fd5b506102606102bd366004611de7565b610a17565b3480156102ce57600080fd5b50610229610a49565b3480156102e357600080fd5b50610260610a5c565b3480156102f857600080fd5b50610229610a62565b34801561030d57600080fd5b5061024961031c366004611afc565b610a7a565b34801561032d57600080fd5b50610249610b19565b34801561034257600080fd5b50610249610351366004611de7565b610b3e565b34801561036257600080fd5b50610260610b69565b34801561037757600080fd5b50610249610386366004611abd565b610b6f565b34801561039757600080fd5b50610229610baa565b3480156103ac57600080fd5b50610229610bb9565b6102496103c3366004611ce2565b610bc8565b3480156103d457600080fd5b50610260610e50565b3480156103e957600080fd5b506103fd6103f8366004611abd565b610e56565b60405161020b9190611fe9565b34801561041657600080fd5b50610249610425366004611abd565b610e6b565b34801561043657600080fd5b50610249610445366004611de7565b610ea4565b34801561045657600080fd5b506101fd610465366004611c1e565b610ecf565b34801561047657600080fd5b50610260610485366004611abd565b610fce565b34801561049657600080fd5b506102496104a5366004611abd565b611393565b3480156104b657600080fd5b506102606113c0565b3480156104cb57600080fd5b506102296113c9565b3480156104e057600080fd5b506102496104ef366004611abd565b6113d8565b34801561050057600080fd5b50610229611410565b34801561051557600080fd5b5061022961141f565b600754604051632e4aba1f60e21b8152600091829182916001600160a01b03169063b92ae87c90610553908790600401611ed3565b60206040518083038186803b15801561056b57600080fd5b505afa15801561057f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a39190611bd2565b90506105ad61196c565b60075460405163335d71f560e21b81526001600160a01b039091169063cd75c7d4906105dd908890600401611ed3565b60c06040518083038186803b1580156105f557600080fd5b505afa158015610609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062d9190611c59565b9050816106435750600092508291506106e59050565b600086600181111561065157fe5b14801561066057508060a00151155b156106745750600092508291506106e59050565b600061067f86610fce565b9050600187600181111561068f57fe5b14156106b1576020909101516001600160801b03168110935091506106e59050565b60008760018111156106bf57fe5b14156106e1576040909101516001600160801b03168111935091506106e59050565b5050505b9250929050565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b336000908152600a602052604090205460ff1661072057600080fd5b6003546040516370a0823160e01b815281906eb3f879cb30fe243b4dfee438691c04906370a0823190610757903090600401611ed3565b60206040518083038186803b15801561076f57600080fd5b505afa158015610783573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a79190611dff565b106108325760405163d8ccd0f360e01b81526eb3f879cb30fe243b4dfee438691c049063d8ccd0f3906107de908490600401612151565b602060405180830381600087803b1580156107f857600080fd5b505af115801561080c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108309190611bd2565b505b60008061084060008561051e565b915091508161084e57600080fd5b600061085b600554610a17565b6006546008546040519293506001600160a01b0391821692638a0e833f9234928a92911690610892908d908d908990602401612098565b60408051601f198184030181529181526020820180516001600160e01b031663f708847b60e01b1790525160e086901b6001600160e01b03191681526108dd93929190600401611ee7565b6000604051808303818588803b1580156108f657600080fd5b505af115801561090a573d6000803e3d6000fd5b505050505060008061091d600088610ecf565b915091508161092b57600080fd5b61093361142e565b6009546040516001600160a01b039091169063d061ce509030908a9061095f908990879060200161215a565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161098c93929190611f76565b600060405180830381600087803b1580156109a657600080fd5b505af11580156109ba573d6000803e3d6000fd5b50505050505050505050505050565b60055481565b6001546001600160a01b031633146109e657600080fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b031681565b60008064746a5288003a1115610a325764746a528800610a34565b3a5b9050610a408184611464565b9150505b919050565b6eb3f879cb30fe243b4dfee438691c0481565b60045481565b735c55b921f590a89c1ebe84df170e655a82b6212681565b6000546001600160a01b03163314610a9157600080fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0383161415610af557600080546040516001600160a01b039091169183156108fc02918491818181858888f19350505050158015610aef573d6000803e3d6000fd5b50610b15565b600054610b15906001600160a01b0384811691168363ffffffff61148e16565b5050565b6000546001600160a01b03163314610b3057600080fd5b6000546001600160a01b0316ff5b6000546001600160a01b03163314610b5557600080fd5b622dc6c08110610b6457600080fd5b600455565b60035481565b6000546001600160a01b03163314610b8657600080fd5b6001600160a01b03166000908152600a60205260409020805460ff19166001179055565b6007546001600160a01b031681565b6000546001600160a01b031681565b336000908152600a602052604090205460ff16610be457600080fd5b6002546040516370a0823160e01b815281906eb3f879cb30fe243b4dfee438691c04906370a0823190610c1b903090600401611ed3565b60206040518083038186803b158015610c3357600080fd5b505afa158015610c47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6b9190611dff565b10610cf65760405163d8ccd0f360e01b81526eb3f879cb30fe243b4dfee438691c049063d8ccd0f390610ca2908490600401612151565b602060405180830381600087803b158015610cbc57600080fd5b505af1158015610cd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf49190611bd2565b505b600080610d0460018561051e565b9150915081610d1257600080fd5b6000610d1f600454610a17565b6006546008546040519293506001600160a01b0391821692638a0e833f9234928a92911690610d56908d908d908990602401612098565b60408051601f198184030181529181526020820180516001600160e01b031663745ce7c160e01b1790525160e086901b6001600160e01b0319168152610da193929190600401611ee7565b6000604051808303818588803b158015610dba57600080fd5b505af1158015610dce573d6000803e3d6000fd5b5050505050600080610de1600188610ecf565b9150915081610def57600080fd5b610df761142e565b6009546040516001600160a01b039091169063d061ce509030908a90610e23908990879060200161215a565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161098c93929190611f1c565b60025481565b600a6020526000908152604090205460ff1681565b6001546001600160a01b03163314610e8257600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314610ebb57600080fd5b622dc6c08110610eca57600080fd5b600555565b600080610eda61196c565b60075460405163335d71f560e21b81526001600160a01b039091169063cd75c7d490610f0a908790600401611ed3565b60c06040518083038186803b158015610f2257600080fd5b505afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190611c59565b90506000610f6785610fce565b90506001866001811115610f7757fe5b1415610f97576040909101516001600160801b03168110925090506106e5565b6000866001811115610fa557fe5b1415610fc5576020909101516001600160801b03168111925090506106e5565b50509250929050565b604051632aff3bff60e21b8152600090606090733d9819210a31b4961b30ef54be2aed79b9c9cd3b9063abfceffc9061100b908690600401611ed3565b60006040518083038186803b15801561102357600080fd5b505afa158015611037573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261105f9190810190611b27565b90506000733d9819210a31b4961b30ef54be2aed79b9c9cd3b6001600160a01b0316637dc0d1d06040518163ffffffff1660e01b815260040160206040518083038186803b1580156110b057600080fd5b505afa1580156110c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e89190611ae0565b9050600080805b845181101561134a57600085828151811061110657fe5b602002602001015190506000806000836001600160a01b031663c37f68e28c6040518263ffffffff1660e01b81526004016111419190611ed3565b60806040518083038186803b15801561115957600080fd5b505afa15801561116d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111919190611e17565b935093509350506111a06119a1565b831515806111ad57508215155b1561123b5760408051602081019182905263fc57d4df60e01b909152806001600160a01b038b1663fc57d4df6111e68960248501611ed3565b60206040518083038186803b1580156111fe57600080fd5b505afa158015611212573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112369190611dff565b905290505b831561132457604051638e8f294b60e01b8152600090733d9819210a31b4961b30ef54be2aed79b9c9cd3b90638e8f294b9061127b908990600401611ed3565b604080518083038186803b15801561129257600080fd5b505afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190611bf2565b9150506112d56119a1565b5060408051602081019091528181526112ec6119a1565b5060408051602081019091528481526113036119a1565b61130e8383876114e9565b91505061131c81898e611542565b9c5050505050505b821561133957611335818489611542565b9750505b5050600190930192506110ef915050565b508061135e57600019945050505050610a44565b60008282670de0b6b3a7640000028161137357fe5b049050611388670de0b6b3a76400008261158f565b979650505050505050565b6000546001600160a01b031633146113aa57600080fd5b6001546001600160a01b031615610e8257600080fd5b64746a52880081565b6006546001600160a01b031681565b6000546001600160a01b031633146113ef57600080fd5b6001600160a01b03166000908152600a60205260409020805460ff19169055565b6009546001600160a01b031681565b6001546001600160a01b031681565b47156114625760405133904780156108fc02916000818181858888f19350505050158015611460573d6000803e3d6000fd5b505b565b600081158061147f5750508082028282828161147c57fe5b04145b61148857600080fd5b92915050565b6114e48363a9059cbb60e01b84846040516024016114ad929190611fd0565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526115bf565b505050565b60006114f36119a1565b60006114fd6119a1565b6115078787611657565b9092509050600082600381111561151a57fe5b1461152957909250905061153a565b6115338186611657565b9350935050505b935093915050565b600080600061154f6119a1565b6115598787611741565b9092509050600082600381111561156c57fe5b1461157d575091506000905061153a565b611533611589826117a9565b866117b8565b6000816115b06115a785670de0b6b3a7640000611464565b600285046117de565b816115b757fe5b049392505050565b6060611614826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166117ee9092919063ffffffff16565b8051909150156114e457808060200190518101906116329190611bd2565b6114e45760405162461bcd60e51b815260040161164e9061204e565b60405180910390fd5b60006116616119a1565b60008061167686600001518660000151611805565b9092509050600082600381111561168957fe5b146116a8575060408051602081019091526000815290925090506106e5565b6000806116bd6706f05b59d3b20000846117b8565b909250905060008260038111156116d057fe5b146116f357816040518060200160405280600081525095509550505050506106e5565b60008061170883670de0b6b3a7640000611844565b9092509050600082600381111561171b57fe5b1461172257fe5b604080516020810190915290815260009a909950975050505050505050565b600061174b6119a1565b60008061175c866000015186611805565b9092509050600082600381111561176f57fe5b1461178e575060408051602081019091526000815290925090506106e5565b60408051602081019091529081526000969095509350505050565b51670de0b6b3a7640000900490565b6000808383018481106117d0576000925090506106e5565b5060029150600090506106e5565b8082018281101561148857600080fd5b60606117fd848460008561186f565b949350505050565b60008083611818575060009050806106e5565b8383028385828161182557fe5b0414611839575060029150600090506106e5565b6000925090506106e5565b6000808261185857506001905060006106e5565b600083858161186357fe5b04915091509250929050565b606061187a85611933565b6118965760405162461bcd60e51b815260040161164e90612017565b60006060866001600160a01b031685876040516118b39190611eb7565b60006040518083038185875af1925050503d80600081146118f0576040519150601f19603f3d011682016040523d82523d6000602084013e6118f5565b606091505b509150915081156119095791506117fd9050565b8051156119195780518082602001fd5b8360405162461bcd60e51b815260040161164e9190612004565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906117fd575050151592915050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b6040518060200160405280600081525090565b8035611488816121bb565b8051611488816121bb565b600082601f8301126119da578081fd5b6119e46040612168565b90508082846040850111156119f857600080fd5b60005b6002811015611a24578135611a0f816121bb565b835260209283019291909101906001016119fb565b50505092915050565b8051801515811461148857600080fd5b600082601f830112611a4d578081fd5b813567ffffffffffffffff811115611a63578182fd5b611a76601f8201601f1916602001612168565b9150808252836020828501011115611a8d57600080fd5b8060208401602084013760009082016020015292915050565b80516001600160801b038116811461148857600080fd5b600060208284031215611ace578081fd5b8135611ad9816121bb565b9392505050565b600060208284031215611af1578081fd5b8151611ad9816121bb565b60008060408385031215611b0e578081fd5b8235611b19816121bb565b946020939093013593505050565b60006020808385031215611b39578182fd5b825167ffffffffffffffff80821115611b50578384fd5b81850186601f820112611b61578485fd5b8051925081831115611b71578485fd5b8383029150611b81848301612168565b8381528481019082860184840187018a1015611b9b578788fd5b8794505b85851015611bc557611bb18a826119bf565b835260019490940193918601918601611b9f565b5098975050505050505050565b600060208284031215611be3578081fd5b81518015158114611ad9578182fd5b60008060408385031215611c04578182fd5b611c0e8484611a2d565b9150602083015190509250929050565b60008060408385031215611c30578182fd5b823560028110611c3e578283fd5b91506020830135611c4e816121bb565b809150509250929050565b600060c08284031215611c6a578081fd5b611c7460c0612168565b8251611c7f816121bb565b8152611c8e8460208501611aa6565b6020820152611ca08460408501611aa6565b6040820152611cb28460608501611aa6565b6060820152611cc48460808501611aa6565b6080820152611cd68460a08501611a2d565b60a08201529392505050565b600080600060808486031215611cf6578081fd5b833567ffffffffffffffff80821115611d0d578283fd5b610120918601808803831315611d21578384fd5b611d2a83612168565b611d3489836119b4565b8152611d4389602084016119b4565b6020820152604082013560408201526060820135606082015260808201356080820152611d738960a084016119b4565b60a0820152611d858960c084016119b4565b60c082015260e0820135935082841115611d9d578485fd5b611da989858401611a3d565b60e08201526101009350838201358482015280965050505050611dcf85602086016119ca565b9150611dde85606086016119b4565b90509250925092565b600060208284031215611df8578081fd5b5035919050565b600060208284031215611e10578081fd5b5051919050565b60008060008060808587031215611e2c578182fd5b505082516020840151604085015160609095015191969095509092509050565b6001600160a01b03169052565b8060005b6002811015611e855781516001600160a01b0316845260209384019390910190600101611e5d565b50505050565b60008151808452611ea381602086016020860161218f565b601f01601f19169290920160200192915050565b60008251611ec981846020870161218f565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03848116825283166020820152606060408201819052600090611f1390830184611e8b565b95945050505050565b6001600160a01b03848116825283166020820152608060408201819052601690820152754175746f6d61746963436f6d706f756e64526570617960501b60a082015260c060608201819052600090611f1390830184611e8b565b6001600160a01b0384811682528316602082015260806040820181905260169082015275105d5d1bdb585d1a58d0dbdb5c1bdd5b99109bdbdcdd60521b60a082015260c060608201819052600090611f1390830184611e8b565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b9115158252602082015260400190565b600060208252611ad96020830184611e8b565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6000608082526120ac608083018651611e4c565b60208501516120be60a0840182611e4c565b50604085015160c0830152606085015160e08301526080850151610100818185015260a087015191506101206120f681860184611e4c565b60c0880151925061210b610140860184611e4c565b60e0880151925080610160860152506121286101a0850183611e8b565b90870151610180850152915061214390506020830185611e59565b826060830152949350505050565b90815260200190565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561218757600080fd5b604052919050565b60005b838110156121aa578181015183820152602001612192565b83811115611e855750506000910152565b6001600160a01b038116811461146057600080fdfea2646970667358221220ee38c0ddb1aa646452ad377130f15c76115575c8cbbf7a1dc02030d52cf71f4164736f6c634300060a0033000000000000000000000000b1cf8de8e791e4ed1bd86c03e2fc1f14389cb10a00000000000000000000000052015effd577e08f498a0ccc11905925d58d6207000000000000000000000000decdc15131ae4f829df6624c34a7c55e03d095e5

Deployed Bytecode

0x6080604052600436106101d85760003560e01c806379521f0211610102578063c91d59fe11610095578063ec23ef2711610064578063ec23ef27146104bf578063eef21cd2146104d4578063f24ccbfe146104f4578063f851a44014610509576101d8565b8063c91d59fe146102c2578063d98bb5b11461046a578063deca5f881461048a578063e3bbb4f1146104aa576101d8565b8063a71975af116100d1578063a71975af146103dd578063a7304bf71461040a578063a8c903231461042a578063bfc361721461044a576101d8565b806379521f021461038b5780638da5cb5b146103a057806398a3f265146103b5578063a56f9718146103c8576101d8565b806332ac5cd21161017a57806341c0e1b51161014957806341c0e1b5146103215780634d3f199e14610336578063696806c014610356578063747293fb1461036b576101d8565b806332ac5cd2146102c257806336fc603f146102d757806339df1878146102ec5780633a12832214610301576101d8565b806318bf60e1116101b657806318bf60e11461024b5780631e48907b1461026d57806329ad0f361461028d5780632a56f602146102a2576101d8565b806306d5e37e146101dd578063109d0af8146102145780631391989414610236575b600080fd5b3480156101e957600080fd5b506101fd6101f8366004611c1e565b61051e565b60405161020b929190611ff4565b60405180910390f35b34801561022057600080fd5b506102296106ec565b60405161020b9190611ed3565b610249610244366004611ce2565b610704565b005b34801561025757600080fd5b506102606109c9565b60405161020b9190612151565b34801561027957600080fd5b50610249610288366004611abd565b6109cf565b34801561029957600080fd5b50610229610a08565b3480156102ae57600080fd5b506102606102bd366004611de7565b610a17565b3480156102ce57600080fd5b50610229610a49565b3480156102e357600080fd5b50610260610a5c565b3480156102f857600080fd5b50610229610a62565b34801561030d57600080fd5b5061024961031c366004611afc565b610a7a565b34801561032d57600080fd5b50610249610b19565b34801561034257600080fd5b50610249610351366004611de7565b610b3e565b34801561036257600080fd5b50610260610b69565b34801561037757600080fd5b50610249610386366004611abd565b610b6f565b34801561039757600080fd5b50610229610baa565b3480156103ac57600080fd5b50610229610bb9565b6102496103c3366004611ce2565b610bc8565b3480156103d457600080fd5b50610260610e50565b3480156103e957600080fd5b506103fd6103f8366004611abd565b610e56565b60405161020b9190611fe9565b34801561041657600080fd5b50610249610425366004611abd565b610e6b565b34801561043657600080fd5b50610249610445366004611de7565b610ea4565b34801561045657600080fd5b506101fd610465366004611c1e565b610ecf565b34801561047657600080fd5b50610260610485366004611abd565b610fce565b34801561049657600080fd5b506102496104a5366004611abd565b611393565b3480156104b657600080fd5b506102606113c0565b3480156104cb57600080fd5b506102296113c9565b3480156104e057600080fd5b506102496104ef366004611abd565b6113d8565b34801561050057600080fd5b50610229611410565b34801561051557600080fd5b5061022961141f565b600754604051632e4aba1f60e21b8152600091829182916001600160a01b03169063b92ae87c90610553908790600401611ed3565b60206040518083038186803b15801561056b57600080fd5b505afa15801561057f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a39190611bd2565b90506105ad61196c565b60075460405163335d71f560e21b81526001600160a01b039091169063cd75c7d4906105dd908890600401611ed3565b60c06040518083038186803b1580156105f557600080fd5b505afa158015610609573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061062d9190611c59565b9050816106435750600092508291506106e59050565b600086600181111561065157fe5b14801561066057508060a00151155b156106745750600092508291506106e59050565b600061067f86610fce565b9050600187600181111561068f57fe5b14156106b1576020909101516001600160801b03168110935091506106e59050565b60008760018111156106bf57fe5b14156106e1576040909101516001600160801b03168111935091506106e59050565b5050505b9250929050565b733d9819210a31b4961b30ef54be2aed79b9c9cd3b81565b336000908152600a602052604090205460ff1661072057600080fd5b6003546040516370a0823160e01b815281906eb3f879cb30fe243b4dfee438691c04906370a0823190610757903090600401611ed3565b60206040518083038186803b15801561076f57600080fd5b505afa158015610783573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107a79190611dff565b106108325760405163d8ccd0f360e01b81526eb3f879cb30fe243b4dfee438691c049063d8ccd0f3906107de908490600401612151565b602060405180830381600087803b1580156107f857600080fd5b505af115801561080c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108309190611bd2565b505b60008061084060008561051e565b915091508161084e57600080fd5b600061085b600554610a17565b6006546008546040519293506001600160a01b0391821692638a0e833f9234928a92911690610892908d908d908990602401612098565b60408051601f198184030181529181526020820180516001600160e01b031663f708847b60e01b1790525160e086901b6001600160e01b03191681526108dd93929190600401611ee7565b6000604051808303818588803b1580156108f657600080fd5b505af115801561090a573d6000803e3d6000fd5b505050505060008061091d600088610ecf565b915091508161092b57600080fd5b61093361142e565b6009546040516001600160a01b039091169063d061ce509030908a9061095f908990879060200161215a565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161098c93929190611f76565b600060405180830381600087803b1580156109a657600080fd5b505af11580156109ba573d6000803e3d6000fd5b50505050505050505050505050565b60055481565b6001546001600160a01b031633146109e657600080fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6008546001600160a01b031681565b60008064746a5288003a1115610a325764746a528800610a34565b3a5b9050610a408184611464565b9150505b919050565b6eb3f879cb30fe243b4dfee438691c0481565b60045481565b735c55b921f590a89c1ebe84df170e655a82b6212681565b6000546001600160a01b03163314610a9157600080fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0383161415610af557600080546040516001600160a01b039091169183156108fc02918491818181858888f19350505050158015610aef573d6000803e3d6000fd5b50610b15565b600054610b15906001600160a01b0384811691168363ffffffff61148e16565b5050565b6000546001600160a01b03163314610b3057600080fd5b6000546001600160a01b0316ff5b6000546001600160a01b03163314610b5557600080fd5b622dc6c08110610b6457600080fd5b600455565b60035481565b6000546001600160a01b03163314610b8657600080fd5b6001600160a01b03166000908152600a60205260409020805460ff19166001179055565b6007546001600160a01b031681565b6000546001600160a01b031681565b336000908152600a602052604090205460ff16610be457600080fd5b6002546040516370a0823160e01b815281906eb3f879cb30fe243b4dfee438691c04906370a0823190610c1b903090600401611ed3565b60206040518083038186803b158015610c3357600080fd5b505afa158015610c47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6b9190611dff565b10610cf65760405163d8ccd0f360e01b81526eb3f879cb30fe243b4dfee438691c049063d8ccd0f390610ca2908490600401612151565b602060405180830381600087803b158015610cbc57600080fd5b505af1158015610cd0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf49190611bd2565b505b600080610d0460018561051e565b9150915081610d1257600080fd5b6000610d1f600454610a17565b6006546008546040519293506001600160a01b0391821692638a0e833f9234928a92911690610d56908d908d908990602401612098565b60408051601f198184030181529181526020820180516001600160e01b031663745ce7c160e01b1790525160e086901b6001600160e01b0319168152610da193929190600401611ee7565b6000604051808303818588803b158015610dba57600080fd5b505af1158015610dce573d6000803e3d6000fd5b5050505050600080610de1600188610ecf565b9150915081610def57600080fd5b610df761142e565b6009546040516001600160a01b039091169063d061ce509030908a90610e23908990879060200161215a565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161098c93929190611f1c565b60025481565b600a6020526000908152604090205460ff1681565b6001546001600160a01b03163314610e8257600080fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b03163314610ebb57600080fd5b622dc6c08110610eca57600080fd5b600555565b600080610eda61196c565b60075460405163335d71f560e21b81526001600160a01b039091169063cd75c7d490610f0a908790600401611ed3565b60c06040518083038186803b158015610f2257600080fd5b505afa158015610f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5a9190611c59565b90506000610f6785610fce565b90506001866001811115610f7757fe5b1415610f97576040909101516001600160801b03168110925090506106e5565b6000866001811115610fa557fe5b1415610fc5576020909101516001600160801b03168111925090506106e5565b50509250929050565b604051632aff3bff60e21b8152600090606090733d9819210a31b4961b30ef54be2aed79b9c9cd3b9063abfceffc9061100b908690600401611ed3565b60006040518083038186803b15801561102357600080fd5b505afa158015611037573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261105f9190810190611b27565b90506000733d9819210a31b4961b30ef54be2aed79b9c9cd3b6001600160a01b0316637dc0d1d06040518163ffffffff1660e01b815260040160206040518083038186803b1580156110b057600080fd5b505afa1580156110c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110e89190611ae0565b9050600080805b845181101561134a57600085828151811061110657fe5b602002602001015190506000806000836001600160a01b031663c37f68e28c6040518263ffffffff1660e01b81526004016111419190611ed3565b60806040518083038186803b15801561115957600080fd5b505afa15801561116d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111919190611e17565b935093509350506111a06119a1565b831515806111ad57508215155b1561123b5760408051602081019182905263fc57d4df60e01b909152806001600160a01b038b1663fc57d4df6111e68960248501611ed3565b60206040518083038186803b1580156111fe57600080fd5b505afa158015611212573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112369190611dff565b905290505b831561132457604051638e8f294b60e01b8152600090733d9819210a31b4961b30ef54be2aed79b9c9cd3b90638e8f294b9061127b908990600401611ed3565b604080518083038186803b15801561129257600080fd5b505afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca9190611bf2565b9150506112d56119a1565b5060408051602081019091528181526112ec6119a1565b5060408051602081019091528481526113036119a1565b61130e8383876114e9565b91505061131c81898e611542565b9c5050505050505b821561133957611335818489611542565b9750505b5050600190930192506110ef915050565b508061135e57600019945050505050610a44565b60008282670de0b6b3a7640000028161137357fe5b049050611388670de0b6b3a76400008261158f565b979650505050505050565b6000546001600160a01b031633146113aa57600080fd5b6001546001600160a01b031615610e8257600080fd5b64746a52880081565b6006546001600160a01b031681565b6000546001600160a01b031633146113ef57600080fd5b6001600160a01b03166000908152600a60205260409020805460ff19169055565b6009546001600160a01b031681565b6001546001600160a01b031681565b47156114625760405133904780156108fc02916000818181858888f19350505050158015611460573d6000803e3d6000fd5b505b565b600081158061147f5750508082028282828161147c57fe5b04145b61148857600080fd5b92915050565b6114e48363a9059cbb60e01b84846040516024016114ad929190611fd0565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526115bf565b505050565b60006114f36119a1565b60006114fd6119a1565b6115078787611657565b9092509050600082600381111561151a57fe5b1461152957909250905061153a565b6115338186611657565b9350935050505b935093915050565b600080600061154f6119a1565b6115598787611741565b9092509050600082600381111561156c57fe5b1461157d575091506000905061153a565b611533611589826117a9565b866117b8565b6000816115b06115a785670de0b6b3a7640000611464565b600285046117de565b816115b757fe5b049392505050565b6060611614826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166117ee9092919063ffffffff16565b8051909150156114e457808060200190518101906116329190611bd2565b6114e45760405162461bcd60e51b815260040161164e9061204e565b60405180910390fd5b60006116616119a1565b60008061167686600001518660000151611805565b9092509050600082600381111561168957fe5b146116a8575060408051602081019091526000815290925090506106e5565b6000806116bd6706f05b59d3b20000846117b8565b909250905060008260038111156116d057fe5b146116f357816040518060200160405280600081525095509550505050506106e5565b60008061170883670de0b6b3a7640000611844565b9092509050600082600381111561171b57fe5b1461172257fe5b604080516020810190915290815260009a909950975050505050505050565b600061174b6119a1565b60008061175c866000015186611805565b9092509050600082600381111561176f57fe5b1461178e575060408051602081019091526000815290925090506106e5565b60408051602081019091529081526000969095509350505050565b51670de0b6b3a7640000900490565b6000808383018481106117d0576000925090506106e5565b5060029150600090506106e5565b8082018281101561148857600080fd5b60606117fd848460008561186f565b949350505050565b60008083611818575060009050806106e5565b8383028385828161182557fe5b0414611839575060029150600090506106e5565b6000925090506106e5565b6000808261185857506001905060006106e5565b600083858161186357fe5b04915091509250929050565b606061187a85611933565b6118965760405162461bcd60e51b815260040161164e90612017565b60006060866001600160a01b031685876040516118b39190611eb7565b60006040518083038185875af1925050503d80600081146118f0576040519150601f19603f3d011682016040523d82523d6000602084013e6118f5565b606091505b509150915081156119095791506117fd9050565b8051156119195780518082602001fd5b8360405162461bcd60e51b815260040161164e9190612004565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906117fd575050151592915050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b6040518060200160405280600081525090565b8035611488816121bb565b8051611488816121bb565b600082601f8301126119da578081fd5b6119e46040612168565b90508082846040850111156119f857600080fd5b60005b6002811015611a24578135611a0f816121bb565b835260209283019291909101906001016119fb565b50505092915050565b8051801515811461148857600080fd5b600082601f830112611a4d578081fd5b813567ffffffffffffffff811115611a63578182fd5b611a76601f8201601f1916602001612168565b9150808252836020828501011115611a8d57600080fd5b8060208401602084013760009082016020015292915050565b80516001600160801b038116811461148857600080fd5b600060208284031215611ace578081fd5b8135611ad9816121bb565b9392505050565b600060208284031215611af1578081fd5b8151611ad9816121bb565b60008060408385031215611b0e578081fd5b8235611b19816121bb565b946020939093013593505050565b60006020808385031215611b39578182fd5b825167ffffffffffffffff80821115611b50578384fd5b81850186601f820112611b61578485fd5b8051925081831115611b71578485fd5b8383029150611b81848301612168565b8381528481019082860184840187018a1015611b9b578788fd5b8794505b85851015611bc557611bb18a826119bf565b835260019490940193918601918601611b9f565b5098975050505050505050565b600060208284031215611be3578081fd5b81518015158114611ad9578182fd5b60008060408385031215611c04578182fd5b611c0e8484611a2d565b9150602083015190509250929050565b60008060408385031215611c30578182fd5b823560028110611c3e578283fd5b91506020830135611c4e816121bb565b809150509250929050565b600060c08284031215611c6a578081fd5b611c7460c0612168565b8251611c7f816121bb565b8152611c8e8460208501611aa6565b6020820152611ca08460408501611aa6565b6040820152611cb28460608501611aa6565b6060820152611cc48460808501611aa6565b6080820152611cd68460a08501611a2d565b60a08201529392505050565b600080600060808486031215611cf6578081fd5b833567ffffffffffffffff80821115611d0d578283fd5b610120918601808803831315611d21578384fd5b611d2a83612168565b611d3489836119b4565b8152611d4389602084016119b4565b6020820152604082013560408201526060820135606082015260808201356080820152611d738960a084016119b4565b60a0820152611d858960c084016119b4565b60c082015260e0820135935082841115611d9d578485fd5b611da989858401611a3d565b60e08201526101009350838201358482015280965050505050611dcf85602086016119ca565b9150611dde85606086016119b4565b90509250925092565b600060208284031215611df8578081fd5b5035919050565b600060208284031215611e10578081fd5b5051919050565b60008060008060808587031215611e2c578182fd5b505082516020840151604085015160609095015191969095509092509050565b6001600160a01b03169052565b8060005b6002811015611e855781516001600160a01b0316845260209384019390910190600101611e5d565b50505050565b60008151808452611ea381602086016020860161218f565b601f01601f19169290920160200192915050565b60008251611ec981846020870161218f565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03848116825283166020820152606060408201819052600090611f1390830184611e8b565b95945050505050565b6001600160a01b03848116825283166020820152608060408201819052601690820152754175746f6d61746963436f6d706f756e64526570617960501b60a082015260c060608201819052600090611f1390830184611e8b565b6001600160a01b0384811682528316602082015260806040820181905260169082015275105d5d1bdb585d1a58d0dbdb5c1bdd5b99109bdbdcdd60521b60a082015260c060608201819052600090611f1390830184611e8b565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b9115158252602082015260400190565b600060208252611ad96020830184611e8b565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6000608082526120ac608083018651611e4c565b60208501516120be60a0840182611e4c565b50604085015160c0830152606085015160e08301526080850151610100818185015260a087015191506101206120f681860184611e4c565b60c0880151925061210b610140860184611e4c565b60e0880151925080610160860152506121286101a0850183611e8b565b90870151610180850152915061214390506020830185611e59565b826060830152949350505050565b90815260200190565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561218757600080fd5b604052919050565b60005b838110156121aa578181015183820152602001612192565b83811115611e855750506000910152565b6001600160a01b038116811461146057600080fdfea2646970667358221220ee38c0ddb1aa646452ad377130f15c76115575c8cbbf7a1dc02030d52cf71f4164736f6c634300060a0033

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

000000000000000000000000b1cf8de8e791e4ed1bd86c03e2fc1f14389cb10a00000000000000000000000052015effd577e08f498a0ccc11905925d58d6207000000000000000000000000decdc15131ae4f829df6624c34a7c55e03d095e5

-----Decoded View---------------
Arg [0] : _compoundMonitorProxy (address): 0xB1cF8DE8e791E4Ed1Bd86c03E2fc1f14389Cb10a
Arg [1] : _subscriptions (address): 0x52015EFFD577E08f498a0CCc11905925D58D6207
Arg [2] : _compoundFlashLoanTaker (address): 0xdEcDC15131Ae4F829df6624c34a7c55e03D095e5

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000b1cf8de8e791e4ed1bd86c03e2fc1f14389cb10a
Arg [1] : 00000000000000000000000052015effd577e08f498a0ccc11905925d58d6207
Arg [2] : 000000000000000000000000decdc15131ae4f829df6624c34a7c55e03d095e5


Deployed Bytecode Sourcemap

52945:8589:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;58451:749;;;;;;;;;;-1:-1:-1;58451:749:0;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;37116:108;;;;;;;;;;;;;:::i;:::-;;;;;;;;56650:1137;;;;;;;;;:::i;:::-;;53295:36;;;;;;;;;;;;;:::i;:::-;;;;;;;;9730:122;;;;;;;;;;-1:-1:-1;9730:122:0;;;;;;;;:::i;53649:44::-;;;;;;;;;;;;;:::i;60195:203::-;;;;;;;;;;-1:-1:-1;60195:203:0;;;;;;;;:::i;53340:96::-;;;;;;;;;;;;;:::i;53252:36::-;;;;;;;;;;;;;:::i;53443:85::-;;;;;;;;;;;;;:::i;10025:285::-;;;;;;;;;;-1:-1:-1;10025:285:0;;;;;;;;:::i;9898:80::-;;;;;;;;;;;;;:::i;60940:144::-;;;;;;;;;;-1:-1:-1;60940:144:0;;;;;;;;:::i;53141:32::-;;;;;;;;;;;;;:::i;61207:103::-;;;;;;;;;;-1:-1:-1;61207:103:0;;;;;;;;:::i;53592:50::-;;;;;;;;;;;;;:::i;8837:20::-;;;;;;;;;;;;;:::i;55117:1134::-;;;;;;;;;:::i;53102:32::-;;;;;;;;;;;;;:::i;53850:47::-;;;;;;;;;;-1:-1:-1;53850:47:0;;;;;;;;:::i;:::-;;;;;;;;9509:122;;;;;;;;;;-1:-1:-1;9509:122:0;;;;;;;;:::i;60637:144::-;;;;;;;;;;-1:-1:-1;60637:144:0;;;;;;;;:::i;59497:490::-;;;;;;;;;;-1:-1:-1;59497:490:0;;;;;;;;:::i;37343:1709::-;;;;;;;;;;-1:-1:-1;37343:1709:0;;;;;;;;:::i;9226:161::-;;;;;;;;;;-1:-1:-1;9226:161:0;;;;;;;;:::i;53182:49::-;;;;;;;;;;;;;:::i;53537:48::-;;;;;;;;;;;;;:::i;61422:107::-;;;;;;;;;;-1:-1:-1;61422:107:0;;;;;;;;:::i;53702:65::-;;;;;;;;;;;;;:::i;8864:20::-;;;;;;;;;;;;;:::i;58451:749::-;58560:21;;:41;;-1:-1:-1;;;58560:41:0;;58519:4;;;;;;-1:-1:-1;;;;;58560:21:0;;:34;;:41;;58595:5;;58560:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;58542:59;;58612:50;;:::i;:::-;58665:21;;:38;;-1:-1:-1;;;58665:38:0;;-1:-1:-1;;;;;58665:21:0;;;;:31;;:38;;58697:5;;58665:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;58612:91;;58760:10;58755:34;;-1:-1:-1;58780:5:0;;-1:-1:-1;58780:5:0;;-1:-1:-1;58772:17:0;;-1:-1:-1;58772:17:0;58755:34;58862:12;58851:7;:23;;;;;;;;;:47;;;;;58879:6;:19;;;58878:20;58851:47;58847:70;;;-1:-1:-1;58908:5:0;;-1:-1:-1;58908:5:0;;-1:-1:-1;58900:17:0;;-1:-1:-1;58900:17:0;58847:70;58930:14;58947:21;58962:5;58947:14;:21::i;:::-;58930:38;-1:-1:-1;58996:12:0;58985:7;:23;;;;;;;;;58981:212;;;59045:15;;;;;-1:-1:-1;;;;;59033:27:0;;;;-1:-1:-1;59033:9:0;-1:-1:-1;59025:47:0;;-1:-1:-1;59025:47:0;58981:212;59105:12;59094:7;:23;;;;;;;;;59090:103;;;59154:15;;;;;-1:-1:-1;;;;;59142:27:0;;;;-1:-1:-1;59142:9:0;-1:-1:-1;59134:47:0;;-1:-1:-1;59134:47:0;59090:103;58451:749;;;;;;;;;:::o;37116:108::-;37181:42;37116:108;:::o;56650:1137::-;53965:10;53949:27;;;;:15;:27;;;;;;;;53941:36;;;;;;56863:15:::1;::::0;1502:33:::1;::::0;-1:-1:-1;;;1502:33:0;;56863:15;;1404:42:::1;::::0;1502:18:::1;::::0;:33:::1;::::0;1529:4:::1;::::0;1502:33:::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:44;1498:99;;1563:22;::::0;-1:-1:-1;;;1563:22:0;;1404:42:::1;::::0;1563:13:::1;::::0;:22:::1;::::0;1577:7;;1563:22:::1;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1498:99;56894:14:::2;56910:16:::0;56930:28:::2;56938:12;56952:5;56930:7;:28::i;:::-;56893:65;;;;56977:9;56969:18;;;::::0;::::2;;57031:15;57049:27;57061:14;;57049:11;:27::i;:::-;57089:20;::::0;57174:29:::2;::::0;57218:246:::2;::::0;57031:45;;-1:-1:-1;;;;;;57089:20:0;;::::2;::::0;:32:::2;::::0;57129:9:::2;::::0;57154:5;;57174:29;::::2;::::0;57218:246:::2;::::0;57386:7;;57412:11;;57031:45;;57218:246:::2;;;;;;::::0;;-1:-1:-1;;57218:246:0;;::::2;::::0;;;;;;::::2;::::0;::::2;::::0;;-1:-1:-1;;;;;57218:246:0::2;-1:-1:-1::0;;;57218:246:0::2;::::0;;57089:386;::::2;::::0;;;-1:-1:-1;;;;;;57089:386:0;;;::::2;::::0;;;57218:246;57089:386:::2;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;57491:16;57509:15:::0;57528:35:::2;57543:12;57557:5;57528:14;:35::i;:::-;57490:73;;;;57582:11;57574:20;;;::::0;::::2;;57660:11;:9;:11::i;:::-;57684:6;::::0;57743:35:::2;::::0;-1:-1:-1;;;;;57684:6:0;;::::2;::::0;:10:::2;::::0;57703:4:::2;::::0;57710:5;;57743:35:::2;::::0;57754:11;;57767:10;;57743:35:::2;;;;;;;;;;;;;;;;;57684:95;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;1609:1;;;;;53988::::1;56650:1137:::0;;;:::o;53295:36::-;;;;:::o;9730:122::-;9811:5;;-1:-1:-1;;;;;9811:5:0;9797:10;:19;9789:28;;;;;;9830:5;:14;;-1:-1:-1;;;;;;9830:14:0;-1:-1:-1;;;;;9830:14:0;;;;;;;;;;9730:122::o;53649:44::-;;;-1:-1:-1;;;;;53649:44:0;;:::o;60195:203::-;60254:4;60271:13;53219:12;60287:11;:28;;:58;;53219:12;60287:58;;;60318:11;60287:58;60271:74;;60365:25;60369:8;60379:10;60365:3;:25::i;:::-;60358:32;;;60195:203;;;;:::o;53340:96::-;53394:42;53340:96;:::o;53252:36::-;;;;:::o;53443:85::-;53486:42;53443:85;:::o;10025:285::-;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;10125:42:::1;-1:-1:-1::0;;;;;10115:52:0;::::1;;10111:192;;;10192:5;::::0;;10184:32:::1;::::0;-1:-1:-1;;;;;10192:5:0;;::::1;::::0;10184:32;::::1;;;::::0;10208:7;;10184:32;10192:5;10184:32;10208:7;10192:5;10184:32;::::1;;;;;;;;;;;;;::::0;::::1;;;;;;10111:192;;;10276:5;::::0;10249:42:::1;::::0;-1:-1:-1;;;;;10249:26:0;;::::1;::::0;10276:5:::1;10283:7:::0;10249:42:::1;:26;:42;:::i;:::-;10025:285:::0;;:::o;9898:80::-;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;9963:5:::1;::::0;-1:-1:-1;;;;;9963:5:0::1;9942:28;60940:144:::0;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;61030:7:::1;61019:8;:18;61011:27;;;::::0;::::1;;61051:14;:25:::0;60940:144::o;53141:32::-;;;;:::o;61207:103::-;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;-1:-1:-1;;;;;61271:24:0::1;;::::0;;;:15:::1;:24;::::0;;;;:31;;-1:-1:-1;;61271:31:0::1;61298:4;61271:31;::::0;;61207:103::o;53592:50::-;;;-1:-1:-1;;;;;53592:50:0;;:::o;8837:20::-;;;-1:-1:-1;;;;;8837:20:0;;:::o;55117:1134::-;53965:10;53949:27;;;;:15;:27;;;;;;;;53941:36;;;;;;55330:15:::1;::::0;1502:33:::1;::::0;-1:-1:-1;;;1502:33:0;;55330:15;;1404:42:::1;::::0;1502:18:::1;::::0;:33:::1;::::0;1529:4:::1;::::0;1502:33:::1;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:44;1498:99;;1563:22;::::0;-1:-1:-1;;;1563:22:0;;1404:42:::1;::::0;1563:13:::1;::::0;:22:::1;::::0;1577:7;;1563:22:::1;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1498:99;55361:14:::2;55377:16:::0;55397:28:::2;55405:12;55419:5;55397:7;:28::i;:::-;55360:65;;;;55444:9;55436:18;;;::::0;::::2;;55498:15;55516:27;55528:14;;55516:11;:27::i;:::-;55556:20;::::0;55641:29:::2;::::0;55685:246:::2;::::0;55498:45;;-1:-1:-1;;;;;;55556:20:0;;::::2;::::0;:32:::2;::::0;55596:9:::2;::::0;55621:5;;55641:29;::::2;::::0;55685:246:::2;::::0;55853:7;;55879:11;;55498:45;;55685:246:::2;;;;;;::::0;;-1:-1:-1;;55685:246:0;;::::2;::::0;;;;;;::::2;::::0;::::2;::::0;;-1:-1:-1;;;;;55685:246:0::2;-1:-1:-1::0;;;55685:246:0::2;::::0;;55556:386;::::2;::::0;;;-1:-1:-1;;;;;;55556:386:0;;;::::2;::::0;;;55685:246;55556:386:::2;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;55956:16;55974:15:::0;55993:35:::2;56008:12;56022:5;55993:14;:35::i;:::-;55955:73;;;;56047:11;56039:20;;;::::0;::::2;;56124:11;:9;:11::i;:::-;56148:6;::::0;56207:35:::2;::::0;-1:-1:-1;;;;;56148:6:0;;::::2;::::0;:10:::2;::::0;56167:4:::2;::::0;56174:5;;56207:35:::2;::::0;56218:11;;56231:10;;56207:35:::2;;;;;;;;;;;;;;;;;56148:95;;;;;;;;;;;;;;;;;;53102:32:::0;;;;:::o;53850:47::-;;;;;;;;;;;;;;;:::o;9509:122::-;9590:5;;-1:-1:-1;;;;;9590:5:0;9576:10;:19;9568:28;;;;;;9609:5;:14;;-1:-1:-1;;;;;;9609:14:0;-1:-1:-1;;;;;9609:14:0;;;;;;;;;;9509:122::o;60637:144::-;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;60727:7:::1;60716:8;:18;60708:27;;;::::0;::::1;;60748:14;:25:::0;60637:144::o;59497:490::-;59572:4;59578;59595:50;;:::i;:::-;59666:21;;:38;;-1:-1:-1;;;59666:38:0;;-1:-1:-1;;;;;59666:21:0;;;;:31;;:38;;59698:5;;59666:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;59658:46;;59717:14;59734:21;59749:5;59734:14;:21::i;:::-;59717:38;-1:-1:-1;59783:12:0;59772:7;:23;;;;;;;;;59768:212;;;59832:15;;;;;-1:-1:-1;;;;;59820:27:0;;;;-1:-1:-1;59820:9:0;-1:-1:-1;59812:47:0;;59768:212;59892:12;59881:7;:23;;;;;;;;;59877:103;;;59941:15;;;;;-1:-1:-1;;;;;59929:27:0;;;;-1:-1:-1;59929:9:0;-1:-1:-1;59921:47:0;;59877:103;59497:490;;;;;;;:::o;37343:1709::-;37491:23;;-1:-1:-1;;;37491:23:0;;37403:4;;37465:23;;37181:42;;37491:16;;:23;;37508:5;;37491:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;37491:23:0;;;;;;;;;;;;;;37465:49;;37525:18;37181:42;-1:-1:-1;;;;;37546:11:0;;:13;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37525:34;-1:-1:-1;37574:18:0;;;37638:1242;37659:6;:13;37655:1;:17;37638:1242;;;37694:13;37710:6;37717:1;37710:9;;;;;;;;;;;;;;37694:25;;37739:18;37759;37779:25;37865:5;-1:-1:-1;;;;;37849:41:0;;37891:5;37849:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;37736:161;;;;;;;37914:22;;:::i;:::-;37957:18;;;;:40;;-1:-1:-1;37979:18:0;;;37957:40;37953:173;;;38032:78;;;;;;;;;;-1:-1:-1;;;38047:61:0;;;38032:78;-1:-1:-1;;;;;38047:54:0;;;:61;38102:5;38047:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38032:78;;38018:92;-1:-1:-1;37953:173:0;38187:18;;38183:497;;38258:28;;-1:-1:-1;;;38258:28:0;;38231:23;;37181:42;;38258:12;;:28;;38279:5;;38258:28;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;38228:58;;;38307:27;;:::i;:::-;-1:-1:-1;38337:35:0;;;;;;;;;;;;38391:23;;:::i;:::-;-1:-1:-1;38417:37:0;;;;;;;;;;;;38478:22;;:::i;:::-;38504:52;38512:16;38530:12;38544:11;38504:7;:52::i;:::-;38475:81;;;38597:67;38622:11;38635:13;38650;38597:24;:67::i;:::-;38577:87;-1:-1:-1;;;;;;38183:497:0;38735:18;;38731:138;;38790:63;38815:11;38828:13;38843:9;38790:24;:63::i;:::-;38774:79;-1:-1:-1;;38731:138:0;-1:-1:-1;;37674:3:0;;;;;-1:-1:-1;37638:1242:0;;-1:-1:-1;;37638:1242:0;;-1:-1:-1;38896:14:0;38892:35;;-1:-1:-1;;38912:15:0;;;;;;;;38892:35;38940:20;38986:13;38964:9;38976:6;38964:18;38963:36;;;;;;38940:59;;39017:27;39022:4;39028:15;39017:4;:27::i;:::-;39010:34;37343:1709;-1:-1:-1;;;;;;;37343:1709:0:o;9226:161::-;9307:5;;-1:-1:-1;;;;;9307:5:0;9293:10;:19;9285:28;;;;;;9332:5;;-1:-1:-1;;;;;9332:5:0;:19;9324:28;;;;;53182:49;53219:12;53182:49;:::o;53537:48::-;;;-1:-1:-1;;;;;53537:48:0;;:::o;61422:107::-;8933:5;;-1:-1:-1;;;;;8933:5:0;8942:10;8933:19;8925:28;;;;;;-1:-1:-1;;;;;61489:24:0::1;61516:5;61489:24:::0;;;:15:::1;:24;::::0;;;;:32;;-1:-1:-1;;61489:32:0::1;::::0;;61422:107::o;53702:65::-;;;-1:-1:-1;;;;;53702:65:0;;:::o;8864:20::-;;;-1:-1:-1;;;;;8864:20:0;;:::o;57868:184::-;57949:21;:25;57945:100;;57991:42;;:10;;58011:21;57991:42;;;;;;;;;58011:21;57991:10;:42;;;;;;;;;;;;;;;;;;;;;57945:100;57868:184::o;21694:127::-;21752:9;21782:6;;;:30;;-1:-1:-1;;21797:5:0;;;21811:1;21806;21797:5;21806:1;21792:15;;;;;:20;21782:30;21774:39;;;;;;21694:127;;;;:::o;6980:176::-;7062:86;7082:5;7112:23;;;7137:2;7141:5;7089:58;;;;;;;;;;;;;;-1:-1:-1;;7089:58:0;;;;;;;;;;;;;;-1:-1:-1;;;;;7089:58:0;-1:-1:-1;;;;;;7089:58:0;;;;;;;;;;7062:19;:86::i;:::-;6980:176;;;:::o;35111:284::-;35193:9;35204:10;;:::i;:::-;35228:13;35243;;:::i;:::-;35260:12;35267:1;35270;35260:6;:12::i;:::-;35227:45;;-1:-1:-1;35227:45:0;-1:-1:-1;35294:18:0;35287:3;:25;;;;;;;;;35283:74;;35337:3;;-1:-1:-1;35342:2:0;-1:-1:-1;35329:16:0;;35283:74;35374:13;35381:2;35385:1;35374:6;:13::i;:::-;35367:20;;;;;;35111:284;;;;;;;:::o;31583:328::-;31680:9;31691:4;31709:13;31724:18;;:::i;:::-;31746:20;31756:1;31759:6;31746:9;:20::i;:::-;31708:58;;-1:-1:-1;31708:58:0;-1:-1:-1;31788:18:0;31781:3;:25;;;;;;;;;31777:73;;-1:-1:-1;31831:3:0;-1:-1:-1;31836:1:0;;-1:-1:-1;31823:15:0;;31777:73;31869:34;31877:17;31886:7;31877:8;:17::i;:::-;31896:6;31869:7;:34::i;22735:120::-;22794:9;22846:1;22820:23;22824:11;22828:1;22424:6;22824:3;:11::i;:::-;22841:1;22837;:5;22820:3;:23::i;:::-;:27;;;;;;;22735:120;-1:-1:-1;;;22735:120:0:o;8352:419::-;8434:23;8460:69;8488:4;8460:69;;;;;;;;;;;;;;;;;8468:5;-1:-1:-1;;;;;8460:27:0;;;:69;;;;;:::i;:::-;8544:17;;8434:95;;-1:-1:-1;8544:21:0;8540:224;;8686:10;8675:30;;;;;;;;;;;;;;8667:85;;;;-1:-1:-1;;;8667:85:0;;;;;;;;;;;;;;;;33607:1136;33674:9;33685:10;;:::i;:::-;33711:14;33727:24;33755:31;33763:1;:10;;;33775:1;:10;;;33755:7;:31::i;:::-;33710:76;;-1:-1:-1;33710:76:0;-1:-1:-1;33809:18:0;33801:4;:26;;;;;;;;;33797:92;;-1:-1:-1;33858:18:0;;;;;;;;;-1:-1:-1;33858:18:0;;33852:4;;-1:-1:-1;33858:18:0;-1:-1:-1;33844:33:0;;33797:92;34206:14;;34263:42;29104:10;34285:19;34263:7;:42::i;:::-;34205:100;;-1:-1:-1;34205:100:0;-1:-1:-1;34328:18:0;34320:4;:26;;;;;;;;;34316:92;;34371:4;34377:18;;;;;;;;34392:1;34377:18;;;34363:33;;;;;;;;;;34316:92;34421:14;34437:12;34453:51;34461:32;29064:4;34453:7;:51::i;:::-;34420:84;;-1:-1:-1;34420:84:0;-1:-1:-1;34650:18:0;34642:4;:26;;;;;;;;;34635:34;;;;34710:24;;;;;;;;;;;;-1:-1:-1;;34710:24:0;;-1:-1:-1;33607:1136:0;-1:-1:-1;;;;;;;;33607:1136:0:o;30659:353::-;30728:9;30739:10;;:::i;:::-;30763:14;30779:19;30802:27;30810:1;:10;;;30822:6;30802:7;:27::i;:::-;30762:67;;-1:-1:-1;30762:67:0;-1:-1:-1;30852:18:0;30844:4;:26;;;;;;;;;30840:92;;-1:-1:-1;30901:18:0;;;;;;;;;-1:-1:-1;30901:18:0;;30895:4;;-1:-1:-1;30901:18:0;-1:-1:-1;30887:33:0;;30840:92;30972:31;;;;;;;;;;;;-1:-1:-1;;30972:31:0;;-1:-1:-1;30659:353:0;-1:-1:-1;;;;30659:353:0:o;35938:213::-;36120:12;29064:4;36120:23;;;35938:213::o;28394:258::-;28450:9;;28487:5;;;28509:6;;;28505:140;;28540:18;;-1:-1:-1;28560:1:0;-1:-1:-1;28532:30:0;;28505:140;-1:-1:-1;28603:26:0;;-1:-1:-1;28631:1:0;;-1:-1:-1;28595:38:0;;21452:113;21545:5;;;21540:16;;;;21532:25;;;;;3351:196;3454:12;3486:53;3509:6;3517:4;3523:1;3526:12;3486:22;:53::i;:::-;3479:60;3351:196;-1:-1:-1;;;;3351:196:0:o;27285:343::-;27341:9;;27373:6;27369:69;;-1:-1:-1;27404:18:0;;-1:-1:-1;27404:18:0;27396:30;;27369:69;27459:5;;;27463:1;27459;:5;:1;27481:5;;;;;:10;27477:144;;-1:-1:-1;27516:26:0;;-1:-1:-1;27544:1:0;;-1:-1:-1;27508:38:0;;27477:144;27587:18;;-1:-1:-1;27607:1:0;-1:-1:-1;27579:30:0;;27723:215;27779:9;;27811:6;27807:77;;-1:-1:-1;27842:26:0;;-1:-1:-1;27870:1:0;27834:38;;27807:77;27904:18;27928:1;27924;:5;;;;;;27896:34;;;;27723:215;;;;;:::o;4113:979::-;4243:12;4276:18;4287:6;4276:10;:18::i;:::-;4268:60;;;;-1:-1:-1;;;4268:60:0;;;;;;;;;4402:12;4416:23;4443:6;-1:-1:-1;;;;;4443:11:0;4463:8;4474:4;4443:36;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4401:78;;;;4494:7;4490:595;;;4525:10;-1:-1:-1;4518:17:0;;-1:-1:-1;4518:17:0;4490:595;4639:17;;:21;4635:439;;4902:10;4896:17;4963:15;4950:10;4946:2;4942:19;4935:44;4850:148;5045:12;5038:20;;-1:-1:-1;;;5038:20:0;;;;;;;;;2138:619;2198:4;2666:20;;2509:66;2706:23;;;;;;:42;;-1:-1:-1;;2733:15:0;;;2698:51;-1:-1:-1;;2138:619:0:o;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;:::o;5:130::-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;302:616;;417:3;410:4;402:6;398:17;394:27;384:2;;-1:-1;;425:12;384:2;478:78;25230:17;478:78;;;469:87;;562:16;621:17;679:3;25230:17;654:3;650:27;647:36;644:2;;;696:1;;686:12;644:2;721:1;706:206;459:4;728:1;725:13;706:206;;;85:6;72:20;97:33;124:5;97:33;;;799:50;;25242:4;863:14;;;;891;;;;;753:1;746:9;706:206;;;710:14;;;377:541;;;;;1674:128;1749:13;;27467;;27460:21;30162:32;;30152:2;;30208:1;;30198:12;1810:440;;1911:3;1904:4;1896:6;1892:17;1888:27;1878:2;;-1:-1;;1919:12;1878:2;1966:6;1953:20;25723:18;25715:6;25712:30;25709:2;;;-1:-1;;25745:12;25709:2;1988:64;25818:9;25799:17;;-1:-1;;25795:33;25886:4;25876:15;1988:64;;;1979:73;;2072:6;2065:5;2058:21;2176:3;25886:4;2167:6;2100;2158:16;;2155:25;2152:2;;;2193:1;;2183:12;2152:2;29528:6;25886:4;2100:6;2096:17;25886:4;2134:5;2130:16;29505:30;29584:1;29566:16;;;25886:4;29566:16;29559:27;2134:5;1871:379;-1:-1;;1871:379;5319:134;5397:13;;-1:-1;;;;;27555:46;;30395:35;;30385:2;;30444:1;;30434:12;5738:241;;5842:2;5830:9;5821:7;5817:23;5813:32;5810:2;;;-1:-1;;5848:12;5810:2;85:6;72:20;97:33;124:5;97:33;;;5900:63;5804:175;-1:-1;;;5804:175;5986:263;;6101:2;6089:9;6080:7;6076:23;6072:32;6069:2;;;-1:-1;;6107:12;6069:2;226:6;220:13;238:33;265:5;238:33;;6256:366;;;6377:2;6365:9;6356:7;6352:23;6348:32;6345:2;;;-1:-1;;6383:12;6345:2;85:6;72:20;97:33;124:5;97:33;;;6435:63;6535:2;6574:22;;;;5527:20;;-1:-1;;;6339:283;6629:392;;6769:2;;6757:9;6748:7;6744:23;6740:32;6737:2;;;-1:-1;;6775:12;6737:2;6826:17;6820:24;6864:18;;6856:6;6853:30;6850:2;;;-1:-1;;6886:12;6850:2;6988:6;6977:9;6973:22;1072:3;1065:4;1057:6;1053:17;1049:27;1039:2;;-1:-1;;1080:12;1039:2;1120:6;1114:13;1100:27;;6864:18;25420:6;25417:30;25414:2;;;-1:-1;;25450:12;25414:2;6769;25487:6;25483:17;;;1142:80;6769:2;25483:17;25548:15;1142:80;;;1250:21;;;1307:14;;;;1282:17;;;1387:27;;;;;1384:36;-1:-1;1381:2;;;-1:-1;;1423:12;1381:2;-1:-1;1449:10;;1443:217;1468:6;1465:1;1462:13;1443:217;;;1548:48;1592:3;1580:10;1548:48;;;1536:61;;1490:1;1483:9;;;;;1611:14;;;;1639;;1443:217;;;-1:-1;6906:99;6731:290;-1:-1;;;;;;;;6731:290;7028:257;;7140:2;7128:9;7119:7;7115:23;7111:32;7108:2;;;-1:-1;;7146:12;7108:2;1755:6;1749:13;30187:5;27467:13;27460:21;30165:5;30162:32;30152:2;;-1:-1;;30198:12;7292:393;;;7421:2;7409:9;7400:7;7396:23;7392:32;7389:2;;;-1:-1;;7427:12;7389:2;7489:61;7542:7;7518:22;7489:61;;;7479:71;;7587:2;7641:9;7637:22;5675:13;7595:74;;7383:302;;;;;;7692:388;;;7824:2;7812:9;7803:7;7799:23;7795:32;7792:2;;;-1:-1;;7830:12;7792:2;2349:6;2336:20;30304:1;30297:5;30294:12;30284:2;;-1:-1;;30310:12;30284:2;7882:74;-1:-1;7993:2;8032:22;;72:20;97:33;72:20;97:33;;;8001:63;;;;7786:294;;;;;;8087:328;;8234:3;8222:9;8213:7;8209:23;8205:33;8202:2;;;-1:-1;;8241:12;8202:2;2635:20;8234:3;2635:20;;;226:6;220:13;238:33;265:5;238:33;;;2712:86;;2896:60;2952:3;2863:2;2928:22;;2896:60;;;2863:2;2882:5;2878:16;2871:86;3055:60;3111:3;3022:2;3091:9;3087:22;3055:60;;;3022:2;3041:5;3037:16;3030:86;3223:60;3279:3;3190:2;3259:9;3255:22;3223:60;;;3190:2;3209:5;3205:16;3198:86;3392:60;3448:3;3358;3428:9;3424:22;3392:60;;;3358:3;3378:5;3374:16;3367:86;3556:57;3609:3;3522;3589:9;3585:22;3556:57;;;3522:3;3538:16;;3531:83;3542:5;8196:219;-1:-1;;;8196:219;8422:684;;;;8613:3;8601:9;8592:7;8588:23;8584:33;8581:2;;;-1:-1;;8620:12;8581:2;8678:17;8665:31;8716:18;;8708:6;8705:30;8702:2;;;-1:-1;;8738:12;8702:2;3805:6;;8819:22;;3784:19;;;3780:32;-1:-1;3777:2;;;-1:-1;;3815:12;3777:2;3843:22;3805:6;3843:22;;;3950:49;3995:3;3971:22;3950:49;;;3932:16;3925:75;4098:49;4143:3;4065:2;4123:9;4119:22;4098:49;;;4065:2;4084:5;4080:16;4073:75;4214:2;4272:9;4268:22;5527:20;4214:2;4233:5;4229:16;4222:75;4364:2;4422:9;4418:22;5527:20;4364:2;4383:5;4379:16;4372:75;8613:3;4571:9;4567:22;5527:20;8613:3;4532:5;4528:16;4521:75;4694:49;4739:3;4660;4719:9;4715:22;4694:49;;;4660:3;4680:5;4676:16;4669:75;4847:49;4892:3;4813;4872:9;4868:22;4847:49;;;4813:3;4833:5;4829:16;4822:75;4990:3;4979:9;4975:19;4962:33;4948:47;;8716:18;5007:6;5004:30;5001:2;;;-1:-1;;5037:12;5001:2;5082:58;5136:3;5127:6;5116:9;5112:22;5082:58;;;4990:3;5068:5;5064:16;5057:84;5205:3;;;;5266:9;5262:22;5527:20;5205:3;5225:5;5221:18;5214:77;8758:93;;;;;;;8906:76;8974:7;4065:2;8954:9;8950:22;8906:76;;;8896:86;;9037:53;9082:7;4364:2;9062:9;9058:22;9037:53;;;9027:63;;8575:531;;;;;;9113:241;;9217:2;9205:9;9196:7;9192:23;9188:32;9185:2;;;-1:-1;;9223:12;9185:2;-1:-1;5527:20;;9179:175;-1:-1;9179:175;9361:263;;9476:2;9464:9;9455:7;9451:23;9447:32;9444:2;;;-1:-1;;9482:12;9444:2;-1:-1;5675:13;;9438:186;-1:-1;9438:186;9631:672;;;;;9797:3;9785:9;9776:7;9772:23;9768:33;9765:2;;;-1:-1;;9804:12;9765:2;-1:-1;;5675:13;;9967:2;10017:22;;5675:13;10086:2;10136:22;;5675:13;10205:2;10255:22;;;5675:13;;;;;-1:-1;5675:13;;-1:-1;9759:544;-1:-1;9759:544;10492:103;-1:-1;;;;;27675:54;10553:37;;10547:48;10755:660;11118:21;11160:1;11145:258;26106:4;11167:1;11164:13;11145:258;;;11231:13;;-1:-1;;;;;27675:54;10553:37;;10473:4;10464:14;;;;26471;;;;25723:18;11185:9;11145:258;;;11149:14;;10867:548;;;11534:323;;11666:5;26214:12;26751:6;26746:3;26739:19;11749:52;11794:6;26788:4;26783:3;26779:14;26788:4;11775:5;11771:16;11749:52;;;25818:9;29945:14;-1:-1;;29941:28;11813:39;;;;26788:4;11813:39;;11614:243;-1:-1;;11614:243;17283:271;;12374:5;26214:12;12485:52;12530:6;12525:3;12518:4;12511:5;12507:16;12485:52;;;12549:16;;;;;17417:137;-1:-1;;17417:137;17561:222;-1:-1;;;;;27675:54;;;;10553:37;;17688:2;17673:18;;17659:124;17790:528;-1:-1;;;;;27675:54;;;10553:37;;27675:54;;18155:2;18140:18;;10553:37;17991:2;18192;18177:18;;18170:48;;;17790:528;;18232:76;;17976:18;;18294:6;18232:76;;;18224:84;17962:356;-1:-1;;;;;17962:356;18325:834;-1:-1;;;;;27675:54;;;10553:37;;27675:54;;18792:2;18777:18;;10553:37;18627:3;18829:2;18814:18;;18807:48;;;14095:2;18612:19;;;26739;-1:-1;;;27686:42;26779:14;;14111:45;14175:12;19033:2;19018:18;;19011:48;;;18325:834;;19073:76;;14175:12;;19135:6;19073:76;;19166:834;-1:-1;;;;;27675:54;;;10553:37;;27675:54;;19633:2;19618:18;;10553:37;19468:3;19670:2;19655:18;;19648:48;;;14426:2;19453:19;;;26739;-1:-1;;;27686:42;26779:14;;14442:45;14506:12;19874:2;19859:18;;19852:48;;;19166:834;;19914:76;;14506:12;;19976:6;19914:76;;20007:333;-1:-1;;;;;27675:54;;;;10553:37;;20326:2;20311:18;;17114:37;20162:2;20147:18;;20133:207;20347:210;27467:13;;27460:21;11488:34;;20468:2;20453:18;;20439:118;20564:321;27467:13;;27460:21;11488:34;;20871:2;20856:18;;17114:37;20713:2;20698:18;;20684:201;22311:310;;22458:2;22479:17;22472:47;22533:78;22458:2;22447:9;22443:18;22597:6;22533:78;;22628:416;22828:2;22842:47;;;14757:2;22813:18;;;26739:19;14793:31;26779:14;;;14773:52;14844:12;;;22799:245;23051:416;23251:2;23265:47;;;15095:2;23236:18;;;26739:19;15131:34;26779:14;;;15111:55;-1:-1;;;15186:12;;;15179:34;15232:12;;;23222:245;23474:705;;23763:3;23785:17;23778:47;15604:63;23763:3;23752:9;23748:19;15581:16;15575:23;15604:63;;;15750:4;15743:5;15739:16;15733:23;15762:63;15810:14;23752:9;15810:14;15796:12;15762:63;;;;15909:4;15902:5;15898:16;15892:23;15969:14;23752:9;15969:14;17114:37;16069:4;16062:5;16058:16;16052:23;16129:14;23752:9;16129:14;17114:37;23763:3;16220:5;16216:16;16210:23;16287:14;16210:23;16287:14;23752:9;16287:14;17114:37;15810:14;16377:5;16373:16;16367:23;16347:43;;15504:6;16396:63;15504:6;23752:9;16444:14;16430:12;16396:63;;;15969:14;16539:5;16535:16;16529:23;16509:43;;16558:63;16606:14;23752:9;16606:14;16592:12;16558:63;;;16129:14;16697:5;16693:16;16687:23;16667:43;;15504:6;16730:14;23752:9;16730:14;16723:38;;16776:71;15495:16;23752:9;15495:16;16828:12;16776:71;;;16924:18;;;16918:25;16997:16;;;17114:37;23831:126;-1:-1;23968:118;;-1:-1;15750:4;24067:18;;24058:6;23968:118;;;17144:5;16069:4;24154:9;24150:18;17114:37;23734:445;;;;;;;24186:222;17114:37;;;24313:2;24298:18;;24284:124;24415:333;17114:37;;;24734:2;24719:18;;17114:37;24570:2;24555:18;;24541:207;24755:256;24817:2;24811:9;24843:17;;;24918:18;24903:34;;24939:22;;;24900:62;24897:2;;;24975:1;;24965:12;24897:2;24817;24984:22;24795:216;;-1:-1;24795:216;29601:268;29666:1;29673:101;29687:6;29684:1;29681:13;29673:101;;;29754:11;;;29748:18;29735:11;;;29728:39;29709:2;29702:10;29673:101;;;29789:6;29786:1;29783:13;29780:2;;;-1:-1;;29666:1;29836:16;;29829:27;29650:219;29982:117;-1:-1;;;;;27675:54;;30041:35;;30031:2;;30090:1;;30080:12

Swarm Source

ipfs://ee38c0ddb1aa646452ad377130f15c76115575c8cbbf7a1dc02030d52cf71f41

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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