From 25bb56700345050883acad2fc0345d5ad39d4d7f Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 25 Aug 2026 09:00:00 +0200 Subject: [PATCH] feat(factory): add concentrated-liquidity pool factory --- src/FluxClFactory.sol | 70 +++++++++++++++++++++++++++++++ src/interfaces/IFluxClFactory.sol | 20 +++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/FluxClFactory.sol create mode 100644 src/interfaces/IFluxClFactory.sol diff --git a/src/FluxClFactory.sol b/src/FluxClFactory.sol new file mode 100644 index 0000000..148c56d --- /dev/null +++ b/src/FluxClFactory.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.36; + +import {FluxClPool} from "./FluxClPool.sol"; +import {IFluxClFactory} from "./interfaces/IFluxClFactory.sol"; + +/// @title Deploys one concentrated-liquidity pool per token pair and fee tier. +contract FluxClFactory is IFluxClFactory { + address public owner; + + mapping(uint24 => int24) public feeTierTickSpacing; + mapping(address => mapping(address => mapping(uint24 => address))) private pools; + address[] public allPools; + + constructor() { + owner = msg.sender; + emit OwnerChanged(address(0), msg.sender); + + feeTierTickSpacing[500] = 10; + emit FeeTierEnabled(500, 10); + feeTierTickSpacing[3000] = 60; + emit FeeTierEnabled(3000, 60); + feeTierTickSpacing[10000] = 200; + emit FeeTierEnabled(10000, 200); + } + + function allPoolsLength() external view returns (uint256) { + return allPools.length; + } + + function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool) { + return pools[tokenA][tokenB][fee]; + } + + function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool) { + require(tokenA != tokenB, "Flux: IDENTICAL"); + (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + require(token0 != address(0), "Flux: ZERO_ADDRESS"); + int24 tickSpacing = feeTierTickSpacing[fee]; + require(tickSpacing != 0, "Flux: FEE_TIER"); + require(pools[token0][token1][fee] == address(0), "Flux: POOL_EXISTS"); + + pool = address( + new FluxClPool{salt: keccak256(abi.encode(token0, token1, fee))}( + address(this), token0, token1, fee, tickSpacing + ) + ); + + pools[token0][token1][fee] = pool; + pools[token1][token0][fee] = pool; + allPools.push(pool); + emit PoolCreated(token0, token1, fee, tickSpacing, pool); + } + + function enableFeeTier(uint24 fee, int24 tickSpacing) external { + require(msg.sender == owner, "Flux: FORBIDDEN"); + require(fee < 1000000, "Flux: FEE_RANGE"); + require(tickSpacing > 0 && tickSpacing < 16384, "Flux: SPACING_RANGE"); + require(feeTierTickSpacing[fee] == 0, "Flux: FEE_TIER_SET"); + + feeTierTickSpacing[fee] = tickSpacing; + emit FeeTierEnabled(fee, tickSpacing); + } + + function setOwner(address newOwner) external { + require(msg.sender == owner, "Flux: FORBIDDEN"); + emit OwnerChanged(owner, newOwner); + owner = newOwner; + } +} diff --git a/src/interfaces/IFluxClFactory.sol b/src/interfaces/IFluxClFactory.sol new file mode 100644 index 0000000..bfffce2 --- /dev/null +++ b/src/interfaces/IFluxClFactory.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.36; + +interface IFluxClFactory { + event PoolCreated( + address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool + ); + event FeeTierEnabled(uint24 indexed fee, int24 indexed tickSpacing); + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + + function owner() external view returns (address); + function feeTierTickSpacing(uint24 fee) external view returns (int24); + function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool); + function allPools(uint256 index) external view returns (address pool); + function allPoolsLength() external view returns (uint256); + + function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool); + function enableFeeTier(uint24 fee, int24 tickSpacing) external; + function setOwner(address newOwner) external; +}