Migrator 컨트랙트는 Uniswap V1 에 있는 자산을 Uniswap V2 로 이동시키기 위한 컨트랙트이다.
따라서 Uniswap V2 코드를 커스터마이징하여 사용할 경우에는 불필요한 컨트랙트이다.
우선 인터페이스를 보면 매우 심플하다.
pragma solidity >=0.5.0;
interface IUniswapV2Migrator {
function migrate(address token, uint amountTokenMin, uint amountETHMin, address to, uint deadline) external;
}
pragma solidity =0.6.6;
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import './interfaces/IUniswapV2Migrator.sol';
import './interfaces/V1/IUniswapV1Factory.sol';
import './interfaces/V1/IUniswapV1Exchange.sol';
import './interfaces/IUniswapV2Router02.sol';
import './interfaces/IERC20.sol';
contract UniswapV2Migrator is IUniswapV2Migrator {
IUniswapV1Factory immutable factoryV1;
IUniswapV2Router01 immutable router;
constructor(address _factoryV1, address _router) public {
factoryV1 = IUniswapV1Factory(_factoryV1);
router = IUniswapV2Router01(_router);
}
// needs to accept ETH from any v1 exchange and the router. ideally this could be enforced, as in the router,
// but it's not possible because it requires a call to the v1 factory, which takes too much gas
receive() external payable {}
Migrator 컨트랙트 코드의 첫 부분이다.
생성자에서 Uniswap V1 의 Factory 주소와 V2 의 Router 주소를 가져와 참조하고 있다.
주석을 보면 이 컨트랙트는 V1 에 있는 자산을 V2 로 이동시키기 위한 목적임을 유추할 수 있다.
백서를 보면 V1 과 V2 에는 큰 변화가 있었다. V1 에서는 토큰 교환을 위해 일종의 기축 통화로써 Eth 를 사용했다. 모든 ERC20 토큰이 Eth 와 pair 를 맺고 있었던 것이다. V2 에서는 이러한 Eth <> ERC20 토큰 swap 방식을 버리고, ERC20 <> ERC20 swap 방식만 가능하도록 변경했다.
따라서 V1 과 V2 사이에서는 호환이 안됐을 것이고, V1 에 묶여있는 사용자의 자산을 안전하고 저렴하게 V2 로 이동시키는 방법을 고민했을 것이다.
function migrate(address token, uint amountTokenMin, uint amountETHMin, address to, uint deadline)
external
override
{
IUniswapV1Exchange exchangeV1 = IUniswapV1Exchange(factoryV1.getExchange(token));
uint liquidityV1 = exchangeV1.balanceOf(msg.sender);
require(exchangeV1.transferFrom(msg.sender, address(this), liquidityV1), 'TRANSFER_FROM_FAILED');
(uint amountETHV1, uint amountTokenV1) = exchangeV1.removeLiquidity(liquidityV1, 1, 1, uint(-1));
TransferHelper.safeApprove(token, address(router), amountTokenV1);
(uint amountTokenV2, uint amountETHV2,) = router.addLiquidityETH{value: amountETHV1}(
token,
amountTokenV1,
amountTokenMin,
amountETHMin,
to,
deadline
);
if (amountTokenV1 > amountTokenV2) {
TransferHelper.safeApprove(token, address(router), 0); // be a good blockchain citizen, reset allowance to 0
TransferHelper.safeTransfer(token, msg.sender, amountTokenV1 - amountTokenV2);
} else if (amountETHV1 > amountETHV2) {
// addLiquidityETH guarantees that all of amountETHV1 or amountTokenV1 will be used, hence this else is safe
TransferHelper.safeTransferETH(msg.sender, amountETHV1 - amountETHV2);
}
}