134 lines
5.1 KiB
Solidity
134 lines
5.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity =0.8.20;
|
|
|
|
import {IUniswapV2Pair} from "../interfaces/IUniswapV2Pair.sol";
|
|
import {UniswapV2Pair} from "../UniswapV2Pair.sol";
|
|
|
|
library UniswapV2Library {
|
|
// 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) {
|
|
if (tokenA == tokenB) revert("UniswapV2Library: IDENTICAL_ADDRESSES");
|
|
(token0, token1) = tokenA < tokenB
|
|
? (tokenA, tokenB)
|
|
: (tokenB, tokenA);
|
|
if (token0 == address(0)) revert("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) {
|
|
(address token0, address token1) = sortTokens(tokenA, tokenB);
|
|
pair = address(
|
|
uint160(
|
|
uint256(
|
|
keccak256(
|
|
abi.encodePacked(
|
|
bytes1(0xff),
|
|
factory,
|
|
keccak256(abi.encodePacked(token0, token1)),
|
|
keccak256(type(UniswapV2Pair).creationCode) // init code hash
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
// fetches and sorts the reserves for a pair
|
|
function getReserves(
|
|
address factory,
|
|
address tokenA,
|
|
address tokenB
|
|
) internal view returns (uint256 reserveA, uint256 reserveB) {
|
|
(address token0, ) = sortTokens(tokenA, tokenB);
|
|
(uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
|
|
pairFor(factory, tokenA, tokenB)
|
|
).getReserves();
|
|
(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(
|
|
uint256 amountA,
|
|
uint256 reserveA,
|
|
uint256 reserveB
|
|
) internal pure returns (uint256 amountB) {
|
|
if (amountA == 0) revert("UniswapV2Library: INSUFFICIENT_AMOUNT");
|
|
if (reserveA == 0 || reserveB == 0) revert("UniswapV2Library: INSUFFICIENT_LIQUIDITY");
|
|
amountB = (amountA * reserveB) / reserveA;
|
|
}
|
|
|
|
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
|
|
function getAmountOut(
|
|
uint256 amountIn,
|
|
uint256 reserveIn,
|
|
uint256 reserveOut
|
|
) internal pure returns (uint256 amountOut) {
|
|
if (amountIn == 0) revert("UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
|
|
if (reserveIn == 0 || reserveOut == 0) revert("UniswapV2Library: INSUFFICIENT_LIQUIDITY");
|
|
uint256 amountInWithFee = amountIn * 997;
|
|
uint256 numerator = amountInWithFee * reserveOut;
|
|
uint256 denominator = reserveIn * 1000 + amountInWithFee;
|
|
amountOut = numerator / denominator;
|
|
}
|
|
|
|
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset
|
|
function getAmountIn(
|
|
uint256 amountOut,
|
|
uint256 reserveIn,
|
|
uint256 reserveOut
|
|
) internal pure returns (uint256 amountIn) {
|
|
if (amountOut == 0) revert("UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
|
|
if (reserveIn == 0 || reserveOut == 0) revert("UniswapV2Library: INSUFFICIENT_LIQUIDITY");
|
|
uint256 numerator = reserveIn * amountOut * 1000;
|
|
uint256 denominator = (reserveOut - amountOut) * 997;
|
|
amountIn = numerator / denominator + 1;
|
|
}
|
|
|
|
// performs chained getAmountOut calculations on any number of pairs
|
|
function getAmountsOut(
|
|
address factory,
|
|
uint256 amountIn,
|
|
address[] memory path
|
|
) internal view returns (uint256[] memory amounts) {
|
|
if (path.length < 2) revert("UniswapV2Library: INVALID_PATH");
|
|
amounts = new uint256[](path.length);
|
|
amounts[0] = amountIn;
|
|
for (uint256 i; i < path.length - 1; i++) {
|
|
(uint256 reserveIn, uint256 reserveOut) = getReserves(
|
|
factory,
|
|
path[i],
|
|
path[i + 1]
|
|
);
|
|
amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
|
|
}
|
|
}
|
|
|
|
// performs chained getAmountIn calculations on any number of pairs
|
|
function getAmountsIn(
|
|
address factory,
|
|
uint256 amountOut,
|
|
address[] memory path
|
|
) internal view returns (uint256[] memory amounts) {
|
|
if (path.length < 2) revert("UniswapV2Library: INVALID_PATH");
|
|
amounts = new uint256[](path.length);
|
|
amounts[amounts.length - 1] = amountOut;
|
|
for (uint256 i = path.length - 1; i > 0; i--) {
|
|
(uint256 reserveIn, uint256 reserveOut) = getReserves(
|
|
factory,
|
|
path[i - 1],
|
|
path[i]
|
|
);
|
|
amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
|
|
}
|
|
}
|
|
}
|