UniswapV2Library.sol은 UniswapV2Router02.sol에서 많이 사용하는 함수를 가지고 있는 라이브러리 솔리디티 파일이다.
각 함수별로 어떤 용도로 쓰이는 지 핵심만 나열해보면 다음과 같다. 여기에 있는 함수를 이해해야 UniswapV2Router02.sol도 이해할 수 있다.
다음은 핵심함수들에 대한 설명이다.
하단에 코드분석을 통해서는 코드에 대한 디테일한 사항들을 알 수 있으며 핵심 함수 설명을 통해서는 전체적인 그림을 그려볼 수 있다. 코드가 길지 않아서 금방 파악할 수 있다.
pragma solidity >=0.5.0;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import "./SafeMath.sol";
library UniswapV2Library {
using SafeMath for uint;
// returns sorted token addresses, used to handle return values from pairs sorted in this order
function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
//두 개의 토큰 주소를 입력받아 정렬된 토큰 주소를 반환하는 함수이다.
//토큰A와 토큰B가 같지 않아야 하며 같으면 UniswapV2Library: IDENTICAL_ADDRESSES과 같은 에러가 발생한다.
require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
//두 개의 토큰 주소가 서로 다른지 확인하고, 작은 주소를 token0, 큰 주소를 token1에 할당한다.
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
//token0이 0x0 주소인지 확인하고, 그렇다면 예외를 발생시킨다.
require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
}
// calculates the CREATE2 address for a pair without making any external calls
//팩토리 주소와 두 개의 토큰 주소를 입력받아 해당 페어의 주소를 계산하는 함수이다.
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
//sortTokens 함수를 사용하여 토큰 주소를 정렬하고, keccak256 해시 함수를 사용하여 CREATE2 주소를 계산한다.
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
))));
}
// fetches and sorts the reserves for a pair
//팩토리 주소와 두 개의 토큰 주소를 입력받아 해당 페어의 예약을 가져오는 함수이다.
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
//sortTokens 함수를 사용하여 토큰 주소를 정렬하고,
(address token0,) = sortTokens(tokenA, tokenB);
//IUniswapV2Pair 인터페이스의 getReserves 함수를 호출하여 예약을 가져온다.
(uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
//tokenA가 token0이면 reserveA에 reserve0을, tokenB가 token1이면 reserveB에 reserve1을 할당한다.
//아니면 반대로 동작한다.
(reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
//어떤 자산의 금액과 페어의 예약을 입력받아 다른 자산의 등가 금액을 반환하는 함수이다.
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
//amountA가 0보다 큰지 확인하고, reserveA와 reserveB가 모두 0보다 큰지 확인한다.
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
// amountA는 reserveB와 곱한 후 reserveA로 나누어 amountB를 계산한다.
amountB = amountA.mul(reserveB) / reserveA;
}
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
//어떤 자산의 입력 금액과 해당 페어의 예약을 입력받아 다른 자산의 최대 출력 금액을 반환하는 함수이다.
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
//amountIn이 0보다 큰지 확인하고, reserveIn과 reserveOut이 모두 0보다 큰지 확인한다.
require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
//amountIn에 997을 곱한 값을 계산하고, amountInWithFee에 할당한다.
uint amountInWithFee = amountIn.mul(997);
//numerator는 amountInWithFee와 reserveOut과 곱한 값을 할당한다.
uint numerator = amountInWithFee.mul(reserveOut);
// denominator는 reserveIn에 1000을 곱하고 mountInWithFee를 더한다.
uint denominator = reserveIn.mul(1000).add(amountInWithFee);
//amountOut은 numerator를 denominator로 나눈다.
amountOut = numerator / denominator;
}
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
//getAmountIn 함수는 reserveIn과 reserveOut이라는 두 자산의 보유량과 amountOut이라는 출력 자산의 양이 주어졌을 때, 입력 자산의 양을 계산해주는 함수입니다.
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
//amountOut과 reserveIn, reserveOut은 0보다 커야합니다.
require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
//numerator는 reserveIn과 출력토큰량인 amountOut을 곱하고 이후 1000을 곱합니다.
uint numerator = reserveIn.mul(amountOut).mul(1000);
//denominator는 reserveOut에서 amountOut을 빼고 997을 곱합니다.
uint denominator = reserveOut.sub(amountOut).mul(997);
// amountIn 입력토큰 량은 (numerator / denominator)를 나눈 값에 1을 더한다.
amountIn = (numerator / denominator).add(1);
}
// performs chained getAmountOut calculations on any number of pairs
//factory와 입력토큰량, 계산할 토큰 배열인 path를 인자로 받는다.
function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
//배열길이는 2이상이어야한다. 토큰1, 토큰2
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
//출력 토큰량은 path 길이만큼 uint 값을 할당한다.
amounts = new uint[](path.length);
//배열의 첫번째 값은 amountIn이다. 입력토큰량이다.
amounts[0] = amountIn;
//path의 길이만큼 반복문 실행
for (uint i; i < path.length - 1; i++) {
//(uint reserveIn, uint reserveOut)은 getReserves를 호출해서 값을 각각 값을 가져온다.
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
//amounts의 마지막 값은 getAmountOut를 호출한 값이다.
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
}
}
// performs chained getAmountIn calculations on any number of pairs
//factory와 출력 토큰량, 계산할 토큰 배열인 path를 인자로 받는다.
function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
//배열의 길이는 2이상이어야 한다.
require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
//출력토큰량은 path의 길이만큼 할당한다.
amounts = new uint[](path.length);
//amounts의 마지막 값은 amountOut이다.
amounts[amounts.length - 1] = amountOut;
//path 길이만큼 반복문 실행
for (uint i = path.length - 1; i > 0; i--) {
//getReserves를 호출한 값에서 reserveIn과 reserveOut에 변수를 할당한다.
(uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
//amounts의 마지막 값은 getAmountIn을 호출한 값으로 할당한다.
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
}
}
}
UniswapV2-core - UniswapV2Factory.sol 코드 분석 (0) | 2023.03.26 |
---|---|
UniswapV2-core - UniswapV2Pair.sol 코드 분석 (0) | 2023.03.26 |
UniswapV2 periphery - Uniswapv2Router.sol 코드 분석 (0) | 2023.03.17 |
UniswapV2-periphery UniswapV2Migrator.sol 코드 분석 (0) | 2023.03.15 |
Uniswap V2 Protocol 개요 (0) | 2023.03.09 |