swapExactTokensForTokens
일정 수량의 토큰을 다른 토큰으로 스왑(input을 고정)
amountIn ( 토큰을 몇개를 내겠다. ex) 100)
amountOutMin ( 최소한 몇개는 받아야 되겠다. )
특정 토큰을 100개를 줄테니 그 만큼 다른 토큰으로 교환할 때
swapTikensForExactTokens
특정 수량의 토큰을 얻기 위해 스왑(output을 고정)
amountOut ( B토큰을 이 만큼 받겠다. )
amountInMax ( amountOut만큼 얻는데 드는 비용은 amountInMax보다는 적어야 한다. )
B토큰을 얻고 싶은데 A토큰이 몇개 필요하는지 모르고 B토큰을 100개를 받으려고 할 때.
path
토큰 스왑 경로
ex) [ETH, USDT, USDC]
이더리움 토큰을 통해 이더리움을 USDT로 바꾸고, USDT를 USDC로 바꿔서 받겠다는 뜻이 됨.
이런 식으로 PATH를 넘겨줌으로 최적의 경로를 통해 최대한 토큰 손실이 없이 토큰 교환을 수행할 수 있도록 함.
스왑패스는 토큰 스왑 순서를 나타내는 정보인데, 이 path를 어떻게 설정하냐에 따라서 최종적으로 수령할 수 있는 토큰의 수량이 달라진다.
AB, BC, CA 유동성 풀이 있고. 각각의 비율이 존재한다. 유저가 토큰 스왑을 진행할 때, A 10개로 C토큰을 받으려고 한다. swap path를 [A-B-C]로 설정했다면, 스왑 요청을 Router로 보내게 되는데,
일단 AB LP에서 A10개를 주고 B를 100개 받게 된다. 그다음 BC풀에 B를 100개를 주고 C를 100개 받게 되고, 이대로 유저에게 준다.
A 10개 -> B 100개 -> C 100개 의 순서대로 스왑이 이루어지는 것을 볼 수 있다.
하지만 [A-C]로 swap path를 설정하게 된다면, Router는 AC LP에 A를 10개를 주게 되고 C 80개를 받게 된다.
즉, swap path를 어떻게 설정하느냐에 따라 최종적으로 받을 수 있는 토큰의 개수가 달라진다.
(이 예시는 복잡한 계산을 제외하고 단순히 swap path에 따른 수량 변화를 보여주기 위함이다.)
function swapExactTokenToToken(uint amountIn, address[] calldata path) external { // 주는 토큰에 대한 스왑함수
// [A-B-C..] A토큰이 스왑할 토큰이기 때문에 path의 첫째 값이 inputToken이 된다.
address inputToken = path[0];
// transferFrom으로 inputToken을 받아온다.
IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn);
approveToken(inputToken, ROUTER);
// Swap
IUniswapV2Router01(ROUTER).swapExactTokensForTokens(amountIn, 0, path, msg.sender, block.timestamp + 10);
}
function swapTokenToExactToken(uint amountOut, uint amountInMax, address[] calldata path) external { // 돌려받는 토큰에 대한 스왑 함수.
uint[] memory amountsIn = IUniswapV2Router01(ROUTER).getAmountsIn(amountOut, path);
uint amountIn = amountsIn[0];
require(amountInMax >= amountIn, "exceed amountInMax");
address inputToken = path[0];
IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn);
approveToken(inputToken, ROUTER);
IUniswapV2Router01(ROUTER).swapTokensForExactTokens(amountOut, amountIn, path, msg.sender, block.timestamp + 10);
}
function swapExactKlayToToken(address[] calldata path) payable external {
require(path[0] == WKLAY, "invalid path");
require(msg.value > 0, "zero msg.value");
IUniswapV2Router01(ROUTER).swapExactETHForTokens{value: msg.value}(0, path, msg.sender, block.timestamp + 10);
}
function swapKlayToExactToken(uint amountOut, address[] calldata path) payable external {
require(path[0] == WKLAY, "invalid path");
require(msg.value > 0, "zero msg.value");
uint[] memory amountsIn = IUniswapV2Router01(ROUTER).getAmountsIn(amountOut, path);
IUniswapV2Router01(ROUTER).swapETHForExactTokens{value: amountsIn[0]}(amountOut, path, msg.sender, block.timestamp + 10);
// return remaining KLAY
if (msg.value > amountsIn[0]){
(bool success,) = (msg.sender).call{value: msg.value - amountsIn[0]}(new bytes(0));
require(success, "fail to trasfer KLAY");
}
}