Overview

The following documentation assumes you are familiar with Solidity, its best practices, and the UUPS proxy pattern. The documentation is meant to summarize complex parts of the code and the protocol's architecture. For this reason, solidity practices and straightforward functionality will not be thoroughly explained. For a full understanding of the codebase, it is highly recommended to spend significant time reviewing the code, itself.

Learn more about Solidity here

Learn more about common Solidity Patterns here

Learn more about Proxy Upgrades here

Learn more about the UUPS proxies here and here

Architecture Overview

Core vs Non-Core

Mezzanine contracts are split between core and non-core contracts.

Core contracts are those related to an instance of Mezzanine and deployed as proxies via the Mezz Deployer. Core contracts are initialized atomically upon deployment. Examples of core contracts include a company's Treasury, Token Timelock, and Equity Financing Module, among others.

Non-core contracts refer to adjacent contracts that do not make up an instance of a company. Non-core contracts are not redeployed when a new Mezzanine company is created and include the Billing Router, Document Registry, and Mezz Hub, among others. Core contracts query non-core contracts for their functionality. For example, core contracts query the protocol state from the Mezz Hub.

Proxies

All contracts in Mezzanine are deployed via EIP-1167 minimal proxies and EIP-1967 upgradeable proxies and follow EIP-1967 storage slots to avoid storage collisions between versions. Upgradeable contracts follow the UUPS proxy pattern. The deployment of the Mezzanine companies via proxies drastically reduces the cost of deployments while also enabling upgradeability.

Proxies cannot call the constructor of the implementation upon their deployment. Constructors are not stored in a contract's runtime code. Instead, every contract in Mezzanine that needs constructor-like functionality to initialize state implements an init function, which can only be called once.

Some then may be curious as to why Mezzanine contract implementations have a constructor. Constructors are used to both disable the initializers for implementations and set immutable variables. In Solidity, immutable variables are stored in a contract's runtime code, not storage. Therefore, the value for the immutable variables across all proxies will be the same as the implementation. This is useful for storing the addresses of the implementation and non-core contracts, such as the Mezz Hub, since these values should not change across company instances. A Mezzanine contract’s constructor will never initialize a storage variable.

Mezz Hub

All core implementations, excluding Mezz guards, are aware of the MezzHub. Core contracts query the following information from the Mezz Hub:

  1. The address of the Fee Controller

  2. The address of the Document Registry

  3. The address of the Mezz Deployer

  4. The protocol state

  5. Whether or not an address is a whitelisted denomination asset

  6. Whether or not an implementation is frozen

  7. Whether or not a deployment is frozen

  8. Whether or not an address is a defender

The Mezz Hub sits at the heart of the Mezzanine protocol. All core and non-core contracts rely on the Mezz Hub's state for their functionality. It is by far the most centralized contract across the entire protocol.

The Mezz Hub is ownable. Ownable contracts are frequently used throughout Solidity codebases. The terminology stems from OpenZeppelin's ownable implementation. Ownable contracts are controlled by an admin, referred to as the owner. Access control is implemented such that only the owner can call admin-like functions.

The owner of the Mezz Hub will be a multi-signature wallet controlled by the Mezzanine team and will have special privileges. The owner of the Mezz Hub can:

  • Call admin-like functions for non-core contracts, setting their state

  • Reset or set new core implementations in the Mezz Migrator, which is responsible for versioning implementations and upgrading contracts

  • Cancel any pending proposals or pending actions in shareholder governance or a company's payroll

  • Set the protocol state

  • Freeze deployments or implementations

  • Whitelist denomination assets

  • Reset any upgradeable core contract to a patched version, given that the protocol state is not Active

Each signer of the multi-signature wallet that is the owner of the Mezz Hub will safeguard their keys using best security practices. An adequate number of signers will be used to decrease security risks stemming from centralization.

Protocol State

The Mezzanine protocol's state is set in the Mezz Hub. The protocol can be in three different states:

ActivePausedFrozen

Core contract Functionality

Enabled

Partially enabled

Vast majority disabled

Upgrades to new versions

Enabled

Disabled

Disabled

Patches

Disabled

Enabled

Enabled

Contract Upgrades

Contract upgrades in Mezzanine follow strict rules such that a contract can only change its implementation following strict version control. The Mezzanine team has chosen to enable migrations via upgrades to enable companies to update the logic of their contracts without altering or copying contract storage. Without upgrades, migrating stored data would be extremely costly at scale. Consider a company with 100 employees. To upgrade to a new method of payroll, 100 new employee contracts would need to be made for the sole purpose of updating contract logic.

Last updated