Overview
Last updated
Last updated
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
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.
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.
All core implementations, excluding Mezz guards, are aware of the MezzHub. Core contracts query the following information from the Mezz Hub:
The address of the Fee Controller
The address of the Document Registry
The address of the Mezz Deployer
The protocol state
Whether or not an address is a whitelisted denomination asset
Whether or not an implementation is frozen
Whether or not a deployment is frozen
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.
The Mezzanine protocol's state is set in the Mezz Hub. The protocol can be in three different states:
Core contract Functionality
Enabled
Partially enabled
Vast majority disabled
Upgrades to new versions
Enabled
Disabled
Disabled
Patches
Disabled
Enabled
Enabled
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.