CapitalStack

Git Source

Inherits: StateAware, Patchable, BoardControlled, ICapitalStack

Author: Daniel Yamagata & Naveen Ailawadi

A smart contract that lays out the logic for the capital stack of a company. The capital stack is the foundation for the mergers, acquistions, recapitalization, and liquidation of companies in the Mezzanine protocol A capital stack is made up of:

  • Seniority Levels

  • Assets

Each seniority level and asset is associated with an 'asset class'. An asset of a given class can be inserted into only one seniority level of the same class. There are three types of asset classes, defined by the following enum:

  • 0: Null

  • 1: Common Equity

  • 2: Preferred Equity

  • 3: Debt

A seniority level or asset with asset class 'Null' is considered invalid and cannot be added to the stack. Seniority levels must be added, such that a new seniority level's asset class cannot be below the previous's asset class and cannot exceed the next's asset class. For example, a seniority level of 'Common Equity' cannot be inserted above 'Debt' Here is a visual example:


IndexAsset ClassAssets

2

Debt

D, E, F, G

3

Preferred Equity

B, C

1

Common Equity

A

Seniority levels are represented by a circular, doubly linked list. Their indexes are not assumed to be sequential

State Variables

CapitalStackStorageLocation

bytes32 private constant CapitalStackStorageLocation =
    0x36092dfc4ccd895585e40ef098e02e99f066e08bb0ed611f9c0eac213dfb8b00;

Functions

_getCapitalStackStorage

function _getCapitalStackStorage() internal pure returns (CapitalStackStorage storage $);

constructor

constructor(address _mezzHub, address _mezzMigrator) StateAware(_mezzHub) Patchable(_mezzMigrator);

init

Initializes state, inserts a common equity seniority level, adds the common shares to the capital, and creates a document for the common shares in the document registry. Can only be called once

Called atomically by the MezzDeployer during the deployment of a Mezz Instance

function init(address initTreasury, address commonShares) external virtual initializer;

Parameters

NameTypeDescription

initTreasury

address

The treasury to be used by the capital stack

commonShares

address

The address of the common shares

__CapitalStack_init

function __CapitalStack_init(address commonShares) internal onlyInitializing;

addSeniorityLevel

Adds a seniority level to the capital stack. Only callable by the company's board of directors, who are the signers of the treasury contract. Seniority levels in a capital stack follow specific rules in how they can be inserted: Each seniority level has an associated 'AssetClass' defined by the following enum:

  • 0: Null

  • 1: Common Equity

  • 2: Preferred Equity

  • 3: Debt

A seniority level cannot be inserted if the previous seniority level, associated with 'previousSeniorityLevelIndex', has a higher 'AssetClass' than the proposed seniority level. For example, a seniority level of Common Equity cannot be inserted above a seniority level of Preferred Equity. Once inserted, only assets of that given class can be added to the given seniority level. A Mezzanine company can insert seniority levels above or below seniority levels of the same asset class. For example, a seniority level of Preferred Equity can be inserted above or below another seniority level of Preferred Equity. A company may do this since these different levels may be treated differently during liquidation, recapitalization, etc. Seniority levels with new types of asset classes can be added easily via upgrades

function addSeniorityLevel(uint256 previousSeniorityLevelIndex, uint8 proposedAssetClass)
    external
    virtual
    onlyBoard
    pausable
    returns (uint256);

Parameters

NameTypeDescription

previousSeniorityLevelIndex

uint256

The index of the previous seniority level, which is the point of insertion

proposedAssetClass

uint8

The asset class of the proposed seniority level

Returns

NameTypeDescription

<none>

uint256

The index of the newly inserted seniority level

_addSeniorityLevel

Adding a seniority level has four cases:

  • (A) The list is empty. Should only be true during initialization

  • (B) An insertion at the front of the list

    • 'previousSeniorityLevelIndex' is zero

  • (C) An insertion at the back of the list

    • The 'next' of 'previousSeniorityLevelIndex' is zero

  • (D) An insertion in the middle of the list

    • 'previousSeniorityLevelIndex' is greater than zero but its 'next' is not equal to zero

    The sentinel acts both as a header and a tail. The 'next' of the sentinel indicates the 'front' of the list while the 'previous' of the sentinel indicates the 'back' of the list Cases (A) and (D) are handled the same. Cases are handled differently to optimize for SSTOREs

function _addSeniorityLevel(uint256 previousSeniorityLevelIndex, uint8 proposedAssetClass) internal returns (uint256);

_addToFrontOfSeniorityLevelList

Case (B): An insertion at the front of the list

function _addToFrontOfSeniorityLevelList(
    uint256 seniorityLevelIndexToAdd,
    uint8 proposedAssetClass,
    uint256 oldNextIndex,
    DataTypes.SeniorityLevel storage oldNextSeniorityLevelPointer,
    CapitalStackStorage storage $
) internal virtual;

_addToBackOfSeniorityLevelList

Case (C): An insertion at the back of the list

function _addToBackOfSeniorityLevelList(
    uint256 seniorityLevelIndexToAdd,
    uint8 proposedAssetClass,
    uint256 previousSeniorityLevelIndex,
    DataTypes.SeniorityLevel storage previousSeniorityLevelPointer,
    CapitalStackStorage storage $
) internal virtual;

_addToMiddleOfSeniorityLevelList

Cases (A) and (D): An insertion in an empty list or in the middle of the list, respectively

function _addToMiddleOfSeniorityLevelList(
    uint256 seniorityLevelIndexToAdd,
    uint8 proposedAssetClass,
    uint256 previousSeniorityLevelIndex,
    uint256 oldNextIndex,
    DataTypes.SeniorityLevel storage previousSeniorityLevelPointer,
    DataTypes.SeniorityLevel storage oldNextSeniorityLevelPointer,
    CapitalStackStorage storage $
) internal virtual;

removeSeniorityLevel

Removes a seniority level with 'seniorityLevelIndexToRemove' from the capital stack. Only callable by the company's board of directors, who are the signers of the Treasury. The seniority level must be empty, meaning it contains no assets.

function removeSeniorityLevel(uint256 seniorityLevelIndexToRemove) external virtual onlyBoard freezable;

Parameters

NameTypeDescription

seniorityLevelIndexToRemove

uint256

The index of the seniority level to remove

addAssetToSeniorityLevel

Adds an asset to a seniority level. Only callable by the company's board of directors, who are the signers of the Treasury The asset must have the same 'AssetClass' as the seniority level it is being added to. This function adds a document to the document registry that is associated with the asset. It can be updated at any time. An asset can only be part of one seniority level and cannot be moved retroactively to a different seniority level.

A treasury cannot call this function with its multi-sig functionality but instead must call the 'addAssetToCapitalStack' function

function addAssetToSeniorityLevel(
    uint256 seniorityLevelIndex,
    address assetToAdd,
    string memory assetDocumentName,
    string memory assetDocumentUri
) external virtual onlyBoard pausable returns (uint256);

Parameters

NameTypeDescription

seniorityLevelIndex

uint256

The index of the seniority level to add the asset to

assetToAdd

address

assetDocumentName

string

The name of the asset document

assetDocumentUri

string

The URI of the asset document

Returns

NameTypeDescription

<none>

uint256

The document index for the asset

_addAssetToSeniorityLevel

Adds an asset to the seniority level at 'seniorityLevelIndex' and creates a document for the asset

function _addAssetToSeniorityLevel(
    uint256 seniorityLevelIndex,
    address assetToAdd,
    string memory assetDocumentName,
    string memory assetDocumentUri
) internal virtual returns (uint256);

Returns

NameTypeDescription

<none>

uint256

The document index for the asset

removeAsset

Removes an asset from the capital stack. Only callable by the company's board of directors, who are the signers of the Treasury. A company cannot remove their initial common shares from the capital stack. All Mezz assets must implement a 'removable()' function that returns a bool indicating the asset's removability upon certain conditions. For example, Mezz shares are not removable if their total supply is greater than zero. If the 'removable()' of 'assetToRemove' returns false, this function will revert

A treasury cannot call this function with its multi-sig functionality but instead must call the 'removeAssetFromCapitalStack' function

function removeAsset(address assetToRemove) external onlyBoard freezable;

Parameters

NameTypeDescription

assetToRemove

address

The address of the asset to remove

updateAssetDocument

Updates an asset's document in the document registry

function updateAssetDocument(address asset, string memory updatedDocumentName, string memory updatedDocumentUri)
    public
    virtual
    returns (uint256);

Returns

NameTypeDescription

<none>

uint256

The new version of the document, which is originally indexed at zero

getSeniorityLevelInfo

Returns information regarding a seniority level as a 'DataTypes.SeniorityLevelInfo' struct

Returns an empty struct if the seniority level does not exist

function getSeniorityLevelInfo(uint256 seniorityLevelIndex) public view returns (DataTypes.SeniorityLevelInfo memory);

getSeniorityLevels

Returns the seniority levels of the capital stack in ascending order as an array of 'DataTypes.SenioritylevelInfo' structs. For instance, the ith index of the return values would be considered below the ith + 1 index of the return values in the capital stack.

Seniority level indexes are non-sequential

function getSeniorityLevels() public view virtual returns (DataTypes.SeniorityLevelInfo[] memory);

getAssetsInSeniorityLevel

Returns the assets in the seniority level with 'seniorityLevelIndex' as an array of addresses

Returns an empty array of addresses if 'seniorityLevelIndex' does not exist

function getAssetsInSeniorityLevel(uint256 seniorityLevelIndex) public view returns (address[] memory);

getSeniorityLevelIndexByAsset

Returns zero if the asset is not in the stack. Otherwise, returns the seniority level index for 'asset'

function getSeniorityLevelIndexByAsset(address asset) public view returns (uint256);

getNumberOfAssetsInSeniorityLevel

Returns the number of assets in the seniority level with 'seniorityLevelIndex'

Will also returns zero if the seniority level does not exist

function getNumberOfAssetsInSeniorityLevel(uint256 seniorityLevelIndex) public view returns (uint256);

isAssetInCapitalStack

Returns true if 'asset' is in the capital stack, false otherwise

function isAssetInCapitalStack(address asset) public view returns (bool);

totalSeniorityLevels

Returns the total number of seniority levels

function totalSeniorityLevels() public view returns (uint256);

getDocumentIndexByAsset

Returns the document index for 'asset'

Reverts if 'asset' is not in the stack

function getDocumentIndexByAsset(address asset) public view returns (uint256);

_validateAssetIsInStack

Reverts if 'asset' has not been added to the capital stack

function _validateAssetIsInStack(address asset) internal view virtual;

_addDocumentForAsset

Creates a new document in the document registry, sets the '_documentIndexByAsset' mapping, and returns the document index

function _addDocumentForAsset(address asset, string memory documentName, string memory documentUri)
    internal
    virtual
    returns (uint256);

_incrementCurrentSeniorityLevelIndex

This function is kept as virtual in case overflow protection is desired in the future

function _incrementCurrentSeniorityLevelIndex() internal virtual returns (uint256);

_incrementSeniorityLevelsRemoved

This function is kept as virtual in case overflow protection is desired in the future

function _incrementSeniorityLevelsRemoved() internal virtual;

coreId

Returns the coreId of the implementation as a bytes32

The core ID is the keccak256 hash of the contract name followed by a version under the following syntax: "mezzanine.coreId.ContractName.vX" For example, the core ID of the 2nd version of the Treasury would be the following: keccak256(abi.encodePacked("mezzanine.coreId.Treasury.v2"))

function coreId() public pure virtual override(Credentialed, ICredentialed) returns (bytes32);

version

Returns the version of the implementation as a uint256

function version() public pure virtual override(Credentialed, ICredentialed) returns (uint256);

supportsInterface

ERC165 support

function supportsInterface(bytes4 interfaceId)
    public
    view
    virtual
    override(MezzUUPSUpgradeable, IERC165)
    returns (bool);

upgradeToNewerVersion

Upgrades 'this' to a newer version via the Mezz Migrator. Only callable by the Treasury, whose signers are the board of directors

Will revert if the protocol state is 'Paused' or 'Frozen'

function upgradeToNewerVersion(uint256 newVersion, bytes memory data) public virtual onlyProxy onlyBoard;

Parameters

NameTypeDescription

newVersion

uint256

The new version to upgrade to

data

bytes

The data to be passed to the new implementation, which likely should be a reinitializer function if used

_authorizePatch

Access control for 'resetToPatchedLatestVersion()'

function _authorizePatch(bytes memory) internal view virtual override;

Structs

CapitalStackStorage

struct CapitalStackStorage {
    uint256 _currentSeniorityLevelIndex;
    uint256 _seniorityLevelsRemoved;
    mapping(uint256 => DataTypes.SeniorityLevel) _seniorityLevels;
    mapping(address => uint256) _seniorityLevelIndexByAsset;
    mapping(address => uint256) _documentIndexByAsset;
}

Last updated