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-marketfee
, 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
andflash
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 addresslt_impl
(address
) - Current LT (Leveraged Token) implementation addressvirtual_pool_impl
(address
) - Current Virtual Pool implementation addressprice_oracle_impl
(address
) - Current Price Oracle implementation addressstaker_impl
(address
) - Current Staker implementation address
System Configuration
STABLECOIN
(IERC20
) - The stablecoin used by the protocol (immutable)agg
(address
) - Current price aggregator addressflash
(address
) - Current flash loan contract addressgauge_controller
(address
) - Curve gauge controller addressfee_receiver
(address
) - Address that receives collected feesadmin
(address
) - Primary admin addressemergency_admin
(address
) - Emergency admin addressmin_admin_fee
(uint256
) - Minimum admin fee rate
Market Management
markets
(Market[MAX_MARKETS]
) - Array of all created marketsmarket_count
(uint256
) - Current number of marketsallocators
(HashMap[address, uint256]
) - Stablecoin allocation limits per addressmint_factory
(address
) - crvUSD mint factory address
Data Structs
Market
asset_token
(IERC20
) - Asset token contract addresscryptopool
(CurveCryptoPool
) - Curve crypto pool contract addressamm
(address
) - AMM contract addresslt
(address
) - LT contract addressprice_oracle
(address
) - Price oracle contract addressvirtual_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 addresslt
(address
) - New LT implementation addressvirtual_pool
(address
) - New Virtual Pool implementation addressprice_oracle
(address
) - New Price Oracle implementation addressstaker
(address
) - New Staker implementation address
SetAllocator (Emitted when stablecoin allocation limits are set)
allocator
(address
) - Address that can allocate stablecoinsamount
(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 addressemergency_admin
(address
) - New emergency admin addressold_admin
(address
) - Previous admin addressold_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 indexasset_token
(address
) - Asset token addresscryptopool
(address
) - Curve pool addressamm
(address
) - AMM contract addresslt
(address
) - LT contract addressprice_oracle
(address
) - Price oracle addressvirtual_pool
(address
) - Virtual pool addressstaker
(address
) - Staker addressagg
(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.
Input | Type | Description |
---|---|---|
pool | CurveCryptoPool | Curve crypto pool for the asset |
fee | uint256 | Trading fee (max 10%) |
rate | uint256 | Interest rate for the market |
debt_ceiling | uint256 | Maximum 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.
Input | Type | Description |
---|---|---|
i | uint256 | Market 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.
Input | Type | Description |
---|---|---|
mint_factory | address | Address 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.
Input | Type | Description |
---|---|---|
allocator | address | Address to allocate stablecoins to |
amount | uint256 | New 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.
Input | Type | Description |
---|---|---|
agg | address | New 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.
Input | Type | Description |
---|---|---|
flash | address | New 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.
Input | Type | Description |
---|---|---|
new_admin | address | New admin address |
new_emergency_admin | address | New 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.
Input | Type | Description |
---|---|---|
new_fee_receiver | address | New 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.
Input | Type | Description |
---|---|---|
gc | address | Gauge 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.
Input | Type | Description |
---|---|---|
new_min_admin_fee | uint256 | New 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.
Input | Type | Description |
---|---|---|
amm | address | New AMM implementation address (optional) |
lt | address | New LT implementation address (optional) |
virtual_pool | address | New Virtual Pool implementation address (optional) |
price_oracle | address | New Price Oracle implementation address (optional) |
staker | address | New 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)