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 viaHybridVault.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 contractADMIN(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 deploymentsuser_to_vault(HashMap[address, HybridVault]) - Maps user address to vaultvault_to_user(HashMap[HybridVault, address]) - Maps vault to user addressstablecoin_fraction(uint256) - Required crvUSD backing as fraction of 1e18 (default: 55e16)pool_limits(HashMap[uint256, uint256]) - Global deposit limit per pool IDallowed_crvusd_vaults(HashMap[address, bool]) - Allowlist for crvUSD vaultscrvusd_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 vaultcrvusd_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.
| Input | Type | Description |
|---|---|---|
crvusd_vault | address | The 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.
| Input | Type | Description |
|---|---|---|
impl | address | New 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.
| Input | Type | Description |
|---|---|---|
frac | uint256 | The 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.
| Input | Type | Description |
|---|---|---|
pool_id | uint256 | The identifier of the pool |
pool_limit | uint256 | The 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
limitparameter defaults to 0 (unlimited). Calling this function without explicitly passing a limit will silently reset any previously set limit to 0. If togglingallowedon/off, be sure to re-pass the desired limit value.
Emits:
SetAllowedCrvusdVault, SetCrvusdVaultLimit.
| Input | Type | Description |
|---|---|---|
vault | address | The crvUSD vault address |
allowed | bool | Whether the vault is allowed |
limit | uint256 | Maximum 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.
| Input | Type | Description |
|---|---|---|
crvusd_vault | address | The crvUSD vault address |
limit | uint256 | The 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 assertsallowed_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.
| Input | Type | Description |
|---|---|---|
crvusd_vault | address | The crvUSD vault address the HybridVault uses |
new_required | uint256 | The new total required crvUSD for this HybridVault |
check_limit | bool | If 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.
| Input | Type | Description |
|---|---|---|
lt | address | The liquidity token address to allocate stablecoins for |
limit | uint256 | The 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)
Related
- Core: HybridVault — the vault instance deployed by this factory.
- Core: Factory — YB core Factory (parent admin chain).
- User: Hybrid Vaults — user-level explainer.