반응형
1. UniSwap Github
- https://github.com/Uniswap
- V2-core : LP pair, factory 등 핵심적인 기능의 컨트랙트
- v2-periphery : 핵심기능을 편리하게 사용할 수 있도록 도와주는 컨트랙트
- flashSwap
- priceOracle
2. UniSwap 구조
- Router : 유저와 인터렉션(=상호작용) 하는 컨트랙트
- Pair : 유동성을 관리하는 LP Pair 컨트랙트
- Factory : Pair 컨트랙트를 생성하는 컨트랙트
- FeeTo : Pair에서 발생하는 토큰 스왑에 대한 수수료가 모이는 컨트랙트
⇒ 유저가 만약 토큰 스왑을 요청하고 싶다면 라우터 컨트랙트에 요청 → 라우터는 스왑 경로를 통해 해당하는 페어에 따라 토큰을 교환
⇒ 유저가 유동성 공급을 요청했을 때는 기존에 존재하는 유동성 풀이 아닌 새로운 유동성 풀을 생성하는 경우 라우터가 팩토리를 통해 엘피를 생성
3. V2 Core
1) UniSwapV2Pair.sol
- Liquidity
- 두 토큰 쌍에 대한 유동성 관리
- LP토큰을 통해 유동성 풀의 지분 증명
- Swap
- 유동성에 따라 토큰 교환
- 스왑 수수료 수취
- FlashSwap
- 유동성 풀에서 필요한 만큼 토큰을 먼저 가져온 뒤, 한 트랜잭션 내에서 다시 상환 가능
- Price Oracle
- 시간 가중 평균 가격(TWAP) 계산을 위한 수치 제공
- 시간 가중 평균 가격(Time-Weighted Average Price)
2) UniSwapV2Factory.sol
- createPair
- 두 토큰 쌍에 대한 LP 컨트랙트 생성
- low level call을 통한 컨트랙트 배포
- allPair / getPair
- 두 토큰 쌍을 통해 pair 컨트랙트 주소 조회
- 전체 pair 컨트랙트 목록 관리
- feeTo / feeToSetter
- 유동성 공급자들이 swap 수수료를 받을 주소 설정
4. V2 Periphery
1) UniSwapV2Router01.sol
- Utility : 토큰 교환 및 유동성 공급 등을 쉽게 할 수 있게 도와줌
- add / remove Liquidity : 유동성 공급 및 해제
- addLiquidity
function addLiquidity( address tokenA, // 유동성을 공급하려는 Pair Token A address tokenB, // 유동성을 공급하려는 Pair Token B uint amountADesired, // 공급하고자 하는 A 토큰 수량 uint amountBDesired, // 공급하고자 하는 B 토큰 수량 uint amountAMin, // A 토큰에 대한 최소 공급 수량 uint amountBMin, // B 토큰에 대한 최소 공급 수량 address to, // 유동성 공급자의 주소(=LP토큰을 수령할 주소) uint deadline // 실행 기한, 트랜잭션 실행시 최소한 설정값 이전에 반드시 실행해야 함 ) external override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IUniswapV2Pair(pair).mint(to); }
- removeLiquidity
function removeLiquidity( address tokenA, // 유동성을 해제 하려는 Pair Token A address tokenB, // 유동성을 해제 하려는 Pair Token B uint liquidity, // 해제하려는 Pool에 대한 LP 토큰 수량 uint amountAMin, // A 토큰 최소 수령 수량 uint amountBMin, // B 토큰 최소 수령 수량 address to, uint deadline ) public override ensure(deadline) returns (uint amountA, uint amountB) { address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (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'); }
- swapToken : 주어진 스왑 경로에 따라 토큰 스왑
function swapTokensForExactTokens( // 특정 수량의 토큰을 얻기 위해 스왑할 때 uint amountOut, // output을 고정 => 받을 토큰의 양 설정 가능 uint amountInMax, // 최대 이만큼까지는 던질 수 있다 address[] calldata path, address to, // 사용자의 주소 uint deadline ) external override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]); _swap(amounts, path, to);}
- function swapExactTokensForTokens( // 일정 수량의 토큰을 다른 토큰으로 스왑할 때 uint amountIn, // input을 고정 => 던지는 토큰의 양 설정 가능 uint amountOutMin, // 최소한 이만큼은 받고 싶다 address[] calldata path, // 토큰 스왑 경로 address to, // 사용자의 주소 uint deadline ) external override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]); _swap(amounts, path, to);} /* address[] calldata path, // 토큰 스왑 경로 => [ETH, USDT, USDC] 배열에 이렇게 존재한다면, `path[0] -> path[1] ETH에서 USDT로 바꾸겠다` 대충 뭐 이런 뜻? 경로를 지정해줌으로써 토큰 손실 없이 최적의 스왑이 가능해짐? <- 이건 코드 봐야 할 듯*/
- getAmounts : 토큰 스왑 시 예상 수량 계산
// 유동성 풀의 reserve를 알고 있을 때 스왑에 필요한 토큰의 수량 계산 function getAmountIn(uint amountOut/*몇 개나 받을 거냐*/, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) { return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut);}
// 토큰 수량과 스왑 경로를 알고 있을 때 각 경로별 토큰 수량 계산 function getAmountsOut(uint amountIn, address[] memory path) public view override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsOut(factory, amountIn, path);} function getAmountsIn(uint amountOut, address[] memory path) public view override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsIn(factory, amountOut, path);}
- // 유동성 풀의 reseve를 알고 있을 때 수령할 수 있는 토큰의 예상 수량 계산 function getAmountOut(uint amountIn/*몇 개나 던질 거냐*/, uint reserveIn, uint reserveOut) public pure override returns (uint amountOut) { return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);}
4. PancakeSwap의 MasterChef.sol
- 거버넌스 토큰을 분배하기 위해 필요한 컨트랙트이다.
- 구조
- 유저들이 유동성 공급 후 받은 LP 토큰을 MC에 예치를 하게 되면 MC는 유저가 예치한 LP 토큰의 수량과 예치 기간 등을 종합적으로 계산하여 거버넌스 토큰인 CAKE 토큰을 유저에게 던져준다.
- MC는 각각의 LP별로 거버넌스 토큰을 얼마나 분배해주는지, LP토큰에 대한 정보 등을 Pool Info라는 공간에 담는다.
5. 컨트랙트 만들기
1) 유동성 공급 및 해제 ⇒ Trader.sol
- TRADER - 유동성 공급 메서드
function addLiquidityKlay(address token, uint amountDesired) payable external {
// native token으로 pair된 애들이 들어오는 곳(A, B로 나누어져있지 않음)
// 이 함수가 실행되는 시점에서는 이미 네이티브 토큰을 받은 상태! 그러므로 페어로 맺은 token만 amountDesired만큼 가져오면 된다
IERC20(token).transferFrom(msg.sender, address(this), amountDesired);
approveToken(token, ROUTER);
IUniswapV2Router01(ROUTER).addLiquidityETH{value:msg.value}(token, amountDesired, 0, 0, msg.sender, block.timestamp+10);
}
function addLiquidity(address tokenA, address tokenB, uint amountA, uint amountB) external {
// 토큰의 이동경로 : 유동성 공급자 -> 현재 이 컨트랙트의 CA(TRADER) -> 실제 풀이 생성되어 있는 곳으로 연결시킬 수 있는 컨트랙트의 CA(DEX-ROUTER) -> 실제 풀이 있는 CA(DEX-PAIR)
// 유저에게서 유동성 토큰을 받기 위해서는 transferFrom 메서드를 사용해 함!
IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);
// 현재 이 컨트랙트(TRADER)에서 ROUTER CA(DEX)로 금액을 옮겨야 하기 때문에 approve가 필요하다
// IERC20(tokenA).approve(ROUTER, amountA), IERC20(tokenA).approve(ROUTER, amountB)를 사용해도 되고 approveToken 함수를 사용해도 된다!
approveToken(tokenA, ROUTER);
approveToken(tokenB, ROUTER);
// 이 함수가 발동되면 실제로 공급이 되어야 하기 때문에 이어서 Router에서 addLiquidity를 실행시켜야 함?
IUniswapV2Router01(ROUTER).addLiquidity(tokenA, tokenB, amountA, amountB, 0, 0, msg.sender, block.timestamp + 10 );
}
- TRADER - 유동성 해제 메서드
function removeLiquidity(address tokenA, address tokenB, uint liquidity) external {
// 유동성을 해제하고자 하는 풀에 있는 두 토큰의 주소를 받고,
// 그 Pool의 LP토큰 수량을 인자값으로 받아(liquidity) 두 토큰으로 구성되어 있는 풀에서 LP토큰 수량만큼 해제시킴으로써 돌려받게 됨
// 가장 먼저 엘피 토큰을 가져와서 유동성을 해제시킨다 => transferFrom으로 엘피토큰을 가져옴
// 엘피토큰의 주소는 받은 두 토큰의 주소라는 인자값을 통해 뽑아야 함 => lp토큰은 factory에서 관장함, 배포 되어 있는 애 가져오면 됨!(위에서 가져옴)
address lp = IUniswapV2Factory(FACTORY).getPair(tokenA, tokenB);
IERC20(lp).transferFrom(msg.sender, address(this), liquidity);
// 가져온 엘피토큰을 라우터에 보내기 전에, 라우터가 사용할 수 있도록 approve
approveToken(lp, ROUTER);
// 라우터에서 remove
IUniswapV2Router01(ROUTER).removeLiquidity(tokenA, tokenB, liquidity, 0, 0, msg.sender, block.timestamp + 10);
}
function removeLiquidityKlay(address token, uint liquidity) external {
// 이미 유동성 풀에 두 토큰 하나가 자체토큰으로 고정되어 있기 때문에 페어된 다른 토큰의 address와 해지할 엘피토큰의 수량만 가져오면 됨
address lp = IUniswapV2Factory(FACTORY).getPair(token, WKLAY);
// 여기서 WKLAY는 클레이토큰(자체토큰)을 민팅한 컨트랙트의 CA인가?
IERC20(lp).transferFrom(msg.sender, address(this), liquidity);
approveToken(lp, ROUTER);
IUniswapV2Router01(ROUTER).removeLiquidityETH(token, liquidity, 0, 0, msg.sender, block.timestamp + 10);
}
2) 토큰 교환 ⇒ Router.sol
-
- 토큰 스왑 시 스왑되는 순서
- 패스 설정(유동성)에 따라 받을 수 있는 토큰 수량의 차이가 발생하게 된다.Swap 요청을 Router에게 패스 → A-B 스왑 진행(A 10개를 주고 B 100개를 받아옴) → B-C 스왑 진행(B 100개를 주고 C 100개를 받음) → Router는 받은 C 100개를 user에게 건네줌
- 최종적으로는 A 10 → C 100 으로 교환 완료됨!
- ⇒ A토큰을 가지고 C토큰을 교환받으려고 할 때 설정한 패스는 A-B-C라고 가정했을 시Swap path
- ⇒ 만약 A-C 다이렉트로 진행하게 된다면 A 10 → C 80으로 교환됨
- TRADER - 던지는 토큰의 수량 지정 시 메서드(= 얼마를 팔고싶다, 보내는 값 설정)
function swapExactTokenToToken(uint amountIn, address[] calldata path) external {
// 얼만큼 던질 건지, 어떤 순서대로 토큰을 바꿀 건지를 인자값으로 받는다
// 먼저 유저로부터 토큰을 받아야 하기 때문에 transferFrom 사용해 inputToken을 유저의 계정으로부터 가져온다.
// 여기서 inputToken은 던지는 토큰이기 때문에, path[]의 첫번째 index 값!
address inputToken = path[0];
IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn);
// router가 처리해줄 수 있도록 허락하기 위해 aprrove 해주기!
approveToken(inputToken, ROUTER);
// router에서 토큰을 교환하는 메서드를 실행!
IUniswapV2Router01(ROUTER).swapExactTokensForTokens(amountIn, 0, path, msg.sender, block.timestamp + 10);
// (얼마나 교환할 건지, 최소 얼만큼을 받을 건지, 어떤 토큰->어떤 토큰으로 바꿀 건지, 누구의 계좌로 바뀐 토큰이 들어갈 건지, 데드라인이 언제까진지)
}
- TRADER - 받을 토큰의 수량 지정 시 메서드(= 얼마를 얻고싶다, 받는 값 설정)
function swapTokenToExactToken(uint amountOut, uint amountInMax, address[] calldata path) external {
// 얼만큼 받고 싶은지, 최대 얼만큼 쓸 수 있는지, 어떤 순서대로 토큰을 바꿀 건지
// amountInMax는 슬리피지를 반영한 값이 들어가는 건가?
// 얼만큼의 토큰을 사용해야 하는지 계산
uint[] memory amountsIn = IUniswapV2Router01(ROUTER).getAmountsIn(amountOut, path);
uint amountIn = amountsIn[0]; // 근데 이 값이 amountInMax값을 넘으면 안되니까 require로 한번 걸러주기!
require(amountIn <= amountInMax, "exceed amountInMax");
address inputToken = path[0];
// amountInMax만큼 우선 빼온 다음에 바꿔주고 나서 남은 금액 되돌려줘도 됨
// IERC20(inputToken).transferFrom(msg.sender, address(this), amountInMax);
// 혹은 amountInMax 범위 안에서 스왑에 필요한 토큰 수량만 사용할 수도 있음! => 이런 경우 먼저 연산을 해준다. getAmountsIn 사용
IERC20(inputToken).transferFrom(msg.sender, address(this), amountIn);
// approve + 라우터에서 교환
approveToken(inputToken, ROUTER);
IUniswapV2Router01(ROUTER).swapTokensForExactTokens(amountOut, amountIn, path, msg.sender, block.timestamp + 10);
}
-
- 시간 가중 평균 가격(TWAP) 계산을 위한 수치 제공
- 시간 가중 평균 가격(Time-Weighted Average Price)
2) UniSwapV2Factory.sol
- createPair
- 두 토큰 쌍에 대한 LP 컨트랙트 생성
- low level call을 통한 컨트랙트 배포
- allPair / getPair
- 두 토큰 쌍을 통해 pair 컨트랙트 주소 조회
- 전체 pair 컨트랙트 목록 관리
- feeTo / feeToSetter
- 유동성 공급자들이 swap 수수료를 받을 주소 설정
4. V2 Periphery
1) UniSwapV2Router01.sol
- Utility : 토큰 교환 및 유동성 공급 등을 쉽게 할 수 있게 도와줌
- add / remove Liquidity : 유동성 공급 및 해제
- addLiquidity
function addLiquidity( address tokenA, // 유동성을 공급하려는 Pair Token A address tokenB, // 유동성을 공급하려는 Pair Token B uint amountADesired, // 공급하고자 하는 A 토큰 수량 uint amountBDesired, // 공급하고자 하는 B 토큰 수량 uint amountAMin, // A 토큰에 대한 최소 공급 수량 uint amountBMin, // B 토큰에 대한 최소 공급 수량 address to, // 유동성 공급자의 주소(=LP토큰을 수령할 주소) uint deadline // 실행 기한, 트랜잭션 실행시 최소한 설정값 이전에 반드시 실행해야 함 ) external override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) { (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin); address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA); TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB); liquidity = IUniswapV2Pair(pair).mint(to); }
- removeLiquidity
function removeLiquidity( address tokenA, // 유동성을 해제 하려는 Pair Token A address tokenB, // 유동성을 해제 하려는 Pair Token B uint liquidity, // 해제하려는 Pool에 대한 LP 토큰 수량 uint amountAMin, // A 토큰 최소 수령 수량 uint amountBMin, // B 토큰 최소 수령 수량 address to, uint deadline ) public override ensure(deadline) returns (uint amountA, uint amountB) { address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB); IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair (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'); }
- swapToken : 주어진 스왑 경로에 따라 토큰 스왑
function swapTokensForExactTokens( // 특정 수량의 토큰을 얻기 위해 스왑할 때 uint amountOut, // output을 고정 => 받을 토큰의 양 설정 가능 uint amountInMax, // 최대 이만큼까지는 던질 수 있다 address[] calldata path, address to, // 사용자의 주소 uint deadline ) external override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path); require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT'); TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]); _swap(amounts, path, to);}
- function swapExactTokensForTokens( // 일정 수량의 토큰을 다른 토큰으로 스왑할 때 uint amountIn, // input을 고정 => 던지는 토큰의 양 설정 가능 uint amountOutMin, // 최소한 이만큼은 받고 싶다 address[] calldata path, // 토큰 스왑 경로 address to, // 사용자의 주소 uint deadline ) external override ensure(deadline) returns (uint[] memory amounts) { amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path); require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'); TransferHelper.safeTransferFrom(path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]); _swap(amounts, path, to);} /* address[] calldata path, // 토큰 스왑 경로 => [ETH, USDT, USDC] 배열에 이렇게 존재한다면, `path[0] -> path[1] ETH에서 USDT로 바꾸겠다` 대충 뭐 이런 뜻? 경로를 지정해줌으로써 토큰 손실 없이 최적의 스왑이 가능해짐? <- 이건 코드 봐야 할 듯*/
- getAmounts : 토큰 스왑 시 예상 수량 계산
// 유동성 풀의 reserve를 알고 있을 때 스왑에 필요한 토큰의 수량 계산 function getAmountIn(uint amountOut/*몇 개나 받을 거냐*/, uint reserveIn, uint reserveOut) public pure override returns (uint amountIn) { return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut);}
// 토큰 수량과 스왑 경로를 알고 있을 때 각 경로별 토큰 수량 계산 function getAmountsOut(uint amountIn, address[] memory path) public view override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsOut(factory, amountIn, path);} function getAmountsIn(uint amountOut, address[] memory path) public view override returns (uint[] memory amounts) { return UniswapV2Library.getAmountsIn(factory, amountOut, path);}
- // 유동성 풀의 reseve를 알고 있을 때 수령할 수 있는 토큰의 예상 수량 계산 function getAmountOut(uint amountIn/*몇 개나 던질 거냐*/, uint reserveIn, uint reserveOut) public pure override returns (uint amountOut) { return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);}
4. PancakeSwap의 MasterChef.sol
- 거버넌스 토큰을 분배하기 위해 필요한 컨트랙트이다.
- 구조
- 유저들이 유동성 공급 후 받은 LP 토큰을 MC에 예치를 하게 되면 MC는 유저가 예치한 LP 토큰의 수량과 예치 기간 등을 종합적으로 계산하여 거버넌스 토큰인 CAKE 토큰을 유저에게 던져준다.
- MC는 각각의 LP별로 거버넌스 토큰을 얼마나 분배해주는지, LP토큰에 대한 정보 등을 Pool Info라는 공간에 담는다.
5. 컨트랙트 만들기
1) 유동성 공급 및 해제 ⇒ Trader.sol
- TRADER - 유동성 공급 메서드
function addLiquidityKlay(address token, uint amountDesired) payable external {
// native token으로 pair된 애들이 들어오는 곳(A, B로 나누어져있지 않음)
// 이 함수가 실행되는 시점에서는 이미 네이티브 토큰을 받은 상태! 그러므로 페어로 맺은 token만 amountDesired만큼 가져오면 된다
IERC20(token).transferFrom(msg.sender, address(this), amountDesired);
approveToken(token, ROUTER);
IUniswapV2Router01(ROUTER).addLiquidityETH{value:msg.value}(token, amountDesired, 0, 0, msg.sender, block.timestamp+10);
}
function addLiquidity(address tokenA, address tokenB, uint amountA, uint amountB) external {
// 토큰의 이동경로 : 유동성 공급자 -> 현재 이 컨트랙트의 CA(TRADER) -> 실제 풀이 생성되어 있는 곳으로 연결시킬 수 있는 컨트랙트의 CA(DEX-ROUTER) -> 실제 풀이 있는 CA(DEX-PAIR)
// 유저에게서 유동성 토큰을 받기 위해서는 transferFrom 메서드를 사용해 함!
IERC20(tokenA).transferFrom(msg.sender, address(this), amountA);
IERC20(tokenB).transferFrom(msg.sender, address(this), amountB);
// 현재 이 컨트랙트(TRADER)에서 ROUTER CA(DEX)로 금액을 옮겨야 하기 때문에 approve가 필요하다
// IERC20(tokenA).approve(ROUTER, amountA), IERC20(tokenA).approve(ROUTER, amountB)를 사용해도 되고 approveToken 함수를 사용해도 된다!
approveToken(tokenA, ROUTER);
approveToken(tokenB, ROUTER);
// 이 함수가 발동되면 실제로 공급이 되어야 하기 때문에 이어서 Router에서 addLiquidity를 실행시켜야 함?
IUniswapV2Router01(ROUTER).addLiquidity(tokenA, tokenB, amountA, amountB, 0, 0, msg.sender, block.timestamp + 10 );
}
- TRADER - 유동성 해제 메서드
function removeLiquidity(address tokenA, address tokenB, uint liquidity) external {
// 유동성을 해제하고자 하는 풀에 있는 두 토큰의 주소를 받고,
// 그 Pool의 LP토큰 수량을 인자값으로 받아(liquidity) 두 토큰으로 구성되어 있는 풀에서 LP토큰 수량만큼 해제시킴으로써 돌려받게 됨
// 가장 먼저 엘피 토큰을 가져와서 유동성을 해제시킨다 => transferFrom으로 엘피토큰을 가져옴
// 엘피토큰의 주소는 받은 두 토큰의 주소라는 인자값을 통해 뽑아야 함 => lp토큰은 factory에서 관장함, 배포 되어 있는 애 가져오면 됨!(위에서 가져옴)
address lp = IUniswapV2Factory(FACTORY).getPair(tokenA, tokenB);
IERC20(lp).transferFrom(msg.sender, address(this), liquidity);
// 가져온 엘피토큰을 라우터에 보내기 전에, 라우터가 사용할 수 있도록 approve
approveToken(lp, ROUTER);
// 라우터에서 remove
IUniswapV2Router01(ROUTER).removeLiquidity(tokenA, tokenB, liquidity, 0, 0, msg.sender, block.timestamp + 10);
}
function removeLiquidityKlay(address token, uint liquidity) external {
// 이미 유동성 풀에 두 토큰 하나가 자체토큰으로 고정되어 있기 때문에 페어된 다른 토큰의 address와 해지할 엘피토큰의 수량만 가져오면 됨
address lp = IUniswapV2Factory(FACTORY).getPair(token, WKLAY);
// 여기서 WKLAY는 클레이토큰(자체토큰)을 민팅한 컨트랙트의 CA인가?
IERC20(lp).transferFrom(msg.sender, address(this), liquidity);
approveToken(lp, ROUTER);
IUniswapV2Router01(ROUTER).removeLiquidityETH(token, liquidity, 0, 0, msg.sender, block.timestamp + 10);
}
반응형
'개발공부일지 > Block-Chain' 카테고리의 다른 글
[Project] Block-Chain DeFi 시스템 구현하기 후기 (0) | 2023.07.28 |
---|---|
DeFi - Swap 기본 사항 이해하기 (0) | 2023.07.28 |
DeFi - Staking 기본 학습하기 (0) | 2023.07.28 |
DeFi - Pool 기본 개념 학습하기 (0) | 2023.07.28 |
DeFi - LP(Liquidity Pool) 기본개념 알아보기 (0) | 2023.07.28 |
댓글