Skip to main content

Factory

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

Overview

The Factory is the on-chain market registry and deployer for YieldBasis. It instantiates and wires together per-market components for a given 2-coin Curve CryptoPool (stablecoin, asset) and maintains global protocol configuration. Each market created by the Factory has its own LT, AMM, and Oracle instances, with optional VirtualPool and Staker helpers, and is recorded in an indexed registry.

For each Curve pool, the Factory deploys:

  • LT (loan/token) — holds stablecoin capacity, tracks rate; receives unlimited stablecoin approval from the Factory.
  • AMM — parameterized with LEVERAGE = 2e18, a per-market fee, and the market’s Oracle.
  • Price Oracle — constructed against the Curve pool and the current global aggregator agg.
  • VirtualPool — created only if both virtual_pool_impl and flash are set.
  • Staker — created only if staker_impl is set and then registered on LT.

All market addresses are stored in a Market struct and exposed via markets[i] with market_count tracking.

Components are deployed by cloning blueprint implementations using Vyper’s built-in create_from_blueprint function. The Factory holds implementation addresses (amm_impl, lt_impl, price_oracle_impl, and optionally virtual_pool_impl, staker_impl); on add_market(...) it calls create_from_blueprint with the appropriate constructor arguments to deploy the Oracle, LT, and AMM, then sets them (LT.set_amm(amm), LT.set_rate(rate)) and, if configured, deploys the VirtualPool (create_from_blueprint(virtual_pool_impl, amm)) and Staker (create_from_blueprint(staker_impl, lt)). Updating implementations can only be done by the contract's admin via set_implementations(...) affects future markets; existing ones can be retrofitted for helpers via fill_staker_vpool(i).


Function Documentation

Public Variables

The Factory contract exposes several public variables for reading contract state and configuration.

Constants

  • MAX_MARKETS (uint256) - Maximum number of markets that can be created (50,000)
  • LEVERAGE (uint256) - Fixed leverage ratio for all markets (2x = 2e18)

Implementation Addresses

  • amm_impl (address) - Current AMM implementation address
  • lt_impl (address) - Current LT (Leveraged Token) implementation address
  • virtual_pool_impl (address) - Current Virtual Pool implementation address
  • price_oracle_impl (address) - Current Price Oracle implementation address
  • staker_impl (address) - Current Staker implementation address

System Configuration

  • STABLECOIN (IERC20) - The stablecoin used by the protocol (immutable)
  • agg (address) - Current price aggregator address
  • flash (address) - Current flash loan contract address
  • gauge_controller (address) - Curve gauge controller address
  • fee_receiver (address) - Address that receives collected fees
  • admin (address) - Primary admin address
  • emergency_admin (address) - Emergency admin address
  • min_admin_fee (uint256) - Minimum admin fee rate

Market Management

  • markets (Market[MAX_MARKETS]) - Array of all created markets
  • market_count (uint256) - Current number of markets
  • allocators (HashMap[address, uint256]) - Stablecoin allocation limits per address
  • mint_factory (address) - crvUSD mint factory address

Data Structs

Market

  • asset_token (IERC20) - Asset token contract address
  • cryptopool (CurveCryptoPool) - Curve crypto pool contract address
  • amm (address) - AMM contract address
  • lt (address) - LT contract address
  • price_oracle (address) - Price oracle contract address
  • virtual_pool (address) - Virtual pool contract address (optional)
  • staker (address) - Staker contract address (optional)

Events

SetImplementations (Emitted when contract implementations are updated)

  • amm (address) - New AMM implementation address
  • lt (address) - New LT implementation address
  • virtual_pool (address) - New Virtual Pool implementation address
  • price_oracle (address) - New Price Oracle implementation address
  • staker (address) - New Staker implementation address

SetAllocator (Emitted when stablecoin allocation limits are set)

  • allocator (address) - Address that can allocate stablecoins
  • amount (uint256) - Allocation amount

SetFeeReceiver (Emitted when the fee receiver address is updated)

  • fee_receiver (address) - New fee receiver address

SetAgg (Emitted when the price aggregator is updated)

  • agg (address) - New price aggregator address

SetFlash (Emitted when the flash loan contract is updated)

  • flash (address) - New flash loan contract address

SetAdmin (Emitted when admin addresses are changed)

  • admin (address) - New admin address
  • emergency_admin (address) - New emergency admin address
  • old_admin (address) - Previous admin address
  • old_emergency_admin (address) - Previous emergency admin address

SetMinAdminFee (Emitted when the minimum admin fee is updated)

  • admin_fee (uint256) - New minimum admin fee

SetGaugeController (Emitted when the gauge controller is set)

  • gc (address) - New gauge controller address

MarketParameters (Emitted when a new market is created or updated)

  • idx (uint256) - Market index
  • asset_token (address) - Asset token address
  • cryptopool (address) - Curve pool address
  • amm (address) - AMM contract address
  • lt (address) - LT contract address
  • price_oracle (address) - Price oracle address
  • virtual_pool (address) - Virtual pool address
  • staker (address) - Staker address
  • agg (address) - Price aggregator address

add_market()

Description:
Creates a new market for a specific crypto asset. This function deploys all the necessary contracts (price oracle, LT, AMM, virtual pool, staker) and sets up the market infrastructure. This function can only be called by the admin of the contract.

Returns:
Market - The newly created market struct containing all contract addresses.

Emits:
MarketParameters - Market creation event with all contract addresses and market index.

InputTypeDescription
poolCurveCryptoPoolCurve crypto pool for the asset
feeuint256Trading fee (max 10%)
rateuint256Interest rate for the market
debt_ceilinguint256Maximum stablecoin borrowing limit
📄 View Source Code
@external
@nonreentrant
def add_market(
pool: CurveCryptoPool,
fee: uint256,
rate: uint256,
debt_ceiling: uint256
) -> Market:
assert msg.sender == self.admin, "Access"
assert staticcall pool.coins(0) == STABLECOIN.address, "Wrong stablecoin"
assert staticcall pool.decimals() == 18

market: Market = empty(Market)
agg: address = self.agg

market.asset_token = IERC20(staticcall pool.coins(1))
market.cryptopool = pool
market.price_oracle = create_from_blueprint(self.price_oracle_impl, pool.address, agg)
market.lt = create_from_blueprint(
self.lt_impl,
market.asset_token.address,
STABLECOIN,
pool.address,
self
)
market.amm = create_from_blueprint(
self.amm_impl,
market.lt,
STABLECOIN.address,
pool.address,
LEVERAGE,
fee,
market.price_oracle
)
extcall LT(market.lt).set_amm(market.amm)
extcall LT(market.lt).set_rate(rate)
assert extcall STABLECOIN.approve(market.lt, max_value(uint256), default_return_value=True)
extcall LT(market.lt).allocate_stablecoins(debt_ceiling)

if self.virtual_pool_impl != empty(address) and self.flash != empty(address):
market.virtual_pool = create_from_blueprint(
self.virtual_pool_impl,
market.amm
)
if self.staker_impl != empty(address):
market.staker = create_from_blueprint(
self.staker_impl,
market.lt)
extcall LT(market.lt).set_staker(market.staker)

i: uint256 = self.market_count
assert i < MAX_MARKETS, "Too many markets"
self.market_count = i + 1
self.markets[i] = market

log MarketParameters(
idx=i,
asset_token=market.asset_token.address,
cryptopool=market.cryptopool.address,
amm=market.amm,
lt=market.lt,
price_oracle=market.price_oracle,
virtual_pool=market.virtual_pool,
staker=market.staker,
agg=agg
)

return market

fill_staker_vpool()

Description:
Updates existing markets with new Virtual Pool or Staker implementations. This function allows upgrading specific markets with new contract implementations without recreating the entire market. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
MarketParameters - Market update event with updated contract addresses.

InputTypeDescription
iuint256Market index to update
📄 View Source Code
@external
def fill_staker_vpool(i: uint256):
assert msg.sender == self.admin, "Access"
assert i < self.market_count, "Nonexistent market"

market: Market = self.markets[i]
assert market.lt != empty(address)
assert market.amm != empty(address)

new_virtual_pool: bool = False
if market.virtual_pool == empty(address):
new_virtual_pool = self.virtual_pool_impl != empty(address) and self.flash != empty(address)
else:
new_virtual_pool = (staticcall VirtualPool(market.virtual_pool).IMPL()) != self.virtual_pool_impl

if new_virtual_pool:
market.virtual_pool = create_from_blueprint(
self.virtual_pool_impl,
market.amm
)

if market.staker == empty(address) and self.staker_impl != empty(address):
market.staker = create_from_blueprint(
self.staker_impl,
market.lt)
extcall LT(market.lt).set_staker(market.staker)

self.markets[i] = market

log MarketParameters(
idx=i,
asset_token=market.asset_token.address,
cryptopool=market.cryptopool.address,
amm=market.amm,
lt=market.lt,
price_oracle=market.price_oracle,
virtual_pool=market.virtual_pool,
staker=market.staker,
agg=(staticcall LPOracle(market.price_oracle).AGG())
)

set_mint_factory()

Description:
Sets the crvUSD mint factory address. This function can only be called once and gives the mint factory unlimited approval to withdraw stablecoins from the Factory. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetAllocator - Allocation event for the mint factory with unlimited amount.

InputTypeDescription
mint_factoryaddressAddress of the crvUSD mint factory
📄 View Source Code
@external
@nonreentrant
def set_mint_factory(mint_factory: address):
assert msg.sender == self.admin, "Access"
assert self.mint_factory == empty(address), "Only set once"
assert mint_factory != empty(address)
self.mint_factory = mint_factory
# crvUSD factory can take back as much as it wants. Very important function - this is why it can be called only once
assert extcall STABLECOIN.approve(mint_factory, max_value(uint256), default_return_value=True)

log SetAllocator(allocator=mint_factory, amount=max_value(uint256))

set_allocator()

Description:
Sets stablecoin allocation limits for specific addresses. This function allows the admin to control how much stablecoins each allocator can borrow from the system. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetAllocator - Allocation update event with allocator address and new amount.

InputTypeDescription
allocatoraddressAddress to allocate stablecoins to
amountuint256New allocation amount
📄 View Source Code
@external
@nonreentrant
def set_allocator(allocator: address, amount: uint256):
assert msg.sender == self.admin, "Access"
assert allocator != self.mint_factory, "Minter"
assert allocator != empty(address)

old_allocation: uint256 = self.allocators[allocator]
if amount > old_allocation:
# Use transferFrom
assert extcall STABLECOIN.transferFrom(allocator, self, amount - old_allocation, default_return_value=True)
self.allocators[allocator] = amount

elif amount < old_allocation:
# Allow to take back the allocation via transferFrom, but not more than the allocation reduction
assert extcall STABLECOIN.approve(allocator, (staticcall STABLECOIN.allowance(self, allocator)) + old_allocation - amount, default_return_value=True)
self.allocators[allocator] = amount

log SetAllocator(allocator=allocator, amount=amount)

set_agg()

Description:
Sets the price aggregator address. This function updates the price feed used by all markets for stablecoin pricing and validates the new aggregator. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetAgg - Aggregator update event with new aggregator address.

InputTypeDescription
aggaddressNew price aggregator address
📄 View Source Code
@external
def set_agg(agg: address):
assert msg.sender == self.admin, "Access"
assert agg != empty(address)
self.agg = agg
self._validate_agg()
log SetAgg(agg=agg)

set_flash()

Description:
Sets the flash loan contract address. This function updates the flash loan provider used by Virtual Pools for arbitrage operations. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetFlash - Flash loan contract update event with new contract address.

InputTypeDescription
flashaddressNew flash loan contract address
📄 View Source Code
@external
def set_flash(flash: address):
assert msg.sender == self.admin, "Access"
self.flash = flash
log SetFlash(flash=flash)

set_admin()

Description:
Sets new admin and emergency admin addresses. This function allows transferring administrative control of the Factory contract. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetAdmin - Admin transfer event with new and old admin addresses.

InputTypeDescription
new_adminaddressNew admin address
new_emergency_adminaddressNew emergency admin address
📄 View Source Code
@external
def set_admin(new_admin: address, new_emergency_admin: address):
assert msg.sender == self.admin, "Access"
assert new_admin != empty(address)
assert new_emergency_admin != empty(address)
log SetAdmin(admin=new_admin, emergency_admin=new_emergency_admin, old_admin=self.admin, old_emergency_admin=self.emergency_admin)
self.admin = new_admin
self.emergency_admin = new_emergency_admin

set_fee_receiver()

Description:
Sets the fee receiver address. This function updates where collected fees are sent in the system. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetFeeReceiver - Fee receiver update event with new receiver address.

InputTypeDescription
new_fee_receiveraddressNew fee receiver address
📄 View Source Code
@external
def set_fee_receiver(new_fee_receiver: address):
assert msg.sender == self.admin, "Access"
self.fee_receiver = new_fee_receiver
log SetFeeReceiver(fee_receiver=new_fee_receiver)

set_gauge_controller()

Description:
Sets the gauge controller address. This function can only be called once and sets the Curve gauge controller for staking rewards. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetGaugeController - Gauge controller update event with new controller address.

InputTypeDescription
gcaddressGauge controller address
📄 View Source Code
@external
def set_gauge_controller(gc: address):
assert msg.sender == self.admin, "Access"
assert gc == empty(address), "Already set"
self.gauge_controller = gc
log SetGaugeController(gc=gc)

set_min_admin_fee()

Description:
Sets the minimum admin fee rate. This function controls the minimum fee that can be charged by LT contracts for admin operations. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetMinAdminFee - Minimum admin fee update event with new fee rate.

InputTypeDescription
new_min_admin_feeuint256New minimum admin fee (max 100%)
📄 View Source Code
@external
def set_min_admin_fee(new_min_admin_fee: uint256):
assert msg.sender == self.admin, "Access"
assert new_min_admin_fee <= 10**18, "Admin fee too high"
self.min_admin_fee = new_min_admin_fee
log SetMinAdminFee(admin_fee=new_min_admin_fee)

set_implementations()

Description:
Updates contract implementations for future deployments. This function allows the admin to upgrade the system by setting new implementation addresses for AMM, LT, Virtual Pool, Price Oracle, and Staker contracts. This function can only be called by the admin of the contract.

Returns:
No return value (void function).

Emits:
SetImplementations - Implementation update event with new contract addresses.

InputTypeDescription
ammaddressNew AMM implementation address (optional)
ltaddressNew LT implementation address (optional)
virtual_pooladdressNew Virtual Pool implementation address (optional)
price_oracleaddressNew Price Oracle implementation address (optional)
stakeraddressNew Staker implementation address (optional)
📄 View Source Code
@external
def set_implementations(amm: address, lt: address, virtual_pool: address, price_oracle: address, staker: address):
assert msg.sender == self.admin, "Access"
if amm != empty(address):
self.amm_impl = amm
if lt != empty(address):
self.lt_impl = lt
if virtual_pool != empty(address):
self.virtual_pool_impl = virtual_pool
if price_oracle != empty(address):
self.price_oracle_impl = price_oracle
if staker != empty(address):
self.staker_impl = staker
log SetImplementations(amm=amm, lt=lt, virtual_pool=virtual_pool, price_oracle=price_oracle, staker=staker)