Skip to main content

Arbitrage and MEV

Arbitrageurs close price gaps in two places in each YieldBasis market: the Curve pool and the LEVAMM, the market's leveraged automated market maker. In each place, the arbitrageur pays a fee and keeps the rest of the gap's value. Some of that value is maximal extractable value (MEV): value that can be captured by including, excluding, or reordering transactions in a block. On a pool that runs the oracle-report policy, the policy's fee rules change how the value splits between the pool and the arbitrageur.

Why arbitrage happens​

Each YieldBasis market has two parts that hold prices:

  • The Curve pool (Cryptoswap). It holds crvUSD and the asset. It concentrates liquidity around its internal price, price_scale. Its spot price follows the market only when traders trade against it.
  • The LEVAMM. It holds the Curve pool's liquidity provider (LP) tokens as collateral and owes crvUSD debt. Its target is debt equal to half of the collateral value, which is 2× leverage. It values the collateral with an oracle price that uses price_scale.

When the market price moves, two gaps open:

  1. The Curve pool price differs from the market price. Traders swap on the Curve pool to close this gap.
  2. When price_scale follows, the LEVAMM moves away from 2× leverage. Its quote then differs from the value of the LP in the Curve pool. Arbitrageurs trade against the LEVAMM to close this gap.

The Curve pool trade keeps the Curve pool near the market price. The LEVAMM trade keeps the market at 2× leverage.

LEVAMM arbitrage​

To trade against the LEVAMM, an arbitrageur calls the VirtualPool contract. The VirtualPool combines a crvUSD flash loan, a Curve pool deposit or withdrawal, and a LEVAMM swap in one transaction. A flash loan is a loan that the borrower takes and repays in the same transaction.

Asset to crvUSD​

Arbitrageurs use this path when the LEVAMM has less debt than its target. The VirtualPool does these steps:

  1. It borrows crvUSD with a flash loan.
  2. It deposits the arbitrageur's asset and the borrowed crvUSD into the Curve pool, in the pool's balance ratio, to get LP tokens.
  3. It sells the LP tokens to the LEVAMM for crvUSD.
  4. It repays the flash loan.

crvUSD to asset​

Arbitrageurs use this path when the LEVAMM has more debt than its target. The VirtualPool does these steps:

  1. It borrows crvUSD with a flash loan.
  2. It buys LP tokens from the LEVAMM with the arbitrageur's own crvUSD and the borrowed crvUSD.
  3. It withdraws from the Curve pool in balance, to get the asset and crvUSD.
  4. It repays the flash loan.

The flash-loan amount for the crvUSD-to-asset path solves a quadratic. For that equation, read Math primer Eq (3.15).

Who pays and who keeps the value​

The arbitrageur pays the LEVAMM fee and keeps the rest of the spread. Governance sets the LEVAMM fee for each market. To check it, call fee() on the market's AMM contract, which is the LEVAMM.

The LEVAMM fee, plus gas, sets the smallest spread that pays. Below that spread, the gap stays open. Above it, arbitrageurs compete on speed and priority fees.

The LEVAMM fee stays in the LEVAMM and adds to the value of the position. LT, the market's vault contract, then splits that gain between unstaked yb-LP holders and admin fees. For where admin fees go, read Fees and veYB revenue.

Curve pool arbitrage​

A Curve pool arbitrage is a direct swap on the Curve pool. It moves the pool price toward the market price. The arbitrageur pays the Curve pool swap fee. The fee stays in the Curve pool for its LP holders, and the LEVAMM holds most of that LP for yb-LP holders. To check the pool's admin fee, call admin_fee() on the pool.

A pool policy is a contract that a Curve Cryptoswap pool calls to set its swap fee, its price_scale target, or both. To see which policy a pool runs, call POLICY() on the pool or read the table in Contract addresses. For what each policy changes for an LP, read Pool policies.

Swap fee by policy type​

A pool with no policy, or one that runs YBTwocryptoPolicy, charges its native dynamic fee. The fee is between mid_fee() and out_fee() and depends on the pool balance. The fee does not depend on whether a swap moves the pool toward or away from the market, so a swap toward the market still pays at least mid_fee().

On a pool that runs the oracle-report policy, YBOraclizedPolicy, the policy sets the swap fee from a signed USD price of the asset. Pyth Pro (Pyth Lazer) signs the price. The signed price is a price report, and any trader can deliver one to the policy.

Under the oracle-report policy, only a corrective swap with a positive edge and a report younger than the relax time can pay less than the fallback fee. These terms have the following meanings:

  • A corrective swap moves the pool price toward the price report and does not pass it.
  • The edge is the gain of the swap at the reported price, before fees. The policy counts 1 crvUSD as $1.
  • The relax time is the price report age at which the discount ends. The discount fades with the square root of the report age.
  • The fallback fee is the fee of every other swap, and the highest fee of the policy.

The policy settings in this section are examples: the deployment settings of the policy. The YieldBasis DAO (YB DAO) can change them within fixed limits. To read the current values, call config() on the policy.

With the example settings and a price report of age 0, a corrective swap pays min(1.75%, 0.50% + 67.5% × (edge − 0.50%)). The lowest fee is 0.50%, the fallback fee is 1.75%, and the relax time is 300 seconds (5 minutes). For the discount conditions and the fade formula, read Swap fee in Pool policies.

Who keeps the value under the oracle-report policy​

With the native fee, an arbitrageur who closes a large gap keeps everything above the native fee. A small gap below the native fee stays open.

With the oracle-report policy and its example settings, the pool keeps more of each corrective trade:

  • The pool keeps 0.50% plus 67.5% of the edge above 0.50%.
  • The arbitrageur keeps 32.5% of the edge above 0.50%.
  • A swap with an edge of 0.50% or less earns nothing, so arbitrageurs do not make it.
  • Above an edge of about 2.35%, the fee stays at 1.75%, and the arbitrageur keeps the rest.

The lower fee also makes smaller gaps worth closing. The Curve pool price can then stay closer to the market price.

The following example uses the deployment settings and a price report of age 0. A corrective swap has an edge of 1.20%. The fee to the pool is 0.50% + 67.5% × (1.20% − 0.50%) = 0.9725%. Before gas, the arbitrageur keeps 1.20% − 0.9725% = 0.2275% of the swap value.

Now suppose that the price report is 12 seconds old, counted from its Pyth observation time. With a relax time of 300 seconds, the part of the discount that is gone is √(12 / 300) = 0.2. The fee on the same swap is then 0.9725% + (1.75% − 0.9725%) × 0.2 ≈ 1.128%, and the arbitrageur keeps about 0.072%. So arbitrageurs deliver the price report and swap in the same transaction.

Price reports and price_scale​

The report lifetime is a policy setting: the report age at which a report stops steering the price_scale target (example: 90 minutes). During the report lifetime, counted from its observation, a price report also steers that target. For how price reports steer the target, read price_scale target in Pool policies.

The pool limits each price_scale step to adjustment_step_max, and the policy limits each request to its step cap, a policy setting. The LEVAMM oracle uses price_scale. So price reports also affect when LEVAMM arbitrage opens. For the pool step rules, read Step limits on price_scale in Oracle design.

Oracle-report policy risks​

On a pool that runs the oracle-report policy, the policy counts 1 crvUSD as $1. If crvUSD trades away from $1, the policy prices swaps wrongly, and a price report steers price_scale toward the USD price. Both errors are about the size of the difference. The policy also accepts any price that the Pyth verifier accepts. A wrong signed price can lower fees and steer price_scale within the policy's limits.

The YB DAO or the Curve Emergency DAO can kill the policy. For each failure mode and who controls each setting, read What can go wrong in Pool policies.

MEV characteristics​

Each VirtualPool trade is atomic: it completes in one transaction or reverts. Size limits come from the crvUSD flash-loan capacity and the LEVAMM swap limits.

The LEVAMM values positions at its oracle price. The oracle uses virtual_price, price_scale, and the crvUSD aggregator price, and ignores the Curve pool spot price. price_scale changes at most once per block, so a sandwich attack inside one block cannot move these inputs far. For each oracle input and its limits, read Oracle design.

On a pool that runs the oracle-report policy, the earliest corrective swap in a block takes the gap. A price report that lands in its own transaction lets the next trader use it. So the trader who delivered the report can lose the gap to that next trader.