Mezz Guards
Last updated
Last updated
Guards are an important component of safes, which are multi-signature wallets designed by Safe (formerly Gnosis Safe). A safe can set another contract to arbitrarily block certain transactions. Departments and treasuries in Mezzanine directly inherit from safe, enabling them to have multi-signature functionality. As multi-signature wallets, they can execute arbitrary transactions so long as a sufficient number of signatures are provided. Guards in Mezzanine are key in blocking both departments and treasuries from managing their signers, resetting their guard, changing their threshold, or executing an arbitrary delegate call. Guards are atomically deployed with departments and treasuries by the Mezz Deployer.
A standard safe manages its signers (known as owners in Safe's source code) and guard by executing an external call on itself via its multi-signature functionality. This guarantees that the threshold is reached when changing its state.
A guard's checkTransaction function is called each time that a safe uses its mult-signature functionality:
The blocking of calls by a guard is completely bespoke and up to the implementation. It should be noted that checkTransaction is called via a static call and does not change state.
Guards in Mezzanine, known as Mezz Guards, are used to block direct calls made to manage a treasury or department's signers, its threshold, or its guard:
These functions can only be called via other functions, which enable strict access control. For example, only an ancestor can swap the guard for a department:
A Mezzanine treasury or department cannot change their guards to user-created ones. Guards must be deployed via the Mezz Deployer, which deploys implementations that the Mezzanine team sets.
There are three different types of Guards in Mezzanine: a whitelist guard, a blacklist guard, and a shareholder guard. Treasuries and departments can use any of these three guards. By default, all departments and treasuries use a blacklist, since it is much less heavy-handed. Whitelists should only be used when the restriction of a department's capabilities is of the utmost importance.
A Mezz Guard keeps also track of two lists which can be used as either a whitelist or blacklist depending on the implementation. One list is an enumerable set of contract addresses, while the other ‘list’ is a dynamic array of guard selectors. A guard selector is the pairing of a contract address and a function selector.
For example, assume a department uses a guard that is a blacklist. The guard's contract list contains the billing router, and its list of guard selectors contains USDC and the function selector to transfer. All calls to the billing router or to transfer USDC by the department will revert if called via its multi-signature functionality.
Both whitelist and blacklist guards will check in their checkTransaction function if the call is made to any contracts or guard selectors on the list.
If either of these conditions is true, the blacklist guard will revert the transaction, while the whitelist guard will not revert the transaction. This enables a department’s ancestor or the treasury’s governance to either blacklist or whitelist certain function calls.
By default, Mezzanine uses blacklist guards. Whitelist guards require an extensive amount of overhead. Due to the high number of dependencies in Mezzanine, all other core contracts, such as the token timelock, would need to be whitelisted in addition to all other non-Mezzanine contracts that the department may use.
Guards can be easily changed from one implementation to another. It should be noted that when a guard is swapped, the lists are not maintained. For example, if a whitelist guard is used such that swaps on a DEX revert, those transactions will not be blocked if the user swaps to a blacklist guard. Instead, the controller of the guard must manually update the list.
Each guard has a controller which is set during the guard's initialization. The controller is responsible for setting the contract and guard selector lists. The controller of a department's guard will be its parent, while the controller of a treasury's guard will be the system used for shareholder governance.
The shareholder guard is unique. It is a blacklist guard whose controller is always the company's governance system. Departments may want to use a shareholder guard in special circumstances. For instance, a Mezzanine company may desire to be more democratic in its operations. All decisions then to blacklist transactions for departments should then stem from governance. Treasuries, by default, are deployed with shareholder guards.