# Enzyme 文件-源碼 ## Persistent Architecture This migration pattern is accomplished through two persistent contracts: a Dispatcher and per-fund VaultProxy instances. The VaultProxy specifies a VaultLib as its target logic, and these logic contracts are deployed per release, and swapped out during a migration. A VaultLibBaseCore contract defines the absolutely essential state and logic for every VaultProxy. This includes: - a standard ERC20 implementation called SharesTokenBase - the functions required of a IProxiableVault called by the Dispatcher - core access control roles: owner, accessor, creator, and migrator owner is the fund's owner. migrator is an optional role to allow a non-owner to migrate the fund creator is the Dispatcher contract, and only this role is allowed to update the accessor and vaultLib values. The accessor is the primary account that can make state-changing calls to the VaultProxy . In practice, this is the release-level contract that interacts with a vault's assets, updates shares balances, etc. The VaultLibBaseCore contract can be extended with new storage and events by implementing a new VaultLibBaseN that extends the previous base. For example, this first release implements abstract contract VaultLibBase1 is VaultLibBaseCore . The next release that adds to state or events would continue this pattern with abstract contract VaultLibBase2 is VaultLibBase1 , and so on. The release itself will provide a VaultLib that extends the latest VaultLibBaseN implementation with the required logic of that release. ### Dispatcher An overarching, non-upgradable Dispatcher contract is charged with: deploying new instances of VaultProxy migrating a VaultProxy from an old release to the current release maintaining global state such as the current release, the global owner (i.e., the Enzyme Council) and the default symbol value for fund shares tokens The Dispatcher stores the currentFundDeployer (a generic reference to the latest release's contract that is responsible for deploying and migrating funds), and only a msg.sender with that value is allowed to call functions to deploy or migrate a VaultProxy . This release-level FundDeployer can optionally implement migration hooks provided by IMigrationHookHandler, which give the release an opportunity to run arbitrary logic as the Dispatcher invokes those hooks at every step of the migration process. As was the case described above with the VaultLibBaseCore , this abstracted notion of a FundDeployer - in which the Dispatcher only cares about its identity for access and for optional callbacks - is totally unrestrictive to the shape of the release-level protocol. ## Release Architecture ### Core #### FundDeployer The FundDeployer is the gateway for a user to create a new fund and signal the migration of a fund from an old release to the current release. As described in the previous page, this is the contract that the Dispatcher considers as the currentFundDeployer, thus allowing it to deploy and migrate VaultProxy instances. The FundDeployer deploys configuration contracts ( ComptrollerProxy ) per-fund that are then attached to VaultProxy instances (more in the next section). The FundDeployer is also used as a release-wide reference point for a couple types of values. The first is the releaseStatus . When set to Paused , any functions that write to VaultProxy storage are prohibited, other than redeeming shares. This is a safety mechanism in the case that a critical bug is discovered in one of the protocol contracts. The second storage value of FundDeployer that is referred to release-wide is a registry of allowed "vault calls" , which is referred to by the ComptrollerLib. This allows for arbitrary calls to be made from the VaultProxy as msg.sender , which will be used in calls such as delegating the SynthetixAdapter as an approved trader of Synths directly from the VaultProxy . There is 1 shared FundDeployer for the release. #### ComptrollerProxy One ComptrollerProxy is 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 VaultProxy via the latter's accessor role described on the previous page. All state-changing calls to the VaultProxy related to the fund's holdings and shares must thus pass through the ComptrollerProxy, making it a critically important bottleneck of access control. The storage and logic of the ComptrollerProxy are defined by the ComptrollerLib and its associated libraries. Though it inherits the same upgradable Proxy as the VaultProxy, there is no way to call for an upgrade in this release. #### VaultLib As described on the previous page, the VaultLib contract contains the storage layout, event signatures, and logic for VaultProxy instances that are attached to this release. There is 1 shared VaultLib for the release. ### Extensions 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 VaultProxy instances. In order to make such a state-changing call, two conditions must be met: 1. The Extension function must have been called by a ComptrollerProxy via a function with the allowsPermissionedVaultAction modifier, which opens the calling ComptrollerProxy to VaultProxy state changes. 2. The state-changing call must pass back through the ComptrollerProxy, and is delegated to the PermissionedVaultActionLib to 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 VaultProxy if it was called by that VaultProxy's corresponding ComptrollerProxy and if the Extension is permitted to make such a change at all. Though this might seem like overkill for the current release, where extensions are all trusted and audited, it reduces the auditing surface area (e.g,. PolicyManager has no permitted actions) and opens the door for a subsequent release to allow arbitrary Extensions. In this release, there are three Extensions. All funds share one contract per Extension. #### IntegrationManager The IntegrationManager allows exchanging a fund's assets for other assets via "adapters" to DeFi protocols (e.g., Uniswap, Kyber, Compound, Chai). It treats these adapter plugins in an almost untrusted manner (it does rely on adapters to report the expected assets to spend and receive based on user input), validating asset values spent and received against expected values, and also providing the opportunity for policies to implement hooks that are run pre- and post-asset exchange. #### PolicyManager The PolicyManager allows state validation via "policies" that implement hooks invoked while buying shares and making an exchange in the IntegrationManager There is no trust involved in policies, as the PolicyManager has no access to state-changing vault actions. #### FeeManager The FeeManager allows for "fees" to dictate the minting, burning, or transferal of fund shares, according to their internal logics. Like the PolicyManager, the FeeManager invokes hooks at different points in core logic, namely while buying shares, redeeming shares, and upon a specific function to invoke a "continuous" hook (e.g., for a ManagementFee that grows every block). ### Plugins Each of the Extensions above make use of plugins. The IntegrationManager uses "adapters", the PolicyManager uses "policies", and the FeeManager uses "fees". Allowed plugins are all defined on registries in their respective Extensions. As with Extensions, the plan for subsequent releases is to open up these plugins for third party development. ### Infrastructure In addition to "core" and "extension" release-level contracts, there are also entirely decoupled "infrastructure" contracts that can theoretically be recycled between releases. Currently, this category only contains contracts that are related to asset prices and values, but it can also contain contracts such as a forthcoming release that will implement protocol fees. #### ValueInterpreter The ValueInterpreter is the single point of aggregation of various price feeds 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 with which to convert one primitive to any other (e.g., WETH, MLN, etc) "derivatives" - assets for which we only have rates in terms of underlying assets (e.g., Chai, Compound cTokens, Uniswap pool tokens, etc) The ValueInterpreter determines whether an asset is a primitive or derivative, and executes logic to use corresponding price feeds to determine the value in the output asset. There is only one supported price feed for each category in this release, so both are hardcoded as immutable variables. ChainlinkPriceFeed (IPrimitivePriceFeed) The ChainlinkPriceFeed provides all conversions between primitives. This feed registers assets with their Chainlink aggregators, thus defining the primitive asset universe for the release. #### AggregatedDerivativePriceFeed The AggregatedDerivativePriceFeed serves as a central registry that maps derivatives to their corresponding price feeds, and fetches rates from them. There are several individual price feeds that provide the actual rates of derivatives to their underlying assets. Each inherits IDerivativePriceFeed to provide a standard interface for the AggregatedDerivativePriceFeed to register derivative mappings and grab rates. e.g., CompoundPriceFeed, ChaiPriceFeed ### Interfaces All interfaces to external contracts are contained in the release/interfaces/ directory. 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). The idea was to have a handy visual reference to the intra-release surface area of interactions between contracts.