DLMMFactory

0xdcd5f77697914e27f56fd263ef82923c8524abac

Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
13,908 bytes
ABI entries
104
29 read · 25 write
License
none

Contract information

Address
0xdcd5f77697914e27f56fd263ef82923c8524abac
Chain
Robinhood Chain (4663)
Compiler
v0.8.30+commit.73712a01
Optimization
Enabled
Creation tx
0xef64e3d85e…fcc67a627c

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (29)

DEFAULT_ADMIN_ROLE()bytes32
DEFAULT_PROTOCOL_SHARE()uint16
GAUGED_PROTOCOL_SHARE()uint16
LB_HOOKS_MANAGER_ROLE()bytes32
defaultProtocolShare()uint16
feeCollector()address
gaugedProtocolShare()uint16
getAllBinSteps()uint256[]
getAllLBPairs(address, address)tuple[]
getFeeRecipient()address
getFlashLoanFee()uint256
getLBPairAtIndex(uint256)address
getLBPairImplementation()address
getLBPairInformation(address, address, uint256)tuple
getMaxFlashLoanFee()uint256
getMinBinStep()uint256
getNumberOfLBPairs()uint256
getNumberOfQuoteAssets()uint256
getOpenBinSteps()uint256[]
getPreset(uint256)uint256, uint256, uint256, uint256, uint256, uint256, uint256, bool
getQuoteAssetAtIndex(uint256)address
getRoleAdmin(bytes32)bytes32
hasRole(bytes32, address)bool
isPool(address)bool
isQuoteAsset(address)bool
owner()address
pendingOwner()address
supportsInterface(bytes4)bool
voter()address

Events (16)

DLMMVoterSetFeeRecipientSetFlashLoanFeeSetLBPairCreatedLBPairIgnoredStateChangedLBPairImplementationSetOwnershipTransferStartedOwnershipTransferredPresetOpenStateChangedPresetRemovedPresetSetQuoteAssetAddedQuoteAssetRemovedRoleAdminChangedRoleGrantedRoleRevoked

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "feeRecipient",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "initialOwner",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "flashLoanFee",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AccessControlBadConfirmation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "neededRole",
        "type": "bytes32"
      }
    ],
    "name": "AccessControlUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      }
    ],
    "name": "DLMMFactory__InvalidPool",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "caller",
        "type": "address"
      }
    ],
    "name": "DLMMFactory__NotOwnerOrVoter",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "voter",
        "type": "address"
      }
    ],
    "name": "DLMMFactory__SameVoter",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "key",
        "type": "bytes32"
      }
    ],
    "name": "EnumerableMapNonexistentKey",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__AddressZero",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__BinStepHasNoPreset",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__BinStepTooLow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__CannotGrantDefaultAdminRole",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "fees",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "maxFees",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__FlashLoanFeeAboveMax",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "LBFactory__IdenticalAddresses",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__ImplementationNotSet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__InvalidHooksParameters",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__LBPairAlreadyExists",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__LBPairDoesNotExist",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__LBPairIgnoredIsAlreadyInTheSameState",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__LBPairNotCreated",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "LBPairImplementation",
        "type": "address"
      }
    ],
    "name": "LBFactory__LBPairSafetyCheckFailed",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__PresetIsLockedForUsers",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LBFactory__PresetOpenStateIsAlreadyInTheSameState",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "LBFactory__QuoteAssetAlreadyWhitelisted",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "LBFactory__QuoteAssetNotWhitelisted",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "feeRecipient",
        "type": "address"
      }
    ],
    "name": "LBFactory__SameFeeRecipient",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "flashLoanFee",
        "type": "uint256"
      }
    ],
    "name": "LBFactory__SameFlashLoanFee",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "hooksImplementation",
        "type": "address"
      }
    ],
    "name": "LBFactory__SameHooksImplementation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "hooksParameters",
        "type": "bytes32"
      }
    ],
    "name": "LBFactory__SameHooksParameters",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "LBPairImplementation",
        "type": "address"
      }
    ],
    "name": "LBFactory__SameImplementation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PairParametersHelper__InvalidParameter",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SafeCast__Exceeds16Bits",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "x",
        "type": "uint256"
      },
      {
        "internalType": "int256",
        "name": "y",
        "type": "int256"
      }
    ],
    "name": "Uint128x128Math__PowUnderflow",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "oldVoter",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newVoter",
        "type": "address"
      }
    ],
    "name": "DLMMVoterSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "oldRecipient",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "newRecipient",
        "type": "address"
      }
    ],
    "name": "FeeRecipientSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "oldFlashLoanFee",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newFlashLoanFee",
        "type": "uint256"
      }
    ],
    "name": "FlashLoanFeeSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "contract IDLMMPool",
        "name": "LBPair",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "pid",
        "type": "uint256"
      }
    ],
    "name": "LBPairCreated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "contract IDLMMPool",
        "name": "LBPair",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "ignored",
        "type": "bool"
      }
    ],
    "name": "LBPairIgnoredStateChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "oldLBPairImplementation",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "LBPairImplementation",
        "type": "address"
      }
    ],
    "name": "LBPairImplementationSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "bool",
        "name": "isOpen",
        "type": "bool"
      }
    ],
    "name": "PresetOpenStateChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "PresetRemoved",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "baseFactor",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "filterPeriod",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "decayPeriod",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "reductionFactor",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "variableFeeControl",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "protocolShare",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "maxVolatilityAccumulator",
        "type": "uint256"
      }
    ],
    "name": "PresetSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "QuoteAssetAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "QuoteAssetRemoved",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousAdminRole",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newAdminRole",
        "type": "bytes32"
      }
    ],
    "name": "RoleAdminChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleGranted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleRevoked",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEFAULT_PROTOCOL_SHARE",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "GAUGED_PROTOCOL_SHARE",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LB_HOOKS_MANAGER_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "addQuoteAsset",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "activeId",
        "type": "uint24"
      },
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      }
    ],
    "name": "createLBPair",
    "outputs": [
      {
        "internalType": "contract IDLMMPool",
        "name": "pair",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "defaultProtocolShare",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeCollector",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IDLMMPool",
        "name": "pair",
        "type": "address"
      }
    ],
    "name": "forceDecay",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "gaugedProtocolShare",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getAllBinSteps",
    "outputs": [
      {
        "internalType": "uint256[]",
        "name": "binStepWithPreset",
        "type": "uint256[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      }
    ],
    "name": "getAllLBPairs",
    "outputs": [
      {
        "components": [
          {
            "internalType": "uint16",
            "name": "binStep",
            "type": "uint16"
          },
          {
            "internalType": "contract IDLMMPool",
            "name": "LBPair",
            "type": "address"
          },
          {
            "internalType": "bool",
            "name": "createdByOwner",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "ignoredForRouting",
            "type": "bool"
          }
        ],
        "internalType": "struct IDLMMFactory.LBPairInformation[]",
        "name": "lbPairsAvailable",
        "type": "tuple[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getFeeRecipient",
    "outputs": [
      {
        "internalType": "address",
        "name": "feeRecipient",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getFlashLoanFee",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "flashLoanFee",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "getLBPairAtIndex",
    "outputs": [
      {
        "internalType": "contract IDLMMPool",
        "name": "lbPair",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getLBPairImplementation",
    "outputs": [
      {
        "internalType": "address",
        "name": "lbPairImplementation",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenA",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenB",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "getLBPairInformation",
    "outputs": [
      {
        "components": [
          {
            "internalType": "uint16",
            "name": "binStep",
            "type": "uint16"
          },
          {
            "internalType": "contract IDLMMPool",
            "name": "LBPair",
            "type": "address"
          },
          {
            "internalType": "bool",
            "name": "createdByOwner",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "ignoredForRouting",
            "type": "bool"
          }
        ],
        "internalType": "struct IDLMMFactory.LBPairInformation",
        "name": "lbPairInformation",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getMaxFlashLoanFee",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "maxFee",
        "type": "uint256"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getMinBinStep",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "minBinStep",
        "type": "uint256"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getNumberOfLBPairs",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "lbPairNumber",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getNumberOfQuoteAssets",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "numberOfQuoteAssets",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getOpenBinSteps",
    "outputs": [
      {
        "internalType": "uint256[]",
        "name": "openBinStep",
        "type": "uint256[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "binStep",
        "type": "uint256"
      }
    ],
    "name": "getPreset",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "baseFactor",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "filterPeriod",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "decayPeriod",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reductionFactor",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "variableFeeControl",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "protocolShare",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "maxVolatilityAccumulator",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "isOpen",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "getQuoteAssetAtIndex",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "asset",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      }
    ],
    "name": "getRoleAdmin",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "grantRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "hasRole",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      }
    ],
    "name": "isPool",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "isQuoteAsset",
    "outputs": [
      {
        "internalType": "bool",
        "name": "isQuote",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      }
    ],
    "name": "removeLBHooksOnPair",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      }
    ],
    "name": "removePreset",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "quoteAsset",
        "type": "address"
      }
    ],
    "name": "removeQuoteAsset",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "callerConfirmation",
        "type": "address"
      }
    ],
    "name": "renounceRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "revokeRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "feeRecipient",
        "type": "address"
      }
    ],
    "name": "setFeeRecipient",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "baseFactor",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "filterPeriod",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "decayPeriod",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "reductionFactor",
        "type": "uint16"
      },
      {
        "internalType": "uint24",
        "name": "variableFeeControl",
        "type": "uint24"
      },
      {
        "internalType": "uint16",
        "name": "protocolShare",
        "type": "uint16"
      },
      {
        "internalType": "uint24",
        "name": "maxVolatilityAccumulator",
        "type": "uint24"
      }
    ],
    "name": "setFeesParametersOnPair",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "flashLoanFee",
        "type": "uint256"
      }
    ],
    "name": "setFlashLoanFee",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "bytes32",
        "name": "hooksParameters",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "onHooksSetData",
        "type": "bytes"
      }
    ],
    "name": "setLBHooksParametersOnPair",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "tokenX",
        "type": "address"
      },
      {
        "internalType": "contract IERC20",
        "name": "tokenY",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "bool",
        "name": "ignored",
        "type": "bool"
      }
    ],
    "name": "setLBPairIgnored",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newLBPairImplementation",
        "type": "address"
      }
    ],
    "name": "setLBPairImplementation",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      }
    ],
    "name": "setPoolDefaultProtocolShare",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      }
    ],
    "name": "setPoolGaugedProtocolShare",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "protocolShare",
        "type": "uint16"
      }
    ],
    "name": "setPoolProtocolShare",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "baseFactor",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "filterPeriod",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "decayPeriod",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "reductionFactor",
        "type": "uint16"
      },
      {
        "internalType": "uint24",
        "name": "variableFeeControl",
        "type": "uint24"
      },
      {
        "internalType": "uint16",
        "name": "protocolShare",
        "type": "uint16"
      },
      {
        "internalType": "uint24",
        "name": "maxVolatilityAccumulator",
        "type": "uint24"
      },
      {
        "internalType": "bool",
        "name": "isOpen",
        "type": "bool"
      }
    ],
    "name": "setPreset",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "bool",
        "name": "isOpen",
        "type": "bool"
      }
    ],
    "name": "setPresetOpenState",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "binStep",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "protocolShare",
        "type": "uint16"
      }
    ],
    "name": "setPresetProtocolShare",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newVoter",
        "type": "address"
      }
    ],
    "name": "setVoter",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "voter",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {EnumerableMap} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";

import {PairParameterHelper} from "./libraries/PairParameterHelper.sol";
import {Encoded} from "./libraries/math/Encoded.sol";
import {ImmutableClone} from "./libraries/ImmutableClone.sol";
import {PriceHelper} from "./libraries/PriceHelper.sol";
import {SafeCast} from "./libraries/math/SafeCast.sol";
import {Hooks} from "./libraries/Hooks.sol";

import {IDLMMFactory} from "./interfaces/IDLMMFactory.sol";
import {IDLMMPool} from "./interfaces/IDLMMPool.sol";
import {IDLMMHooks} from "./interfaces/IDLMMHooks.sol";

/// @title DLMM Factory
/// @notice Contract used to deploy and register new DLMM pools
contract DLMMFactory is Ownable2Step, AccessControl, IDLMMFactory {
    using SafeCast for uint256;
    using Encoded for bytes32;
    using PairParameterHelper for bytes32;
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.UintSet;
    using EnumerableMap for EnumerableMap.UintToUintMap;

    bytes32 public constant LB_HOOKS_MANAGER_ROLE = keccak256("LB_HOOKS_MANAGER_ROLE");

    uint16 public constant DEFAULT_PROTOCOL_SHARE = 2_500;
    uint16 public constant GAUGED_PROTOCOL_SHARE = 10_000;

    uint256 private constant _OFFSET_IS_PRESET_OPEN = 255;

    uint256 private constant _MIN_BIN_STEP = 1; // 0.001%

    uint256 private constant _MAX_FLASHLOAN_FEE = 0.1e18; // 10%

    address private _feeRecipient;
    uint256 private _flashLoanFee;
    address private _voter;

    address private _lbPairImplementation;

    IDLMMPool[] private _allLBPairs;

    /// @dev Mapping from a (tokenA, tokenB, binStep) to a DLMM pool. The tokens are ordered to save gas, but they can be
    /// in the reverse order in the actual pair.
    /// Always query one of the 2 tokens of the pair to assert the order of the 2 tokens
    mapping(IERC20 => mapping(IERC20 => mapping(uint256 => LBPairInformation))) private _lbPairsInfo;

    EnumerableMap.UintToUintMap private _presets;
    EnumerableSet.AddressSet private _quoteAssetWhitelist;

    /// @dev Mapping from a (tokenA, tokenB) to a set of available bin steps, this is used to keep track of the
    /// bin steps that are already used for a pair.
    /// The tokens are ordered to save gas, but they can be in the reverse order in the actual pair.
    /// Always query one of the 2 tokens of the pair to assert the order of the 2 tokens
    mapping(IERC20 => mapping(IERC20 => EnumerableSet.UintSet)) private _availableLBPairBinSteps;

    /// @notice Constructor
    /// @param feeRecipient The address of the fee recipient
    /// @param flashLoanFee The value of the fee for flash loan
    constructor(address feeRecipient, address initialOwner, uint256 flashLoanFee) Ownable(initialOwner) {
        if (flashLoanFee > _MAX_FLASHLOAN_FEE) {
            revert LBFactory__FlashLoanFeeAboveMax(flashLoanFee, _MAX_FLASHLOAN_FEE);
        }

        _setFeeRecipient(feeRecipient);

        _flashLoanFee = flashLoanFee;
        emit FlashLoanFeeSet(0, flashLoanFee);
    }

    /// @notice Get the minimum bin step a pair can have
    /// @return minBinStep
    function getMinBinStep() external pure override returns (uint256 minBinStep) {
        return _MIN_BIN_STEP;
    }

    /// @notice Get the protocol fee recipient
    /// @return feeRecipient
    function getFeeRecipient() external view override returns (address feeRecipient) {
        return _feeRecipient;
    }

    function feeCollector() external view override returns (address) {
        return _feeRecipient;
    }

    function voter() external view override returns (address) {
        return _voter;
    }

    function defaultProtocolShare() external pure override returns (uint16) {
        return DEFAULT_PROTOCOL_SHARE;
    }

    function gaugedProtocolShare() external pure override returns (uint16) {
        return GAUGED_PROTOCOL_SHARE;
    }

    function setVoter(address newVoter) external override onlyOwner {
        if (newVoter == _voter) revert DLMMFactory__SameVoter(newVoter);

        emit DLMMVoterSet(_voter, newVoter);
        _voter = newVoter;
    }

    function setPoolProtocolShare(address pool, uint16 protocolShare) external override {
        _checkOwnerOrVoter();
        _setPoolProtocolShare(pool, protocolShare);
    }

    function setPoolDefaultProtocolShare(address pool) external override {
        _checkOwnerOrVoter();
        _setPoolProtocolShare(pool, DEFAULT_PROTOCOL_SHARE);
    }

    function setPoolGaugedProtocolShare(address pool) external override {
        _checkOwnerOrVoter();
        _setPoolProtocolShare(pool, GAUGED_PROTOCOL_SHARE);
    }

    /// @notice Get the maximum fee percentage for flashLoans
    /// @return maxFee
    function getMaxFlashLoanFee() external pure override returns (uint256 maxFee) {
        return _MAX_FLASHLOAN_FEE;
    }

    /// @notice Get the fee for flash loans, in 1e18
    /// @return flashLoanFee The fee for flash loans, in 1e18
    function getFlashLoanFee() external view override returns (uint256 flashLoanFee) {
        return _flashLoanFee;
    }

    /// @notice Get the address of the DLMM pool implementation
    /// @return lbPairImplementation The address of the DLMM pool implementation
    function getLBPairImplementation() external view override returns (address lbPairImplementation) {
        return _lbPairImplementation;
    }

    /// @notice View function to return the number of DLMM pools created
    /// @return lbPairNumber
    function getNumberOfLBPairs() external view override returns (uint256 lbPairNumber) {
        return _allLBPairs.length;
    }

    /// @notice View function to return the DLMM pool created at index `index`
    /// @param index The index
    /// @return lbPair The address of the DLMM pool at index `index`
    function getLBPairAtIndex(uint256 index) external view override returns (IDLMMPool lbPair) {
        return _allLBPairs[index];
    }

    /// @notice View function to return the number of quote assets whitelisted
    /// @return numberOfQuoteAssets The number of quote assets
    function getNumberOfQuoteAssets() external view override returns (uint256 numberOfQuoteAssets) {
        return _quoteAssetWhitelist.length();
    }

    /// @notice View function to return the quote asset whitelisted at index `index`
    /// @param index The index
    /// @return asset The address of the quoteAsset at index `index`
    function getQuoteAssetAtIndex(uint256 index) external view override returns (IERC20 asset) {
        return IERC20(_quoteAssetWhitelist.at(index));
    }

    /// @notice View function to return whether a token is a quotedAsset (true) or not (false)
    /// @param token The address of the asset
    /// @return isQuote Whether the token is a quote asset or not
    function isQuoteAsset(IERC20 token) external view override returns (bool isQuote) {
        return _quoteAssetWhitelist.contains(address(token));
    }

    function isPool(address pool) external view override returns (bool) {
        if (pool.code.length == 0) return false;

        IDLMMPool dlmmPool = IDLMMPool(pool);
        try dlmmPool.getFactory() returns (IDLMMFactory factory) {
            if (address(factory) != address(this)) return false;

            return address(
                _getLBPairInformation(dlmmPool.getTokenX(), dlmmPool.getTokenY(), dlmmPool.getBinStep()).LBPair
            ) == pool;
        } catch {
            return false;
        }
    }

    /// @notice Returns the pool information if it exists,
    /// if not, then the address 0 is returned. The order doesn't matter
    /// @param tokenA The address of the first token of the pair
    /// @param tokenB The address of the second token of the pair
    /// @param binStep The bin step of the DLMM pool
    /// @return lbPairInformation The pool information
    function getLBPairInformation(IERC20 tokenA, IERC20 tokenB, uint256 binStep)
        external
        view
        override
        returns (LBPairInformation memory lbPairInformation)
    {
        return _getLBPairInformation(tokenA, tokenB, binStep);
    }

    /// @notice View function to return the different parameters of the preset
    /// Will revert if the preset doesn't exist
    /// @param binStep The bin step of the preset
    /// @return baseFactor The base factor
    /// @return filterPeriod The filter period of the preset
    /// @return decayPeriod The decay period of the preset
    /// @return reductionFactor The reduction factor of the preset
    /// @return variableFeeControl The variable fee control of the preset
    /// @return protocolShare The protocol share of the preset
    /// @return maxVolatilityAccumulator The max volatility accumulator of the preset
    /// @return isOpen Whether the preset is open or not
    function getPreset(uint256 binStep)
        external
        view
        override
        returns (
            uint256 baseFactor,
            uint256 filterPeriod,
            uint256 decayPeriod,
            uint256 reductionFactor,
            uint256 variableFeeControl,
            uint256 protocolShare,
            uint256 maxVolatilityAccumulator,
            bool isOpen
        )
    {
        if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);

        bytes32 preset = bytes32(_presets.get(binStep));

        baseFactor = preset.getBaseFactor();
        filterPeriod = preset.getFilterPeriod();
        decayPeriod = preset.getDecayPeriod();
        reductionFactor = preset.getReductionFactor();
        variableFeeControl = preset.getVariableFeeControl();
        protocolShare = preset.getProtocolShare();
        maxVolatilityAccumulator = preset.getMaxVolatilityAccumulator();

        isOpen = preset.decodeBool(_OFFSET_IS_PRESET_OPEN);
    }

    /// @notice View function to return the list of available binStep with a preset
    /// @return binStepWithPreset The list of binStep
    function getAllBinSteps() external view override returns (uint256[] memory binStepWithPreset) {
        return _presets.keys();
    }

    /// @notice View function to return the list of open binSteps
    /// @return openBinStep The list of open binSteps
    function getOpenBinSteps() external view override returns (uint256[] memory openBinStep) {
        uint256 length = _presets.length();

        if (length > 0) {
            openBinStep = new uint256[](length);

            uint256 index;

            for (uint256 i; i < length; ++i) {
                (uint256 binStep, uint256 preset) = _presets.at(i);

                if (_isPresetOpen(bytes32(preset))) {
                    openBinStep[index] = binStep;
                    index++;
                }
            }

            if (index < length) {
                assembly {
                    mstore(openBinStep, index)
                }
            }
        }
    }

    /// @notice View function to return all the DLMM pools of a pair of tokens
    /// @param tokenX The first token of the pair
    /// @param tokenY The second token of the pair
    /// @return lbPairsAvailable The list of available DLMM pools
    function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
        external
        view
        override
        returns (LBPairInformation[] memory lbPairsAvailable)
    {
        unchecked {
            (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);

            EnumerableSet.UintSet storage addressSet = _availableLBPairBinSteps[tokenA][tokenB];

            uint256 length = addressSet.length();

            if (length > 0) {
                lbPairsAvailable = new LBPairInformation[](length);

                mapping(uint256 => LBPairInformation) storage lbPairsInfo = _lbPairsInfo[tokenA][tokenB];

                for (uint256 i = 0; i < length; ++i) {
                    uint16 binStep = addressSet.at(i).safe16();

                    lbPairsAvailable[i] = LBPairInformation({
                        binStep: binStep,
                        LBPair: lbPairsInfo[binStep].LBPair,
                        createdByOwner: lbPairsInfo[binStep].createdByOwner,
                        ignoredForRouting: lbPairsInfo[binStep].ignoredForRouting
                    });
                }
            }
        }
    }

    /// @notice Set the DLMM pool implementation address
    /// @dev Needs to be called by the owner
    /// @param newLBPairImplementation The address of the implementation
    function setLBPairImplementation(address newLBPairImplementation) external override onlyOwner {
        if (IDLMMPool(newLBPairImplementation).getFactory() != this) {
            revert LBFactory__LBPairSafetyCheckFailed(newLBPairImplementation);
        }

        address oldLBPairImplementation = _lbPairImplementation;
        if (oldLBPairImplementation == newLBPairImplementation) {
            revert LBFactory__SameImplementation(newLBPairImplementation);
        }

        _lbPairImplementation = newLBPairImplementation;

        emit LBPairImplementationSet(oldLBPairImplementation, newLBPairImplementation);
    }

    /// @notice Create a DLMM pool for tokenX and tokenY
    /// @param tokenX The address of the first token
    /// @param tokenY The address of the second token
    /// @param activeId The active id of the pair
    /// @param binStep The bin step in basis point, used to calculate log(1 + binStep / 10_000)
    /// @return pair The address of the newly created DLMM pool
    function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
        external
        override
        returns (IDLMMPool pair)
    {
        if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);

        bytes32 preset = bytes32(_presets.get(binStep));
        bool isOwner = msg.sender == owner();

        if (!_isPresetOpen(preset) && !isOwner) {
            revert LBFactory__PresetIsLockedForUsers(msg.sender, binStep);
        }

        if (!_quoteAssetWhitelist.contains(address(tokenY))) revert LBFactory__QuoteAssetNotWhitelisted(tokenY);

        if (tokenX == tokenY) revert LBFactory__IdenticalAddresses(tokenX);

        // safety check, making sure that the price can be calculated
        PriceHelper.getPriceFromId(activeId, binStep);

        // We sort token for storage efficiency, only one input needs to be stored because they are sorted
        (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);
        // single check is sufficient
        if (address(tokenA) == address(0)) revert LBFactory__AddressZero();
        if (address(_lbPairsInfo[tokenA][tokenB][binStep].LBPair) != address(0)) {
            revert LBFactory__LBPairAlreadyExists(tokenX, tokenY, binStep);
        }

        {
            address implementation = _lbPairImplementation;

            if (implementation == address(0)) revert LBFactory__ImplementationNotSet();

            pair = IDLMMPool(
                ImmutableClone.cloneDeterministic(
                    implementation,
                    abi.encodePacked(tokenX, tokenY, binStep),
                    keccak256(abi.encode(tokenA, tokenB, binStep))
                )
            );
        }

        _lbPairsInfo[tokenA][tokenB][binStep] =
            LBPairInformation({binStep: binStep, LBPair: pair, createdByOwner: isOwner, ignoredForRouting: false});

        _allLBPairs.push(pair);
        _availableLBPairBinSteps[tokenA][tokenB].add(binStep);

        emit LBPairCreated(tokenX, tokenY, binStep, pair, _allLBPairs.length - 1);

        pair.initialize(
            preset.getBaseFactor(),
            preset.getFilterPeriod(),
            preset.getDecayPeriod(),
            preset.getReductionFactor(),
            preset.getVariableFeeControl(),
            preset.getProtocolShare(),
            preset.getMaxVolatilityAccumulator(),
            activeId
        );
    }

    /// @notice Function to set whether the pair is ignored or not for routing, it will make the pair unusable by the router
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The pair doesn't exist
    /// - The ignored state is already in the same state
    /// @param tokenX The address of the first token of the pair
    /// @param tokenY The address of the second token of the pair
    /// @param binStep The bin step in basis point of the pair
    /// @param ignored Whether to ignore (true) or not (false) the pair for routing
    function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external override onlyOwner {
        (IERC20 tokenA, IERC20 tokenB) = _sortTokens(tokenX, tokenY);

        LBPairInformation memory pairInformation = _lbPairsInfo[tokenA][tokenB][binStep];
        if (address(pairInformation.LBPair) == address(0)) {
            revert LBFactory__LBPairDoesNotExist(tokenX, tokenY, binStep);
        }

        if (pairInformation.ignoredForRouting == ignored) revert LBFactory__LBPairIgnoredIsAlreadyInTheSameState();

        _lbPairsInfo[tokenA][tokenB][binStep].ignoredForRouting = ignored;

        emit LBPairIgnoredStateChanged(pairInformation.LBPair, ignored);
    }

    /// @notice Sets the preset parameters of a bin step
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The binStep is lower than the minimum bin step
    /// @param binStep The bin step in basis point, used to calculate the price
    /// @param baseFactor The base factor, used to calculate the base fee, baseFee = baseFactor * binStep
    /// @param filterPeriod The period where the accumulator value is untouched, prevent spam
    /// @param decayPeriod The period where the accumulator value is decayed, by the reduction factor
    /// @param reductionFactor The reduction factor, used to calculate the reduction of the accumulator
    /// @param variableFeeControl The variable fee control, used to control the variable fee, can be 0 to disable it
    /// @param protocolShare The share of the fees received by the protocol
    /// @param maxVolatilityAccumulator The max value of the volatility accumulator
    function setPreset(
        uint16 binStep,
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator,
        bool isOpen
    ) external override onlyOwner {
        if (binStep < _MIN_BIN_STEP) revert LBFactory__BinStepTooLow(binStep);

        bytes32 preset = bytes32(0)
            .setStaticFeeParameters(
                baseFactor,
                filterPeriod,
                decayPeriod,
                reductionFactor,
                variableFeeControl,
                protocolShare,
                maxVolatilityAccumulator
            );

        if (isOpen) {
            preset = preset.setBool(true, _OFFSET_IS_PRESET_OPEN);
        }

        _presets.set(binStep, uint256(preset));

        emit PresetSet(
            binStep,
            baseFactor,
            filterPeriod,
            decayPeriod,
            reductionFactor,
            variableFeeControl,
            protocolShare,
            maxVolatilityAccumulator
        );

        emit PresetOpenStateChanged(binStep, isOpen);
    }

    /// @notice Sets only the protocol share for an existing preset.
    /// @dev Needs to be called by the owner.
    /// @param binStep The bin step of the preset
    /// @param protocolShare The share of the fees received by the protocol
    function setPresetProtocolShare(uint16 binStep, uint16 protocolShare) external override onlyOwner {
        if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);

        bytes32 preset = bytes32(_presets.get(binStep));

        uint16 baseFactor = preset.getBaseFactor();
        uint16 filterPeriod = preset.getFilterPeriod();
        uint16 decayPeriod = preset.getDecayPeriod();
        uint16 reductionFactor = preset.getReductionFactor();
        uint24 variableFeeControl = preset.getVariableFeeControl();
        uint24 maxVolatilityAccumulator = preset.getMaxVolatilityAccumulator();

        preset = preset.setStaticFeeParameters(
            baseFactor,
            filterPeriod,
            decayPeriod,
            reductionFactor,
            variableFeeControl,
            protocolShare,
            maxVolatilityAccumulator
        );

        _presets.set(binStep, uint256(preset));

        emit PresetSet(
            binStep,
            baseFactor,
            filterPeriod,
            decayPeriod,
            reductionFactor,
            variableFeeControl,
            protocolShare,
            maxVolatilityAccumulator
        );
    }

    /// @notice Sets if the preset is open or not to be used by users
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The binStep doesn't have a preset
    /// - The preset is already in the same state
    /// @param binStep The bin step in basis point, used to calculate the price
    /// @param isOpen Whether the preset is open or not
    function setPresetOpenState(uint16 binStep, bool isOpen) external override onlyOwner {
        if (!_presets.contains(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);

        bytes32 preset = bytes32(_presets.get(binStep));

        if (preset.decodeBool(_OFFSET_IS_PRESET_OPEN) == isOpen) {
            revert LBFactory__PresetOpenStateIsAlreadyInTheSameState();
        }

        _presets.set(binStep, uint256(preset.setBool(isOpen, _OFFSET_IS_PRESET_OPEN)));

        emit PresetOpenStateChanged(binStep, isOpen);
    }

    /// @notice Remove the preset linked to a binStep
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The binStep doesn't have a preset
    /// @param binStep The bin step to remove
    function removePreset(uint16 binStep) external override onlyOwner {
        if (!_presets.remove(binStep)) revert LBFactory__BinStepHasNoPreset(binStep);

        emit PresetRemoved(binStep);
    }

    /// @notice Function to set the fee parameter of a DLMM pool
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The pair doesn't exist
    /// @param tokenX The address of the first token
    /// @param tokenY The address of the second token
    /// @param binStep The bin step in basis point, used to calculate the price
    /// @param baseFactor The base factor, used to calculate the base fee, baseFee = baseFactor * binStep
    /// @param filterPeriod The period where the accumulator value is untouched, prevent spam
    /// @param decayPeriod The period where the accumulator value is decayed, by the reduction factor
    /// @param reductionFactor The reduction factor, used to calculate the reduction of the accumulator
    /// @param variableFeeControl The variable fee control, used to control the variable fee, can be 0 to disable it
    /// @param protocolShare The share of the fees received by the protocol
    /// @param maxVolatilityAccumulator The max value of volatility accumulator
    function setFeesParametersOnPair(
        IERC20 tokenX,
        IERC20 tokenY,
        uint16 binStep,
        uint16 baseFactor,
        uint16 filterPeriod,
        uint16 decayPeriod,
        uint16 reductionFactor,
        uint24 variableFeeControl,
        uint16 protocolShare,
        uint24 maxVolatilityAccumulator
    ) external override onlyOwner {
        IDLMMPool lbPair = _getLBPairInformation(tokenX, tokenY, binStep).LBPair;

        if (address(lbPair) == address(0)) revert LBFactory__LBPairNotCreated(tokenX, tokenY, binStep);

        lbPair.setStaticFeeParameters(
            baseFactor,
            filterPeriod,
            decayPeriod,
            reductionFactor,
            variableFeeControl,
            protocolShare,
            maxVolatilityAccumulator
        );
    }

    /// @notice Function to set the hooks parameters of a pair
    /// @dev Needs to be called by an address with the LB_HOOKS_MANAGER_ROLE
    /// Reverts if:
    /// - The pair doesn't exist
    /// - The hooks is `address(0)` or the hooks flags are all false
    /// @param tokenX The address of the first token
    /// @param tokenY The address of the second token
    /// @param binStep The bin step in basis point, used to calculate the price
    /// @param hooksParameters The hooks parameters
    /// @param onHooksSetData The data to pass to the onHooksSet function
    function setLBHooksParametersOnPair(
        IERC20 tokenX,
        IERC20 tokenY,
        uint16 binStep,
        bytes32 hooksParameters,
        bytes calldata onHooksSetData
    ) external override onlyRole(LB_HOOKS_MANAGER_ROLE) {
        if (Hooks.getHooks(hooksParameters) == address(0) || Hooks.getFlags(hooksParameters) == 0) {
            revert LBFactory__InvalidHooksParameters();
        }

        _setLBHooksParametersOnPair(tokenX, tokenY, binStep, hooksParameters, onHooksSetData);
    }

    /// @notice Function to remove the hooks contract from the pair
    /// @dev Needs to be called by an address with the LB_HOOKS_MANAGER_ROLE
    /// Reverts if:
    /// - The pair doesn't exist
    /// @param tokenX The address of the first token
    /// @param tokenY The address of the second token
    /// @param binStep The bin step in basis point, used to calculate the price
    function removeLBHooksOnPair(IERC20 tokenX, IERC20 tokenY, uint16 binStep)
        external
        override
        onlyRole(LB_HOOKS_MANAGER_ROLE)
    {
        _setLBHooksParametersOnPair(tokenX, tokenY, binStep, 0, new bytes(0));
    }

    /// @notice Function to set the recipient of the fees. This address needs to be able to receive ERC20s
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The feeRecipient is `address(0)`
    /// - The feeRecipient is the same as the current one
    /// @param feeRecipient The address of the recipient
    function setFeeRecipient(address feeRecipient) external override onlyOwner {
        _setFeeRecipient(feeRecipient);
    }

    /// @notice Function to set the flash loan fee
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The flashLoanFee is the same as the current one
    /// - The flashLoanFee is above the maximum flash loan fee
    /// @param flashLoanFee The value of the fee for flash loan
    function setFlashLoanFee(uint256 flashLoanFee) external override onlyOwner {
        uint256 oldFlashLoanFee = _flashLoanFee;

        if (oldFlashLoanFee == flashLoanFee) revert LBFactory__SameFlashLoanFee(flashLoanFee);
        if (flashLoanFee > _MAX_FLASHLOAN_FEE) {
            revert LBFactory__FlashLoanFeeAboveMax(flashLoanFee, _MAX_FLASHLOAN_FEE);
        }

        _flashLoanFee = flashLoanFee;
        emit FlashLoanFeeSet(oldFlashLoanFee, flashLoanFee);
    }

    /// @notice Function to add an asset to the whitelist of quote assets
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The quoteAsset is already whitelisted
    /// @param quoteAsset The quote asset (e.g: NATIVE, USDC...)
    function addQuoteAsset(IERC20 quoteAsset) external override onlyOwner {
        if (!_quoteAssetWhitelist.add(address(quoteAsset))) {
            revert LBFactory__QuoteAssetAlreadyWhitelisted(quoteAsset);
        }

        emit QuoteAssetAdded(quoteAsset);
    }

    /// @notice Function to remove an asset from the whitelist of quote assets
    /// @dev Needs to be called by the owner
    /// Reverts if:
    /// - The quoteAsset was not whitelisted
    /// @param quoteAsset The quote asset (e.g: NATIVE, USDC...)
    function removeQuoteAsset(IERC20 quoteAsset) external override onlyOwner {
        if (!_quoteAssetWhitelist.remove(address(quoteAsset))) revert LBFactory__QuoteAssetNotWhitelisted(quoteAsset);

        emit QuoteAssetRemoved(quoteAsset);
    }

    function _isPresetOpen(bytes32 preset) internal pure returns (bool) {
        return preset.decodeBool(_OFFSET_IS_PRESET_OPEN);
    }

    /// @notice Internal function to set the recipient of the fee
    /// @param feeRecipient The address of the recipient
    function _setFeeRecipient(address feeRecipient) internal {
        if (feeRecipient == address(0)) revert LBFactory__AddressZero();

        address oldFeeRecipient = _feeRecipient;
        if (oldFeeRecipient == feeRecipient) revert LBFactory__SameFeeRecipient(_feeRecipient);

        _feeRecipient = feeRecipient;
        emit FeeRecipientSet(oldFeeRecipient, feeRecipient);
    }

    /// @notice Function to force the decay of the volatility accumulator of a pair
    /// @dev Needs to be called by the owner
    /// @param pair The pair to force the decay
    function forceDecay(IDLMMPool pair) external override onlyOwner {
        pair.forceDecay();
    }

    function _checkOwnerOrVoter() internal view {
        if (msg.sender != owner() && msg.sender != _voter) revert DLMMFactory__NotOwnerOrVoter(msg.sender);
    }

    function _setPoolProtocolShare(address pool, uint16 protocolShare) internal {
        if (pool.code.length == 0) revert DLMMFactory__InvalidPool(pool);

        IDLMMPool dlmmPool = IDLMMPool(pool);
        if (address(dlmmPool.getFactory()) != address(this)) revert DLMMFactory__InvalidPool(pool);

        (
            uint16 baseFactor,
            uint16 filterPeriod,
            uint16 decayPeriod,
            uint16 reductionFactor,
            uint24 variableFeeControl,,
            uint24 maxVolatilityAccumulator
        ) = dlmmPool.getStaticFeeParameters();

        dlmmPool.setStaticFeeParameters(
            baseFactor,
            filterPeriod,
            decayPeriod,
            reductionFactor,
            variableFeeControl,
            protocolShare,
            maxVolatilityAccumulator
        );
    }

    /// @notice Returns the pool information if it exists,
    /// if not, then the address 0 is returned. The order doesn't matter
    /// @param tokenA The address of the first token of the pair
    /// @param tokenB The address of the second token of the pair
    /// @param binStep The bin step of the DLMM pool
    /// @return The pool information
    function _getLBPairInformation(IERC20 tokenA, IERC20 tokenB, uint256 binStep)
        private
        view
        returns (LBPairInformation memory)
    {
        (tokenA, tokenB) = _sortTokens(tokenA, tokenB);
        return _lbPairsInfo[tokenA][tokenB][binStep];
    }

    /// @notice Private view function to sort 2 tokens in ascending order
    /// @param tokenA The first token
    /// @param tokenB The second token
    /// @return The sorted first token
    /// @return The sorted second token
    function _sortTokens(IERC20 tokenA, IERC20 tokenB) private pure returns (IERC20, IERC20) {
        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
        return (tokenA, tokenB);
    }

    /// @notice Internal function to set a hooks contract to the pair
    /// @param tokenX The address of the first token
    /// @param tokenY The address of the second token
    /// @param binStep The bin step in basis point, used to calculate the price
    /// @param hooksParameters The hooks parameters
    /// @param onHooksSetData The data to pass to the onHooksSet function
    function _setLBHooksParametersOnPair(
        IERC20 tokenX,
        IERC20 tokenY,
        uint16 binStep,
        bytes32 hooksParameters,
        bytes memory onHooksSetData
    ) internal {
        IDLMMPool lbPair = _getLBPairInformation(tokenX, tokenY, binStep).LBPair;

        if (address(lbPair) == address(0)) revert LBFactory__LBPairNotCreated(tokenX, tokenY, binStep);
        if (lbPair.getLBHooksParameters() == hooksParameters) revert LBFactory__SameHooksParameters(hooksParameters);

        lbPair.setHooksParameters(hooksParameters, onHooksSetData);
    }

    /// @notice Returns whether the caller has the role or not, only the owner has the DEFAULT_ADMIN_ROLE
    /// @param role The role to check
    /// @param account The address to check
    /// @return Whether the account has the role or not
    function hasRole(bytes32 role, address account) public view override returns (bool) {
        if (role == DEFAULT_ADMIN_ROLE) return account == owner();
        return super.hasRole(role, account);
    }

    /// @notice Grants a role to an address, the DEFAULT_ADMIN_ROLE can not be granted
    /// @param role The role to grant
    /// @param account The address to grant the role to
    /// @return Whether the role has been granted or not
    function _grantRole(bytes32 role, address account) internal override returns (bool) {
        if (role == DEFAULT_ADMIN_ROLE) revert LBFactory__CannotGrantDefaultAdminRole();
        return super._grantRole(role, account);
    }
}
Chain explorer1953msChain node73ms