Core
There are several basic principles that shape the design of the Mellow Permissionless Vaults protocol:
  1. 1.
    Liquidity put into protocol can always be withdrawn. That is governance, strategy or any other entity cannot preclude a Liquidity Provider from withdrawing the funds.
  2. 2.
    Actions of Strategist or Governance that change substantial protocol parameters undergo some clearance time (typically 1 day).
  3. 3.
    Strategy actions are limited by the permissions of the particular Vault System.
  4. 4.
    Everyone can create a Vault System ready for accepting deposits by Liquidity Providers and managing the liquidity by the Strategy.
  5. 5.
    The Strategy is an arbitrary contract constrained by the Vault System permissions system
Mellow permissionless vaults smart contracts

Vault groups

Vault group is a set of contracts that manage Vaults of a specific Vault kind. It is used to create a new Vault or manage Vault params. It has 3 main contracts:
  1. 1.
    Vault Governance - contains a set of params for a specific Vault kind managed by Strategist or Governance. The params are defined by being:
    • Being instant or delayed (changes take into effect after some delay)
    • Being applied to all Vaults in the Vault Group System or to some specific vaults
    • Being managed by Strategist or by Governance.
  2. 2.
    Vault Factory is a prototype for all vaults in the Vault Group
  3. 3.
    Vault instances (pink color) - each new vault is created using EIP-1167 lightweight proxy clone from Vault Factory

Protocol Governance

Protocol governance manages global params of the protocol.
Param
Description
Unit
Default value
maxTokensPerVault
Maximum different tokens that a vault can manage
#
10
governanceDelay
The delay that has to pass for updating certain class of params (delayed)
secs
86400
protocolTreasury
The treasury address for protocol fees
address
forceAllowMask
If a bit in this mask is set, the permission is allowed for all addresses
bitmask (uint256)
0
withdrawLimit
The maximum withdrawal per block. The hardcoded minimum value is 200_000 USD
USD
200_000
unitPrices
Token price estimates, used for withdrawLimit. For new token can be set immediately, for updates a cool-down period of 14 days is used.
address (token) => units of token per 1 USD
USDC => 10 ^ 6 ETH => 10 ^ 18 / 3000 BTC => 10 ^ 8 / 45000
Withdrawal limits
For each withdrawn token, there's a per block limit = withdrawLimit * unitPrices[token]. This is used mainly for the flashloan attacks protection as a potential attacker couldn't withdraw large amounts from the pool in one block.
To protect the protocol from the governance hijack DDOS attack (set withdraw limit to 0), the minimum threshold of 200_000 USD for withdrawLimit is hardcoded. The other vector of attack could be setting unitPrices to 0 but that's mitigated by the required 14 days delay to update prices.
Given the above, unitPrices don't need to be updated very frequently as they are used only for withdrawals limit (used only for protection from attacks). For example, the ETH price of 3000 USD / ETH could be used for months.
Permissions
ProtocolGovernance manages permission system for other vaults. The ProtocolGovernance itself doesn't impose any restrictions with these permissions, rather it's only the data used by Vaults to impose restrictions. Each permission is a mapping from the address to a bitmask.
Permission
Description
Bitmask
REGISTER_VAULT
The address is allowed to register a new vault in Vault Registry
0x1 (0th bit)
CREATE_VAULT
The address is allowed to create a new vault
0x10 (1st bit)
ERC20_TRANSFER
The address (token) is allowed to be transfered by the vault
0x100 (2nd bit)
ERC20_VAULT_TOKEN
The address (token) is allowed to be a vault token
0x1000 (3rd bit)
ERC20_APPROVE
The address (other protocol) can have ERC20 allowance from the Vault
0x10000 (4th bit)
ERC20_APPROVE_RESTRICTED
The address (other protocol) can have ERC20 allowance from the Vault if the Vault is Trusted
0x100000 (5th bit)
TRUSTED_STRATEGY
The vault is Trusted
0x1000000 (6th bit)
Note: forceAllowMask in protocolParams overrides the permission for all addresses (if a bit in this mask is set)
Roles
  • ADMIN: allowed to update any params and assign ADMIN and ADMIN_DELEGATE roles
  • ADMIN_DELEGATE: allowed to update any params
  • OPERATOR: allowed to update Operator params in the vault (currently only to stop deposits)
Validators
ProtocolGovernance also manages a set of validators for external calls (calls a Strategy can do on behalf of Vault). Validator is a contract that verifies that an external call is made in a good faith with correct params.
ProtocolGovernance has a mapping of address to a validator. If the validator is missing then the call to an address is forbidden. Otherwise, validator checks if the call is valid, using ProtocolGovernance permissions and other validation logic.

VaultRegistry

VaultRegistry stores information about all open vaults.
The registry adheres to ERC721 standard: each Vault is assigned an NFT. NFT owner is allowed to deposit and withdraw ERC20 tokens from the vault. ERC20 approved address is allowed to redistribute tokens between vaults in the same Vault System. The semantics is that NFT owner would be ERC20Root vault that manages liquidity providers and NFT approved address is the Strategy that manages the liquidity.

Oracles

Oracles are used for estimating the value of the deposited / withdrawn tokens compared to lp tokens of the vault. Mellow oracle has 5 price safety levels:
  1. 1.
    Spot price - exact price, but easy to manipulate
  2. 2.
    UniV3 TWAP for 2.5 munites - somewhat stale price, but harder to manipulate
  3. 3.
    Same as 2 but for 7.5 minutes
  4. 4.
    Same as 2 but for 30 minutes
  5. 5.
    Chainlink oracle
Each time oracle is queried, an acceptable safety level is supplied and prices are returned accordingly.

Deposits / Withdrawals

Deposits are made through ERC20RootVault in several steps:
  1. 1.
    A higher estimate for ERC20RootVault TVL is fetched (each vault has 2 TVL estimates - higher and lower. The discrepancies in TVLs data are caused by different oracles price discrepancies)
  2. 2.
    Deposited tokens are used in the same ratio as in the TVL estimate
  3. 3.
    Tokens are transferred into ERC20RootVault
  4. 4.
    The shares of deposited tokens to the TVL estimate are calculated for each token. Minumum is calculated
  5. 5.
    Using the minimum in step 4 and total LP supply, LP tokens amount is calculated and minted
The withdrawal is made similarly but with some modifications:
  1. 1.
    A lower estimate for ERC20RootVault TVL is fetched
  2. 2.
    Burned lp token, lp supply and TVL is used to calculate each token amount pending withdrawal
  3. 3.
    ERC20RootVault transfers tokens from the first subvault (typically ERC20Vault) in the Vault System. If those amounts are not enough then it goes to 2nd subvault, etc.
  4. 4.
    After ERC20RootVault has enough tokens on balance it burns lp tokens and transfers ERC20 tokens to withdrawer
  5. 5.
    All leftovers are transferred to the first subvault
Withdrawal amounts are subject to withdrawal limits imposed by ProtocolGovernance (see Protocol Governance)

Rebalances

To rebalance tokens between vaults pull method is used (available for anyone approved for ERC721 vault NFT). If it's called on any subvault except the first, it pulls tokens into the first vault. If it's called on the first vault then tokens can be pulled to any Vault of the Vault System.
To rebalance tokens within the first vault typically externalCall function is used. External calls are allowed to be called by Strategy on behalf of the Vault to external DEXes like Uniswap and Curve.