// SPDX-License-Identifier: MIT pragma solidity 0.6.12; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol'; import './interfaces/IUniswapV2Pair.sol'; import './interfaces/IUniswapV2Router01.sol'; import './interfaces/IUniswapV2Factory.sol'; import './libraries/UniswapV2Library.sol'; // SushiRoll helps your migrate your existing Uniswap LP tokens to SushiSwap LP ones contract SushiRoll { using SafeERC20 for IERC20; IUniswapV2Router01 public oldRouter; IUniswapV2Router01 public router; constructor(IUniswapV2Router01 _oldRouter, IUniswapV2Router01 _router) public { oldRouter = _oldRouter; router = _router; } function migrateWithPermit( address tokenA, address tokenB, uint256 liquidity, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public { IUniswapV2Pair pair = IUniswapV2Pair(pairForOldRouter(tokenA, tokenB)); pair.permit(msg.sender, address(this), liquidity, deadline, v, r, s); migrate(tokenA, tokenB, liquidity, amountADesired, amountBDesired, amountAMin, amountBMin, deadline); } // msg.sender should have approved 'liquidity' amount of LP token of 'tokenA' and 'tokenB' function migrate( address tokenA, address tokenB, uint256 liquidity, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, uint256 deadline ) public { require(deadline >= block.timestamp, 'SushiSwap: EXPIRED'); // Remove liquidity from the old router with permit (uint256 amountA, uint256 amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin); // Add liquidity to the new router (uint256 pooledAmountA, uint256 pooledAmountB) = _addLiquidity( tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin ); // Send remaining tokens to msg.sender if (amountA > pooledAmountA) { IERC20(tokenA).safeTransfer(msg.sender, amountA - pooledAmountA); } if (amountB > pooledAmountB) { IERC20(tokenB).safeTransfer(msg.sender, amountB - pooledAmountB); } } function removeLiquidity( address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin ) internal returns (uint256 amountA, uint256 amountB) { IUniswapV2Pair pair = IUniswapV2Pair(pairForOldRouter(tokenA, tokenB)); pair.transferFrom(msg.sender, address(pair), liquidity); (uint256 amount0, uint256 amount1) = pair.burn(address(this)); (address token0, ) = UniswapV2Library.sortTokens(tokenA, tokenB); (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0); require(amountA >= amountAMin, 'SushiRoll: INSUFFICIENT_A_AMOUNT'); require(amountB >= amountBMin, 'SushiRoll: INSUFFICIENT_B_AMOUNT'); } // calculates the CREATE2 address for a pair without making any external calls function pairForOldRouter(address tokenA, address tokenB) internal view returns (address pair) { (address token0, address token1) = UniswapV2Library.sortTokens(tokenA, tokenB); pair = address( uint256( keccak256( abi.encodePacked( hex'ff', oldRouter.factory(), keccak256(abi.encodePacked(token0, token1)), hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash ) ) ) ); } function addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline ) internal returns ( uint256 amountA, uint256 amountB, uint256 liquidity ) { require(deadline >= block.timestamp, 'SushiSwap: EXPIRED'); (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = UniswapV2Library.pairFor(router.factory(), tokenA, tokenB); IERC20(tokenA).safeTransferFrom(msg.sender, pair, amountA); IERC20(tokenB).safeTransferFrom(msg.sender, pair, amountB); liquidity = IUniswapV2Pair(pair).mint(to); } function _addLiquidity( address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin ) internal returns (uint256 amountA, uint256 amountB) { // create the pair if it doesn't exist yet IUniswapV2Factory factory = IUniswapV2Factory(router.factory()); if (factory.getPair(tokenA, tokenB) == address(0)) { factory.createPair(tokenA, tokenB); } (uint256 reserveA, uint256 reserveB) = UniswapV2Library.getReserves(address(factory), tokenA, tokenB); if (reserveA == 0 && reserveB == 0) { (amountA, amountB) = (amountADesired, amountBDesired); } else { uint256 amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); if (amountBOptimal <= amountBDesired) { require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT'); (amountA, amountB) = (amountADesired, amountBOptimal); } else { uint256 amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA); assert(amountAOptimal <= amountADesired); require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT'); (amountA, amountB) = (amountAOptimal, amountBDesired); } } } }