Oracle Design
The oracle returns the USD value of one Curve LP token at the EMA-smoothed external market price (EMA = exponential moving average, a rolling price average that weights recent observations more than old ones, so a single flash-loan swap cannot move the feed in one block because it averages across many prior blocks). Valuation reads cannot be moved in-block. The Factory validates the crvUSD aggregator price within 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_scaleandvirtual_price.price_scaleis the pool's internal target price (slow-moving, only updated when rebalance conditions trigger).virtual_priceis 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
- : Cryptoswap's
virtual_price, the pool's internal accounting of per-LP-token growth net of slippage. - : the pool's internal target price, updated only when Cryptoswap's rebalance conditions are satisfied.
- : 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
- gives the USD value of one LP token under the balanced-pool assumption.
- Multiplying by 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 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_priceand balances in-block, butprice_scaleonly updates when the Cryptoswap internal rebalance conditions are met (fee-funded, gated). Over normal timescalesprice_scaletracks 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 (stable) and (volatile), satisfying . The pool's internal price tracks the ratio, so at balance and the total USD pool value is . Dividing by LP supply and expressing in virtual-price terms yields per LP token.
Safety band on the crvUSD aggregator
The Factory validates the crvUSD aggregator price within (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.
Related
- Compounding Leverage — the consumer of this oracle inside LEVAMM's target computation.
- Dev: CryptopoolLPOracle — contract implementing the formula on this page.