Skip to main content

HybridVaultFactory

The source code of the HybridVaultFactory.vy contract can be found on GitHub. The contract is written with Vyper version 0.4.3.

Overview

The HybridVaultFactory deploys and manages HybridVault instances. Each address can create exactly one vault. The factory also holds global configuration that vaults read at runtime: the stablecoin fraction, per-pool deposit limits, allowed crvUSD vaults, and per-vault crvUSD tracking for enforcing global caps.

Key responsibilities:

  • Vault deployment: Deploys minimal proxies of the vault implementation and initializes them with the user's chosen crvUSD vault.
  • Stablecoin fraction: Configures what fraction of position value must be backed by crvUSD (default 55%).
  • Pool limits: Sets global maximum deposit sizes per pool. Note: the effective limit for a given vault is personal_limit[pool_id] + factory.pool_limits(pool_id) — each vault can also have a personal limit boost set by admin via HybridVault.set_personal_limit().
  • crvUSD vault allowlist: Controls which ERC-4626 crvUSD vaults (e.g., scrvUSD) can be used, with optional global deposit limits per vault.
  • Required crvUSD tracking: Tracks each vault's required crvUSD and enforces global limits per crvUSD vault (crvusd_vault_total_required).
  • Stablecoin allocation forwarding: Vaults call lt_allocate_stablecoins() through the factory, which forwards to the YB Factory's admin.

Function Documentation

Public Variables

Immutable Variables

  • FACTORY (Factory) - YB Factory contract
  • ADMIN (address) - Admin address, derived via a two-hop chain at deploy time: factory.admin().ADMIN() (Factory → HybridFactoryOwner → ADMIN)

State Variables

  • vault_impl (address) - Current vault implementation for new deployments
  • user_to_vault (HashMap[address, HybridVault]) - Maps user address to vault
  • vault_to_user (HashMap[HybridVault, address]) - Maps vault to user address
  • stablecoin_fraction (uint256) - Required crvUSD backing as fraction of 1e18 (default: 55e16)
  • pool_limits (HashMap[uint256, uint256]) - Global deposit limit per pool ID
  • allowed_crvusd_vaults (HashMap[address, bool]) - Allowlist for crvUSD vaults
  • crvusd_vault_limits (HashMap[address, uint256]) - Max total required crvUSD per crvUSD vault (0 = unlimited)
  • crvusd_vault_total_required (HashMap[address, uint256]) - Current total required crvUSD across all vaults using a given crvUSD vault
  • crvusd_vault_required (HashMap[HybridVault, uint256]) - Required crvUSD tracked per individual vault

Events

VaultCreateduser (indexed), vault (indexed)

SetVaultImplimpl

SetStablecoinFractionstablecoin_fraction

SetPoolLimitpool_id, limit

SetAllowedCrvusdVaultvault, allowed

SetCrvusdVaultLimitcrvusd_vault, limit


create_vault()

Description: Create a new HybridVault for the caller. Deploys a minimal proxy pointing to vault_impl and initializes it with the caller's chosen crvUSD vault. Each address can only create one vault.

Returns: HybridVault.

Emits: VaultCreated.

InputTypeDescription
crvusd_vaultaddressThe crvUSD vault (e.g., scrvUSD) to use for this vault
📄 View Source Code
@external
def create_vault(crvusd_vault: address) -> HybridVault:
assert self.user_to_vault[msg.sender] == empty(HybridVault), "Already created"
assert self.allowed_crvusd_vaults[crvusd_vault], "Vault not allowed"
assert self.vault_impl != empty(address), "Vault impl not set"

vault: HybridVault = HybridVault(create_minimal_proxy_to(self.vault_impl))
extcall vault.initialize(msg.sender, crvusd_vault)

self.user_to_vault[msg.sender] = vault
self.vault_to_user[vault] = msg.sender

log VaultCreated(user=msg.sender, vault=vault)
return vault

set_vault_impl()

Description: Update the vault implementation used for new vault deployments. Does not affect existing vaults. Only callable by ADMIN.

Emits: SetVaultImpl.

InputTypeDescription
impladdressNew vault implementation address
📄 View Source Code
@external
def set_vault_impl(impl: address):
assert msg.sender == ADMIN, "Access"
self.vault_impl = impl
log SetVaultImpl(impl=impl)

set_stablecoin_fraction()

Description: Set the target fraction of deposits to be held as stablecoins (scrvUSD). Value is in 18-decimal precision (e.g., 55e16 = 55%). Only callable by ADMIN.

Emits: SetStablecoinFraction.

InputTypeDescription
fracuint256The stablecoin fraction as a fraction of 1e18
📄 View Source Code
@external
def set_stablecoin_fraction(frac: uint256):
assert msg.sender == ADMIN, "Access"
self.stablecoin_fraction = frac
log SetStablecoinFraction(stablecoin_fraction=frac)

set_pool_limit()

Description: Set the global deposit limit for a specific pool. Only callable by ADMIN.

Emits: SetPoolLimit.

InputTypeDescription
pool_iduint256The identifier of the pool
pool_limituint256The maximum deposit amount allowed for the pool
📄 View Source Code
@external
def set_pool_limit(pool_id: uint256, pool_limit: uint256):
assert msg.sender == ADMIN, "Access"
self.pool_limits[pool_id] = pool_limit
log SetPoolLimit(pool_id=pool_id, limit=pool_limit)

set_allowed_crvusd_vault()

Description: Enable or disable a crvUSD vault for use, with an optional deposit limit. A limit of 0 means unlimited. Only callable by ADMIN.

Caution: The limit parameter defaults to 0 (unlimited). Calling this function without explicitly passing a limit will silently reset any previously set limit to 0. If toggling allowed on/off, be sure to re-pass the desired limit value.

Emits: SetAllowedCrvusdVault, SetCrvusdVaultLimit.

InputTypeDescription
vaultaddressThe crvUSD vault address
allowedboolWhether the vault is allowed
limituint256Maximum total required crvUSD across all HybridVaults using this vault (default: 0)
📄 View Source Code
@external
def set_allowed_crvusd_vault(vault: address, allowed: bool, limit: uint256 = 0):
assert msg.sender == ADMIN, "Access"
self.allowed_crvusd_vaults[vault] = allowed
self.crvusd_vault_limits[vault] = limit
log SetAllowedCrvusdVault(vault=vault, allowed=allowed)
log SetCrvusdVaultLimit(crvusd_vault=vault, limit=limit)

set_crvusd_vault_limit()

Description: Set the total deposit limit for a crvUSD vault across all HybridVaults. 0 means no limit. Only callable by ADMIN.

Emits: SetCrvusdVaultLimit.

InputTypeDescription
crvusd_vaultaddressThe crvUSD vault address
limituint256The maximum total required crvUSD allowed
📄 View Source Code
@external
def set_crvusd_vault_limit(crvusd_vault: address, limit: uint256):
assert msg.sender == ADMIN, "Access"
self.crvusd_vault_limits[crvusd_vault] = limit
log SetCrvusdVaultLimit(crvusd_vault=crvusd_vault, limit=limit)

update_vault_required()

Description: Update the tracked required crvUSD for a HybridVault. On increase, enforces the global vault limit if check_limit=True. On decrease, reduces the global total. Only callable by registered HybridVaults.

Note: When check_limit=True, the function also asserts allowed_crvusd_vaults[crvusd_vault] — if a crvUSD vault has been removed from the allowlist, no vault can increase its requirement (reverts with "Vault wind-down"). Vaults can only decrease their requirement in this state.

InputTypeDescription
crvusd_vaultaddressThe crvUSD vault address the HybridVault uses
new_requireduint256The new total required crvUSD for this HybridVault
check_limitboolIf True, revert when increase would exceed the vault limit (default: True)
📄 View Source Code
@external
def update_vault_required(crvusd_vault: address, new_required: uint256, check_limit: bool = True):
hybrid_vault: HybridVault = HybridVault(msg.sender)
assert self.vault_to_user[hybrid_vault] != empty(address), "Only vaults can call"

old_required: uint256 = self.crvusd_vault_required[hybrid_vault]

if new_required > old_required:
increase: uint256 = new_required - old_required
total: uint256 = self.crvusd_vault_total_required[crvusd_vault] + increase
if check_limit:
assert self.allowed_crvusd_vaults[crvusd_vault], "Vault wind-down"
vault_limit: uint256 = self.crvusd_vault_limits[crvusd_vault]
if vault_limit > 0:
assert total <= vault_limit, "Beyond vault limit"
self.crvusd_vault_total_required[crvusd_vault] = total
elif new_required < old_required:
decrease: uint256 = old_required - new_required
self.crvusd_vault_total_required[crvusd_vault] -= decrease

self.crvusd_vault_required[hybrid_vault] = new_required

lt_allocate_stablecoins()

Description: Allocate stablecoins to a liquidity token via the factory admin. Calls FACTORY.admin() which returns the HybridFactoryOwner contract, then calls lt_allocate_stablecoins() on it. The HybridFactoryOwner checks if the caller is the ADMIN or a registered limit_setter. Only callable by registered HybridVaults.

InputTypeDescription
ltaddressThe liquidity token address to allocate stablecoins for
limituint256The allocation limit
📄 View Source Code
@external
def lt_allocate_stablecoins(lt: address, limit: uint256):
assert self.vault_to_user[HybridVault(msg.sender)] != empty(address), "Only vaults can call"
extcall (staticcall FACTORY.admin()).lt_allocate_stablecoins(lt, limit)