Axis-Fi / axis-core

Axis Protocol
https://axis.finance
Other
6 stars 1 forks source link

Auction House: Module Management #9

Closed Oighty closed 8 months ago

Oighty commented 9 months ago

Modules allow extending the core auction system with additional logic. Inspired by Default Framework, we will use a Keycode system to label and store references to external contracts which implement one of a variety of module interfaces. Standardized functions, as specified in design/ARCHITECTURE.md and the src/modules/Modules.sol file, allow for installation, retrieval, and validation of modules.

Module Types

Auction

Modules that implement logic for specific types of auctions that sellers can create. They store data about an auction. The AuctionHouse interacts with Auction modules via the standard interface defined in design/ARCHITECTURE.md. Auctions do not receive or custody funds, but determine the amount of tokens to sell based on the auction parameters and buyer input.

Derivative

Modules that create derivative tokens from an underlying base token. Derivative modules allow auction sellers (or just general users) to offer a derivative of a token for sale instead of just the base token. These modules custody collateral for the particular derivative type that they implement. All Derivative modules are ERC6909 contracts and store data for the tokens they issue locally. They can also optionally wrap the multitoken standard into ERC20 clones for particular token IDs if users need them to be composable with other applications.

Condensor

Auctions have the ability to output additional data about a buyer payout other than the quantity, including parameters to input into the specified Derivative module for the auction. Due to the general way Auctions and Derivatives are defined, they expect inputs in a certain way and output standard data. It is not possible to solve for every possible case on just the Derivative interfaces, so we need a way to combine seller input with auction output data into a format expected by a Derivative module. Condensor modules perform this function by implementing a single condense function as specified in design/ARCHITECTURE.md. They can be thought of as a link between a specific Auction that outputs extra data and a specific Derivative module.

Transformer

An interesting feature of the singleton AuctionHouse design is that the core contract has control over all Derivative types that exist in the system. This allows for certain functionality that wouldn't be available if they were locked down as standalone implementations. Specifically, it allows the AuctionHouse to "break" the derivative rules and unbundle the underlying collateral in cases where it deems it allowable to do so. Allowing this functionality adds some risk, but, if used correctly, can allow for tranforming one derivative into another if the new derivative upholds certain criteria of the existing derivative. They can be thought of as a mapping between a Derivative and another Derivative. A few examples could be:

  1. Vesting token -> Call option that cannot be executed until after the vesting token would have been redeemable
  2. Vesting token -> longer dated Vesting token (may be reasonable to due this to align maturities with a popular issue for better secondary liquidity)
  3. Rage vesting token or Linear vesting token -> Cliff vesting token of equal or longer expiry (you're losing optionality here)

Module Management

Installation and Replacement of Modules

We've thought extensively about the characteristics of this specific module system in the context of the singleton AuctionHouse design. The designs/ARCHITECTURE.md and src/modules/Modules file provide a specification and initial implementation of these concepts. The purpose of these design decisions is discussed here.

The Keycode-based identifiers allow for "labeling" specific modules with a name and version instead of needing to know/remember the contract address of the deployed module. The Keycode must be unique. In addition, we can store the address once and then store references to it using the smaller keycode to save storage space. Default framework uses a 5 byte Keycode for Modules and a 20 byte SubKeycode (which includes the parent Keycode in the first 5 bytes) for Submodules. The 5 byte version allows for short, memorable names, but doesn't allow for versioning within the Keycode. This is by design since Modules in the Default Framework are upgradeable, but we want to avoid that in this case, as explained below. The 20 byte SubKeycodes do not save any storage space and are probably a bit overkill for our purposes here. We believe a 10 byte keycode provides adequate room for differentiating or versioning modules within a single variable.

As mentioned above, Modules in this system will not be upgradable. Auctions store information about ongoing auction sales. Derivatives store token data AND custody collateral for the derivative. "Upgrading" them in the Default sense would be expensive (if copying storage and/or transferring assets). On the other hand, we could make the Modules all be Upgradable Proxies, but that opens the door for exploits in the future, especially when the system is decentralized. The safest way to protect ongoing auctions and derivatives is to not allow their code to be updated at all. However, it is possible that there will be bugs in Modules over time and the community may wish to replace a specific version with a new one in this cases. Therefore, we allow Modules to be "sunset" which prohibits new usage of the module, but allows existing auctions to complete and derivative tokens to be redeemed on them. As a replacement, a new Module can be installed with a slightly different Keycode to represent the new version, which clients can update to reference.

Permissioned Functionality on Modules

Modules are able to restrict access to functionality on their contracts to only the AuctionHouse by using the onlyParent modifier. We denote which standard functions on the above Module types should be restricted by default in the design/ARCHITECTURE.md specification. In order to make governance streamlined in the future, this should be the only access control used on any Module. While this makes sense for the functionality that was designed to be restricted to the AuctionHouse in the first place, this may sound strange for more administrative cases, such as updating default values in an Auction. As such, we need a way to have "pass-through" administration of a Module via the AuctionHouse. This is accomplished with a generic execOnSubmodule function which the owner of the AuctionHouse can use to call any permissioned function on a Module. It may make sense to block certain function signatures that are not administrative (e.g. purchase on Auction modules or mint on Derivative modules) from this as a protection against malicious governance in the future.

Oighty commented 9 months ago

Per discussion today, it may be better to use a shorter Keycode, allow versioning, and allow upgrades in the sunset sense with particular IDs referring to specific Modules + versions.

0xJem commented 8 months ago

Regarding module upgrades:

Version compatibility: