Skip to main content

Oracle Design

The oracle returns the USD value of one Curve LP token at an EMA-smoothed external market price, so a single flash-loan swap cannot move the feed within one block. (EMA is defined just below.) The Factory validates the crvUSD aggregator price within (0.90,1.10)(0.90, 1.10) when the aggregator is set; routine operations rely on the last-validated aggregator without re-checking.

Before you read this​

This page assumes some upstream knowledge. If any of the below is unfamiliar, follow the link before continuing. For the basics in one place, see Glossary.

  • What an oracle is. An on-chain price feed, external to any single pool, used by the protocol to value positions. See Glossary.
  • Exponential moving average (EMA). A rolling average weighted toward recent observations; a single new price barely moves a long-history EMA in one block.
  • Curve Cryptoswap's price_scale and virtual_price. price_scale is the pool's internal target price (slow-moving, only updated when rebalance conditions trigger). virtual_price is the pool's per-LP-token growth accounting, net of slippage.
  • Why flash-loan manipulation matters for AMM pricing. A flash loan can push a pool's spot price far from market for one block. A naive oracle reading spot price would be fooled; an EMA-smoothed or aggregator-based oracle is not.

LP price formula​

The LP token price, denominated in USD, is

lp_price=2⋅vp⋅price_scale⋅pagg\mathrm{lp\_price} = 2 \cdot v_p \cdot \sqrt{\mathrm{price\_scale}} \cdot p_{\text{agg}}

  • vpv_p: Cryptoswap's virtual_price, the pool's internal accounting of per-LP-token growth net of slippage.
  • price_scale\mathrm{price\_scale}: the pool's internal target price, updated only when Cryptoswap's rebalance conditions are satisfied.
  • paggp_{\text{agg}}: the normalised crvUSD/USD price read from Curve's off-pool price aggregator.

This is the formula implemented by CryptopoolLPOracle.vy.

Factor-by-factor meaning​

  • 2⋅vp⋅price_scale2 \cdot v_p \cdot \sqrt{\mathrm{price\_scale}} gives the USD value of one LP token under the balanced-pool assumption.
  • Multiplying by paggp_{\text{agg}} re-anchors to external crvUSD value. A flash-loan swap can move the pool's instantaneous price away from market, but it cannot move the external aggregator (which pulls from multiple DEX quotes via EMA). Multiplying by paggp_{\text{agg}} normalises any temporary pool-versus-market imbalance. The oracle price tracks where the market is, not where a flash-loan attacker pushed the pool for one block.
  • Price_scale is EMA-smoothed. A flash-loan swap changes virtual_price and balances in-block, but price_scale only updates when the Cryptoswap internal rebalance conditions are met (fee-funded, gated). Over normal timescales price_scale tracks market; within one block it does not. Combined with the aggregator, this gives sandwich-resistance.

Derivation of the balanced-pool form​

At balance, the pool holds equal USD value on each side. Let reserves be xx (stable) and yy (volatile), satisfying xy=kpoolxy = k_{\text{pool}}. The pool's internal price price_scale\mathrm{price\_scale} tracks the ratio, so at balance x=kpool⋅price_scalex = \sqrt{k_{\text{pool}} \cdot \mathrm{price\_scale}} and the total USD pool value is 2kpool⋅price_scale2\sqrt{k_{\text{pool}} \cdot \mathrm{price\_scale}}. Dividing by LP supply and expressing kpoolk_{\text{pool}} in virtual-price terms yields vp⋅2⋅price_scalev_p \cdot 2 \cdot \sqrt{\mathrm{price\_scale}} per LP token.

Safety band on the crvUSD aggregator​

The Factory validates the crvUSD aggregator price within (0.90,1.10)(0.90, 1.10) (open interval on both ends) when the aggregator is initialized at deploy and when governance updates it via set_agg. Outside that band, the aggregator setter reverts and the new aggregator is not adopted; the previously valid aggregator continues to feed the oracle. Routine deposit, withdraw, and exchange operations do not re-validate the band on every call — they trust whatever aggregator was last validated. The band is deliberately wider than ordinary intraday volatility and tighter than any flash-loan-class swing.

Lending oracle wrapper​

YBLendingOracle.vy wraps CryptopoolLPOracle.vy for use by LEVAMM's rebalancing invariant. It exposes price() for view-only reads and price_w() as the writing variant that advances the EMA state.