swapExactTokensForTokens: 사용자가 정확한 양의 입력 토큰을 정확한 양의 출력 토큰으로 스왑할 수 있으며, 수수료 수취인을 선택적으로 지정할 수 있습니다.
swapTokensForExactTokens: 사용자가 거래를 이행하는 데 필요한 만큼의 입력 토큰으로 정확한 양의 출력 토큰을 교환할 수 있으며, 수수료 수취인을 선택적으로 지정할 수 있습니다.
swapExactETHForTokens: 사용자가 수수료 수취인(선택 사항)을 지정하여 정확한 양의 이더리움을 지정된 양의 출력 토큰으로 교환할 수 있습니다.
swapTokensForExactETH: 사용자가 수수료 수취인(옵션)과 함께 입력 토큰을 정확한 양의 이더리움으로 교환할 수 있도록 허용합니다.
swapExactTokensForETH: 사용자가 수수료 수취인(옵션)과 함께 정확한 양의 입력 토큰을 지정된 양의 이더리움으로 교환할 수 있습니다.
swapETHForExactTokens: 사용자가 수수료 수취인(옵션)을 지정하여 지정된 금액의 이더리움을 지정된 금액의 출력 토큰으로 스왑할 수 있도록 허용합니다.
addLiquidity: 사용자가 동일한 가치의 두 토큰을 예치하고 그 대가로 LP 토큰을 받아 유니스왑 풀에 유동성을 공급할 수 있습니다.
removeLiquidity: 사용자가 유니스왑 풀에서 유동성을 제거하고 풀에 예치된 두 토큰의 동등한 가치를 유동성 풀의 지분에 비례하여 돌려받을 수 있습니다.
removeLiquidityWithPermit: 제거유동성과 유사하지만, 사용자가 라우터가 유동성 토큰을 사용하도록 승인하기 위해 EIP-712 허가서에 서명해야 합니다.
addLiquidityETH: 추가 유동성과 유사하지만, 사용자가 유동성을 제공하기 위해 지정된 ERC20 토큰과 함께 이더를 예치할 수 있습니다.
removeLiquidityETH: removeLiquidity와 유사하지만, 사용자가 유니스왑 풀에서 지정된 ERC20 토큰과 함께 이더를 인출할 수 있습니다.
removeLiquidityETHWithPermit: 제거유동성과 유사하지만, 사용자가 유니스왑 풀에서 지정된 ERC20 토큰과 함께 이더를 출금할 수 있도록 허용합니다.
pragma solidity =0.6.6;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
import '@uniswap/lib/contracts/libraries/TransferHelper.sol';
import './interfaces/IUniswapV2Router02.sol';
import './libraries/UniswapV2Library.sol';
import './libraries/SafeMath.sol';
import './interfaces/IERC20.sol';
import './interfaces/IWETH.sol';
contract UniswapV2Router02 is IUniswapV2Router02 {
using SafeMath for uint; //UniswapV2Router02 컨트랙트를 정의합니다. IUniswapV2Router02 인터페이스를 구현하며, uint 타입에 대한 SafeMath 라이브러리를 사용합니다.
//immutable타입으로 factory와 WETH 변수로 선언
address public immutable override factory;
address public immutable override WETH;
//주어진 deadline이 현재 블록 타임스탬프 이상인지 확인하는 역할을 합니다.
//modifier ensure는 데드라인이 현재 블록 타임스탬프보다 크거나 같은지 확인합니다. 그렇지 않으면 "UniswapV2Router: EXPIRED" 오류가 발생합니다.
modifier ensure(uint deadline) {
require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
_;
}
//constructor는 스마트 컨트랙트가 빌드될 때 한번 실행된다. 생성자 constructor는 factory와 WETH 주소를 받아와서 저장합니다.
constructor(address _factory, address _WETH) public {
factory = _factory;
WETH = _WETH;
}
//receive() 함수는 fallback 함수로, ETH를 WETH 컨트랙트를 통해서만 받을 수 있도록 지정합니다.
receive() external payable {
assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
}
// **** ADD LIQUIDITY ****
//_addLiquidity() 함수는 tokenA와 tokenB 토큰의 유동성을 추가하는 데 필요한 최적의 amountA와 amountB를 계산하는 함수입니다. 함수 내부에서는 다음과 같은 일을 합니다:
function _addLiquidity(
address tokenA, //tokenA: 추가하려는 유동성을 가진 첫 번째 토큰의 주소
address tokenB, //tokenB: 추가하려는 유동성을 가진 두 번째 토큰의 주소
uint amountADesired, //amountADesired: 첫 번째 토큰을 얼마나 추가하려고 하는지를 나타내는 양
uint amountBDesired, //amountBDesired: 두 번째 토큰을 얼마나 추가하려고 하는지를 나타내는 양
uint amountAMin, //amountAMin: 첫 번째 토큰의 최소한의 양
uint amountBMin //amountBMin: 두 번째 토큰의 최소한의 양
) internal virtual returns (uint amountA, uint amountB) {
// create the pair if it doesn't exist yet
//먼저 해당 토큰 쌍의 풀이 존재하지 않는 경우(createPair 함수를 호출해서), 토큰 쌍을 생성합니다.
if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
IUniswapV2Factory(factory).createPair(tokenA, tokenB);
}
//reserveA, reserveB 변수에 UniswapV2Library.getReserves(factory, tokenA, tokenB); 함수를 호출해서 reserve에서 가져옴
(uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
//reseveA와 reserveB가 둘 다 0이면 amountA=amountADesired 이고 amountB=amountBDesired
//그 다음, 해당 토큰 쌍의 현재 reserveA와 reserveB를 가져와서 두 값이 모두 0이면, amountADesired와 amountBDesired를 그대로 사용합니다.
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
//그렇지 않으면, UniswapV2Library의 quote 함수를 호출해서 optimal한 양을 계산합니다.
uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
//만약 amountBOptimal이 amountBDesired 이하인 경우, amountBOptimal이 amountBMin 이상인지 확인한 후 amountA와 amountB에 amountADesired와 amountBOptimal을 할당합니다.
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
//
} else {
uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
assert(amountAOptimal <= amountADesired);
// amountAOptimal을 계산하고 amountAOptimal이 amountAMin 이상인지 확인한 후 amountA와 amountB에 amountAOptimal과 amountBDesired를 할당합니다.
require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
//따라서 이 함수는 적절한 양을 계산하고, 그 양을 반환합니다. 이 함수가 반환하는 값은 추가된 토큰의 양을 나타내는 amountA와 amountB입니다.
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
function addLiquidity(
address tokenA, //유동성을 추가하려는 tokenA의 주소
address tokenB, //유동성을 추가하려는 tokenB의 주소
uint amountADesired, //tokenA의 추가하려는 양
uint amountBDesired, //tokenB의 추가하려는 양
uint amountAMin, //최소한으로 받아들일 수 있는 tokenA의 양
uint amountBMin, //최소한으로 받아들일 수 있는 tokenB의 양
address to, //유동성 토큰을 발행하고 싶은 주소
uint deadline //함수가 실행되기 전까지의 시간 제한
) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
//는 먼저 내부 함수 _addLiquidity를 호출하여 풀에 추가할 각 토큰의 실제 금액을 계산합니다.
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
// 그런 다음 UniswapV2Library.pairFor 함수를 사용하여 두 토큰에 해당하는 Uniswap V2 쌍의 주소를 가져옵니다.
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
//그런 다음 이 함수는 TransferHelper.safeTransferFrom 함수를 사용하여 각 토큰의 계산된 금액을 호출자로부터 해당 쌍으로 전송합니다.
//토큰이 페어에 들어오면 이 함수는 페어 컨트랙트의 발행 함수를 호출하여 지정된 수신자에게 LP 토큰을 발행합니다.
TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
//마지막으로 이 함수는 풀에 추가된 각 토큰의 실제 금액과 발행된 LP 토큰의 양을 반환합니다.
liquidity = IUniswapV2Pair(pair).mint(to);
}
//이 기능은 사용자가 유니스왑 V2 풀에 유동성을 추가할 수 있도록 하는 유니스왑 V2 라우터02 스마트 콘트랙트에서 제공하는 또 다른 방법입니다.
// 그러나 이 기능을 사용하면 두 개의 ERC20 토큰을 제공하는 대신 이더와 단일 ERC20 토큰을 사용하여 유동성을 추가할 수 있습니다.
//이 함수는 ERC20 토큰의 주소, 풀에 추가할 ERC20 토큰의 원하는 수량, 풀에 추가해야 하는 ERC20 토큰과 이더리움의 최소 수량,
// 유동성 추가 시 발행되는 LP 토큰의 수신자 주소, 거래가 실행되어야 하는 기한 등 여러 매개변수를 입력받습니다.
function addLiquidityETH(
address token, // ERC20 토큰의 주소,
uint amountTokenDesired, //, 풀에 추가할 ERC20 토큰의 원하는 수량
uint amountTokenMin, //풀에 추가해야 하는 ERC20 토큰 최소 수량
uint amountETHMin, //풀에 추가해야 하는 이더리움의 최소 수량
address to, //유동성 추가 시 발행되는 LP 토큰의 수신자 주소,
uint deadline //거래가 실행되어야 하는 기한
//이 함수는 유료이므로 호출자는 트랜잭션의 일부로 해당 함수에 이더를 보내야 합니다. 전송된 이더리움의 양은 풀에 유동성을 추가하는 데 사용됩니다.
) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
//이 함수는 ERC20 토큰, WETH 주소(유니스왑 V2에서 사용하는 래핑된 이더리움 토큰), 원하는 ERC20 토큰 수량, 호출자가 전송한 ETH 수량으로 내부 함수 _addLiquidity를 호출합니다.
(amountToken, amountETH) = _addLiquidity(
token,
WETH,
amountTokenDesired,
msg.value,
amountTokenMin,
amountETHMin
);
//이 함수는 UniswapV2Library.pairFor 함수를 사용해 ERC20 토큰과 이더리움에 해당하는 유니스왑 V2 쌍의 주소를 가져옵니다.
address pair = UniswapV2Library.pairFor(factory, token, WETH);
//이 함수는 TransferHelper.safeTransferFrom 함수를 사용하여 호출자로부터 원하는 양의 ERC20 토큰을 쌍으로 전송합니다.
TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
//그런 다음 이 함수는 WETH 컨트랙트의 입금 함수를 호출하여 수신한 이더를 랩드 이더(WETH)로 변환합니다.
IWETH(WETH).deposit{value: amountETH}();
//그런 다음 이 함수는 어설트 함수를 사용하여 IWETH 인터페이스의 전송 함수를 사용하여 랩드 이더가 페어 컨트랙트로 전송되는지 확인합니다.
assert(IWETH(WETH).transfer(pair, amountETH));
//그런 다음 이 함수는 페어 컨트랙트의 발행 함수를 호출하여 지정된 수신자에게 LP 토큰을 발행합니다.
liquidity = IUniswapV2Pair(pair).mint(to);
// refund dust eth, if any
//유동성을 추가한 후 초과 이더리움이 남는 경우, 이 함수는 TransferHelper.safeTransferETH 함수를 사용하여 초과분을 호출자에게 다시 전송합니다.
//이는 소량의 이더가 컨트랙트에 먼지로 잠기는 것을 방지하기 위한 것입니다.
if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
//전반적으로 이 기능을 통해 사용자는 이더와 단일 ERC20 토큰을 사용해 유니스왑 V2 풀에 유동성을 공급할 수 있으며, 이는 거래에 사용하거나 기초 자산으로 상환할 수 있습니다.
}
// **** REMOVE LIQUIDITY ****
//function removeLiquidity(): 유동성을 제거하여 토큰들을 분리합니다.
function removeLiquidity(
address tokenA, //tokenA: 첫 번째 토큰의 주소
address tokenB, //tokenB: 두 번째 토큰의 주소
uint liquidity, //유동성: 제거할 유동성의 양
uint amountAMin, //amountAMin: 받을 토큰 A의 최소 금액
uint amountBMin, //amountBMin: 받을 토큰 B의 최소 금액
address to, //받는 사람: 토큰을 보낼 주소
uint deadline //기한: 트랜잭션이 포함되어야 하는 타임스탬프
) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
//이 함수는 먼저 유니스왑V2 라이브러리에서 pairFor() 함수를 사용하여 페어 컨트랙트의 주소를 가져옵니다.
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
//그런 다음 transferFrom() 함수를 사용하여 발신자의 유동성 토큰을 페어 콘트랙트로 전송합니다.
IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
//그런 다음 페어 컨트랙트의 burn() 함수를 호출하여 유동성을 제거하고 받은 토큰 A와 토큰 B의 양을 가져옵니다. 그런 다음 이 함수는 토큰을 정렬하고 받은 토큰의 양을 반환합니다.
(uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
(address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
}
//removeLiquidityETH(): 이 함수는 유니스왑 풀에서 한 쌍의 이더와 다른 토큰의 유동성을 제거하는 데 사용됩니다. 이 함수는 다음 매개변수를 받습니다:
function removeLiquidityETH(
address token, //토큰: 유동성을 제거할 토큰의 주소입니다.
uint liquidity, //유동성: 제거할 유동성의 양
uint amountTokenMin, //amountTokenMin: 받을 토큰의 최소 금액
uint amountETHMin, //amountETHMin: 받을 이더리움의 최소 금액
address to, //to: 토큰을 보낼 주소
uint deadline //deadline: 트랜잭션이 포함되어야 하는 타임스탬프
) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
//이 함수는 먼저 removeLiquidity() 함수를 호출하여 수령한 토큰과 이더리움의 금액을 가져옵니다.
//
(amountToken, amountETH) = removeLiquidity(
token,
WETH,
liquidity,
amountTokenMin,
amountETHMin,
address(this),
deadline
);
//그런 다음 safeTransfer() 함수를 사용하여 토큰을 전송하고
TransferHelper.safeTransfer(token, to, amountToken);
//withdraw() 함수를 사용하여 ETH를 WETH로 변환하고
IWETH(WETH).withdraw(amountETH);
//ETH를 수신자 주소로 전송합니다.
TransferHelper.safeTransferETH(to, amountETH);
}
//이는 온체인 트랜잭션이 아닌 서명으로 승인할 수 있는 ERC-20 표준의 특징인 허가(permit)를 통해 유동성을 제거할 수 있는 UniswapV2Router02 콘트랙트의 함수다.
function removeLiquidityWithPermit(
address tokenA, //pair에 있는 토큰A 주소
address tokenB, , //pair에 있는 토큰B 주소
uint liquidity, //제거할 유동성 수량
uint amountAMin, //토큰A의 최소 허용 수량,
uint amountBMin, // 토큰B의 최소 허용 수량,
address to, //수신자 주소
uint deadline, //마감일
bool approveMax, //최대 유동성 수량 승인 여부를 나타내는 bool 값
uint8 v, bytes32 r, bytes32 s //허가서명(v, r, s)를 입력으로 받는다.
) external virtual override returns (uint amountA, uint amountB) {
//이 함수는 먼저 페어 주소를 가져온 다음
address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
//삼항 연산자 value가 true면 uint(-1) 값 아니면 liquidity를 할당
//최대 유동성 수량 승인 여부를 나타내는 bool 값
uint value = approveMax ? uint(-1) : liquidity;
//페어 컨트랙트에서 허가 함수를 호출하여 허가 서명을 확인하고 발신자를 대신해 유동성을 사용할 수 있도록 UniswapV2Router02 컨트랙트를 승인합니다.
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
//마지막으로, 이 함수는 동일한 입력 매개변수를 사용하여 removeLiquidity 함수를 호출하고 수신한 토큰A와 토큰B의 금액을 반환합니다.
(amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
}
//이는 온체인 트랜잭션이 아닌 서명으로 승인할 수 있는 ERC-20 표준의 특징인 허가(permit)를 통해 유동성을 제거할 수 있는 UniswapV2Router02 콘트랙트의 함수다.
// 위의 함수와 달리 ERC-20 토큰과 WETH 토큰을 받는다. 수신한 ERC-20 토큰과 ETH의 금액을 반환합니다.
function removeLiquidityETHWithPermit(
address token, //pair에 있는 토큰 주소
uint liquidity, //제거할 유동성
uint amountTokenMin, //토큰의 최소 허용 수량,
uint amountETHMin, //ETH의 최소 허용 수량,
address to, //수신인
uint deadline, //마감일
bool approveMax, //최대 유동성 수량 승인 여부를 나타내는 bool 값
uint8 v, bytes32 r, bytes32 s //허가서명(v, r, s)를 입력으로 받는다.
) external virtual override returns (uint amountToken, uint amountETH) {
//이 함수는 먼저 페어 주소를 가져온 다음
address pair = UniswapV2Library.pairFor(factory, token, WETH);
//삼항 연산자 value가 true면 uint(-1) 값 아니면 liquidity를 할당
//최대 유동성 수량 승인 여부를 나타내는 bool 값
uint value = approveMax ? uint(-1) : liquidity;
//페어 컨트랙트에서 허가 함수를 호출하여 허가 서명을 확인하고 발신자를 대신해 유동성을 사용할 수 있도록 UniswapV2Router02 컨트랙트를 승인합니다.
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
//페어 컨트랙트에서 permit을 호출한 후 내부적으로 removeLiquidityETH 함수를 호출합니다.
(amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
}
// **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token, //주소 토큰,: 유동성 풀의 일부인 ERC20 토큰의 주소입니다.
uint liquidity, //uint 유동성,: 풀에서 제거할 유동성(LP) 토큰의 양입니다.
uint amountTokenMin, //사용자가 받을 것으로 예상되는 최소 토큰 수량입니다.
uint amountETHMin, //사용자가 받을 것으로 예상되는 최소 이더리움 수량입니다.
address to, // 유동성이 제거된 후 토큰과 이더리움을 받을 주소입니다.
uint deadline //트랜잭션이 실행될 기한이며, 이 기한이 지나면 트랜잭션이 처리되지 않습니다.
) public virtual override ensure(deadline) returns (uint amountETH) {
// public virtual override 컨트랙트 외부에서 함수에 접근할 수 있고 자식 컨트랙트에 의해 재정의될 수 있음을 지정하는 접근 수정자입니다.
// ensure(deadline) : 트랜잭션 실행 기한이 지나지 않았는지 확인하는 수정자입니다.
// returns (uint amountETH) {: 이 줄은 사용자에게 반환되는 이더리움의 양인 함수의 반환값을 선언합니다.
(, amountETH) = removeLiquidity(
// (, amountETH) = removeLiquidity(: 이 줄은 여러 매개 변수를 사용하여 다른 함수 removeLiquidity를 호출하며,
// 이 함수는 사용자가 받게 될 토큰 금액과 사용자가 받게 될 ETH 금액이라는 두 가지 값을 반환합니다.
token, //유동성 풀의 일부인 ERC20 토큰의 주소입니다.
WETH, //유동성 풀의 일부인 랩드 이더(WETH) 토큰의 주소입니다
liquidity, //풀에서 제거할 유동성(LP) 토큰의 양입니다.
amountTokenMin, // 사용자가 받을 것으로 예상되는 토큰의 최소 금액입니다.
amountETHMin, //사용자가 받을 것으로 예상되는 최소 이더리움 수량입니다
address(this), //유동성이 제거된 후 LP 토큰을 받을 주소입니다.
deadline //트랜잭션이 실행될 기한입니다.
);
//TransferHelper.safeTransfer(토큰, to, IERC20(토큰).balanceOf(주소(this)));: 이 줄은 (유동성이 제거된 후) 남은 ERC20 토큰을 사용자 주소로 전송합니다.
TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
//IWETH(WETH).withdraw(amountETH);: 이 줄은 WETH 토큰을 ETH로 변환합니다
IWETH(WETH).withdraw(amountETH);
//TransferHelper.safeTransferETH(to, amountETH);: 이 줄은 변환된 이더를 사용자의 주소로 전송합니다.
TransferHelper.safeTransferETH(to, amountETH);
//전반적으로 이 함수는 유니스왑 유동성 풀에서 유동성을 제거하고, WETH 토큰을 ETH로 변환한 다음, 나머지 ERC20 토큰과 변환된 ETH를 사용자 주소로 전송합니다.
}
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token, //주소 토큰,: 유동성을 제거할 ERC-20 토큰의 주소입니다.
uint liquidity, //제거할 유동성의 양입니다.
uint amountTokenMin, //유동성을 제거한 대가로 받을 최소 토큰 수량입니다.
uint amountETHMin, //제거되는 유동성의 대가로 받을 최소 이더리움 수량입니다.
address to, //토큰과 이더리움을 받을 주소입니다.
uint deadline, //트랜잭션이 실행되어야 하는 기한입니다.
bool approveMax, //최대 유동성을 승인할지 여부를 결정하는 bool 플래그입니다.
uint8 v, bytes32 r, bytes32 s // 허가 서명의 v,r,s 구성 요소입니다.
// removeLiquidityETHWithPermitSupportingFeeOnTransferTokens 함수의 구현이므로 외부, 가상, 오버라이드로 표시되어 있습니다. 이 함수는 유동성 대가로 받은 이더리움의 양을 반환합니다.
) external virtual override returns (uint amountETH) {
//주소 쌍 = UniswapV2Library.pairFor(factory, token, WETH);:
//이 줄은 공장 주소, 토큰 주소, WETH 주소와 함께 유니스왑V2라이브러리 컨트랙트의 pairFor 함수를 호출하여 유동성 풀의 주소를 결정합니다
address pair = UniswapV2Library.pairFor(factory, token, WETH);
// uint 값 = 승인 최대 ? uint(-1) : 유동성;: 이 줄은 허가에 대해 승인할 값을 설정합니다.
//approveMax가 참이면 값을 가능한 최대값(uint(-1))으로 설정하고, 그렇지 않으면 값을 유동성으로 설정합니다.
uint value = approveMax ? uint(-1) : liquidity;
//IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);:
//이 줄은 유동성 풀 콘트랙트의 허가 함수를 호출하여 라우터가 지정된 양의 유동성을 사용할 수 있도록 승인합니다.
// 허가 함수는 발신자(msg.sender), 수신자(address(this)), 승인할 값(유동성 또는 uint(-1)), 기한, 허가 서명(v, r, s) 등 여러 인수를 받습니다.
IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
//amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(토큰, 유동성, 금액토큰민, 금액ETH민, to, 마감일);:
// 이 줄은 풀에서 유동성을 제거하고 받은 이더리움의 양을 반환합니다. 지정된 인수를 사용하여 라우터 컨트랙트의
//removeLiquidityETHSupportingFeeOnTransferTokens 함수를 호출하여 유동성을 제거하고 토큰과 ETH를 수신자 주소로 전송합니다. 반환된 값은 amountETH에 할당됩니다.
amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
token, liquidity, amountTokenMin, amountETHMin, to, deadline
);
}
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
//_스왑에 대한 함수 정의입니다. 이 함수는 스왑할 금액 배열, 스왑할 토큰의 주소가 포함된 경로 배열, 스왑한 토큰을 보낼 주소인 _to의 세 가지 인수를 받습니다.
// 이 함수는 내부적이고 가상이므로 컨트랙트 내에서만 접근할 수 있으며 하위 컨트랙트에 의해 재정의될 수 있습니다.
function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
//amount은 입력 토큰부터 시작하여 거래에서 스왑할 각 토큰의 금액을 지정하는 금액 배열입니다.
//path는 입력 토큰에서 출력 토큰까지 거래의 경로를 나타내는 토큰 주소 배열입니다.
//to는 트랜잭션의 출력이 전송될 주소입니다.
for (uint i; i < path.length - 1; i++) {
//이 함수는 path 배열을 반복하며 path를 따라 연속된 각 토큰 쌍에 대해 스왑을 실행합니다.
//각 토큰 쌍에 대해 함수는 token0 및 token1 주소와 출력 토큰 amountOut의 양을 결정합니다.
//(주소 입력, 주소 출력) = (경로[i], 경로[i + 1]);: 이 줄은 디스트럭처링을 사용하여 경로[i]를 입력에, 경로[i + 1]을 출력에 할당합니다.
(address input, address output) = (path[i], path[i + 1]);
//(주소 토큰0,) = UniswapV2Library.sortTokens(입력, 출력);:
//이 줄은 UniswapV2Library의 sortTokens 함수를 사용하여 입력 토큰과 출력 토큰을 정렬하고 정렬된 순서에서 가장 먼저 오는 토큰의 주소를 반환합니다.
//이 주소는 token0에 할당됩니다.
(address token0,) = UniswapV2Library.sortTokens(input, output);
//uint amountOut = amounts[i + 1];: 이 줄은 금액 배열의 다음 값을 amountOut에 할당합니다.
uint amountOut = amounts[i + 1];
//(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));: 이 줄은 입력 토큰이 token0과 같은지 확인합니다.
//같으면 amount0Out에 0을 할당하고 amount1Out에 amountOut을 할당합니다. 그렇지 않으면, amount0Out에 amountOut이 할당되고 amount1Out에 0이 할당됩니다.
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
//address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;: 이 줄은 i가 경로의 길이에서 2를 뺀 값보다 작은지 확인합니다.
// 이보다 작으면 출력 토큰과 경로의 다음 토큰을 포함하는 쌍의 주소가 에 할당됩니다. 그렇지 않으면 _to가 to에 할당됩니다.
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
// IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(amount0Out, amount1Out, to, new bytes(0));:
// 이 줄은 입력 토큰과 출력 토큰이 포함된 유니스왑 쌍에서 스왑 함수를 호출합니다.
// amount0Out과 amount1Out 변수는 스왑할 각 토큰의 양을 결정하고, to는 스왑된 토큰을 전송할 주소이며, new bytes(0) 매개 변수는 필요한 경우 추가 데이터에 사용됩니다.
IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
amount0Out, amount1Out, to, new bytes(0)
);
}
}
//function swapExactTokensForTokens(): 입력한 토큰을 목적 토큰으로 교환합니다.
//함수 정의는 함수 이름 swapExactTokensForTokens와 입력 인수 amountIn, amountOutMin, path, to, deadline으로 시작됩니다. 이 함수는 금액이라는 부호 없는 정수의 배열을 반환합니다.
function swapExactTokensForTokens(
uint amountIn, //입력금액
uint amountOutMin, //사용자가 지정한 최소 출력량
address[] calldata path, //주소 배열 path
address to, //수신받을사람의 주소
uint deadline //거래기한
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
//이 줄은 유니스왑V2 라이브러리 콘트랙트에서 getAmountsOut 함수를 호출하여 주어진 입력 금액(amountIn)과 토큰의 경로(path)에 대한 예상 출력 금액(amountsOut)을 계산합니다.
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
//이 줄은 예상 출력량(amountsOut)이 사용자가 지정한 최소 출력량(amountOutMin)보다 크거나 같은지 확인합니다. 예상 출력량이 최소 출력량보다 작으면 함수는 오류 메시지를 던집니다.
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
//이 줄은 함수 호출자(msg.sender)로부터 입력 토큰(경로[0])을 TransferHelper 컨트랙트의 safeTransferFrom 함수를 사용하여
// 해당 UniswapV2 페어 컨트랙트(UniswapV2Library.pairFor(factory, 경로[0], 경로[1]))로 전송합니다.
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
);
//이 줄은 내부 _swap 함수를 호출하여 스왑 작업을 실행합니다. 스왑 함수는 주어진 토큰 경로(패스)에서 입력 토큰과 출력 토큰 쌍 간에 일련의 토큰 스왑을 실행하는 역할을 담당합니다.
_swap(amounts, path, to);
}
// swapTokensForExactTokens()는 입력 토큰을 지정된 양의 출력 토큰으로 교환할 수 있게 해줍니다.
function swapTokensForExactTokens(
uint amountOut, // amountOut(원하는 출력 토큰의 양)
uint amountInMax, // amountInMax(사용할 최대 입력 토큰의 양),
address[] calldata path, //path(스왑을 위해 취할 경로를 나타내는 토큰 주소 배열)
address to, //to(출력 토큰의 수신자 주소),
uint deadline //deadline(트랜잭션이 더 이상 유효하지 않은 유닉스 타임스탬프)의 입력 파라미터가 포함되어 있습니다.
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
//ensure() 수정자는 기한이 지나지 않았는지 확인합니다.
//원하는 출력 토큰을 받는 데 필요한 입력 토큰의 양을 결정하기 위해 UniswapV2Library의 getAmountsIn() 함수가 호출됩니다.
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
//require() 문은 최대 입력 금액이 초과되지 않았는지 확인합니다.
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
//TransferHelper.safeTransferFrom()이 호출되어 호출자로부터 입력 토큰을 지정된 유니스왑V2 페어 컨트랙트로 전송합니다.
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
);
//_swap() 함수는 금액, 경로, to 매개변수와 함께 호출됩니다.
_swap(amounts, path, to);
}
//function swapExactETHForTokens(): 지정된 양의 출력 토큰으로 이더를 교환할 수 있습니다.
//함수 정의에는 amountOutMin(받을 최소 출력 토큰 수량), path(스왑 경로를 나타내는 토큰 주소 배열), to(출력 토큰의 수신자 주소),
//deadline(트랜잭션이 더 이상 유효하지 않은 유닉스 타임스탬프)의 입력 파라미터가 포함되어 있습니다.
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
virtual
override
payable
ensure(deadline) //ensure() 수정자는 기한이 지나지 않았는지 확인합니다.
returns (uint[] memory amounts)
{
//require() 문은 경로 배열의 첫 번째 토큰이 WETH 토큰인지 확인합니다.
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
//지정된 이더리움 수량에 대해 수신할 출력 토큰의 양을 결정하기 위해 UniswapV2Library의 getAmountsOut() 함수가 호출됩니다.
amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
//require() 문은 최소 출력 금액이 충족되는지 확인합니다.
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
//IWETH 컨트랙트에서 deposit() 함수를 호출하여 WETH 토큰을 입금하고,
IWETH(WETH).deposit{value: amounts[0]}();
// 지정된 금액의 WETH를 지정된 UniswapV2 페어 컨트랙트로 이체합니다.
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
//스왑() 함수는 금액, 경로, to 매개변수와 함께 호출됩니다.
_swap(amounts, path, to);
}
//function swapTokensForExactETH(): 목적 이더리움을 입력한 토큰으로 교환합니다.
/*
uint amountOut : 교환하려는 이더리움의 양 (출력값)
uint amountInMax : 입력으로 허용되는 최대 ERC20 토큰의 양
address[] calldata path : 교환하려는 ERC20 토큰의 경로. 경로의 첫 번째 토큰은 입력으로 주어지고, 마지막 토큰은 이더리움입니다.
address to : 이더리움을 받을 주소
uint deadline : 거래 마감 시간
*/
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
virtual
override
ensure(deadline)
returns (uint[] memory amounts)
{
//path[path.length - 1] == WETH 값이 맞는지 확인합니다.
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
//amounts 변수에 UniswapV2Library.getAmountsIn 호출하여 교환전에 교환 비율을 계산한다.
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
//계산된 입력값이 amounts[0]에 저장되어 있습니다.
//이 입력값이 입력으로 주어진 최대 ERC20 토큰의 양 amountInMax를 초과하지 않는지 확인한 후,
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
// 계산된 입력값만큼의 ERC20 토큰을 사용자의 계정에서 해당 ERC20 토큰의 UniswapV2 풀에 전송합니다
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
);
//_swap() 함수를 호출하여 실제로 ERC20 토큰을 이더리움으로 교환하고,
_swap(amounts, path, address(this));
///WETH에서는 amounts[amounts.length - 1]만큼 인출합니다.
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
//이더리움을 amounts[amounts.length - 1] 양만큼 사용자가 지정한 주소 to로 전송합니다.
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
virtual
override
ensure(deadline)
returns (uint[] memory amounts)
{
//입력된 경로(path)의 마지막 토큰이 이더리움(WETH)인지 확인합니다. 그렇지 않으면 함수를 중지하고 'UniswapV2Router: INVALID_PATH' 에러 메시지를 반환합니다
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
//UniswapV2Library에서 getAmountsOut 함수를 호출하여 입력된 토큰(amountIn)의 교환에 필요한 출력(amounts)을 계산합니다. 이때, factory는 UniswapV2 팩토리 컨트랙트 주소입니다.
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
//amountOutMin보다 교환된 출력 토큰의 양이 큰지 확인합니다. 만약 그렇지 않다면 함수를 중지하고 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT' 에러 메시지를 반환합니다.
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
//TransferHelper를 사용하여 교환에 필요한 첫 번째 페어에 토큰을 보냅니다.
//이때 msg.sender는 토큰을 전송한 계정 주소이며, pairFor 함수는 UniswapV2 팩토리 컨트랙트에서 토큰의 페어 주소를 가져옵니다.
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
);
//내부 _swap 함수를 호출하여 토큰 교환을 수행합니다. 이때, _swap 함수는 amounts와 path를 전달하고, 마지막으로 교환을 위해 사용할 컨트랙트 주소를 지정합니다.
_swap(amounts, path, address(this));
//이더리움을 WETH 토큰으로 다시 변환합니다. 이때, IWETH는 WETH 토큰의 인터페이스이며, withdraw 함수를 호출하여 이더리움을 WETH 토큰으로 다시 변환합니다.
IWETH(WETH).withdraw(amounts[amounts.length - 1]);
//TransferHelper를 사용하여 지정된 수신자(to)에게 교환된 이더리움을 안전하게 전송합니다. 이때, amounts 배열의 마지막 항목에는 교환된 이더리움의 양이 저장되어 있습니다.
TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
}
//swapETHForExactTokens 함수는 ETH를 다른 토큰으로 교환하는 함수입니다.
//amountOut은 교환할 토큰의 수량입니다.
//path는 교환 경로입니다. 이 경로는 WETH를 경유해야 하며, 다른 토큰과 직접적으로 교환이 가능한 경로여야 합니다.
//to는 교환 결과를 받을 주소입니다.
//deadline는 거래의 유효 기간을 의미합니다. 이 기간 이전에 거래가 완료되어야 합니다.
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
virtual
override
payable
ensure(deadline)
returns (uint[] memory amounts)
{
//require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');는 path 배열의 첫 번째 토큰이 WETH인지 확인합니다.
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
//amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);는 amountOut을 교환하기 위해 필요한 WETH의 수량과 path 상의 각 토큰 수량을 계산합니다.
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
//require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');는 msg.value가 교환에 필요한 최소 WETH 수량보다 큰지 확인합니다.
require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
//IWETH(WETH).deposit{value: amounts[0]}();는 WETH를 입금합니다.
IWETH(WETH).deposit{value: amounts[0]}();
//assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));는 WETH를 path상의 다음 토큰으로 전송합니다.
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
//_swap(amounts, path, to);는 _swap 함수를 호출하여 토큰을 교환합니다.
_swap(amounts, path, to);
// refund dust eth, if any
//if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);는 교환 후 남은 WETH를 msg.sender에게 반환합니다.
if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
}
// **** SWAP (supporting fee-on-transfer tokens) ****
// requires the initial amount to have already been sent to the first pair
//_swapSupportingFeeOnTransferTokens 함수는 수수료를 부과하는 토큰에 대한 교환이 가능한 함수입니다.
//address[] memory path: 통화 교환 경로. 예를 들어, DAI를 WETH로 교환하는 경우 [DAI, WETH] 배열입니다.
//address _to: 교환 결과를 수신할 주소.
function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
//이 함수는 반복문을 통해 교환을 수행합니다. i 변수는 경로 배열을 반복하는 인덱스 역할을 합니다. 각 반복에서 다음 작업을 수행합니다.
for (uint i; i < path.length - 1; i++) {
//(address input, address output) = (path[i], path[i + 1]): 현재 입력과 출력 토큰을 가져옵니다.
(address input, address output) = (path[i], path[i + 1]);
//(address token0,) = UniswapV2Library.sortTokens(input, output): 현재 페어에서 토큰 0과 토큰 1을 가져옵니다.
(address token0,) = UniswapV2Library.sortTokens(input, output);
//IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)): 현재 입력과 출력 토큰으로 Uniswap V2 페어를 가져옵니다.
IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
uint amountInput;
uint amountOutput;
{ // scope to avoid stack too deep errors
//(uint reserve0, uint reserve1,) = pair.getReserves(): 페어의 예약량을 가져옵니다.
(uint reserve0, uint reserve1,) = pair.getReserves();
//(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0): 입력 토큰과 출력 토큰의 예약량을 가져옵니다.
(uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
//amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput): 입력 토큰의 양을 계산합니다.
amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
//amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput): 출력 토큰의 예상 수량을 계산합니다.
amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
}
//(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0)): 출력 토큰의 양을 설정합니다.
(uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
//address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) :
// _to: 다음 입력 토큰과 출력 토큰이 모두 경로 배열에 남아 있는 경우, 다음 교환 페어의 주소를 계산합니다. 그렇지 않으면 _to 변수의 값을 사용합니다.
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
//pair.swap(amount0Out, amount1Out, to, new bytes(0)): 교환을 수행합니다. 교환된 출력 토큰은 다음 교환에서 입력 토큰으로 사용됩니다.
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
}
//이 함수는 주어진 토큰을 다른 토큰으로 교환하는 기능을 구현하고, 이 때 fee-on-transfer 토큰을 지원한다.
// 입력받은 amountIn 만큼의 path[0] 토큰을 교환하여 최소한 amountOutMin 만큼의 path[path.length - 1] 토큰을 to 주소로 보내는 함수를 정의한다.
//이 때, fee-on-transfer 토큰을 지원하기 위해 _swapSupportingFeeOnTransferTokens 함수를 호출한다.
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external virtual override ensure(deadline) {
//TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn): msg.sender 가 path[0] 토큰을 amountIn 만큼 보내는 함수이다.
//이를 위해 TransferHelper.safeTransferFrom 함수를 호출하고, path[0] 와 path[1] 에 대응하는 페어 주소를 전달한다.
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
);
//path 배열에서 마지막 토큰의 계정 잔액을 to 주소의 잔액으로 설정합니다.
uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
//_swapSupportingFeeOnTransferTokens(path, to): _swapSupportingFeeOnTransferTokens 함수를 호출하여 교환을 수행한다.
_swapSupportingFeeOnTransferTokens(path, to);
//require(IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'): 교환 이후 to 주소의 path[path.length - 1] 토큰
// balanceBefore 보다 적으면 INSUFFICIENT_OUTPUT_AMOUNT 에러를 발생시킨다.
require(
IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
);
}
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin, //uint amountOutMin: 구매하려는 토큰의 최소 양입니다.
address[] calldata path, //address[] calldata path: 거래 경로를 나타내는 배열입니다. 이 경우에는 ETH와 구매하려는 토큰이 포함됩니다.
address to, //address to: 구매한 토큰을 보낼 대상 주소입니다.
uint deadline //: 거래의 유효 기간을 나타내는 타임스탬프입니다.
)
external
virtual
override
payable
ensure(deadline)
{
//require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');: 거래 경로의 첫번째 토큰이 WETH인지 확인합니다.
require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
//uint amountIn = msg.value;: 입력 값으로 전송된 ETH의 양을 저장합니다.
uint amountIn = msg.value;
//IWETH(WETH).deposit{value: amountIn}();: ETH를 WETH로 변환합니다.
IWETH(WETH).deposit{value: amountIn}();
//assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));: WETH를 UniswapV2의 해당 페어 주소로 이전합니다.
assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
//uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);: 거래 이전에 대상 주소에서 구매하려는 토큰의 잔액을 가져옵니다.
uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
//_swapSupportingFeeOnTransferTokens(path, to);: 거래를 수행하고, 수수료를 지원하는 토큰을 처리하는 내부 함수를 호출합니다.
_swapSupportingFeeOnTransferTokens(path, to);
//require(IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');:
//구매한 토큰의 양이 최소 구매 양 이상인지 확인합니다. 만약 구매한 토큰의 양이 적으면 함수는 실패합니다.
require(
IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
);
}
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn, //amountIn: 함수가 처리할 토큰 양을 나타냅니다.
uint amountOutMin, //amountOutMin: 교환 후 토큰의 최소 허용 양입니다.
address[] calldata path, //path: 교환을 위한 토큰 경로입니다. path[0]은 input token이며 path[path.length - 1]은 output token, 즉 WETH입니다.
address to, //to: 교환 결과를 받을 주소입니다.
uint deadline //deadline: 교환에 대한 기한을 나타냅니다.
)
external
virtual
override
ensure(deadline)
{
//path[path.length - 1] == WETH인지 확인합니다.
require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
//TransferHelper.safeTransferFrom() 함수를 사용하여 사용자가 보낸 토큰을 UniswapV2Library.pairFor() 함수를 사용하여 교환을 수행할 페어에 안전하게 전송합니다.
TransferHelper.safeTransferFrom(
path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
);
//_swapSupportingFeeOnTransferTokens() 함수를 호출하여 교환을 수행합니다. 이 함수는 path에서 지정된 토큰 경로를 따라 교환을 수행하며, 해당 교환에 대한 수수료를 적용합니다.
_swapSupportingFeeOnTransferTokens(path, address(this));
//교환 결과로 얻은 WETH 양을 계산합니다.
uint amountOut = IERC20(WETH).balanceOf(address(this));
//amountOut이 amountOutMin보다 큰지 확인합니다.
require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
//IWETH(WETH).withdraw() 함수를 사용하여 WETH를 이더리움으로 전환하고,
IWETH(WETH).withdraw(amountOut);
//TransferHelper.safeTransferETH() 함수를 사용하여 이더리움을 to 주소로 안전하게 전송합니다.
TransferHelper.safeTransferETH(to, amountOut);
}
// **** LIBRARY FUNCTIONS ****
//quote(): 입력 토큰 수량과 두 개의 리저브(자산의 잔액)를 사용하여 목적 토큰 수량을 계산합니다.
function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
return UniswapV2Library.quote(amountA, reserveA, reserveB);
}
//getAmountOut(): 입력 토큰 수량과 두 개의 리저브(자산의 잔액)를 사용하여 목적 토큰 수량을 계산합니다.
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
public
pure
virtual
override
returns (uint amountOut)
{
return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
}
//getAmountIn(): 목적 토큰 수량과 두 개의 리저브(자산의 잔액)를 사용하여 필요한 입력 토큰 수량을 계산합니다.
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
public
pure
virtual
override
returns (uint amountIn)
{
return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
}
//getAmountsOut(): 입력 토큰 수량과 경로 배열을 사용하여 목적 토큰 수량을 계산합니다.
//이 함수는 경로에 따라 연속적인 거래를 수행하는 경우 토큰 교환에 필요한 최종 결과 수량을 계산하는 데 사용됩니다.
function getAmountsOut(uint amountIn, address[] memory path)
public
view
virtual
override
returns (uint[] memory amounts)
{
return UniswapV2Library.getAmountsOut(factory, amountIn, path);
}
//getAmountsIn(): 목적 토큰 수량과 경로 배열을 사용하여 필요한 입력 토큰 수량을 계산합니다.
//이 함수는 경로에 따라 연속적인 거래를 수행하는 경우 필요한 최소 입력 수량을 계산하는 데 사용됩니다.
function getAmountsIn(uint amountOut, address[] memory path)
public
view
virtual
override
returns (uint[] memory amounts)
{
return UniswapV2Library.getAmountsIn(factory, amountOut, path);
}
}
UniswapV2-core - UniswapV2Pair.sol 코드 분석 (0) | 2023.03.26 |
---|---|
UniswapV2-periphery - UniswapV2Library.sol 코드 분석 (0) | 2023.03.17 |
UniswapV2-periphery UniswapV2Migrator.sol 코드 분석 (0) | 2023.03.15 |
Uniswap V2 Protocol 개요 (0) | 2023.03.09 |
Uniswap V1 백서 및 코드 (0) | 2023.03.09 |