feat(router): add constant-product swap router
This commit is contained in:
1 parent
3c6d80aabb
commit
e04164cd24
1 file changed
+158
@@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.36;
|
||||
|
||||
import {IFluxFactory} from "./interfaces/IFluxFactory.sol";
|
||||
import {IFluxPair} from "./interfaces/IFluxPair.sol";
|
||||
import {IErc20} from "./interfaces/IErc20.sol";
|
||||
import {FluxLibrary} from "./libraries/FluxLibrary.sol";
|
||||
|
||||
/// @title User-facing entrypoint for liquidity and multi-hop swaps.
|
||||
contract FluxRouter {
|
||||
address public immutable factory;
|
||||
|
||||
modifier ensure(uint256 deadline) {
|
||||
require(deadline >= block.timestamp, "FluxRouter: EXPIRED");
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address factoryAddress) {
|
||||
factory = factoryAddress;
|
||||
}
|
||||
|
||||
function _safeTransferFrom(address token, address from, address to, uint256 value) private {
|
||||
(bool success, bytes memory returndata) =
|
||||
token.call(abi.encodeWithSelector(IErc20.transferFrom.selector, from, to, value));
|
||||
require(
|
||||
success && (returndata.length == 0 || abi.decode(returndata, (bool))), "FluxRouter: TRANSFER_FROM_FAILED"
|
||||
);
|
||||
}
|
||||
|
||||
function _addLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 amountADesired,
|
||||
uint256 amountBDesired,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin
|
||||
) private returns (uint256 amountA, uint256 amountB) {
|
||||
if (IFluxFactory(factory).getPair(tokenA, tokenB) == address(0)) {
|
||||
IFluxFactory(factory).createPair(tokenA, tokenB);
|
||||
}
|
||||
(uint256 reserveA, uint256 reserveB) = FluxLibrary.getReserves(factory, tokenA, tokenB);
|
||||
if (reserveA == 0 && reserveB == 0) {
|
||||
(amountA, amountB) = (amountADesired, amountBDesired);
|
||||
} else {
|
||||
uint256 amountBOptimal = FluxLibrary.quote(amountADesired, reserveA, reserveB);
|
||||
if (amountBOptimal <= amountBDesired) {
|
||||
require(amountBOptimal >= amountBMin, "FluxRouter: INSUFFICIENT_B_AMOUNT");
|
||||
(amountA, amountB) = (amountADesired, amountBOptimal);
|
||||
} else {
|
||||
uint256 amountAOptimal = FluxLibrary.quote(amountBDesired, reserveB, reserveA);
|
||||
assert(amountAOptimal <= amountADesired);
|
||||
require(amountAOptimal >= amountAMin, "FluxRouter: INSUFFICIENT_A_AMOUNT");
|
||||
(amountA, amountB) = (amountAOptimal, amountBDesired);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 amountADesired,
|
||||
uint256 amountBDesired,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
|
||||
(amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
|
||||
address pair = FluxLibrary.pairFor(factory, tokenA, tokenB);
|
||||
_safeTransferFrom(tokenA, msg.sender, pair, amountA);
|
||||
_safeTransferFrom(tokenB, msg.sender, pair, amountB);
|
||||
liquidity = IFluxPair(pair).mint(to);
|
||||
}
|
||||
|
||||
function removeLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 liquidity,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) public ensure(deadline) returns (uint256 amountA, uint256 amountB) {
|
||||
address pair = FluxLibrary.pairFor(factory, tokenA, tokenB);
|
||||
IFluxPair(pair).transferFrom(msg.sender, pair, liquidity);
|
||||
(uint256 amount0, uint256 amount1) = IFluxPair(pair).burn(to);
|
||||
(address token0,) = FluxLibrary.sortTokens(tokenA, tokenB);
|
||||
(amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
|
||||
require(amountA >= amountAMin, "FluxRouter: INSUFFICIENT_A_AMOUNT");
|
||||
require(amountB >= amountBMin, "FluxRouter: INSUFFICIENT_B_AMOUNT");
|
||||
}
|
||||
|
||||
function _swap(uint256[] memory amounts, address[] memory path, address to) private {
|
||||
for (uint256 i; i < path.length - 1; i++) {
|
||||
(address input, address output) = (path[i], path[i + 1]);
|
||||
(address token0,) = FluxLibrary.sortTokens(input, output);
|
||||
uint256 amountOut = amounts[i + 1];
|
||||
(uint256 amount0Out, uint256 amount1Out) =
|
||||
input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
|
||||
address recipient = i < path.length - 2 ? FluxLibrary.pairFor(factory, output, path[i + 2]) : to;
|
||||
IFluxPair(FluxLibrary.pairFor(factory, input, output)).swap(amount0Out, amount1Out, recipient, new bytes(0));
|
||||
}
|
||||
}
|
||||
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external ensure(deadline) returns (uint256[] memory amounts) {
|
||||
amounts = FluxLibrary.getAmountsOut(factory, amountIn, path);
|
||||
require(amounts[amounts.length - 1] >= amountOutMin, "FluxRouter: INSUFFICIENT_OUTPUT_AMOUNT");
|
||||
_safeTransferFrom(path[0], msg.sender, FluxLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
|
||||
_swap(amounts, path, to);
|
||||
}
|
||||
|
||||
function swapTokensForExactTokens(
|
||||
uint256 amountOut,
|
||||
uint256 amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external ensure(deadline) returns (uint256[] memory amounts) {
|
||||
amounts = FluxLibrary.getAmountsIn(factory, amountOut, path);
|
||||
require(amounts[0] <= amountInMax, "FluxRouter: EXCESSIVE_INPUT_AMOUNT");
|
||||
_safeTransferFrom(path[0], msg.sender, FluxLibrary.pairFor(factory, path[0], path[1]), amounts[0]);
|
||||
_swap(amounts, path, to);
|
||||
}
|
||||
|
||||
function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB) {
|
||||
return FluxLibrary.quote(amountA, reserveA, reserveB);
|
||||
}
|
||||
|
||||
function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)
|
||||
external
|
||||
pure
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
return FluxLibrary.getAmountOut(amountIn, reserveIn, reserveOut);
|
||||
}
|
||||
|
||||
function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)
|
||||
external
|
||||
pure
|
||||
returns (uint256 amountIn)
|
||||
{
|
||||
return FluxLibrary.getAmountIn(amountOut, reserveIn, reserveOut);
|
||||
}
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts) {
|
||||
return FluxLibrary.getAmountsOut(factory, amountIn, path);
|
||||
}
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts) {
|
||||
return FluxLibrary.getAmountsIn(factory, amountOut, path);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user