Fund in the Shell: a transplant approach to upgradable Enzyme portfolios

Sean Casey
Jan 18, 2021 · 8 min read

TL;DR — Enzyme funds are now fully upgradeable through a process that allows managers to opt-in to the latest release, while providing investors a window during which to opt-out. The technical approach involves transplanting the “soul” of the fund (ownership, asset holdings, and shares) into a new shell (release configuration).

Since late 2019, the core development team at Avantgarde Finance has been tirelessly working towards the second major release of the Enzyme Protocol (formerly Melon).

In mid-2020, our team was entering a push for a Q3 release of the first major version since the original launch of Melon in Q1 2019. The protocol was on track to deliver significant improvements such as single-tx fund setup, lending integrations, across-the-board gas savings, and abstracted and extensible fees, policies, and DeFi adapters. Yet, as the target date approached, our team was haunted by a persistent user experience concern: funds were not upgradable.

Non-upgradable funds are a user experience nightmare

Generally, when there is a new release of the Enzyme Protocol, a fund manager would likely want to move to that release to take advantage of new features, improvements, and continuously-maintained infrastructure (e.g., price feeds and the official Avantgarde Finance UI).

Previously, such a fund manager would have needed to create a new fund on the new release, then somehow communicate to their investors to redeem shares from the old fund and invest in the new fund. This creates a user-experience nightmare:

We were aware that several potential power users were waiting on the sidelines for fund upgradability, and it became clear that if we wanted to both win them over and roll out new functionality continuously and aggressively (we do!), then we would need upgradable funds.

Upgrading Enzyme funds in a way that allows user agency is extremely complicated

Finding the appropriate upgradability paradigm for Enzyme was extremely complicated because of three factors:

The Enzyme Protocol has a sprawling contract universe (currently 32 deployed contracts) that includes core logic for accounting and participation, extended functionality to handle fees, policies, and DeFi actions, and infrastructure to price and convert asset values.

Furthermore, these contracts were tightly coupled; the protocol was hardcoded with knowledge about specific fees, a specific price feed, specific types of extended functionality, and so on. (A big focus of this release cycle has been decoupling and abstracting these contracts).

Independently, none of these factors would necessarily make for a difficult upgradability paradigm. For example, if code complexity (or even tight coupling) were the only issue, the Enzyme Council could still upgrade the entire protocol and force all users to use the same versions of all contracts. Indeed, this is a generally-accepted paradigm for upgrading DeFi protocols.

However, it was philosophically important to us to allow our users agency in deciding whether or not to go along with any proposed upgrade.

From a “code is law” perspective, when users decide to start using a protocol, they are agreeing to the rules of how that protocol operates. While the argument can be made that agreeing to the rules of a proxy contract constitutes such an agreement, this gives carte blanche to the governance structure. Additionally, there could be practical or legal ramifications for some portfolio managers if there are changes to the way that assets are custodied, shares are bought or redeemed, assets are valued, etc.

This desire for optional upgrades was made all the more difficult by the fact that we have two user groups to consider: portfolio managers (of investment products) and investors (in those investment products).

The confluence of these three factors made a traditional approach to upgrades daunting and dangerous. Multiple user groups would need to participate in upgrading different types of contracts created during different release cycles, yielding limitless conformations of versions that would somehow need to be tested to guarantee cross-compatibility of all contracts within each conformation.

Fund in the shell: finding the “soul” of Enzyme funds

In trying to make sense of upgrading within this sprawling complexity, one question that emerged early on in our team was: what is the soul of an Enzyme fund?

For me, this question evokes cyborg imagery from cyberpunk sci-fi such as Ghost in the Shell or the more recent Altered Carbon. Such dystopian futuristic universes focus on upgrading or completely substituting the physical body while maintaining the integrity of the mind/soul. These settings are modern extrapolations of classical Cartesian philosophical discussions of mind-body duality.

So what is the “soul”, the essence, of Enzyme funds? Roughly ordered from most to least essential:

We also needed to consider which of these components were abstract enough so as to not handcuff us to particular architectural structures for future releases of the Enzyme Protocol. For example, persisting policies (the mandate) and fees would require each subsequent release to have the same understanding of not only what policies and fees are, but also to maintain consistency of what each instance in storage represents (e.g., use of a PerformanceFee with particular settings).

Ultimately, we decided that the soul of a fund is its asset holdings and its shares, along with a basic access control structure.

Now, it was time to create a paradigm for transplanting these ghosts to new shells.

Upgrading funds via transplant migration

In traditional database-driven systems, “migration” generally refers to using a mapping between schemas to read from a legacy database and write to the subsequent database.

As is frequently pointed out, a blockchain is not a database (and should not be used as one), and indeed it would be cumbersome and expensive to copy and write entire storage layouts to new contracts for each upgrade.

Transplanting an entire contract contract is a much more practical approach.

A rough diagram to conceptualize the varying lifespans, dependencies, and control pathways in the Enzyme Protocol

The above image is the architectural pattern that we settled on for this transplant-based upgradability process. The diagram is crude, complicated, and not 100% accurate, but it serves to illustrate a few key points below. Note, for example, that the colors of group boxes represent different categories of lifespan: the purple boxes represent perpetual contracts that continue to exist across all releases, the blue boxes represent architecture that can potentially be recycled between releases, and the green boxes represent release-specific architecture.

The ghost and the shell: VaultProxy and VaultLib

We decided to implement the oft-used proxy upgradability pattern (following EIP-1822 and EIP-1967) by deploying a VaultProxy proxy contract per fund that points to a shared VaultLib logic contract particular to the current release. This VaultProxy is then upgraded by pointing it to the VaultLib of the next release.

The VaultProxy persists across releases, and thus provides a canonical fund address at which to house asset holdings. Meanwhile, the essential storage layout for the remaining elements of this soul (ownership and an ERC20 layout for shares) are defined within a VaultLibBaseCore contract that is the foundation of each VaultLib. Subsequent releases can add to this core storage layout by extending the base of the previous release. For example, this first release adds a storage pattern and events for tracking assets held by the fund.

This is all a fairly common upgradability pattern.

The real magic comes from the architecture to transplant these ghosts to new shells.

The transplant surgeon: Dispatcher

There is a non-upgradable contract called the Dispatcher that sits atop all releases. Its primary responsibilities are to deploy new VaultProxy instances, understand which release each VaultProxy instance is currently on, and to facilitate the migration of VaultProxy instances to the current release.

The Dispatcher is ostensibly the surgeon for transplanting VaultProxy instances to new releases, and only accepts a command to do so from the latest release. As far as this overarching infrastructure goes, the Enzyme Council only needs to keep the Dispatcher up-to-date with the current release.

The transplant itself is incredibly simple: the Dispatcher simply updates two storage variables on the VaultProxy:

This abstract handoff between releases allows for release architecture to evolve almost limitlessly, as the Dispatcher is completely indifferent to how releases operate.

The only necessary architectural components of new releases is a broadly-defined top-level contract to interact with the `Dispatcher` , and the new `VaultLib`

In order to grant users agency whether to upgrade to the newest release, the Dispatcher follows a signal-execute pathway:

This pathway affords both managers and investors the agency to upgrade. The fund manager opts-in by actively signaling and executing the upgrade, and investors are given a window during which they can opt-out, i.e., rage quit.

For a much more detailed (and technically correct) overview of the migration path that includes how it works within the current release, see the audited Enzyme General Spec

The full codebase will be made public along with the forthcoming audit report.

Protocol upgrades that fit the soul

Upgradability paradigms are by no means perfect or a one-size-fits-all solution for all projects. Finding novel approaches to upgradability will be a continuous theme of smart contracts development for the foreseeable future.

For the Enzyme Protocol, it was a helpful exercise to think about what is philosophically important in an upgrade, as well as what constitutes the “soul” to be upgraded. This led to a bespoke paradigm of transplant migration that will be the bedrock of (hopefully!) many releases to come.

Enzyme Finance (formerly Melon)

A Blog Detailing the Endeavours of the Enzyme ecosystem…