Fund

Hub & Spoke

General

The Hub and Spoke contracts make up the core contract framework of the watermelon fund. Hub and Spoke is an architecture design to help reason about all the moving parts of fund as constructed by an interconnected system of linked smart contracts. The Hub will contain all relevant information for all Spokes registered with the Hub. Each Spoke will independently contain all relevant information about the Hub. Individual Spokes will have programmatic access to information in specific other Spokes.

In general, there is one Hub contract and separate, distinct Spoke contracts for each fund. (CHECK: Implementation for shared components as spoke) The Hub forms the core of the individual watermelon fund with each Spoke contributing specific services to the fund. The Hub contract stores the manager’s address and the fund name permanently in state, and also contains the functionality to permanently and irreversibly shut the watermelon fund down.

Hub.sol

Description

The Hub maintains the specific Spoke components of the fund in terms of their routing and their permissioning. Permissioning is of utmost importance as it ensures only design-intended access to specific components of the watermelon fund smart contract system. The permissioning has a very low-level granularity, exclusively permitting only specific Spokes to invoke specific functions on specific target Spokes. This surgical permissioning system ensures that sensitive function execution can occur as intended and not by external actors.

Inherits from

DSGuard (link)  

On Construction

The constructor requires and sets the manager address and the fund name as well as the creator and `creationTime.  

Structs

Routes

Member variables:

address accounting address feeManager address participation address policyManager address shares address trading address vault address priceSource address registry address version address engine address mlnToken

This struct stores the contract addresses of the listed components and spokes.  

Enums

None.

 

Modifiers

modifier onlyCreator()

A modifier requiring that the msg.sender is the creator.

 

Events

None.

 

Public State Variables

Routes public routes

A Routes struct containing Spoke and component addresses.  

address public manager

The address of the fund manager.  

string public name

The name of the fund as defined by the manager at fund set up.  

bool public isShutDown

A boolean variable defining the operational status of the fund.  

bool public spokesSet

A boolean variable defining the completeness status of all spoke settings.  

bool public routingSet

A boolean variable defining the completeness status of all routing settings.  

bool public permissionsSet

A boolean variable defining the completeness status of all permissions settings.  

address public creator

An address variable defining the creator of the watermelon fund, i.e. msg.sender.  

uint public creationTime

An integer variable representing the watermelon fund’s creation time.  

mapping (address => bool) public isSpoke

A public mapping associating an address to a boolean indicating that the address is a Spoke of the water<b>melon</b> fund.  

Public Functions

function shutDownFund() public

This function sets the ìsShutDown state variable to true, effectively disabling the fund for all activities except investor redemptions. This function can only be successfully called by the manager address. The function’s actions are permanent and irreversible.  

function setSpokes(address[12] _spokes) onlyCreator

This function takes an array of addresses and sets all member variables of the routes state variable struct if they have not already been set as part of the fund initialization sequence. The function then sets the spokesSet state variable to true. This function implements the onlyCreator modifier.  

function setRouting() onlyCreator

This function requires that the Spokes and Routings have been set. It then initializes (see Spoke initialize() below) all registered Spokes and finally sets the routingSet state variable to true. This function implements the onlyCreator modifier.  

function setPermissions() onlyCreator

This function requires that the Spokes and Routings have been set, but that permissions have not been set. Next, the functions explicitly sets specific, design-intended permissions between the calling contracts and called contracts’ functions. The function then finally sets the permissionsSet state variable to true. This function implements the onlyCreator modifier.  

function vault() view returns (address)

This view function returns the vault Spoke address.  

function accounting() view returns (address)

This view function returns the accounting Spoke address.  

function priceSource() view returns (address)

This view function returns the priceSource contract address.  

function participation() view returns (address)

This view function returns the participation Spoke address.  

function trading() view returns (address)

This view function returns the trading Spoke address.  

function shares() view returns (address)

This view function returns the shares Spoke address.  

function policyManager() view returns (address)

This view function returns the policyManager Spoke address.  

Spoke.sol

Description

All Spoke contracts are registered with only one Hub and are initialized, storing the routes to all other Spokes registered with the Hub. Spokes serve as an abstraction of functionality and each Spoke has distinct and separate business logic domains, but may pragmatically access other Spokes registered with their Hub.  

Inherits from

DSAuth (link)

 

On Construction

Sets the hub state variable and sets the the hub as the authority and owner as defined for permissioning within DSAuth.

 

Structs

Routes

Member variables:

address accounting address feeManager address participation address policyManager address shares address trading address vault address priceSource address canonicalRegistrar address version address engine address mlnAddress

This struct stores the contract addresses of the listed components and spokes.  

Enums

None.

 

Modifiers

modifier notShutDown()

This modifier requires that the fund is not shut down and is evaluated prior to the execution of the functionality of the implementing function.  

modifier onlyInitialized()

This modifier requires that the initialized state variable is set to true.  

Events

None.

 

Public State Variables

Hub public hub

A variable of type Hub, defining the specific Hub contract to which the Spoke is connected.  

Routes public routes

A Routes struct storing all initialized Spoke and component addresses.  

bool public initialized

A boolean variable defining the initialization status of the Spoke.  

Public Functions

function initialize(address[12] _spokes) public auth

This function requires firstly that the Spoke not be initialized, then takes an array of addresses of all Spoke- and component contracts, sets all members of the routes struct state variable and finally sets initialized = true and the owner to address “0”.  

function engine() view returns (address)

This view function returns the engine component contract address.  

function mlnToken() view returns (address)

This view function returns the MLN token contract address.  

function priceSource() view returns (address)

This view function returns the priceSource component contract address.  

function version() view returns (address)

This view function returns the version component contract address.  


Fund Manager

Overview

The Manager (Investment Manager) is the central actor in the watermelon Fund. The Investment Manager is the creator of the watermelon Fund and as such, the owner of the watermelon Fund smart contract. In this capacity, the Investment Manager designs the initial set up of the watermelon Fund, including:

Once the watermelon Fund is set up, it will be open for investment from Investors. When Investors transfer investment capital to the watermelon Fund, the Investment Manager has full discretionary investment allocation authority over the assets, although Investors retain ultimate control over their respective shares in the watermelon Fund. The discretionary investment allocation authority is, however, constrained by those Risk Engineering rules put in place by the Investment Manager during fund set up.

The critical point about a watermelon Fund is that Investors retain control of their investment in the fund, while delegating asset management activity to the Investment Manager, who has the fine-grained trade and asset management authority, but no ability to remove asset tokens from the segregated safety of the watermelon Fund’s smart contact custody.

Fund Interaction

The Investment Manager in the capacity of owner of the watermelon Fund smart contract can as the ability to interact with the following smart contract functions:

Please refer to the Fund components documentation for further details on these functions.


Investors

A watermelon Fund is setup to serve Investors. That is, an Investment Manager creates a watermelon Fund in order to provide the service of managing capital on behalf of participating Investors.

Investors are individuals or institutions participating in the performance of watermelon Funds by sending cryptographic Asset tokens to the specific watermelon Fund via the fund’s requestInvestment() function.

In the context of the platform, the Investor is the Ethereum address from which the requestInvestment() was called and tokens sent (i.e. msg.sender).

Investor Address

The individual Investor must retain the responsibility of secure custody of the Investor address’s private key. The private key is required to sign valid Ethereum transactions which are ultimately responsible for calling specific functions and sending token amounts, covering the activities of investing, redeeming and redeeming by slice.

WARNING: Loss or poor security of the Investor address’s private key will result in the irreversible and permanent loss of all crypto asset tokens associated with that address.

Ethereum Addresses and Keys

An Ethereum address takes the hexidecimal format of 20 bytes prefixed by “0x”. Ethereum addresses are all lowercase, but may be mixed case if a checksum is implemented. The private key is 256 random bits.

Example Ethereum Address:
0xdd4A3d9D44A670a80bAbbd60FD300a4C8C38561f

Example Ethereum Private Key: d43689ae52d6a4e0e95d41bc638e95a66c4e7d0852f6db83d44c234ce9267d0d

Participation.sol

Description

The Participation contract encompasses the entire watermelon fund interface to the investor and manages or delegates all functionality pertaining to fund subscription and redemption activities including the creation/destruction of watermelon fund shares, fee calculations, asset token transfers and enabling/disabling specific asset tokens for subscription.

Inherits from

ParticipationInterface, DSMath, AmguConsumer, Spoke (links)

On Construction

The contract requires the hub address and an array of default asset addresses. These inputs set the hub and enable investment for the assets passed in to the constructor.

The Participation contract is created from the ParticipationFactory contract, which creates a new instance of Participation given the hub address, registering the address of the newly created Participation contract as a child of the ParticipationFactory.

Structs

Request

Member variables:

address investmentAsset - The address of the asset token with which the subscription is made. uint investmentAmount - The quantity of tokens of the investmentAsset uint requestedShares - The quantity of fund shares for the request uint timestamp - The timestamp of the block containing the subscription transaction uint atUpdateId - [CHECK]

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

mapping (address => Request) public requests

A public mapping which assigns an investor’s address to a Request struct.  

mapping (address => bool) public investAllowed

A public mapping which specifies all asset tokens which have been enabled for subscription to the watermelon fund.  

uint constant public SHARES_DECIMALS = 18

An integer constant which specifies the decimal precision of a single share. The value is set to 18.  

uint constant public INVEST_DELAY = 10 minutes

An integer constant which specifies the the number of seconds a valid subscription request must be delayed before a subscription is executed.  

Public Functions

function enableInvestment(address[] _assets) public auth

This function requires that the caller is the owner or the current contract. The function iterates over an array of provided asset token addresses, ensuring each is registered with the watermelon fund’s PriceFeed, and ensures that registered asset token addresses are set to true in the investAllowed mapping. Finally the function emits the EnableInvestment() event, logging the _assets.  

function disableInvestment(address[] _assets) public auth

This function requires that the caller is the owner or the current contract. The function iterates over an array of provided asset token addresses and ensures that asset token addresses are set to false in the investAllowed mapping. Finally the function emits the DisableInvestment() event, logging the _assets.  

function requestInvestment(uint requestedShares, uint investmentAmount, address investmentAsset) external notShutDown payable amguPayable onlyInitialized

This function ensures that the fund is not shutdown and that subscription is permitted in the provided ìnvestmentAsset. The function then creates and populates a Request struct (see details above) from the function parameters provided and adds this to the requests mapping corresponding to the msg.sender. Finally, this function emits the InvestmentRequest event. Execution of this functions requires payment of AMGU ETH to the watermelon Engine. This function implements the notShutDown, payable, amguPayable and onlyInitialized modifiers.  

function cancelRequest() external

This function removes the request from the requests mapping for the request corresponding to the msg.sender address. The function requres the request timestamp to be greater than “0”, then emits the CancelRequest event.  

function executeRequestFor(address requestOwner) public notShutDown amguPayable payable

This function: ensures that the fund is not shutDown, ensures that the investor address is permitted to carry out the action, ensures that a request corresponding to msg.sender exists in the requests mapping, ensures that subscription is permitted in the subscribing asset token, ensures that the price for the subscribing asset token is recent, ensures that management fee shares have been calculated and allocated immediately prior to subscription, calculates the cost of the share quantity requested (in denomination asset terms), ensures that the cost of the share quantity requested is sufficiently covered by the investmentAmount, maintains the requests mapping by removing the current Request corresponding to msg.sender, ensures the transfer of the subscription tokens to the fund’s vault, creates and transfers/allocates the commensurate number of shares for the subscription to the msg.sender (investor) address, determines if the asset is currently held by the fund; if not, the asset is added to the fund’s owned assets, and finally, the function emits the RequestExecuted() event, broadcasting the event’s parameters (see event specification above). The function reverts all work if any permission, quantity, asset or address are determined to be invalid.  

function getOwedPerformanceFees(uint shareQuantity) view returns (uint remainingShareQuantity)

This view function calculates and returns the quantity of shares owed for payment of accrued performance fees given the provided quantity of shares being redeemed.  

function redeem() public

This function determines the quantity of shares owned by msg.sender and calls redeemQuantity(). A share-commensurate quantity of all token assets in the fund are transferred to msg.sender, i.e. the investor. This function redeems all shares owned by msg.sender.  

function redeemQuantity(uint shareQuantity) public

This function allows the investor to redeem a specified quantity of shares held by the msg.sender, i.e. the investor.  

function redeemWithConstraints(uint shareQuantity, address[] requestedAssets) public

This function: ensures that the requested redemption share quantity is less than or equal to the share quantity owned by the investor (msg.sender), ensures that management fee shares have been calculated and allocated immediately prior to redemption, ensures that accrued performance fee shares have been calculated, allocated to the manager, removed from the investor’s balance and added to total share supply immediately prior to redemption, maintains parity between requestedAssets[] array parameter and the redeemedAssets[] internal array, ensuring that individual asset tokens are only redeemed once, calculates the proportionate quantity of each token asset owned by the investor (msg.sender) based on share quantity, destroys the investor’s shares and reduces the fund’s total share supply by the same amount, safely transfers all token assets in the correct quantities to the msg.sender (investor) address using the ERC20 transfer() function, and finally, the function emits the SuccessfulRedemption() event along with the quantity of successfully redeemed shares.  

function hasRequest(address _who) view returns (bool)

This view function returns a boolean indicating whether the provided address has a corresponding active request with a positive quantity of requested shares in the requests mapping.  

function hasValidRequest(address _who) public view returns (bool)

This public view function returns a boolean indicating the _who address parameter has a valid request, meaning that either this is the address’s first subscription or the INVEST_DELAY is respected, the subscription amount is greater than “0” and the requested share quantity is greater than “0”.


Shares

Shares.sol

Description

In fund management, shares assign proportionate ownership of the collective underlying assets held in the fund. Each share in a fund has equal claim to the fund’s assets. Shareholders then have a claim to the underlying fund assets in proportion to the quantity of shares under their control, i.e. ownership.

In a watermelon fund, fund shares are represented as ERC20 tokens. Each token has equal and proportionate claim to the underlying assets. The share tokens are fungible but are not transferrable [CHECK if this will remain so.] between owners.

As subscribed capital enters the fund, the Shares contract will create a proportionate quantity of share tokens based on the current market prices of the fund’s underlying assets and the market price of the subscribed capital and assign allocate these newly created share tokens to the subscribing address.

When share tokens are redeemed. A proportionate quantity of the fund’s underlying asset tokens are transferred to the redeeming address and the redeeming address’s redeemed share tokens are destroyed (i.e. set to 0) and the fund’s total share quantity is reduced by that same quantity.

Inherits from

Spoke, StandardToken (links)

 

On construction

The Shares contract requires the address of the hub and becomes a spoke of the hub. The Shares contract describes the watermelon fund’s shares as ERC20 tokens. The share tokens take their name as defined by the hub. The share token’s symbol and decimals are hardcoded in the contract.

The Shares contract is created from the sharesFactory contract, which creates a new instance of Shares given the hub address, register the address of the newly created shares contract as a child of the sharesFactory.  

Public State variables

string public symbol

A public string variable denoting the watermelon fund’s share token symbol, currently defaulting to “MLNF”.  

string public name

A public string variable denoting the watermelon fund’s name as specified by the manager at set up.  

uint8 public decimals

A public integer variable storing the decimal precision of the watermelon fund’s share quantity, currently defaulting to 18, the same decimal precision as ETH and many other ERC20 tokens.  

Structs

None.

 

Enums

None.

 

Public Functions

function createFor(address who, uint amount) auth

This function requires that the caller is the owner or the current contract. This function calls internal function _mint(), which increases both totalSupply and the balance of the address by the same quantity.  

function destroyFor(address who, uint amount) auth

This function requires that the caller is the owner or the current contract. This function calls internal function _burn(), which decreases both totalSupply and the balance of the address by the same quantity.  

Reverting functions:

function transfer(address to, uint amount) public returns (bool)  

function transferFrom(address from, address to, uint amount) public returns (bool)  

function approve(address spender, uint amount) public returns (bool)  

function increaseApproval(address spender, uint amount) public returns (bool)  

function decreaseApproval(address spender, uint amount) public returns (bool)  


Vault

General

Vault.sol

Description

The Vault makes use of the auth modifier from the DSAuth dependency. The auth functionality ensures the precise provisioning of call permissions, and focuses on granting owner or the current contract call permissions.

The contract provides extra security around ERC20 deposit and withdraw functionality, into- and from the vault, respectively. This is achieved by ensuring that only the owner can withdraw from the vault when the vault’s locked state is false.

The Vault contract is created from the vaultFactory contract, which creates a new instance of the Vault given the hub address, register the address of the newly created vault contract as a child of the vaultFactory and finally emits an event, broadcasting the creation event along with the address of the vault contract.

 

Inherits from

VaultInterface, Spoke (link)

 

On construction

Requires the Hub address and uses this to instantiate itself as a Spoke.  

Structs

None

 

Modifiers

onlyUnlocked()

Before any execution, this modifier requires that the vault contract’s locked state variable is false.  

Events

event Lock(bool status)

This event is triggered when the status of the locked state variable is changed. The event logs the new status.

Public State Variables

bool public locked

A public boolean state variable which indicates the lock state of the vault.  

Public Functions

function lockdown() auth

This function requires that the caller is the owner or the current contract. The function only sets the locked state variable to true.  

function unlock() auth

This function requires that the caller is the owner or the current contract. The function only sets the locked state variable to false.  

function withdraw(address token, uint amount) onlyUnlocked auth

This function requires that the caller is the owner or the current contract, and that the locked state of the vault be false. This function calls the transfer() ERC20 function of the provided asset token contract address, transferring ownership of the provided amount from the vault to the custody of the owner.  


Accounting

General

Accounting.sol

Description

The Accounting contract defines the accounting rules implemented by the fund. All operations concerning the underlying fund positions, fund position maintenance, asset token pricing, fees, Gross- and Net Asset value calculations and per-share calculations come together in this contract’s business logic.  

Inherits from

AccountingInterface, AmguConsumer, Spoke (links)

 

On Construction

The contract requires the hub address, the denomination asset address, the native asset address and an array of default asset addresses. These inputs set the accounting spoke’s denomination asset, denomination asset decimals, the default share price (1.0 in denomination asset terms) and add all default assets passed in to the ownedAssets array state variable.

The Accounting contract is created from the AccountingFactory contract, which creates a new instance of Accounting given the hub address, registering the address of the newly created Accounting contract as a child of the AccountingFactory.  

Structs

Calculations

Member Variables:

uint gav - The Gross Asset Value of all fund positions
uint nav - The Net Asset Value of all fund positions
uint allocatedFees - Fee shares accrued since the previous fee calculation about to be allocated uint totalSupply - The quantity of fund shares
uint timestamp - The timestamp of the current transactions block  

Enums

None.

Modifiers

None.

Public State Variables

uint constant public MAX_OWNED_ASSETS = 20

A constant integer determining the maximum quantity of individual asset token positions able to be held at any point in time by a watermelon fund. This constant is set to “20”.  

address[] public ownedAssets

A pubic array containing the addresses of tokens currently held by the fund.  

mapping (address => bool) public isInAssetList

A mapping defining the status of an asset’s membership in the ownedAssets array.  

uint public constant SHARES_DECIMALS = 18

A public constant representing the decimal precision of watermelon fund share token.  

address public NATIVE_ASSET

The address of the asset token native to the platform.  

address public DENOMINATION_ASSET

The address of the token defined to be the denomination asset, or base currency of the fund. NAV, performance and all fund-level metrics will be denominated in this asset.  

uint public DENOMINATION_ASSET_DECIMALS

An integer determining the decimal precision, or the degree of divisibility, of the denomination asset.  

uint public DEFAULT_SHARE_PRICE

An integer determining the initial “sizing” of one share in the fund relative to the denomination asset. The share price at fund inception will always be one unit of the denomination asset.  

Calculations public atLastAllocation

A Calculations structure holding the latest state of the member fund calculations described above.  

Public functions

function getOwnedAssetsLength() view returns (uint)

This public view function returns the length of the ownedAssets array state variable.

function getFundHoldings() returns (uint[], address[])

This function returns the current quantities and corresponding addresses of the funds token positions as two distinct order-dependent arrays.  

function calcAssetGAV(address ofAsset) returns (uint)

This function calculates and returns the current fund position GAV (in denomination asset terms) of the individual asset token as specified by the address provided.  

function assetHoldings(address _asset) public returns (uint)

This function returns the fund position quantity of the asset token as specified by the address provided.  

function calcGav() public returns (uint gav)

This function calculates and returns the current Gross Asset Value (GAV) of all fund assets in denomination asset terms.  

function calcNav(uint gav, uint unclaimedFees) public pure returns (uint)

This function calculates and returns the fund’s Net Asset Value (NAV) given the provided fund GAV and current quantity of unclaimed fee shares.  

function valuePerShare(uint totalValue, uint numShares) view returns (uint)

This function calculates and returns the value (in denomination asset terms) of a single share in the fund given the fund total value and the total number of shares provided.  

function performCalculations() returns (uint gav, uint unclaimedFees, uint feesInShares, uint nav, uint sharePrice)

This view function returns bundled calculations for GAV, NAV, unclaimed fees, fee share quantity and current share price (in denomination asset terms).  

function calcSharePrice() returns (uint sharePrice)

This function calculates and returns the current price (in denomination asset terms) of a single share in the fund.  

function getShareCostInAsset(uint _numShares, address _altAsset) returns (uint)

This public function calculates and returns the quantity of the _altAsset asset token commensurate with the value of _numShares quantity of the watermelon fund’s shares.  

function triggerRewardAllFees() public amguPayable payable

This public function updates ownedAssets and rewards all fees accrued to the current point in time. The function then updates the atLastAllocation struct state variable. The function is payable and also implements the amguPayable modifier, requiring amgu payment.  

function updateOwnedAssets() public

This function maintains the ownedAssets array by removing or adding asset addresses as the fund holdings composition changes.  

function addAssetToOwnedAssets(address _asset) public auth

This function requires that the caller is the owner or the current contract. The function maintains the ownedAssets array and the isInOwedAssets mapping by adding asset addresses as the fund holdings composition changes.  

function removeFromOwnedAssets(address _asset) public auth

This function requires that the caller is the owner or the current contract. The function maintains the ownedAssets array and the isInOwedAssets mapping by removing asset addresses as the fund holdings composition changes.  


Fees

General

Fees are charges levied against a watermelon fund’s assets for:

Legacy investment funds are typically run as legal vehicle; a business with income and expenses. As a traditional fund incurs expenses (not only those listed above, but others like administration-, custody-, directors-, audit fess), traditional funds must liquidate assets to cash or pay the expenses out of cash held by the fund. This necessarily reduces the assets of the fund and incurs further trading costs.

watermelon funds employ a novel and elegant solution: Instead of using cash, or liquidating positions to pay fees, the watermelon fund smart contract itself calculates and creates new shares (or share fractions), allocating them to the investment manager’s own account holdings within the fund as payment.

At that point, the investment manager can continue holding an increased number of shares in their own fund, or redeem the shares to pay their own operating costs, expand their research activities or whatever else they deem appropriate. The watermelon Fund smart contract autonomously and verifiably maintains the shareholder accounting impact in a truly reliable and transparent manner.

Paying fees in this manner has a few interesting side-effects: Assets do not leave the fund, as would a cash payment. There are no unnecessary trading or cash management transactions. Investors and managers have access to real-time fee accrual metrics. Finally, the incentives to the manager are reinforced beyond a cash performance fee by being paid in the currency that is their own product.

The Management Fee and Performance Fee are each represented by an individual contract. The business logic and functionality of each fee is defined within the respective contract. The contract will interact with the various components of the fund to calculate and return the quantity of shares to create. This quantity is then added to the Manager address balance and to the total supply balance.

The Fee Manager component manages the individual fee contracts which have been configured for the watermelon fund at set up. The core of the Fee Manager is an array of Fee contract instances. Functions exist to prime this array at fund setup with the selected and configured fee contracts, as well as basic query functionality to aid the user interface in calculating and representing the share NAV.

Fees are calculated and allocated when fund actions such as subscribe, redeem or claim fees are executed by a participant. Calling rewardAllFees() will iterate over the array of registered fees in the fee manager, calling each fee’s calculation logic. This returns the quantity of shares to create and allocate to the manager.

It is important to note that fee calculations take place before the fund’s share quantity is impacted by subscriptions or redemptions. To this end, when a subscription or redemption action is initiated by an investor, the execution order first calculates fee amounts and creates the corresponding share quantity, as the elapsed time and share quantity at the start is known. Essentially, the watermelon fund calculates and records a reconciled state immediately and in the same transaction where share quantity changes due subscription/redemption.

Management Fees

Management Fees are earned with the passage of time, irrespective of performance. The order of fee calculations is important. The Management Fee share quantity calculation is a prerequisite to the Performance Fee calculation, as fund performance must be reduced by the Management Fee expense to fairly ascertain net performance.

The Management Fee calculation business logic is fully encapsulated by the Management Fee contract. This logic can be represented as follows.

First, the time-weighted, pre-dilution share quantity is calculated:  

     

then, this figure is scaled such that investors retain their original share holdings quantity, but newly created shares represent the commensurate fee percentage amount:  

     

where,

      = pre-dilution quantity of shares

      = current shares outstanding

      = number of seconds elapsed since previous conversion event

      = number of seconds in a year ( = 60 _ 60 _ 24 * 365 )

      = Management Fee rate

      = number shares to create to compensate Management Fees earned during the conversion period  

Performance Fees

Performance Fees accrue over time with performance, but can only be harvested after regular, pre-determined points in time. This period is referred to as the Measurement Period and is decided by the fund manager and configured at fund set up.

Performance is assessed at the end of the Measurement Period by comparing the fund’s current share price net of Management Fees to the fund’s current high-water mark (HWM).

The HWM represents the highest share valuation which the watermelon fund has historically achieved at a Measurement Period ending time. More clearly, it is not a fund all-time-high, but rather the maximum share valuation of all Measurement Period-end snapshot valuations.

If the difference to the HWM is positive, performance has been achieved and a Performance Fee is due to the fund manager. This calculation is straightforward and is the aforementioned difference multiplied by the Performance Fee rate. The Performance Fee is not an annualized fee rate.

The calculation of the Performance Fee requires that, at that moment, no Management Fees are due. This will be true as the code structure always invokes the Management Fee calculation and allocation immediately preceding, and in the same transaction, the Performance Fee calculation.

The Performance Fee calculation business logic is fully encapsulated by the Performance Fee contract. This logic can be represented as follows.  

     

 

     

 

     

 

     

where,

      = high-water mark for the Measurement Period

      = current share price net of Management Fee

      = current fund Gross Asset Value

      = current fund GAV per share

      = performance for the Measurement Period

      = pre-dilution quantity of shares

      = current shares outstanding

      = Performance Fee rate

     = number shares to create to compensate Performance Fees earned during the conversion period  

While Performance Fees are only crystalized at the end of each measurement period, there must be a mechanism whereby redeeming investors compensate for their current share of accrued performance fees prior to redemption.

In the case where an investor redeems prior to the current Measurement Period’s end and where the current share price exceeds the fund HWM, a Performance Fee is due. The Redemption business logic calculates the accrued Performance Fee for the entire fund at the time of redemption and weights this by the redemption share quantity’s proportion to the fund’s total share quantity. The resulting percentage is the proportion of the redemption share quantity due as the redeeming investor’s Performance Fee payment. This quantity is deducted from the redeeming share quantity and credited to the manager’s share account balance. The remaining redeeming share quantity is destroyed as the proportionate individual token assets are transferred out of the fund and the fund’s total share quantity and the investor’s share quantity is reduced by this net redeeming share quantity.

Note on Fee Share Allocation

The intended behavior is for the Manager to immediately redeem fee shares as they are created. This will ensure a fair and precise share allocation. The implemented code that represents the fee calculations above contain smart contract optimizations for state variable storage. By not immediately redeeming fee shares, a negligible deviation to the manager fee payout will arise due to this optimization.

FeeManager.sol

Description

The Fee Manager is a spoke which is initialized and permissioned in the same manner as all other spokes. The Fee Manager registers and administers the execution order of the individual fee contracts.

Inherits from

Spoke, DSMath (link)

 

On Construction

Sets the hub of the spoke.

 

Structs

None.

 

Enums

None.

 

Public State Variables

Fee[] public fees

An array of type fees storing the defined fees.  

mapping (address => bool) public feeIsRegistered

A mapping storing the registration status of a fee address.  

Public Functions

function register(address feeAddress) public

This function adds the feeAddress provided to the Fee[] array and sets the feeIsRegistered mapping for that address to true.  

function totalFeeAmount() public view returns (uint total)

This function returns the total amount of fees incurred for the hub.  

function rewardAllFees() public

This function creates shares commensurate with all fees stored in the Fee[] state variable array.  

function rewardManagementFee() public

This public function calculates, creates and allocates the quantity of shares currently due as the management fee.  

function performanceFeeAmount() public view returns (uint)

This public view function calculates and returns the quantity of shares currently due as the performance fee.  

ManagementFee.sol

Description

The ManagementFee contract contains the complete business logic for the creation of fund shares based on assets managed over a specified time period.  

Inherits from

Fee and DSMath (link)

 

On Construction

None.

 

Structs

None.

 

Enums

None.

 

Public State Variables

uint public PAYMENT_TERM

An integer defining the measurement period in seconds.  

uint public MANAGEMENT_FEE_RATE

An integer defining the management fee percentage rate.  

uint public DIVISOR

An integer defining the standard divisor.  

uint public lastPayoutTime

An integer defining the block time in UNIX epoch seconds when the previous fee payout was executed.  

Public Functions

function feeAmount(address hub) public view returns (uint feeInShares)

This function calculates and returns the number of shares to be created given the amount of time since the previous fee payment, asset value and the defined management fee rate.  

function updateState(address hub) external

This function sets lastPayoutTime to the current block timestamp.  

PerformanceFee.sol

Description

The PerformanceFee contract contains the complete business logic for the creation of fund shares based on fund performance over a specified Measurement Period and relative to the fund-internally-defined HWM.

Inherits from

Fee, DSMath (link)

 

On Construction

None.

 

Structs

None.

 

Enums

None.

 

Public State Variables

uint public PERFORMANCE_FEE_RATE

An integer defining the performance fee percentage rate.  

uint public constant DIVISOR = 10 ** 18

A constant integer defining the standard divisor.  

uint public constant INITIAL_SHARE_PRICE = 10 ** 18

A constant integer defining the initial share price to “1”.  

mapping(address => uint) public highWaterMark

A public mapping associating the watermelon fund address to the watermelon fund’s highWaterMark, which defines the asset value which must be exceeded at the measurement period’s end to facilitate the determination of the performance fee due to the manager.  

mapping(address => uint) public lastPayoutTime

A public mapping associating the watermelon fund address to the watermelon fund’s last previous time a performance fee calculation and payout was executed. It is an integer defining the block time in UNIX epoch seconds when the previous fee payout was executed.  

mapping(address => uint) public performanceFeeRate

A public mapping associating the watermelon fund address to the watermelon fund’s configured performance fee rate.  

mapping(address => uint) public performanceFeePeriod

A public mapping associating the watermelon fund address to the watermelon fund’s configured performance measurement period. This integer express the performance measurement period in seconds.  

Public Functions

function initializeForUser(uint feeRate, uint feePeriod) external

This external function is execution once at the initialization of the watermelon fund. The function sets the watermelon fund’s performance fee rate, the performance measurement period, the initial high-watermark and the lastPayoutTime.

function feeAmount() public view returns (uint feeInShares)

This function calculates and returns the number of shares to be created given the fund performance since the previous measurement period payment, asset value and the defined performance fee rate.  

function updateState(address hub) external

This function sets the highwatermark and lastPayoutTime if applicable.  


Trading

The watermelon Protocol integrates with decentralized exchanges to facilitate the trading of Assets, one of the essential functionalities of a watermelon Fund. This means that a watermelon Fund must accommodate multiple different decentralized exchanges smart contracts if the fund is to draw from a wider pool of liquidity.

Trading.sol

Description

The Trading contract is created by the tradingFactory, which holds a queryable array of all created Trading contracts and emits the instanceCreated event along with the trading contract’s address.

The trading contract:

-manages the set up of the interface infrastructure to the various selected exchanges.

-manages open make orders that the fund has submitted to the various registered exchanges.

-houses the common function for calling specific exchange functions through each exchange’s respective exchange adapters. Note that each exchange will have their own unique interface and required parameters, which the adapters accommodate.

-provides a public function to transfer an array of token assets to the fund’s vault.

-provides view functions to get order information regarding specific assets on specific exchanges as well as specific order detail.

The contract has a fallback function to retrieve ETH.

Inherits from Spoke, DSMath, TradingInterface (link)

On Construction

The contract requires the hub address, an array of exchange addresses, an array of exchange adapter addresses and an array of booleans indicating if an exchange takes custody of tokens for make orders. The contract becomes a spoke to the hub. The constructor of the trading contract requires that the length of the exchange array matches the length of exchange adapter array and the length of the exchange array matches the length of boolean custody status array. Finally, the constructor builds the public variable exchanges[] array of Exchange structs.

Structs

Exchange

Member Variables

address exchange - The address of the decentralized exchange smart contact.

address adapter - The address of the corresponding adapter smart contract.

bool takesCustody - An flag specifying whether the exchange holds the asset token in its own custody for make orders.  

Order

Member Variables

address exchangeAddress - The address of the decentralized exchange smart contact.

bytes32 orderId - A unique identifier for a specific order on the specific exchange.

UpdateType updateType - A struct indicating the update behavior/action of the order. Permitted values are make, take and cancel.

address makerAsset - The address of the asset token owned and provided in exchange.

address takerAsset - The address of the asset token to be received in exchange.

uint makerQuantity - An integer representing the quantity of the asset token owned and provided in exchange.

uint takerQuantity - An integer representing the quantity of the asset token owned and provided in exchange.

uint timestamp - The timestamp of the block in which the submitted order transaction was mined.

uint fillTakerQuantity - An integer representing the quantity of the maker asset token traded in the make order. This value is not necessarily the same as the makerQuantity because taker participants can choose to only partially execute the order, i.e. take a lower quantity of the makerAsset token than specified by the order’s makerQuantity.  

OpenMakeOrder

Member Variables

uint id - An integer originating from the exchange which uniquely identifies the order within the exchange.

uint expiresAt - An integer representing the time when the order expires. The timestamp is represented by the Ethereum blockchain as a UNIX Epoch. After order expiration, the order will no longer [exist/be active] on the exchange and custody, if the exchange held custody, returns from the exchange contract to the fund contract.

Enums

UpdateType - An enum which characterizes the type of update to the order.

Member Types

make - Indicates a make order type, where a quantity of a specific asset is offered at a specified price.

take - Indicates a take order type, where the offered quantity (or less) of a specific asset is accepted at the specified price by the counterparty. Taking less than the offered quantity in the order would be considered a “partial fill” of the order.

cancel - A type indicating that the update performed will cancel the order.

swap- A type specific to the Kyber exchange adapter, similar in functionality to take described above.

Public State variables

Exchange[] public exchanges

A public array of Exchange structs which stores all exchanges with which the fund has been initialized.  

Order[] public orders

A public array of Order structs which stores all active orders [CHECK] on the  

mapping (address => bool) public exchangeIsAdded

A public mapping which indicates that a specific exchange (as identified by the exchange address) is registered for the fund.  

mapping (address => mapping(address => OpenMakeOrder)) public exchangesToOpenMakeOrders

A public compound mapping associating an exchange address to open make orders from the fund on the specific exchange.  

mapping (address => bool) public isInOpenMakeOrder

A public mapping indicating that the specified token asset is currently offered in an open make order on an exchange.  

mapping (bytes32 => LibOrder.Order) public orderIdToZeroExOrder

A public mapping a ZeroEx order identifier to a ZeroEx Order struct.  

uint public constant ORDER_LIFESPAN = 1 days

A public constant specifying the number of seconds that an order will remain active on an exchange. This number is added to the order creation date’s timestamp to fully specify the order’s expiration date. 1 days is equal to 86400 ( 60 * 60 * 24 ).  

Modifiers

delegateInternal()

A modifier which requires that the caller (msg.sender) is the current contract Trading.sol. This ensures that only the current contract can call a function implementing this modifier.  

Public functions

function() public payable

The contracts fallback function making the contract able to receive ETH sent to the contract without a funciton call.  

function isOrderExpired(address exchange, address asset) view returns (bool)

This public view function returns a boolean indicating whether an order for the asset token and exchange provided is currently expired.  

function addExchange(address _exchange, address _adapter, bool _takesCustody) internal

This is an internal function which is called for each exchange address passed to the constructor, adding the full exchange struct to the exchanges array state variable. The function ensures that an exchange has not previously been registered.  

function callOnExchange( uint exchangeIndex, string methodSignature, address[6] orderAddresses, uint[8] orderValues, bytes32 identifier, bytes makerAssetData, bytes takerAssetData, bytes signature ) public onlyInitialized

This is the fund’s general interface to each registered exchange for trading asset tokens. The client will call this function for specific exchange/trading interactions. This function first calls the policyManager to ensure that function-specific policies are pre- or post-executed to ensure that the exchange trade adheres to the policies configured for the fund. This function implements the onlyInitialized modifier. Finally, the function emits the ExchangeMethodCall() event, logging the specified parameters.  

function addOpenMakeOrder( address ofExchange, address sellAsset, uint orderId, uint expirationTime ) delegateInternal

This function can only be called from within the current contract. The function ensures that the sell asset token does not already have a current open sell order and that there are one or more orders in the orders array. If the expirationTime is set to “0”, the order’s expiration time is set to ORDER_LIFESPAN. The expiration time is required to be greater that the current block.timestamp and less than or equal to the sum of the ORDER_LIFESPAN and block.timestamp. The function then sets the isInOpenMakeOrder mapping for the sell asset token address to true, and sets the details of the address’s openMakeOrder struct on the contracts exchangesToOpenMakeOrders mapping.  

function removeOpenMakeOrder( address exchange, address sellAsset ) delegateInternal

This function can only be called from within the current contract. The function removes the provided sell asset token address entry for the provided exchange address.  

function orderUpdateHook( address ofExchange, bytes32 orderId, UpdateType updateType, address[2] orderAddresses, uint[3] orderValues ) delegateInternal

This function can only be called from within the current contract. The function used the input parameters and the current execution block’s timestamp to push make- or take orders to the orders array. [Why only make or take orders??]  

function updateAndGetQuantityBeingTraded(address _asset) returns (uint)

This function returns the sum of the quantity of the provided asset token address held by the current contract and the quantity of the provided asset token held across all registered exchanges in the fund’s make orders. The sum returned excluded quantities in make orders where the exchange does not take custody of the tokens.  

function updateAndGetQuantityHeldInExchange(address ofAsset) returns (uint)

This function sums and returns all quantities of the provided asset token address in make orders across all registered exchanges, excluding, however, quantities in make orders where the exchange does not take custody of the tokens, but uses the ERC-20 “approve” functionality. The rationale is that token quantities in “approve” status are not actually held by the exchange. The function also maintains the exchangesToOpenMakeOrders and isInOpenMakeOrder mappings.  

function returnAssetToVault(address _token) public

This public function transfers all token quantities of the provided array of token asset addresses from the current current contract’s custody back to the fund’s vault.  

function addZeroExOrderData(bytes32 orderId, LibOrder.Order zeroExOrderData) delegateInternal

This function adds the data provided by the parameters to the orderIdToZeroExOrder mapping state variable.  

function returnBatchToVault(address[] _tokens) public

This public function returns all asset tokens represented by the _tokens address array parameter and returns the asset tokens to the watermelon fund’s vault.  

function getExchangeInfo() view returns (address[], address[], bool[])

This public view function returns two address arrays and one boolean array with all corresponding registered exchange contract addresses, adapter contract addresses and the takesCustoday indicators.  

function getOpenOrderInfo(address ofExchange, address ofAsset) view returns (uint, uint, uint)

This public view function takes the exchange contract address and an asset token contract address and returns three integers: the order identifier, the order expiration time and the order index.  

function getOrderDetails(uint orderIndex) view returns (address, address, uint, uint)

This public view function takes the order index as a parameter and returns the maker asset token contract address, the taker asset token contract address, the maker asset token quantity and the taker asset token quantity.  

function getZeroExOrderDetails(bytes32 orderId) view returns (LibOrder.Order)

This public view function takes the order identifier as a parameter and returns the corresponding populated Order struct.  

Exchange Adapters

Exchange Adapters are smart contracts which communicate directly and on-chain with the intended DEX smart contract. They serve as a translation bridge between the watermelon Fund and the DEX.

Currently, the watermelon Protocol has adapters to integrate the following DEXs:

Each exchange is tied to a specific adapter by the canonical registrar. A fund can be setup to use multiple exchanges, provided they are registered by the registrar.

Adapter Function Details

The Fund.sol smart contract in the watermelon Protocol (the blockchain fund instance) commonly uses the following functions in the Exchange Adapter to interact with the intended DEX for trading purposes:

A single event is emitted by the Exchange Adapter:

The following functions are public view functions:

Note that fund is not limited to these functions and can call arbitrary functions on the exchange adapters using delegate calls, provided the function signature is whitelisted by the canonical registrar.  


Fund Policy

Policy Manager

The policyManager contract is the core of risk management and compliance policies. Policies are individual contracts that define and enforce specific business logic codified within. Policies are registered with the watermelon fund’s policyManager contract for specific function call pertaining to trading fund positions or investor subscriptions.

The policyManager is embedded into specific function calls in other Spokes of the watermelon fund as required through the modifiers described below.  

PolicyManager.sol

Description

In many of the functions below an array of address addresses, an array of uint values and an identifier are passed as parameters. For the arrays, the order of the address or value in the array is semantically significant. The individual array positions, [n], are defined within the policyManager contract as follows:

address[5] addresses:

[0] order maker - the address initiating or offering the trade [1] order taker - the address filling or partially filling the offered trade [2] maker asset - the token address of the asset token intended by the order maker to exchange for another token asset, i.e. the taker asset [3] taker asset - the token address of the asset token to be received by the offer maker in exchange for the maker asset [4] exchange address - address of the exchange contract on which the order is to be placed

uint[3] values:

[0] maker token quantity - the maximum quantity of the maker asset token to be given by the order maker in exchange for the specified taker asset token [1] taker token quantity - the maximum quantity of the taker asset token to be received by the order maker in exchange for the specified maker asset token [2] Fill amount - the quantity of the taker token exchanged in the transaction. Must be less than or equal to the taker token quantity [1]

Finally, identifier and sig parameters are described below:

bytes32 identifier - order id for exchanges utilizing a unique, exchange-specific order identifier

bytes4 sig - the keccak256 hash of the plain text function signature of the function which triggers the specific policy validation.  

Inherits from

Spoke (link)

 

On Construction

The PolicyManager contract is passed the corresponding Hub address and sets this as the hub state variable inherited from Spoke.

The PolicyManager contract is created from the PolicyManagerFactory contract, which creates a new instance of PolicyManager given the hub address, registering the address of the newly created PolicyManager contract as a child of the PolicyManagerFactory.  

Structs

Entry

Member variables:

Policy[] pre - An array of Policy contract addresses which are registered to be validated as pre-conditions to defined function calls.

Policy[] post - An array of Policy contract addresses which are registered to be validated as post-conditions to defined function calls.  

Enums

None.

 

Modifiers

modifier isValidPolicyBySig(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier)

This modifier ensures that preValidate() is called prior to applied function code and that postValidate() is called after the applied function code, sending the function signature hash sig as provided.  

modifier isValidPolicy(address[5] addresses, uint[3] values, bytes32 identifier)

This modifier ensures that preValidate() is called prior to applied function code and that postValidate() is called after the applied function code, sending the calling function signature hash msg.sig.  

Events

None.

 

Public State Variables

mapping(bytes4 => Entry) policies

A mapping of bytes4 to an Entry struct.  

Public Functions

function registerBatch(bytes4[] sig, address[] ofPolicies) public

This function requires equal length of both array parameters. The function then iterates over the sig array calling the register() function, providing each function signature hash and the corresponding Policy contract address.
 

function register(bytes4 sig, address ofPolicy) public

This function first ascertains whether the Policy being registered with the PolicyManager is to be executed as a pre- or post condition and then pushes the Policy with the corresponding signature hash on to the respective pre or post Policy array within the policies mapping. Once a Policy is registered, the condition defined within the Policy will be the standard against which the policy-registered function’s consequential state changes will be tested. If the state changes pass the policy test, the function will continue execution unhindered and the state changes, e.g. a trade and the respective changes to token allocation) will become final as part of a transaction in a mined block. If the state changes do not pass the policy test, the transaction will revert and no state change will be affected.  

function getPoliciesBySig(bytes4 sig) public view returns (address[], address[])

This view function returns two address arrays (pre and post, respectively) containing all Policy contract addresses registered for the provided function signature hash. This gives the ability to query registered policies for a specific function call.  

function preValidate(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view public

This view function calls the validate() function, explicitly passing the pre array from the policies mapping state variable filtered for Policies registered for the provided function signature hash sig.  

function postValidate(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view public

This view function calls the validate() function, explicitly passing the post array from the policies mapping state variable filtered for Policies registered for the provided function signature hash sig.  

function validate(Policy[] storage aux, bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) view internal

This internal view function receives a function-specific filtered array of Policy contract addresses and iterates over the array, calling each Policy’s implemented rule() function. If the call to a Policy’s rule() evaluates to true, execution and any state transition proceeds. If the call to a Policy’s rule() evaluates to false, all execution and preliminary state transitions are reverted.  

Policies

Polices are individual smart contracts which define rule or set of rules to be compared to the state of the watermelon fund. Policies simply assess the current state of the watermelon fund and resolve to a boolean decision, whether the action may be executed or not, returning true for allowed actions and false for disallowed actions. The watermelon fund-specific Policies are deployed with parameterized values which the defined Policy logic uses to assess the permissibility of the action.

Pre- and Post-Conditionality

Policies are intended to be rules; they are intended to permit or prevent specific behavior or action depending on the state as compared to specified criteria.

Some Policies, due to the nature of the data required, can be immediately resolved based on the current state. The resolution of the Policy result is trivial because all data needed is at hand and must nod be derived or calculated. Such Policies can be defined as “pre-condition” Policies.

Other Policies may need to assess the consequence of the behavior or action before the logic can assess its permissibility relative to the defined rule. To do this, the result of the action must be derived or calculated, essentially asking, “What will the state be if this action is executed?” Such Policies can be defined as “post-condition” Policies.

On the blockchain and in smart contracts, we can use a fortunate side-effect of the process of mining and block finalization to help determine the validity of post-condition Policies. With post-condition Policies, the action or behavior is executed with the smart contract logic and the changed (but not yet finalized or mined) state is assessed against the logic and defined parameters of the post-condition Policy. In the case where this new state complies with the logic and criteria of the Policy, the action is allowed, meaning the smart contract execution is allowed to run to completion, the block is eventually mined and this new compliant state is finalized in that mined block. In the case where the new state does not comply with the logic and criteria of the Policy, the action is disallowed and the revert() function is called, stopping execution and discarding (or rolling back) all state changes. In calling the revert() function, gas is consumed to arrive at the reference state, but any unused gas is returned to the caller as the reference state is discarded.  

Policy.sol

Description

The Policy contract is inherited by implemented Policies. This contract will be changed to an interface when upgraded to Solidity 0.5, as enums and structs are allowed to be defined in interfaces in that version.  

Inherits from

None.

 

On Construction

None.

 

Structs

None.

 

Enums

Applied - An enum which characterizes the conditionality type of the Policy.

Member Types

pre - Indicates that the Policy will be evaluated prior to the corresponding function’s execution.

post - Indicates that the Policy will be evaluated after the corresponding function’s execution.  

Modifiers

None.

 

Events

None.

 

Public State Variables

None.

 

Public Functions

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function is called by the PolicyManager to ensure that specific registered function calls are validated by their respective registered Policy validation logic. The function returns true when conditions defined in the Policy are met and execution continues to successful completion. If conditions defined in the Policy are no met, the function returns false, all execution up to that point is reverted and execution cannot complete, returning all remaining gas.

The rule() function takes a function signature hash sig, an array of address addresses, an array of uint values and an identifier are passed as parameters. For the arrays, the order of the address or value in the array is semantically significant. The individual array positions, [n], are defined as follows:

address[5] addresses:

[0] order maker - the address initiating or offering the trade [1] order taker - the address filling or partially filling the offered trade [2] maker asset - the token address of the asset token intended by the order maker to exchange for another token asset, i.e. the taker asset [3] taker asset - the token address of the asset token to be received by the offer maker in exchange for the maker asset [4] exchange address - address of the exchange contract on which the order is to be placed

uint[3] values:

[0] maker token quantity - the maximum quantity of the maker asset token to be given by the order maker in exchange for the specified taker asset token [1] taker token quantity - the maximum quantity of the taker asset token to be received by the order maker in exchange for the specified maker asset token [2] Fill amount - the quantity of the taker token exchanged in the transaction. Must be less than or equal to the taker token quantity [1]

Finally, identifier and sig parameters are described below:

bytes32 identifier - order id for exchanges utilizing a unique, exchange-specific order identifier

bytes4 sig - the keccak256 hash of the plain text function signature of the function which triggers the specific policy validation.  

function position() external view returns (Applied)

This view function returns the enum Applied which is defined for each specific Policy contract. The Applied enum indicates whether the Policy logic is applied prior to, or after the corresponding function’s execution. This function is called when the Policy contract is registered with the PolicyManager contract.  


Compliance

General

The contracts below define the business logic for the screening of investor addresses allowed to- or prevented from subscribing to a watermelon fund.

In the watermelon Protocol, the term “Compliance” revolves around the specifics of investing as an Investor into watermelon Fund. This is often referred to as a “Subscription”. Details around which addresses, amounts and when Investors may subscribe to the watermelon Fund are configured in-, and managed by the Compliance module. To clarify, in the traditional investment management industry, “compliance” is also used to refer to trading of underlying fund assets, as in a whether a certain asset or trade is compliant with portfolio guidelines, laws and regulations. This type of intra-portfolio asset level compliance is handled by the Risk Engineering module in the watermelon Protocol.

The main use case for the watermelon Fund Compliance module is the ability to create and maintain a whitelist for specific addresses. That is, the Investment Manager can explicitly define specific addresses which will then have the ability to subscribe to the watermelon Fund.

Note that any listing of an address on the Compliance module whitelist only impacts the Investor’s ability to subscribe to the watermelon Fund. An existing investment from a specific address will remain invested irrespective of any change in that address’s whitelist status. The current implementation does not affect an address status after the fact. Blacklisting does not affect an invested address’s current invested status or ability to redeem, but will prevent future subscriptions. The whitelist can be seen as a positive filter, creating a known universe of allowed investor addresses. Investor addresses can be added to- or removed from either list, individually or in batch by the fund manager (owner).

Future Directions

The watermelon Fund Compliance module potentially has great significance for regulators and regulatory frameworks. Possible use cases are: Regulators or KYC/AML providers may create and maintain their own whitelist of Investor addresses in a watermelon Compliance module that individual watermelon Funds could query; the ability for the Investment Manager to “Hard Close” or “Soft Close” subscriptions; the ability to cap subscription amounts.

Hard Close - A fund refuses all new investment subscriptions, usually due to capacity limitations for a specific strategy.

Soft Close - A fund refuses investment subscriptions new Investors, but accepts investment top-ups from existing Investors (addresses).

Whitelist.sol

Inherits from

Policy, DSAuth (link)

 

Description

This contract defines a positive filter list, against which subscribing addresses are verified for membership. Member addresses are allowed for subscription.

Inherits from

Policy, DSAuth (links)

 

On Construction

The Whitelist contract requires an array of addresses which are added to the whitelisted mapping.

 

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Public State Variables

mapping (address => bool) whitelisted

Mapping which designates an investor address as being eligible to subscribe to the fund.

 

Public Functions

function Whitelist(address[] _preApproved) public

Constructor function which enables the bulk addition of many addresses designated as eligible for subscription to the fund.  

function addToWhitelist(address _who) public auth

This function requires that the caller is the owner or the current contract. This function sets the whitelisted mapping for the provided address to true.  

function removeFromWhitelist(address _who) public auth

This function requires that the caller is the owner or the current contract. This function sets the whitelisted mapping for the provided address to false. Addresses which had previously subscribed and are invested can not subsequently subscribe further amounts.  

function batchAddToWhitelist(address[] _members) public auth

This function requires that the caller is the owner or the current contract. The function, with one transaction, enables multiple addresses in the whitelisted mapping to be set to true.  

function batchRemoveFromWhitelist(address[] _members) public auth

This function requires that the caller is the owner or the current contract. The function, with one transaction, enables multiple addresses in the whitelisted mapping to be set to false. Addresses which had previously subscribed and are invested can not subsequently subscribe further amounts.  

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This function is called by the Policy Manager when functions registered for this specific policy are called. See further documentation under Policy Manager (link).  

function position() external view returns (uint)

This function is called by the Policy Manager to determine whether the policy should be executed and evaluated as a pre-condition (0) or as a post-condition (1). Compliance contracts are called as a pre-condition to subscription and therefore return 0 during the whitelist compliance check. See further documentation under Policy Manager (link).  

Blacklist.sol

Description

This contract defines a negative filter list, against which subscribing addresses are verified for membership. Member addresses are disallowed for subscription.  

Inherits from

Policy, DSAuth (links)

 

On Construction

The Blacklist contract requires an array of addresses which are added to the blacklisted mapping.  

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Public State Variables

mapping (address => bool) blacklisted

Mapping which designates an investor address as being ineligible to subscribe to the fund.  

Public functions

function Blacklist(address[] _preBlacklisted) public

Constructor function which enables the bulk addition of many addresses designated as ineligible for subscription to the fund.  

function addToBlacklist(address _who) public auth

This function requires that the caller is the owner or the current contract. This function sets blacklisted mapping for the provided address to true.  

function removeFromBlacklist(address _who) public auth

This function requires that the caller is the owner or the current contract. This function sets the blacklisted mapping for the provided address to false. Addresses which previously were denied subscription would now be permitted.  

function batchAddToBlacklist(address[] _members) public auth

This function requires that the caller is the owner or the current contract. The function, with one transaction, enables multiple addresses in the blacklisted mapping to be set to true.  

function batchRemoveFromWhitelist(address[] _members) public auth

This function requires that the caller is the owner or the current contract. The function, with one transaction, enables multiple addresses in the blacklisted mapping to be set to false. Addresses which previously were denied subscription would now be permitted.  

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This function is called by the Policy Manager when functions registered for this specific policy are called. See further documentation under Policy Manager (link).  

function position() external view returns (uint)

This function is called by the Policy Manager to determine whether the policy should be executed and evaluated as a pre-condition (0) or as a post-condition (1). Compliance contracts are called as a pre-condition to subscription and therefore return 0 during the blacklist compliance check. See further documentation under Policy Manager (link).  


Risk Engineering Policies

General

Risk Management vs Risk Engineering

Risk is the effect of uncertainty on objectives; the risk is the possibility that an event will occur and adversely affect the achievement of an objective.

Risk is uncertainty with implicit consequences. In general, the investor cares about two types of risk: 1) those for which she receives compensation in return for bearing, and 2) those for which no compensation is received. It is obvious that the latter must be eliminated to the extent possible.

For example, the following shows risk dissected into systematic risk and unsystematic risk:

Related image

We can observe that unsystematic risk can be completely eliminated from the portfolio by sufficiently diversifying or simply having a sufficient number of assets in the portfolio. Of course, these assets must be reasonably uncorrelated with each other.

Critically, an investor is NOT compensated for bearing unsystematic risk, and this would be detrimental to the portfolio’s return (and the investor’s wealth) over time.

Ex Post -> “Management”

Risks which have materialized should be managed to the extent possible. Examples:

Ex Ante -> “Engineering”

There are potential risks that we can explicitly prohibit with blockchain tooling. This is where we want to shine.

Portfolio guidelines are rules which are laid out in a legal document (Offering Memorandum or Prospectus) prior to fund inception. Here, the parties agree on the types of instruments to be invested, strategy, rules about concentration and other rule-based metrics.

All institutional fund managers use software to help them manage portfolios. They will say, “Our software will not allow a trade which conflicts with the guidelines”, or they will have a process in place which has a name like “pre-trade clearance” or “hypothetical trading”. These methods have served the industry to a satisfactory level, but non-compliant trades do find their way into a portfolio.

watermelon funds approach risk differently. watermelon fund currently implement risk engineering i.e. anticipating a specific risk and handling it in the code of the smart contract. Essentially, this is a proactive posture, as opposed to a reactive posture.

Risk engineering is the anticipation and identification of different risks, and the action of preventing their occurrence through code. Rules coded into smart contracts that are meant to ensure that something cannot not happen in the structure of a watermelon fund.

What is risk management ?

In Melon, it’s different. We’re not doing risk management, rather are we doing risk engineering ie. anticipating the risk ahead of time, and handling it in the code of the smart contract. Being proactive vs reactive.

risk engineering = the anticipation and identification of different risks ahead of time, and the action of preventing their occurrence through code. Rules coded into smart contracts that are meant to make sure that something will not happen in the context of a watermelon fund.

What risks can we identify within the context of a watermelon fund?

What could on-chain risk engineering entail?

Which kind of off-chain risk engineering tooling can we provide?

[1] (Downside Capture) In relation to hedge funds, and in particular those that claim absolute return objectives, the measure of downside capture can indicate how correlated a fund is to a market when the market declines. The lower the downside capture, the better the fund preserves wealth during market downturns. This metric is figured by calculating the cumulative return of the fund for each month that the market/benchmark was down, and dividing it by the cumulative return of the market/benchmark in the same time frame. Perfect correlation with the market will equate to a 100% downside capture and typically is only possible when comparing the benchmark to itself.

2 Another measure of a fund’s risk is maximum drawdown. Maximum drawdown measures the percentage drop in cumulative return from a previously reached high. This metric is good for identifying funds that preserve wealth by minimizing drawdowns throughout up/down cycles, and gives an analyst a good indication of the possible losses that this fund can experience at any given point in time. Months to recover, on the other hand, gives a good indication of how quickly a fund can recuperate losses. Take the case where a hedge fund has a maximum drawdown of 4%, for example. If it took three months to reach that maximum drawdown, as investors, we would want to know if the returns could be recovered in three months or less. In some cases where the drawdown was sharp, it should take longer to recover. The key is to understand the speed and depth of a drawdown with the time it takes to recover these losses. Do they make sense given the strategy?

Rule set identification and specification

This section contains all the possible on-chain rules we have identified. Only a few of them will be implemented in v1 (see implemented policies).

Rule 1: Maximum number of positions

Definition: A fund may not exceed X positions. Rationale: (i) Avoid over-diversification. (ii) Avoid excessive rebalancing transactions in the event of a new subscription (or redemption). Implementation: Before a trade is authorized, the system must get the number of different assets held by the fund, and make sure that adding 1 asset does not breach the X maximum number of positions. Parameters: X as the maximum number of underlying positions.

Rule 2: Whitelisted assets

Definition: The fund can only invest in explicitly permitted assets (included in the whitelisted asset universe defined at fund setup). Rationale: (i) Investors have a clear understanding of which assets can ever be invested in by the fund. (ii) Restrict the investment manager. Tighten scope of the investment universe. (iii) Fund can only invest in a niche classification (eg. ESG, utility tokens, virtual currencies, commodities only etc.) Implementation: Before a trade is authorized, the system must check that the asset being bought is included in the whitelisted asset universe of the fund. The asset registrar may contain an additional field categories that informs about the nature/type of the asset. This category field can then be queried by the contract before authorizing trading of the asset. Other relevant categories: type (utility, security etc.), sector, country (of jurisdiction) etc. Parameters: Whitelisted assets list or list of category (with specification of AND or OR relationship) Side note: There could be a method to remove an asset from the whitelist. Fund manager should not be able to add an asset to the whitelist.

Rule 3: Blacklisted assets

Definition: The fund is explicitly prohibited from investing in some assets. Rationale: (i) Investors have a guarantee that the fund will never invest in a type of asset. (ii) Restricts Investment manager (iii) Regulatory compliance (iv) Investor preferences. Implementation: Before a trade is authorized, the system must check that the asset being bought is not included in the blacklisted assets list. The asset registrar may contain an additional field categories that informs about the nature/type of the asset. This category field can then be queried by the contract before authorizing trading of the asset. Parameters: Blacklisted assets list Side note: There could be a method to add an asset from the backlist. Fund manager should not be able to remove an asset from the blacklist.

Rule 4: Max concentration (%)

Definition: A position (or positions in a single category) can not actively exceed X% of the NAV. Rationale: (i) Avoid cluster risk. (ii) Portfolio diversification (iii) Reduce unsystematic risk. Implementation: Before a trade is authorized, the system must check the current proportion of NAV that this asset (or category) represents and computes the post trade proportion of NAV. If the post trade proportion of NAV exceeds X%, the trade will not be authorized. Parameters: X as the maximum % a position’s value can represent of the NAV.

Rule 5: Early redemption penalty (this may be contained in the compliance contract)

Definition: An investor can be incurred a penalty if he redeems his shares before the end of the specified period. Rationale: VC funds, private equity funds. Longer time horizon funds. Implementation: This is not part of the risk management contract per se, rather should be implemented as a compliance/participation module. Parameters: period (probably expressed in # of blocks before redemption is possible) and penalty.

Rule 6: Best price execution

Definition: A fund manager is not allowed to deviate more than X% away from the reference price provided by the price feed. Rationale: (i) Prohibit predatory investment manager behavior. (ii) Wash trading mitigation Implementation: Before a trade is authorized, the system must check the price of the order against the reference price and ensure the former does not deviate away more than X% from the reference price. Parameters: x as % of allowed deviation.

Rule 7: Turnover (expressed in # of trades)

Definition: Restrict the number of trades. The fund manager is restricted from exceeding the maximum number of trades allowed. Rationale: (i) Prevent predatory behavior from the investment manager. (ii) Wash trading prevention. (iii) Keep transactions fees to the minimum. Implementation: Before a trade is authorized, the system must check if this additional trade does not exceed the maximum # of trades allowed on the defined time period. Parameters: max number of trades, time period.

Consider volume turnover in terms of portfolio base currency.

Note, top-ups (reductions) due to subscriptions (redemptions) should not count as turnover. Only discretionary investment decisions

Rule 8: Turnover (expressed in volume traded)

Definition: Restrict the number of trades. The fund manager is restricted from exceeding the maximum volume in trades allowed in a given timeframe. Rationale: (i) Prevent predatory behavior from the investment manager. (ii) Wash trading prevention. (iii) Keep transactions fees to the minimum. Implementation: Before a trade is authorized, the system must check if this additional trade does not exceed the maximum volume of trades allowed on the defined time period. Parameters: max number volume of trades (expressed in the currency of denomination of the fund), time period.

Consider volume turnover in terms of portfolio base currency.

Note, top-ups (reductions) due to subscriptions (redemptions) should not count as turnover. Only discretionary investment decisions

Rule 9: Market cap range

Definition: A fund can only invest in assets whose market cap is contained in a certain range [x;y] (ie x < market cap < y). Rationale: Strategy focus enforce Implementation: Before trade is authorized, the system must check the market cap of the assets being traded and verify that the market cap is compliant with the market cap range defined at fund setup. The asset registrar may contain an additional field market cap for each asset. Open question: how to measure Market Cap. Only influences the trade permission at time of trade. That is, there is no consequence when an owned asset’s market cap breaches the upper or lower specified bounds. Parameters: x as low boundary of the range and y as high boundary of the range.

Rule 10: Volatility threshold for a specific asset

Definition: A fund can only invest (purchase) in an asset whose volatility reference indicator is contained in a certain range [x;y] (ie x < volatility < y). Rationale: Investors have an understanding of the volatility level of individual assets that are allowed in the portfolio at the time of inclusion. Implementation: Before a trade is authorized, the system must check the volatility reference indicator of the said asset and make sure it is included in the specified [x;y] range. The volatility reference indicator could be standard deviation but also downside deviation. Possible relevant indicators: standard deviation, Sharpe ratio, Sortino indicator. Parameters: x as low boundary of the acceptable volatility range and y as high boundary of the acceptable volatility range.

Portfolio base currency cannot be considered for this rule.

Rule 11: Portfolio volatility threshold

Definition: A fund is prevented from performing a trade if the effect on the portfolio volatility is undesirable (ie if volatility post trade exceeds X% level of volatility) Rationale: Investors have an understanding of the volatility level of the portfolio maintained at the time of trading. Implementation: Before a trade is authorized, the system must check the post volatility of the total portfolio and make sure it is included in the specified [x;y] range. Parameters: x as low boundary of the acceptable portfolio volatility range and y as high boundary of the acceptable portfolio volatility range. Side note: This computation may be too expensive on Ethereum smart contract. We might be able to use a simplified computation. But this rule is to be envisioned in the watermelon chain context.

Edge case: Could also allow trades which incrementally reduce (increase) portfolio volatility. Basically, is the trade beneficial to the investor and in the spirit of the guidelines.

Rule 12: Liquidity

Definition: A fund can only invest in assets whose liquidity is contained in a certain range [x;y] (ie x < liquidity < y).
Rationale: (i) enforce strategy focus Implementation: Before a trade is authorized, the system must check the liquidity available for that asset and make sure it is included in the specified [x;y] range. The liquidity data is retrieved from the exchanges the fund is allowed to trade on. Liquidity can be expressed as the % of your trade volume over the 30-days ADV (average daily volume traded). Parameters: x as low boundary and y as high boundary of range.

Rule 13: Correlation of adding an asset to portfolio

Definition: A fund can invest in an asset if its correlation with the current portfolio is contained between a range [x;y] Rationale: (i) making sure that adding an asset helps optimizing the portfolio or at least move it in the right direction. Implementation: Before a trade is authorized, the system must compute the correlation of that asset to the portfolio and make sure it is included in the specified [x;y] range. Parameters: x as low boundary and y as high boundary of the correlation range.

Rule 14: Time horizon

Definition: A fund manager needs to hold an asset for specific average period of time. Implementation: We voluntarily decide not to encode that rule in a smart contract as it can lead to dangerous situations for the fund. Could be as a guideline for informational purposes but won’t be enforced.

(not seen in practice, could be gamed by investor redemption and subsequent re-investment; must be thoughtfully aligned with lockup.)

Rule 15: Full investment

Definition: A fund manager can defined the full investment threshold ie. the proportion of assets invested vs the proportion of cash held. Rationale: The fund manager should not stay passive collecting management fees just by holding too large proportion of cash (taking no risk) risk. Implementation: We do not want to enforce that rule as the fund manager should have the flexibility to put everything in safety in case of imminent market crash or similar event. It could simply be an informational rule that can be used in monitoring/reporting tool to compare with actual investment levels of the fund. Parameters: X as the percentage of NAV invested. Remove, unimportant; could cause sub-optimal behavior - keep as indicator for reporting.

Rule 16: Generalizable 5/10/40 rule.

This is a current UCITS rule. Positions may not exceed 10% of NAV, and that assets which exceed 5% of NAV summed together may not exceed 40% as a group.

Rule 17: Specific Asset/Category Max (%)

This rule assigns a maximum allocation as a percentage of NAV to a specific token or category. This is slightly different from Rule 4 which is an unspecified, blanket maximum allocation for any specific asset token or category.

Rule 18: Fund Cap

This rule could be implemented by an Investment Manager to cap the overall AuM of a watermelon Fund for various reasons, in particular when a specific strategy may have no further market capacity within its mandate.

Rule 19: Fund Audit

This rule could be implemented by an Investment Manager to enforce fund audits by a pre-determined auditor at defined time intervals. Funds implementing this rule would be restricted from trading (except into the denomination asset) when an audit has not been performed within the defined time interval.

Implemented Risk Policies

AssetBlacklist.sol

Description

The Policy explicitly prohibits the watermelon fund from investing in or hold any asset token which is part of the contract’s blacklist. The rationale for this Policy my be: (i) investors have a guarantee that the fund will never invest in an asset. (ii) explicit restrictions on the manager (iii) regulatory compliance (iv) investor preferences. Assets token addresses can be added, but not removed from the contract’s blacklist.  

Inherits from

Policy, AddressList (Link)

 

On Construction

The contract requires an array of addresses, where each address is added to the contract’s internal list.  

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

None.

 

Public Functions

function addToBlacklist(address _asset) external auth

This function requires that the caller is the owner or the current contract. The function then requires that the token asset address is not a member of the list. If it is not a member of the list, the address provided is then added to the contract’s internal list. Once a token asset address is blacklisted, it cannot be removed.  

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function returns true if the taker asset address (position [3] in the addresses array parameter) is not a member of the blacklist. The function returns false if the taker asset is a member of the blacklist. For a full description of the parameters, please refer to rule() in the general Policy contract above.  

function position() external view returns (Applied)

This view function returns the enum pre, indicating the the Policy logic be evaluated prior to execution of the Policy’s corresponding registered function call.  

AssetWhitelist.sol

Description

The Policy explicitly permits the watermelon fund to invest in and hold any asset token which is part of the contract’s whitelist as defined at fund setup. The rationale for this Policy my be: (i) Investors have a clear understanding of which assets can ever be invested in by the fund. (ii) restricting the investment manager - tightening the scope of the investment universe. (iii) the fund can only invest in a niche classification (eg. ESG, utility tokens, virtual currencies, commodities only etc.). Assets token addresses can be removed from, but not added to the contract’s whitelist.  

Inherits from

Policy, AddressList (Link)

 

On Construction

The contract requires an array of addresses, where each address is added to the contract’s internal list.

 

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

None.

 

Public Functions

function removeFromWhitelist(address _asset) external auth

This function requires that the caller is the owner or the current contract. The function then requires that the token asset address is a member of the contract’s list. If it is a member of the list, the address provided is then removed from the contract’s internal list. Whitelisted token asset addresses can be removed from, but not added to, the contract’s list.  

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function returns true if the taker asset address (position [3] in the addresses array parameter) is a member of the whitelist. The function returns false if the taker asset is not a member of the whitelist. For a full description of the parameters, please refer to rule() in the general Policy contract above.  

function position() external view returns (Applied)

This view function returns the enum pre, indicating the the Policy logic be evaluated prior to execution of the Policy’s corresponding registered function call.  

MaxConcentration.sol

Description

The Policy explicitly prevents a position from actively exceeding the specified percentage of fund value. The rationale for this Policy is to: (i) avoid cluster risk, (ii) aid portfolio diversification and (iii) reduce unsystematic risk. This Policy is evaluated after execution of the registered corresponding function.  

Inherits from

DSMath, Policy (Link)

 

On Construction

The contract requires the parameter representing the concentration upper limit as a percentage be less than 100%. The public state variable maxConcentration is then set to the value provided by this parameter.  

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

uint public maxConcentration

This public variable stores the upper limit, in percentage of fund value, which an individual asset token position can have. The value is stored using 18 decimal precision, i.e. 100000000000000000 would represent a maximum concentration for any individual token asset position of 10% of fund value.  

Public Functions

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function returns true if watermelon fund’s denomination asset is the taker asset. The rationale for this is that denomination asset should not be subject to the maximum concentration rule as the denomination asset represents non-exposure to the wider market. In cases where the taker asset is not the denomination asset, the function evaluates the post-trade allocation percentage of the position to fund value, returning true if the entire fund post-trade position in the taker asset does not exceed the maximum concentration percentage. If the fund’s post-trade position in the taker asset does exceed the maximum concentration percentage, the function returns false and the transaction is reverted. For a full description of the parameters, please refer to rule() in the general Policy contract above.  

function position() external view returns (Applied)

This view function returns the enum post, indicating the the Policy logic be evaluated after execution of the Policy’s corresponding registered function call.  

MaxPositions.sol

Description

The Policy explicitly prevents a position from entering the fund if the quantity of non-denomination asset token positions exceed the specified maxPositions state variable. The rationale for this is to: (i) avoid over-diversification and (ii) avoid excessive rebalancing transactions in the event of a new subscription (or redemption). This Policy is evaluated after execution of the registered corresponding function.  

Inherits from

Policy (Link)

 

On Construction

The contract requires a parameter representing the maximum number of non-denomination asset token positions permitted in the watermelon fund. The public state variable maxPositions is then set to the value provided by this parameter. A value of 0 means that no non-denomination assets positions are permitted.  

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

uint public maxPositions

This public variable stores the upper limit of the number of non-denomination asset positions permitted in the watermelon fund. For example, maxPositions == 5 means that a maximum of five non-denomination asset token positions are permitted at any time.  

Public Functions

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function returns true if watermelon fund’s denomination asset is the taker asset. The rationale for this is that denomination asset should not be subject to the maximum positions rule as the denomination asset represents non-exposure to the wider market. In cases where the taker asset is not the denomination asset, the function evaluates the post-trade number of non-denomination asset positions in the fund, returning true if the post-trade number of non-denomination asset positions in the fund does not exceed the maximum positions configuration. If the fund’s post-trade position in the taker asset causes the fund’s non-denomination asset position quantity to exceed the maximum positions quantity, the function returns false and the transaction is reverted. For a full description of the parameters, please refer to rule() in the general Policy contract above.  

function position() external view returns (Applied)

This view function returns the enum post, indicating the the Policy logic be evaluated after execution of the Policy’s corresponding registered function call.  

PriceTolerance.sol

Description

The PriceTolerance Policy explicitly prevents a make order from being submitted to an exchange, or explicitly prevents a take order trade from executing depending on a comparison between the implicit order price, adjusted for the specified tolerance, and the current reference price as provided by the price feed. The rationale is that the Policy: (i) prevents predatory investment manager behavior and (ii) mitigates wash trading. This Policy is evaluated prior to execution of the registered corresponding functions.  

Inherits from

Policy, DSMath (Link)

 

On Construction

The contract requires a parameter representing the maximum price feed deviation of an asset trade to the detriment of fund investors. The tolerance state variable is then set to this value. A parameter value of 10 would represent a maximum order price deviation to the current reference price of 10%.  

Structs

None.

 

Enums

None.

 

Modifiers

None.

 

Events

None.

 

Public State Variables

uint tolerance

This state variable stores the permitted deviation in percentage of the order price to the current reference price. A value of 10 would represent a maximum order price deviation to the current reference price of 10%.  

Public Functions

function takeOasisDex(address ofExchange, bytes32 identifier, uint fillTakerQuantity) view returns (bool)

This view function gathers data from the existing Oasis Dex order and respective amounts filled, then compares the resulting price with the tolerance percentage-adjusted reference price from the price feed. If the order price does not exceed the tolerance percentage-adjusted reference price, the function returns true and the fund is permitted to take the order (partial- or complete fill); otherwise the function returns false, the trade is not permitted and the transaction is reverted.  

function takeGenericOrder(address makerAsset, address takerAsset, uint[3] values) view returns (bool)

This view function accommodates generic exchange take order functionality, calculating the maker fill quantity given the taker quantity, maker quantity and the taker fill quantity, then compares the resulting price (fill quantity ratio) with the tolerance percentage-adjusted reference price from the price feed. If the take order price does not exceed the tolerance percentage-adjusted reference price, the function returns true and the fund is permitted to take the order (partial- or complete fill); otherwise the function returns false, the trade is not permitted and the transaction is reverted.  

function takeOrder(address[5] addresses, uint[3] values, bytes32 identifier) public view returns (bool)

This view function is evaluated prior to executing take orders on exchanges and determines the execution routing for take orders depending on value of the identifier parameter. If the parameter identifier == 0x0 (denoting a 0x-integrated exchange), the function calls takeGenericOrder() and takeOasisDex() otherwise, passing all input parameters on to the subsequent function call.  

function makeOrder(address[5] addresses, uint[3] values, bytes32 identifier) public view returns (bool)

This view function is evaluated prior to executing make orders on exchanges. The function determines the current reference price for the asset token pair, then determines the price as determined by the make offer quantities. If the make order price does not exceed the tolerance percentage-adjusted reference price, the function returns true and the fund is permitted to submit the make order to the exchange; otherwise the function returns false, the make order is not permitted and the transaction is reverted.  

function rule(bytes4 sig, address[5] addresses, uint[3] values, bytes32 identifier) external view returns (bool)

This view function is evaluated prior to executing make orders or take orders on exchanges and determines the execution routing for these order types depending on value of the sig parameter, which is the keccak256 hash of the plain text function signature of makeOrder() or takeOrder(). The function returns true for permitted orders, i.e. make orders can be submitted to the exchange and take orders can be traded. The function returns false for prohibited orders which violate the price tolerance criteria, reverting the transaction. For a full description of the parameters, please refer to rule() in the general Policy contract above.  

function position() external view returns (Applied)

This view function returns the enum pre, indicating the the Policy logic be evaluated prior to execution of the Policy’s corresponding registered function call.