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:
0xE707c7a9dD58fb7eEa17acFF875CEF8d10eD1a9Fpoints to an olderHybridFactoryOwnerthat is no longer the Factory admin. Its allocation calls revert with"Access".0x3e6Db519752d4d1EEEd0539A5F7BCF3Aa4089b62accepts 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.
| Name | Notes |
|---|---|
STABLECOIN | crvUSD. |
FACTORY_OWNER | The 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.
| Signature | Purpose |
|---|---|
preview_migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256 | View, unstaked path. Returns the lt_to shares. |
preview_migrate_staked(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256 | View, 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
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:
- Choose the function:
- For unstaked LT shares, use
migrate_plain. - For staked gauge shares, use
migrate_staked.
- For unstaked LT shares, use
- Approve
LTMigratorfor the shares that you migrate:- For
migrate_plain, approveLTMigratorfor the LT shares oflt_from. - For
migrate_staked, approveLTMigratorfor the gauge shares of thelt_fromgauge.
- For
- Call the matching preview,
preview_migrate_plainorpreview_migrate_staked, with the samelt_from,lt_to,shares_in, anddebt_coefficient. The preview returns thelt_toshares or gauge shares that you can expect. - Set
min_outbelow the preview result by your slippage tolerance. - Call
migrate_plainormigrate_staked.
Reverts
The deployed migrator can revert with these strings. For the strings of master, read Changes in yb-core master (not deployed).
| String | Cause |
|---|---|
same LT | lt_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 deprecated | disabled_lts[lt_to] is true, or lt_to.stablecoin_allocation() <= 1. |
Slippage | migrate_plain: the shares from lt_to.deposit() are fewer than min_out. |
not enough out | migrate_staked: the gauge shares are fewer than min_out. |
Access | From HybridFactoryOwner: the migrator is not a limit setter. |
LT deviation | Only 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:
- Check that
lt_from != lt_to, that both LTs passlt_in_factory, and thatlt_tois not a deprecated market. - Call
collect_fees()on thelt_fromLEVAMM, so that fee accruals do not change the measurements. - Call
lt_from.withdraw(shares_in, 0). This step has no minimum. The only slippage check ismin_outat the end. - Calculate the debt for
lt_to:- Same Curve pool: the crvUSD that the withdrawal freed in the
lt_fromLEVAMM. - Different Curve pool:
assets · cpool_to.price_oracle() / 10**asset_decimals.
- Same Curve pool: the crvUSD that the withdrawal freed in the
- Read
previous_allocation = lt_to.stablecoin_allocation(). - Deallocate
lt_fromthroughFACTORY_OWNER: withlimit = 0iflt_fromis disabled, else withlimit = 1. A revert in this call stops the migration. - Call
FACTORY_OWNER.lt_allocate_stablecoins(lt_to, max((pool_value + additional_crvusd) · 2.2, previous_allocation)). Herepool_valueis the LEVAMM value of thelt_tomarket, andadditional_crvusdis the value of the new shares. This call gives the deposit space under theAMM.max_debt() / 2cap. - Multiply the debt by
debt_coefficient / 1e18. Calllt_to.deposit(assets, debt, min_out, receiver). - 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".
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_directionrequireslt_toto be the newest Factory market for the asset oflt_from. A migration into an older or a superseded market reverts with"lt_to superseded".lt_fromcan be any Factory market, including v1 markets. - New revert string.
"lt_from not in factory"replaces thelt_in_factory(lt_from)check that had no message. - Dropped checks. The new version does not call
lt_in_factoryforlt_to. It also drops thestablecoin_allocation() > 1check. - Deallocation of
lt_from. The new version drops thelimit = 1call for a livelt_from. Iflt_fromis disabled, the migrator trieslt_allocate_stablecoins(lt_from, 0). A failure does not stop the migration. - Order. The new version applies
debt_coefficientbefore the allocation sizing, not after. - Holder share. The value share for allocation sizing is
total / (total + admin). It returns 0 for an emptylt_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:
| String | Functions | Cause |
|---|---|---|
same LT | All four | lt_from == lt_to. |
lt_from not in factory | All four | FACTORY_OWNER.lt_in_factory(lt_from) is false. |
lt_to deprecated | All four | disabled_lts[lt_to] is true. |
lt_to superseded | All four | lt_to is not the newest Factory market for the asset of lt_from. |
Slippage | migrate_plain | The shares from lt_to.deposit() are fewer than min_out. |
not enough out | migrate_staked | The gauge shares are fewer than min_out. |
The Access, LT deviation, slippage, and donation above cap! rows of Reverts apply to master too.
Related
- LT: the vault contract that the migrator withdraws from and deposits into.
- Deposit and withdraw: how to compute a
debtthat 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.