diff --git a/src/libraries/FluxLibrary.sol b/src/libraries/FluxLibrary.sol new file mode 100644 index 0000000..9e5ad1c --- /dev/null +++ b/src/libraries/FluxLibrary.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.36; + +import {IFluxFactory} from "../interfaces/IFluxFactory.sol"; +import {IFluxPair} from "../interfaces/IFluxPair.sol"; + +/// @title Pricing and reserve helpers shared by the router. +library FluxLibrary { + function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { + require(tokenA != tokenB, "FluxLibrary: IDENTICAL_ADDRESSES"); + (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), "FluxLibrary: ZERO_ADDRESS"); + } + + function pairFor(address factory, address tokenA, address tokenB) internal view returns (address pair) { + pair = IFluxFactory(factory).getPair(tokenA, tokenB); + require(pair != address(0), "FluxLibrary: PAIR_NOT_FOUND"); + } + + function getReserves(address factory, address tokenA, address tokenB) + internal + view + returns (uint256 reserveA, uint256 reserveB) + { + (address token0,) = sortTokens(tokenA, tokenB); + (uint112 reserve0, uint112 reserve1,) = IFluxPair(pairFor(factory, tokenA, tokenB)).getReserves(); + (reserveA, reserveB) = + tokenA == token0 ? (uint256(reserve0), uint256(reserve1)) : (uint256(reserve1), uint256(reserve0)); + } + + /// @notice Equal-value counterpart amount given current reserves. + function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) internal pure returns (uint256 amountB) { + require(amountA > 0, "FluxLibrary: INSUFFICIENT_AMOUNT"); + require(reserveA > 0 && reserveB > 0, "FluxLibrary: INSUFFICIENT_LIQUIDITY"); + amountB = amountA * reserveB / reserveA; + } + + /// @notice Output for an exact input, 0.3% fee applied to the input. + function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) + internal + pure + returns (uint256 amountOut) + { + require(amountIn > 0, "FluxLibrary: INSUFFICIENT_INPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "FluxLibrary: INSUFFICIENT_LIQUIDITY"); + uint256 amountInWithFee = amountIn * 997; + uint256 numerator = amountInWithFee * reserveOut; + uint256 denominator = reserveIn * 1000 + amountInWithFee; + amountOut = numerator / denominator; + } + + /// @notice Input required for an exact output, 0.3% fee applied to the input. + function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) + internal + pure + returns (uint256 amountIn) + { + require(amountOut > 0, "FluxLibrary: INSUFFICIENT_OUTPUT_AMOUNT"); + require(reserveIn > 0 && reserveOut > 0, "FluxLibrary: INSUFFICIENT_LIQUIDITY"); + uint256 numerator = reserveIn * amountOut * 1000; + uint256 denominator = (reserveOut - amountOut) * 997; + amountIn = numerator / denominator + 1; + } + + function getAmountsOut(address factory, uint256 amountIn, address[] memory path) + internal + view + returns (uint256[] memory amounts) + { + require(path.length >= 2, "FluxLibrary: 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); + } + } + + function getAmountsIn(address factory, uint256 amountOut, address[] memory path) + internal + view + returns (uint256[] memory amounts) + { + require(path.length >= 2, "FluxLibrary: 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); + } + } +}