Skip to main content

LTMigrator

LTMigrator.vy moves an LT position from one market to a newer market of the same asset in one transaction. The user does not withdraw and deposit by hand. LT is the market's vault contract. The migrator supports unstaked LT shares (the yb-LP shares of LT) and staked gauge shares (the shares of the market's LiquidityGauge).

Integrators can use this page to migrate a position. Governance reviewers can use it to compare the deployed migrator with yb-core master.

Deployed version​

This page describes the deployed LTMigrator.vy source. The yb-core vote scripts name 0xd5b450fDD5aF5341436Fc37A46BBEc39CA8F92CB, built from that source, as the live migrator. To check that a migrator is active, call limit_setters(migrator) on HybridFactoryOwner.

yb-core master changes the migrator. A YB DAO vote script (scripts/voting/create_vote_migrator_direction_fix.py) defines a vote that deploys a new migrator from master and replaces the live one.

Until that vote executes, the live migrator does not have the master changes. For example, the live migrator reverts without a message in migrate_plain and migrate_staked when lt_from is not a Factory LT. Its previews do not check this. The master version reverts with "lt_from not in factory" in both. For the full list of differences, read Changes in yb-core master (not deployed).

Newer migrators replace these older ones:

  • 0xE707c7a9dD58fb7eEa17acFF875CEF8d10eD1a9F points to an older HybridFactoryOwner that is no longer the Factory admin. Its allocation calls revert with "Access".
  • 0x3e6Db519752d4d1EEEd0539A5F7BCF3Aa4089b62 accepts a migration from an LT to the same LT.

The yb-core vote scripts remove both as limit setters.

Immutables​

The migrator has these immutables.

NameNotes
STABLECOINcrvUSD.
FACTORY_OWNERThe HybridFactoryOwner that owns the Factory. To check, call FACTORY_OWNER() on the migrator. The source names its interface MFOwner, but the migrator does not use MigrationFactoryOwner. The migrator must be in HybridFactoryOwner.limit_setters, because it calls lt_allocate_stablecoins(lt_to, limit) with a limit that is not 0.

External functions​

The migrator has these external functions. The signatures are the same in the deployed version and in master. LT is an address in the ABI.

SignaturePurpose
preview_migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256View, unstaked path. Returns the lt_to shares.
preview_migrate_staked(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256View, staked path. Returns the lt_to gauge shares.
migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, min_out: uint256, debt_coefficient: uint256 = 10**18)Execute unstaked migration. Returns nothing.
migrate_staked(lt_from: LT, lt_to: LT, shares_in: uint256, min_out: uint256, debt_coefficient: uint256 = 10**18)Execute staked migration. Returns nothing.

debt_coefficient scales the crvUSD debt of the deposit into lt_to. It has 18 decimals, and the default is 1e18 (no change). For example, debt_coefficient = 999 * 10**15 (0.999) lowers a 10,000 crvUSD debt to 9,990 crvUSD. The LEVAMM (contract AMM), the market's leveraged AMM, fixes LT leverage, so debt_coefficient does not change the leverage. It changes the ratio of debt to value of the new deposit.

Migrate a position​

Set debt_coefficient when the target pool runs the oracle-report policy

If the pool of lt_to runs the oracle-report policy, set debt_coefficient as LT deviation check describes. If you do not, the migration can revert with "LT deviation".

To migrate a position, do these steps:

  1. Choose the function:
    • For unstaked LT shares, use migrate_plain.
    • For staked gauge shares, use migrate_staked.
  2. Approve LTMigrator for the shares that you migrate:
    • For migrate_plain, approve LTMigrator for the LT shares of lt_from.
    • For migrate_staked, approve LTMigrator for the gauge shares of the lt_from gauge.
  3. Call the matching preview, preview_migrate_plain or preview_migrate_staked, with the same lt_from, lt_to, shares_in, and debt_coefficient. The preview returns the lt_to shares or gauge shares that you can expect.
  4. Set min_out below the preview result by your slippage tolerance.
  5. Call migrate_plain or migrate_staked.

Reverts​

The deployed migrator can revert with these strings. For the strings of master, read Changes in yb-core master (not deployed).

StringCause
same LTlt_from == lt_to.
(no message)FACTORY_OWNER.lt_in_factory(lt_from) or FACTORY_OWNER.lt_in_factory(lt_to) is false. The previews do not do this check.
lt_to deprecateddisabled_lts[lt_to] is true, or lt_to.stablecoin_allocation() <= 1.
Slippagemigrate_plain: the shares from lt_to.deposit() are fewer than min_out.
not enough outmigrate_staked: the gauge shares are fewer than min_out.
AccessFrom HybridFactoryOwner: the migrator is not a limit setter.
LT deviationOnly when the pool of lt_to runs the oracle-report policy YBOraclizedPolicy. For the check, read LT deviation check.
slippage, donation above cap!From a Curve pool: the borrower-fee donation at the end of lt_from.withdraw() or lt_to.deposit() fails. For the donation, read LT: distribute_borrower_fees.

The deployed version does not check that lt_to is newer than lt_from, or that both LTs have the same asset.

Flow​

The deployed version runs these steps in _migrate_plain:

  1. Check that lt_from != lt_to, that both LTs pass lt_in_factory, and that lt_to is not a deprecated market.
  2. Call collect_fees() on the lt_from LEVAMM, so that fee accruals do not change the measurements.
  3. Call lt_from.withdraw(shares_in, 0). This step has no minimum. The only slippage check is min_out at the end.
  4. Calculate the debt for lt_to:
    • Same Curve pool: the crvUSD that the withdrawal freed in the lt_from LEVAMM.
    • Different Curve pool: assets · cpool_to.price_oracle() / 10**asset_decimals.
  5. Read previous_allocation = lt_to.stablecoin_allocation().
  6. Deallocate lt_from through FACTORY_OWNER: with limit = 0 if lt_from is disabled, else with limit = 1. A revert in this call stops the migration.
  7. Call FACTORY_OWNER.lt_allocate_stablecoins(lt_to, max((pool_value + additional_crvusd) · 2.2, previous_allocation)). Here pool_value is the LEVAMM value of the lt_to market, and additional_crvusd is the value of the new shares. This call gives the deposit space under the AMM.max_debt() / 2 cap.
  8. Multiply the debt by debt_coefficient / 1e18. Call lt_to.deposit(assets, debt, min_out, receiver).
  9. Call FACTORY_OWNER.lt_allocate_stablecoins(lt_to, previous_allocation).

HybridFactoryOwner applies max(limit, 0.95 · lp_price · collateral) to each request. So step 7 and step 9 cannot set the allocation below 95% of the LP-priced collateral of lt_to. Step 9 can leave the allocation above previous_allocation. In step 6, limit = 1 becomes the same 95% floor for a live lt_from.

For migrate_staked, the migrator redeems the gauge shares of lt_from first and deposits the new LT shares into the lt_to gauge after step 9.

LT deviation check​

On a pool that runs the oracle-report policy (YBOraclizedPolicy), the policy checks each LT.deposit() into the market of that pool. Step 8 calls LT.deposit(), so the check applies to a migration into such a market. To see which policy a pool runs, call POLICY() on the pool or read the table in Contract addresses.

The deposit reverts with "LT deviation" if its debt does not match the book ratio (the LEVAMM debt-to-collateral ratio) within ratio_tolerance. For the formula, the ratio_tolerance bound, and the exemptions, read Pool callback and LT deviation check.

The migrator does not size debt to the book ratio of the target LEVAMM. In step 4, it uses the freed crvUSD or assets · price_oracle. So a migration into a market whose pool runs this policy can revert with "LT deviation".

Set debt_coefficient for the target book

Before you migrate into a market whose pool runs this policy, calculate the book-proportional debt: the debt at the book ratio of the lt_to LEVAMM. For the steps, read Compute debt on a pool that runs the oracle-report policy. For a flowchart of the rule, read Choose the debt rule. Set debt_coefficient so that the step 4 debt, multiplied by debt_coefficient / 1e18, is within ratio_tolerance of that value. Calculate against the current state, because the book changes with each operation. If the debt is outside ratio_tolerance, the migration reverts.

Changes in yb-core master (not deployed)​

yb-core master changes the migrator. These changes take effect only after the YB DAO vote replaces the live migrator:

  • Direction rule. A new _check_direction requires lt_to to be the newest Factory market for the asset of lt_from. A migration into an older or a superseded market reverts with "lt_to superseded". lt_from can be any Factory market, including v1 markets.
  • New revert string. "lt_from not in factory" replaces the lt_in_factory(lt_from) check that had no message.
  • Dropped checks. The new version does not call lt_in_factory for lt_to. It also drops the stablecoin_allocation() > 1 check.
  • Deallocation of lt_from. The new version drops the limit = 1 call for a live lt_from. If lt_from is disabled, the migrator tries lt_allocate_stablecoins(lt_from, 0). A failure does not stop the migration.
  • Order. The new version applies debt_coefficient before the allocation sizing, not after.
  • Holder share. The value share for allocation sizing is total / (total + admin). It returns 0 for an empty lt_to.

In master, all four external functions run _check_direction, so the previews revert with the same direction-check strings as the migrations. Master reverts with these strings:

StringFunctionsCause
same LTAll fourlt_from == lt_to.
lt_from not in factoryAll fourFACTORY_OWNER.lt_in_factory(lt_from) is false.
lt_to deprecatedAll fourdisabled_lts[lt_to] is true.
lt_to supersededAll fourlt_to is not the newest Factory market for the asset of lt_from.
Slippagemigrate_plainThe shares from lt_to.deposit() are fewer than min_out.
not enough outmigrate_stakedThe gauge shares are fewer than min_out.

The Access, LT deviation, slippage, and donation above cap! rows of Reverts apply to master too.

  • LT: the vault contract that the migrator withdraws from and deposits into.
  • Deposit and withdraw: how to compute a debt that passes the LT deviation check.
  • Pool policy: the full LT deviation rule behind LT deviation.
  • Factory: the market list that the master direction rule scans.
  • HybridFactoryOwner: the limit-setter role and the 95% floor.