Skip to main content

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:

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:

Contracts of one market: LT adds liquidity and donates borrower interest to the Curve pool, the pool mints LP tokens to the LEVAMM, CryptopoolLPOracle prices that LP for the LEVAMM from the pool and the crvUSD aggregator, the VirtualPool trades against the LEVAMM with a crvUSD flash loan, the gauge holds staked yb-LP, and LT mints admin fees as yb-LP to the FeeSplitter.Contracts of one market: LT adds liquidity and donates borrower interest to the Curve pool, the pool mints LP tokens to the LEVAMM, CryptopoolLPOracle prices that LP for the LEVAMM from the pool and the crvUSD aggregator, the VirtualPool trades against the LEVAMM with a crvUSD flash loan, the gauge holds staked yb-LP, and LT mints admin fees as yb-LP to the FeeSplitter.

The contracts of a market work in this way:

  1. The Factory deploys each market with add_market(). It creates the CryptopoolLPOracle, LT, and LEVAMM from blueprints. It also creates a VirtualPool and a LiquidityGauge when their blueprints are set. The Curve pool is an input to add_market(); the Factory does not deploy it.
  2. 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. LT then books the LP and the crvUSD debt in the LEVAMM. A withdrawal runs the same path in reverse.
  3. The LEVAMM prices its LP with CryptopoolLPOracle. The oracle reads virtual_price and price_scale from the pool and the crvUSD price from the aggregator.
  4. Arbitrageurs trade crvUSD against LP tokens with AMM.exchange. VirtualPool wraps 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.
  5. The LiquidityGauge is the staker of LT and holds the staked yb-LP.
  6. LT mints admin fees as yb-LP to Factory.fee_receiver(), the FeeSplitter.

Shared contracts​

These contracts serve all markets:

  • Factory: deploys markets and keeps the registry (markets(i), market_count()). Its getters agg(), flash(), and fee_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 one HybridVault proxy 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 pop_o. 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, LL is the leverage ratio, so L=2L = 2 is 2× leverage.

When the LEVAMM quote and the Curve pool disagree, arbitrageurs who trade through VirtualPool move the state back toward L=2L = 2. 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 Factory and 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 Factory constraints.
  • 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_price growth. The reserved_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. LT sweeps 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 CryptopoolLPOracle reads: virtual_price and price_scale, combined into the local LP-price formula 2 · virtual_price · √price_scale. The oracle does not call POOL.lp_price().
  • Pool balances. VirtualPool and LT read balances(i). VirtualPool uses them to size the flash loan and to quote. LT uses them in preview_emergency_withdraw.
  • LP token mint and burn. LT adds liquidity with add_liquidity and withdraws with remove_liquidity_fixed_out. VirtualPool uses add_liquidity and remove_liquidity. The arbitrage path mints LP with flash-borrowed crvUSD, trades inside the LEVAMM, and burns LP to repay.
  • Donation channel. LT.distribute_borrower_fees calls add_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_scale toward a target. The LEVAMM oracle reads price_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_price can move price_scale. If that operation fails the profit test (vp_boosted > lp_xcp_profit), price_scale does 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 below price_oracle. YieldBasis does not call this function. Only the v3.0.0 pools of markets 7 to 10 have the policy hook.
  • 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, call POLICY() 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 the price_scale target and checks the debt ratio of LT deposits (the LT deviation check).
    • A pool that runs YBTwocryptoPolicy keeps its own fee. That policy sets only the price_scale target.
  • Trading fees. The pool splits its profit by reserved_profit_fraction, which the Curve DAO sets per pool. The reserved profit fraction raises lp_xcp_profit, the level that virtual_price must stay above after a price_scale move. That share therefore stays with LPs. A rebalance can spend the rest, the rebalance budget. To check the fraction, call reserved_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.

ContractRoleReference
Factory.vyDeploys 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.vyMarket 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.vyWraps 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.vyPrices one LP token as 2 × virtual_price × √price_scale × p_agg, computed locally from the pool state.CryptopoolLPOracle
LiquidityGauge.vyERC-4626-style wrapper over yb-LP that pays YB emissions and up to 7 other reward tokens.LiquidityGauge
StakeZap.vyDeposits and stakes, or unstakes and withdraws, in one call.StakeZap
HybridVaultFactory.vy, HybridVault.vyDeploy and run one vault for each user that pairs market positions with crvUSD backing.HybridVault
YB.vy, VotingEscrow.vy, GaugeController.vyThe governance token, veYB locks, and gauge weights with YB emissions.YB token, VotingEscrow, GaugeController
FeeSplitter.vy, FeeDistributor.vySplit 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 termOnchain symbolLocation
yb-LPERC-20 share of LT (ERC-4626-like but not strict)LT.vy
LEVAMMAMMAMM.vy
VaultLTLT.vy
VirtualPoolVirtualPoolVirtualPool.vy
Oracle price (pop_o)CryptopoolLPOracle.price(); the LEVAMM calls price_w()CryptopoolLPOracle.vy
Debt-to-value ratio (DTV)AMM.get_debt() / (AMM.collateral_amount() × pop_o); 50% at 2× leverageAMM.vy
Watermarkideal_staked, a field of LT.liquidity()LT.vy
Recovery Modestate: staked < ideal_stakedLT.vy
Admin feeadmin, a field of LT.liquidity(); withdraw_admin_fees() mints it as yb-LP to Factory.fee_receiver()LT.vy
RefuelingLT.distribute_borrower_fees()LT.vy
TRDobserved: 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 Valuepreview_withdraw(shares): live Curve pool unwind through calc_withdraw_fixed_outLT.vy
GaugeLiquidityGauge, the staker() of LT; it wraps yb-LP shares with ERC-4626 accountingLiquidityGauge.vy
StakeZapdeposit_and_stake, withdraw_and_unstakeStakeZap.vy
Hybrid VaultHybridVault, created by HybridVaultFactory.create_vault()HybridVault.vy
FeeSplitterFactory.fee_receiver(); its share for net pressure is split_fraction()FeeSplitter.vy
veYB fee revenueFeeDistributorFeeDistributor.vy
Pool policyPOLICY() on the Curve pool (functions on Pool policy)Curve Twocrypto v3.0.0 pool
Oracle-report policyYBOraclizedPolicy, returned by POLICY() on a pool that runs itPool 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_lifetimePool policy contract
Gauge votingGaugeController.vote_for_gauge_weights: votes instant, 10-day cooldown per gaugeGaugeController.vy
veYBERC-721 NFT minted by VotingEscrow.create_lockVotingEscrow.vy

Core invariants​

Assume these invariants in integration code. Do not violate them.

  1. L=2L = 2: the value is immutable for each Factory deployment. It is not a DAO-mutable parameter.
  2. Safe-debt bounds: MIN_SAFE_DEBT ≤ debt / collateral_value ≤ MAX_SAFE_DEBT. At L=2L = 2, roughly [1/16, 8.5/16] of collateral value. A trade that pushes the ratio outside the band and away from equilibrium reverts.
  3. Watermark: ideal_staked rises on stake. It does not fall on loss. It falls in proportion when shares leave the gauge.
  4. 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_withdraw has no clamp. For the formula with unit scaling, read preview_withdraw and pricePerShare in Deposit and withdraw.
  5. Aggregator band: the Factory constructor and set_agg check that the crvUSD aggregator price is inside (0.90,1.10)(0.90, 1.10), open at both ends. The Factory does not check the band at other times.

Mathematical foundation​

The LEVAMM invariant solves a quadratic for x0(po)x_0(p_o):

x0=coll_value+coll_value2−4⋅coll_value⋅LEV_RATIO⋅debt2⋅LEV_RATIOx_0 = \frac{\text{coll\_value} + \sqrt{\text{coll\_value}^2 - 4 \cdot \text{coll\_value} \cdot \text{LEV\_RATIO} \cdot \text{debt}}}{2 \cdot \text{LEV\_RATIO}}

where LEV_RATIO = L² / (2L − 1)². At L=2L = 2, 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.