Skip to content

Commit

Permalink
Cp migrator
Browse files Browse the repository at this point in the history
Can migrate between different cp configurations and cp pools from different factories
  • Loading branch information
gasper committed Mar 23, 2022
1 parent ef1f4ed commit 80d7ad5
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 13 deletions.
58 changes: 50 additions & 8 deletions contracts/migration/TridentSushiRollCP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ import "../interfaces/ITridentRouter.sol";
import "../interfaces/IMasterDeployer.sol";
import "../interfaces/IPoolFactory.sol";
import "../interfaces/IPool.sol";
import "../interfaces/IConstantProductPool.sol";

/// @notice Liquidity migrator from UniV2 style pool to Trident Constant product pool.
contract TridentSushiRollCP is SelfPermit, Multicall {
error MinimumOutput();

IBentoBoxMinimal internal immutable bentoBox;
IPoolFactory internal immutable poolFactory;
IMasterDeployer internal immutable masterDeployer;
IBentoBoxMinimal public immutable bentoBox;
IPoolFactory public immutable poolFactoryCP;
IMasterDeployer public immutable masterDeployer;

constructor(
IBentoBoxMinimal _bentoBox,
IPoolFactory _poolFactory,
IPoolFactory _poolFactoryCP,
IMasterDeployer _masterDeployer
) {
bentoBox = _bentoBox;
poolFactory = _poolFactory;
poolFactoryCP = _poolFactoryCP;
masterDeployer = _masterDeployer;
}

Expand All @@ -38,7 +39,7 @@ contract TridentSushiRollCP is SelfPermit, Multicall {
@param minToken1Received Slippage protection for removing liquidity from a UniV2 style pool.
@param minLpReceived Slippage protection for minting liquidity on the Trident CP pool.
@dev If the pool with the current conditions doesn't exist it will be deployed. */
function migrate(
function migrateLegacyToCP(
IUniswapV2Minimal pair,
uint256 amount,
uint256 swapFee,
Expand All @@ -51,10 +52,10 @@ contract TridentSushiRollCP is SelfPermit, Multicall {
address token1 = pair.token1();

bytes memory poolData = abi.encode(token0, token1, swapFee, twapSupport);
address tridentPool = poolFactory.configAddress(keccak256(poolData));
address tridentPool = poolFactoryCP.configAddress(keccak256(poolData));

if (tridentPool == address(0)) {
tridentPool = masterDeployer.deployPool(address(poolFactory), poolData);
tridentPool = masterDeployer.deployPool(address(poolFactoryCP), poolData);
}

pair.transferFrom(msg.sender, address(pair), amount);
Expand All @@ -69,4 +70,45 @@ contract TridentSushiRollCP is SelfPermit, Multicall {

if (liquidity < minLpReceived) revert MinimumOutput();
}

/** @notice Function to migrate betewwn Trident CP pools with different fee / twap settings.
@param currentPool Trident CP pool address we want to migrate from. Can be form an outdated CP factory.
@param amount Liquidity amount (Lp token balance) to be migrated.
@param swapFee Swap fee of the Trident CP pool we are migrating into.
@param twapSupport Whether the Trident CP pool we are migrating into supports twap oracles.
@param minToken0Received Slippage protection for removing liquidity. Values are in BentoBox shares.
@param minToken1Received Slippage protection for removing liquidity. Values are in BentoBox shares.
@param minLpReceived Slippage protection for minting liquidity on the Trident CP pool.
@dev If the pool with the current conditions doesn't exist it will be deployed. */
function migrateCP(
IConstantProductPool currentPool,
uint256 amount,
uint256 swapFee,
bool twapSupport,
uint256 minToken0Received,
uint256 minToken1Received,
uint256 minLpReceived
) external returns (uint256 liquidity) {
address[] memory tokens = currentPool.getAssets();

bytes memory newPoolData = abi.encode(tokens[0], tokens[1], swapFee, twapSupport);

address newPool = poolFactoryCP.configAddress(keccak256(newPoolData));

if (newPool == address(0)) {
newPool = masterDeployer.deployPool(address(poolFactoryCP), newPoolData);
}

currentPool.transferFrom(msg.sender, address(currentPool), amount);

IPool.TokenAmount[] memory tokenAmounts = currentPool.burn(abi.encode(newPool, false));

(uint256 amount0, uint256 amount1) = (tokenAmounts[0].amount, tokenAmounts[1].amount);

if (amount0 < minToken0Received || amount1 < minToken1Received) revert MinimumOutput();

liquidity = IPool(newPool).mint(abi.encode(msg.sender));

if (liquidity < minLpReceived) revert MinimumOutput();
}
}
34 changes: 29 additions & 5 deletions test/migration/Migration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from "chai";
import { customError } from "../utilities";
import { Migrator__factory, TridentSushiRollCP, TridentSushiRollCP__factory } from "../../types";

describe("Migration", function () {
describe.only("Migration", function () {
let _owner, owner, chef, migrator, usdcWethLp, usdc, weth, masterDeployer, factory, Pool, snapshotId, ERC20;

let manualMigrator: TridentSushiRollCP;
Expand Down Expand Up @@ -109,20 +109,44 @@ describe("Migration", function () {

it("Should migrate uniswap v2 style Lp positions outside of MasterChef", async () => {
// _owner has some usdc-weth lp coins we can migrate
const balance = await usdcWethLp.balanceOf(_owner);
const balance = (await usdcWethLp.balanceOf(_owner)).div(10);
usdcWethLp.connect(owner).approve(manualMigrator.address, balance);
await expect(
manualMigrator.connect(owner).migrate(usdcWethLp.address, balance.div(2), 30, false, balance, balance, balance)
manualMigrator
.connect(owner)
.migrateLegacyToCP(usdcWethLp.address, balance.div(2), 30, false, balance, balance, balance)
).to.be.revertedWith(customError("MinimumOutput"));
await manualMigrator.connect(owner).migrate(usdcWethLp.address, balance.div(2), 30, false, 0, 0, 0);
await manualMigrator.connect(owner).migrateLegacyToCP(usdcWethLp.address, balance.div(2), 30, false, 0, 0, 0);
const poolAddy = (await factory.getPools(usdc.address, weth.address, 0, 1))[0];
const pool = await ERC20.attach(poolAddy);
const newBalance = await pool.balanceOf(_owner);
await manualMigrator.connect(owner).migrate(usdcWethLp.address, balance.div(2), 30, false, 0, 0, 0);
await manualMigrator.connect(owner).migrateLegacyToCP(usdcWethLp.address, balance.div(2), 30, false, 0, 0, 0);
expect(newBalance.gt(0)).to.be.true;
expect((await pool.balanceOf(_owner)).gt(newBalance)).to.be.true;
});

it("Should migrate from one trident CP configuration to another", async () => {
// _owner has some usdc-weth lp coins we can migrate
const balance = (await usdcWethLp.balanceOf(_owner)).div(10);
usdcWethLp.connect(owner).approve(manualMigrator.address, balance);
await manualMigrator.connect(owner).migrateLegacyToCP(usdcWethLp.address, balance, 30, false, 0, 0, 0);

const poolAddy = (await factory.getPools(usdc.address, weth.address, 0, 1))[0];
const pool = await ERC20.attach(poolAddy);
const poolBalance = await pool.balanceOf(_owner);
pool.connect(owner).approve(manualMigrator.address, poolBalance);

await manualMigrator.connect(owner).migrateCP(poolAddy, poolBalance, 30, true, 0, 0, 0);

const newPoolAddy = (await factory.getPools(usdc.address, weth.address, 1, 1))[0];
const newPool = await ERC20.attach(newPoolAddy);
const newPoolBalance = await newPool.balanceOf(_owner);

expect((await pool.balanceOf(_owner)).eq(0)).to.be.true;
expect(poolBalance.gt(newPoolBalance)).to.be.true; // balance won't be equal since the pool burns some liquidity
expect(poolBalance.lt(newPoolBalance.add(10001))).to.be.true;
});

after(async () => {
await network.provider.request({
method: "hardhat_reset",
Expand Down

0 comments on commit 80d7ad5

Please sign in to comment.