HybridFactoryOwner
The source code of the HybridFactoryOwner.vy contract can be found on GitHub. The contract is written with Vyper version 0.4.3.
Overview
HybridFactoryOwner is the address held as Factory.admin. The DAO does not call Factory.set_admin / LT.set_rate directly; it calls the equivalent wrapper on HybridFactoryOwner, and the wrapper forwards. Three reasons:
- One ACL surface. Every privileged call checks
msg.sender == ADMIN. The Factory and its LT instances never need their own DAO logic. - Allowlist for stablecoin allocators.
limit_settersgrants non-admin contracts (notably eachHybridVault) permission to calllt_allocate_stablecoinswith non-zero limits, without giving them broader admin rights. - Permissionless deallocation of disabled LTs. When the admin disables a market via
lt_allocate_stablecoins(lt, 0), thedisabled_lts[lt]flag flips. After that, anyone can pull stablecoins out of that market (down to a 75% safety floor on remaining reserves) — useful for winding down without DAO-vote latency.
For market discovery, integrators read the Factory directly. They only need this contract when they are operating inside a HybridVault or running governance tooling.
Immutable variables
| Name | Type | Notes |
|---|---|---|
ADMIN | address | DAO / multisig that owns this proxy. Never changes after deploy. |
FACTORY | Factory | The YB Factory this proxy controls. Held as Factory.admin. |
STABLECOIN | IERC20 | crvUSD, cached from factory.STABLECOIN() at deploy. |
Storage
| Name | Type | Description |
|---|---|---|
disabled_lts | HashMap[LT, bool] | Flag set by ADMIN when an LT is wound down (lt_allocate_stablecoins(lt, 0)). Once true, anyone can drain the market's allocation down to safe reserves. LTMigrator refuses to migrate INTO a disabled LT. |
limit_setters | HashMap[address, bool] | Allowlist of non-admin contracts that may call lt_allocate_stablecoins(lt, limit) with limit > 0. HybridVault instances are added here so they can size their own stablecoin allocation per deposit. |
Events
SetLimitSetter — emitted by set_limit_setter
setter(address) — address whose privilege changedenabled(bool) — true to grant, false to revoke
Privileged LT controls
All require msg.sender == ADMIN.
| Function | Effect |
|---|---|
lt_set_rate(lt, rate) | Forwards to LT.set_rate(rate). Changes the borrow / refuel rate on the AMM. |
lt_set_amm_fee(lt, fee) | Forwards to LT.set_amm_fee(fee). Bounded by AMM.MAX_FEE (10%). |
lt_set_killed(lt, is_killed) | Kill or unkill the market via LT.set_killed. Killed markets force the emergency_withdraw exit path. |
lt_distribute_borrower_fees(lt, discount) | Forwards to LT.distribute_borrower_fees(discount). Anyone can call the default-discount variant directly on LT; this wrapper exists so admin can pass a non-default discount. |
Stablecoin allocation
@external
def lt_allocate_stablecoins(lt: LT, limit: uint256 = max_value(uint256))
Three call patterns:
| Caller | limit | Behaviour |
|---|---|---|
ADMIN | > 0 | Sets LT.stablecoin_allocation to limit, clears disabled_lts[lt]. |
limit_setters[caller] == True | > 0 | Same as above. This is the path HybridVaults use to size allocation during deposits. |
ADMIN | 0 | Flips disabled_lts[lt] = True. Does not change the LT's allocation directly — that is the next step. |
| Anyone | 0, disabled_lts[lt] == True | Reads LT.amm.value_oracle().value, computes safe_limit = value × 3 / 4, and shrinks LT.stablecoin_allocation down to that floor (or available_limit if smaller). Reverts "Not enough reserves" if available_limit is below safe_limit. |
Anyone, disabled_lts[lt] == False, limit == 0 | — | Reverts "Not disabled". |
Anyone else, limit > 0 | — | Reverts "Access". |
The 75% floor is the same conservative bound used internally by LT.allocate_stablecoins when deflating. It guarantees that even after permissionless deallocation, the AMM still holds three-quarters of its oracle-priced collateral value as stablecoin reserves.
Factory passthroughs
All require msg.sender == ADMIN. Each is a thin one-line forward.
| Function | Forwards to |
|---|---|
transfer_ownership_back() | FACTORY.set_admin(ADMIN, FACTORY.emergency_admin()) — gives admin role back to the DAO directly, bypassing this proxy. |
add_market(pool, fee, rate, debt_ceiling) | FACTORY.add_market(...) |
set_fee_receiver(addr) | FACTORY.set_fee_receiver(addr) |
set_implementations(amm, lt, virtual_pool, price_oracle, staker) | FACTORY.set_implementations(...) |
set_min_admin_fee(value) | FACTORY.set_min_admin_fee(value) |
fill_staker_vpool(i) | FACTORY.fill_staker_vpool(i) |
set_allocator(allocator, amount) | FACTORY.set_allocator(...) |
set_agg(agg) | FACTORY.set_agg(agg) |
set_flash(flash) | FACTORY.set_flash(flash) |
Limit-setter management
@external
def set_limit_setter(setter: address, enabled: bool)
Only ADMIN. Toggles limit_setters[setter]. Emits SetLimitSetter.
When the HybridVaultFactory deploys a new HybridVault, the DAO is expected to add the vault to limit_setters so it can call lt_allocate_stablecoins during deposit. Revoking the flag freezes the vault's ability to grow stablecoin allocation on a market.
Public views
| Function | Returns |
|---|---|
lt_in_factory(lt) | True if STABLECOIN.allowance(FACTORY, lt) > 0 — a stand-in check for "factory has approved this LT to spend its crvUSD," used by LTMigrator and by HybridVaults before they route deposits. |
lt_needs_withdraw(lt) | Quote: number of yb-LP shares the holder would need to withdraw to bring available_limit = lp_price × collateral_amount back down to the LT's stablecoin_allocated. Returns 0 if no withdrawal is needed. |
Common revert strings
| String | Cause |
|---|---|
Access | Caller is not ADMIN, or (for lt_allocate_stablecoins with limit > 0) not in limit_setters. |
Not disabled | Permissionless lt_allocate_stablecoins(lt, 0) called but the LT was never flagged for wind-down. |
Not enough reserves | Permissionless deallocation attempt would leave the AMM below the 75% safe floor of oracle-priced collateral value. |
Related
- Core: Factory — the contract owned by this proxy.
- Core: LT — receives all the
lt_*forwards. - Core: HybridVault — typical
limit_setterand the consumer oflt_allocate_stablecoinsfrom non-admin callers. - Core: LTMigrator — reads
lt_in_factoryanddisabled_ltsbefore routing migrations. - Core: MigrationFactoryOwner — earlier admin-proxy variant without the
limit_settersandlt_distribute_borrower_feessurface.