ETH Price: $3,528.23 (+5.46%)

Contract

0x7FE3Fe4b523D7F83dC05224573B01296877dC163
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update119315782021-02-26 7:49:081370 days ago1614325748IN
0x7FE3Fe4b...6877dC163
0 ETH1.26726284191
Update119251782021-02-25 8:15:041371 days ago1614240904IN
0x7FE3Fe4b...6877dC163
0 ETH0.92833244140
Update119188832021-02-24 9:00:011372 days ago1614157201IN
0x7FE3Fe4b...6877dC163
0 ETH1.14607601184
Update119018562021-02-21 18:14:021374 days ago1613931242IN
0x7FE3Fe4b...6877dC163
0 ETH1.02557559130
Update118447852021-02-12 23:17:401383 days ago1613171860IN
0x7FE3Fe4b...6877dC163
0 ETH1.57665424211
Update118382242021-02-11 23:17:521384 days ago1613085472IN
0x7FE3Fe4b...6877dC163
0 ETH1.01605042136
Update118317212021-02-10 23:17:061385 days ago1612999026IN
0x7FE3Fe4b...6877dC163
0 ETH1.1067619148
Update118252292021-02-09 23:20:411386 days ago1612912841IN
0x7FE3Fe4b...6877dC163
0 ETH0.9792833124
Update118204002021-02-09 5:39:521387 days ago1612849192IN
0x7FE3Fe4b...6877dC163
0 ETH0.77804473111
Update118134292021-02-08 3:39:001388 days ago1612755540IN
0x7FE3Fe4b...6877dC163
0 ETH0.82871534111
Update118083702021-02-07 9:07:241389 days ago1612688844IN
0x7FE3Fe4b...6877dC163
0 ETH0.7388259199
Update118029162021-02-06 13:04:051390 days ago1612616645IN
0x7FE3Fe4b...6877dC163
0 ETH0.90275788121
Update117745992021-02-02 4:14:311394 days ago1612239271IN
0x7FE3Fe4b...6877dC163
0 ETH0.82053181104
Update117680772021-02-01 4:00:261395 days ago1612152026IN
0x7FE3Fe4b...6877dC163
0 ETH0.93457012125
Update117615002021-01-31 4:12:001396 days ago1612066320IN
0x7FE3Fe4b...6877dC163
0 ETH1.0232521130
Update117550212021-01-30 4:00:261397 days ago1611979226IN
0x7FE3Fe4b...6877dC163
0 ETH0.5603148171
Update117485342021-01-29 4:00:051398 days ago1611892805IN
0x7FE3Fe4b...6877dC163
0 ETH0.98048002130
Update117420582021-01-28 4:00:051399 days ago1611806405IN
0x7FE3Fe4b...6877dC163
0 ETH0.485821261
Update117355862021-01-27 4:03:121400 days ago1611720192IN
0x7FE3Fe4b...6877dC163
0 ETH0.3659221346
Update117290642021-01-26 4:00:261401 days ago1611633626IN
0x7FE3Fe4b...6877dC163
0 ETH0.6345002181
Update117225672021-01-25 4:09:571402 days ago1611547797IN
0x7FE3Fe4b...6877dC163
0 ETH0.7045527690
Update117172172021-01-24 8:21:431403 days ago1611476503IN
0x7FE3Fe4b...6877dC163
0 ETH0.3160850848
Update117106382021-01-23 8:04:121404 days ago1611389052IN
0x7FE3Fe4b...6877dC163
0 ETH0.438584156
Update117041122021-01-22 8:03:371405 days ago1611302617IN
0x7FE3Fe4b...6877dC163
0 ETH0.6817144287
Update116975812021-01-21 8:01:411406 days ago1611216101IN
0x7FE3Fe4b...6877dC163
0 ETH0.3368046343
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
KyberPriceFeed

Compiler Version
v0.6.1+commit.e6f7d5a4

Optimization Enabled:
Yes with 200 runs

Other Settings:
istanbul EvmVersion, GNU GPLv3 license
File 1 of 77 : KyberPriceFeed.sol
pragma solidity 0.6.1;

import "../dependencies/token/IERC20.sol";
import "../dependencies/DSMath.sol";
import "../dependencies/DSAuth.sol"; // TODO: remove? this may not be used at all
import "../exchanges/interfaces/IKyberNetworkProxy.sol";
import "../version/Registry.sol";

/// @title Price Feed Template
/// @author Melonport AG <[email protected]>
/// @notice Routes external data to smart contracts
/// @notice Where external data includes sharePrice of Melon funds
/// @notice PriceFeed operator could be staked and sharePrice input validated on chain
contract KyberPriceFeed is DSMath, DSAuth {
    event PriceUpdate(address[] token, uint[] price);

    address public KYBER_NETWORK_PROXY;
    address public QUOTE_ASSET;
    address public UPDATER;
    Registry public REGISTRY;
    uint public MAX_SPREAD;
    address public constant KYBER_ETH_TOKEN = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
    uint public constant KYBER_PRECISION = 18;
    uint public constant VALIDITY_INTERVAL = 2 days;
    uint public lastUpdate;

    // FIELDS

    mapping (address => uint) public prices;

    // METHODS

    // CONSTRUCTOR

    /// @dev Define and register a quote asset against which all prices are measured/based against
    constructor(
        address ofRegistry,
        address ofKyberNetworkProxy,
        uint ofMaxSpread,
        address ofQuoteAsset,
        address initialUpdater
    )
        public
    {
        KYBER_NETWORK_PROXY = ofKyberNetworkProxy;
        MAX_SPREAD = ofMaxSpread;
        QUOTE_ASSET = ofQuoteAsset;
        REGISTRY = Registry(ofRegistry);
        UPDATER = initialUpdater;
    }

    /// @dev Stores zero as a convention for invalid price
    function update() external {
        require(
            msg.sender == REGISTRY.owner() || msg.sender == UPDATER,
            "Only registry owner or updater can call"
        );
        address[] memory assets = REGISTRY.getRegisteredAssets();
        uint[] memory newPrices = new uint[](assets.length);
        for (uint i; i < assets.length; i++) {
            bool isValid;
            uint price;
            if (assets[i] == QUOTE_ASSET) {
                isValid = true;
                price = 1 ether;
            } else {
                (isValid, price) = getKyberPrice(assets[i], QUOTE_ASSET);
            }
            newPrices[i] = isValid ? price : 0;
            prices[assets[i]] = newPrices[i];
        }
        lastUpdate = block.timestamp;
        emit PriceUpdate(assets, newPrices);
    }

    function setUpdaterToRegistryOwner() external {
        require(msg.sender == UPDATER, "Only current updater can do this");
        UPDATER = REGISTRY.owner();
    }

    function setUpdater(address _updater) external {
        require(msg.sender == REGISTRY.owner(), "Only registry owner can set");
        UPDATER = _updater;
    }

    function setRegistry(address _newRegistry) external {
        require(msg.sender == REGISTRY.owner(), "Only registry owner can set");
        REGISTRY = Registry(_newRegistry);
    }

    /// @notice _maxSpread becomes a percentage when divided by 10^18
    /// @notice (e.g. 10^17 becomes 10%)
    function setMaxSpread(uint _maxSpread) external {
        require(msg.sender == REGISTRY.owner(), "Only registry owner can set");
        MAX_SPREAD = _maxSpread;
    }

    // PUBLIC VIEW METHODS

    // FEED INFORMATION

    function getQuoteAsset() public view returns (address) { return QUOTE_ASSET; }

    // PRICES

    /**
    @notice Gets price of an asset multiplied by ten to the power of assetDecimals
    @dev Asset has been registered
    @param _asset Asset for which price should be returned
    @return price Price formatting: mul(exchangePrice, 10 ** decimal), to avoid floating numbers
    @return timestamp When the asset's price was updated
    }
    */
    function getPrice(address _asset)
        public
        view
        returns (uint price, uint timestamp)
    {
        (price, ) =  getReferencePriceInfo(_asset, QUOTE_ASSET);
        timestamp = now;
    }

    function getPrices(address[] memory _assets)
        public
        view
        returns (uint256[] memory, uint256[] memory)
    {
        uint[] memory newPrices = new uint[](_assets.length);
        uint[] memory timestamps = new uint[](_assets.length);
        for (uint i; i < _assets.length; i++) {
            (newPrices[i], timestamps[i]) = getPrice(_assets[i]);
        }
        return (newPrices, timestamps);
    }

    function hasValidPrice(address _asset)
        public
        view
        returns (bool)
    {
        bool isRegistered = REGISTRY.assetIsRegistered(_asset);
        bool isFresh = block.timestamp < add(lastUpdate, VALIDITY_INTERVAL);
        return prices[_asset] != 0 && isRegistered && isFresh;
    }

    function hasValidPrices(address[] memory _assets)
        public
        view
        returns (bool)
    {
        for (uint i; i < _assets.length; i++) {
            if (!hasValidPrice(_assets[i])) {
                return false;
            }
        }
        return true;
    }

    /**
    @param _baseAsset Address of base asset
    @param _quoteAsset Address of quote asset
    @return referencePrice Quantity of quoteAsset per whole baseAsset
    @return decimals Decimal places for quoteAsset
    }
    */
    function getReferencePriceInfo(address _baseAsset, address _quoteAsset)
        public
        view
        returns (uint referencePrice, uint decimals)
    {
        bool isValid;
        (
            isValid,
            referencePrice,
            decimals
        ) = getRawReferencePriceInfo(_baseAsset, _quoteAsset);
        require(isValid, "Price is not valid");
        return (referencePrice, decimals);
    }

    function getRawReferencePriceInfo(address _baseAsset, address _quoteAsset)
        public
        view
        returns (bool isValid, uint256 referencePrice, uint256 decimals)
    {
        isValid = hasValidPrice(_baseAsset) && hasValidPrice(_quoteAsset);
        uint256 quoteDecimals = ERC20WithFields(_quoteAsset).decimals();

        if (prices[_quoteAsset] == 0) {
            return (false, 0, 0);  // return early and avoid revert
        }

        referencePrice = mul(
            prices[_baseAsset],
            10 ** uint(quoteDecimals)
        ) / prices[_quoteAsset];

        return (isValid, referencePrice, quoteDecimals);
    }

    function getPriceInfo(address _asset)
        public
        view
        returns (uint256 price, uint256 assetDecimals)
    {
        return getReferencePriceInfo(_asset, QUOTE_ASSET);
    }

    /**
    @notice Gets inverted price of an asset
    @dev Asset has been initialised and its price is non-zero
    @param _asset Asset for which inverted price should be return
    @return invertedPrice Price based (instead of quoted) against QUOTE_ASSET
    @return assetDecimals Decimal places for this asset
    }
    */
    function getInvertedPriceInfo(address _asset)
        public
        view
        returns (uint256 invertedPrice, uint256 assetDecimals)
    {
        return getReferencePriceInfo(QUOTE_ASSET, _asset);
    }

    /// @dev Get Kyber representation of ETH if necessary
    function getKyberMaskAsset(address _asset) public view returns (address) {
        if (_asset == REGISTRY.nativeAsset()) {
            return KYBER_ETH_TOKEN;
        }
        return _asset;
    }

    /// @notice Returns validity and price from Kyber
    function getKyberPrice(address _baseAsset, address _quoteAsset)
        public
        view
        returns (bool, uint)
    {
        uint bidRate;
        uint bidRateOfReversePair;
        (bidRate,) = IKyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate(
            getKyberMaskAsset(_baseAsset),
            getKyberMaskAsset(_quoteAsset),
            REGISTRY.getReserveMin(_baseAsset)
        );
        (bidRateOfReversePair,) = IKyberNetworkProxy(KYBER_NETWORK_PROXY).getExpectedRate(
            getKyberMaskAsset(_quoteAsset),
            getKyberMaskAsset(_baseAsset),
            REGISTRY.getReserveMin(_quoteAsset)
        );

        if (bidRate == 0 || bidRateOfReversePair == 0) {
            return (false, 0);  // return early and avoid revert
        }

        uint askRate = 10 ** (KYBER_PRECISION * 2) / bidRateOfReversePair;
        /**
          Average the bid/ask prices:
          avgPriceFromKyber = (bidRate + askRate) / 2
          kyberPrice = (avgPriceFromKyber * 10^quoteDecimals) / 10^kyberPrecision
          or, rearranged:
          kyberPrice = ((bidRate + askRate) * 10^quoteDecimals) / 2 * 10^kyberPrecision
        */
        uint kyberPrice = mul(
            add(bidRate, askRate),
            10 ** uint(ERC20WithFields(_quoteAsset).decimals()) // use original quote decimals (not defined on mask)
        ) / mul(2, 10 ** uint(KYBER_PRECISION));

        // Find the "quoted spread", to inform caller whether it is below maximum
        uint spreadFromKyber;
        if (bidRate > askRate) {
            spreadFromKyber = 0; // crossed market condition
        } else {
            spreadFromKyber = mul(
                sub(askRate, bidRate),
                10 ** uint(KYBER_PRECISION)
            ) / askRate;
        }

        return (
            spreadFromKyber <= MAX_SPREAD && bidRate != 0 && askRate != 0,
            kyberPrice
        );
    }

    /// @notice Gets price of Order
    /// @param sellAsset Address of the asset to be sold
    /// @param sellQuantity Quantity in base units being sold of sellAsset
    /// @param buyQuantity Quantity in base units being bought of buyAsset
    /// @return orderPrice Price as determined by an order
    function getOrderPriceInfo(
        address sellAsset,
        uint sellQuantity,
        uint buyQuantity
    )
        public
        view
        returns (uint orderPrice)
    {
        // TODO: decimals
        return mul(buyQuantity, 10 ** uint(ERC20WithFields(sellAsset).decimals())) / sellQuantity;
    }

    /// @notice Checks whether data exists for a given asset pair
    /// @dev Prices are only upated against QUOTE_ASSET
    /// @param sellAsset Asset for which check to be done if data exists
    /// @param buyAsset Asset for which check to be done if data exists
    function existsPriceOnAssetPair(address sellAsset, address buyAsset)
        public
        view
        returns (bool)
    {
        return
            hasValidPrice(sellAsset) && // Is tradable asset (TODO cleaner) and datafeed delivering data
            hasValidPrice(buyAsset);
    }

    /// @notice Get quantity of toAsset equal in value to given quantity of fromAsset
    function convertQuantity(
        uint fromAssetQuantity,
        address fromAsset,
        address toAsset
    )
        public
        view
        returns (uint)
    {
        uint fromAssetPrice;
        (fromAssetPrice,) = getReferencePriceInfo(fromAsset, toAsset);
        uint fromAssetDecimals = ERC20WithFields(fromAsset).decimals();
        return mul(
            fromAssetQuantity,
            fromAssetPrice
        ) / (10 ** uint(fromAssetDecimals));
    }

    function getLastUpdate() public view returns (uint) { return lastUpdate; }
}

File 2 of 77 : DSAuth.sol
/// @notice Modified from DappHub (https://git.io/fpwrq)

pragma solidity 0.6.1;

abstract contract DSAuthority {
    function canCall(
        address src, address dst, bytes4 sig
    ) public view virtual returns (bool);
}

contract DSAuthEvents {
    event LogSetAuthority (address indexed authority);
    event LogSetOwner     (address indexed owner);
}

contract DSAuth is DSAuthEvents {
    DSAuthority  public  authority;
    address      public  owner;

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

    function setOwner(address owner_)
        public
        auth
    {
        owner = owner_;
        emit LogSetOwner(owner);
    }

    function setAuthority(DSAuthority authority_)
        public
        auth
    {
        authority = authority_;
        emit LogSetAuthority(address(authority));
    }

    modifier auth {
        require(isAuthorized(msg.sender, msg.sig), "ds-auth-unauthorized");
        _;
    }

    function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
        if (src == address(this)) {
            return true;
        } else if (src == owner) {
            return true;
        } else if (authority == DSAuthority(0)) {
            return false;
        } else {
            return authority.canCall(src, address(this), sig);
        }
    }
}

File 3 of 77 : DSGuard.sol
/// @notice Retrieved from DappHub (https://git.io/fpwMi)

pragma solidity 0.6.1;

import "./DSAuth.sol";

contract DSGuardEvents {
    event LogPermit(
        bytes32 indexed src,
        bytes32 indexed dst,
        bytes32 indexed sig
    );

    event LogForbid(
        bytes32 indexed src,
        bytes32 indexed dst,
        bytes32 indexed sig
    );
}

contract DSGuard is DSAuth, DSAuthority, DSGuardEvents {
    bytes32 constant public ANY = bytes32(uint(-1));

    mapping (bytes32 => mapping (bytes32 => mapping (bytes32 => bool))) acl;

    function canCall(
        address src_, address dst_, bytes4 sig
    ) public view override returns (bool) {
        bytes32 src = bytes32(bytes20(src_));
        bytes32 dst = bytes32(bytes20(dst_));

        return acl[src][dst][sig]
            || acl[src][dst][ANY]
            || acl[src][ANY][sig]
            || acl[src][ANY][ANY]
            || acl[ANY][dst][sig]
            || acl[ANY][dst][ANY]
            || acl[ANY][ANY][sig]
            || acl[ANY][ANY][ANY];
    }

    function permit(bytes32 src, bytes32 dst, bytes32 sig) public auth {
        acl[src][dst][sig] = true;
        emit LogPermit(src, dst, sig);
    }

    function forbid(bytes32 src, bytes32 dst, bytes32 sig) public auth {
        acl[src][dst][sig] = false;
        emit LogForbid(src, dst, sig);
    }

    function permit(address src, address dst, bytes32 sig) public {
        permit(bytes32(bytes20(src)), bytes32(bytes20(dst)), sig);
    }
    function forbid(address src, address dst, bytes32 sig) public {
        forbid(bytes32(bytes20(src)), bytes32(bytes20(dst)), sig);
    }

}

contract DSGuardFactory {
    mapping (address => bool)  public  isGuard;

    function newGuard() public returns (DSGuard guard) {
        guard = new DSGuard();
        guard.setOwner(msg.sender);
        isGuard[address(guard)] = true;
    }
}

File 4 of 77 : DSMath.sol
/// DSMath.sol -- mixin for inline numerical wizardry

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity >0.4.13;

contract DSMath {
    function add(uint x, uint y) internal pure returns (uint z) {
        require((z = x + y) >= x, "ds-math-add-overflow");
    }
    function sub(uint x, uint y) internal pure returns (uint z) {
        require((z = x - y) <= x, "ds-math-sub-underflow");
    }
    function mul(uint x, uint y) internal pure returns (uint z) {
        require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
    }

    function min(uint x, uint y) internal pure returns (uint z) {
        return x <= y ? x : y;
    }
    function max(uint x, uint y) internal pure returns (uint z) {
        return x >= y ? x : y;
    }
    function imin(int x, int y) internal pure returns (int z) {
        return x <= y ? x : y;
    }
    function imax(int x, int y) internal pure returns (int z) {
        return x >= y ? x : y;
    }

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

    function wmul(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, y), WAD / 2) / WAD;
    }
    function rmul(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, y), RAY / 2) / RAY;
    }
    function wdiv(uint x, uint y) internal pure returns (uint z) {
        z = add(mul(x, WAD), y / 2) / y;
    }
    function rdiv(uint x, uint y) internal pure returns (uint 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(uint x, uint n) internal pure returns (uint z) {
        z = n % 2 != 0 ? x : RAY;

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

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }
}

File 5 of 77 : SafeMath.sol
pragma solidity 0.6.1;


/**
 * @title SafeMath
 * @dev Math operations with safety checks that revert on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, reverts on overflow.
  */
  function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (_a == 0) {
      return 0;
    }

    uint256 c = _a * _b;
    require(c / _a == _b);

    return c;
  }

  /**
  * @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
  */
  function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
    require(_b > 0); // Solidity only automatically asserts when dividing by 0
    uint256 c = _a / _b;
    // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold

    return c;
  }

  /**
  * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
    require(_b <= _a);
    uint256 c = _a - _b;

    return c;
  }

  /**
  * @dev Adds two numbers, reverts on overflow.
  */
  function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
    uint256 c = _a + _b;
    require(c >= _a);

    return c;
  }

  /**
  * @dev Divides two numbers and returns the remainder (unsigned integer modulo),
  * reverts when dividing by zero.
  */
  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    require(b != 0);
    return a % b;
  }
}

File 6 of 77 : BurnableToken.sol
pragma solidity 0.6.1;

import "./PreminedToken.sol";

/// @dev Just a wrapper for premined tokens which can actually be burnt
contract BurnableToken is PreminedToken {
    constructor(string memory _symbol, uint8 _decimals, string memory _name)
        public
        PreminedToken(_symbol, _decimals, _name)
    {}

    function burn(uint _amount) public {
        _burn(msg.sender, _amount);
    }

    function burnFrom(address from, uint256 value) public {
        _burnFrom(from, value);
    }
}

File 7 of 77 : IERC20.sol
pragma solidity 0.6.1;

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 * Altered from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/ERC20.sol
 */
interface IERC20 {
  function totalSupply() external view returns (uint256);

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

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

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

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

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

  event Transfer(
    address indexed from,
    address indexed to,
    uint256 value
  );

  event Approval(
    address indexed owner,
    address indexed spender,
    uint256 value
  );
}

/// @dev Just adds extra functions that we use elsewhere
abstract contract ERC20WithFields is IERC20 {
    string public symbol;
    string public name;
    uint8 public decimals;
}

File 8 of 77 : PreminedToken.sol
pragma solidity 0.6.1;

import "./StandardToken.sol";

contract PreminedToken is StandardToken {
    string public symbol;
    string public  name;
    uint8 public decimals;

    constructor(string memory _symbol, uint8 _decimals, string memory _name) public {
        symbol = _symbol;
        decimals = _decimals;
        name = _name;
        totalSupply_ = 1000000 * 10**uint(decimals);
        balances[msg.sender] = totalSupply_;
        emit Transfer(address(0), msg.sender, totalSupply_);
    }
}

File 9 of 77 : StandardToken.sol
pragma solidity 0.6.1;

import "./IERC20.sol";
import "../SafeMath.sol";

/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
 * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
 * Modified from https://github.com/OpenZeppelin/openzeppelin-solidity/blob/a466e76d26c394b1faa6e2797aefe34668566392/contracts/token/ERC20/StandardToken.sol
 */
contract StandardToken is IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) balances;

    mapping (address => mapping (address => uint256)) allowed;

    uint256 totalSupply_;

    /**
     * @dev Total number of tokens in existence
     */
    function totalSupply() public view override returns (uint256) {
        return totalSupply_;
    }

    /**
     * @dev Gets the balance of the specified address.
     * @param _owner The address to query the the balance of.
     * @return An uint256 representing the amount owned by the passed address.
     */
    function balanceOf(address _owner) public view override returns (uint256) {
        return balances[_owner];
    }

    /**
     * @dev Function to check the amount of tokens that an owner allowed to a spender.
     * @param _owner address The address which owns the funds.
     * @param _spender address The address which will spend the funds.
     * @return A uint256 specifying the amount of tokens still available for the spender.
     */
    function allowance(
        address _owner,
        address _spender
    )
        public
        view
        override
        returns (uint256)
    {
        return allowed[_owner][_spender];
    }

    /**
    * @dev Transfer token for a specified address
    * @param _to The address to transfer to.
    * @param _value The amount to be transferred.
    */
    function transfer(address _to, uint256 _value) public virtual override returns (bool) {
        require(_value <= balances[msg.sender]);
        require(_to != address(0));

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    /**
    * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
    * Beware that changing an allowance with this method brings the risk that someone may use both the old
    * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
    * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
    * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    * @param _spender The address which will spend the funds.
        * @param _value The amount of tokens to be spent.
        */
    function approve(address _spender, uint256 _value) public virtual override returns (bool) {
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
    * @dev Transfer tokens from one address to another
    * @param _from address The address which you want to send tokens from
    * @param _to address The address which you want to transfer to
    * @param _value uint256 the amount of tokens to be transferred
    */
    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
        public
        virtual
        override
        returns (bool)
    {
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);
        require(_to != address(0));

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Approval(_from, msg.sender, allowed[_from][msg.sender]);
        emit Transfer(_from, _to, _value);
        return true;
    }

    /**
    * @dev Increase the amount of tokens that an owner allowed to a spender.
    * approve should be called when allowed[_spender] == 0. To increment
    * allowed value is better to use this function to avoid 2 calls (and wait until
    * the first transaction is mined)
    * From MonolithDAO Token.sol
    * @param _spender The address which will spend the funds.
    * @param _addedValue The amount of tokens to increase the allowance by.
    */
    function increaseApproval(
        address _spender,
        uint256 _addedValue
    )
        public
        virtual
        returns (bool)
    {
        allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].add(_addedValue));
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
     * @dev Decrease the amount of tokens that an owner allowed to a spender.
     * approve should be called when allowed[_spender] == 0. To decrement
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * @param _spender The address which will spend the funds.
     * @param _subtractedValue The amount of tokens to decrease the allowance by.
     */
    function decreaseApproval(
        address _spender,
        uint256 _subtractedValue
    )
        public
        virtual
        returns (bool)
    {
        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue >= oldValue) {
            allowed[msg.sender][_spender] = 0;
        } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
    * @dev Internal function that mints an amount of the token and assigns it to
    * an account. This encapsulates the modification of balances such that the
    * proper events are emitted.
    * @param _account The account that will receive the created tokens.
    * @param _amount The amount that will be created.
     */
    function _mint(address _account, uint256 _amount) internal {
        require(_account != address(0));
        totalSupply_ = totalSupply_.add(_amount);
        balances[_account] = balances[_account].add(_amount);
        emit Transfer(address(0), _account, _amount);
    }

    /**
     * @dev Internal function that burns an amount of the token of a given
     * account.
     * @param _account The account whose tokens will be burnt.
     * @param _amount The amount that will be burnt.
     */
    function _burn(address _account, uint256 _amount) internal {
        require(_account != address(0));
        require(_amount <= balances[_account]);

        totalSupply_ = totalSupply_.sub(_amount);
        balances[_account] = balances[_account].sub(_amount);
        emit Transfer(_account, address(0), _amount);
    }

    /**
     * @dev Internal function that burns an amount of the token of a given
     * account, deducting from the sender's allowance for said account. Uses the
     * internal _burn function.
     * @param _account The account whose tokens will be burnt.
     * @param _amount The amount that will be burnt.
     */
    function _burnFrom(address _account, uint256 _amount) internal {
        require(_amount <= allowed[_account][msg.sender]);
        allowed[_account][msg.sender] = allowed[_account][msg.sender].sub(_amount);
        emit Approval(_account, msg.sender, allowed[_account][msg.sender]);
        _burn(_account, _amount);
    }
}

File 10 of 77 : TokenUser.sol
pragma solidity 0.6.1;

import "./token/IERC20.sol";
import "./DSMath.sol";

/// @notice Wrapper to ensure tokens are received
contract TokenUser is DSMath {
    function safeTransfer(
        address _token,
        address _to,
        uint _value
    ) internal {
        uint receiverPreBalance = IERC20(_token).balanceOf(_to);
        IERC20(_token).transfer(_to, _value);
        uint receiverPostBalance = IERC20(_token).balanceOf(_to);
        require(
            add(receiverPreBalance, _value) == receiverPostBalance,
            "Receiver did not receive tokens in transfer"
        );
    }

    function safeTransferFrom(
        address _token,
        address _from,
        address _to,
        uint _value
    ) internal {
        uint receiverPreBalance = IERC20(_token).balanceOf(_to);
        IERC20(_token).transferFrom(_from, _to, _value);
        uint receiverPostBalance = IERC20(_token).balanceOf(_to);
        require(
            add(receiverPreBalance, _value) == receiverPostBalance,
            "Receiver did not receive tokens in transferFrom"
        );
    }
}

File 11 of 77 : WETH.sol
pragma solidity 0.6.1;

contract WETH {
    string public name     = "Wrapped Ether";
    string public symbol   = "WETH";
    uint8  public decimals = 18;

    event  Approval(address indexed src, address indexed guy, uint wad);
    event  Transfer(address indexed src, address indexed dst, uint wad);
    event  Deposit(address indexed dst, uint wad);
    event  Withdrawal(address indexed src, uint wad);

    mapping (address => uint)                       public  balanceOf;
    mapping (address => mapping (address => uint))  public  allowance;

    receive() external payable {
        deposit();
    }
    function deposit() public payable {
        balanceOf[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
    function withdraw(uint wad) public {
        require(balanceOf[msg.sender] >= wad);
        balanceOf[msg.sender] -= wad;
        msg.sender.transfer(wad);
        emit Withdrawal(msg.sender, wad);
    }

    function totalSupply() public view returns (uint) {
        return address(this).balance;
    }

    function approve(address guy, uint wad) public returns (bool) {
        allowance[msg.sender][guy] = wad;
        emit Approval(msg.sender, guy, wad);
        return true;
    }

    function transfer(address dst, uint wad) public returns (bool) {
        return transferFrom(msg.sender, dst, wad);
    }

    function transferFrom(address src, address dst, uint wad)
        public
        returns (bool)
    {
        require(balanceOf[src] >= wad);

        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
            require(allowance[src][msg.sender] >= wad);
            allowance[src][msg.sender] -= wad;
        }

        balanceOf[src] -= wad;
        balanceOf[dst] += wad;

        emit Transfer(src, dst, wad);

        return true;
    }
}

File 12 of 77 : AmguConsumer.sol
pragma solidity 0.6.1;

import "../dependencies/DSMath.sol";
import "../dependencies/token/IERC20.sol";
import "../prices/IPriceSource.sol";
import "../version/IVersion.sol";
import "./IEngine.sol";
import "../version/Registry.sol";

/// @notice Abstract contracts
/// @notice inherit this to pay AMGU on a function call
abstract contract AmguConsumer is DSMath {

    /// @dev each of these must be implemented by the inheriting contract
    function engine() public view virtual returns (address);
    function mlnToken() public view virtual returns (address);
    function priceSource() public view virtual returns (address);
    function registry() public view virtual returns (address);
    event AmguPaid(address indexed payer, uint256 totalAmguPaidInEth, uint256 amguChargableGas, uint256 incentivePaid);

    /// bool deductIncentive is used when sending extra eth beyond amgu
    modifier amguPayable(bool deductIncentive) {
        uint preGas = gasleft();
        _;
        uint postGas = gasleft();

        uint mlnPerAmgu = IEngine(engine()).getAmguPrice();
        uint mlnQuantity = mul(
            mlnPerAmgu,
            sub(preGas, postGas)
        );
        address nativeAsset = Registry(registry()).nativeAsset();
        uint ethToPay = IPriceSource(priceSource()).convertQuantity(
            mlnQuantity,
            mlnToken(),
            nativeAsset
        );
        uint incentiveAmount;
        if (deductIncentive) {
            incentiveAmount = Registry(registry()).incentive();
        } else {
            incentiveAmount = 0;
        }
        require(
            msg.value >= add(ethToPay, incentiveAmount),
            "Insufficent AMGU and/or incentive"
        );
        IEngine(engine()).payAmguInEther.value(ethToPay)();

        require(
            msg.sender.send(
                sub(
                    sub(msg.value, ethToPay),
                    incentiveAmount
                )
            ),
            "Refund failed"
        );
        emit AmguPaid(msg.sender, ethToPay, sub(preGas, postGas), incentiveAmount);
    }
}

File 13 of 77 : Engine.sol
pragma solidity 0.6.1;

import "../dependencies/DSMath.sol";
import "../dependencies/token/BurnableToken.sol";
import "../prices/IPriceSource.sol";
import "../version/Registry.sol";

/// @notice Liquidity contract and token sink
contract Engine is DSMath {

    event RegistryChange(address registry);
    event SetAmguPrice(uint amguPrice);
    event AmguPaid(uint amount);
    event Thaw(uint amount);
    event Burn(uint amount);

    uint public constant MLN_DECIMALS = 18;

    Registry public registry;
    uint public amguPrice;
    uint public frozenEther;
    uint public liquidEther;
    uint public lastThaw;
    uint public thawingDelay;
    uint public totalEtherConsumed;
    uint public totalAmguConsumed;
    uint public totalMlnBurned;

    constructor(uint _delay, address _registry) public {
        lastThaw = block.timestamp;
        thawingDelay = _delay;
        _setRegistry(_registry);
    }

    modifier onlyMGM() {
        require(
            msg.sender == registry.MGM(),
            "Only MGM can call this"
        );
        _;
    }

    /// @dev Registry owner is MTC
    modifier onlyMTC() {
        require(
            msg.sender == registry.owner(),
            "Only MTC can call this"
        );
        _;
    }

    function _setRegistry(address _registry) internal {
        registry = Registry(_registry);
        emit RegistryChange(address(registry));
    }

    /// @dev only callable by MTC
    function setRegistry(address _registry)
        external
        onlyMTC
    {
        _setRegistry(_registry);
    }

    /// @dev set price of AMGU in MLN (base units)
    /// @dev only callable by MGM
    function setAmguPrice(uint _price)
        external
        onlyMGM
    {
        amguPrice = _price;
        emit SetAmguPrice(_price);
    }

    function getAmguPrice() public view returns (uint) { return amguPrice; }

    function premiumPercent() public view returns (uint) {
        if (liquidEther < 1 ether) {
            return 0;
        } else if (liquidEther >= 1 ether && liquidEther < 5 ether) {
            return 5;
        } else if (liquidEther >= 5 ether && liquidEther < 10 ether) {
            return 10;
        } else if (liquidEther >= 10 ether) {
            return 15;
        }
    }

    function payAmguInEther() external payable {
        require(
            registry.isFundFactory(msg.sender) ||
            registry.isFund(msg.sender),
            "Sender must be a fund or the factory"
        );
        uint mlnPerAmgu = getAmguPrice();
        uint ethPerMln;
        (ethPerMln,) = priceSource().getPrice(address(mlnToken()));
        uint amguConsumed;
        if (mlnPerAmgu > 0 && ethPerMln > 0) {
            amguConsumed = (mul(msg.value, 10 ** uint(MLN_DECIMALS))) / (mul(ethPerMln, mlnPerAmgu));
        } else {
            amguConsumed = 0;
        }
        totalEtherConsumed = add(totalEtherConsumed, msg.value);
        totalAmguConsumed = add(totalAmguConsumed, amguConsumed);
        frozenEther = add(frozenEther, msg.value);
        emit AmguPaid(amguConsumed);
    }

    /// @notice Move frozen ether to liquid pool after delay
    /// @dev Delay only restarts when this function is called
    function thaw() external {
        require(
            block.timestamp >= add(lastThaw, thawingDelay),
            "Thawing delay has not passed"
        );
        require(frozenEther > 0, "No frozen ether to thaw");
        lastThaw = block.timestamp;
        liquidEther = add(liquidEther, frozenEther);
        emit Thaw(frozenEther);
        frozenEther = 0;
    }

    /// @return ETH per MLN including premium
    function enginePrice() public view returns (uint) {
        uint ethPerMln;
        (ethPerMln, ) = priceSource().getPrice(address(mlnToken()));
        uint premium = (mul(ethPerMln, premiumPercent()) / 100);
        return add(ethPerMln, premium);
    }

    function ethPayoutForMlnAmount(uint mlnAmount) public view returns (uint) {
        return mul(mlnAmount, enginePrice()) / 10 ** uint(MLN_DECIMALS);
    }

    /// @notice MLN must be approved first
    function sellAndBurnMln(uint mlnAmount) external {
        require(registry.isFund(msg.sender), "Only funds can use the engine");
        require(
            mlnToken().transferFrom(msg.sender, address(this), mlnAmount),
            "MLN transferFrom failed"
        );
        uint ethToSend = ethPayoutForMlnAmount(mlnAmount);
        require(ethToSend > 0, "No ether to pay out");
        require(liquidEther >= ethToSend, "Not enough liquid ether to send");
        liquidEther = sub(liquidEther, ethToSend);
        totalMlnBurned = add(totalMlnBurned, mlnAmount);
        msg.sender.transfer(ethToSend);
        mlnToken().burn(mlnAmount);
        emit Burn(mlnAmount);
    }

    /// @dev Get MLN from the registry
    function mlnToken()
        public
        view
        returns (BurnableToken)
    {
        return BurnableToken(registry.mlnToken());
    }

    /// @dev Get PriceSource from the registry
    function priceSource()
        public
        view
        returns (IPriceSource)
    {
        return IPriceSource(registry.priceSource());
    }
}

File 14 of 77 : IEngine.sol
pragma solidity 0.6.1;


interface IEngine {
    function payAmguInEther() external payable;
    function getAmguPrice() external view returns (uint256);
}

File 15 of 77 : EngineAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../engine/Engine.sol";
import "../fund/hub/Hub.sol";
import "../fund/trading/Trading.sol";
import "../fund/vault/Vault.sol";
import "../dependencies/DSMath.sol";
import "../dependencies/WETH.sol";
import "../dependencies/token/IERC20.sol";
import "./ExchangeAdapter.sol";
import "../dependencies/TokenUser.sol";

/// @notice Trading adapter to Melon Engine
contract EngineAdapter is DSMath, TokenUser, ExchangeAdapter {

    /// @notice Buys Ether from the engine, selling MLN
    /// @param targetExchange Address of the engine
    /// @param orderValues [0] Min Eth to receive from the engine
    /// @param orderValues [1] MLN quantity
    /// @param orderValues [6] Same as orderValues[1]
    /// @param orderAddresses [2] WETH token
    /// @param orderAddresses [3] MLN token
    function takeOrder (
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override onlyManager notShutDown {
        Hub hub = getHub();

        address wethAddress = orderAddresses[2];
        address mlnAddress = orderAddresses[3];
        uint minEthToReceive = orderValues[0];
        uint mlnQuantity = orderValues[1];

        require(
            wethAddress == Registry(hub.registry()).nativeAsset(),
            "maker asset doesnt match nativeAsset on registry"
        );
        require(
            orderValues[1] == orderValues[6],
            "fillTakerQuantity must equal takerAssetQuantity"
        );

        approveAsset(mlnAddress, targetExchange, mlnQuantity, "takerAsset");

        uint ethToReceive = Engine(targetExchange).ethPayoutForMlnAmount(mlnQuantity);

        require(
            ethToReceive >= minEthToReceive,
            "Expected ETH to receive is less than takerQuantity (minEthToReceive)"
        );

        Engine(targetExchange).sellAndBurnMln(mlnQuantity);
        WETH(payable(wethAddress)).deposit.value(ethToReceive)();
        safeTransfer(wethAddress, address(Vault(hub.vault())), ethToReceive);

        getAccounting().addAssetToOwnedAssets(wethAddress);
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(0),
            Trading.UpdateType.take,
            [payable(wethAddress), payable(mlnAddress)],
            [ethToReceive, mlnQuantity, mlnQuantity]
        );
    }
}

File 16 of 77 : EthfinexAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/token/IERC20.sol";
import "../fund/trading/Trading.sol";
import "../fund/hub/Hub.sol";
import "../fund/vault/Vault.sol";
import "../fund/accounting/Accounting.sol";
import "../version/Registry.sol";
import "../dependencies/WETH.sol";
import "../dependencies/DSMath.sol";
import "./interfaces/IZeroExV2.sol";
import "./interfaces/IEthfinex.sol";
import "./ExchangeAdapter.sol";

/// @title EthfinexAdapter Contract
/// @author Melonport AG <[email protected]>
/// @notice Adapter to EthFinex exchange
contract EthfinexAdapter is DSMath, ExchangeAdapter {
    /// @param _orderAddresses [2] Order maker asset
    /// @param _orderAddresses [3] Order taker asset
    /// @param _orderData [0] Encoded data specific to maker asset
    /// @param _orderData [1] Encoded data specific to taker asset
    modifier orderAddressesMatchOrderData(
        address[8] memory _orderAddresses,
        bytes[4] memory _orderData
    )
    {
        require(
            getAssetAddress(_orderData[0]) == getWrapperToken(_orderAddresses[2]),
            "Maker asset data does not match order address in array"
        );
        require(
            getAssetAddress(_orderData[1]) == _orderAddresses[3],
            "Taker asset data does not match order address in array"
        );
        _;
    }

    //  METHODS

    //  PUBLIC METHODS

    /// @notice Make order by pre-approving signatures
    function makeOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        onlyManager
        notShutDown
        orderAddressesMatchOrderData(_orderAddresses, _orderData)
    {
        ensureCanMakeOrder(_orderAddresses[2]);

        IZeroExV2.Order memory order = constructOrderStruct(_orderAddresses, _orderValues, _orderData);
        bytes memory wrappedMakerAssetData = _orderData[0];
        bytes memory takerAssetData = _orderData[1];
        address makerAsset = _orderAddresses[2];
        address takerAsset = getAssetAddress(takerAssetData);

        // Order parameter checks
        getTrading().updateAndGetQuantityBeingTraded(makerAsset);
        ensureNotInOpenMakeOrder(makerAsset);

        wrapMakerAsset(_targetExchange, makerAsset, wrappedMakerAssetData, order.makerAssetAmount, order.expirationTimeSeconds);

        IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(_targetExchange).getOrderInfo(order);
        IZeroExV2(_targetExchange).preSign(orderInfo.orderHash, address(this), _signature);

        require(
            IZeroExV2(_targetExchange).isValidSignature(
                orderInfo.orderHash,
                address(this),
                _signature
            ),
            "INVALID_ORDER_SIGNATURE"
        );

        updateStateMakeOrder(_targetExchange, order);
    }

    /// @notice Cancel the 0x make order
    function cancelOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        orderAddressesMatchOrderData(_orderAddresses, _orderData)
    {
        IZeroExV2.Order memory order = getTrading().getZeroExV2OrderDetails(_identifier);
        ensureCancelPermitted(_targetExchange, _orderAddresses[2], _identifier);
        IZeroExV2(_targetExchange).cancelOrder(order);

        updateStateCancelOrder(_targetExchange, order);
    }

    /// @notice Unwrap (withdraw) tokens, uses _orderAddresses for input list of tokens to be unwrapped
    /// @dev Call to "withdraw" fails if timestamp < `Wrapper.depositLock(tradingComponent)`
    function withdrawTokens(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
    {
        Hub hub = getHub();
        address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET();

        for (uint i = 0; i < _orderAddresses.length; i++) {
            if (_orderAddresses[i] == address(0)) continue;
            address wrappedToken = getWrapperToken(_orderAddresses[i]);
            uint balance = IWrapperLock(wrappedToken).balanceOf(address(this));
            require(balance > 0, "Insufficient balance");
            IWrapperLock(wrappedToken).withdraw(balance, 0, bytes32(0), bytes32(0), 0);
            if (_orderAddresses[i] == nativeAsset) {
                WETH(payable(nativeAsset)).deposit.value(balance)();
            }
            getTrading().removeOpenMakeOrder(_targetExchange, _orderAddresses[i]);
            getTrading().returnAssetToVault(_orderAddresses[i]);
        }
    }

     /// @notice Minor: Wrapped tokens directly sent to the fund are not accounted. To be called by Trading spoke
    function getOrder(address _targetExchange, uint256 _id, address _makerAsset)
        public
        view
        override
        returns (address, address, uint256, uint256)
    {
        uint orderId;
        uint orderIndex;
        address takerAsset;
        uint makerQuantity;
        uint takerQuantity;
        (orderId, , orderIndex) = Trading(msg.sender).getOpenOrderInfo(_targetExchange, _makerAsset);
        (, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex);

        // Check if order has been completely filled
        uint takerAssetFilledAmount = IZeroExV2(_targetExchange).filled(bytes32(orderId));
        if (sub(takerQuantity, takerAssetFilledAmount) == 0) {
            return (_makerAsset, takerAsset, 0, 0);
        }

        // Check if tokens have been withdrawn (cancelled order may still need to be accounted if there is balance)
        uint balance = IWrapperLock(getWrapperTokenFromAdapterContext(_makerAsset)).balanceOf(msg.sender);
        if (balance == 0) {
            return (_makerAsset, takerAsset, 0, 0);
        }
        return (_makerAsset, takerAsset, makerQuantity, sub(takerQuantity, takerAssetFilledAmount));
    }

    // INTERNAL METHODS

    /// @notice needed to avoid stack too deep error
    /// @dev deposit time should be greater than 1 hour
    function wrapMakerAsset(
        address _targetExchange,
        address _makerAsset,
        bytes memory _wrappedMakerAssetData,
        uint _makerQuantity,
        uint _orderExpirationTime
    )
        internal
    {
        Hub hub = getHub();

        // Deposit to rounded up value of time difference of expiration time and current time (in hours)
        uint depositTime = (
            sub(_orderExpirationTime, block.timestamp) / 1 hours
        ) + 1;

        address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET();
        address wrappedToken = getWrapperToken(_makerAsset);
        // Handle case for WETH vs ERC20
        if (_makerAsset == nativeAsset) {
            Vault vault = Vault(hub.vault());
            vault.withdraw(_makerAsset, _makerQuantity);
            WETH(payable(nativeAsset)).withdraw(_makerQuantity);
            IWrapperLockEth(wrappedToken).deposit.value(_makerQuantity)(_makerQuantity, depositTime);
        } else {
            approveAsset(
                _makerAsset,
                wrappedToken,
                _makerQuantity,
                "makerAsset"
            );
            IWrapperLock(wrappedToken).deposit(_makerQuantity, depositTime);
        }
    }

    // @dev avoids stack too deep error
    function updateStateCancelOrder(address _targetExchange, IZeroExV2.Order memory _order)
        internal
    {
        // Order is not removed from OpenMakeOrder mapping as it's needed for accounting (wrapped tokens)
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            _targetExchange,
            IZeroExV2(_targetExchange).getOrderInfo(_order).orderHash,
            Trading.UpdateType.cancel,
            [address(0), address(0)],
            [uint(0), uint(0), uint(0)]
        );
    }

    // @dev avoids stack too deep error
    function updateStateMakeOrder(address _targetExchange, IZeroExV2.Order memory _order)
        internal
    {
        address wrapperRegistry = Registry(getTrading().registry()).ethfinexWrapperRegistry();
        address wrappedMakerAsset = getAssetAddress(_order.makerAssetData);
        address makerAsset = IWrapperRegistryEFX(
            wrapperRegistry
        ).wrapper2TokenLookup(wrappedMakerAsset);
        address takerAsset = getAssetAddress(_order.takerAssetData);
        IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(_targetExchange).getOrderInfo(_order);

        getAccounting().addAssetToOwnedAssets(takerAsset);
        getTrading().orderUpdateHook(
            _targetExchange,
            orderInfo.orderHash,
            Trading.UpdateType.make,
            [payable(makerAsset), payable(takerAsset)],
            [_order.makerAssetAmount, _order.takerAssetAmount, uint(0)]
        );
        getTrading().addOpenMakeOrder(
            _targetExchange,
            makerAsset,
            takerAsset,
            address(0),
            uint256(orderInfo.orderHash),
            _order.expirationTimeSeconds
        );
        getTrading().addZeroExV2OrderData(orderInfo.orderHash, _order);
    }

    // VIEW METHODS

    function constructOrderStruct(
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData
    )
        internal
        view
        returns (IZeroExV2.Order memory _order)
    {
        _order = IZeroExV2.Order({
            makerAddress: _orderAddresses[0],
            takerAddress: _orderAddresses[1],
            feeRecipientAddress: _orderAddresses[4],
            senderAddress: _orderAddresses[5],
            makerAssetAmount: _orderValues[0],
            takerAssetAmount: _orderValues[1],
            makerFee: _orderValues[2],
            takerFee: _orderValues[3],
            expirationTimeSeconds: _orderValues[4],
            salt: _orderValues[5],
            makerAssetData: _orderData[0],
            takerAssetData: _orderData[1]
        });
    }

    function getAssetProxy(address _targetExchange, bytes memory _assetData)
        internal
        view
        returns (address assetProxy_)
    {
        bytes4 assetProxyId;
        assembly {
            assetProxyId := and(mload(
                add(_assetData, 32)),
                0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
            )
        }
        assetProxy_ = IZeroExV2(_targetExchange).getAssetProxy(assetProxyId);
    }

    function getAssetAddress(bytes memory _assetData)
        internal
        view
        returns (address assetAddress_)
    {
        assembly {
            assetAddress_ := mload(add(_assetData, 36))
        }
    }

    /// @dev Function to be called from Trading spoke context (Delegate call)
    function getWrapperToken(address _token)
        internal
        view
        returns (address)
    {
        address wrapperRegistry = Registry(getTrading().registry()).ethfinexWrapperRegistry();
        return IWrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(_token);
    }

    /// @dev Function to be called by Trading spoke without change of context (Non delegate call)
    function getWrapperTokenFromAdapterContext(address _token)
        internal
        view
        returns (address)
    {
        address wrapperRegistry = Registry(Trading(msg.sender).registry()).ethfinexWrapperRegistry();
        return IWrapperRegistryEFX(wrapperRegistry).token2WrapperLookup(_token);
    }
}

File 17 of 77 : ExchangeAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/DSMath.sol";
import "../dependencies/token/IERC20.sol";
import "../fund/accounting/Accounting.sol";
import "../fund/hub/Hub.sol";
import "../fund/trading/Trading.sol";
import "../fund/vault/Vault.sol";

/// @title Exchange Adapter base contract
/// @author Melonport AG <[email protected]>
/// @notice Override the public methods to implement an adapter
contract ExchangeAdapter is DSMath {

    modifier onlyManager() {
        require(
            getManager() == msg.sender,
            "Manager must be sender"
        );
        _;
    }

    modifier notShutDown() {
        require(
            !hubShutDown(),
            "Hub must not be shut down"
        );
        _;
    }

    /// @dev Either manager sends, fund shut down, or order expired
    function ensureCancelPermitted(address _exchange, address _asset, bytes32 _id) internal {
        require(
            getManager() == msg.sender ||
            hubShutDown() ||
            getTrading().isOrderExpired(_exchange, _asset),
            "No cancellation condition met"
        );
        uint256 storedId;
        (storedId,,,,) = getTrading().exchangesToOpenMakeOrders(_exchange, _asset);
        require(
            uint256(_id) == storedId,
            "Passed identifier does not match that stored in Trading"
        );
    }

    function getTrading() internal view returns (Trading) {
        return Trading(payable(address(this)));
    }

    function getHub() internal view returns (Hub) {
        return Hub(getTrading().hub());
    }

    function getAccounting() internal view returns (Accounting) {
        return Accounting(getHub().accounting());
    }

    function hubShutDown() internal view returns (bool) {
        return getHub().isShutDown();
    }

    function getManager() internal view returns (address) {
        return getHub().manager();
    }

    function ensureNotInOpenMakeOrder(address _asset) internal view {
        require(
            !getTrading().isInOpenMakeOrder(_asset),
            "This asset is already in an open make order"
        );
    }

    function ensureCanMakeOrder(address _asset) internal view {
        require(
            block.timestamp >= getTrading().makerAssetCooldown(_asset),
            "Cooldown for the maker asset not reached"
        );
    }

    /// @notice Increment allowance of an asset for some target
    function approveAsset(
        address _asset,
        address _target,
        uint256 _amount,
        string memory _assetType
    )
        internal
    {
        Hub hub = getHub();
        Vault vault = Vault(hub.vault());

        require(
            IERC20(_asset).balanceOf(address(vault)) >= _amount,
            string(abi.encodePacked("Insufficient balance: ", _assetType))
        );

        vault.withdraw(_asset, _amount);
        uint256 allowance = IERC20(_asset).allowance(address(this), _target);
        require(
            IERC20(_asset).approve(_target, add(allowance, _amount)),
            string(abi.encodePacked("Approval failed: ", _assetType))
        );
    }

    /// @notice Reduce allowance of an asset for some target
    function revokeApproveAsset(
        address _asset,
        address _target,
        uint256 _amount,
        string memory _assetType
    )
        internal
    {
        uint256 allowance = IERC20(_asset).allowance(address(this), _target);
        uint256 newAllowance = (_amount > allowance) ? allowance : sub(allowance, _amount);
        require(
            IERC20(_asset).approve(_target, newAllowance),
            string(abi.encodePacked("Revoke approval failed: ", _assetType))
        );
    }

    /// @param orderAddresses [0] Order maker
    /// @param orderAddresses [1] Order taker
    /// @param orderAddresses [2] Order maker asset
    /// @param orderAddresses [3] Order taker asset
    /// @param orderAddresses [4] feeRecipientAddress
    /// @param orderAddresses [5] senderAddress
    /// @param orderAddresses [6] maker fee asset
    /// @param orderAddresses [7] taker fee asset
    /// @param orderValues [0] makerAssetAmount
    /// @param orderValues [1] takerAssetAmount
    /// @param orderValues [2] Maker fee
    /// @param orderValues [3] Taker fee
    /// @param orderValues [4] expirationTimeSeconds
    /// @param orderValues [5] Salt/nonce
    /// @param orderValues [6] Fill amount: amount of taker token to be traded
    /// @param orderValues [7] Dexy signature mode
    /// @param orderData [0] Encoded data specific to maker asset
    /// @param orderData [1] Encoded data specific to taker asset
    /// @param orderData [2] Encoded data specific to maker asset fee
    /// @param orderData [3] Encoded data specific to taker asset fee
    /// @param identifier Order identifier
    /// @param signature Signature of order maker

    // Responsibilities of makeOrder are:
    // - check sender
    // - check fund not shut down
    // - check price recent
    // - check risk management passes
    // - approve funds to be traded (if necessary)
    // - make order on the exchange
    // - check order was made (if possible)
    // - place asset in ownedAssets if not already tracked
    function makeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public virtual { revert("Unimplemented"); }

    // Responsibilities of takeOrder are:
    // - check sender
    // - check fund not shut down
    // - check not buying own fund tokens
    // - check price exists for asset pair
    // - check price is recent
    // - check price passes risk management
    // - approve funds to be traded (if necessary)
    // - take order from the exchange
    // - check order was taken (if possible)
    // - place asset in ownedAssets if not already tracked
    function takeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public virtual { revert("Unimplemented"); }

    // responsibilities of cancelOrder are:
    // - check sender is owner, or that order expired, or that fund shut down
    // - remove order from tracking array
    // - cancel order on exchange
    function cancelOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public virtual { revert("Unimplemented"); }

    // PUBLIC METHODS
    // PUBLIC VIEW METHODS
    /*
    @return {
        "makerAsset": "Maker asset",
        "takerAsset": "Taker asset",
        "makerQuantity": "Amount of maker asset"
        "takerQuantity": "Amount of taker asset"
    }
    */
    function getOrder(
        address onExchange,
        uint id,
        address makerAsset
    ) public view virtual returns (
        address,
        address,
        uint,
        uint
    ) { revert("Unimplemented"); }
}

File 18 of 77 : IEthfinex.sol
pragma solidity 0.6.1;
// pragma experimental ABIEncoderV2;

/// @dev Minimal interface for our interactions with EthFinex WrapperLock
interface IWrapperLock {
    function balanceOf(address) external view returns (uint256);
    function withdraw(uint256, uint8, bytes32, bytes32, uint256) external returns (bool);
    function deposit(uint256, uint256) external returns (bool);
}

/// @dev Minimal interface for our interactions with EthFinex WrapperLockEth
interface IWrapperLockEth {
    function balanceOf(address) external view returns (uint256);
    function deposit(uint256, uint256) external payable returns (bool);
}

/// @dev Minimal interface for our interactions with EthFinex WrapperRegistryEFX
interface IWrapperRegistryEFX {
    function token2WrapperLookup(address) external view returns (address);
    function wrapper2TokenLookup(address) external view returns (address);
}

File 19 of 77 : IKyberNetworkProxy.sol
pragma solidity 0.6.1;


/// @title Kyber Network interface
interface IKyberNetworkProxy {
    function maxGasPrice() external view returns(uint256);
    function getUserCapInWei(address) external view returns(uint256);
    function getUserCapInTokenWei(address, address) external view returns(uint256);
    function enabled() external view returns(bool);
    function info(bytes32) external view returns(uint256);
    function swapEtherToToken(address, uint256) external payable returns(uint256);
    function swapTokenToEther(address, uint256, uint256) external returns(uint256);
    function swapTokenToToken(address, uint256, address, uint256) external returns(uint);
    function getExpectedRate(address, address, uint256) external view returns (uint256, uint256);
    function tradeWithHint(
        address, uint256, address, address, uint256, uint256, address, bytes calldata
    ) external payable returns(uint256);
}

File 20 of 77 : IOasisDex.sol
pragma solidity 0.6.1;

/// @dev Minimal interface for our interactions with OasisDex MatchingMarket
interface IOasisDex {
    function getFirstUnsortedOffer() external view returns(uint256);
    function getNextUnsortedOffer(uint256) external view returns(uint256);
    function getBestOffer(address, address) external view returns(uint256);
    function getOffer(uint256) external view returns (uint256, address, uint256, address);
    function getWorseOffer(uint256) external view returns(uint256);
    function isActive(uint256) external view returns (bool);
    function buy(uint256, uint256) external returns (bool);
    function cancel(uint256) external returns (bool);
    function offer(uint256, address, uint256, address) external returns (uint256);
}

File 21 of 77 : IUniswapExchange.sol
pragma solidity 0.6.1;

/// @dev Minimal interface for our interactions with UniswapExchange
interface IUniswapExchange {
    // Trade ETH to ERC20
    function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient)
        external
        payable
        returns (uint256 tokens_bought);
    // Trade ERC20 to ETH
    function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline)
        external
        returns (uint256 eth_bought);
    // Trade ERC20 to ERC20
    function tokenToTokenTransferInput(
        uint256 tokens_sold,
        uint256 min_tokens_bought,
        uint256 min_eth_bought,
        uint256 deadline,
        address recipient,
        address token_addr
    )
        external
        returns (uint256 tokens_bought);

    /// @dev The following functions are only used in tests
    // Provide Liquidity
    function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline)
        external
        payable
        returns (uint256);
    // Get Prices
    function getEthToTokenInputPrice(uint256 eth_sold)
        external
        view
        returns (uint256 tokens_bought);
    function getTokenToEthInputPrice(uint256 tokens_sold)
        external
        view
        returns (uint256 eth_bought);
}

File 22 of 77 : IUniswapFactory.sol
pragma solidity 0.6.1;

/// @dev Minimal interface for our interactions with UniswapFactory
interface IUniswapFactory {
    function getExchange(address token) external view returns (address exchange);
}

File 23 of 77 : IZeroExV2.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

/// @dev Minimal interface for our interactions with the ZeroEx Exchange contract
interface IZeroExV2 {
    struct Order {
        address makerAddress;
        address takerAddress;
        address feeRecipientAddress;
        address senderAddress;
        uint256 makerAssetAmount;
        uint256 takerAssetAmount;
        uint256 makerFee;
        uint256 takerFee;
        uint256 expirationTimeSeconds;
        uint256 salt;
        bytes makerAssetData;
        bytes takerAssetData;
    }

    struct OrderInfo {
        uint8 orderStatus;
        bytes32 orderHash;
        uint256 orderTakerAssetFilledAmount;
    }

    struct FillResults {
        uint256 makerAssetFilledAmount;
        uint256 takerAssetFilledAmount;
        uint256 makerFeePaid;
        uint256 takerFeePaid;
    }

    function ZRX_ASSET_DATA() external view returns (bytes memory);
    function filled(bytes32) external view returns (uint256);
    function cancelled(bytes32) external view returns (bool);
    function getOrderInfo(Order calldata) external view returns (OrderInfo memory);
    function getAssetProxy(bytes4) external view returns (address);
    function isValidSignature(bytes32, address, bytes calldata) external view returns (bool);
    function preSign(bytes32, address, bytes calldata) external;
    function cancelOrder(Order calldata) external;
    function fillOrder(Order calldata, uint256, bytes calldata) external returns (FillResults memory);
}

File 24 of 77 : IZeroExV3.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

/// @dev Minimal interface for our interactions with the ZeroEx Exchange contract
interface IZeroExV3 {
    struct Order {
        address makerAddress;
        address takerAddress;
        address feeRecipientAddress;
        address senderAddress;
        uint256 makerAssetAmount;
        uint256 takerAssetAmount;
        uint256 makerFee;
        uint256 takerFee;
        uint256 expirationTimeSeconds;
        uint256 salt;
        bytes makerAssetData;
        bytes takerAssetData;
        bytes makerFeeAssetData;
        bytes takerFeeAssetData;
    }

    struct OrderInfo {
        uint8 orderStatus;
        bytes32 orderHash;
        uint256 orderTakerAssetFilledAmount;
    }

    struct FillResults {
        uint256 makerAssetFilledAmount;
        uint256 takerAssetFilledAmount;
        uint256 makerFeePaid;
        uint256 takerFeePaid;
        uint256 protocolFeePaid;
    }

    function cancelled(bytes32) external view returns (bool);
    function cancelOrder(Order calldata) external;
    function filled(bytes32) external view returns (uint256);
    function fillOrder(Order calldata, uint256, bytes calldata) external payable returns (FillResults memory);
    function getAssetProxy(bytes4) external view returns (address);
    function getOrderInfo(Order calldata) external view returns (OrderInfo memory);
    function isValidOrderSignature(Order calldata, bytes calldata) external view returns (bool);
    function preSign(bytes32) external;
    function protocolFeeCollector() external view returns (address);
    function protocolFeeMultiplier() external view returns (uint256);
}

File 25 of 77 : KyberAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/WETH.sol";
import "../dependencies/token/IERC20.sol";
import "../fund/trading/Trading.sol";
import "../fund/hub/Hub.sol";
import "../fund/vault/Vault.sol";
import "../fund/accounting/Accounting.sol";
import "../prices/IPriceSource.sol";
import "./interfaces/IKyberNetworkProxy.sol";
import "./ExchangeAdapter.sol";

contract KyberAdapter is DSMath, ExchangeAdapter {
    address public constant ETH_TOKEN_ADDRESS = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);

    // NON-CONSTANT METHODS

    // Responsibilities of takeOrder (Kybers swapToken) are:
    // - check price recent
    // - check risk management passes
    // - approve funds to be traded (if necessary)
    // - perform swap order on the exchange
    // - place asset in ownedAssets if not already tracked
    /// @notice Swaps srcAmount of srcToken for destAmount of destToken
    /// @dev Variable naming to be close to Kyber's naming
    /// @dev For the purpose of PriceTolerance, fillTakerQuantity == takerAssetQuantity = Dest token amount
    /// @param targetExchange Address of the exchange
    /// @param orderAddresses [2] Maker asset (Dest token)
    /// @param orderAddresses [3] Taker asset (Src token)
    /// @param orderValues [0] Maker asset quantity (Dest token amount)
    /// @param orderValues [1] Taker asset quantity (Src token amount)
    function takeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override onlyManager notShutDown {
        Hub hub = getHub();

        require(
            orderValues[1] == orderValues[6],
            "fillTakerQuantity must equal takerAssetQuantity"
        );

        address makerAsset = orderAddresses[2];
        address takerAsset = orderAddresses[3];
        uint makerAssetAmount = orderValues[0];
        uint takerAssetAmount = orderValues[1];

        uint minRate = calcMinRate(
            takerAsset,
            makerAsset,
            takerAssetAmount,
            makerAssetAmount
        );

        uint actualReceiveAmount = dispatchSwap(
            targetExchange, takerAsset, takerAssetAmount, makerAsset, minRate
        );
        require(
            actualReceiveAmount >= makerAssetAmount,
            "Received less than expected from Kyber swap"
        );

        getAccounting().addAssetToOwnedAssets(makerAsset);
        getAccounting().updateOwnedAssets();
        uint256 timesMakerAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerAsset);
        if (
            !getTrading().isInOpenMakeOrder(makerAsset) &&
            timesMakerAssetUsedAsFee == 0
        ) {
            getTrading().returnAssetToVault(makerAsset);
        }
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(0),
            Trading.UpdateType.take,
            [payable(makerAsset), payable(takerAsset)],
            [actualReceiveAmount, takerAssetAmount, takerAssetAmount]
        );
    }

    // INTERNAL FUNCTIONS

    /// @notice Call different functions based on type of assets supplied
    function dispatchSwap(
        address targetExchange,
        address srcToken,
        uint srcAmount,
        address destToken,
        uint minRate
    )
        internal
        returns (uint actualReceiveAmount)
    {

        Hub hub = getHub();
        address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET();

        if (srcToken == nativeAsset) {
            actualReceiveAmount = swapNativeAssetToToken(targetExchange, nativeAsset, srcAmount, destToken, minRate);
        }
        else if (destToken == nativeAsset) {
            actualReceiveAmount = swapTokenToNativeAsset(targetExchange, srcToken, srcAmount, nativeAsset, minRate);
        }
        else {
            actualReceiveAmount = swapTokenToToken(targetExchange, srcToken, srcAmount, destToken, minRate);
        }
    }

    /// @dev If minRate is not defined, uses expected rate from the network
    /// @param targetExchange Address of Kyber proxy contract
    /// @param nativeAsset Native asset address as src token
    /// @param srcAmount Amount of native asset supplied
    /// @param destToken Address of dest token
    /// @param minRate Minimum rate supplied to the Kyber proxy
    /// @return receivedAmount Actual amount of destToken received from the exchange
    function swapNativeAssetToToken(
        address targetExchange,
        address nativeAsset,
        uint srcAmount,
        address destToken,
        uint minRate
    )
        internal
        returns (uint receivedAmount)
    {
        // Convert WETH to ETH
        Hub hub = getHub();
        Vault vault = Vault(hub.vault());
        vault.withdraw(nativeAsset, srcAmount);
        WETH(payable(nativeAsset)).withdraw(srcAmount);
        receivedAmount = IKyberNetworkProxy(targetExchange).swapEtherToToken.value(srcAmount)(destToken, minRate);
    }

    /// @dev If minRate is not defined, uses expected rate from the network
    /// @param targetExchange Address of Kyber proxy contract
    /// @param srcToken Address of src token
    /// @param srcAmount Amount of src token supplied
    /// @param nativeAsset Native asset address as src token
    /// @param minRate Minimum rate supplied to the Kyber proxy
    /// @return receivedAmount Actual amount of destToken received from the exchange
    function swapTokenToNativeAsset(
        address targetExchange,
        address srcToken,
        uint srcAmount,
        address nativeAsset,
        uint minRate
    )
        internal
        returns (uint receivedAmount)
    {
        approveAsset(srcToken, targetExchange, srcAmount, "takerAsset");
        receivedAmount = IKyberNetworkProxy(targetExchange).swapTokenToEther(srcToken, srcAmount, minRate);

        // Convert ETH to WETH
        WETH(payable(nativeAsset)).deposit.value(receivedAmount)();
    }

    /// @dev If minRate is not defined, uses expected rate from the network
    /// @param targetExchange Address of Kyber proxy contract
    /// @param srcToken Address of src token
    /// @param srcAmount Amount of src token supplied
    /// @param destToken Address of dest token
    /// @param minRate Minimum rate supplied to the Kyber proxy
    /// @return receivedAmount Actual amount of destToken received from the exchange
    function swapTokenToToken(
        address targetExchange,
        address srcToken,
        uint srcAmount,
        address destToken,
        uint minRate
    )
        internal
        returns (uint receivedAmount)
    {
        approveAsset(srcToken, targetExchange, srcAmount, "takerAsset");

        receivedAmount = IKyberNetworkProxy(targetExchange).swapTokenToToken(srcToken, srcAmount, destToken, minRate);
    }

    /// @param srcToken Address of src token
    /// @param destToken Address of dest token
    /// @param srcAmount Amount of src token
    /// @return minRate Minimum rate to be supplied to the network for some order params
    function calcMinRate(
        address srcToken,
        address destToken,
        uint srcAmount,
        uint destAmount
    )
        internal
        view
        returns (uint minRate)
    {
        IPriceSource pricefeed = IPriceSource(getHub().priceSource());
        minRate = pricefeed.getOrderPriceInfo(
            srcToken,
            srcAmount,
            destAmount
        );
    }
}

File 26 of 77 : OasisDexAccessor.sol
pragma solidity 0.6.1;

import "./interfaces/IOasisDex.sol";

contract OasisDexAccessor {
    function getUnsortedOfferIds(
        address targetExchange,
        address sellAsset,
        address buyAsset
    )
    public
    view
    returns (uint[] memory)
    {
        IOasisDex market = IOasisDex(targetExchange);
        uint[] memory ids = new uint[](1000);
        uint count = 0;

        // Iterate over all unsorted offers up to 1000 iterations.
        uint id = market.getFirstUnsortedOffer();
        for (uint i = 0; i < 1000; i++) {
            if (id == 0) {
                break;
            }

            if (market.isActive(id)) {
                address sellGem;
                address buyGem;
                (, sellGem, , buyGem) = market.getOffer(id);

                if (sellGem == sellAsset && buyGem == buyAsset) {
                    ids[count++] = id;
                }
            }

            // Get the next offer and repeat.
            id = market.getNextUnsortedOffer(id);
        }

        // Create a new array of offers with the correct size.
        uint[] memory copy = new uint[](count);
        for (uint i = 0; i < count; i++) {
            copy[i] = ids[i];
        }

        return copy;
    }

    function getSortedOfferIds(
        address targetExchange,
        address sellAsset,
        address buyAsset
    )
    public
    view
    returns(uint[] memory)
    {
        IOasisDex market = IOasisDex(targetExchange);
        uint[] memory ids = new uint[](1000);
        uint count = 0;

        // Iterate over all sorted offers.
        uint id = market.getBestOffer(sellAsset, buyAsset);
        for (uint i = 0; i < 1000 ; i++ ) {
            if (id == 0) {
                break;
            }

            if (market.isActive(id)) {
                ids[count++] = id;
            }

            // Get the next offer and repeat.
            id = market.getWorseOffer(id);
        }

        // Create a new array of offers with the correct size.
        uint[] memory copy = new uint[](count);
        for (uint i = 0; i < count; i++) {
            copy[i] = ids[i];
        }

        return copy;
    }

    function getOrders(
        address targetExchange,
        address sellAsset,
        address buyAsset
    )
    public
    view
    returns (uint[] memory, uint[] memory, uint[] memory) {
        IOasisDex market = IOasisDex(targetExchange);
        uint[] memory sIds = getSortedOfferIds(targetExchange, sellAsset, buyAsset);
        uint[] memory uIds = getUnsortedOfferIds(targetExchange, sellAsset, buyAsset);
        uint[] memory ids = new uint[](uIds.length + sIds.length);
        uint[] memory sellQtys = new uint[](ids.length);
        uint[] memory buyQtys = new uint[](ids.length);

        for (uint i = 0; i < sIds.length; i++) {
            ids[i] = sIds[i];
        }

        for (uint i = 0; i < uIds.length; i++) {
            ids[i + sIds.length] = uIds[i];
        }

        for (uint i = 0; i < ids.length; i++) {
            uint sellQty;
            uint buyQty;
            (sellQty, , buyQty,) = market.getOffer(ids[i]);
            sellQtys[i] = sellQty;
            buyQtys[i] = buyQty;
        }

        return (ids, sellQtys, buyQtys);
    }
}

File 27 of 77 : OasisDexAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../fund/hub/Hub.sol";
import "../fund/trading/Trading.sol";
import "../fund/vault/Vault.sol";
import "../fund/accounting/Accounting.sol";
import "../dependencies/DSMath.sol";
import "./interfaces/IOasisDex.sol";
import "./ExchangeAdapter.sol";

/// @title OasisDexAdapter Contract
/// @author Melonport AG <[email protected]>
/// @notice Adapter between Melon and OasisDex Matching Market
contract OasisDexAdapter is DSMath, ExchangeAdapter {

    event OrderCreated(uint256 id);

    //  METHODS

    //  PUBLIC METHODS

    // Responsibilities of makeOrder are:
    // - check sender
    // - check fund not shut down
    // - check price recent
    // - check risk management passes
    // - approve funds to be traded (if necessary)
    // - make order on the exchange
    // - check order was made (if possible)
    // - place asset in ownedAssets if not already tracked
    /// @notice Makes an order on the selected exchange
    /// @dev These orders are not expected to settle immediately
    /// @param targetExchange Address of the exchange
    /// @param orderAddresses [2] Order maker asset
    /// @param orderAddresses [3] Order taker asset
    /// @param orderValues [0] Maker token quantity
    /// @param orderValues [1] Taker token quantity
    function makeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override onlyManager notShutDown {
        ensureCanMakeOrder(orderAddresses[2]);
        address makerAsset = orderAddresses[2];
        address takerAsset = orderAddresses[3];
        uint256 makerQuantity = orderValues[0];
        uint256 takerQuantity = orderValues[1];

        // Order parameter checks
        getTrading().updateAndGetQuantityBeingTraded(makerAsset);
        ensureNotInOpenMakeOrder(makerAsset);

        approveAsset(makerAsset, targetExchange, makerQuantity, "makerAsset");

        uint256 orderId = IOasisDex(targetExchange).offer(makerQuantity, makerAsset, takerQuantity, takerAsset);

        // defines success in MatchingMarket
        require(orderId != 0, "Order ID should not be zero");

        getAccounting().addAssetToOwnedAssets(takerAsset);
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(orderId),
            Trading.UpdateType.make,
            [payable(makerAsset), payable(takerAsset)],
            [makerQuantity, takerQuantity, uint256(0)]
        );
        getTrading().addOpenMakeOrder(
            targetExchange,
            makerAsset,
            takerAsset,
            address(0),
            orderId,
            orderValues[4]
        );
        emit OrderCreated(orderId);
    }

    // Responsibilities of takeOrder are:
    // - check sender
    // - check fund not shut down
    // - check not buying own fund tokens
    // - check price exists for asset pair
    // - check price is recent
    // - check price passes risk management
    // - approve funds to be traded (if necessary)
    // - take order from the exchange
    // - check order was taken (if possible)
    // - place asset in ownedAssets if not already tracked
    /// @notice Takes an active order on the selected exchange
    /// @dev These orders are expected to settle immediately
    /// @param targetExchange Address of the exchange
    /// @param orderValues [6] Fill amount : amount of taker token to fill
    /// @param identifier Active order id
    function takeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override onlyManager notShutDown {
        Hub hub = getHub();
        uint256 fillTakerQuantity = orderValues[6];
        uint256 maxMakerQuantity;
        address makerAsset;
        uint256 maxTakerQuantity;
        address takerAsset;
        (
            maxMakerQuantity,
            makerAsset,
            maxTakerQuantity,
            takerAsset
        ) = IOasisDex(targetExchange).getOffer(uint256(identifier));
        uint256 fillMakerQuantity = mul(fillTakerQuantity, maxMakerQuantity) / maxTakerQuantity;

        require(
            makerAsset == orderAddresses[2] && takerAsset == orderAddresses[3],
            "Maker and taker assets do not match the order addresses"
        );
        require(
            makerAsset != takerAsset,
            "Maker and taker assets cannot be the same"
        );
        require(fillMakerQuantity <= maxMakerQuantity, "Maker amount to fill above max");
        require(fillTakerQuantity <= maxTakerQuantity, "Taker amount to fill above max");

        approveAsset(takerAsset, targetExchange, fillTakerQuantity, "takerAsset");

        require(
            IOasisDex(targetExchange).buy(uint256(identifier), fillMakerQuantity),
            "Buy on matching market failed"
        );

        getAccounting().addAssetToOwnedAssets(makerAsset);
        getAccounting().updateOwnedAssets();
        uint256 timesMakerAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerAsset);
        if (
            !getTrading().isInOpenMakeOrder(makerAsset) &&
            timesMakerAssetUsedAsFee == 0
        ) {
            getTrading().returnAssetToVault(makerAsset);
        }
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(identifier),
            Trading.UpdateType.take,
            [payable(makerAsset), payable(takerAsset)],
            [maxMakerQuantity, maxTakerQuantity, fillTakerQuantity]
        );
    }

    // responsibilities of cancelOrder are:
    // - check sender is owner, or that order expired, or that fund shut down
    // - remove order from tracking array
    // - cancel order on exchange
    /// @notice Cancels orders that were not expected to settle immediately
    /// @param targetExchange Address of the exchange
    /// @param orderAddresses [2] Order maker asset
    /// @param identifier Order ID on the exchange
    function cancelOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override {
        require(uint256(identifier) != 0, "ID cannot be zero");
        address makerAsset;
        (, makerAsset, ,) = IOasisDex(targetExchange).getOffer(uint256(identifier));
        ensureCancelPermitted(targetExchange, makerAsset, identifier);

        require(
            address(makerAsset) == orderAddresses[2],
            "Retrieved and passed assets do not match"
        );

        getTrading().removeOpenMakeOrder(targetExchange, makerAsset);
        IOasisDex(targetExchange).cancel(uint256(identifier));
        uint256 timesMakerAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerAsset);
        if (timesMakerAssetUsedAsFee == 0) {
            getTrading().returnAssetToVault(makerAsset);
        }
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(identifier),
            Trading.UpdateType.cancel,
            [address(0), address(0)],
            [uint256(0), uint256(0), uint256(0)]
        );
    }

    // VIEW METHODS

    function getOrder(address targetExchange, uint256 id, address makerAsset)
        public
        view
        override
        returns (address, address, uint256, uint256)
    {
        uint256 sellQuantity;
        address sellAsset;
        uint256 buyQuantity;
        address buyAsset;
        (
            sellQuantity,
            sellAsset,
            buyQuantity,
            buyAsset
        ) = IOasisDex(targetExchange).getOffer(id);
        return (
            sellAsset,
            buyAsset,
            sellQuantity,
            buyQuantity
        );
    }
}

File 28 of 77 : UniswapAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/token/IERC20.sol";
import "../dependencies/WETH.sol";
import "../fund/accounting/Accounting.sol";
import "../fund/hub/Hub.sol";
import "../fund/trading/Trading.sol";
import "../fund/vault/Vault.sol";
import "./interfaces/IUniswapFactory.sol";
import "./interfaces/IUniswapExchange.sol";
import "./ExchangeAdapter.sol";

contract UniswapAdapter is DSMath, ExchangeAdapter {
    /// @notice Take order that uses a user-defined src token amount to trade for a dest token amount
    /// @dev For the purpose of PriceTolerance, _orderValues [1] == _orderValues [6] = Dest token amount
    /// @param _targetExchange Address of Uniswap factory contract
    /// @param _orderAddresses [2] Maker asset (Dest token)
    /// @param _orderAddresses [3] Taker asset (Src token)
    /// @param _orderValues [0] Maker asset quantity (Dest token amount)
    /// @param _orderValues [1] Taker asset quantity (Src token amount)
    /// @param _orderValues [6] Taker asset fill amount
    function takeOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        onlyManager
        notShutDown
    {
        Hub hub = getHub();

        require(
            _orderValues[1] == _orderValues[6],
            "Taker asset amount must equal taker asset fill amount"
        );

        address makerAsset = _orderAddresses[2];
        address takerAsset = _orderAddresses[3];
        uint makerAssetAmount = _orderValues[0];
        uint takerAssetAmount = _orderValues[1];

        uint actualReceiveAmount = dispatchSwap(
            _targetExchange, takerAsset, takerAssetAmount, makerAsset, makerAssetAmount
        );
        require(
            actualReceiveAmount >= makerAssetAmount,
            "Received less than expected from Uniswap exchange"
        );

        updateStateTakeOrder(
            _targetExchange,
            makerAsset,
            takerAsset,
            takerAssetAmount,
            actualReceiveAmount
        );
    }

    // INTERNAL FUNCTIONS

    /// @notice Call different functions based on type of assets supplied
    /// @param _targetExchange Address of Uniswap factory contract
    /// @param _srcToken Address of src token
    /// @param _srcAmount Amount of src token supplied
    /// @param _destToken Address of dest token
    /// @param _minDestAmount Minimum amount of dest token to receive
    /// @return actualReceiveAmount_ Actual amount of _destToken received
    function dispatchSwap(
        address _targetExchange,
        address _srcToken,
        uint _srcAmount,
        address _destToken,
        uint _minDestAmount
    )
        internal
        returns (uint actualReceiveAmount_)
    {
        require(
            _srcToken != _destToken,
            "Src token cannot be the same as dest token"
        );

        Hub hub = getHub();
        address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET();

        if (_srcToken == nativeAsset) {
            actualReceiveAmount_ = swapNativeAssetToToken(
                _targetExchange,
                nativeAsset,
                _srcAmount,
                _destToken,
                _minDestAmount
            );
        } else if (_destToken == nativeAsset) {
            actualReceiveAmount_ = swapTokenToNativeAsset(
                _targetExchange,
                _srcToken,
                _srcAmount,
                nativeAsset,
                _minDestAmount
            );
        } else {
            actualReceiveAmount_ = swapTokenToToken(
                _targetExchange,
                _srcToken,
                _srcAmount,
                _destToken,
                _minDestAmount
            );
        }
    }

    /// @param _targetExchange Address of Uniswap factory contract
    /// @param _nativeAsset Native asset address as src token
    /// @param _srcAmount Amount of native asset supplied
    /// @param _destToken Address of dest token
    /// @param _minDestAmount Minimum amount of dest token to get back
    /// @return actualReceiveAmount_ Actual amount of _destToken received
    function swapNativeAssetToToken(
        address _targetExchange,
        address _nativeAsset,
        uint _srcAmount,
        address _destToken,
        uint _minDestAmount
    )
        internal
        returns (uint actualReceiveAmount_)
    {
        // Convert WETH to ETH
        Hub hub = getHub();
        Vault vault = Vault(hub.vault());
        vault.withdraw(_nativeAsset, _srcAmount);
        WETH(payable(_nativeAsset)).withdraw(_srcAmount);

        address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_destToken);
        actualReceiveAmount_ = IUniswapExchange(tokenExchange).ethToTokenTransferInput.value(
            _srcAmount
        )
        (
            _minDestAmount,
            add(block.timestamp, 1),
            address(vault)
        );
    }

    /// @param _targetExchange Address of Uniswap factory contract
    /// @param _srcToken Address of src token
    /// @param _srcAmount Amount of src token supplied
    /// @param _nativeAsset Native asset address as dest token
    /// @param _minDestAmount Minimum amount of dest token to get back
    /// @return actualReceiveAmount_ Actual amount of _destToken received
    function swapTokenToNativeAsset(
        address _targetExchange,
        address _srcToken,
        uint _srcAmount,
        address _nativeAsset,
        uint _minDestAmount
    )
        internal
        returns (uint actualReceiveAmount_)
    {
        Hub hub = getHub();
        Vault vault = Vault(hub.vault());
        vault.withdraw(_srcToken, _srcAmount);

        address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_srcToken);
        approveAsset(_srcToken, tokenExchange, _srcAmount, "takerAsset");
        actualReceiveAmount_ = IUniswapExchange(tokenExchange).tokenToEthSwapInput(
            _srcAmount,
            _minDestAmount,
            add(block.timestamp, 1)
        );

        // Convert ETH to WETH and move to Vault
        WETH(payable(_nativeAsset)).deposit.value(actualReceiveAmount_)();
        uint256 timesNativeAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(_nativeAsset);
        if (
            !getTrading().isInOpenMakeOrder(_nativeAsset) &&
            timesNativeAssetUsedAsFee == 0
        ) {
            getTrading().returnAssetToVault(_nativeAsset);
        }
    }

    /// @param _targetExchange Address of Uniswap factory contract
    /// @param _srcToken Address of src token
    /// @param _srcAmount Amount of src token supplied
    /// @param _destToken Address of dest token
    /// @param _minDestAmount Minimum amount of dest token to get back
    /// @return actualReceiveAmount_ Actual amount of _destToken received
    function swapTokenToToken(
        address _targetExchange,
        address _srcToken,
        uint _srcAmount,
        address _destToken,
        uint _minDestAmount
    )
        internal
        returns (uint actualReceiveAmount_)
    {
        Hub hub = getHub();
        Vault vault = Vault(hub.vault());
        vault.withdraw(_srcToken, _srcAmount);

        address tokenExchange = IUniswapFactory(_targetExchange).getExchange(_srcToken);
        approveAsset(_srcToken, tokenExchange, _srcAmount, "takerAsset");
        actualReceiveAmount_ = IUniswapExchange(tokenExchange).tokenToTokenTransferInput(
            _srcAmount,
            _minDestAmount,
            1,
            add(block.timestamp, 1),
            address(vault),
            _destToken
        );
    }

    function updateStateTakeOrder(
        address _targetExchange,
        address _makerAsset,
        address _takerAsset,
        uint256 _takerAssetAmount,
        uint256 _actualReceiveAmount
    )
        internal
    {
        getAccounting().addAssetToOwnedAssets(_makerAsset);
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            _targetExchange,
            bytes32(0),
            Trading.UpdateType.take,
            [payable(_makerAsset), payable(_takerAsset)],
            [_actualReceiveAmount, _takerAssetAmount, _takerAssetAmount]
        );
    }
}

File 29 of 77 : ZeroExV2Adapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/token/IERC20.sol";
import "../fund/trading/Trading.sol";
import "../fund/hub/Hub.sol";
import "../fund/vault/Vault.sol";
import "../fund/accounting/Accounting.sol";
import "../dependencies/DSMath.sol";
import "./interfaces/IZeroExV2.sol";
import "./ExchangeAdapter.sol";

/// @title ZeroExV2Adapter Contract
/// @author Melonport AG <[email protected]>
/// @notice Adapter to 0xV2 Exchange Contract
contract ZeroExV2Adapter is DSMath, ExchangeAdapter {
    /// @param orderAddresses [2] Order maker asset
    /// @param orderAddresses [3] Order taker asset
    /// @param orderData [0] Order maker asset data
    /// @param orderData [1] Order taker asset data
    modifier orderAddressesMatchOrderData(
        address[8] memory orderAddresses,
        bytes[4] memory orderData
    )
    {
        require(
            getAssetAddress(orderData[0]) == orderAddresses[2],
            "Maker asset data does not match order address in array"
        );
        require(
            getAssetAddress(orderData[1]) == orderAddresses[3],
            "Taker asset data does not match order address in array"
        );
        _;
    }

    //  METHODS

    //  PUBLIC METHODS

    /// @notice Make order by pre-approving signatures
    function makeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    )
        public
        override
        onlyManager
        notShutDown
        orderAddressesMatchOrderData(orderAddresses, orderData)
    {
        ensureCanMakeOrder(orderAddresses[2]);

        IZeroExV2.Order memory order = constructOrderStruct(orderAddresses, orderValues, orderData);
        address makerAsset = getAssetAddress(orderData[0]);
        address takerAsset = getAssetAddress(orderData[1]);

        // Order parameter checks
        getTrading().updateAndGetQuantityBeingTraded(makerAsset);
        ensureNotInOpenMakeOrder(makerAsset);

        approveAssetsMakeOrder(targetExchange, order);

        IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(targetExchange).getOrderInfo(order);
        IZeroExV2(targetExchange).preSign(orderInfo.orderHash, address(this), signature);

        require(
            IZeroExV2(targetExchange).isValidSignature(
                orderInfo.orderHash,
                address(this),
                signature
            ),
            "INVALID_ORDER_SIGNATURE"
        );

        updateStateMakeOrder(targetExchange, order);
    }

    // Responsibilities of takeOrder are:
    // - check sender
    // - check fund not shut down
    // - check not buying own fund tokens
    // - check price exists for asset pair
    // - check price is recent
    // - check price passes risk management
    // - approve funds to be traded (if necessary)
    // - take order from the exchange
    // - check order was taken (if possible)
    // - place asset in ownedAssets if not already tracked
    /// @notice Takes an active order on the selected exchange
    /// @dev These orders are expected to settle immediately
    /// @param targetExchange Address of the exchange
    /// @param orderAddresses [0] Order maker
    /// @param orderAddresses [1] Order taker
    /// @param orderAddresses [2] Order maker asset
    /// @param orderAddresses [3] Order taker asset
    /// @param orderAddresses [4] feeRecipientAddress
    /// @param orderAddresses [5] senderAddress
    /// @param orderValues [0] makerAssetAmount
    /// @param orderValues [1] takerAssetAmount
    /// @param orderValues [2] Maker fee
    /// @param orderValues [3] Taker fee
    /// @param orderValues [4] expirationTimeSeconds
    /// @param orderValues [5] Salt/nonce
    /// @param orderValues [6] Fill amount: amount of taker token to be traded
    /// @param orderValues [7] Dexy signature mode
    /// @param orderData [0] Encoded data specific to maker asset
    /// @param orderData [1] Encoded data specific to taker asset
    /// @param orderData [2] Encoded data specific to maker asset fee
    /// @param orderData [3] Encoded data specific to taker asset fee
    /// @param identifier Order identifier
    /// @param signature Signature of the order.
    function takeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    )
        public
        override
        onlyManager
        notShutDown
        orderAddressesMatchOrderData(orderAddresses, orderData)
    {
        IZeroExV2.Order memory order = constructOrderStruct(orderAddresses, orderValues, orderData);

        uint fillTakerQuantity = orderValues[6];

        approveAssetsTakeOrder(targetExchange, order, fillTakerQuantity);

        uint takerAssetFilledAmount = executeFill(targetExchange, order, fillTakerQuantity, signature);
        require(
            takerAssetFilledAmount == fillTakerQuantity,
            "Filled amount does not match desired fill amount"
        );

        updateStateTakeOrder(targetExchange, order, fillTakerQuantity);
    }

    /// @notice Cancel the 0x make order
    function cancelOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    )
        public
        override
        orderAddressesMatchOrderData(orderAddresses, orderData)
    {
        IZeroExV2.Order memory order = getTrading().getZeroExV2OrderDetails(identifier);
        ensureCancelPermitted(targetExchange, orderAddresses[2], identifier);

        if (order.expirationTimeSeconds > block.timestamp) {
            IZeroExV2(targetExchange).cancelOrder(order);
        }

        revokeApproveAssetsCancelOrder(targetExchange, order);

        updateStateCancelOrder(targetExchange, order);
    }

    /// @dev Get order details
    function getOrder(address targetExchange, uint256 id, address makerAsset)
        public
        view
        override
        returns (address, address, uint256, uint256)
    {
        uint orderId;
        uint orderIndex;
        address takerAsset;
        uint makerQuantity;
        uint takerQuantity;
        (orderId, , orderIndex) = Trading(msg.sender).getOpenOrderInfo(targetExchange, makerAsset);
        (, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex);
        uint takerAssetFilledAmount = IZeroExV2(targetExchange).filled(bytes32(orderId));
        uint makerAssetFilledAmount = mul(takerAssetFilledAmount, makerQuantity) / takerQuantity;
        if (IZeroExV2(targetExchange).cancelled(bytes32(orderId)) || sub(takerQuantity, takerAssetFilledAmount) == 0) {
            return (makerAsset, takerAsset, 0, 0);
        }
        return (
            makerAsset,
            takerAsset,
            sub(makerQuantity, makerAssetFilledAmount),
            sub(takerQuantity, takerAssetFilledAmount)
        );
    }

    // INTERNAL METHODS

    /// @notice Approves makerAsset, makerFee
    function approveAssetsMakeOrder(address _targetExchange, IZeroExV2.Order memory _order)
        internal
    {
        approveAsset(
            getAssetAddress(_order.makerAssetData),
            getAssetProxy(_targetExchange, _order.makerAssetData),
            _order.makerAssetAmount,
            "makerAsset"
        );
        if (_order.makerFee > 0) {
            bytes memory zrxAssetData = IZeroExV2(_targetExchange).ZRX_ASSET_DATA();
            approveAsset(
                getAssetAddress(zrxAssetData),
                getAssetProxy(_targetExchange, zrxAssetData),
                _order.makerFee,
                "makerFeeAsset"
            );
        }
    }

    /// @notice Approves takerAsset, takerFee
    function approveAssetsTakeOrder(
        address _targetExchange,
        IZeroExV2.Order memory _order,
        uint256 _fillTakerAmount
    )
        internal
    {
        approveAsset(
            getAssetAddress(_order.takerAssetData),
            getAssetProxy(_targetExchange, _order.takerAssetData),
            _fillTakerAmount,
            "takerAsset"
        );
        uint256 takerFeeAmount = mul(_order.takerFee, _fillTakerAmount) / _order.takerAssetAmount;
        if (takerFeeAmount > 0) {
            bytes memory zrxAssetData = IZeroExV2(_targetExchange).ZRX_ASSET_DATA();
            approveAsset(
                getAssetAddress(zrxAssetData),
                getAssetProxy(_targetExchange, zrxAssetData),
                takerFeeAmount,
                "takerFeeAsset"
            );
        }
    }

    /// @dev Needed to avoid stack too deep error
    function executeFill(
        address targetExchange,
        IZeroExV2.Order memory order,
        uint256 takerAssetFillAmount,
        bytes memory signature
    )
        internal
        returns (uint256)
    {
        address makerAsset = getAssetAddress(order.makerAssetData);
        uint preMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this));

        IZeroExV2.FillResults memory fillResults = IZeroExV2(targetExchange).fillOrder(
            order,
            takerAssetFillAmount,
            signature
        );

        uint256 postMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this));

        // Account for case where makerAsset is ZRX (same as takerFee)
        uint256 makerAssetFeesTotal;
        if (makerAsset == getAssetAddress(IZeroExV2(targetExchange).ZRX_ASSET_DATA())) {
            makerAssetFeesTotal = add(makerAssetFeesTotal, order.takerFee);
        }

        require(
            postMakerAssetBalance == sub(
                add(preMakerAssetBalance, fillResults.makerAssetFilledAmount),
                makerAssetFeesTotal
            ),
            "Maker asset balance different than expected"
        );

        return fillResults.takerAssetFilledAmount;
    }

    /// @notice Revoke asset approvals and return assets to vault
    function revokeApproveAssetsCancelOrder(
        address _targetExchange,
        IZeroExV2.Order memory _order
    )
        internal
    {
        address makerAsset = getAssetAddress(_order.makerAssetData);
        bytes memory makerFeeAssetData = IZeroExV2(_targetExchange).ZRX_ASSET_DATA();
        address makerFeeAsset = getAssetAddress(makerFeeAssetData);
        bytes32 orderHash = IZeroExV2(_targetExchange).getOrderInfo(_order).orderHash;
        uint takerAssetFilledAmount = IZeroExV2(_targetExchange).filled(orderHash);
        uint makerAssetFilledAmount = mul(takerAssetFilledAmount, _order.makerAssetAmount) / _order.takerAssetAmount;
        uint256 makerAssetRemainingInOrder = sub(_order.makerAssetAmount, makerAssetFilledAmount);
        uint256 makerFeeRemainingInOrder = mul(_order.makerFee, makerAssetRemainingInOrder) / _order.makerAssetAmount;

        revokeApproveAsset(
            makerAsset,
            getAssetProxy(_targetExchange, _order.makerAssetData),
            makerAssetRemainingInOrder,
            "makerAsset"
        );
        uint256 timesMakerAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerAsset);
        // only return makerAsset early when it is not being used as a fee anywhere
        if (timesMakerAssetUsedAsFee == 0) {
            getTrading().returnAssetToVault(makerAsset);
        }

        if (_order.makerFee > 0) {
            revokeApproveAsset(
                makerFeeAsset,
                getAssetProxy(_targetExchange, makerFeeAssetData),
                makerFeeRemainingInOrder,
                "makerFeeAsset"
            );
            // only return feeAsset when not used in another makeOrder AND
            //  when it is only used as a fee in this order that we are cancelling
            uint256 timesFeeAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerFeeAsset);
            if (
                !getTrading().isInOpenMakeOrder(makerFeeAsset) &&
                timesFeeAssetUsedAsFee == 1
            ) {
                getTrading().returnAssetToVault(makerFeeAsset);
            }
        }
    }

    /// @dev Avoids stack too deep error
    function updateStateCancelOrder(address targetExchange, IZeroExV2.Order memory order)
        internal
    {
        address makerAsset = getAssetAddress(order.makerAssetData);

        getTrading().removeOpenMakeOrder(targetExchange, makerAsset);
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            targetExchange,
            IZeroExV2(targetExchange).getOrderInfo(order).orderHash,
            Trading.UpdateType.cancel,
            [address(0), address(0)],
            [uint(0), uint(0), uint(0)]
        );
    }

    /// @dev Avoids stack too deep error
    function updateStateMakeOrder(address targetExchange, IZeroExV2.Order memory order)
        internal
    {
        address makerAsset = getAssetAddress(order.makerAssetData);
        address takerAsset = getAssetAddress(order.takerAssetData);
        IZeroExV2.OrderInfo memory orderInfo = IZeroExV2(targetExchange).getOrderInfo(order);

        getAccounting().addAssetToOwnedAssets(takerAsset);
        getTrading().orderUpdateHook(
            targetExchange,
            orderInfo.orderHash,
            Trading.UpdateType.make,
            [payable(makerAsset), payable(takerAsset)],
            [order.makerAssetAmount, order.takerAssetAmount, uint(0)]
        );
        getTrading().addOpenMakeOrder(
            targetExchange,
            makerAsset,
            takerAsset,
            getAssetAddress(IZeroExV2(targetExchange).ZRX_ASSET_DATA()),
            uint256(orderInfo.orderHash),
            order.expirationTimeSeconds
        );
        getTrading().addZeroExV2OrderData(orderInfo.orderHash, order);
    }

    /// @dev avoids stack too deep error
    function updateStateTakeOrder(
        address targetExchange,
        IZeroExV2.Order memory order,
        uint256 fillTakerQuantity
    )
        internal
    {
        address makerAsset = getAssetAddress(order.makerAssetData);
        address takerAsset = getAssetAddress(order.takerAssetData);

        getAccounting().addAssetToOwnedAssets(makerAsset);
        getAccounting().updateOwnedAssets();
        if (
            !getTrading().isInOpenMakeOrder(makerAsset) &&
            getTrading().openMakeOrdersUsingAssetAsFee(makerAsset) == 0
        ) {
            getTrading().returnAssetToVault(makerAsset);
        }
        getTrading().orderUpdateHook(
            targetExchange,
            IZeroExV2(targetExchange).getOrderInfo(order).orderHash,
            Trading.UpdateType.take,
            [payable(makerAsset), payable(takerAsset)],
            [order.makerAssetAmount, order.takerAssetAmount, fillTakerQuantity]
        );
    }

    // VIEW METHODS

    function constructOrderStruct(
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData
    )
        internal
        view
        returns (IZeroExV2.Order memory order)
    {
        order = IZeroExV2.Order({
            makerAddress: orderAddresses[0],
            takerAddress: orderAddresses[1],
            feeRecipientAddress: orderAddresses[4],
            senderAddress: orderAddresses[5],
            makerAssetAmount: orderValues[0],
            takerAssetAmount: orderValues[1],
            makerFee: orderValues[2],
            takerFee: orderValues[3],
            expirationTimeSeconds: orderValues[4],
            salt: orderValues[5],
            makerAssetData: orderData[0],
            takerAssetData: orderData[1]
        });
    }

    function getAssetProxy(address targetExchange, bytes memory assetData)
        internal
        view
        returns (address assetProxy)
    {
        bytes4 assetProxyId;
        assembly {
            assetProxyId := and(mload(
                add(assetData, 32)),
                0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
            )
        }
        assetProxy = IZeroExV2(targetExchange).getAssetProxy(assetProxyId);
    }

    function getAssetAddress(bytes memory assetData)
        internal
        view
        returns (address assetAddress)
    {
        assembly {
            assetAddress := mload(add(assetData, 36))
        }
    }
}

File 30 of 77 : ZeroExV3Adapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../dependencies/token/IERC20.sol";
import "../fund/trading/Trading.sol";
import "../fund/hub/Hub.sol";
import "../fund/vault/Vault.sol";
import "../fund/accounting/Accounting.sol";
import "../dependencies/DSMath.sol";
import "./interfaces/IZeroExV3.sol";
import "./ExchangeAdapter.sol";

/// @title ZeroExV3Adapter Contract
/// @author Melonport AG <[email protected]>
/// @notice Adapter to 0xV3 Exchange Contract
contract ZeroExV3Adapter is DSMath, ExchangeAdapter {

    /// @param _orderAddresses [2] Order maker asset
    /// @param _orderAddresses [3] Order taker asset
    /// @param _orderAddresses [6] Order maker fee asset
    /// @param _orderAddresses [7] Order taker fee asset
    /// @param _orderValues [2] Order maker fee amount
    /// @param _orderValues [3] Order taker fee amount
    modifier orderAddressesMatchOrderData(
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData
    )
    {
        require(
            getAssetAddress(_orderData[0]) == _orderAddresses[2],
            "Maker asset data does not match order address in array"
        );
        require(
            getAssetAddress(_orderData[1]) == _orderAddresses[3],
            "Taker asset data does not match order address in array"
        );
        if (_orderValues[2] > 0) {
            require(
                getAssetAddress(_orderData[2]) == _orderAddresses[6],
                "Maker fee asset data does not match order address in array"
            );
        }
        if (_orderValues[3] > 0) {
            require(
                getAssetAddress(_orderData[3]) == _orderAddresses[7],
                "Taker fee asset data does not match order address in array"
            );
        }
        _;
    }

    //  METHODS

    //  PUBLIC METHODS

    /// @notice Make order by pre-approving signatures
    /// @param _targetExchange Address of the exchange
    /// @param _orderAddresses [2] Maker asset (Dest token)
    /// @param _orderAddresses [3] Taker asset (Src token)
    /// @param _orderData [0] Encoded data specific to maker asset
    /// @param _orderData [1] Encoded data specific to taker asset
    /// @param _signature _signature of the order.
    function makeOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        onlyManager
        notShutDown
        orderAddressesMatchOrderData(_orderAddresses, _orderValues, _orderData)
    {
        ensureCanMakeOrder(_orderAddresses[2]);

        IZeroExV3.Order memory order = constructOrderStruct(_orderAddresses, _orderValues, _orderData);
        address makerAsset = getAssetAddress(_orderData[0]);
        address takerAsset = getAssetAddress(_orderData[1]);

        // Order parameter checks
        getTrading().updateAndGetQuantityBeingTraded(makerAsset);
        ensureNotInOpenMakeOrder(makerAsset);

        approveAssetsMakeOrder(_targetExchange, order);

        IZeroExV3.OrderInfo memory orderInfo = IZeroExV3(_targetExchange).getOrderInfo(order);
        IZeroExV3(_targetExchange).preSign(orderInfo.orderHash);

        require(
            IZeroExV3(_targetExchange).isValidOrderSignature(order, _signature),
            "INVALID_ORDER_SIGNATURE"
        );

        updateStateMakeOrder(_targetExchange, order);
    }

    /// @notice Takes an active order on the selected exchange
    /// @dev These orders are expected to settle immediately
    /// @param _targetExchange Address of the exchange
    /// @param _orderAddresses [2] Order maker asset
    /// @param _orderAddresses [3] Order taker asset
    /// @param _orderValues [6] Fill amount: amount of taker token to be traded
    /// @param _signature _signature of the order.
    function takeOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        onlyManager
        notShutDown
        orderAddressesMatchOrderData(_orderAddresses, _orderValues, _orderData)
    {
        IZeroExV3.Order memory order = constructOrderStruct(_orderAddresses, _orderValues, _orderData);
        require(IZeroExV3(_targetExchange).isValidOrderSignature(order, _signature), "Order _signature is invalid");

        uint256 fillTakerQuantity = _orderValues[6];

        approveAssetsTakeOrder(_targetExchange, order, fillTakerQuantity);

        uint256 takerAssetFilledAmount = executeFill(_targetExchange, order, fillTakerQuantity, _signature);
        require(
            takerAssetFilledAmount == fillTakerQuantity,
            "Filled amount does not match desired fill amount"
        );

        updateStateTakeOrder(_targetExchange, order, fillTakerQuantity);
    }

    /// @notice Cancel the 0x make order
    /// @param _targetExchange Address of the exchange
    /// @param _orderAddresses [2] Order maker asset
    /// @param _identifier Order _identifier
    function cancelOrder(
        address _targetExchange,
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData,
        bytes32 _identifier,
        bytes memory _signature
    )
        public
        override
        orderAddressesMatchOrderData(_orderAddresses, _orderValues, _orderData)
    {
        IZeroExV3.Order memory order = getTrading().getZeroExV3OrderDetails(_identifier);
        ensureCancelPermitted(_targetExchange, getAssetAddress(order.makerAssetData), _identifier);
        if (order.expirationTimeSeconds > block.timestamp) {
            IZeroExV3(_targetExchange).cancelOrder(order);
        }

        revokeApproveAssetsCancelOrder(_targetExchange, order);

        updateStateCancelOrder(_targetExchange, order);
    }

    /// @dev Get order details
    function getOrder(address _targetExchange, uint256 _id, address _makerAsset)
        public
        view
        override
        returns (address, address, uint256, uint256)
    {
        uint orderId;
        uint orderIndex;
        address takerAsset;
        uint makerQuantity;
        uint takerQuantity;
        (orderId, , orderIndex) = Trading(msg.sender).getOpenOrderInfo(_targetExchange, _makerAsset);
        (, takerAsset, makerQuantity, takerQuantity) = Trading(msg.sender).getOrderDetails(orderIndex);
        uint takerAssetFilledAmount = IZeroExV3(_targetExchange).filled(bytes32(orderId));
        uint makerAssetFilledAmount = mul(takerAssetFilledAmount, makerQuantity) / takerQuantity;
        if (IZeroExV3(_targetExchange).cancelled(bytes32(orderId)) || sub(takerQuantity, takerAssetFilledAmount) == 0) {
            return (_makerAsset, takerAsset, 0, 0);
        }
        return (
            _makerAsset,
            takerAsset,
            sub(makerQuantity, makerAssetFilledAmount),
            sub(takerQuantity, takerAssetFilledAmount)
        );
    }

    // INTERNAL METHODS

    /// @notice Approves makerAsset, makerFeeAsset
    function approveAssetsMakeOrder(address _targetExchange, IZeroExV3.Order memory _order)
        internal
    {
        approveAsset(
            getAssetAddress(_order.makerAssetData),
            getAssetProxy(_targetExchange, _order.makerAssetData),
            _order.makerAssetAmount,
            "makerAsset"
        );
        if (_order.makerFee > 0) {
            approveAsset(
                getAssetAddress(_order.makerFeeAssetData),
                getAssetProxy(_targetExchange, _order.makerFeeAssetData),
                _order.makerFee,
                "makerFeeAsset"
            );
        }
    }

    /// @notice Approves takerAsset, takerFeeAsset, protocolFee
    function approveAssetsTakeOrder(
        address _targetExchange,
        IZeroExV3.Order memory _order,
        uint256 _fillTakerAmount
    )
        internal
    {
        approveProtocolFeeAsset(_targetExchange);
        uint256 takerFeeAmount = mul(_order.takerFee, _fillTakerAmount) / _order.takerAssetAmount;
        approveAsset(
            getAssetAddress(_order.takerAssetData),
            getAssetProxy(_targetExchange, _order.takerAssetData),
            _fillTakerAmount,
            "takerAsset"
        );
        if (takerFeeAmount > 0) {
            approveAsset(
                getAssetAddress(_order.takerFeeAssetData),
                getAssetProxy(_targetExchange, _order.takerFeeAssetData),
                takerFeeAmount,
                "takerFeeAsset"
            );
        }
    }

    function approveProtocolFeeAsset(address _targetExchange) internal {
        address protocolFeeCollector = IZeroExV3(_targetExchange).protocolFeeCollector();
        uint256 protocolFeeAmount = calcProtocolFeeAmount(_targetExchange);
        if (protocolFeeCollector == address(0) || protocolFeeAmount == 0) return;

        Hub hub = getHub();
        address nativeAsset = Accounting(hub.accounting()).NATIVE_ASSET();

        approveAsset(nativeAsset, protocolFeeCollector, protocolFeeAmount, "protocolFee");
    }

    /// @dev Needed to avoid stack too deep error
    function executeFill(
        address _targetExchange,
        IZeroExV3.Order memory _order,
        uint256 _takerAssetFillAmount,
        bytes memory _signature
    )
        internal
        returns (uint256)
    {
        Hub hub = getHub();
        address makerAsset = getAssetAddress(_order.makerAssetData);
        uint preMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this));

        IZeroExV3.FillResults memory fillResults = IZeroExV3(_targetExchange).fillOrder(
            _order,
            _takerAssetFillAmount,
            _signature
        );

        uint256 postMakerAssetBalance = IERC20(makerAsset).balanceOf(address(this));

        // Account for case where makerAsset, takerFee, protocolFee are the same
        uint256 makerAssetFeesTotal;
        if (
            makerAsset == Accounting(hub.accounting()).NATIVE_ASSET() &&
            IZeroExV3(_targetExchange).protocolFeeCollector() != address(0)
        )
        {
            makerAssetFeesTotal = calcProtocolFeeAmount(_targetExchange);
        }
        if (makerAsset == getAssetAddress(_order.takerFeeAssetData)) {
            makerAssetFeesTotal = add(makerAssetFeesTotal, _order.takerFee);
        }

        require(
            postMakerAssetBalance == sub(
                add(preMakerAssetBalance, fillResults.makerAssetFilledAmount),
                makerAssetFeesTotal
            ),
            "Maker asset balance different than expected"
        );

        return fillResults.takerAssetFilledAmount;
    }

    /// @notice Revoke asset approvals and return assets to vault
    function revokeApproveAssetsCancelOrder(
        address _targetExchange,
        IZeroExV3.Order memory _order
    )
        internal
    {
        address makerAsset = getAssetAddress(_order.makerAssetData);
        address makerFeeAsset = getAssetAddress(_order.makerFeeAssetData);
        bytes32 orderHash = IZeroExV3(_targetExchange).getOrderInfo(_order).orderHash;
        uint takerAssetFilledAmount = IZeroExV3(_targetExchange).filled(orderHash);
        uint makerAssetFilledAmount = mul(takerAssetFilledAmount, _order.makerAssetAmount) / _order.takerAssetAmount;
        uint256 makerAssetRemainingInOrder = sub(_order.makerAssetAmount, makerAssetFilledAmount);
        uint256 makerFeeRemainingInOrder = mul(_order.makerFee, makerAssetRemainingInOrder) / _order.makerAssetAmount;

        revokeApproveAsset(
            makerAsset,
            getAssetProxy(_targetExchange, _order.makerAssetData),
            makerAssetRemainingInOrder,
            "makerAsset"
        );
        uint256 timesMakerAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerAsset);
        // only return makerAsset early when it is not being used as a fee anywhere
        if (timesMakerAssetUsedAsFee == 0) {
            getTrading().returnAssetToVault(makerAsset);
        }

        if (_order.makerFee > 0) {
            revokeApproveAsset(
                makerFeeAsset,
                getAssetProxy(_targetExchange, _order.makerFeeAssetData),
                makerFeeRemainingInOrder,
                "makerFeeAsset"
            );
            // only return feeAsset when not used in another makeOrder AND
            //  when it is only used as a fee in this order that we are cancelling
            uint256 timesFeeAssetUsedAsFee = getTrading().openMakeOrdersUsingAssetAsFee(makerFeeAsset);
            if (
                !getTrading().isInOpenMakeOrder(makerFeeAsset) &&
                timesFeeAssetUsedAsFee == 1
            ) getTrading().returnAssetToVault(makerFeeAsset);
        }
    }

    function updateStateCancelOrder(address _targetExchange, IZeroExV3.Order memory _order)
        internal
    {
        address makerAsset = getAssetAddress(_order.makerAssetData);

        getTrading().removeOpenMakeOrder(_targetExchange, makerAsset);
        getAccounting().updateOwnedAssets();
        getTrading().orderUpdateHook(
            _targetExchange,
            IZeroExV3(_targetExchange).getOrderInfo(_order).orderHash,
            Trading.UpdateType.cancel,
            [address(0), address(0)],
            [uint(0), uint(0), uint(0)]
        );
    }

    function updateStateMakeOrder(address _targetExchange, IZeroExV3.Order memory _order)
        internal
    {
        address makerAsset = getAssetAddress(_order.makerAssetData);
        address takerAsset = getAssetAddress(_order.takerAssetData);
        IZeroExV3.OrderInfo memory orderInfo = IZeroExV3(_targetExchange).getOrderInfo(_order);

        getAccounting().addAssetToOwnedAssets(takerAsset);
        getTrading().orderUpdateHook(
            _targetExchange,
            orderInfo.orderHash,
            Trading.UpdateType.make,
            [payable(makerAsset), payable(takerAsset)],
            [_order.makerAssetAmount, _order.takerAssetAmount, uint(0)]
        );
        getTrading().addOpenMakeOrder(
            _targetExchange,
            makerAsset,
            takerAsset,
            getAssetAddress(_order.makerFeeAssetData),
            uint256(orderInfo.orderHash),
            _order.expirationTimeSeconds
        );
        getTrading().addZeroExV3OrderData(orderInfo.orderHash, _order);
    }

    /// @dev Avoids stack too deep error
    function updateStateTakeOrder(
        address _targetExchange,
        IZeroExV3.Order memory _order,
        uint256 _fillTakerQuantity
    )
        internal
    {
        address makerAsset = getAssetAddress(_order.makerAssetData);
        address takerAsset = getAssetAddress(_order.takerAssetData);

        getAccounting().addAssetToOwnedAssets(makerAsset);
        getAccounting().updateOwnedAssets();
        if (
            !getTrading().isInOpenMakeOrder(makerAsset) &&
            getTrading().openMakeOrdersUsingAssetAsFee(makerAsset) == 0
        ) {
            getTrading().returnAssetToVault(makerAsset);
        }
        getTrading().orderUpdateHook(
            _targetExchange,
            IZeroExV3(_targetExchange).getOrderInfo(_order).orderHash,
            Trading.UpdateType.take,
            [payable(makerAsset), payable(takerAsset)],
            [_order.makerAssetAmount, _order.takerAssetAmount, _fillTakerQuantity]
        );
    }

    // VIEW METHODS
    function calcProtocolFeeAmount(address _targetExchange) internal view returns (uint256) {
        return mul(IZeroExV3(_targetExchange).protocolFeeMultiplier(), tx.gasprice);
    }

    function constructOrderStruct(
        address[8] memory _orderAddresses,
        uint[8] memory _orderValues,
        bytes[4] memory _orderData
    )
        internal
        view
        returns (IZeroExV3.Order memory order_)
    {
        order_ = IZeroExV3.Order({
            makerAddress: _orderAddresses[0],
            takerAddress: _orderAddresses[1],
            feeRecipientAddress: _orderAddresses[4],
            senderAddress: _orderAddresses[5],
            makerAssetAmount: _orderValues[0],
            takerAssetAmount: _orderValues[1],
            makerFee: _orderValues[2],
            takerFee: _orderValues[3],
            expirationTimeSeconds: _orderValues[4],
            salt: _orderValues[5],
            makerAssetData: _orderData[0],
            takerAssetData: _orderData[1],
            makerFeeAssetData: _orderData[2],
            takerFeeAssetData: _orderData[3]
        });
    }

    function getAssetProxy(address _targetExchange, bytes memory _assetData)
        internal
        view
        returns (address assetProxy_)
    {
        bytes4 assetProxyId;
        assembly {
            assetProxyId := and(mload(
                add(_assetData, 32)),
                0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
            )
        }
        assetProxy_ = IZeroExV3(_targetExchange).getAssetProxy(assetProxyId);
    }

    function getAssetAddress(bytes memory _assetData)
        internal
        view
        returns (address assetAddress_)
    {
        assembly {
            assetAddress_ := mload(add(_assetData, 36))
        }
    }
}

File 31 of 77 : Factory.sol
pragma solidity 0.6.1;


contract Factory {
    mapping (address => bool) public childExists;

    event NewInstance(
        address indexed hub,
        address indexed instance
    );

    function isInstance(address _child) public view returns (bool) {
        return childExists[_child];
    }
}

File 32 of 77 : FundFactory.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../fund/accounting/IAccounting.sol";
import "../fund/fees/IFeeManager.sol";
import "../fund/hub/Hub.sol";
import "../fund/policies/IPolicyManager.sol";
import "../fund/participation/IParticipation.sol";
import "../fund/shares/IShares.sol";
import "../fund/trading/ITrading.sol";
import "../fund/vault/IVault.sol";
import "../version/IVersion.sol";
import "../engine/AmguConsumer.sol";
import "./Factory.sol";

/// @notice Creates fund routes and links them together
contract FundFactory is AmguConsumer, Factory {

    event NewFund(
        address indexed manager,
        address indexed hub,
        address[11] routes
    );

    IVersion public version;
    Registry public associatedRegistry;
    IAccountingFactory public accountingFactory;
    IFeeManagerFactory public feeManagerFactory;
    IParticipationFactory public participationFactory;
    IPolicyManagerFactory public policyManagerFactory;
    ISharesFactory public sharesFactory;
    ITradingFactory public tradingFactory;
    IVaultFactory public vaultFactory;

    address[] public funds;
    mapping (address => address) public managersToHubs;
    mapping (address => Hub.Routes) public managersToRoutes;
    mapping (address => Settings) public managersToSettings;

    /// @dev Parameters stored when beginning setup
    struct Settings {
        string name;
        address[] exchanges;
        address[] adapters;
        address denominationAsset;
        address[] defaultInvestmentAssets;
        address[] fees;
        uint[] feeRates;
        uint[] feePeriods;
    }

    constructor(
        address _accountingFactory,
        address _feeManagerFactory,
        address _participationFactory,
        address _sharesFactory,
        address _tradingFactory,
        address _vaultFactory,
        address _policyManagerFactory,
        address _version
    )
        public
    {
        accountingFactory = IAccountingFactory(_accountingFactory);
        feeManagerFactory = IFeeManagerFactory(_feeManagerFactory);
        participationFactory = IParticipationFactory(_participationFactory);
        sharesFactory = ISharesFactory(_sharesFactory);
        tradingFactory = ITradingFactory(_tradingFactory);
        vaultFactory = IVaultFactory(_vaultFactory);
        policyManagerFactory = IPolicyManagerFactory(_policyManagerFactory);
        version = IVersion(_version);
    }

    function componentExists(address _component) internal pure returns (bool) {
        return _component != address(0);
    }

    function ensureComponentNotSet(address _component) internal {
        require(
            !componentExists(_component),
            "This step has already been run"
        );
    }

    function ensureComponentSet(address _component) internal {
        require(
            componentExists(_component),
            "Component preprequisites not met"
        );
    }

    function beginSetup(
        string memory _name,
        address[] memory _fees,
        uint[] memory _feeRates,
        uint[] memory _feePeriods,
        address[] memory _exchanges,
        address[] memory _adapters,
        address _denominationAsset,
        address[] memory _defaultInvestmentAssets
    )
        public
    {
        ensureComponentNotSet(managersToHubs[msg.sender]);
        associatedRegistry.reserveFundName(
            msg.sender,
            _name
        );
        require(
            associatedRegistry.assetIsRegistered(_denominationAsset),
            "Denomination asset must be registered"
        );

        managersToHubs[msg.sender] = address(new Hub(msg.sender, _name));
        managersToSettings[msg.sender] = Settings(
            _name,
            _exchanges,
            _adapters,
            _denominationAsset,
            _defaultInvestmentAssets,
            _fees,
            _feeRates,
            _feePeriods
        );
        managersToRoutes[msg.sender].registry = address(associatedRegistry);
        managersToRoutes[msg.sender].version = address(version);
        managersToRoutes[msg.sender].engine = engine();
        managersToRoutes[msg.sender].mlnToken = mlnToken();
    }

    function _createAccountingFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].accounting);
        managersToRoutes[_manager].accounting = accountingFactory.createInstance(
            managersToHubs[_manager],
            managersToSettings[_manager].denominationAsset,
            associatedRegistry.nativeAsset()
        );
    }

    function createAccountingFor(address _manager) external amguPayable(false) payable { _createAccountingFor(_manager); }
    function createAccounting() external amguPayable(false) payable { _createAccountingFor(msg.sender); }

    function _createFeeManagerFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].feeManager);
        managersToRoutes[_manager].feeManager = feeManagerFactory.createInstance(
            managersToHubs[_manager],
            managersToSettings[_manager].denominationAsset,
            managersToSettings[_manager].fees,
            managersToSettings[_manager].feeRates,
            managersToSettings[_manager].feePeriods,
            managersToRoutes[_manager].registry
        );
    }

    function createFeeManagerFor(address _manager) external amguPayable(false) payable { _createFeeManagerFor(_manager); }
    function createFeeManager() external amguPayable(false) payable { _createFeeManagerFor(msg.sender); }

    function _createParticipationFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].participation);
        managersToRoutes[_manager].participation = participationFactory.createInstance(
            managersToHubs[_manager],
            managersToSettings[_manager].defaultInvestmentAssets,
            managersToRoutes[_manager].registry
        );
    }

    function createParticipationFor(address _manager) external amguPayable(false) payable { _createParticipationFor(_manager); }
    function createParticipation() external amguPayable(false) payable { _createParticipationFor(msg.sender); }

    function _createPolicyManagerFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].policyManager);
        managersToRoutes[_manager].policyManager = policyManagerFactory.createInstance(
            managersToHubs[_manager]
        );
    }

    function createPolicyManagerFor(address _manager) external amguPayable(false) payable { _createPolicyManagerFor(_manager); }
    function createPolicyManager() external amguPayable(false) payable { _createPolicyManagerFor(msg.sender); }

    function _createSharesFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].shares);
        managersToRoutes[_manager].shares = sharesFactory.createInstance(
            managersToHubs[_manager]
        );
    }

    function createSharesFor(address _manager) external amguPayable(false) payable { _createSharesFor(_manager); }
    function createShares() external amguPayable(false) payable { _createSharesFor(msg.sender); }

    function _createTradingFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].trading);
        managersToRoutes[_manager].trading = tradingFactory.createInstance(
            managersToHubs[_manager],
            managersToSettings[_manager].exchanges,
            managersToSettings[_manager].adapters,
            managersToRoutes[_manager].registry
        );
    }

    function createTradingFor(address _manager) external amguPayable(false) payable { _createTradingFor(_manager); }
    function createTrading() external amguPayable(false) payable { _createTradingFor(msg.sender); }

    function _createVaultFor(address _manager)
        internal
    {
        ensureComponentSet(managersToHubs[_manager]);
        ensureComponentNotSet(managersToRoutes[_manager].vault);
        managersToRoutes[_manager].vault = vaultFactory.createInstance(
            managersToHubs[_manager]
        );
    }

    function createVaultFor(address _manager) external amguPayable(false) payable { _createVaultFor(_manager); }
    function createVault() external amguPayable(false) payable { _createVaultFor(msg.sender); }

    function _completeSetupFor(address _manager) internal {
        Hub.Routes memory routes = managersToRoutes[_manager];
        Hub hub = Hub(managersToHubs[_manager]);
        require(!childExists[address(hub)], "Setup already complete");
        require(
            componentExists(address(hub)) &&
            componentExists(routes.accounting) &&
            componentExists(routes.feeManager) &&
            componentExists(routes.participation) &&
            componentExists(routes.policyManager) &&
            componentExists(routes.shares) &&
            componentExists(routes.trading) &&
            componentExists(routes.vault),
            "Components must be set before completing setup"
        );
        childExists[address(hub)] = true;
        hub.initializeAndSetPermissions([
            routes.accounting,
            routes.feeManager,
            routes.participation,
            routes.policyManager,
            routes.shares,
            routes.trading,
            routes.vault,
            routes.registry,
            routes.version,
            routes.engine,
            routes.mlnToken
        ]);
        funds.push(address(hub));
        associatedRegistry.registerFund(
            address(hub),
            _manager,
            managersToSettings[_manager].name
        );

        emit NewFund(
            msg.sender,
            address(hub),
            [
                routes.accounting,
                routes.feeManager,
                routes.participation,
                routes.policyManager,
                routes.shares,
                routes.trading,
                routes.vault,
                routes.registry,
                routes.version,
                routes.engine,
                routes.mlnToken
            ]
        );
    }

    function completeSetupFor(address _manager) external amguPayable(false) payable { _completeSetupFor(_manager); }
    function completeSetup() external amguPayable(false) payable { _completeSetupFor(msg.sender); }

    function getFundById(uint withId) external view returns (address) { return funds[withId]; }
    function getLastFundId() external view returns (uint) { return funds.length - 1; }

    function mlnToken() public view override returns (address) {
        return address(associatedRegistry.mlnToken());
    }
    function engine() public view override returns (address) {
        return address(associatedRegistry.engine());
    }
    function priceSource() public view override returns (address) {
        return address(associatedRegistry.priceSource());
    }
    function registry() public view override returns (address) { return address(associatedRegistry); }
    function getExchangesInfo(address user) public view returns (address[] memory) {
        return (managersToSettings[user].exchanges);
    }
}

File 33 of 77 : Accounting.sol
pragma solidity 0.6.1;

import "../../dependencies/token/StandardToken.sol";
import "../../factory/Factory.sol";
import "../../prices/IPriceSource.sol";
import "../fees/FeeManager.sol";
import "../hub/Spoke.sol";
import "../shares/Shares.sol";
import "../trading/ITrading.sol";
import "../vault/Vault.sol";
import "../../engine/AmguConsumer.sol";

contract Accounting is AmguConsumer, Spoke {

    event AssetAddition(address indexed asset);
    event AssetRemoval(address indexed asset);

    struct Calculations {
        uint gav;
        uint nav;
        uint allocatedFees;
        uint totalSupply;
        uint timestamp;
    }

    uint constant public MAX_OWNED_ASSETS = 20;
    address[] public ownedAssets;
    mapping (address => bool) public isInAssetList;
    uint public constant SHARES_DECIMALS = 18;
    address public NATIVE_ASSET;
    address public DENOMINATION_ASSET;
    uint public DENOMINATION_ASSET_DECIMALS;
    uint public DEFAULT_SHARE_PRICE;
    Calculations public atLastAllocation;

    constructor(address _hub, address _denominationAsset, address _nativeAsset)
        public
        Spoke(_hub)
    {
        DENOMINATION_ASSET = _denominationAsset;
        NATIVE_ASSET = _nativeAsset;
        DENOMINATION_ASSET_DECIMALS = ERC20WithFields(DENOMINATION_ASSET).decimals();
        DEFAULT_SHARE_PRICE = 10 ** uint(DENOMINATION_ASSET_DECIMALS);
    }

    function getOwnedAssetsLength() external view returns (uint256) {
        return ownedAssets.length;
    }

    function getOwnedAssets() external view returns (address[] memory) {
        return ownedAssets;
    }

    function assetHoldings(address _asset) public returns (uint256) {
        return add(
            uint256(ERC20WithFields(_asset).balanceOf(routes.vault)),
            ITrading(routes.trading).updateAndGetQuantityBeingTraded(_asset)
        );
    }

    /// @dev Returns sparse array
    function getFundHoldings() external returns (uint[] memory, address[] memory) {
        uint[] memory _quantities = new uint[](ownedAssets.length);
        address[] memory _assets = new address[](ownedAssets.length);
        for (uint i = 0; i < ownedAssets.length; i++) {
            address ofAsset = ownedAssets[i];
            // assetHoldings formatting: mul(exchangeHoldings, 10 ** assetDecimal)
            uint quantityHeld = assetHoldings(ofAsset);
            _assets[i] = ofAsset;
            _quantities[i] = quantityHeld;
        }
        return (_quantities, _assets);
    }

    function calcAssetGAV(address _queryAsset) external returns (uint) {
        uint queryAssetQuantityHeld = assetHoldings(_queryAsset);
        return IPriceSource(priceSource()).convertQuantity(
            queryAssetQuantityHeld, _queryAsset, DENOMINATION_ASSET
        );
    }

    // prices are quoted in DENOMINATION_ASSET so they use denominationDecimals
    function calcGav() public returns (uint gav) {
        for (uint i = 0; i < ownedAssets.length; ++i) {
            address asset = ownedAssets[i];
            // assetHoldings formatting: mul(exchangeHoldings, 10 ** assetDecimals)
            uint quantityHeld = assetHoldings(asset);
            // Dont bother with the calculations if the balance of the asset is 0
            if (quantityHeld == 0) {
                continue;
            }
            // gav as sum of mul(assetHoldings, assetPrice) with formatting: mul(mul(exchangeHoldings, exchangePrice), 10 ** shareDecimals)
            gav = add(
                gav,
                IPriceSource(priceSource()).convertQuantity(
                    quantityHeld, asset, DENOMINATION_ASSET
                )
            );
        }
        return gav;
    }

    function calcNav(uint gav, uint unclaimedFeesInDenominationAsset) public pure returns (uint) {
        return sub(gav, unclaimedFeesInDenominationAsset);
    }

    function valuePerShare(uint totalValue, uint numShares) public pure returns (uint) {
        require(numShares > 0, "No shares to calculate value for");
        return (totalValue * 10 ** uint(SHARES_DECIMALS)) / numShares;
    }

    function performCalculations()
        public
        returns (
            uint gav,
            uint feesInDenominationAsset,  // unclaimed amount
            uint feesInShares,             // unclaimed amount
            uint nav,
            uint sharePrice,
            uint gavPerShareNetManagementFee
        )
    {
        gav = calcGav();
        uint totalSupply = Shares(routes.shares).totalSupply();
        feesInShares = FeeManager(routes.feeManager).totalFeeAmount();
        feesInDenominationAsset = (totalSupply == 0) ?
            0 :
            mul(feesInShares, gav) / add(totalSupply, feesInShares);
        nav = calcNav(gav, feesInDenominationAsset);

        // The total share supply including the value of feesInDenominationAsset, measured in shares of this fund
        uint totalSupplyAccountingForFees = add(totalSupply, feesInShares);
        sharePrice = (totalSupply > 0) ?
            valuePerShare(gav, totalSupplyAccountingForFees) :
            DEFAULT_SHARE_PRICE;
        gavPerShareNetManagementFee = (totalSupply > 0) ?
            valuePerShare(gav, add(totalSupply, FeeManager(routes.feeManager).managementFeeAmount())) :
            DEFAULT_SHARE_PRICE;
        return (gav, feesInDenominationAsset, feesInShares, nav, sharePrice, gavPerShareNetManagementFee);
    }

    function calcGavPerShareNetManagementFee()
        public
        returns (uint gavPerShareNetManagementFee)
    {
        (,,,,,gavPerShareNetManagementFee) = performCalculations();
        return gavPerShareNetManagementFee;
    }

    function getShareCostInAsset(uint _numShares, address _altAsset)
        external
        returns (uint)
    {
        uint denominationAssetQuantity = mul(
            _numShares,
            calcGavPerShareNetManagementFee()
        ) / 10 ** uint(SHARES_DECIMALS);
        return IPriceSource(priceSource()).convertQuantity(
            denominationAssetQuantity, DENOMINATION_ASSET, _altAsset
        );
    }

    /// @notice Reward all fees and perform some updates
    /// @dev Anyone can call this
    function triggerRewardAllFees()
        external
        amguPayable(false)
        payable
    {
        updateOwnedAssets();
        uint256 gav;
        uint256 feesInDenomination;
        uint256 feesInShares;
        uint256 nav;
        (gav, feesInDenomination, feesInShares, nav,,) = performCalculations();
        uint256 totalSupply = Shares(routes.shares).totalSupply();
        FeeManager(routes.feeManager).rewardAllFees();
        atLastAllocation = Calculations({
            gav: gav,
            nav: nav,
            allocatedFees: feesInDenomination,
            totalSupply: totalSupply,
            timestamp: block.timestamp
        });
    }

    /// @dev Check holdings for all assets, and adjust list
    function updateOwnedAssets() public {
        for (uint i = 0; i < ownedAssets.length; i++) {
            address asset = ownedAssets[i];
            if (
                assetHoldings(asset) == 0 &&
                !(asset == address(DENOMINATION_ASSET)) &&
                ITrading(routes.trading).getOpenMakeOrdersAgainstAsset(asset) == 0
            ) {
                _removeFromOwnedAssets(asset);
            }
        }
    }

    function addAssetToOwnedAssets(address _asset) external auth {
        _addAssetToOwnedAssets(_asset);
    }

    function removeFromOwnedAssets(address _asset) external auth {
        _removeFromOwnedAssets(_asset);
    }

    /// @dev Just pass if asset already in list
    function _addAssetToOwnedAssets(address _asset) internal {
        if (isInAssetList[_asset]) { return; }

        require(
            ownedAssets.length < MAX_OWNED_ASSETS,
            "Max owned asset limit reached"
        );
        isInAssetList[_asset] = true;
        ownedAssets.push(_asset);
        emit AssetAddition(_asset);
    }

    /// @dev Just pass if asset not in list
    function _removeFromOwnedAssets(address _asset) internal {
        if (!isInAssetList[_asset]) { return; }

        isInAssetList[_asset] = false;
        for (uint i; i < ownedAssets.length; i++) {
            if (ownedAssets[i] == _asset) {
                ownedAssets[i] = ownedAssets[ownedAssets.length - 1];
                ownedAssets.pop();
                break;
            }
        }
        emit AssetRemoval(_asset);
    }

    function engine() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.engine(); }
    function mlnToken() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.mlnToken(); }
    function priceSource() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.priceSource(); }
    function registry() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.registry(); }
}

contract AccountingFactory is Factory {
    event NewInstance(
        address indexed hub,
        address indexed instance,
        address denominationAsset,
        address nativeAsset
    );

    function createInstance(address _hub, address _denominationAsset, address _nativeAsset) external returns (address) {
        address accounting = address(new Accounting(_hub, _denominationAsset, _nativeAsset));
        childExists[accounting] = true;
        emit NewInstance(_hub, accounting, _denominationAsset, _nativeAsset);
        return accounting;
    }
}

File 34 of 77 : IAccounting.sol
pragma solidity 0.6.1;

/// @notice Gives metrics about a Fund
interface IAccounting {
    function getOwnedAssetsLength() external view returns (uint);
    function getFundHoldings() external returns (uint[] memory, address[] memory);
    function calcAssetGAV(address ofAsset) external returns (uint);
    function calcGav() external returns (uint gav);
    function calcNav(uint gav, uint unclaimedFees) external pure returns (uint);
    function valuePerShare(uint totalValue, uint numShares) external view returns (uint);
    function performCalculations() external returns (
        uint gav,
        uint unclaimedFees,
        uint feesInShares,
        uint nav,
        uint sharePrice,
        uint gavPerShareNetManagementFee
    );
    function calcGavPerShareNetManagementFee() external returns (uint);
}

interface IAccountingFactory {
    function createInstance(address _hub, address _denominationAsset, address _nativeAsset) external returns (address);
}

File 35 of 77 : FeeManager.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "./IFee.sol";
import "../hub/Spoke.sol";
import "../shares/Shares.sol";
import "../../factory/Factory.sol";
import "../../version/Registry.sol";
import "../../dependencies/DSMath.sol";
import "./IFeeManager.sol";

/// @notice Manages and allocates fees for a particular fund
contract FeeManager is DSMath, Spoke {

    event FeeReward(uint shareQuantity);
    event FeeRegistration(address fee);

    struct FeeInfo {
        address feeAddress;
        uint feeRate;
        uint feePeriod;
    }

    IFee[] public fees;
    mapping (address => bool) public feeIsRegistered;

    constructor(address _hub, address _denominationAsset, address[] memory _fees, uint[] memory _rates, uint[] memory _periods, address _registry) Spoke(_hub) public {
        for (uint i = 0; i < _fees.length; i++) {
            require(
                Registry(_registry).isFeeRegistered(_fees[i]),
                "Fee must be known to Registry"
            );
            register(_fees[i], _rates[i], _periods[i], _denominationAsset);
        }
        if (fees.length > 0) {
            require(
                fees[0].identifier() == 0,
                "Management fee must be at 0 index"
            );
        }
        if (fees.length > 1) {
            require(
                fees[1].identifier() == 1,
                "Performance fee must be at 1 index"
            );
        }
    }

    function register(address feeAddress, uint feeRate, uint feePeriod, address denominationAsset) internal {
        require(!feeIsRegistered[feeAddress], "Fee already registered");
        feeIsRegistered[feeAddress] = true;
        fees.push(IFee(feeAddress));
        IFee(feeAddress).initializeForUser(feeRate, feePeriod, denominationAsset);  // initialize state
        emit FeeRegistration(feeAddress);
    }

    function totalFeeAmount() external returns (uint total) {
        for (uint i = 0; i < fees.length; i++) {
            total = add(total, fees[i].feeAmount());
        }
        return total;
    }

    /// @dev Shares to be inflated after update state
    function _rewardFee(IFee fee) internal {
        require(feeIsRegistered[address(fee)], "Fee is not registered");
        uint rewardShares = fee.feeAmount();
        fee.updateState();
        Shares(routes.shares).createFor(hub.manager(), rewardShares);
        emit FeeReward(rewardShares);
    }

    function _rewardAllFees() internal {
        for (uint i = 0; i < fees.length; i++) {
            _rewardFee(fees[i]);
        }
    }

    /// @dev Used when calling from other components
    function rewardAllFees() public auth { _rewardAllFees(); }

    /// @dev Convenience function; anyone can reward management fee any time
    /// @dev Convention that management fee is 0
    function rewardManagementFee() public {
        if (fees.length >= 1) _rewardFee(fees[0]);
    }

    /// @dev Convenience function
    /// @dev Convention that management fee is 0
    function managementFeeAmount() external returns (uint) {
        if (fees.length < 1) return 0;
        return fees[0].feeAmount();
    }

    /// @dev Convenience function
    /// @dev Convention that performace fee is 1
    function performanceFeeAmount() external returns (uint) {
        if (fees.length < 2) return 0;
        return fees[1].feeAmount();
    }
}

contract FeeManagerFactory is Factory {
    function createInstance(
        address _hub,
        address _denominationAsset,
        address[] memory _fees,
        uint[] memory _feeRates,
        uint[] memory _feePeriods,
        address _registry
    ) public returns (address) {
        address feeManager = address(
            new FeeManager(_hub, _denominationAsset, _fees, _feeRates, _feePeriods, _registry)
        );
        childExists[feeManager] = true;
        emit NewInstance(_hub, feeManager);
        return feeManager;
    }
}

File 36 of 77 : IFee.sol
pragma solidity 0.6.1;

/// @dev Exposes "feeAmount", which maps fund state and fee state to uint
/// @dev Notice that "feeAmount" *may* change contract state
/// @dev Also exposes "updateState", which changes fee's internal state
interface IFee {
    function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external;
    function feeAmount() external returns (uint);
    function updateState() external;

    /// @notice Used to enforce a convention
    function identifier() external view returns (uint);
}

File 37 of 77 : IFeeManager.sol
pragma solidity 0.6.1;

interface IFeeManagerFactory {
    function createInstance(
        address _hub,
        address _denominationAsset,
        address[] calldata _fees,
        uint[] calldata _feeRates,
        uint[] calldata _feePeriods,
        address _registry
    ) external returns (address);
}

File 38 of 77 : ManagementFee.sol
pragma solidity 0.6.1;

import "./FeeManager.sol";
import "../hub/Hub.sol";
import "../shares/Shares.sol";
import "../../dependencies/DSMath.sol";

contract ManagementFee is DSMath {

    uint public DIVISOR = 10 ** 18;

    mapping (address => uint) public managementFeeRate;
    mapping (address => uint) public lastPayoutTime;

    function feeAmount() external view returns (uint feeInShares) {
        Hub hub = FeeManager(msg.sender).hub();
        Shares shares = Shares(hub.shares());
        if (shares.totalSupply() == 0 || managementFeeRate[msg.sender] == 0) {
            feeInShares = 0;
        } else {
            uint timePassed = sub(block.timestamp, lastPayoutTime[msg.sender]);
            uint preDilutionFeeShares = mul(mul(shares.totalSupply(), managementFeeRate[msg.sender]) / DIVISOR, timePassed) / 365 days;
            feeInShares =
                mul(preDilutionFeeShares, shares.totalSupply()) /
                sub(shares.totalSupply(), preDilutionFeeShares);
        }
        return feeInShares;
    }

    function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external {
        require(lastPayoutTime[msg.sender] == 0);
        managementFeeRate[msg.sender] = feeRate;
        lastPayoutTime[msg.sender] = block.timestamp;
    }

    function updateState() external {
        lastPayoutTime[msg.sender] = block.timestamp;
    }

    function identifier() external pure returns (uint) {
        return 0;
    }
}

File 39 of 77 : PerformanceFee.sol
pragma solidity 0.6.1;

import "./FeeManager.sol";
import "../accounting/Accounting.sol";
import "../hub/Hub.sol";
import "../shares/Shares.sol";
import "../../dependencies/DSMath.sol";

contract PerformanceFee is DSMath {

    event HighWaterMarkUpdate(address indexed feeManager, uint indexed hwm);

    uint public constant DIVISOR = 10 ** 18;
    uint public constant REDEEM_WINDOW = 1 weeks;

    mapping(address => uint) public highWaterMark;
    mapping(address => uint) public lastPayoutTime;
    mapping(address => uint) public initializeTime;
    mapping(address => uint) public performanceFeeRate;
    mapping(address => uint) public performanceFeePeriod;

    /// @notice Sets initial state of the fee for a user
    function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external {
        require(lastPayoutTime[msg.sender] == 0, "Already initialized");
        performanceFeeRate[msg.sender] = feeRate;
        performanceFeePeriod[msg.sender] = feePeriod;
        highWaterMark[msg.sender] = 10 ** uint(ERC20WithFields(denominationAsset).decimals());
        lastPayoutTime[msg.sender] = block.timestamp;
        initializeTime[msg.sender] = block.timestamp;
    }

    /// @notice Assumes management fee is zero
    function feeAmount() external returns (uint feeInShares) {
        Hub hub = FeeManager(msg.sender).hub();
        Accounting accounting = Accounting(hub.accounting());
        Shares shares = Shares(hub.shares());
        uint gav = accounting.calcGav();
        uint gavPerShare = shares.totalSupply() > 0 ?
            accounting.valuePerShare(gav, shares.totalSupply())
            : accounting.DEFAULT_SHARE_PRICE();
        if (
            gavPerShare > highWaterMark[msg.sender] &&
            shares.totalSupply() != 0 &&
            gav != 0
        ) {
            uint sharePriceGain = sub(gavPerShare, highWaterMark[msg.sender]);
            uint totalGain = mul(sharePriceGain, shares.totalSupply()) / DIVISOR;
            uint feeInAsset = mul(totalGain, performanceFeeRate[msg.sender]) / DIVISOR;
            uint preDilutionFee = mul(shares.totalSupply(), feeInAsset) / gav;
            feeInShares =
                mul(preDilutionFee, shares.totalSupply()) /
                sub(shares.totalSupply(), preDilutionFee);
        }
        else {
            feeInShares = 0;
        }
        return feeInShares;
    }

    function canUpdate(address _who) public view returns (bool) {
        uint timeSinceInit = sub(
            block.timestamp,
            initializeTime[_who]
        );
        uint secondsSinceLastPeriod = timeSinceInit % performanceFeePeriod[_who];
        uint lastPeriodEnd = sub(block.timestamp, secondsSinceLastPeriod);
        return (
            secondsSinceLastPeriod <= REDEEM_WINDOW &&
            lastPayoutTime[_who] < lastPeriodEnd
        );
    }

    /// @notice Assumes management fee is zero
    function updateState() external {
        require(lastPayoutTime[msg.sender] != 0, "Not initialized");
        require(
            canUpdate(msg.sender),
            "Not within a update window or already updated this period"
        );
        Hub hub = FeeManager(msg.sender).hub();
        Accounting accounting = Accounting(hub.accounting());
        Shares shares = Shares(hub.shares());
        uint gav = accounting.calcGav();
        uint currentGavPerShare = accounting.valuePerShare(gav, shares.totalSupply());
        require(
            currentGavPerShare > highWaterMark[msg.sender],
            "Current share price does not pass high water mark"
        );
        lastPayoutTime[msg.sender] = block.timestamp;
        highWaterMark[msg.sender] = currentGavPerShare;
        emit HighWaterMarkUpdate(msg.sender, currentGavPerShare);
    }

    function identifier() external pure returns (uint) {
        return 1;
    }
}

File 40 of 77 : Hub.sol
pragma solidity 0.6.1;

import "../../dependencies/DSGuard.sol";
import "./Spoke.sol";
import "../../version/Registry.sol";

/// @notice Router for communication between components
/// @notice Has one or more Spokes
contract Hub is DSGuard {

    event FundShutDown();

    struct Routes {
        address accounting;
        address feeManager;
        address participation;
        address policyManager;
        address shares;
        address trading;
        address vault;
        address registry;
        address version;
        address engine;
        address mlnToken;
    }

    Routes public routes;
    address public manager;
    address public creator;
    string public name;
    bool public isShutDown;
    bool public fundInitialized;
    uint public creationTime;
    mapping (address => bool) public isSpoke;

    constructor(address _manager, string memory _name) public {
        creator = msg.sender;
        manager = _manager;
        name = _name;
        creationTime = block.timestamp;
    }

    modifier onlyCreator() {
        require(msg.sender == creator, "Only creator can do this");
        _;
    }

    function shutDownFund() external {
        require(msg.sender == routes.version);
        isShutDown = true;
        emit FundShutDown();
    }

    function initializeAndSetPermissions(address[11] calldata _spokes) external onlyCreator {
        require(!fundInitialized, "Fund is already initialized");
        for (uint i = 0; i < _spokes.length; i++) {
            isSpoke[_spokes[i]] = true;
        }
        routes.accounting = _spokes[0];
        routes.feeManager = _spokes[1];
        routes.participation = _spokes[2];
        routes.policyManager = _spokes[3];
        routes.shares = _spokes[4];
        routes.trading = _spokes[5];
        routes.vault = _spokes[6];
        routes.registry = _spokes[7];
        routes.version = _spokes[8];
        routes.engine = _spokes[9];
        routes.mlnToken = _spokes[10];

        Spoke(routes.accounting).initialize(_spokes);
        Spoke(routes.feeManager).initialize(_spokes);
        Spoke(routes.participation).initialize(_spokes);
        Spoke(routes.policyManager).initialize(_spokes);
        Spoke(routes.shares).initialize(_spokes);
        Spoke(routes.trading).initialize(_spokes);
        Spoke(routes.vault).initialize(_spokes);

        permit(routes.participation, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
        permit(routes.trading, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
        permit(routes.participation, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
        permit(routes.participation, routes.shares, bytes4(keccak256('destroyFor(address,uint256)')));
        permit(routes.feeManager, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
        permit(routes.participation, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
        permit(routes.trading, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
        permit(routes.trading, routes.accounting, bytes4(keccak256('removeFromOwnedAssets(address)')));
        permit(routes.accounting, routes.feeManager, bytes4(keccak256('rewardAllFees()')));
        permit(manager, routes.policyManager, bytes4(keccak256('register(bytes4,address)')));
        permit(manager, routes.policyManager, bytes4(keccak256('batchRegister(bytes4[],address[])')));
        permit(manager, routes.participation, bytes4(keccak256('enableInvestment(address[])')));
        permit(manager, routes.participation, bytes4(keccak256('disableInvestment(address[])')));
        permit(manager, routes.trading, bytes4(keccak256('addExchange(address,address)')));
        fundInitialized = true;
    }

    function vault() external view returns (address) { return routes.vault; }
    function accounting() external view returns (address) { return routes.accounting; }
    function priceSource() external view returns (address) { return Registry(routes.registry).priceSource(); }
    function participation() external view returns (address) { return routes.participation; }
    function trading() external view returns (address) { return routes.trading; }
    function shares() external view returns (address) { return routes.shares; }
    function registry() external view returns (address) { return routes.registry; }
    function version() external view returns (address) { return routes.version; }
    function policyManager() external view returns (address) { return routes.policyManager; }
}

File 41 of 77 : Spoke.sol
pragma solidity 0.6.1;

import "./Hub.sol";
import "../../dependencies/DSAuth.sol";

/// @notice Has one Hub
contract Spoke is DSAuth {
    Hub public hub;
    Hub.Routes public routes;
    bool public initialized;

    modifier onlyInitialized() {
        require(initialized, "Component not yet initialized");
        _;
    }

    modifier notShutDown() {
        require(!hub.isShutDown(), "Hub is shut down");
        _;
    }

    constructor(address _hub) public {
        hub = Hub(_hub);
        setAuthority(hub);
        setOwner(address(hub)); // temporary, to allow initialization
    }

    function initialize(address[11] calldata _spokes) external auth {
        require(msg.sender == address(hub));
        require(!initialized, "Already initialized");
        routes = Hub.Routes(
            _spokes[0],
            _spokes[1],
            _spokes[2],
            _spokes[3],
            _spokes[4],
            _spokes[5],
            _spokes[6],
            _spokes[7],
            _spokes[8],
            _spokes[9],
            _spokes[10]
        );
        initialized = true;
        setOwner(address(0));
    }

    function engine() public view virtual returns (address) { return routes.engine; }
    function mlnToken() public view virtual returns (address) { return routes.mlnToken; }
    function priceSource() public view virtual returns (address) { return hub.priceSource(); }
    function version() public view virtual returns (address) { return routes.version; }
    function registry() public view virtual returns (address) { return routes.registry; }
}

File 42 of 77 : IParticipation.sol
pragma solidity 0.6.1;

/// @notice Investor Fund interactions
/// @notice Handles redemptions and requests for investment
interface IParticipation {
    function requestInvestment(
        uint requestedShares,
        uint investmentAmount,
        address investmentAsset
    ) external payable;
    function hasRequest(address) external view returns (bool);
    function cancelRequest() external payable;
    function executeRequestFor(address requestOwner) external payable;
    function redeem() external;
    function redeemWithConstraints(uint shareQuantity, address[] calldata requestedAssets) external;
}

interface IParticipationFactory {
    function createInstance(address _hub, address[] calldata _defaultAssets, address _registry) external returns (address);
}

File 43 of 77 : Participation.sol
pragma solidity 0.6.1;

import "../vault/Vault.sol";
import "../shares/Shares.sol";
import "../policies/PolicyManager.sol";
import "../hub/Spoke.sol";
import "../accounting/Accounting.sol";
import "../../prices/IPriceSource.sol";
import "../../factory/Factory.sol";
import "../../engine/AmguConsumer.sol";
import "../../dependencies/token/IERC20.sol";
import "../../dependencies/DSMath.sol";
import "../../dependencies/TokenUser.sol";

/// @notice Entry and exit point for investors
contract Participation is TokenUser, AmguConsumer, Spoke {
    event EnableInvestment (address[] asset);
    event DisableInvestment (address[] assets);

    event InvestmentRequest (
        address indexed requestOwner,
        address indexed investmentAsset,
        uint requestedShares,
        uint investmentAmount
    );

    event RequestExecution (
        address indexed requestOwner,
        address indexed executor,
        address indexed investmentAsset,
        uint investmentAmount,
        uint requestedShares
    );

    event CancelRequest (
        address indexed requestOwner
    );

    event Redemption (
        address indexed redeemer,
        address[] assets,
        uint[] assetQuantities,
        uint redeemedShares
    );

    struct Request {
        address investmentAsset;
        uint investmentAmount;
        uint requestedShares;
        uint timestamp;
    }

    uint constant public SHARES_DECIMALS = 18;
    uint constant public REQUEST_LIFESPAN = 1 days;

    mapping (address => Request) public requests;
    mapping (address => bool) public investAllowed;
    mapping (address => bool) public hasInvested; // for information purposes only (read)

    address[] public historicalInvestors; // for information purposes only (read)

    constructor(address _hub, address[] memory _defaultAssets, address _registry)
        public
        Spoke(_hub)
    {
        routes.registry = _registry;
        _enableInvestment(_defaultAssets);
    }

    receive() external payable {}

    function _enableInvestment(address[] memory _assets) internal {
        for (uint i = 0; i < _assets.length; i++) {
            require(
                Registry(routes.registry).assetIsRegistered(_assets[i]),
                "Asset not registered"
            );
            investAllowed[_assets[i]] = true;
        }
        emit EnableInvestment(_assets);
    }

    function enableInvestment(address[] calldata _assets) external auth {
        _enableInvestment(_assets);
    }

    function disableInvestment(address[] calldata _assets) external auth {
        for (uint i = 0; i < _assets.length; i++) {
            investAllowed[_assets[i]] = false;
        }
        emit DisableInvestment(_assets);
    }

    function hasRequest(address _who) public view returns (bool) {
        return requests[_who].timestamp > 0;
    }

    function hasExpiredRequest(address _who) public view returns (bool) {
        return block.timestamp > add(requests[_who].timestamp, REQUEST_LIFESPAN);
    }

    /// @notice Whether request is OK and invest delay is being respected
    /// @dev Request valid if price update happened since request and not expired
    /// @dev If no shares exist and not expired, request can be executed immediately
    function hasValidRequest(address _who) public view returns (bool) {
        IPriceSource priceSource = IPriceSource(priceSource());
        bool delayRespectedOrNoShares = requests[_who].timestamp < priceSource.getLastUpdate() ||
            Shares(routes.shares).totalSupply() == 0;

        return hasRequest(_who) &&
            delayRespectedOrNoShares &&
            !hasExpiredRequest(_who) &&
            requests[_who].investmentAmount > 0 &&
            requests[_who].requestedShares > 0;
    }

    function requestInvestment(
        uint requestedShares,
        uint investmentAmount,
        address investmentAsset
    )
        external
        notShutDown
        payable
        amguPayable(true)
        onlyInitialized
    {
        PolicyManager(routes.policyManager).preValidate(
            msg.sig,
            [msg.sender, address(0), address(0), investmentAsset, address(0)],
            [uint(0), uint(0), uint(0)],
            bytes32(0)
        );
        require(
            investAllowed[investmentAsset],
            "Investment not allowed in this asset"
        );
        safeTransferFrom(
            investmentAsset, msg.sender, address(this), investmentAmount
        );
        require(
            requests[msg.sender].timestamp == 0,
            "Only one request can exist at a time"
        );
        requests[msg.sender] = Request({
            investmentAsset: investmentAsset,
            investmentAmount: investmentAmount,
            requestedShares: requestedShares,
            timestamp: block.timestamp
        });
        PolicyManager(routes.policyManager).postValidate(
            msg.sig,
            [msg.sender, address(0), address(0), investmentAsset, address(0)],
            [uint(0), uint(0), uint(0)],
            bytes32(0)
        );

        emit InvestmentRequest(
            msg.sender,
            investmentAsset,
            requestedShares,
            investmentAmount
        );
    }

    function _cancelRequestFor(address requestOwner) internal {
        require(hasRequest(requestOwner), "No request to cancel");
        IPriceSource priceSource = IPriceSource(priceSource());
        Request memory request = requests[requestOwner];
        require(
            !priceSource.hasValidPrice(request.investmentAsset) ||
            hasExpiredRequest(requestOwner) ||
            hub.isShutDown(),
            "No cancellation condition was met"
        );
        IERC20 investmentAsset = IERC20(request.investmentAsset);
        uint investmentAmount = request.investmentAmount;
        delete requests[requestOwner];
        msg.sender.transfer(Registry(routes.registry).incentive());
        safeTransfer(address(investmentAsset), requestOwner, investmentAmount);

        emit CancelRequest(requestOwner);
    }

    /// @notice Can only cancel when no price, request expired or fund shut down
    /// @dev Only request owner can cancel their request
    function cancelRequest() external payable amguPayable(false) {
        _cancelRequestFor(msg.sender);
    }

    function cancelRequestFor(address requestOwner)
        external
        payable
        amguPayable(false)
    {
        _cancelRequestFor(requestOwner);
    }

    function executeRequestFor(address requestOwner)
        external
        notShutDown
        amguPayable(false)
        payable
    {
        Request memory request = requests[requestOwner];
        require(
            hasValidRequest(requestOwner),
            "No valid request for this address"
        );

        FeeManager(routes.feeManager).rewardManagementFee();

        uint totalShareCostInInvestmentAsset = Accounting(routes.accounting)
            .getShareCostInAsset(
                request.requestedShares,
                request.investmentAsset
            );

        require(
            totalShareCostInInvestmentAsset <= request.investmentAmount,
            "Invested amount too low"
        );
        // send necessary amount of investmentAsset to vault
        safeTransfer(
            request.investmentAsset,
            routes.vault,
            totalShareCostInInvestmentAsset
        );

        uint investmentAssetChange = sub(
            request.investmentAmount,
            totalShareCostInInvestmentAsset
        );

        // return investmentAsset change to request owner
        if (investmentAssetChange > 0) {
            safeTransfer(
                request.investmentAsset,
                requestOwner,
                investmentAssetChange
            );
        }

        msg.sender.transfer(Registry(routes.registry).incentive());

        Shares(routes.shares).createFor(requestOwner, request.requestedShares);
        Accounting(routes.accounting).addAssetToOwnedAssets(request.investmentAsset);

        if (!hasInvested[requestOwner]) {
            hasInvested[requestOwner] = true;
            historicalInvestors.push(requestOwner);
        }

        emit RequestExecution(
            requestOwner,
            msg.sender,
            request.investmentAsset,
            request.investmentAmount,
            request.requestedShares
        );
        delete requests[requestOwner];
    }

    function getOwedPerformanceFees(uint shareQuantity)
        public
        returns (uint remainingShareQuantity)
    {
        Shares shares = Shares(routes.shares);

        uint totalPerformanceFee = FeeManager(routes.feeManager).performanceFeeAmount();
        // The denominator is augmented because performanceFeeAmount() accounts for inflation
        // Since shares are directly transferred, we don't need to account for inflation in this case
        uint performanceFeePortion = mul(
            totalPerformanceFee,
            shareQuantity
        ) / add(shares.totalSupply(), totalPerformanceFee);
        return performanceFeePortion;
    }

    /// @dev "Happy path" (no asset throws & quantity available)
    /// @notice Redeem all shares and across all assets
    function redeem() external {
        uint ownedShares = Shares(routes.shares).balanceOf(msg.sender);
        redeemQuantity(ownedShares);
    }

    /// @notice Redeem shareQuantity across all assets
    function redeemQuantity(uint shareQuantity) public {
        address[] memory assetList;
        assetList = Accounting(routes.accounting).getOwnedAssets();
        redeemWithConstraints(shareQuantity, assetList);
    }

    // TODO: reconsider the scenario where the user has enough funds to force shutdown on a large trade (any way around this?)
    /// @dev Redeem only selected assets (used only when an asset throws)
    function redeemWithConstraints(uint shareQuantity, address[] memory requestedAssets) public {
        Shares shares = Shares(routes.shares);
        require(
            shares.balanceOf(msg.sender) >= shareQuantity &&
            shares.balanceOf(msg.sender) > 0,
            "Sender does not have enough shares to fulfill request"
        );

        uint owedPerformanceFees = 0;
        if (
            IPriceSource(priceSource()).hasValidPrices(requestedAssets) &&
            msg.sender != hub.manager()
        ) {
            FeeManager(routes.feeManager).rewardManagementFee();
            owedPerformanceFees = getOwedPerformanceFees(shareQuantity);
            shares.destroyFor(msg.sender, owedPerformanceFees);
            shares.createFor(hub.manager(), owedPerformanceFees);
        }
        uint remainingShareQuantity = sub(shareQuantity, owedPerformanceFees);

        address ofAsset;
        uint[] memory ownershipQuantities = new uint[](requestedAssets.length);
        address[] memory redeemedAssets = new address[](requestedAssets.length);
        // Check whether enough assets held by fund
        Accounting accounting = Accounting(routes.accounting);
        for (uint i = 0; i < requestedAssets.length; ++i) {
            ofAsset = requestedAssets[i];
            require(
                accounting.isInAssetList(ofAsset),
                "Requested asset not in asset list"
            );
            for (uint j = 0; j < redeemedAssets.length; j++) {
                require(
                    ofAsset != redeemedAssets[j],
                    "Asset can only be redeemed once"
                );
            }
            redeemedAssets[i] = ofAsset;
            uint quantityHeld = accounting.assetHoldings(ofAsset);
            if (quantityHeld == 0) continue;

            // participant's ownership percentage of asset holdings
            ownershipQuantities[i] = mul(quantityHeld, remainingShareQuantity) / shares.totalSupply();
        }

        shares.destroyFor(msg.sender, remainingShareQuantity);

        // Transfer owned assets
        for (uint k = 0; k < requestedAssets.length; ++k) {
            ofAsset = requestedAssets[k];
            if (ownershipQuantities[k] == 0) {
                continue;
            } else {
                Vault(routes.vault).withdraw(ofAsset, ownershipQuantities[k]);
                safeTransfer(ofAsset, msg.sender, ownershipQuantities[k]);
            }
        }
        emit Redemption(
            msg.sender,
            requestedAssets,
            ownershipQuantities,
            remainingShareQuantity
        );
    }

    function getHistoricalInvestors() external view returns (address[] memory) {
        return historicalInvestors;
    }

    function engine() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.engine(); }
    function mlnToken() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.mlnToken(); }
    function priceSource() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.priceSource(); }
    function registry() public view override(AmguConsumer, Spoke) returns (address) { return Spoke.registry(); }
}

contract ParticipationFactory is Factory {
    event NewInstance(
        address indexed hub,
        address indexed instance,
        address[] defaultAssets,
        address registry
    );

    function createInstance(address _hub, address[] calldata _defaultAssets, address _registry)
        external
        returns (address)
    {
        address participation = address(
            new Participation(_hub, _defaultAssets, _registry)
        );
        childExists[participation] = true;
        emit NewInstance(_hub, participation, _defaultAssets, _registry);
        return participation;
    }
}

File 44 of 77 : AddressList.sol
pragma solidity 0.6.1;

import "../../dependencies/DSAuth.sol";

/// @notice Generic AddressList
contract AddressList is DSAuth {

    event ListAddition(address[] ones);

    mapping(address => bool) internal list;
    address[] internal mirror;

    constructor(address[] memory _assets) public {
        for (uint i = 0; i < _assets.length; i++) {
            if (!isMember(_assets[i])) { // filter duplicates in _assets
                list[_assets[i]] = true;
                mirror.push(_assets[i]);
            }
        }
        emit ListAddition(_assets);
    }

    /// @return whether an asset is in the list
    function isMember(address _asset) public view returns (bool) {
        return list[_asset];
    }

    /// @return number of assets specified in the list
    function getMemberCount() external view returns (uint) {
        return mirror.length;
    }

    /// @return array of all listed asset addresses
    function getMembers() external view returns (address[] memory) { return mirror; }
}

File 45 of 77 : UserWhitelist.sol
pragma solidity 0.6.1;

import "../../../dependencies/DSAuth.sol";

contract UserWhitelist is DSAuth {
    enum Applied { pre, post }

    event ListAddition(address indexed who);
    event ListRemoval(address indexed who);

    mapping (address => bool) public whitelisted;

    constructor(address[] memory _preApproved) public {
        batchAddToWhitelist(_preApproved);
    }

    function addToWhitelist(address _who) public auth {
        whitelisted[_who] = true;
        emit ListAddition(_who);
    }

    function removeFromWhitelist(address _who) public auth {
        whitelisted[_who] = false;
        emit ListRemoval(_who);
    }

    function batchAddToWhitelist(address[] memory _members) public auth {
        for (uint i = 0; i < _members.length; i++) {
            addToWhitelist(_members[i]);
        }
    }

    function batchRemoveFromWhitelist(address[] memory _members) public auth {
        for (uint i = 0; i < _members.length; i++) {
            removeFromWhitelist(_members[i]);
        }
    }

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) {
        return whitelisted[addresses[0]];
    }

    function position() external pure returns (Applied) { return Applied.pre; }
    function identifier() external pure returns (string memory) { return 'UserWhitelist'; }
}

File 46 of 77 : IPolicy.sol
pragma solidity 0.6.1;

interface IPolicy {
    enum Applied { pre, post }

    // In Trading context:
    // addresses: Order maker, Order taker, Order maker asset, Order taker asset, Exchange address
    // values: Maker token quantity, Taker token quantity, Fill Taker Quantity

    // In Participation context:
    // address[0]: Investor address, address[3]: Investment asset
    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool);

    function position() external view returns (Applied);
    function identifier() external view returns (string memory);
}

File 47 of 77 : IPolicyManager.sol
pragma solidity 0.6.1;


interface IPolicyManagerFactory {
    function createInstance(address _hub) external returns (address);
}

File 48 of 77 : PolicyManager.sol
pragma solidity 0.6.1;

import "../../factory/Factory.sol";
import "../hub/Spoke.sol";
import "./IPolicy.sol";

contract PolicyManager is Spoke {

    event Registration(
        bytes4 indexed sig,
        IPolicy.Applied position,
        address indexed policy
    );

    struct Entry {
        IPolicy[] pre;
        IPolicy[] post;
    }

    mapping(bytes4 => Entry) policies;

    constructor (address _hub) public Spoke(_hub) {}

    function register(bytes4 sig, address _policy) public auth {
        IPolicy.Applied position = IPolicy(_policy).position();
        if (position == IPolicy.Applied.pre) {
            policies[sig].pre.push(IPolicy(_policy));
        } else if (position == IPolicy.Applied.post) {
            policies[sig].post.push(IPolicy(_policy));
        } else {
            revert("Only pre and post allowed");
        }
        emit Registration(sig, position, _policy);
    }

    function batchRegister(bytes4[] memory sig, address[] memory _policies) public auth {
        require(sig.length == _policies.length, "Arrays lengths unequal");
        for (uint i = 0; i < sig.length; i++) {
            register(sig[i], _policies[i]);
        }
    }

    function PoliciesToAddresses(IPolicy[] storage _policies) internal view returns (address[] memory) {
        address[] memory res = new address[](_policies.length);
        for(uint i = 0; i < _policies.length; i++) {
            res[i] = address(_policies[i]);
        }
        return res;
    }

    function getPoliciesBySig(bytes4 sig) public view returns (address[] memory, address[] memory) {
        return (PoliciesToAddresses(policies[sig].pre), PoliciesToAddresses(policies[sig].post));
    }

    modifier isValidPolicyBySig(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) {
        preValidate(sig, addresses, values, identifier);
        _;
        postValidate(sig, addresses, values, identifier);
    }

    modifier isValidPolicy(address[5] memory addresses, uint[3] memory values, bytes32 identifier) {
        preValidate(msg.sig, addresses, values, identifier);
        _;
        postValidate(msg.sig, addresses, values, identifier);
    }

    function preValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public {
        validate(policies[sig].pre, sig, addresses, values, identifier);
    }

    function postValidate(bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) public {
        validate(policies[sig].post, sig, addresses, values, identifier);
    }

    function validate(IPolicy[] storage aux, bytes4 sig, address[5] memory addresses, uint[3] memory values, bytes32 identifier) internal {
        for(uint i = 0; i < aux.length; i++) {
            require(
                aux[i].rule(sig, addresses, values, identifier),
                string(abi.encodePacked("Rule evaluated to false: ", aux[i].identifier()))
            );
        }
    }
}

contract PolicyManagerFactory is Factory {
    function createInstance(address _hub) external returns (address) {
        address policyManager = address(new PolicyManager(_hub));
        childExists[policyManager] = true;
        emit NewInstance(_hub, policyManager);
        return policyManager;
    }
}

File 49 of 77 : AssetBlacklist.sol
pragma solidity 0.6.1;

import "../AddressList.sol";
import "../TradingSignatures.sol";

/// @notice Assets can be added but not removed from blacklist
contract AssetBlacklist is TradingSignatures, AddressList {
    enum Applied { pre, post }

    // bytes4 constant public MAKE_ORDER = 0x79705be7; // makeOrderSignature
    // bytes4 constant public TAKE_ORDER = 0xe51be6e8; // takeOrderSignature

    constructor(address[] memory _assets) AddressList(_assets) public {}

    function addToBlacklist(address _asset) external auth {
        require(!isMember(_asset), "Asset already in blacklist");
        list[_asset] = true;
        mirror.push(_asset);
    }

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) {
        address incomingToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3];
        return !isMember(incomingToken);
    }

    function position() external pure returns (Applied) { return Applied.pre; }
    function identifier() external pure returns (string memory) { return 'AssetBlacklist'; }
}

File 50 of 77 : AssetWhitelist.sol
pragma solidity 0.6.1;

import "../AddressList.sol";
import "../TradingSignatures.sol";

/// @notice Assets can be removed from but not added to whitelist
contract AssetWhitelist is TradingSignatures, AddressList {
    enum Applied { pre, post }

    constructor(address[] memory _assets) public AddressList(_assets) {}

    function removeFromWhitelist(address _asset) external auth {
        require(isMember(_asset), "Asset not in whitelist");
        delete list[_asset];
        uint i = getAssetIndex(_asset);
        for (i; i < mirror.length-1; i++){
            mirror[i] = mirror[i+1];
        }
        mirror.pop();
    }

    function getAssetIndex(address _asset) public view returns (uint) {
        for (uint i = 0; i < mirror.length; i++) {
            if (mirror[i] == _asset) { return i; }
        }
    }

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) {
        address incomingToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3];
        return isMember(incomingToken);
    }

    function position() external pure returns (Applied) { return Applied.pre; }
    function identifier() external pure returns (string memory) { return 'AssetWhitelist'; }
}

File 51 of 77 : MaxConcentration.sol
pragma solidity 0.6.1;

import "../../../dependencies/DSMath.sol";
import "../../../prices/IPriceSource.sol";
import "../../accounting/Accounting.sol";
import "../../trading/Trading.sol";
import "../TradingSignatures.sol";
import "../../../prices/IPriceSource.sol";

contract MaxConcentration is TradingSignatures, DSMath {
    enum Applied { pre, post }

    uint internal constant ONE_HUNDRED_PERCENT = 10 ** 18;  // 100%
    uint public maxConcentration;

    constructor(uint _maxConcentration) public {
        require(
            _maxConcentration <= ONE_HUNDRED_PERCENT,
            "Max concentration cannot exceed 100%"
        );
        maxConcentration = _maxConcentration;
    }

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier)
        external
        returns (bool)
    {
        Accounting accounting = Accounting(Hub(Trading(msg.sender).hub()).accounting());
        address denominationAsset = accounting.DENOMINATION_ASSET();
        // Max concentration is only checked for non-quote assets
        address takerToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3];
        if (denominationAsset == takerToken) { return true; }

        uint concentration;
        uint totalGav = accounting.calcGav();
        if (sig == MAKE_ORDER) {
            IPriceSource priceSource = IPriceSource(Hub(Trading(msg.sender).hub()).priceSource());
            address makerToken = addresses[2];
            uint makerQuantiyBeingTraded = values[0];
            uint takerQuantityBeingTraded = values[1];

            uint takerTokenGavBeingTraded = priceSource.convertQuantity(
                takerQuantityBeingTraded, takerToken, denominationAsset
            );

            uint makerTokenGavBeingTraded;
            if (makerToken == denominationAsset) {
                makerTokenGavBeingTraded = makerQuantiyBeingTraded;
            }
            else {
                makerTokenGavBeingTraded = priceSource.convertQuantity(
                    makerQuantiyBeingTraded, makerToken, denominationAsset
                );
            }
            concentration = _calcConcentration(
                add(accounting.calcAssetGAV(takerToken), takerTokenGavBeingTraded),
                add(takerTokenGavBeingTraded, sub(totalGav, makerTokenGavBeingTraded))
            );
        }
        else {
            concentration = _calcConcentration(
                accounting.calcAssetGAV(takerToken),
                totalGav
            );
        }
        return concentration <= maxConcentration;
    }

    function position() external pure returns (Applied) { return Applied.post; }
    function identifier() external pure returns (string memory) { return 'MaxConcentration'; }

    function _calcConcentration(uint assetGav, uint totalGav) internal returns (uint) {
        return mul(assetGav, ONE_HUNDRED_PERCENT) / totalGav;
    }
}

File 52 of 77 : MaxPositions.sol
pragma solidity 0.6.1;

import "../../accounting/Accounting.sol";
import "../../trading/Trading.sol";
import "../TradingSignatures.sol";

contract MaxPositions is TradingSignatures {
    enum Applied { pre, post }

    uint public maxPositions;

    /// @dev _maxPositions = 10 means max 10 different asset tokens
    /// @dev _maxPositions = 0 means no asset tokens are investable
    constructor(uint _maxPositions) public { maxPositions = _maxPositions; }

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier)
        external
        returns (bool)
    {
        Accounting accounting = Accounting(Hub(Trading(msg.sender).hub()).accounting());
        address denominationAsset = accounting.DENOMINATION_ASSET();
        // Always allow a trade INTO the quote asset
        address incomingToken = (sig == TAKE_ORDER) ? addresses[2] : addresses[3];
        if (denominationAsset == incomingToken) { return true; }
        return accounting.getOwnedAssetsLength() <= maxPositions;
    }

    function position() external pure returns (Applied) { return Applied.post; }
    function identifier() external pure returns (string memory) { return 'MaxPositions'; }
}

File 53 of 77 : PriceTolerance.sol
pragma solidity 0.6.1;

import "../../hub/Hub.sol";
import "../../../prices/IPriceSource.sol";
import "../TradingSignatures.sol";
import "../../../dependencies/DSMath.sol";
import "../../trading/Trading.sol";
import "../../../exchanges/interfaces/IOasisDex.sol";

contract PriceTolerance is TradingSignatures, DSMath {
    enum Applied { pre, post }

    uint256 public tolerance;

    uint256 constant MULTIPLIER = 10 ** 16; // to give effect of a percentage
    uint256 constant DIVISOR = 10 ** 18;

    // _tolerance: 10 equals to 10% of tolerance
    constructor(uint256 _tolerancePercent) public {
        require(_tolerancePercent <= 100, "Tolerance range is 0% - 100%");
        tolerance = mul(_tolerancePercent, MULTIPLIER);
    }

    /// @notice Taken from OpenZeppelin (https://git.io/fhQqo)
   function signedSafeSub(int256 _a, int256 _b) internal pure returns (int256) {
        int256 c = _a - _b;
        require((_b >= 0 && c <= _a) || (_b < 0 && c > _a));

        return c;
    }

    function checkPriceToleranceTakeOrder(
        address _makerAsset,
        address _takerAsset,
        uint256 _fillMakerQuantity,
        uint256 _fillTakerQuantity
    )
        internal
        view
        returns (bool)
    {
        IPriceSource pricefeed = IPriceSource(Hub(Trading(msg.sender).hub()).priceSource());
        uint256 referencePrice;
        (referencePrice,) = pricefeed.getReferencePriceInfo(_takerAsset, _makerAsset);

        uint256 orderPrice = pricefeed.getOrderPriceInfo(
            _takerAsset,
            _fillTakerQuantity,
            _fillMakerQuantity
        );

        return orderPrice >= sub(
            referencePrice,
            mul(tolerance, referencePrice) / DIVISOR
        );
    }

    function takeGenericOrder(
        address _makerAsset,
        address _takerAsset,
        uint256[3] memory _values
    ) public view returns (bool) {
        uint256 fillTakerQuantity = _values[2];
        uint256 fillMakerQuantity = mul(fillTakerQuantity, _values[0]) / _values[1];
        return checkPriceToleranceTakeOrder(
            _makerAsset, _takerAsset, fillMakerQuantity, fillTakerQuantity
        );
    }

    function takeOasisDex(
        address _exchange,
        bytes32 _identifier,
        uint256 _fillTakerQuantity
    ) public view returns (bool) {
        uint256 maxMakerQuantity;
        address makerAsset;
        uint256 maxTakerQuantity;
        address takerAsset;
        (
            maxMakerQuantity,
            makerAsset,
            maxTakerQuantity,
            takerAsset
        ) = IOasisDex(_exchange).getOffer(uint256(_identifier));

        uint256 fillMakerQuantity = mul(_fillTakerQuantity, maxMakerQuantity) / maxTakerQuantity;
        return checkPriceToleranceTakeOrder(
            makerAsset, takerAsset, fillMakerQuantity, _fillTakerQuantity
        );
    }

    function takeOrder(
        address[5] memory _addresses,
        uint256[3] memory _values,
        bytes32 _identifier
    ) public view returns (bool) {
        if (_identifier == 0x0) {
            return takeGenericOrder(_addresses[2], _addresses[3], _values);
        } else {
            return takeOasisDex(_addresses[4], _identifier, _values[2]);
        }
    }

    function makeOrder(
        address[5] memory _addresses,
        uint256[3] memory _values,
        bytes32 _identifier
    ) public view returns (bool) {
        IPriceSource pricefeed = IPriceSource(Hub(Trading(msg.sender).hub()).priceSource());

        uint256 ratio;
        (ratio,) = IPriceSource(pricefeed).getReferencePriceInfo(_addresses[2], _addresses[3]);
        uint256 value = IPriceSource(pricefeed).getOrderPriceInfo(_addresses[2], _values[0], _values[1]);

        int res = signedSafeSub(int(ratio), int(value));
        if (res < 0) {
            return true;
        } else {
            return wdiv(uint256(res), ratio) <= tolerance;
        }
    }

    function rule(
        bytes4 _sig,
        address[5] calldata _addresses,
        uint256[3] calldata _values,
        bytes32 _identifier
    ) external returns (bool) {
        if (_sig == MAKE_ORDER) {
            return makeOrder(_addresses, _values, _identifier);
        } else if (_sig == TAKE_ORDER) {
            return takeOrder(_addresses, _values, _identifier);
        }
        revert("Signature was neither MakeOrder nor TakeOrder");
    }

    function position() external pure returns (Applied) { return Applied.pre; }
    function identifier() external pure returns (string memory) { return 'PriceTolerance'; }
}

File 54 of 77 : TradingSignatures.sol
pragma solidity 0.6.1;

contract TradingSignatures {
    bytes4 constant public MAKE_ORDER = 0x5f08e909; // makeOrderSignature
    bytes4 constant public TAKE_ORDER = 0x63b24ef1; // takeOrderSignature
}

File 55 of 77 : IShares.sol
pragma solidity 0.6.1;

/// @notice Token representing ownership of the Fund
interface IShares {
    function createFor(address who, uint amount) external;
    function destroyFor(address who, uint amount) external;
}

interface ISharesFactory {
    function createInstance(address _hub) external returns (address);
}

File 56 of 77 : Shares.sol
pragma solidity 0.6.1;

import "../hub/Spoke.sol";
import "../../dependencies/token/StandardToken.sol";
import "../../factory/Factory.sol";

contract Shares is Spoke, StandardToken {
    string public symbol;
    string public name;
    uint8 public decimals;

    constructor(address _hub) public Spoke(_hub) {
        name = hub.name();
        symbol = "MLNF";
        decimals = 18;
    }

    function createFor(address who, uint amount) public auth {
        _mint(who, amount);
    }

    function destroyFor(address who, uint amount) public auth {
        _burn(who, amount);
    }

    function transfer(address to, uint amount) public override returns (bool) {
        revert("Unimplemented");
    }

    function transferFrom(
        address from,
        address to,
        uint amount
    )
        public
        override
        returns (bool)
    {
        revert("Unimplemented");
    }

    function approve(address spender, uint amount) public override returns (bool) {
        revert("Unimplemented");
    }

    function increaseApproval(
        address spender,
        uint amount
    )
        public
        override
        returns (bool)
    {
        revert("Unimplemented");
    }

    function decreaseApproval(
        address spender,
        uint amount
    )
        public
        override
        returns (bool)
    {
        revert("Unimplemented");
    }
}

contract SharesFactory is Factory {
    function createInstance(address _hub) external returns (address) {
        address shares = address(new Shares(_hub));
        childExists[shares] = true;
        emit NewInstance(_hub, shares);
        return shares;
    }
}

File 57 of 77 : ITrading.sol
pragma solidity 0.6.1;

pragma experimental ABIEncoderV2;

// TODO: Restore indexed params

/// @notice Mediation between a Fund and exchanges
interface ITrading {
    function callOnExchange(
        uint exchangeIndex,
        string calldata methodSignature,
        address[8] calldata orderAddresses,
        uint[8] calldata orderValues,
        bytes[4] calldata orderData,
        bytes32 identifier,
        bytes calldata signature
    ) external;

    function addOpenMakeOrder(
        address ofExchange,
        address ofSellAsset,
        address ofBuyAsset,
        address ofFeeAsset,
        uint orderId,
        uint expiryTime
    ) external;

    function removeOpenMakeOrder(
        address ofExchange,
        address ofSellAsset
    ) external;

    function updateAndGetQuantityBeingTraded(address _asset) external returns (uint256);
    function getOpenMakeOrdersAgainstAsset(address _asset) external view returns (uint256);
}

interface ITradingFactory {
     function createInstance(
        address _hub,
        address[] calldata _exchanges,
        address[] calldata _adapters,
        address _registry
    ) external returns (address);
}

File 58 of 77 : Trading.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../hub/Spoke.sol";
import "../vault/Vault.sol";
import "../policies/PolicyManager.sol";
import "../policies/TradingSignatures.sol";
import "../../factory/Factory.sol";
import "../../dependencies/DSMath.sol";
import "../../exchanges/ExchangeAdapter.sol";
import "../../exchanges/interfaces/IZeroExV2.sol";
import "../../exchanges/interfaces/IZeroExV3.sol";
import "../../version/Registry.sol";
import "../../dependencies/TokenUser.sol";

contract Trading is DSMath, TokenUser, Spoke, TradingSignatures {
    event ExchangeMethodCall(
        address indexed exchangeAddress,
        string indexed methodSignature,
        address[8] orderAddresses,
        uint[8] orderValues,
        bytes[4] orderData,
        bytes32 identifier,
        bytes signature
    );

    struct Exchange {
        address exchange;
        address adapter;
        bool takesCustody;
    }

    enum UpdateType { make, take, cancel }

    struct Order {
        address exchangeAddress;
        bytes32 orderId;
        UpdateType updateType;
        address makerAsset;
        address takerAsset;
        uint makerQuantity;
        uint takerQuantity;
        uint timestamp;
        uint fillTakerQuantity;
    }

    struct OpenMakeOrder {
        uint id; // Order Id from exchange
        uint expiresAt; // Timestamp when the order expires
        uint orderIndex; // Index of the order in the orders array
        address buyAsset; // Address of the buy asset in the order
        address feeAsset;
    }

    Exchange[] public exchanges;
    Order[] public orders;
    mapping (address => bool) public adapterIsAdded;
    mapping (address => mapping(address => OpenMakeOrder)) public exchangesToOpenMakeOrders;
    mapping (address => uint) public openMakeOrdersAgainstAsset;
    mapping (address => uint) public openMakeOrdersUsingAssetAsFee;
    mapping (address => bool) public isInOpenMakeOrder;
    mapping (address => uint) public makerAssetCooldown;
    mapping (bytes32 => IZeroExV2.Order) internal orderIdToZeroExV2Order;
    mapping (bytes32 => IZeroExV3.Order) internal orderIdToZeroExV3Order;

    uint public constant ORDER_LIFESPAN = 1 days;
    uint public constant MAKE_ORDER_COOLDOWN = 30 minutes;

    modifier delegateInternal() {
        require(msg.sender == address(this), "Sender is not this contract");
        _;
    }

    constructor(
        address _hub,
        address[] memory _exchanges,
        address[] memory _adapters,
        address _registry
    )
        public
        Spoke(_hub)
    {
        routes.registry = _registry;
        require(_exchanges.length == _adapters.length, "Array lengths unequal");
        for (uint i = 0; i < _exchanges.length; i++) {
            _addExchange(_exchanges[i], _adapters[i]);
        }
    }

    /// @notice Receive ether function (used to receive ETH from WETH)
    receive() external payable {}

    function addExchange(address _exchange, address _adapter) external auth {
        _addExchange(_exchange, _adapter);
    }

    function _addExchange(
        address _exchange,
        address _adapter
    ) internal {
        require(!adapterIsAdded[_adapter], "Adapter already added");
        adapterIsAdded[_adapter] = true;
        Registry registry = Registry(routes.registry);
        require(
            registry.exchangeAdapterIsRegistered(_adapter),
            "Adapter is not registered"
        );

        address registeredExchange;
        bool takesCustody;
        (registeredExchange, takesCustody) = registry.getExchangeInformation(_adapter);

        require(
            registeredExchange == _exchange,
            "Exchange and adapter do not match"
        );
        exchanges.push(Exchange(_exchange, _adapter, takesCustody));
    }

    /// @notice Universal method for calling exchange functions through adapters
    /// @notice See adapter contracts for parameters needed for each exchange
    /// @param exchangeIndex Index of the exchange in the "exchanges" array
    /// @param orderAddresses [0] Order maker
    /// @param orderAddresses [1] Order taker
    /// @param orderAddresses [2] Order maker asset
    /// @param orderAddresses [3] Order taker asset
    /// @param orderAddresses [4] feeRecipientAddress
    /// @param orderAddresses [5] senderAddress
    /// @param orderAddresses [6] maker fee asset
    /// @param orderAddresses [7] taker fee asset
    /// @param orderValues [0] makerAssetAmount
    /// @param orderValues [1] takerAssetAmount
    /// @param orderValues [2] Maker fee
    /// @param orderValues [3] Taker fee
    /// @param orderValues [4] expirationTimeSeconds
    /// @param orderValues [5] Salt/nonce
    /// @param orderValues [6] Fill amount: amount of taker token to be traded
    /// @param orderValues [7] Dexy signature mode
    /// @param orderData [0] Encoded data specific to maker asset
    /// @param orderData [1] Encoded data specific to taker asset
    /// @param orderData [2] Encoded data specific to maker asset fee
    /// @param orderData [3] Encoded data specific to taker asset fee
    /// @param identifier Order identifier
    /// @param signature Signature of order maker
    function callOnExchange(
        uint exchangeIndex,
        string memory methodSignature,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    )
        public
        onlyInitialized
    {
        bytes4 methodSelector = bytes4(keccak256(bytes(methodSignature)));
        require(
            Registry(routes.registry).adapterMethodIsAllowed(
                exchanges[exchangeIndex].adapter,
                methodSelector
            ),
            "Adapter method not allowed"
        );
        PolicyManager(routes.policyManager).preValidate(methodSelector, [orderAddresses[0], orderAddresses[1], orderAddresses[2], orderAddresses[3], exchanges[exchangeIndex].exchange], [orderValues[0], orderValues[1], orderValues[6]], identifier);
        if (
            methodSelector == MAKE_ORDER ||
            methodSelector == TAKE_ORDER
        ) {
            require(Registry(routes.registry).assetIsRegistered(
                orderAddresses[2]), 'Maker asset not registered'
            );
            require(Registry(routes.registry).assetIsRegistered(
                orderAddresses[3]), 'Taker asset not registered'
            );
            if (orderAddresses[6] != address(0) && methodSelector == MAKE_ORDER) {
                require(
                    Registry(routes.registry).assetIsRegistered(orderAddresses[6]),
                    'Maker fee asset not registered'
                );
            }
            if (orderAddresses[7] != address(0) && methodSelector == TAKE_ORDER) {
                require(
                    Registry(routes.registry).assetIsRegistered(orderAddresses[7]),
                    'Taker fee asset not registered'
                );
            }
        }
        (bool success, bytes memory returnData) = exchanges[exchangeIndex].adapter.delegatecall(
            abi.encodeWithSignature(
                methodSignature,
                exchanges[exchangeIndex].exchange,
                orderAddresses,
                orderValues,
                orderData,
                identifier,
                signature
            )
        );
        require(success, string(returnData));
        PolicyManager(routes.policyManager).postValidate(methodSelector, [orderAddresses[0], orderAddresses[1], orderAddresses[2], orderAddresses[3], exchanges[exchangeIndex].exchange], [orderValues[0], orderValues[1], orderValues[6]], identifier);
        emit ExchangeMethodCall(
            exchanges[exchangeIndex].exchange,
            methodSignature,
            orderAddresses,
            orderValues,
            orderData,
            identifier,
            signature
        );
    }

    /// @dev Make sure this is called after orderUpdateHook in adapters
    function addOpenMakeOrder(
        address ofExchange,
        address sellAsset,
        address buyAsset,
        address feeAsset,
        uint orderId,
        uint expirationTime
    ) public delegateInternal {
        require(!isInOpenMakeOrder[sellAsset], "Asset already in open order");
        require(orders.length > 0, "No orders in array");

        // If expirationTime is 0, actualExpirationTime is set to ORDER_LIFESPAN from now
        uint actualExpirationTime = (expirationTime == 0) ? add(block.timestamp, ORDER_LIFESPAN) : expirationTime;

        require(
            actualExpirationTime <= add(block.timestamp, ORDER_LIFESPAN) &&
            actualExpirationTime > block.timestamp,
            "Expiry time greater than max order lifespan or has already passed"
        );
        isInOpenMakeOrder[sellAsset] = true;
        makerAssetCooldown[sellAsset] = add(actualExpirationTime, MAKE_ORDER_COOLDOWN);
        openMakeOrdersAgainstAsset[buyAsset] = add(openMakeOrdersAgainstAsset[buyAsset], 1);
        if (feeAsset != address(0)) {
            openMakeOrdersUsingAssetAsFee[feeAsset] = add(openMakeOrdersUsingAssetAsFee[feeAsset], 1);
        }
        exchangesToOpenMakeOrders[ofExchange][sellAsset].id = orderId;
        exchangesToOpenMakeOrders[ofExchange][sellAsset].expiresAt = actualExpirationTime;
        exchangesToOpenMakeOrders[ofExchange][sellAsset].orderIndex = sub(orders.length, 1);
        exchangesToOpenMakeOrders[ofExchange][sellAsset].buyAsset = buyAsset;
    }

    function _removeOpenMakeOrder(
        address exchange,
        address sellAsset
    ) internal {
        if (isInOpenMakeOrder[sellAsset]) {
            makerAssetCooldown[sellAsset] = add(block.timestamp, MAKE_ORDER_COOLDOWN);
            address buyAsset = exchangesToOpenMakeOrders[exchange][sellAsset].buyAsset;
            address feeAsset = exchangesToOpenMakeOrders[exchange][sellAsset].feeAsset;
            delete exchangesToOpenMakeOrders[exchange][sellAsset];
            openMakeOrdersAgainstAsset[buyAsset] = sub(openMakeOrdersAgainstAsset[buyAsset], 1);
            if (feeAsset != address(0)) {
                openMakeOrdersUsingAssetAsFee[feeAsset] = sub(openMakeOrdersUsingAssetAsFee[feeAsset], 1);
            }
        }
    }

    function removeOpenMakeOrder(
        address exchange,
        address sellAsset
    ) public delegateInternal {
        _removeOpenMakeOrder(exchange, sellAsset);
    }

    /// @dev Bit of Redundancy for now
    function addZeroExV2OrderData(
        bytes32 orderId,
        IZeroExV2.Order memory zeroExOrderData
    ) public delegateInternal {
        orderIdToZeroExV2Order[orderId] = zeroExOrderData;
    }
    function addZeroExV3OrderData(
        bytes32 orderId,
        IZeroExV3.Order memory zeroExOrderData
    ) public delegateInternal {
        orderIdToZeroExV3Order[orderId] = zeroExOrderData;
    }

    function orderUpdateHook(
        address ofExchange,
        bytes32 orderId,
        UpdateType updateType,
        address payable[2] memory orderAddresses,
        uint[3] memory orderValues
    ) public delegateInternal {
        // only save make/take
        if (updateType == UpdateType.make || updateType == UpdateType.take) {
            orders.push(Order({
                exchangeAddress: ofExchange,
                orderId: orderId,
                updateType: updateType,
                makerAsset: orderAddresses[0],
                takerAsset: orderAddresses[1],
                makerQuantity: orderValues[0],
                takerQuantity: orderValues[1],
                timestamp: block.timestamp,
                fillTakerQuantity: orderValues[2]
            }));
        }
    }

    function updateAndGetQuantityBeingTraded(address _asset) external returns (uint) {
        uint quantityHere = IERC20(_asset).balanceOf(address(this));
        return add(updateAndGetQuantityHeldInExchange(_asset), quantityHere);
    }

    function updateAndGetQuantityHeldInExchange(address ofAsset) public returns (uint) {
        uint totalSellQuantity; // quantity in custody across exchanges
        uint totalSellQuantityInApprove; // quantity of asset in approve (allowance) but not custody of exchange
        for (uint i; i < exchanges.length; i++) {
            uint256 orderId = exchangesToOpenMakeOrders[exchanges[i].exchange][ofAsset].id;
            if (orderId == 0) {
                continue;
            }
            address sellAsset;
            uint remainingSellQuantity;
            (sellAsset, , remainingSellQuantity, ) =
                ExchangeAdapter(exchanges[i].adapter)
                .getOrder(
                    exchanges[i].exchange,
                    orderId,
                    ofAsset
                );
            if (remainingSellQuantity == 0) {    // remove id if remaining sell quantity zero (closed)
                _removeOpenMakeOrder(exchanges[i].exchange, ofAsset);
            }
            totalSellQuantity = add(totalSellQuantity, remainingSellQuantity);
            if (!exchanges[i].takesCustody) {
                totalSellQuantityInApprove += remainingSellQuantity;
            }
        }
        if (totalSellQuantity == 0) {
            isInOpenMakeOrder[ofAsset] = false;
        }
        return sub(totalSellQuantity, totalSellQuantityInApprove); // Since quantity in approve is not actually in custody
    }

    function returnBatchToVault(address[] calldata _tokens) external {
        for (uint i = 0; i < _tokens.length; i++) {
            returnAssetToVault(_tokens[i]);
        }
    }

    function returnAssetToVault(address _token) public {
        require(
            msg.sender == address(this) ||
            msg.sender == hub.manager() ||
            hub.isShutDown()            ||
            (!isInOpenMakeOrder[_token] && openMakeOrdersUsingAssetAsFee[_token] == 0),
            "returnAssetToVault: No return condition was met"
        );
        safeTransfer(_token, routes.vault, IERC20(_token).balanceOf(address(this)));
    }

    function getExchangeInfo() public view returns (address[] memory, address[] memory, bool[] memory) {
        address[] memory ofExchanges = new address[](exchanges.length);
        address[] memory ofAdapters = new address[](exchanges.length);
        bool[] memory takesCustody = new bool[](exchanges.length);
        for (uint i = 0; i < exchanges.length; i++) {
            ofExchanges[i] = exchanges[i].exchange;
            ofAdapters[i] = exchanges[i].adapter;
            takesCustody[i] = exchanges[i].takesCustody;
        }
        return (ofExchanges, ofAdapters, takesCustody);
    }

    function getOpenOrderInfo(address ofExchange, address ofAsset) public view returns (uint, uint, uint) {
        OpenMakeOrder memory order = exchangesToOpenMakeOrders[ofExchange][ofAsset];
        return (order.id, order.expiresAt, order.orderIndex);
    }

    function isOrderExpired(address exchange, address asset) public view returns(bool) {
        return (
            exchangesToOpenMakeOrders[exchange][asset].expiresAt <= block.timestamp &&
            exchangesToOpenMakeOrders[exchange][asset].expiresAt > 0
        );
    }

    function getOrderDetails(uint orderIndex) public view returns (address, address, uint, uint) {
        Order memory order = orders[orderIndex];
        return (order.makerAsset, order.takerAsset, order.makerQuantity, order.takerQuantity);
    }

    function getZeroExV2OrderDetails(bytes32 orderId) public view returns (IZeroExV2.Order memory) {
        return orderIdToZeroExV2Order[orderId];
    }

    function getZeroExV3OrderDetails(bytes32 orderId) public view returns (IZeroExV3.Order memory) {
        return orderIdToZeroExV3Order[orderId];
    }

    function getOpenMakeOrdersAgainstAsset(address _asset) external view returns (uint256) {
        return openMakeOrdersAgainstAsset[_asset];
    }
}

contract TradingFactory is Factory {
    event NewInstance(
        address indexed hub,
        address indexed instance,
        address[] exchanges,
        address[] adapters,
        address registry
    );

    function createInstance(
        address _hub,
        address[] memory _exchanges,
        address[] memory _adapters,
        address _registry
    ) public returns (address) {
        address trading = address(new Trading(_hub, _exchanges, _adapters, _registry));
        childExists[trading] = true;
        emit NewInstance(
            _hub,
            trading,
            _exchanges,
            _adapters,
            _registry
        );
        return trading;
    }
}

File 59 of 77 : IVault.sol
pragma solidity 0.6.1;

/// @notice Custody component
interface IVault {
    function withdraw(address token, uint amount) external;
}

interface IVaultFactory {
    function createInstance(address _hub) external returns (address);
}

File 60 of 77 : Vault.sol
pragma solidity 0.6.1;

import "../hub/Spoke.sol";
import "../../factory/Factory.sol";
import "../../dependencies/TokenUser.sol";

/// @notice Dumb custody component
contract Vault is TokenUser, Spoke {

    constructor(address _hub) public Spoke(_hub) {}

    function withdraw(address token, uint amount) external auth {
        safeTransfer(token, msg.sender, amount);
    }
}

contract VaultFactory is Factory {
    function createInstance(address _hub) external returns (address) {
        address vault = address(new Vault(_hub));
        childExists[vault] = true;
        emit NewInstance(_hub, vault);
        return vault;
    }
}

File 61 of 77 : IPriceSource.sol
pragma solidity 0.6.1;

/// @notice Must return a value for an asset
interface IPriceSource {
    function getQuoteAsset() external view returns (address);
    function getLastUpdate() external view returns (uint);

    /// @notice Returns false if asset not applicable, or price not recent
    function hasValidPrice(address) external view returns (bool);
    function hasValidPrices(address[] calldata) external view returns (bool);

    /// @notice Return the last known price, and when it was issued
    function getPrice(address _asset) external view returns (uint price, uint timestamp);
    function getPrices(address[] calldata _assets) external view returns (uint[] memory prices, uint[] memory timestamps);

    /// @notice Get price info, and revert if not valid
    function getPriceInfo(address _asset) external view returns (uint price, uint decimals);
    function getInvertedPriceInfo(address ofAsset) external view returns (uint price, uint decimals);

    function getReferencePriceInfo(address _base, address _quote) external view returns (uint referencePrice, uint decimal);
    function getOrderPriceInfo(address sellAsset, uint sellQuantity, uint buyQuantity) external view returns (uint orderPrice);
    function existsPriceOnAssetPair(address sellAsset, address buyAsset) external view returns (bool isExistent);
    function convertQuantity(
        uint fromAssetQuantity,
        address fromAsset,
        address toAsset
    ) external view returns (uint);
}

File 62 of 77 : IVersion.sol
pragma solidity 0.6.1;

interface IVersion {
    function shutDownFund(address) external;
}

File 63 of 77 : Registry.sol
pragma solidity 0.6.1;

import "../dependencies/DSAuth.sol";
import "../fund/hub/Hub.sol";
import "../dependencies/token/IERC20.sol";

contract Registry is DSAuth {

    // EVENTS
    event AssetUpsert (
        address indexed asset,
        string name,
        string symbol,
        uint decimals,
        string url,
        uint reserveMin,
        uint[] standards,
        bytes4[] sigs
    );

    event ExchangeAdapterUpsert (
        address indexed exchange,
        address indexed adapter,
        bool takesCustody,
        bytes4[] sigs
    );

    event AssetRemoval (address indexed asset);
    event EfxWrapperRegistryChange(address indexed registry);
    event EngineChange(address indexed engine);
    event ExchangeAdapterRemoval (address indexed exchange);
    event IncentiveChange(uint incentiveAmount);
    event MGMChange(address indexed MGM);
    event MlnTokenChange(address indexed mlnToken);
    event NativeAssetChange(address indexed nativeAsset);
    event PriceSourceChange(address indexed priceSource);
    event VersionRegistration(address indexed version);

    // TYPES
    struct Asset {
        bool exists;
        string name;
        string symbol;
        uint decimals;
        string url;
        uint reserveMin;
        uint[] standards;
        bytes4[] sigs;
    }

    struct Exchange {
        bool exists;
        address exchangeAddress;
        bool takesCustody;
        bytes4[] sigs;
    }

    struct Version {
        bool exists;
        bytes32 name;
    }

    // CONSTANTS
    uint public constant MAX_REGISTERED_ENTITIES = 20;
    uint public constant MAX_FUND_NAME_BYTES = 66;

    // FIELDS
    mapping (address => Asset) public assetInformation;
    address[] public registeredAssets;

    // Mapping from adapter address to exchange Information (Adapters are unique)
    mapping (address => Exchange) public exchangeInformation;
    address[] public registeredExchangeAdapters;

    mapping (address => Version) public versionInformation;
    address[] public registeredVersions;

    mapping (address => bool) public isFeeRegistered;

    mapping (address => address) public fundsToVersions;
    mapping (bytes32 => bool) public versionNameExists;
    mapping (bytes32 => address) public fundNameHashToOwner;


    uint public incentive = 10 finney;
    address public priceSource;
    address public mlnToken;
    address public nativeAsset;
    address public engine;
    address public ethfinexWrapperRegistry;
    address public MGM;

    modifier onlyVersion() {
        require(
            versionInformation[msg.sender].exists,
            "Only a Version can do this"
        );
        _;
    }

    // METHODS

    constructor(address _postDeployOwner) public {
        setOwner(_postDeployOwner);
    }

    // PUBLIC METHODS

    /// @notice Whether _name has only valid characters
    function isValidFundName(string memory _name) public pure returns (bool) {
        bytes memory b = bytes(_name);
        if (b.length > MAX_FUND_NAME_BYTES) return false;
        for (uint i; i < b.length; i++){
            bytes1 char = b[i];
            if(
                !(char >= 0x30 && char <= 0x39) && // 9-0
                !(char >= 0x41 && char <= 0x5A) && // A-Z
                !(char >= 0x61 && char <= 0x7A) && // a-z
                !(char == 0x20 || char == 0x2D) && // space, dash
                !(char == 0x2E || char == 0x5F) && // period, underscore
                !(char == 0x2A) // *
            ) {
                return false;
            }
        }
        return true;
    }

    /// @notice Whether _user can use _name for their fund
    function canUseFundName(address _user, string memory _name) public view returns (bool) {
        bytes32 nameHash = keccak256(bytes(_name));
        return (
            isValidFundName(_name) &&
            (
                fundNameHashToOwner[nameHash] == address(0) ||
                fundNameHashToOwner[nameHash] == _user
            )
        );
    }

    function reserveFundName(address _owner, string calldata _name)
        external
        onlyVersion
    {
        require(canUseFundName(_owner, _name), "Fund name cannot be used");
        fundNameHashToOwner[keccak256(bytes(_name))] = _owner;
    }

    function registerFund(address _fund, address _owner, string calldata _name)
        external
        onlyVersion
    {
        require(canUseFundName(_owner, _name), "Fund name cannot be used");
        fundsToVersions[_fund] = msg.sender;
    }

    /// @notice Registers an Asset information entry
    /// @dev Pre: Only registrar owner should be able to register
    /// @dev Post: Address _asset is registered
    /// @param _asset Address of asset to be registered
    /// @param _name Human-readable name of the Asset
    /// @param _symbol Human-readable symbol of the Asset
    /// @param _url Url for extended information of the asset
    /// @param _standards Integers of EIP standards this asset adheres to
    /// @param _sigs Function signatures for whitelisted asset functions
    function registerAsset(
        address _asset,
        string calldata _name,
        string calldata _symbol,
        string calldata _url,
        uint _reserveMin,
        uint[] calldata _standards,
        bytes4[] calldata _sigs
    ) external auth {
        require(registeredAssets.length < MAX_REGISTERED_ENTITIES);
        require(!assetInformation[_asset].exists);
        assetInformation[_asset].exists = true;
        registeredAssets.push(_asset);
        updateAsset(
            _asset,
            _name,
            _symbol,
            _url,
            _reserveMin,
            _standards,
            _sigs
        );
    }

    /// @notice Register an exchange information entry (A mapping from exchange adapter -> Exchange information)
    /// @dev Adapters are unique so are used as the mapping key. There may be different adapters for same exchange (0x / Ethfinex)
    /// @dev Pre: Only registrar owner should be able to register
    /// @dev Post: Address _exchange is registered
    /// @param _exchange Address of the exchange for the adapter
    /// @param _adapter Address of exchange adapter
    /// @param _takesCustody Whether this exchange takes custody of tokens before trading
    /// @param _sigs Function signatures for whitelisted exchange functions
    function registerExchangeAdapter(
        address _exchange,
        address _adapter,
        bool _takesCustody,
        bytes4[] calldata _sigs
    ) external auth {
        require(!exchangeInformation[_adapter].exists, "Adapter already exists");
        exchangeInformation[_adapter].exists = true;
        require(registeredExchangeAdapters.length < MAX_REGISTERED_ENTITIES, "Exchange limit reached");
        registeredExchangeAdapters.push(_adapter);
        updateExchangeAdapter(
            _exchange,
            _adapter,
            _takesCustody,
            _sigs
        );
    }

    /// @notice Versions cannot be removed from registry
    /// @param _version Address of the version contract
    /// @param _name Name of the version
    function registerVersion(
        address _version,
        bytes32 _name
    ) external auth {
        require(!versionInformation[_version].exists, "Version already exists");
        require(!versionNameExists[_name], "Version name already exists");
        versionInformation[_version].exists = true;
        versionNameExists[_name] = true;
        versionInformation[_version].name = _name;
        registeredVersions.push(_version);
        emit VersionRegistration(_version);
    }

    function setIncentive(uint _weiAmount) external auth {
        incentive = _weiAmount;
        emit IncentiveChange(_weiAmount);
    }

    function setPriceSource(address _priceSource) external auth {
        priceSource = _priceSource;
        emit PriceSourceChange(_priceSource);
    }

    function setMlnToken(address _mlnToken) external auth {
        mlnToken = _mlnToken;
        emit MlnTokenChange(_mlnToken);
    }

    function setNativeAsset(address _nativeAsset) external auth {
        nativeAsset = _nativeAsset;
        emit NativeAssetChange(_nativeAsset);
    }

    function setEngine(address _engine) external auth {
        engine = _engine;
        emit EngineChange(_engine);
    }

    function setMGM(address _MGM) external auth {
        MGM = _MGM;
        emit MGMChange(_MGM);
    }

    function setEthfinexWrapperRegistry(address _registry) external auth {
        ethfinexWrapperRegistry = _registry;
        emit EfxWrapperRegistryChange(_registry);
    }

    /// @notice Updates description information of a registered Asset
    /// @dev Pre: Owner can change an existing entry
    /// @dev Post: Changed Name, Symbol, URL and/or IPFSHash
    /// @param _asset Address of the asset to be updated
    /// @param _name Human-readable name of the Asset
    /// @param _symbol Human-readable symbol of the Asset
    /// @param _url Url for extended information of the asset
    function updateAsset(
        address _asset,
        string memory _name,
        string memory _symbol,
        string memory _url,
        uint _reserveMin,
        uint[] memory _standards,
        bytes4[] memory _sigs
    ) public auth {
        require(assetInformation[_asset].exists);
        Asset storage asset = assetInformation[_asset];
        asset.name = _name;
        asset.symbol = _symbol;
        asset.decimals = ERC20WithFields(_asset).decimals();
        asset.url = _url;
        asset.reserveMin = _reserveMin;
        asset.standards = _standards;
        asset.sigs = _sigs;
        emit AssetUpsert(
            _asset,
            _name,
            _symbol,
            asset.decimals,
            _url,
            _reserveMin,
            _standards,
            _sigs
        );
    }

    function updateExchangeAdapter(
        address _exchange,
        address _adapter,
        bool _takesCustody,
        bytes4[] memory _sigs
    ) public auth {
        require(exchangeInformation[_adapter].exists, "Exchange with adapter doesn't exist");
        Exchange storage exchange = exchangeInformation[_adapter];
        exchange.exchangeAddress = _exchange;
        exchange.takesCustody = _takesCustody;
        exchange.sigs = _sigs;
        emit ExchangeAdapterUpsert(
            _exchange,
            _adapter,
            _takesCustody,
            _sigs
        );
    }

    /// @notice Deletes an existing entry
    /// @dev Owner can delete an existing entry
    /// @param _asset address for which specific information is requested
    function removeAsset(
        address _asset,
        uint _assetIndex
    ) external auth {
        require(assetInformation[_asset].exists);
        require(registeredAssets[_assetIndex] == _asset);
        delete assetInformation[_asset];
        delete registeredAssets[_assetIndex];
        for (uint i = _assetIndex; i < registeredAssets.length-1; i++) {
            registeredAssets[i] = registeredAssets[i+1];
        }
        registeredAssets.pop();
        emit AssetRemoval(_asset);
    }

    /// @notice Deletes an existing entry
    /// @dev Owner can delete an existing entry
    /// @param _adapter address of the adapter of the exchange that is to be removed
    /// @param _adapterIndex index of the exchange in array
    function removeExchangeAdapter(
        address _adapter,
        uint _adapterIndex
    ) external auth {
        require(exchangeInformation[_adapter].exists, "Exchange with adapter doesn't exist");
        require(registeredExchangeAdapters[_adapterIndex] == _adapter, "Incorrect adapter index");
        delete exchangeInformation[_adapter];
        delete registeredExchangeAdapters[_adapterIndex];
        for (uint i = _adapterIndex; i < registeredExchangeAdapters.length-1; i++) {
            registeredExchangeAdapters[i] = registeredExchangeAdapters[i+1];
        }
        registeredExchangeAdapters.pop();
        emit ExchangeAdapterRemoval(_adapter);
    }

    function registerFees(address[] calldata _fees) external auth {
        for (uint i; i < _fees.length; i++) {
            isFeeRegistered[_fees[i]] = true;
        }
    }

    function deregisterFees(address[] calldata _fees) external auth {
        for (uint i; i < _fees.length; i++) {
            delete isFeeRegistered[_fees[i]];
        }
    }

    // PUBLIC VIEW METHODS

    // get asset specific information
    function getName(address _asset) external view returns (string memory) {
        return assetInformation[_asset].name;
    }
    function getSymbol(address _asset) external view returns (string memory) {
        return assetInformation[_asset].symbol;
    }
    function getDecimals(address _asset) external view returns (uint) {
        return assetInformation[_asset].decimals;
    }
    function getReserveMin(address _asset) external view returns (uint) {
        return assetInformation[_asset].reserveMin;
    }
    function assetIsRegistered(address _asset) external view returns (bool) {
        return assetInformation[_asset].exists;
    }
    function getRegisteredAssets() external view returns (address[] memory) {
        return registeredAssets;
    }
    function assetMethodIsAllowed(address _asset, bytes4 _sig)
        external
        view
        returns (bool)
    {
        bytes4[] memory signatures = assetInformation[_asset].sigs;
        for (uint i = 0; i < signatures.length; i++) {
            if (signatures[i] == _sig) {
                return true;
            }
        }
        return false;
    }

    // get exchange-specific information
    function exchangeAdapterIsRegistered(address _adapter) external view returns (bool) {
        return exchangeInformation[_adapter].exists;
    }
    function getRegisteredExchangeAdapters() external view returns (address[] memory) {
        return registeredExchangeAdapters;
    }
    function getExchangeInformation(address _adapter)
        public
        view
        returns (address, bool)
    {
        Exchange memory exchange = exchangeInformation[_adapter];
        return (
            exchange.exchangeAddress,
            exchange.takesCustody
        );
    }
    function exchangeForAdapter(address _adapter) external view returns (address) {
        Exchange memory exchange = exchangeInformation[_adapter];
        return exchange.exchangeAddress;
    }
    function getAdapterFunctionSignatures(address _adapter)
        public
        view
        returns (bytes4[] memory)
    {
        return exchangeInformation[_adapter].sigs;
    }
    function adapterMethodIsAllowed(
        address _adapter, bytes4 _sig
    )
        external
        view
        returns (bool)
    {
        bytes4[] memory signatures = exchangeInformation[_adapter].sigs;
        for (uint i = 0; i < signatures.length; i++) {
            if (signatures[i] == _sig) {
                return true;
            }
        }
        return false;
    }

    // get version and fund information
    function getRegisteredVersions() external view returns (address[] memory) {
        return registeredVersions;
    }

    function isFund(address _who) external view returns (bool) {
        if (fundsToVersions[_who] != address(0)) {
            return true; // directly from a hub
        } else {
            Hub hub = Hub(Spoke(_who).hub());
            require(
                hub.isSpoke(_who),
                "Call from either a spoke or hub"
            );
            return fundsToVersions[address(hub)] != address(0);
        }
    }

    function isFundFactory(address _who) external view returns (bool) {
        return versionInformation[_who].exists;
    }
}

File 64 of 77 : Version.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "../factory/FundFactory.sol";
import "../fund/hub/Hub.sol";

/// @notice Controlled by governance
contract Version is FundFactory, DSAuth {

    constructor(
        address _accountingFactory,
        address _feeManagerFactory,
        address _participationFactory,
        address _sharesFactory,
        address _tradingFactory,
        address _vaultFactory,
        address _policyManagerFactory,
        address _registry,
        address _postDeployOwner
    )
        public
        FundFactory(
            _accountingFactory,
            _feeManagerFactory,
            _participationFactory,
            _sharesFactory,
            _tradingFactory,
            _vaultFactory,
            _policyManagerFactory,
            address(this)
        )
    {
        associatedRegistry = Registry(_registry);
        setOwner(_postDeployOwner);
    }

    function shutDownFund(address _hub) external {
        require(
            managersToHubs[msg.sender] == _hub,
            "Conditions not met for fund shutdown"
        );
        Hub(_hub).shutDownFund();
    }
}

File 65 of 77 : BooleanPolicy.sol
pragma solidity 0.6.1;

contract BooleanPolicy {
    enum Applied { pre, post }

    bool allowed;

    function rule(bytes4 sig, address[5] calldata addresses, uint[3] calldata values, bytes32 identifier) external returns (bool) {
        return allowed;
    }

    function position() external pure returns (Applied) { return Applied.pre; }
}

contract TruePolicy is BooleanPolicy {
    constructor() public { allowed = true; }
    function identifier() external pure returns (string memory) { return "TruePolicy"; }
}

contract FalsePolicy is BooleanPolicy {
    constructor() public { allowed = false; }
    function identifier() external pure returns (string memory) { return "FalsePolicy"; }
}

File 66 of 77 : MaliciousToken.sol
pragma solidity 0.6.1;

import "main/dependencies/token/PreminedToken.sol";

contract MaliciousToken is PreminedToken {

    bool public isReverting = false;

    constructor(string memory _symbol, uint8 _decimals, string memory _name)
        public
        PreminedToken(_symbol, _decimals, _name)
    {}

    function startReverting() public {
        isReverting = true;
    }

    function transfer(address _to, uint256 _value) public override returns (bool) {
        require(!isReverting, "I'm afraid I can't do that, Dave");
        super.transfer(_to, _value);
    }

    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
        public
        override
        returns (bool)
    {
        require(!isReverting, "I'm afraid I can't do that, Dave");
        super.transferFrom(_from, _to, _value);
    }
}

File 67 of 77 : MockAccounting.sol
pragma solidity 0.6.1;

import "main/fund/hub/Spoke.sol";

/// @dev Balances are fake and can be set by anyone (testing)
contract MockAccounting is Spoke {

    uint public gav;
    uint public nav;
    uint public unclaimedFees;
    uint public mockValuePerShare;

    address[] public ownedAssets;
    mapping (address => bool) public isInAssetList;
    mapping (address => uint) public held; // mock total held across all components
    mapping (address => uint) public assetGav;
    address public DENOMINATION_ASSET;
    address public NATIVE_ASSET;
    uint public DEFAULT_SHARE_PRICE;
    uint public SHARES_DECIMALS;

    constructor(address _hub, address _denominationAsset, address _nativeAsset)
        public
        Spoke(_hub)
    {
        DENOMINATION_ASSET = _denominationAsset;
        NATIVE_ASSET = _nativeAsset;
        SHARES_DECIMALS = 18;
        DEFAULT_SHARE_PRICE = 10 ** uint(SHARES_DECIMALS);
    }

    function setOwnedAssets(address[] memory _assets) public { ownedAssets = _assets; }
    function getOwnedAssetsLength() public view returns (uint) { return ownedAssets.length; }
    function setGav(uint _gav) public { gav = _gav; }
    function setNav(uint _nav) public { nav = _nav; }
    function setAssetGAV(address _asset, uint _amt) public { assetGav[_asset] = _amt; }
    function setFundHoldings(uint[] memory _amounts, address[] memory _assets) public {
        for (uint i = 0; i < _assets.length; i++) {
            held[_assets[i]] = _amounts[i];
        }
    }

    function getFundHoldings() public view returns (uint[] memory, address[] memory) {
        uint[] memory _quantities = new uint[](ownedAssets.length);
        address[] memory _assets = new address[](ownedAssets.length);
        for (uint i = 0; i < ownedAssets.length; i++) {
            address ofAsset = ownedAssets[i];
            // holdings formatting: mul(exchangeHoldings, 10 ** assetDecimal)
            uint quantityHeld = held[ofAsset];

            if (quantityHeld != 0) {
                _assets[i] = ofAsset;
                _quantities[i] = quantityHeld;
            }
        }
        return (_quantities, _assets);
    }

    function calcGav() public view returns (uint) { return gav; }
    function calcNav() public view returns (uint) { return nav; }

    function calcAssetGAV(address _a) public view returns (uint) { return assetGav[_a]; }

    function valuePerShare(uint totalValue, uint numShares) public view returns (uint) {
        return mockValuePerShare;
    }

    function performCalculations() public view returns (uint, uint, uint, uint, uint) {
        return (gav, unclaimedFees, 0, nav, mockValuePerShare);
    }
}

File 68 of 77 : MockAdapter.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "main/fund/trading/Trading.sol";
import "main/fund/accounting/Accounting.sol";
import "main/exchanges/ExchangeAdapter.sol";

contract MockAdapter is ExchangeAdapter {

    //  METHODS

    //  PUBLIC METHODS

    /// @notice Mock make order
    function makeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override {
        address makerAsset = orderAddresses[2];
        address takerAsset = orderAddresses[3];
        uint makerQuantity = orderValues[0];
        uint takerQuantity = orderValues[1];

        approveAsset(makerAsset, targetExchange, makerQuantity, "makerAsset");

        getTrading().orderUpdateHook(
            targetExchange,
            identifier,
            Trading.UpdateType.make,
            [payable(makerAsset), payable(takerAsset)],
            [makerQuantity, takerQuantity, uint(0)]
        );
        getTrading().addOpenMakeOrder(targetExchange, makerAsset, takerAsset, address(0), uint(identifier), 0);
    }

    /// @notice Mock take order
    function takeOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override {
        address makerAsset = orderAddresses[2];
        address takerAsset = orderAddresses[3];
        uint makerQuantity = orderValues[0];
        uint takerQuantity = orderValues[1];
        uint fillTakerQuantity = orderValues[6];

        approveAsset(takerAsset, targetExchange, fillTakerQuantity, "takerAsset");

        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(identifier),
            Trading.UpdateType.take,
            [payable(makerAsset), payable(takerAsset)],
            [makerQuantity, takerQuantity, fillTakerQuantity]
        );
    }

    /// @notice Mock cancel order
    function cancelOrder(
        address targetExchange,
        address[8] memory orderAddresses,
        uint[8] memory orderValues,
        bytes[4] memory orderData,
        bytes32 identifier,
        bytes memory signature
    ) public override {
        address makerAsset = orderAddresses[2];
        uint makerQuantity = orderValues[0];

        revokeApproveAsset(makerAsset, targetExchange, makerQuantity, "makerAsset");

        getTrading().removeOpenMakeOrder(targetExchange, makerAsset);
        getTrading().orderUpdateHook(
            targetExchange,
            bytes32(identifier),
            Trading.UpdateType.cancel,
            [address(0), address(0)],
            [uint(0), uint(0), uint(0)]
        );
    }
}

File 69 of 77 : MockFee.sol
pragma solidity 0.6.1;

contract MockFee {

    uint public fee;
    uint public FEE_RATE;
    uint public FEE_PERIOD;
    uint public feeNumber;

    constructor(uint _feeNumber) public {
        feeNumber = _feeNumber;
    }

    function setFeeAmount(uint amount) public {
        fee = amount;
    }

    function feeAmount() external returns (uint feeInShares) {
        return fee;
    }

    function initializeForUser(uint feeRate, uint feePeriod, address denominationAsset) external {
        fee = 0;
        FEE_RATE = feeRate;
        FEE_PERIOD = feePeriod;
    }

    function updateState() external {
        fee = 0;
    }

    function identifier() external view returns (uint) {
        return feeNumber;
    }
}

File 70 of 77 : MockFeeManager.sol
pragma solidity 0.6.1;
pragma experimental ABIEncoderV2;

import "main/fund/hub/Spoke.sol";
import "main/fund/shares/Shares.sol";
import "main/factory/Factory.sol";
import "main/dependencies/DSMath.sol";
import "main/engine/AmguConsumer.sol";

contract MockFeeManager is DSMath, Spoke, AmguConsumer {

    struct FeeInfo {
        address feeAddress;
        uint feeRate;
        uint feePeriod;
    }

    uint totalFees;
    uint performanceFees;

    constructor(
        address _hub,
        address _denominationAsset,
        address[] memory _fees,
        uint[] memory _periods,
        uint _rates,
        address registry
    ) Spoke(_hub) public {}

    function setTotalFeeAmount(uint _amt) public { totalFees = _amt; }
    function setPerformanceFeeAmount(uint _amt) public { performanceFees = _amt; }

    function rewardManagementFee() public { return; }
    function performanceFeeAmount() external returns (uint) { return performanceFees; }
    function totalFeeAmount() external returns (uint) { return totalFees; }
    function engine() public view override(AmguConsumer, Spoke) returns (address) { return routes.engine; }
    function mlnToken() public view override(AmguConsumer, Spoke) returns (address) { return routes.mlnToken; }
    function priceSource() public view override(AmguConsumer, Spoke) returns (address) { return hub.priceSource(); }
    function registry() public view override(AmguConsumer, Spoke) returns (address) { return routes.registry; }
}

File 71 of 77 : MockHub.sol
pragma solidity 0.6.1;

import "main/dependencies/DSGuard.sol";
import "main/fund/hub/Spoke.sol";
import "main/version/Registry.sol";

/// @notice Hub used for testing
contract MockHub is DSGuard {

    struct Routes {
        address accounting;
        address feeManager;
        address participation;
        address policyManager;
        address shares;
        address trading;
        address vault;
        address registry;
        address version;
        address engine;
        address mlnAddress;
    }
    Routes public routes;
    address public manager;
    string public name;
    bool public isShutDown;

    function setManager(address _manager) public { manager = _manager; }

    function setName(string memory _name) public { name = _name; }

    function shutDownFund() public { isShutDown = true; }

    function setShutDownState(bool _state) public { isShutDown = _state; }

    function setSpokes(address[11] memory _spokes) public {
        routes.accounting = _spokes[0];
        routes.feeManager = _spokes[1];
        routes.participation = _spokes[2];
        routes.policyManager = _spokes[3];
        routes.shares = _spokes[4];
        routes.trading = _spokes[5];
        routes.vault = _spokes[6];
        routes.registry = _spokes[7];
        routes.version = _spokes[8];
        routes.engine = _spokes[9];
        routes.mlnAddress = _spokes[10];
    }

    function setRouting() public {
        address[11] memory spokes = [
            routes.accounting, routes.feeManager, routes.participation,
            routes.policyManager, routes.shares, routes.trading,
            routes.vault, routes.registry, routes.version,
            routes.engine, routes.mlnAddress
        ];
        Spoke(routes.accounting).initialize(spokes);
        Spoke(routes.feeManager).initialize(spokes);
        Spoke(routes.participation).initialize(spokes);
        Spoke(routes.policyManager).initialize(spokes);
        Spoke(routes.shares).initialize(spokes);
        Spoke(routes.trading).initialize(spokes);
        Spoke(routes.vault).initialize(spokes);
    }

    function setPermissions() public {
        permit(routes.participation, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
        permit(routes.trading, routes.vault, bytes4(keccak256('withdraw(address,uint256)')));
        permit(routes.participation, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
        permit(routes.participation, routes.shares, bytes4(keccak256('destroyFor(address,uint256)')));
        permit(routes.feeManager, routes.shares, bytes4(keccak256('createFor(address,uint256)')));
        permit(routes.participation, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
        permit(routes.participation, routes.accounting, bytes4(keccak256('removeFromOwnedAssets(address)')));
        permit(routes.trading, routes.accounting, bytes4(keccak256('addAssetToOwnedAssets(address)')));
        permit(routes.trading, routes.accounting, bytes4(keccak256('removeFromOwnedAssets(address)')));
        permit(routes.accounting, routes.feeManager, bytes4(keccak256('rewardAllFees()')));
        permit(manager, routes.feeManager, bytes4(keccak256('register(address)')));
        permit(manager, routes.feeManager, bytes4(keccak256('batchRegister(address[])')));
        permit(manager, routes.policyManager, bytes4(keccak256('register(bytes4,address)')));
        permit(manager, routes.policyManager, bytes4(keccak256('batchRegister(bytes4[],address[])')));
        permit(manager, routes.participation, bytes4(keccak256('enableInvestment(address[])')));
        permit(manager, routes.participation, bytes4(keccak256('disableInvestment(address[])')));
        permit(bytes32(bytes20(msg.sender)), ANY, ANY);
    }

    function permitSomething(address _from, address _to, bytes4 _sig) public {
        permit(
            bytes32(bytes20(_from)),
            bytes32(bytes20(_to)),
            _sig
        );
    }

    function initializeSpoke(address _spoke) public {
        address[11] memory spokes = [
            routes.accounting, routes.feeManager, routes.participation,
            routes.policyManager, routes.shares, routes.trading,
            routes.vault, routes.registry, routes.version,
            routes.engine, routes.mlnAddress
        ];
        Spoke(_spoke).initialize(spokes);
    }

    function vault() public view returns (address) { return routes.vault; }
    function accounting() public view returns (address) { return routes.accounting; }
    function priceSource() public view returns (address) { return Registry(routes.registry).priceSource(); }
    function participation() public view returns (address) { return routes.participation; }
    function trading() public view returns (address) { return routes.trading; }
    function shares() public view returns (address) { return routes.shares; }
    function policyManager() public view returns (address) { return routes.policyManager; }
    function registry() public view returns (address) { return routes.registry; }
}

File 72 of 77 : MockRegistry.sol
pragma solidity 0.6.1;

import "main/dependencies/DSAuth.sol";

/// @dev Simplified for testing, and by default rigged to always return true
contract MockRegistry is DSAuth {

    bool public alwaysRegistered = true;
    bool public methodAllowed = true;

    address public priceSource;
    address public mlnToken;
    address public nativeAsset;
    address public engine;
    address public fundFactory;
    address[] public assets;
    uint public incentive;
    mapping (address => bool) public registered;
    mapping (address => bool) public fundExists;
    mapping (address => address) public exchangeForAdapter;
    mapping (address => bool) public takesCustodyForAdapter;


    function register(address _addr) public {
        registered[_addr] = true;
        assets.push(_addr);
    }

    function remove(address _addr) public {
        delete registered[_addr];
    }

    function assetIsRegistered(address _asset) public view returns (bool) {
        return alwaysRegistered || registered[_asset];
    }

    function exchangeAdapterIsRegistered(address _adapter) public view returns (bool) {
        return alwaysRegistered || registered[_adapter];
    }

    function registerExchangeAdapter(
        address _exchange,
        address _adapter
    ) public {
        exchangeForAdapter[_adapter] = _exchange;
        takesCustodyForAdapter[_adapter] = true;
    }

    function adapterMethodIsAllowed(
        address _adapter,
        bytes4 _sig
    ) public view returns (bool) { return methodAllowed; }

    function setPriceSource(address _a) public { priceSource = _a; }
    function setMlnToken(address _a) public { mlnToken = _a; }
    function setNativeAsset(address _a) public { nativeAsset = _a; }
    function setEngine(address _a) public { engine = _a; }
    function setFundFactory(address _a) public { fundFactory = _a; }
    function setIsFund(address _who) public { fundExists[_who] = true; }

    function isFund(address _who) public view returns (bool) { return fundExists[_who]; }
    function isFundFactory(address _who) public view returns (bool) {
        return _who == fundFactory;
    }
    function getRegisteredAssets() public view returns (address[] memory) { return assets; }
    function getReserveMin(address _asset) public view returns (uint) { return 0; }
    function isFeeRegistered(address _fee) public view returns (bool) {
        return alwaysRegistered;
    }
    function getExchangeInformation(address _adapter)
        public
        view
        returns (address, bool)
    {
        return (
            exchangeForAdapter[_adapter],
            takesCustodyForAdapter[_adapter]
        );
    }
}

File 73 of 77 : MockShares.sol
pragma solidity 0.6.1;

import "main/fund/hub/Spoke.sol";
import "main/dependencies/token/StandardToken.sol";

/// @dev Shares can be destroyed and created by anyone (testing)
contract MockShares is Spoke, StandardToken {
    string public symbol;
    string public name;
    uint8 public decimals;

    constructor(address _hub) public Spoke(_hub) {
        name = hub.name();
        symbol = "MOCK";
        decimals = 18;
    }

    function createFor(address who, uint amount) public {
        _mint(who, amount);
    }

    function destroyFor(address who, uint amount) public {
        _burn(who, amount);
    }

    function setBalanceFor(address who, uint newBalance) public {
        uint currentBalance = balances[who];
        if (currentBalance > newBalance) {
            destroyFor(who, currentBalance.sub(newBalance));
        } else if (balances[who] < newBalance) {
            createFor(who, newBalance.sub(currentBalance));
        }
    }
}

File 74 of 77 : MockVersion.sol
pragma solidity 0.6.1;

import "main/fund/hub/Hub.sol";

/// @notice Version contract useful for testing
contract MockVersion {
    uint public amguPrice;
    bool public isShutDown;

    function setAmguPrice(uint _price) public { amguPrice = _price; }
    function securityShutDown() external { isShutDown = true; }
    function shutDownFund(address _hub) external { Hub(_hub).shutDownFund(); }
    function getShutDownStatus() external view returns (bool) {return isShutDown;}
    function getAmguPrice() public view returns (uint) { return amguPrice; }
}

File 75 of 77 : PermissiveAuthority.sol
pragma solidity 0.6.1;

import "main/dependencies/DSAuth.sol";

contract PermissiveAuthority is DSAuthority {
    function canCall(address src, address dst, bytes4 sig)
        public
        view
        override
        returns (bool)
    {
        return true;
    }
}

File 76 of 77 : SelfDestructing.sol
pragma solidity 0.6.1;

/// @dev Useful for testing force-sending of funds
contract SelfDestructing {
    function bequeath(address payable _heir) public {
        selfdestruct(_heir);
    }

    receive() external payable {}
}

File 77 of 77 : TestingPriceFeed.sol
pragma solidity 0.6.1;

import "main/dependencies/token/IERC20.sol";
import "main/dependencies/DSMath.sol";

/// @notice Intended for testing purposes only
/// @notice Updates and exposes price information
contract TestingPriceFeed is DSMath {
    event PriceUpdate(address[] token, uint[] price);

    struct Data {
        uint price;
        uint timestamp;
    }

    address public QUOTE_ASSET;
    uint public updateId;
    uint public lastUpdate;
    mapping(address => Data) public assetsToPrices;
    mapping(address => uint) public assetsToDecimals;
    bool mockIsRecent = true;
    bool neverValid = false;

    constructor(address _quoteAsset, uint _quoteDecimals) public {
        QUOTE_ASSET = _quoteAsset;
        setDecimals(_quoteAsset, _quoteDecimals);
    }

    /**
      Input price is how much quote asset you would get
      for one unit of _asset (10**assetDecimals)
     */
    function update(address[] calldata _assets, uint[] calldata _prices) external {
        require(_assets.length == _prices.length, "Array lengths unequal");
        updateId++;
        for (uint i = 0; i < _assets.length; ++i) {
            assetsToPrices[_assets[i]] = Data({
                timestamp: block.timestamp,
                price: _prices[i]
            });
        }
        lastUpdate = block.timestamp;
        emit PriceUpdate(_assets, _prices);
    }

    function getPrice(address ofAsset)
        public
        view
        returns (uint price, uint timestamp)
    {
        Data storage data = assetsToPrices[ofAsset];
        return (data.price, data.timestamp);
    }

    function getPrices(address[] memory ofAssets)
        public
        view
        returns (uint[] memory, uint[] memory)
    {
        uint[] memory prices = new uint[](ofAssets.length);
        uint[] memory timestamps = new uint[](ofAssets.length);
        for (uint i; i < ofAssets.length; i++) {
            uint price;
            uint timestamp;
            (price, timestamp) = getPrice(ofAssets[i]);
            prices[i] = price;
            timestamps[i] = timestamp;
        }
        return (prices, timestamps);
    }

    function getPriceInfo(address ofAsset)
        public
        view
        returns (uint price, uint assetDecimals)
    {
        (price, ) = getPrice(ofAsset);
        assetDecimals = assetsToDecimals[ofAsset];
    }

    function getInvertedPriceInfo(address ofAsset)
        public
        view
        returns (uint invertedPrice, uint assetDecimals)
    {
        uint inputPrice;
        // inputPrice quoted in QUOTE_ASSET and multiplied by 10 ** assetDecimal
        (inputPrice, assetDecimals) = getPriceInfo(ofAsset);

        // outputPrice based in QUOTE_ASSET and multiplied by 10 ** quoteDecimal
        uint quoteDecimals = assetsToDecimals[QUOTE_ASSET];

        return (
            mul(
                10 ** uint(quoteDecimals),
                10 ** uint(assetDecimals)
            ) / inputPrice,
            quoteDecimals
        );
    }

    function setNeverValid(bool _state) public {
        neverValid = _state;
    }

    function setIsRecent(bool _state) public {
        mockIsRecent = _state;
    }

    // NB: not permissioned; anyone can change this in a test
    function setDecimals(address _asset, uint _decimal) public {
        assetsToDecimals[_asset] = _decimal;
    }

    // needed just to get decimals for prices
    function batchSetDecimals(address[] memory _assets, uint[] memory _decimals) public {
        require(_assets.length == _decimals.length, "Array lengths unequal");
        for (uint i = 0; i < _assets.length; i++) {
            setDecimals(_assets[i], _decimals[i]);
        }
    }

    function getReferencePriceInfo(address ofBase, address ofQuote)
        public
        view
        returns (uint referencePrice, uint decimal)
    {
        uint quoteDecimals = assetsToDecimals[ofQuote];

        bool bothValid = hasValidPrice(ofBase) && hasValidPrice(ofQuote);
        require(bothValid, "Price not valid");
        // Price of 1 unit for the pair of same asset
        if (ofBase == ofQuote) {
            return (10 ** uint(quoteDecimals), quoteDecimals);
        }

        referencePrice = mul(
            assetsToPrices[ofBase].price,
            10 ** uint(quoteDecimals)
        ) / assetsToPrices[ofQuote].price;

        return (referencePrice, quoteDecimals);
    }

    function getOrderPriceInfo(
        address sellAsset,
        uint sellQuantity,
        uint buyQuantity
    )
        public
        view
        returns (uint orderPrice)
    {
        return mul(buyQuantity, 10 ** uint(assetsToDecimals[sellAsset])) / sellQuantity;
    }

    /// @notice Doesn't check validity as TestingPriceFeed has no validity variable
    /// @param _asset Asset in registrar
    /// @return isValid Price information ofAsset is recent
    function hasValidPrice(address _asset)
        public
        view
        returns (bool isValid)
    {
        uint price;
        (price, ) = getPrice(_asset);

        return !neverValid && price != 0;
    }

    function hasValidPrices(address[] memory _assets)
        public
        view
        returns (bool)
    {
        for (uint i; i < _assets.length; i++) {
            if (!hasValidPrice(_assets[i])) {
                return false;
            }
        }
        return true;
    }

    /// @notice Checks whether data exists for a given asset pair
    /// @dev Prices are only upated against QUOTE_ASSET
    /// @param sellAsset Asset for which check to be done if data exists
    /// @param buyAsset Asset for which check to be done if data exists
    function existsPriceOnAssetPair(address sellAsset, address buyAsset)
        public
        view
        returns (bool isExistent)
    {
        return
            hasValidPrice(sellAsset) &&
            hasValidPrice(buyAsset);
    }

    function getLastUpdateId() public view returns (uint) { return updateId; }
    function getQuoteAsset() public view returns (address) { return QUOTE_ASSET; }

    /// @notice Get quantity of toAsset equal in value to given quantity of fromAsset
    function convertQuantity(
        uint fromAssetQuantity,
        address fromAsset,
        address toAsset
    )
        public
        view
        returns (uint)
    {
        uint fromAssetPrice;
        (fromAssetPrice,) = getReferencePriceInfo(fromAsset, toAsset);
        uint fromAssetDecimals = ERC20WithFields(fromAsset).decimals();
        return mul(
            fromAssetQuantity,
            fromAssetPrice
        ) / (10 ** uint(fromAssetDecimals));
    }

    function getLastUpdate() public view returns (uint) { return lastUpdate; }
}

Settings
{
  "remappings": [
    "main=./src"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "istanbul",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"ofRegistry","type":"address"},{"internalType":"address","name":"ofKyberNetworkProxy","type":"address"},{"internalType":"uint256","name":"ofMaxSpread","type":"uint256"},{"internalType":"address","name":"ofQuoteAsset","type":"address"},{"internalType":"address","name":"initialUpdater","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"token","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"price","type":"uint256[]"}],"name":"PriceUpdate","type":"event"},{"inputs":[],"name":"KYBER_ETH_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KYBER_NETWORK_PROXY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KYBER_PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SPREAD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"QUOTE_ASSET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRY","outputs":[{"internalType":"contract Registry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPDATER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VALIDITY_INTERVAL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract DSAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromAssetQuantity","type":"uint256"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"address","name":"toAsset","type":"address"}],"name":"convertQuantity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sellAsset","type":"address"},{"internalType":"address","name":"buyAsset","type":"address"}],"name":"existsPriceOnAssetPair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getInvertedPriceInfo","outputs":[{"internalType":"uint256","name":"invertedPrice","type":"uint256"},{"internalType":"uint256","name":"assetDecimals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getKyberMaskAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_baseAsset","type":"address"},{"internalType":"address","name":"_quoteAsset","type":"address"}],"name":"getKyberPrice","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLastUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sellAsset","type":"address"},{"internalType":"uint256","name":"sellQuantity","type":"uint256"},{"internalType":"uint256","name":"buyQuantity","type":"uint256"}],"name":"getOrderPriceInfo","outputs":[{"internalType":"uint256","name":"orderPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"getPriceInfo","outputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"assetDecimals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"}],"name":"getPrices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuoteAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_baseAsset","type":"address"},{"internalType":"address","name":"_quoteAsset","type":"address"}],"name":"getRawReferencePriceInfo","outputs":[{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"uint256","name":"referencePrice","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_baseAsset","type":"address"},{"internalType":"address","name":"_quoteAsset","type":"address"}],"name":"getReferencePriceInfo","outputs":[{"internalType":"uint256","name":"referencePrice","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"hasValidPrice","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_assets","type":"address[]"}],"name":"hasValidPrices","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"prices","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract DSAuthority","name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSpread","type":"uint256"}],"name":"setMaxSpread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newRegistry","type":"address"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_updater","type":"address"}],"name":"setUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setUpdaterToRegistryOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"update","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50604051611d78380380611d78833981810160405260a081101561003357600080fd5b50805160208201516040808401516060850151608090950151600180546001600160a01b0319163390811790915592519495939491939290917fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9490600090a2600280546001600160a01b03199081166001600160a01b039687161790915560069390935560038054841692851692909217909155600580548316948416949094179093556004805490911692909116919091179055611c81806100f76000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806394a686301161011a578063bf7e214f116100ad578063e0fe42be1161007c578063e0fe42be14610722578063e210c51914610750578063e3b5ce7614610758578063ec10bff914610760578063f06a1e13146107a957610206565b8063bf7e214f146106e4578063c0463711146106ec578063cfba99dc146106f4578063cfed246b146106fc57610206565b8063a91ee0dc116100e9578063a91ee0dc14610673578063abbc653114610699578063b00bdd98146106bf578063bef648db146106c757610206565b806394a686301461060f5780639d54f41914610617578063a159c9371461063d578063a2e620451461066b57610206565b8063730d09431161019d57806382d32ac71161016c57806382d32ac7146104775780638da5cb5b1461047f5780638edbf436146104875780638fb5a482146104ad57806393da4cfd146105e957610206565b8063730d0943146103485780637a9e5e4b1461037a5780637e3bfc2f146103a057806380971a36146103d457610206565b80631709ed21116101d95780631709ed211461029957806341976e09146102b35780634c89867f146102f2578063689a4c8f146102fa57610206565b806306433b1b1461020b5780630ad9f74f1461022f57806313533b551461023957806313af403514610273575b600080fd5b6102136107b1565b604080516001600160a01b039092168252519081900360200190f35b6102376107c0565b005b61025f6004803603602081101561024f57600080fd5b50356001600160a01b03166108ba565b604080519115158252519081900360200190f35b6102376004803603602081101561028957600080fd5b50356001600160a01b0316610989565b6102a1610a37565b60408051918252519081900360200190f35b6102d9600480360360208110156102c957600080fd5b50356001600160a01b0316610a3e565b6040805192835260208301919091528051918290030190f35b6102a1610a64565b6103286004803603604081101561031057600080fd5b506001600160a01b0381358116916020013516610a6a565b604080519315158452602084019290925282820152519081900360600190f35b6102a16004803603606081101561035e57600080fd5b506001600160a01b038135169060208101359060400135610b6a565b6102376004803603602081101561039057600080fd5b50356001600160a01b0316610bf1565b6102a1600480360360608110156103b657600080fd5b508035906001600160a01b0360208201358116916040013516610c9b565b61025f600480360360208110156103ea57600080fd5b81019060208101813564010000000081111561040557600080fd5b82018360208201111561041757600080fd5b8035906020019184602083028401116401000000008311171561043957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610d3a945050505050565b610213610d82565b610213610d9a565b6102d96004803603602081101561049d57600080fd5b50356001600160a01b0316610da9565b610550600480360360208110156104c357600080fd5b8101906020810181356401000000008111156104de57600080fd5b8201836020820111156104f057600080fd5b8035906020019184602083028401116401000000008311171561051257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610dce945050505050565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561059457818101518382015260200161057c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156105d35781810151838201526020016105bb565b5050505090500194505050505060405180910390f35b6102d9600480360360208110156105ff57600080fd5b50356001600160a01b0316610e9a565b610213610eb5565b6102376004803603602081101561062d57600080fd5b50356001600160a01b0316610ec4565b6102d96004803603604081101561065357600080fd5b506001600160a01b0381358116916020013516610fbc565b610237611022565b6102376004803603602081101561068957600080fd5b50356001600160a01b03166113ff565b610213600480360360208110156106af57600080fd5b50356001600160a01b03166114f7565b6102a161159a565b610237600480360360208110156106dd57600080fd5b503561159f565b61021361167a565b6102a1611689565b61021361168f565b6102a16004803603602081101561071257600080fd5b50356001600160a01b031661169e565b61025f6004803603604081101561073857600080fd5b506001600160a01b03813581169160200135166116b0565b6102a16116d4565b6102136116da565b61078e6004803603604081101561077657600080fd5b506001600160a01b03813581169160200135166116e9565b60408051921515835260208301919091528051918290030190f35b610213611a2c565b6005546001600160a01b031681565b6004546001600160a01b0316331461081f576040805162461bcd60e51b815260206004820181905260248201527f4f6e6c792063757272656e7420757064617465722063616e20646f2074686973604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561086d57600080fd5b505afa158015610881573d6000803e3d6000fd5b505050506040513d602081101561089757600080fd5b5051600480546001600160a01b0319166001600160a01b03909216919091179055565b60055460408051631f8d99a960e01b81526001600160a01b038481166004830152915160009384931691631f8d99a9916024808301926020929190829003018186803b15801561090957600080fd5b505afa15801561091d573d6000803e3d6000fd5b505050506040513d602081101561093357600080fd5b505160075490915060009061094b906202a300611a3b565b6001600160a01b03851660009081526008602052604090205442919091109150158015906109765750815b801561097f5750805b925050505b919050565b61099f336000356001600160e01b031916611a8a565b6109e7576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0383811691909117918290556040519116907fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9490600090a250565b6202a30081565b6003546000908190610a5a9084906001600160a01b0316610fbc565b5093429350915050565b60075490565b6000806000610a78856108ba565b8015610a885750610a88846108ba565b92506000846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610ac557600080fd5b505afa158015610ad9573d6000803e3d6000fd5b505050506040513d6020811015610aef57600080fd5b50516001600160a01b03861660009081526008602052604090205460ff9091169150610b25575060009250829150819050610b63565b6001600160a01b03808616600090815260086020526040808220549289168252902054610b5690600a84900a611b71565b81610b5d57fe5b04925090505b9250925092565b600082610be183866001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610baa57600080fd5b505afa158015610bbe573d6000803e3d6000fd5b505050506040513d6020811015610bd457600080fd5b505160ff16600a0a611b71565b81610be857fe5b04949350505050565b610c07336000356001600160e01b031916611a8a565b610c4f576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b03838116919091178083556040519116917f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada491a250565b600080610ca88484610fbc565b50809150506000846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610ce857600080fd5b505afa158015610cfc573d6000803e3d6000fd5b505050506040513d6020811015610d1257600080fd5b505160ff169050600a81900a610d288784611b71565b81610d2f57fe5b049695505050505050565b6000805b8251811015610d7957610d63838281518110610d5657fe5b60200260200101516108ba565b610d71576000915050610984565b600101610d3e565b50600192915050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6001546001600160a01b031681565b6003546000908190610dc59084906001600160a01b0316610fbc565b91509150915091565b60608060608351604051908082528060200260200182016040528015610dfe578160200160208202803883390190505b50905060608451604051908082528060200260200182016040528015610e2e578160200160208202803883390190505b50905060005b8551811015610e8f57610e59868281518110610e4c57fe5b6020026020010151610a3e565b848381518110610e6557fe5b60200260200101848481518110610e7857fe5b602090810291909101019190915252600101610e34565b509092509050915091565b6003546000908190610dc5906001600160a01b031684610fbc565b6004546001600160a01b031681565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f1257600080fd5b505afa158015610f26573d6000803e3d6000fd5b505050506040513d6020811015610f3c57600080fd5b50516001600160a01b03163314610f9a576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000610fcb8585610a6a565b9094509250905080611019576040805162461bcd60e51b8152602060048201526012602482015271141c9a58d9481a5cc81b9bdd081d985b1a5960721b604482015290519081900360640190fd5b505b9250929050565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561107057600080fd5b505afa158015611084573d6000803e3d6000fd5b505050506040513d602081101561109a57600080fd5b50516001600160a01b03163314806110bc57506004546001600160a01b031633145b6110f75760405162461bcd60e51b8152600401808060200182810382526027815260200180611c256027913960400191505060405180910390fd5b600554604080516367ae589960e11b815290516060926001600160a01b03169163cf5cb132916004808301926000929190829003018186803b15801561113c57600080fd5b505afa158015611150573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561117957600080fd5b810190808051604051939291908464010000000082111561119957600080fd5b9083019060208201858111156111ae57600080fd5b82518660208202830111640100000000821117156111cb57600080fd5b82525081516020918201928201910280838360005b838110156111f85781810151838201526020016111e0565b50505050905001604052505050905060608151604051908082528060200260200182016040528015611234578160200160208202803883390190505b50905060005b825181101561133857600354835160009182916001600160a01b039091169086908590811061126557fe5b60200260200101516001600160a01b0316141561128f575060019050670de0b6b3a76400006112c0565b6112ba85848151811061129e57fe5b60209081029190910101516003546001600160a01b03166116e9565b90925090505b816112cc5760006112ce565b805b8484815181106112da57fe5b6020026020010181815250508383815181106112f257fe5b60200260200101516008600087868151811061130a57fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002055505060010161123a565b50426007819055507f7da05e6be6ad8d48fd00ae7fa6e3ed3d7df8c07da8460294dfaedcfbb617b2938282604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156113a757818101518382015260200161138f565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156113e65781810151838201526020016113ce565b5050505090500194505050505060405180910390a15050565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561144d57600080fd5b505afa158015611461573d6000803e3d6000fd5b505050506040513d602081101561147757600080fd5b50516001600160a01b031633146114d5576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b60055460408051631d34cab560e21b815290516000926001600160a01b0316916374d32ad4916004808301926020929190829003018186803b15801561153c57600080fd5b505afa158015611550573d6000803e3d6000fd5b505050506040513d602081101561156657600080fd5b50516001600160a01b0383811691161415611596575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee610984565b5090565b601281565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115ed57600080fd5b505afa158015611601573d6000803e3d6000fd5b505050506040513d602081101561161757600080fd5b50516001600160a01b03163314611675576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600655565b6000546001600160a01b031681565b60075481565b6003546001600160a01b031690565b60086020526000908152604090205481565b60006116bb836108ba565b80156116cb57506116cb826108ba565b90505b92915050565b60065481565b6002546001600160a01b031681565b6002546000908190819081906001600160a01b031663809a9e5561170c886114f7565b611715886114f7565b6005546040805163f4196b6360e01b81526001600160a01b038d811660048301529151919092169163f4196b63916024808301926020929190829003018186803b15801561176257600080fd5b505afa158015611776573d6000803e3d6000fd5b505050506040513d602081101561178c57600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152815160648083019392829003018186803b1580156117de57600080fd5b505afa1580156117f2573d6000803e3d6000fd5b505050506040513d604081101561180857600080fd5b50516002549092506001600160a01b031663809a9e55611827876114f7565b611830896114f7565b6005546040805163f4196b6360e01b81526001600160a01b038c811660048301529151919092169163f4196b63916024808301926020929190829003018186803b15801561187d57600080fd5b505afa158015611891573d6000803e3d6000fd5b505050506040513d60208110156118a757600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152815160648083019392829003018186803b1580156118f957600080fd5b505afa15801561190d573d6000803e3d6000fd5b505050506040513d604081101561192357600080fd5b50519050811580611932575080155b1561194657506000925082915061101b9050565b6000816ec097ce7bc90715b34b9f10000000008161196057fe5b04905060006119786002670de0b6b3a7640000611b71565b6119be6119858685611a3b565b896001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610baa57600080fd5b816119c557fe5b0490506000828511156119da57506000611a01565b826119f66119e88588611bd4565b670de0b6b3a7640000611b71565b816119fd57fe5b0490505b6006548111158015611a1257508415155b8015611a1d57508215155b99919850909650505050505050565b6003546001600160a01b031681565b808201828110156116ce576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b60006001600160a01b038316301415611aa5575060016116ce565b6001546001600160a01b0384811691161415611ac3575060016116ce565b6000546001600160a01b0316611adb575060006116ce565b6000546040805163b700961360e01b81526001600160a01b0386811660048301523060248301526001600160e01b0319861660448301529151919092169163b7009613916064808301926020929190829003018186803b158015611b3e57600080fd5b505afa158015611b52573d6000803e3d6000fd5b505050506040513d6020811015611b6857600080fd5b505190506116ce565b6000811580611b8c57505080820282828281611b8957fe5b04145b6116ce576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b808203828111156116ce576040805162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b604482015290519081900360640190fdfe4f6e6c79207265676973747279206f776e6572206f7220757064617465722063616e2063616c6ca264697066735822122044246fae37c9694e7eb41ba29fab0ffd91512060ac6132441514815b4002bcde64736f6c63430006010033000000000000000000000000b9cb55c9366a224647b7ff66252b3613185da0b9000000000000000000000000818e6fecd516ecc3849daf6845e3ec868087b7550000000000000000000000000000000000000000000000000214e8348c4f0000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000d580ae50b58fe08514deab4e38c0dfdb0d30adc

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102065760003560e01c806394a686301161011a578063bf7e214f116100ad578063e0fe42be1161007c578063e0fe42be14610722578063e210c51914610750578063e3b5ce7614610758578063ec10bff914610760578063f06a1e13146107a957610206565b8063bf7e214f146106e4578063c0463711146106ec578063cfba99dc146106f4578063cfed246b146106fc57610206565b8063a91ee0dc116100e9578063a91ee0dc14610673578063abbc653114610699578063b00bdd98146106bf578063bef648db146106c757610206565b806394a686301461060f5780639d54f41914610617578063a159c9371461063d578063a2e620451461066b57610206565b8063730d09431161019d57806382d32ac71161016c57806382d32ac7146104775780638da5cb5b1461047f5780638edbf436146104875780638fb5a482146104ad57806393da4cfd146105e957610206565b8063730d0943146103485780637a9e5e4b1461037a5780637e3bfc2f146103a057806380971a36146103d457610206565b80631709ed21116101d95780631709ed211461029957806341976e09146102b35780634c89867f146102f2578063689a4c8f146102fa57610206565b806306433b1b1461020b5780630ad9f74f1461022f57806313533b551461023957806313af403514610273575b600080fd5b6102136107b1565b604080516001600160a01b039092168252519081900360200190f35b6102376107c0565b005b61025f6004803603602081101561024f57600080fd5b50356001600160a01b03166108ba565b604080519115158252519081900360200190f35b6102376004803603602081101561028957600080fd5b50356001600160a01b0316610989565b6102a1610a37565b60408051918252519081900360200190f35b6102d9600480360360208110156102c957600080fd5b50356001600160a01b0316610a3e565b6040805192835260208301919091528051918290030190f35b6102a1610a64565b6103286004803603604081101561031057600080fd5b506001600160a01b0381358116916020013516610a6a565b604080519315158452602084019290925282820152519081900360600190f35b6102a16004803603606081101561035e57600080fd5b506001600160a01b038135169060208101359060400135610b6a565b6102376004803603602081101561039057600080fd5b50356001600160a01b0316610bf1565b6102a1600480360360608110156103b657600080fd5b508035906001600160a01b0360208201358116916040013516610c9b565b61025f600480360360208110156103ea57600080fd5b81019060208101813564010000000081111561040557600080fd5b82018360208201111561041757600080fd5b8035906020019184602083028401116401000000008311171561043957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610d3a945050505050565b610213610d82565b610213610d9a565b6102d96004803603602081101561049d57600080fd5b50356001600160a01b0316610da9565b610550600480360360208110156104c357600080fd5b8101906020810181356401000000008111156104de57600080fd5b8201836020820111156104f057600080fd5b8035906020019184602083028401116401000000008311171561051257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610dce945050505050565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561059457818101518382015260200161057c565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156105d35781810151838201526020016105bb565b5050505090500194505050505060405180910390f35b6102d9600480360360208110156105ff57600080fd5b50356001600160a01b0316610e9a565b610213610eb5565b6102376004803603602081101561062d57600080fd5b50356001600160a01b0316610ec4565b6102d96004803603604081101561065357600080fd5b506001600160a01b0381358116916020013516610fbc565b610237611022565b6102376004803603602081101561068957600080fd5b50356001600160a01b03166113ff565b610213600480360360208110156106af57600080fd5b50356001600160a01b03166114f7565b6102a161159a565b610237600480360360208110156106dd57600080fd5b503561159f565b61021361167a565b6102a1611689565b61021361168f565b6102a16004803603602081101561071257600080fd5b50356001600160a01b031661169e565b61025f6004803603604081101561073857600080fd5b506001600160a01b03813581169160200135166116b0565b6102a16116d4565b6102136116da565b61078e6004803603604081101561077657600080fd5b506001600160a01b03813581169160200135166116e9565b60408051921515835260208301919091528051918290030190f35b610213611a2c565b6005546001600160a01b031681565b6004546001600160a01b0316331461081f576040805162461bcd60e51b815260206004820181905260248201527f4f6e6c792063757272656e7420757064617465722063616e20646f2074686973604482015290519081900360640190fd5b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561086d57600080fd5b505afa158015610881573d6000803e3d6000fd5b505050506040513d602081101561089757600080fd5b5051600480546001600160a01b0319166001600160a01b03909216919091179055565b60055460408051631f8d99a960e01b81526001600160a01b038481166004830152915160009384931691631f8d99a9916024808301926020929190829003018186803b15801561090957600080fd5b505afa15801561091d573d6000803e3d6000fd5b505050506040513d602081101561093357600080fd5b505160075490915060009061094b906202a300611a3b565b6001600160a01b03851660009081526008602052604090205442919091109150158015906109765750815b801561097f5750805b925050505b919050565b61099f336000356001600160e01b031916611a8a565b6109e7576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0383811691909117918290556040519116907fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9490600090a250565b6202a30081565b6003546000908190610a5a9084906001600160a01b0316610fbc565b5093429350915050565b60075490565b6000806000610a78856108ba565b8015610a885750610a88846108ba565b92506000846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610ac557600080fd5b505afa158015610ad9573d6000803e3d6000fd5b505050506040513d6020811015610aef57600080fd5b50516001600160a01b03861660009081526008602052604090205460ff9091169150610b25575060009250829150819050610b63565b6001600160a01b03808616600090815260086020526040808220549289168252902054610b5690600a84900a611b71565b81610b5d57fe5b04925090505b9250925092565b600082610be183866001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610baa57600080fd5b505afa158015610bbe573d6000803e3d6000fd5b505050506040513d6020811015610bd457600080fd5b505160ff16600a0a611b71565b81610be857fe5b04949350505050565b610c07336000356001600160e01b031916611a8a565b610c4f576040805162461bcd60e51b8152602060048201526014602482015273191ccb585d5d1a0b5d5b985d5d1a1bdc9a5e995960621b604482015290519081900360640190fd5b600080546001600160a01b0319166001600160a01b03838116919091178083556040519116917f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada491a250565b600080610ca88484610fbc565b50809150506000846001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610ce857600080fd5b505afa158015610cfc573d6000803e3d6000fd5b505050506040513d6020811015610d1257600080fd5b505160ff169050600a81900a610d288784611b71565b81610d2f57fe5b049695505050505050565b6000805b8251811015610d7957610d63838281518110610d5657fe5b60200260200101516108ba565b610d71576000915050610984565b600101610d3e565b50600192915050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6001546001600160a01b031681565b6003546000908190610dc59084906001600160a01b0316610fbc565b91509150915091565b60608060608351604051908082528060200260200182016040528015610dfe578160200160208202803883390190505b50905060608451604051908082528060200260200182016040528015610e2e578160200160208202803883390190505b50905060005b8551811015610e8f57610e59868281518110610e4c57fe5b6020026020010151610a3e565b848381518110610e6557fe5b60200260200101848481518110610e7857fe5b602090810291909101019190915252600101610e34565b509092509050915091565b6003546000908190610dc5906001600160a01b031684610fbc565b6004546001600160a01b031681565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f1257600080fd5b505afa158015610f26573d6000803e3d6000fd5b505050506040513d6020811015610f3c57600080fd5b50516001600160a01b03163314610f9a576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600480546001600160a01b0319166001600160a01b0392909216919091179055565b6000806000610fcb8585610a6a565b9094509250905080611019576040805162461bcd60e51b8152602060048201526012602482015271141c9a58d9481a5cc81b9bdd081d985b1a5960721b604482015290519081900360640190fd5b505b9250929050565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561107057600080fd5b505afa158015611084573d6000803e3d6000fd5b505050506040513d602081101561109a57600080fd5b50516001600160a01b03163314806110bc57506004546001600160a01b031633145b6110f75760405162461bcd60e51b8152600401808060200182810382526027815260200180611c256027913960400191505060405180910390fd5b600554604080516367ae589960e11b815290516060926001600160a01b03169163cf5cb132916004808301926000929190829003018186803b15801561113c57600080fd5b505afa158015611150573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052602081101561117957600080fd5b810190808051604051939291908464010000000082111561119957600080fd5b9083019060208201858111156111ae57600080fd5b82518660208202830111640100000000821117156111cb57600080fd5b82525081516020918201928201910280838360005b838110156111f85781810151838201526020016111e0565b50505050905001604052505050905060608151604051908082528060200260200182016040528015611234578160200160208202803883390190505b50905060005b825181101561133857600354835160009182916001600160a01b039091169086908590811061126557fe5b60200260200101516001600160a01b0316141561128f575060019050670de0b6b3a76400006112c0565b6112ba85848151811061129e57fe5b60209081029190910101516003546001600160a01b03166116e9565b90925090505b816112cc5760006112ce565b805b8484815181106112da57fe5b6020026020010181815250508383815181106112f257fe5b60200260200101516008600087868151811061130a57fe5b6020908102919091018101516001600160a01b0316825281019190915260400160002055505060010161123a565b50426007819055507f7da05e6be6ad8d48fd00ae7fa6e3ed3d7df8c07da8460294dfaedcfbb617b2938282604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156113a757818101518382015260200161138f565b50505050905001838103825284818151815260200191508051906020019060200280838360005b838110156113e65781810151838201526020016113ce565b5050505090500194505050505060405180910390a15050565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561144d57600080fd5b505afa158015611461573d6000803e3d6000fd5b505050506040513d602081101561147757600080fd5b50516001600160a01b031633146114d5576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600580546001600160a01b0319166001600160a01b0392909216919091179055565b60055460408051631d34cab560e21b815290516000926001600160a01b0316916374d32ad4916004808301926020929190829003018186803b15801561153c57600080fd5b505afa158015611550573d6000803e3d6000fd5b505050506040513d602081101561156657600080fd5b50516001600160a01b0383811691161415611596575073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee610984565b5090565b601281565b600560009054906101000a90046001600160a01b03166001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115ed57600080fd5b505afa158015611601573d6000803e3d6000fd5b505050506040513d602081101561161757600080fd5b50516001600160a01b03163314611675576040805162461bcd60e51b815260206004820152601b60248201527f4f6e6c79207265676973747279206f776e65722063616e207365740000000000604482015290519081900360640190fd5b600655565b6000546001600160a01b031681565b60075481565b6003546001600160a01b031690565b60086020526000908152604090205481565b60006116bb836108ba565b80156116cb57506116cb826108ba565b90505b92915050565b60065481565b6002546001600160a01b031681565b6002546000908190819081906001600160a01b031663809a9e5561170c886114f7565b611715886114f7565b6005546040805163f4196b6360e01b81526001600160a01b038d811660048301529151919092169163f4196b63916024808301926020929190829003018186803b15801561176257600080fd5b505afa158015611776573d6000803e3d6000fd5b505050506040513d602081101561178c57600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152815160648083019392829003018186803b1580156117de57600080fd5b505afa1580156117f2573d6000803e3d6000fd5b505050506040513d604081101561180857600080fd5b50516002549092506001600160a01b031663809a9e55611827876114f7565b611830896114f7565b6005546040805163f4196b6360e01b81526001600160a01b038c811660048301529151919092169163f4196b63916024808301926020929190829003018186803b15801561187d57600080fd5b505afa158015611891573d6000803e3d6000fd5b505050506040513d60208110156118a757600080fd5b5051604080516001600160e01b031960e087901b1681526001600160a01b0394851660048201529290931660248301526044820152815160648083019392829003018186803b1580156118f957600080fd5b505afa15801561190d573d6000803e3d6000fd5b505050506040513d604081101561192357600080fd5b50519050811580611932575080155b1561194657506000925082915061101b9050565b6000816ec097ce7bc90715b34b9f10000000008161196057fe5b04905060006119786002670de0b6b3a7640000611b71565b6119be6119858685611a3b565b896001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015610baa57600080fd5b816119c557fe5b0490506000828511156119da57506000611a01565b826119f66119e88588611bd4565b670de0b6b3a7640000611b71565b816119fd57fe5b0490505b6006548111158015611a1257508415155b8015611a1d57508215155b99919850909650505050505050565b6003546001600160a01b031681565b808201828110156116ce576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6164642d6f766572666c6f7760601b604482015290519081900360640190fd5b60006001600160a01b038316301415611aa5575060016116ce565b6001546001600160a01b0384811691161415611ac3575060016116ce565b6000546001600160a01b0316611adb575060006116ce565b6000546040805163b700961360e01b81526001600160a01b0386811660048301523060248301526001600160e01b0319861660448301529151919092169163b7009613916064808301926020929190829003018186803b158015611b3e57600080fd5b505afa158015611b52573d6000803e3d6000fd5b505050506040513d6020811015611b6857600080fd5b505190506116ce565b6000811580611b8c57505080820282828281611b8957fe5b04145b6116ce576040805162461bcd60e51b815260206004820152601460248201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604482015290519081900360640190fd5b808203828111156116ce576040805162461bcd60e51b815260206004820152601560248201527464732d6d6174682d7375622d756e646572666c6f7760581b604482015290519081900360640190fdfe4f6e6c79207265676973747279206f776e6572206f7220757064617465722063616e2063616c6ca264697066735822122044246fae37c9694e7eb41ba29fab0ffd91512060ac6132441514815b4002bcde64736f6c63430006010033

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

000000000000000000000000b9cb55c9366a224647b7ff66252b3613185da0b9000000000000000000000000818e6fecd516ecc3849daf6845e3ec868087b7550000000000000000000000000000000000000000000000000214e8348c4f0000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000d580ae50b58fe08514deab4e38c0dfdb0d30adc

-----Decoded View---------------
Arg [0] : ofRegistry (address): 0xb9Cb55C9366a224647B7ff66252b3613185DA0B9
Arg [1] : ofKyberNetworkProxy (address): 0x818E6FECD516Ecc3849DAf6845e3EC868087B755
Arg [2] : ofMaxSpread (uint256): 150000000000000000
Arg [3] : ofQuoteAsset (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [4] : initialUpdater (address): 0x0d580ae50B58fe08514dEAB4e38c0DFdB0D30adC

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000b9cb55c9366a224647b7ff66252b3613185da0b9
Arg [1] : 000000000000000000000000818e6fecd516ecc3849daf6845e3ec868087b755
Arg [2] : 0000000000000000000000000000000000000000000000000214e8348c4f0000
Arg [3] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [4] : 0000000000000000000000000d580ae50b58fe08514deab4e38c0dfdb0d30adc


Deployed Bytecode Sourcemap

561:10618:60:-:0;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;561:10618:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;764:24;;;:::i;:::-;;;;-1:-1:-1;;;;;764:24:60;;;;;;;;;;;;;;2526:165;;;:::i;:::-;;4488:305;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;4488:305:60;-1:-1:-1;;;;;4488:305:60;;:::i;:::-;;;;;;;;;;;;;;;;;;566:130:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;566:130:0;-1:-1:-1;;;;;566:130:0;;:::i;970:47:60:-;;;:::i;:::-;;;;;;;;;;;;;;;;3842:208;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;3842:208:60;-1:-1:-1;;;;;3842:208:60;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;11103:74;;;:::i;5744:646::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;5744:646:60;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9661:311;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;9661:311:60;;;;;;;;;;;;;:::i;702:167:0:-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;702:167:0;-1:-1:-1;;;;;702:167:0;;:::i;10625:472:60:-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;10625:472:60;;;-1:-1:-1;;;;;10625:472:60;;;;;;;;;;;;:::i;4799:281::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4799:281:60;;;;;;;;21:11:-1;5:28;;2:2;;;46:1;43;36:12;2:2;4799:281:60;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;4799:281:60;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;39:11;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;4799:281:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;4799:281:60;;-1:-1:-1;4799:281:60;;-1:-1:-1;;;;;4799:281:60:i;822:95::-;;;:::i;433:26:0:-;;;:::i;6396:191:60:-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;6396:191:60;-1:-1:-1;;;;;6396:191:60;;:::i;4056:426::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;4056:426:60;;;;;;;;21:11:-1;5:28;;2:2;;;46:1;43;36:12;2:2;4056:426:60;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;4056:426:60;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;39:11;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;4056:426:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;4056:426:60;;-1:-1:-1;4056:426:60;;-1:-1:-1;;;;;4056:426:60:i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4056:426:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;4056:426:60;;;;;;;;;;;;;;;;;;;6920:207;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;6920:207:60;-1:-1:-1;;;;;6920:207:60;;:::i;736:22::-;;;:::i;2697:162::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2697:162:60;-1:-1:-1;;;;;2697:162:60;;:::i;5318:420::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;5318:420:60;;;;;;;;;;:::i;1706:814::-;;;:::i;2865:182::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2865:182:60;-1:-1:-1;;;;;2865:182:60;;:::i;7191:197::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;7191:197:60;-1:-1:-1;;;;;7191:197:60;;:::i;923:41::-;;;:::i;3164:168::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;3164:168:60;;:::i;397:30:0:-;;;:::i;1023:22:60:-;;;:::i;3391:78::-;;;:::i;1067:39::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1067:39:60;-1:-1:-1;;;;;1067:39:60;;:::i;10245:288::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;10245:288:60;;;;;;;;;;:::i;794:22::-;;;:::i;664:34::-;;;:::i;7448:1905::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;7448:1905:60;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;704:26;;;:::i;764:24::-;;;-1:-1:-1;;;;;764:24:60;;:::o;2526:165::-;2604:7;;-1:-1:-1;;;;;2604:7:60;2590:10;:21;2582:66;;;;;-1:-1:-1;;;2582:66:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2668:8;;;;;;;;;-1:-1:-1;;;;;2668:8:60;-1:-1:-1;;;;;2668:14:60;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2668:16:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2668:16:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2668:16:60;2658:7;:26;;-1:-1:-1;;;;;;2658:26:60;-1:-1:-1;;;;;2658:26:60;;;;;;;;;2526:165::o;4488:305::-;4612:8;;:34;;;-1:-1:-1;;;4612:34:60;;-1:-1:-1;;;;;4612:34:60;;;;;;;;;4572:4;;;;4612:8;;:26;;:34;;;;;;;;;;;;;;:8;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;4612:34:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4612:34:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;4612:34:60;4693:10;;4612:34;;-1:-1:-1;4656:12:60;;4689:34;;1011:6;4689:3;:34::i;:::-;-1:-1:-1;;;;;4740:14:60;;;;;;:6;:14;;;;;;4671:15;:52;;;;;-1:-1:-1;4740:19:60;;;;:35;;;4763:12;4740:35;:46;;;;;4779:7;4740:46;4733:53;;;;4488:305;;;;:::o;566:130:0:-;907:33;920:10;932:7;;-1:-1:-1;;;;;;932:7:0;907:12;:33::i;:::-;899:66;;;;;-1:-1:-1;;;899:66:0;;;;;;;;;;;;-1:-1:-1;;;899:66:0;;;;;;;;;;;;;;;642:5:::1;:14:::0;;-1:-1:-1;;;;;;642:14:0::1;-1:-1:-1::0;;;;;642:14:0;;::::1;::::0;;;::::1;::::0;;;;671:18:::1;::::0;683:5;::::1;::::0;671:18:::1;::::0;-1:-1:-1;;671:18:0::1;566:130:::0;:::o;970:47:60:-;1011:6;970:47;:::o;3842:208::-;4006:11;;3921:10;;;;3976:42;;3998:6;;-1:-1:-1;;;;;4006:11:60;3976:21;:42::i;:::-;-1:-1:-1;3963:55:60;4040:3;;-1:-1:-1;3842:208:60;-1:-1:-1;;3842:208:60:o;11103:74::-;11164:10;;11103:74;:::o;5744:646::-;5864:12;5878:22;5902:16;5944:25;5958:10;5944:13;:25::i;:::-;:55;;;;;5973:26;5987:11;5973:13;:26::i;:::-;5934:65;;6009:21;6049:11;-1:-1:-1;;;;;6033:37:60;;:39;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;6033:39:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;6033:39:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;6033:39:60;-1:-1:-1;;;;;6087:19:60;;;;;;:6;6033:39;6087:19;;;;;6009:63;;;;;-1:-1:-1;6083:109:60;;-1:-1:-1;6135:5:60;;-1:-1:-1;6135:5:60;;-1:-1:-1;6135:5:60;;-1:-1:-1;6127:20:60;;6083:109;-1:-1:-1;;;;;6306:19:60;;;;;;;:6;:19;;;;;;;6236:18;;;;;;;;6219:84;;6268:2;:25;;;6219:3;:84::i;:::-;:106;;;;;;;-1:-1:-1;6369:13:60;-1:-1:-1;5744:646:60;;;;;;:::o;9661:311::-;9819:15;9953:12;9883:67;9887:11;9927:9;-1:-1:-1;;;;;9911:35:60;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;9911:37:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;9911:37:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;9911:37:60;9906:43;;9900:2;:49;9883:3;:67::i;:::-;:82;;;;;;;9661:311;-1:-1:-1;;;;9661:311:60:o;702:167:0:-;907:33;920:10;932:7;;-1:-1:-1;;;;;;932:7:0;907:12;:33::i;:::-;899:66;;;;;-1:-1:-1;;;899:66:0;;;;;;;;;;;;-1:-1:-1;;;899:66:0;;;;;;;;;;;;;;;790:9:::1;:22:::0;;-1:-1:-1;;;;;;790:22:0::1;-1:-1:-1::0;;;;;790:22:0;;::::1;::::0;;;::::1;::::0;;;827:35:::1;::::0;851:9;::::1;::::0;827:35:::1;::::0;::::1;702:167:::0;:::o;10625:472:60:-;10785:4;10805:19;10854:41;10876:9;10887:7;10854:21;:41::i;:::-;10834:61;;;;;10905:22;10946:9;-1:-1:-1;;;;;10930:35:60;;:37;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10930:37:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;10930:37:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;10930:37:60;10905:62;;;-1:-1:-1;11060:2:60;:29;;;10984:72;11001:17;11032:14;10984:3;:72::i;:::-;:106;;;;;;;10625:472;-1:-1:-1;;;;;;10625:472:60:o;4799:281::-;4894:4;4919:6;4914:139;4931:7;:14;4927:1;:18;4914:139;;;4971:25;4985:7;4993:1;4985:10;;;;;;;;;;;;;;4971:13;:25::i;:::-;4966:77;;5023:5;5016:12;;;;;4966:77;4947:3;;4914:139;;;-1:-1:-1;5069:4:60;;4799:281;-1:-1:-1;;4799:281:60:o;822:95::-;872:44;822:95;:::o;433:26:0:-;;;-1:-1:-1;;;;;433:26:0;;:::o;6396:191:60:-;6568:11;;6479:13;;;;6538:42;;6560:6;;-1:-1:-1;;;;;6568:11:60;6538:21;:42::i;:::-;6531:49;;;;6396:191;;;:::o;4056:426::-;4146:16;4164;4196:23;4233:7;:14;4222:26;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;4222:26:60;;4196:52;;4258:24;4296:7;:14;4285:26;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;4285:26:60;;4258:53;;4326:6;4321:115;4338:7;:14;4334:1;:18;4321:115;;;4405:20;4414:7;4422:1;4414:10;;;;;;;;;;;;;;4405:8;:20::i;:::-;4374:9;4384:1;4374:12;;;;;;;;;;;;;4388:10;4399:1;4388:13;;;;;;;;;;;;;;;;;4373:52;;;;;4354:3;;4321:115;;;-1:-1:-1;4453:9:60;;-1:-1:-1;4464:10:60;-1:-1:-1;4056:426:60;;;:::o;6920:207::-;7100:11;;7011:21;;;;7078:42;;-1:-1:-1;;;;;7100:11:60;7113:6;7078:21;:42::i;736:22::-;;;-1:-1:-1;;;;;736:22:60;;:::o;2697:162::-;2776:8;;;;;;;;;-1:-1:-1;;;;;2776:8:60;-1:-1:-1;;;;;2776:14:60;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2776:16:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2776:16:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2776:16:60;-1:-1:-1;;;;;2762:30:60;:10;:30;2754:70;;;;;-1:-1:-1;;;2754:70:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;2834:7;:18;;-1:-1:-1;;;;;;2834:18:60;-1:-1:-1;;;;;2834:18:60;;;;;;;;;;2697:162::o;5318:420::-;5435:19;5456:13;5485:12;5591:49;5616:10;5628:11;5591:24;:49::i;:::-;5507:133;;-1:-1:-1;5507:133:60;-1:-1:-1;5507:133:60;-1:-1:-1;5507:133:60;5650:38;;;;;-1:-1:-1;;;5650:38:60;;;;;;;;;;;;-1:-1:-1;;;5650:38:60;;;;;;;;;;;;;;;-1:-1:-1;5318:420:60;;;;;;:::o;1706:814::-;1778:8;;;;;;;;;-1:-1:-1;;;;;1778:8:60;-1:-1:-1;;;;;1778:14:60;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1778:16:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1778:16:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1778:16:60;-1:-1:-1;;;;;1764:30:60;:10;:30;;:55;;-1:-1:-1;1812:7:60;;-1:-1:-1;;;;;1812:7:60;1798:10;:21;1764:55;1743:141;;;;-1:-1:-1;;;1743:141:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1920:8;;:30;;;-1:-1:-1;;;1920:30:60;;;;1894:23;;-1:-1:-1;;;;;1920:8:60;;:28;;:30;;;;;:8;;:30;;;;;;;:8;:30;;;5:2:-1;;;;30:1;27;20:12;5:2;1920:30:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1920:30:60;;;;;;39:16:-1;36:1;17:17;2:54;101:4;1920:30:60;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;1920:30:60;;;;;;;;;;;;;19:11:-1;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;261:11;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;1920:30:60;;421:4:-1;412:14;;;;1920:30:60;;;;;412:14:-1;1920:30:60;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;1920:30:60;;;;;;;;;;;1894:56;;1960:23;1997:6;:13;1986:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;1986:25:60;;1960:51;;2026:6;2021:410;2038:6;:13;2034:1;:17;2021:410;;;2139:11;;2126:9;;2072:12;;;;-1:-1:-1;;;;;2139:11:60;;;;2126:6;;2133:1;;2126:9;;;;;;;;;;;;-1:-1:-1;;;;;2126:24:60;;2122:205;;;-1:-1:-1;2180:4:60;;-1:-1:-1;2210:7:60;2122:205;;;2275:37;2289:6;2296:1;2289:9;;;;;;;;;;;;;;;;;;2300:11;;-1:-1:-1;;;;;2300:11:60;2275:13;:37::i;:::-;2256:56;;-1:-1:-1;2256:56:60;-1:-1:-1;2122:205:60;2355:7;:19;;2373:1;2355:19;;;2365:5;2355:19;2340:9;2350:1;2340:12;;;;;;;;;;;;;:34;;;;;2408:9;2418:1;2408:12;;;;;;;;;;;;;;2388:6;:17;2395:6;2402:1;2395:9;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2388:17:60;;;;;;;;;;;-1:-1:-1;2388:17:60;:32;-1:-1:-1;;2053:3:60;;2021:410;;;;2453:15;2440:10;:28;;;;2483:30;2495:6;2503:9;2483:30;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;2483:30:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;2483:30:60;;;;;;;;;;;;;;;;;;;1706:814;;:::o;2865:182::-;2949:8;;;;;;;;;-1:-1:-1;;;;;2949:8:60;-1:-1:-1;;;;;2949:14:60;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2949:16:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;2949:16:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;2949:16:60;-1:-1:-1;;;;;2935:30:60;:10;:30;2927:70;;;;;-1:-1:-1;;;2927:70:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;3007:8;:33;;-1:-1:-1;;;;;;3007:33:60;-1:-1:-1;;;;;3007:33:60;;;;;;;;;;2865:182::o;7191:197::-;7288:8;;:22;;;-1:-1:-1;;;7288:22:60;;;;7255:7;;-1:-1:-1;;;;;7288:8:60;;:20;;:22;;;;;;;;;;;;;;:8;:22;;;5:2:-1;;;;30:1;27;20:12;5:2;7288:22:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7288:22:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;7288:22:60;-1:-1:-1;;;;;7278:32:60;;;;;;7274:85;;;-1:-1:-1;872:44:60;7326:22;;7274:85;-1:-1:-1;7375:6:60;7191:197::o;923:41::-;962:2;923:41;:::o;3164:168::-;3244:8;;;;;;;;;-1:-1:-1;;;;;3244:8:60;-1:-1:-1;;;;;3244:14:60;;:16;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;3244:16:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;3244:16:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;3244:16:60;-1:-1:-1;;;;;3230:30:60;:10;:30;3222:70;;;;;-1:-1:-1;;;3222:70:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;3302:10;:23;3164:168::o;397:30:0:-;;;-1:-1:-1;;;;;397:30:0;;:::o;1023:22:60:-;;;;:::o;3391:78::-;3455:11;;-1:-1:-1;;;;;3455:11:60;3391:78;:::o;1067:39::-;;;;;;;;;;;;;:::o;10245:288::-;10359:4;10398:24;10412:9;10398:13;:24::i;:::-;:128;;;;;10503:23;10517:8;10503:13;:23::i;:::-;10379:147;;10245:288;;;;;:::o;794:22::-;;;;:::o;664:34::-;;;-1:-1:-1;;;;;664:34:60;;:::o;7448:1905::-;7672:19;;7557:4;;;;;;;;-1:-1:-1;;;;;7672:19:60;7653:55;7722:29;7740:10;7722:17;:29::i;:::-;7765:30;7783:11;7765:17;:30::i;:::-;7809:8;;:34;;;-1:-1:-1;;;7809:34:60;;-1:-1:-1;;;;;7809:34:60;;;;;;;;;:8;;;;;:22;;:34;;;;;;;;;;;;;;:8;:34;;;5:2:-1;;;;30:1;27;20:12;5:2;7809:34:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7809:34:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;7809:34:60;7653:200;;;-1:-1:-1;;;;;;7653:200:60;;;;;;;-1:-1:-1;;;;;7653:200:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5:2:-1;;;;30:1;27;20:12;5:2;7653:200:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7653:200:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;7653:200:60;7908:19;;7653:200;;-1:-1:-1;;;;;;7908:19:60;7889:55;7958:30;7976:11;7958:17;:30::i;:::-;8002:29;8020:10;8002:17;:29::i;:::-;8045:8;;:35;;;-1:-1:-1;;;8045:35:60;;-1:-1:-1;;;;;8045:35:60;;;;;;;;;:8;;;;;:22;;:35;;;;;;;;;;;;;;:8;:35;;;5:2:-1;;;;30:1;27;20:12;5:2;8045:35:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;8045:35:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;8045:35:60;7889:201;;;-1:-1:-1;;;;;;7889:201:60;;;;;;;-1:-1:-1;;;;;7889:201:60;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5:2:-1;;;;30:1;27;20:12;5:2;7889:201:60;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7889:201:60;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;7889:201:60;;-1:-1:-1;8105:12:60;;;:41;;-1:-1:-1;8121:25:60;;8105:41;8101:123;;;-1:-1:-1;8170:5:60;;-1:-1:-1;8170:5:60;;-1:-1:-1;8162:17:60;;-1:-1:-1;8162:17:60;8101:123;8234:12;8279:20;8249:27;8279:20;8249:50;;;;;;-1:-1:-1;8620:15:60;8807:35;8811:1;8814:27;8807:3;:35::i;:::-;8638:166;8655:21;8659:7;8668;8655:3;:21::i;:::-;8717:11;-1:-1:-1;;;;;8701:37:60;;:39;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;8638:166:60;:204;;;;;;8620:222;;8935:20;8979:7;8969;:17;8965:255;;;-1:-1:-1;9020:1:60;8965:255;;;9202:7;9098:101;9119:21;9123:7;9132;9119:3;:21::i;:::-;9158:27;9098:3;:101::i;:::-;:111;;;;;;9080:129;;8965:255;9270:10;;9251:15;:29;;:45;;;;-1:-1:-1;9284:12:60;;;9251:45;:61;;;;-1:-1:-1;9300:12:60;;;9251:61;9230:116;9326:10;;-1:-1:-1;7448:1905:60;;-1:-1:-1;;;;;;;7448:1905:60:o;704:26::-;;;-1:-1:-1;;;;;704:26:60;;:::o;746:126:2:-;829:5;;;824:16;;;;816:49;;;;;-1:-1:-1;;;816:49:2;;;;;;;;;;;;-1:-1:-1;;;816:49:2;;;;;;;;;;;;;;989:370:0;1059:4;-1:-1:-1;;;;;1079:20:0;;1094:4;1079:20;1075:278;;;-1:-1:-1;1122:4:0;1115:11;;1075:278;1154:5;;-1:-1:-1;;;;;1147:12:0;;;1154:5;;1147:12;1143:210;;;-1:-1:-1;1182:4:0;1175:11;;1143:210;1232:1;1207:9;-1:-1:-1;;;;;1207:9:0;1203:150;;-1:-1:-1;1257:5:0;1250:12;;1203:150;1300:9;;:42;;;-1:-1:-1;;;1300:42:0;;-1:-1:-1;;;;;1300:42:0;;;;;;;1331:4;1300:42;;;;-1:-1:-1;;;;;;1300:42:0;;;;;;;;:9;;;;;:17;;:42;;;;;;;;;;;;;;:9;:42;;;5:2:-1;;;;30:1;27;20:12;5:2;1300:42:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;1300:42:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;1300:42:0;;-1:-1:-1;1293:49:0;;1009:140:2;1061:6;1087;;;:30;;-1:-1:-1;;1102:5:2;;;1116:1;1111;1102:5;1111:1;1097:15;;;;;:20;1087:30;1079:63;;;;;-1:-1:-1;;;1079:63:2;;;;;;;;;;;;-1:-1:-1;;;1079:63:2;;;;;;;;;;;;;;877:127;960:5;;;955:16;;;;947:50;;;;;-1:-1:-1;;;947:50:2;;;;;;;;;;;;-1:-1:-1;;;947:50:2;;;;;;;;;;;;;

Swarm Source

://44246fae37c9694e7eb41ba29fab0ffd91512060ac6132441514815b4002bcde

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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