ETH Price: $2,700.06 (-0.13%)

Contract Diff Checker

Contract Name:
Participation

Contract Source Code:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @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);
        }
    }
}

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

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

pragma solidity 0.6.1;

interface IVersion {
    function shutDownFund(address) external;
}

pragma solidity 0.6.1;


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

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

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

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

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

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

Contract Name:
Participation

Contract Source Code:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @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);
        }
    }
}

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

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

pragma solidity 0.6.1;

interface IVersion {
    function shutDownFund(address) external;
}

pragma solidity 0.6.1;


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

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

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

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

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

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

Context size (optional):