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​

VaultCreated — user (indexed), vault (indexed)

SetVaultImpl — impl

SetStablecoinFraction — stablecoin_fraction

SetPoolLimit — pool_id, limit

SetAllowedCrvusdVault — vault, allowed

SetCrvusdVaultLimit — crvusd_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)