forked from sushi-labs/sushiswap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSushiRoll.sol
140 lines (125 loc) Β· 5.29 KB
/
SushiRoll.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./uniswapv2/interfaces/IUniswapV2Pair.sol";
import "./uniswapv2/interfaces/IUniswapV2Router01.sol";
import "./uniswapv2/interfaces/IUniswapV2Factory.sol";
import "./uniswapv2/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 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, 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 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,
deadline
);
// Add liquidity to the new router
(uint256 pooledAmountA, uint256 pooledAmountB) = addLiquidity(tokenA, tokenB, amountA, amountB);
// 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,
uint256 deadline
) 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(uint(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
) internal returns (uint amountA, uint amountB) {
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired);
address pair = UniswapV2Library.pairFor(router.factory(), tokenA, tokenB);
IERC20(tokenA).safeTransfer(pair, amountA);
IERC20(tokenB).safeTransfer(pair, amountB);
IUniswapV2Pair(pair).mint(msg.sender);
}
function _addLiquidity(
address tokenA,
address tokenB,
uint256 amountADesired,
uint256 amountBDesired
) 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) {
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint256 amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
}