Technical overview
Use this overview to choose a starting page, map the contracts of a market, and learn the invariants that your integration code must respect. The Terminology map translates user-doc terms into onchain names.
Each YieldBasis market holds a Curve pool liquidity provider (LP) position at 2× leverage. The design aim is that the depositor keeps full price exposure to BTC or ETH. A central Factory deploys each market as a fixed set of contracts. When the leveraged AMM (LEVAMM) and the Curve pool disagree, a Temporary Redemption Discount (TRD) can occur.
Where to start
Start at the page that matches your task:
- To understand the math, read the Math primer and Compounding leverage.
- To integrate YieldBasis markets for routing, start with the Quickstart, and then read Quote and route.
- To run arbitrage or MEV, read Arbitrage and VirtualPool.
- To build deposit and withdraw flows, read Deposit and withdraw.
- To enumerate deployed markets, read Find a market in the Quickstart and Contract addresses.
- To read all events, use the Event catalog.
- To debug a revert, use Error codes.
- To understand the pool fee and
price_scalepolicies, read Pool policy. - To trade with Pyth price reports on a pool that runs the oracle-report policy, read Oracle report arbitrage.
System architecture
Factory.add_market() deploys LT, the LEVAMM, and the oracle of a market, plus the VirtualPool and the LiquidityGauge when their blueprints are set. The Curve pool is an input to add_market(). All markets share the crvUSD aggregator, the crvUSD flash lender, and the FeeSplitter. The following figure shows how the contracts of one market connect:
The contracts of a market work in this way:
- The
Factorydeploys each market withadd_market(). It creates theCryptopoolLPOracle,LT, and LEVAMM from blueprints. It also creates aVirtualPooland aLiquidityGaugewhen their blueprints are set. The Curve pool is an input toadd_market(); the Factory does not deploy it. LT, the market's vault contract, is the entry point of a market. A deposit adds crvUSD and the asset to the Curve pool, and the pool mints the LP tokens to the LEVAMM as collateral.LTthen books the LP and the crvUSD debt in the LEVAMM. A withdrawal runs the same path in reverse.- The LEVAMM prices its LP with
CryptopoolLPOracle. The oracle readsvirtual_priceandprice_scalefrom the pool and the crvUSD price from the aggregator. - Arbitrageurs trade crvUSD against LP tokens with
AMM.exchange.VirtualPoolwraps that trade into a direct crvUSD ↔ asset swap. It takes a crvUSD flash loan and adds liquidity to, or removes it from, the Curve pool in the same transaction. - The
LiquidityGaugeis thestakerofLTand holds the staked yb-LP. LTmints admin fees as yb-LP toFactory.fee_receiver(), the FeeSplitter.
Shared contracts
These contracts serve all markets:
Factory: deploys markets and keeps the registry (markets(i),market_count()). Its gettersagg(),flash(), andfee_receiver()return the shared crvUSD aggregator, crvUSD flash lender, and FeeSplitter. For its functions, read Factory.- FeeSplitter: sends a YB DAO-set share of the admin fees,
split_fraction(), to the net-pressure controller and the rest to the FeeDistributor, which pays veYB holders weekly. For the split, read Fees and veYB revenue and Net pressure and the PID controller. GaugeController: sends YB emissions to each gauge by gauge weight. veYB holders vote on the weights. For the calls, read GaugeController.HybridVaultFactory: deploys oneHybridVaultproxy for each user. The vault holds positions in up to 16 markets and keeps crvUSD backing in an allowlisted ERC-4626 vault. For the calls, read HybridVault.
Core technical concepts
2× leverage mechanism
Each market keeps a constant 2× compounding leverage through the invariant of the AMM (LEVAMM) contract. For what compounding leverage does to a position, read Compounding leverage. The invariant uses the LP oracle price . The LP oracle derives it from virtual_price and price_scale of the Curve pool and from the crvUSD aggregator. It reads no external price for the asset. On this page, is the leverage ratio, so is 2× leverage.
When the LEVAMM quote and the Curve pool disagree, arbitrageurs who trade through VirtualPool move the state back toward . The system needs no keeper contract. Arbitrageurs rebalance the LEVAMM when the gap is larger than their costs.
Market isolation
These rules separate the markets and name the components that they share:
- Each market keeps separate collateral, debt, and fee balances.
- All markets use crvUSD, the
Factoryand its admin, the crvUSD aggregator (Factory.agg()), the crvUSD flash lender (Factory.flash()), the fee receiver, and governance. A fault in a shared component affects every market. - Each market can have its own fee rates and debt-ceiling allocations, subject to
Factoryconstraints. - Each market keeps the implementations of its deployment. Future markets can use upgraded blueprints. A blueprint change does not change existing markets.
Fee flow
A market has two sources of trading fees. Borrower interest is a separate flow:
- LEVAMM trading fees grow
x0, the anchor term of the LEVAMM pricing formula. - Curve pool trading fees flow into LP
virtual_pricegrowth. Thereserved_profit_fraction, which the Curve DAO sets per pool, sets the share of pool profit that rebalancing cannot spend. - Borrower interest is not LP yield.
LTsweeps it and donates it back to the Curve pool, where it adds to the rebalance budget. This donation is refueling.
For the user-level account of each fee, read Fees and veYB revenue.
Curve dependency
YieldBasis markets sit on Curve pools (asset / crvUSD pairs). In integration code, rely on the pool interfaces in this section, not on assumptions about pool internals. The integration uses these pool features:
- Oracle inputs. Pool state that
CryptopoolLPOraclereads:virtual_priceandprice_scale, combined into the local LP-price formula2 · virtual_price · √price_scale. The oracle does not callPOOL.lp_price(). - Pool balances.
VirtualPoolandLTreadbalances(i).VirtualPooluses them to size the flash loan and to quote.LTuses them inpreview_emergency_withdraw. - LP token mint and burn.
LTadds liquidity withadd_liquidityand withdraws withremove_liquidity_fixed_out.VirtualPoolusesadd_liquidityandremove_liquidity. The arbitrage path mints LP with flash-borrowed crvUSD, trades inside the LEVAMM, and burns LP to repay. - Donation channel.
LT.distribute_borrower_feescallsadd_liquidity(amounts, min_amount, empty(address), donation=True). Borrower interest then flows into the rebalance budget of the pool, and no user receives LP shares. The pool releases donations over its configured window rather than in one block. - Pool rebalancing. The Curve pool moves
price_scaletoward a target. The LEVAMM oracle readsprice_scale, so the target of the pool sets how fast the LEVAMM follows the market. These rules govern the move:- Only the first operation in a block that calls
tweak_pricecan moveprice_scale. If that operation fails the profit test (vp_boosted > lp_xcp_profit),price_scaledoes not move in that block. - Without a policy, the target is the pool's internal exponential moving average (EMA),
price_oracle. - With a policy, the target is
POLICY.get_price_scale(), limited to 20% above or belowprice_oracle. YieldBasis does not call this function. Only the v3.0.0 pools of markets 7 to 10 have the policy hook.
- Only the first operation in a block that calls
- Pool policy. A v3.0.0 pool (markets 7 to 10) can have an external policy contract, which
POLICY()returns (the zero address when the pool has no policy). To see which policy a pool runs, callPOLICY()on the pool or read the table in Contract addresses. For each policy's functions and configuration, read Pool policy. What the policy sets depends on its type:- On a pool that runs the oracle-report policy (
YBOraclizedPolicy), the policy sets the swap fee from signed Pyth Pro (Pyth Lazer) price reports. It also sets theprice_scaletarget and checks the debt ratio ofLTdeposits (the LT deviation check). - A pool that runs
YBTwocryptoPolicykeeps its own fee. That policy sets only theprice_scaletarget.
- On a pool that runs the oracle-report policy (
- Trading fees. The pool splits its profit by
reserved_profit_fraction, which the Curve DAO sets per pool. The reserved profit fraction raiseslp_xcp_profit, the level thatvirtual_pricemust stay above after aprice_scalemove. That share therefore stays with LPs. A rebalance can spend the rest, the rebalance budget. To check the fraction, callreserved_profit_fraction()on the pool (1e10 = 100%).
For the user-level explanation of the oracle, read Oracle design.
Contract architecture
Each market has its own LT, LEVAMM (AMM.vy), VirtualPool, CryptopoolLPOracle, and LiquidityGauge. All markets share one Factory and the governance contracts. The table gives the role of each contract in one line and links its reference page.
| Contract | Role | Reference |
|---|---|---|
Factory.vy | Deploys markets from blueprints, keeps the registry (markets(i)), and controls crvUSD allocations and debt ceilings. A blueprint change applies to future markets only. | Factory |
LT.vy | Market vault: mints yb-LP (for example yb-cbBTC), keeps the staked and unstaked accounting, and donates borrower interest. It is ERC-4626-like but not strict: it has no convertToAssets, mint, or redeem. | LT |
AMM.vy (LEVAMM) | Holds Curve LP tokens as collateral and crvUSD as debt, keeps 2× leverage through the x_0 invariant, keeps fees inside the invariant, and accrues interest through rate_mul. | LEVAMM |
VirtualPool.vy | Wraps the LEVAMM, the Curve pool, and a crvUSD flash loan into direct crvUSD ↔ asset swaps: get_dy(i, j, in_amount) quotes and exchange(...) trades. | VirtualPool |
CryptopoolLPOracle.vy | Prices one LP token as 2 × virtual_price × √price_scale × p_agg, computed locally from the pool state. | CryptopoolLPOracle |
LiquidityGauge.vy | ERC-4626-style wrapper over yb-LP that pays YB emissions and up to 7 other reward tokens. | LiquidityGauge |
StakeZap.vy | Deposits and stakes, or unstakes and withdraws, in one call. | StakeZap |
HybridVaultFactory.vy, HybridVault.vy | Deploy and run one vault for each user that pairs market positions with crvUSD backing. | HybridVault |
YB.vy, VotingEscrow.vy, GaugeController.vy | The governance token, veYB locks, and gauge weights with YB emissions. | YB token, VotingEscrow, GaugeController |
FeeSplitter.vy, FeeDistributor.vy | Split admin fees between the net-pressure controller and veYB holders, and pay the veYB share. | Net pressure (PID), FeeDistributor |
Terminology map
User-facing docs and onchain symbols use different names for the same concept. Use this table to map user-doc terms to code.
| User doc term | Onchain symbol | Location |
|---|---|---|
| yb-LP | ERC-20 share of LT (ERC-4626-like but not strict) | LT.vy |
| LEVAMM | AMM | AMM.vy |
| Vault | LT | LT.vy |
| VirtualPool | VirtualPool | VirtualPool.vy |
| Oracle price () | CryptopoolLPOracle.price(); the LEVAMM calls price_w() | CryptopoolLPOracle.vy |
| Debt-to-value ratio (DTV) | AMM.get_debt() / (AMM.collateral_amount() × ); 50% at 2× leverage | AMM.vy |
| Watermark | ideal_staked, a field of LT.liquidity() | LT.vy |
| Recovery Mode | state: staked < ideal_staked | LT.vy |
| Admin fee | admin, a field of LT.liquidity(); withdraw_admin_fees() mints it as yb-LP to Factory.fee_receiver() | LT.vy |
| Refueling | LT.distribute_borrower_fees() | LT.vy |
| TRD | observed: preview_withdraw(10**18) * 10**(18 - asset_decimals) / pricePerShare() − 1 (signed; negative during a discount) | no variable |
| Fundamental Value (PPS, price per share) | pricePerShare(), priced from price_scale and the crvUSD aggregator. Markets 0 to 2 were deployed from an earlier LT blueprint whose price reads the pool's price_oracle(); current LT.vy reads price_scale() | LT.vy |
| Redemption Value | preview_withdraw(shares): live Curve pool unwind through calc_withdraw_fixed_out | LT.vy |
| Gauge | LiquidityGauge, the staker() of LT; it wraps yb-LP shares with ERC-4626 accounting | LiquidityGauge.vy |
| StakeZap | deposit_and_stake, withdraw_and_unstake | StakeZap.vy |
| Hybrid Vault | HybridVault, created by HybridVaultFactory.create_vault() | HybridVault.vy |
| FeeSplitter | Factory.fee_receiver(); its share for net pressure is split_fraction() | FeeSplitter.vy |
| veYB fee revenue | FeeDistributor | FeeDistributor.vy |
| Pool policy | POLICY() on the Curve pool (functions on Pool policy) | Curve Twocrypto v3.0.0 pool |
| Oracle-report policy | YBOraclizedPolicy, returned by POLICY() on a pool that runs it | Pool policy contract |
| Base fee, capture, fallback fee, relax time, step cap, report lifetime (pool policy) | fields of config() on YBOraclizedPolicy: fee.base_fee, fee.capture, fee.fallback_fee, fee.relax_time, driver.step_cap, driver.report_lifetime | Pool policy contract |
| Gauge voting | GaugeController.vote_for_gauge_weights: votes instant, 10-day cooldown per gauge | GaugeController.vy |
| veYB | ERC-721 NFT minted by VotingEscrow.create_lock | VotingEscrow.vy |
Core invariants
Assume these invariants in integration code. Do not violate them.
- : the value is immutable for each
Factorydeployment. It is not a DAO-mutable parameter. - Safe-debt bounds:
MIN_SAFE_DEBT ≤ debt / collateral_value ≤ MAX_SAFE_DEBT. At , roughly[1/16, 8.5/16]of collateral value. A trade that pushes the ratio outside the band and away from equilibrium reverts. - Watermark:
ideal_stakedrises on stake. It does not fall on loss. It falls in proportion when shares leave the gauge. - TRD sign convention: TRD is the signed gap between Redemption Value (
preview_withdraw) and Fundamental Value (pricePerShare()). A negative TRD means that a withdrawal now returns less. Negative (discount) is the common case. Positive (premium) is possible when live Curve pool state is rich relative to the oracle. This sign pattern is an economic regularity, not a contract-enforced invariant:preview_withdrawhas no clamp. For the formula with unit scaling, readpreview_withdrawandpricePerSharein Deposit and withdraw. - Aggregator band: the
Factoryconstructor andset_aggcheck that the crvUSD aggregator price is inside , open at both ends. TheFactorydoes not check the band at other times.
Mathematical foundation
The LEVAMM invariant solves a quadratic for :
where LEV_RATIO = L² / (2L − 1)². At , LEV_RATIO = 4/9. For the implementation, read AMM.get_x0 in the LEVAMM reference.
The full derivation, including flash-loan sizing and the admin-fee curve, is in §3 of the Math primer.
Source
yield-basis/yb-core holds the full contract source. These docs name contracts and functions and link to files in that repository.
Related
- Contract addresses: the canonical list of mainnet addresses.
- Glossary: full terminology reference.
- Risk disclaimer: the risks that integrators should show to their users.