Skip to main content

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:

  1. One ACL surface. Every privileged call checks msg.sender == ADMIN. The Factory and its LT instances never need their own DAO logic.
  2. Allowlist for stablecoin allocators. limit_setters grants non-admin contracts (notably each HybridVault) permission to call lt_allocate_stablecoins with non-zero limits, without giving them broader admin rights.
  3. Permissionless deallocation of disabled LTs. When the admin disables a market via lt_allocate_stablecoins(lt, 0), the disabled_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​

NameTypeNotes
ADMINaddressDAO / multisig that owns this proxy. Never changes after deploy.
FACTORYFactoryThe YB Factory this proxy controls. Held as Factory.admin.
STABLECOINIERC20crvUSD, cached from factory.STABLECOIN() at deploy.

Storage​

NameTypeDescription
disabled_ltsHashMap[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_settersHashMap[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 changed
  • enabled (bool) — true to grant, false to revoke

Privileged LT controls​

All require msg.sender == ADMIN.

FunctionEffect
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:

CallerlimitBehaviour
ADMIN> 0Sets LT.stablecoin_allocation to limit, clears disabled_lts[lt].
limit_setters[caller] == True> 0Same as above. This is the path HybridVaults use to size allocation during deposits.
ADMIN0Flips disabled_lts[lt] = True. Does not change the LT's allocation directly — that is the next step.
Anyone0, disabled_lts[lt] == TrueReads 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.

FunctionForwards 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​

FunctionReturns
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​

StringCause
AccessCaller is not ADMIN, or (for lt_allocate_stablecoins with limit > 0) not in limit_setters.
Not disabledPermissionless lt_allocate_stablecoins(lt, 0) called but the LT was never flagged for wind-down.
Not enough reservesPermissionless deallocation attempt would leave the AMM below the 75% safe floor of oracle-priced collateral value.
  • Core: Factory — the contract owned by this proxy.
  • Core: LT — receives all the lt_* forwards.
  • Core: HybridVault — typical limit_setter and the consumer of lt_allocate_stablecoins from non-admin callers.
  • Core: LTMigrator — reads lt_in_factory and disabled_lts before routing migrations.
  • Core: MigrationFactoryOwner — earlier admin-proxy variant without the limit_setters and lt_distribute_borrower_fees surface.