Contract Source Code:
File 1 of 1 : contracts/LTMigrator.vy
{{
"language": "Vyper",
"sources": {
"contracts/LTMigrator.vy": {
"content": "# @version 0.4.3\n\"\"\"\n@title LTMigrator\n@notice Migration zap from one version of vault to another\n@author Scientia Spectra AG\n@license Copyright (c) 2025\n\"\"\"\nfrom ethereum.ercs import IERC20\n\n\ninterface MFOwner:\n def lt_allocate_stablecoins(lt: LT, limit: uint256): nonpayable\n def lt_in_factory(lt: LT) -> bool: view\n\ninterface Cryptopool:\n def balances(i: uint256) -> uint256: view\n\ninterface Gauge:\n def deposit(assets: uint256, receiver: address) -> uint256: nonpayable\n def redeem(shares: uint256, receiver: address, owner: address) -> uint256: nonpayable\n def previewDeposit(assets: uint256) -> uint256: view\n def previewRedeem(shares: uint256) -> uint256: view\n\ninterface AMM:\n def collect_fees() -> uint256: nonpayable\n\ninterface LT:\n def deposit(assets: uint256, debt: uint256, min_shares: uint256, receiver: address) -> uint256: nonpayable\n def withdraw(shares: uint256, min_assets: uint256) -> uint256: nonpayable\n def balanceOf(user: address) -> uint256: view\n def approve(_to: address, _amount: uint256) -> bool: nonpayable\n def allowance(_from: address, _to: address) -> uint256: view\n def transferFrom(_from: address, _to: address, _amount: uint256) -> bool: nonpayable\n def ASSET_TOKEN() -> IERC20: view\n def amm() -> AMM: view\n def allocate_stablecoins(): nonpayable\n def CRYPTOPOOL() -> Cryptopool: view\n def preview_emergency_withdraw(shares: uint256) -> (uint256, int256): view\n def preview_deposit(assets: uint256, debt: uint256, raise_overflow: bool) -> uint256: view\n def preview_withdraw(tokens: uint256) -> uint256: view\n def staker() -> Gauge: view\n\n\nSTABLECOIN: public(immutable(IERC20))\nFACTORY_OWNER: public(immutable(MFOwner))\n\n\n@deploy\ndef __init__(stablecoin: IERC20, factory_owner: MFOwner):\n STABLECOIN = stablecoin\n FACTORY_OWNER = factory_owner\n\n\n@internal\n@view\ndef _preview_migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256) -> uint256:\n cpool: Cryptopool = staticcall lt_from.CRYPTOPOOL()\n cpool_stables: uint256 = staticcall cpool.balances(0)\n cpool_assets: uint256 = staticcall cpool.balances(1)\n\n eassets: uint256 = (staticcall lt_from.preview_emergency_withdraw(shares_in))[0]\n debt: uint256 = cpool_stables * eassets // cpool_assets\n\n assets: uint256 = staticcall lt_from.preview_withdraw(shares_in)\n\n return staticcall lt_to.preview_deposit(assets, debt * debt_coefficient // 10**18, False)\n\n\n@external\n@view\ndef preview_migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256:\n return self._preview_migrate_plain(lt_from, lt_to, shares_in, debt_coefficient)\n\n\n@external\n@view\ndef preview_migrate_staked(lt_from: LT, lt_to: LT, shares_in: uint256, debt_coefficient: uint256 = 10**18) -> uint256:\n gauge_from: Gauge = staticcall lt_from.staker()\n gauge_to: Gauge = staticcall lt_to.staker()\n lt_in: uint256 = staticcall gauge_from.previewRedeem(shares_in)\n lt_out: uint256 = self._preview_migrate_plain(lt_from, lt_to, lt_in, debt_coefficient)\n return staticcall gauge_to.previewDeposit(lt_out)\n\n\n@internal\ndef _migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, min_out: uint256, debt_coefficient: uint256,\n _for: address) -> uint256:\n # Check that LTs are in the factory\n assert staticcall FACTORY_OWNER.lt_in_factory(lt_from)\n assert staticcall FACTORY_OWNER.lt_in_factory(lt_to)\n\n # Prepare asset approvals (e.g. WBTC etc)\n asset: IERC20 = staticcall lt_from.ASSET_TOKEN()\n if staticcall asset.allowance(self, lt_to.address) == 0:\n extcall asset.approve(lt_to.address, max_value(uint256))\n amm: AMM = staticcall lt_from.amm()\n\n # Explicitly collect fees to cryptopool so that they don't screw our measurements\n extcall amm.collect_fees()\n\n # Withdraw from LT\n debt: uint256 = staticcall STABLECOIN.balanceOf(amm.address)\n assets: uint256 = extcall lt_from.withdraw(shares_in, 0)\n debt = (staticcall STABLECOIN.balanceOf(amm.address)) - debt\n\n # Now we freed up some stablecoins in the AMM\n extcall FACTORY_OWNER.lt_allocate_stablecoins(lt_from, 0) # Take what freed up from old allocation\n extcall lt_to.allocate_stablecoins() # Give these coins to the new AMM\n\n debt = debt * debt_coefficient // 10**18\n\n return extcall lt_to.deposit(assets, debt, min_out, _for)\n\n\n@external\ndef migrate_plain(lt_from: LT, lt_to: LT, shares_in: uint256, min_out: uint256,\n debt_coefficient: uint256 = 10**18):\n extcall lt_from.transferFrom(msg.sender, self, shares_in)\n self._migrate_plain(lt_from, lt_to, shares_in, min_out, debt_coefficient, msg.sender)\n\n\n@external\ndef migrate_staked(lt_from: LT, lt_to: LT, shares_in: uint256, min_out: uint256,\n debt_coefficient: uint256 = 10**18):\n gauge_from: Gauge = staticcall lt_from.staker()\n gauge_to: Gauge = staticcall lt_to.staker()\n\n if staticcall lt_to.allowance(self, gauge_to.address) == 0:\n extcall lt_to.approve(gauge_to.address, max_value(uint256))\n\n lt_in: uint256 = extcall gauge_from.redeem(shares_in, self, msg.sender)\n lt_out: uint256 = self._migrate_plain(lt_from, lt_to, lt_in, 0, debt_coefficient, self)\n shares_out: uint256 = extcall gauge_to.deposit(lt_out, msg.sender)\n assert shares_out >= min_out, \"not enough out\"\n",
"sha256sum": "97383bc7ee787907b5ee67821dca038cc06a6c0aa50a4f2c41d2a7f82485e4ed"
}
},
"settings": {
"outputSelection": {
"contracts/LTMigrator.vy": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
},
"search_paths": [
"."
]
},
"compiler_version": "v0.4.3+commit.bff19ea2",
"integrity": "89912c45489c2e42b0a98726bfbfdf996808472a91eacba14d5163478132932b"
}}