CLFactory
0x1ac9db4a2608ba45d6127b1737949b51bb54b7f3
Verification
Verified
v0.7.6+commit.7338295f
Type
Contract
4,917 bytes
ABI entries
34
17 read · 8 write
License
gnu_gpl_v2
Contract information
- Address
- 0x1ac9db4a2608ba45d6127b1737949b51bb54b7f3
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.7.6+commit.7338295f
- Optimization
- Enabled
- Creator
- 0x85Fb9f9BC8…459C096651
- Creation tx
- 0x07f11d2097…bee75ce4ed
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (17)
allPools(uint256) → address
allPoolsLength() → uint256
defaultUnstakedFee() → uint24
factoryRegistry() → address
getPool(address, address, int24) → address
getSwapFee(address) → uint24
getUnstakedFee(address) → uint24
isPool(address) → bool
owner() → address
poolImplementation() → address
swapFeeManager() → address
swapFeeModule() → address
tickSpacingToFee(int24) → uint24
tickSpacings() → int24[]
unstakedFeeManager() → address
unstakedFeeModule() → address
voter() → address
Events (8)
DefaultUnstakedFeeChangedOwnerChangedPoolCreatedSwapFeeManagerChangedSwapFeeModuleChangedTickSpacingEnabledUnstakedFeeManagerChangedUnstakedFeeModuleChanged
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_voter",
"type": "address"
},
{
"internalType": "address",
"name": "_poolImplementation",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint24",
"name": "oldUnstakedFee",
"type": "uint24"
},
{
"indexed": true,
"internalType": "uint24",
"name": "newUnstakedFee",
"type": "uint24"
}
],
"name": "DefaultUnstakedFeeChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token0",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "token1",
"type": "address"
},
{
"indexed": true,
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"indexed": false,
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"name": "PoolCreated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldFeeManager",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFeeManager",
"type": "address"
}
],
"name": "SwapFeeManagerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldFeeModule",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFeeModule",
"type": "address"
}
],
"name": "SwapFeeModuleChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"indexed": true,
"internalType": "uint24",
"name": "fee",
"type": "uint24"
}
],
"name": "TickSpacingEnabled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldFeeManager",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFeeManager",
"type": "address"
}
],
"name": "UnstakedFeeManagerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "oldFeeModule",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newFeeModule",
"type": "address"
}
],
"name": "UnstakedFeeModuleChanged",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allPools",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "allPoolsLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "tokenA",
"type": "address"
},
{
"internalType": "address",
"name": "tokenB",
"type": "address"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
}
],
"name": "createPool",
"outputs": [
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "defaultUnstakedFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
}
],
"name": "enableTickSpacing",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factoryRegistry",
"outputs": [
{
"internalType": "contract IFactoryRegistry",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"name": "getPool",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"name": "getSwapFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"name": "getUnstakedFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"name": "isPool",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolImplementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint24",
"name": "_defaultUnstakedFee",
"type": "uint24"
}
],
"name": "setDefaultUnstakedFee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_swapFeeManager",
"type": "address"
}
],
"name": "setSwapFeeManager",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_swapFeeModule",
"type": "address"
}
],
"name": "setSwapFeeModule",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_unstakedFeeManager",
"type": "address"
}
],
"name": "setUnstakedFeeManager",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_unstakedFeeModule",
"type": "address"
}
],
"name": "setUnstakedFeeModule",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "swapFeeManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapFeeModule",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"name": "tickSpacingToFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tickSpacings",
"outputs": [
{
"internalType": "int24[]",
"name": "",
"type": "int24[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "unstakedFeeManager",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "unstakedFeeModule",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "voter",
"outputs": [
{
"internalType": "contract IVoter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: GPL-2.0-or-later
/*
up Finance distributes this derivative work under the GNU GPL v2.0 or later.
The upstream code incorporates GPL-2.0-or-later components from Uniswap v3.
See COPYING for license details.
*/
pragma solidity =0.7.6;
import "./interfaces/ICLFactory.sol";
import "./interfaces/fees/IFeeModule.sol";
import "./interfaces/IVoter.sol";
import "./interfaces/IFactoryRegistry.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "./libraries/ExcessivelySafeCall.sol";
import "./CLPool.sol";
/// @title Canonical CL factory
/// @notice Deploys CL pools and manages ownership and control over pool protocol fees
contract CLFactory is ICLFactory {
using ExcessivelySafeCall for address;
/// @inheritdoc ICLFactory
IVoter public immutable override voter;
/// @inheritdoc ICLFactory
address public immutable override poolImplementation;
/// @inheritdoc ICLFactory
IFactoryRegistry public immutable override factoryRegistry;
/// @inheritdoc ICLFactory
address public override owner;
/// @inheritdoc ICLFactory
address public override swapFeeManager;
/// @inheritdoc ICLFactory
address public override swapFeeModule;
/// @inheritdoc ICLFactory
address public override unstakedFeeManager;
/// @inheritdoc ICLFactory
address public override unstakedFeeModule;
/// @inheritdoc ICLFactory
uint24 public override defaultUnstakedFee;
/// @inheritdoc ICLFactory
mapping(int24 => uint24) public override tickSpacingToFee;
/// @inheritdoc ICLFactory
mapping(address => mapping(address => mapping(int24 => address))) public override getPool;
/// @dev Used in VotingEscrow to determine if a contract is a valid pool
mapping(address => bool) private _isPool;
/// @inheritdoc ICLFactory
address[] public override allPools;
int24[] private _tickSpacings;
constructor(address _voter, address _poolImplementation) {
owner = msg.sender;
swapFeeManager = msg.sender;
unstakedFeeManager = msg.sender;
voter = IVoter(_voter);
factoryRegistry = IVoter(_voter).factoryRegistry();
poolImplementation = _poolImplementation;
defaultUnstakedFee = 100_000;
emit OwnerChanged(address(0), msg.sender);
emit SwapFeeManagerChanged(address(0), msg.sender);
emit UnstakedFeeManagerChanged(address(0), msg.sender);
emit DefaultUnstakedFeeChanged(0, 100_000);
enableTickSpacing(1, 100);
enableTickSpacing(50, 500);
enableTickSpacing(100, 500);
enableTickSpacing(200, 3_000);
enableTickSpacing(2_000, 10_000);
}
/// @inheritdoc ICLFactory
function createPool(address tokenA, address tokenB, int24 tickSpacing, uint160 sqrtPriceX96)
external
override
returns (address pool)
{
require(tokenA != tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
require(tickSpacingToFee[tickSpacing] != 0);
require(getPool[token0][token1][tickSpacing] == address(0));
pool = Clones.cloneDeterministic({
master: poolImplementation,
salt: keccak256(abi.encode(token0, token1, tickSpacing))
});
CLPool(pool).initialize({
_factory: address(this),
_token0: token0,
_token1: token1,
_tickSpacing: tickSpacing,
_factoryRegistry: address(factoryRegistry),
_sqrtPriceX96: sqrtPriceX96
});
allPools.push(pool);
_isPool[pool] = true;
getPool[token0][token1][tickSpacing] = pool;
// populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses
getPool[token1][token0][tickSpacing] = pool;
emit PoolCreated(token0, token1, tickSpacing, pool);
}
/// @inheritdoc ICLFactory
function setOwner(address _owner) external override {
address cachedOwner = owner;
require(msg.sender == cachedOwner);
require(_owner != address(0));
emit OwnerChanged(cachedOwner, _owner);
owner = _owner;
}
/// @inheritdoc ICLFactory
function setSwapFeeManager(address _swapFeeManager) external override {
address cachedSwapFeeManager = swapFeeManager;
require(msg.sender == cachedSwapFeeManager);
require(_swapFeeManager != address(0));
swapFeeManager = _swapFeeManager;
emit SwapFeeManagerChanged(cachedSwapFeeManager, _swapFeeManager);
}
/// @inheritdoc ICLFactory
function setUnstakedFeeManager(address _unstakedFeeManager) external override {
address cachedUnstakedFeeManager = unstakedFeeManager;
require(msg.sender == cachedUnstakedFeeManager);
require(_unstakedFeeManager != address(0));
unstakedFeeManager = _unstakedFeeManager;
emit UnstakedFeeManagerChanged(cachedUnstakedFeeManager, _unstakedFeeManager);
}
/// @inheritdoc ICLFactory
function setSwapFeeModule(address _swapFeeModule) external override {
require(msg.sender == swapFeeManager);
require(_swapFeeModule != address(0));
address oldFeeModule = swapFeeModule;
swapFeeModule = _swapFeeModule;
emit SwapFeeModuleChanged(oldFeeModule, _swapFeeModule);
}
/// @inheritdoc ICLFactory
function setUnstakedFeeModule(address _unstakedFeeModule) external override {
require(msg.sender == unstakedFeeManager);
require(_unstakedFeeModule != address(0));
address oldFeeModule = unstakedFeeModule;
unstakedFeeModule = _unstakedFeeModule;
emit UnstakedFeeModuleChanged(oldFeeModule, _unstakedFeeModule);
}
/// @inheritdoc ICLFactory
function setDefaultUnstakedFee(uint24 _defaultUnstakedFee) external override {
require(msg.sender == unstakedFeeManager);
require(_defaultUnstakedFee <= 500_000);
uint24 oldUnstakedFee = defaultUnstakedFee;
defaultUnstakedFee = _defaultUnstakedFee;
emit DefaultUnstakedFeeChanged(oldUnstakedFee, _defaultUnstakedFee);
}
/// @inheritdoc ICLFactory
function getSwapFee(address pool) external view override returns (uint24) {
if (swapFeeModule != address(0)) {
(bool success, bytes memory data) = swapFeeModule.excessivelySafeStaticCall(
200_000, 32, abi.encodeWithSelector(IFeeModule.getFee.selector, pool)
);
if (success) {
uint24 fee = abi.decode(data, (uint24));
if (fee <= 100_000) {
return fee;
}
}
}
return tickSpacingToFee[CLPool(pool).tickSpacing()];
}
/// @inheritdoc ICLFactory
function getUnstakedFee(address pool) external view override returns (uint24) {
address gauge = voter.gauges(pool);
if (!voter.isAlive(gauge) || gauge == address(0)) {
return 0;
}
if (unstakedFeeModule != address(0)) {
(bool success, bytes memory data) = unstakedFeeModule.excessivelySafeStaticCall(
200_000, 32, abi.encodeWithSelector(IFeeModule.getFee.selector, pool)
);
if (success) {
uint24 fee = abi.decode(data, (uint24));
if (fee <= 1_000_000) {
return fee;
}
}
}
return defaultUnstakedFee;
}
/// @inheritdoc ICLFactory
function enableTickSpacing(int24 tickSpacing, uint24 fee) public override {
require(msg.sender == owner);
require(fee > 0 && fee <= 100_000);
// tick spacing is capped at 16384 to prevent the situation where tickSpacing is so large that
// TickBitmap#nextInitializedTickWithinOneWord overflows int24 container from a valid tick
// 16384 ticks represents a >5x price change with ticks of 1 bips
require(tickSpacing > 0 && tickSpacing < 16384);
require(tickSpacingToFee[tickSpacing] == 0);
tickSpacingToFee[tickSpacing] = fee;
_tickSpacings.push(tickSpacing);
emit TickSpacingEnabled(tickSpacing, fee);
}
/// @inheritdoc ICLFactory
function tickSpacings() external view override returns (int24[] memory) {
return _tickSpacings;
}
/// @inheritdoc ICLFactory
function allPoolsLength() external view override returns (uint256) {
return allPools.length;
}
/// @inheritdoc ICLFactory
function isPool(address pool) external view override returns (bool) {
return _isPool[pool];
}
}
Chain explorer3248msChain node82ms