FundDeployercan be considered the top-level contract of the release. It serves two roles.
Its primary purpose is the gateway to creating, migrating, and reconfiguring funds.
This is the contract that the
Dispatcherconsiders as the
currentFundDeployer, thus allowing it to deploy and migrate
It also handles intra-release migration (completely changing fund config by swapping out a new ComptrollerProxy, referred to as "reconfiguration"), mimicking the
Dispatcher's paradigm for signaling and executing timelocked "reconfiguration requests."
FundDeployerdeploys configuration contracts (
ComptrollerProxy) per-fund that are then attached to
VaultProxyinstances (more in the next section).
FundDeployeris also used as a release-wide registry for limiting allowed values for permissioned calls in a uniform way for all funds.
There is 1 shared
FundDeployerfor the release.
ComptrollerProxyis deployed per-fund, and it is the canonical contract for interacting with a fund in this release. It stores core release-level configuration and is attached to a
VaultProxyvia the latter's
All state-changing calls to the
VaultProxyrelated to the fund's holdings and shares balances must thus pass through the
ComptrollerProxy, making it a critically important bottleneck of access control.
The storage and logic of the
ComptrollerProxyare defined by the
ComptrollerLiband its associated libraries. It is not upgradable, but a fund can be "reconfigured" by deploying a new
ComptrollerProxyinstance to replace the previous one, via the
VaultLibcontract contains the storage layout, event signatures, and logic for
VaultProxyinstances that are attached to this release.
There is 1 shared
VaultLibfor the release.
Extensions extend the logic of the core contracts by adding additional kinds of functionality.
They are semi-trusted, in that they are selectively granted access to state-changing calls to
In order to make such a state-changing call, two conditions must be met:
- 1.The Extension function must have been called by a
ComptrollerProxyvia a function with the
allowsPermissionedVaultActionmodifier, which opens the calling
- 2.The state-changing call must pass back through the
ComptrollerProxy, and is delegated to the
PermissionedVaultActionLibto determine whether the calling Extension is allowed to perform such an action.
This paradigm assures that an Extension can only perform a state-changing action to a
VaultProxyif it was called by that
ComptrollerProxyand if the Extension is permitted to make such a change at all.
Each extension manages a particular type of plugin:
ExternalPositionManager- external positions
Plugins do not have authority to act on a vault's state, unless explicitly granted via the core system. e.g., a
SynthetixAdaptercan trade on Synthetix on behalf of a vault by enabling that action via a
In this release, there are four Extensions. All funds share one contract per Extension.
PolicyManagerallows a fund owner to set up and manage a stack of "policies," which are used to perform bespoke validations during various function calls.
The protocol invokes "policy hooks" during actions where it is deemed important to give the fund owner customizability of what should be allowed/disallowed according to their particular needs.
Policies define which hooks they implement. When a hook is reached, it loops over all policies that a fund has enabled that run on that particular hook and validate whether the particular call is allowed.
IPolicyManager.PolicyHookfor the available hooks.
Policies themselves can only fail or pass, so the
PolicyManagerhas no need or access to state-changing vault actions.
All policies are addable during fund creation, migration, or reconfiguration.
A policy defines for itself whether or not it is updatable or removable.
A policy can be added at any time, unless it runs on a policy hook that restricts current investors (i.e., shares redemption and shares transfer).
FeeManagerallows for "fees" to dictate the minting, burning, or transferal of fund shares, according to their internal logics.
Like policies, fees implement "fee hooks," which are invoked during particular actions.
Fees can either settle and payout immediately, or they can accrue upon settlement as "shares outstanding," and only unlock for payout when specific conditions (defined by the fee) are met.
Fees can only be added or removed during fund setup (creation / migration / reconfiguration).
- exchanging a fund's assets for other assets via "adapters" to DeFi protocols (e.g., Uniswap, Kyber, Compound, Chai)
- tracking assets
- untracking assets
Each of these actions contains a policy hook.
ExternalPositionManagerallows creating and managing "external positions," proxy contracts that represent non-ERC20 holdings of the fund, e.g., a Compound CDP.
ExternalPositionManagermaintains a registry of "libs" and "parsers" per external position, which serves as the "beacon" in this release for the
ExternalPositionProxy's specific beacon proxy pattern.
Each of the available actions for interacting with an external position contains a policy hook.
See "External Positions" section.
Each of the Extensions above make use of plugins. The
IntegrationManageruses "adapters", the
PolicyManageruses "policies", and the
FeeManageruses "fees", and the
ExternalPositionManageruses "external positions."
Arbitrary (third party) plugins are allowed for fees, policies, and integrations. Fund managers can decide whether or not to use third party plugins, and investors will be able to determine if fund configurations are safe for their trust threshold. A setup that only uses official plugins would entail:
- No arbitrary policies that restrict investor actions (can only be added at fund setup)
- No arbitrary fees (can only be added at fund setup)
- No arbitrary adapters (unremovable policy will be provided with a registry of official, Council-verified adapters)
In addition to "core" and "extension" release-level contracts, there is a broad category of "infrastructure" contracts, which are misc dependencies of the release-level protocol. Unlike extensions, they do not receive any permissions to alter fund state.
AssetFinalityResolversettles Synths in an efficient manner for operations that depend on accurate Synth balances.
This release allows for funds to optionally use Gas Station Network relayers to pay for calls to specific contracts and functions.
See "Gas Relayer" section.
ProtocolFeeTrackertracks protocol fee payments and is queried to determine the amount of shares that a fund should mint to the
ProtocolFeeReserveto bring its fees up-to-date.
See "Protocol Fees" section.
ValueInterpreteris the single point of aggregation of various "price feeds" (an additional type of "plugin" that is only managed by the Enzyme Council) that are used to calculate the value of one or many input asset amounts in terms of an output asset.
There are two categories of assets in this release:
- "primitives" - assets for which we have direct rates via Chainlink aggregators that can be used to convert one primitive to any other (e.g., WETH, ZRX, etc)
- "derivatives" - assets for which we only have rates in terms of underlying assets (e.g., Chai, Compound cTokens, Uniswap pool tokens, etc)
ValueInterpreterdetermines whether an asset is a primitive or derivative, and executes logic to use corresponding price feeds to determine the value in the output asset.
All interfaces to external contracts are contained in the
Interfaces for internal contracts (e.g.,
IFundDeployer) are kept beside the contracts to which they refer. These are narrow interfaces that only contain the functions required by other non-plugin, release-level contracts (i.e., those in the "core" and "extensions" sections above).