SteakhouseFactoryCore

0x8072544ba5a0fe5b960d09f70d6c70cbbf693990

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
16,129 bytes
ABI entries
43
25 read · 9 write
License
none

Contract information

Address
0x8072544ba5a0fe5b960d09f70d6c70cbbf693990
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x647e140ba4…b391d648e0

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (25)

FEE_SPLIT_BPS()uint16
MAX_LP_FEE()uint24
MAX_TARGET_MCAP()uint256
MIN_LP_FEE()uint24
MIN_TARGET_MCAP()uint256
TOTAL_SUPPLY()uint256
allTokens(uint256)address
antiPvpRegistry()address
config()address
getCurrentPositionStatus(bytes32)int24, bool[5], uint256[5], bool[5], bool
getFeeClaimMode(bytes32)uint8
getFeeSplit(bytes32)address[5], uint16[5], uint8
getLaunchInfo(bytes32)address, tuple, address, address, uint256, uint256, string, string
getPoolFlags(bytes32)uint256[5], bool[5], bool[5]
getPoolIdForPosition(uint256)bytes32
getPoolState(bytes32)address, address, uint256, uint256, uint256, uint256, bool, bool
helper()address
launchByPoolId(bytes32)tuple
launchByToken(address)tuple
owner()address
poolManager()address
poolStates(bytes32)address, address, uint256, uint256, uint256, uint256, bool, bool
positionManager()address
tokenIdToPoolId(uint256)bytes32
totalLaunches()uint256

Events (3)

AntiPVPRegistrySetOwnershipTransferredTokenLaunched

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract SteakhouseConfig",
        "name": "_config",
        "type": "address"
      },
      {
        "internalType": "contract IPoolManager",
        "name": "_pool_manager",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_position_manager",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_owner",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AntiPVPDuplicateCombo",
    "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": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "registry",
        "type": "address"
      }
    ],
    "name": "AntiPVPRegistrySet",
    "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": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "targetMcapETH",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      }
    ],
    "name": "TokenLaunched",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "FEE_SPLIT_BPS",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_LP_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_TARGET_MCAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_LP_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_TARGET_MCAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOTAL_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "addRecycledETH",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "allTokens",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "antiPvpRegistry",
    "outputs": [
      {
        "internalType": "contract IAntiPVPRegistry",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "clearRecycledETH",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "config",
    "outputs": [
      {
        "internalType": "contract SteakhouseConfig",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "address",
            "name": "beneficiary",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "targetMcapETH",
            "type": "uint256"
          },
          {
            "internalType": "uint24",
            "name": "lpFeeBps",
            "type": "uint24"
          },
          {
            "internalType": "enum SteakhouseFactoryCore.FeeClaimMode",
            "name": "feeClaimMode",
            "type": "uint8"
          },
          {
            "components": [
              {
                "internalType": "address[5]",
                "name": "wallets",
                "type": "address[5]"
              },
              {
                "internalType": "uint16[5]",
                "name": "percentages",
                "type": "uint16[5]"
              },
              {
                "internalType": "uint8",
                "name": "count",
                "type": "uint8"
              }
            ],
            "internalType": "struct SteakhouseFactoryCore.FeeSplit",
            "name": "feeSplit",
            "type": "tuple"
          },
          {
            "internalType": "bool",
            "name": "isBranded",
            "type": "bool"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct SteakhouseFactoryCore.CreateParams",
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "createLaunch",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getCurrentPositionStatus",
    "outputs": [
      {
        "internalType": "int24",
        "name": "currentTick",
        "type": "int24"
      },
      {
        "internalType": "bool[5]",
        "name": "activePositions",
        "type": "bool[5]"
      },
      {
        "internalType": "uint256[5]",
        "name": "positionTokenIds",
        "type": "uint256[5]"
      },
      {
        "internalType": "bool[5]",
        "name": "collectiblePositions",
        "type": "bool[5]"
      },
      {
        "internalType": "bool",
        "name": "graduated",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getFeeClaimMode",
    "outputs": [
      {
        "internalType": "enum SteakhouseFactoryCore.FeeClaimMode",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getFeeSplit",
    "outputs": [
      {
        "internalType": "address[5]",
        "name": "wallets",
        "type": "address[5]"
      },
      {
        "internalType": "uint16[5]",
        "name": "percentages",
        "type": "uint16[5]"
      },
      {
        "internalType": "uint8",
        "name": "count",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getLaunchInfo",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "poolKey",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "targetMcapETH",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "createdAt",
        "type": "uint256"
      },
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getPoolFlags",
    "outputs": [
      {
        "internalType": "uint256[5]",
        "name": "positionTokenIds",
        "type": "uint256[5]"
      },
      {
        "internalType": "bool[5]",
        "name": "positionMinted",
        "type": "bool[5]"
      },
      {
        "internalType": "bool[5]",
        "name": "positionRetired",
        "type": "bool[5]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "getPoolIdForPosition",
    "outputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getPoolState",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "startingMCAP",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "graduationMCAP",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalSupply",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "recycledETH",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "activated",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "graduated",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "helper",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "launchByPoolId",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "beneficiary",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "PoolId",
            "name": "poolId",
            "type": "bytes32"
          },
          {
            "components": [
              {
                "internalType": "Currency",
                "name": "currency0",
                "type": "address"
              },
              {
                "internalType": "Currency",
                "name": "currency1",
                "type": "address"
              },
              {
                "internalType": "uint24",
                "name": "fee",
                "type": "uint24"
              },
              {
                "internalType": "int24",
                "name": "tickSpacing",
                "type": "int24"
              },
              {
                "internalType": "contract IHooks",
                "name": "hooks",
                "type": "address"
              }
            ],
            "internalType": "struct PoolKey",
            "name": "poolKey",
            "type": "tuple"
          },
          {
            "internalType": "uint256",
            "name": "targetMcapETH",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "createdAt",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "createdBlock",
            "type": "uint256"
          },
          {
            "internalType": "uint24",
            "name": "lpFeeBps",
            "type": "uint24"
          },
          {
            "internalType": "enum SteakhouseFactoryCore.FeeClaimMode",
            "name": "feeClaimMode",
            "type": "uint8"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "components": [
              {
                "internalType": "address[5]",
                "name": "wallets",
                "type": "address[5]"
              },
              {
                "internalType": "uint16[5]",
                "name": "percentages",
                "type": "uint16[5]"
              },
              {
                "internalType": "uint8",
                "name": "count",
                "type": "uint8"
              }
            ],
            "internalType": "struct SteakhouseFactoryCore.FeeSplit",
            "name": "feeSplit",
            "type": "tuple"
          }
        ],
        "internalType": "struct SteakhouseFactoryCore.LaunchInfo",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "launchByToken",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "beneficiary",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "PoolId",
            "name": "poolId",
            "type": "bytes32"
          },
          {
            "components": [
              {
                "internalType": "Currency",
                "name": "currency0",
                "type": "address"
              },
              {
                "internalType": "Currency",
                "name": "currency1",
                "type": "address"
              },
              {
                "internalType": "uint24",
                "name": "fee",
                "type": "uint24"
              },
              {
                "internalType": "int24",
                "name": "tickSpacing",
                "type": "int24"
              },
              {
                "internalType": "contract IHooks",
                "name": "hooks",
                "type": "address"
              }
            ],
            "internalType": "struct PoolKey",
            "name": "poolKey",
            "type": "tuple"
          },
          {
            "internalType": "uint256",
            "name": "targetMcapETH",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "createdAt",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "createdBlock",
            "type": "uint256"
          },
          {
            "internalType": "uint24",
            "name": "lpFeeBps",
            "type": "uint24"
          },
          {
            "internalType": "enum SteakhouseFactoryCore.FeeClaimMode",
            "name": "feeClaimMode",
            "type": "uint8"
          },
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "components": [
              {
                "internalType": "address[5]",
                "name": "wallets",
                "type": "address[5]"
              },
              {
                "internalType": "uint16[5]",
                "name": "percentages",
                "type": "uint16[5]"
              },
              {
                "internalType": "uint8",
                "name": "count",
                "type": "uint8"
              }
            ],
            "internalType": "struct SteakhouseFactoryCore.FeeSplit",
            "name": "feeSplit",
            "type": "tuple"
          }
        ],
        "internalType": "struct SteakhouseFactoryCore.LaunchInfo",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "positionIndex",
        "type": "uint256"
      }
    ],
    "name": "markPositionRetired",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "poolStates",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "startingMCAP",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "graduationMCAP",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalSupply",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "recycledETH",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "activated",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "graduated",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionManager",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_registry",
        "type": "address"
      }
    ],
    "name": "setAntiPVPRegistry",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_helper",
        "type": "address"
      }
    ],
    "name": "setHelper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "positionIndex",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "storePositionTokenId",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "tokenIdToPoolId",
    "outputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalLaunches",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";
import {Currency} from "v4-core/src/types/Currency.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
import {StateLibrary} from "v4-core/src/libraries/StateLibrary.sol";

import {TokenSteakHouse} from "./TokenSteakHouse.sol";
import {Token} from "./Token.sol";
import {SteakhouseConfig} from "./SteakhouseConfig.sol";
import {PriceMath} from "../libraries/PriceMath.sol";

interface ISteakhouseHelperMinter {
    function mintAllDirectPositions(PoolId poolId) external payable;
}

/// @notice Minimal view onto the AntiPVP registry (same contract the ADVANCED
///         stack uses). Kept as an interface so DIRECT does not cross-import the
///         advanced-context contract.
interface IAntiPVPRegistry {
    function isComboAvailable(string calldata name, string calldata symbol) external view returns (bool);
    function registerCombo(string calldata name, string calldata symbol) external;
}

contract SteakhouseFactoryCore is Ownable, ReentrancyGuard {
    using PoolIdLibrary for PoolKey;
    using StateLibrary for IPoolManager;

    uint256 public constant TOTAL_SUPPLY = 1_000_000_000 * 1e18;
    uint256 public constant MIN_TARGET_MCAP = 1 ether;
    uint256 public constant MAX_TARGET_MCAP = 10 ether;

    /// @notice V4 fee unit: 1 = 0.0001%. 12000 = 1.2%, 100000 = 10%.
    uint24 public constant MIN_LP_FEE = 12000;
    uint24 public constant MAX_LP_FEE = 100000;
    uint16 public constant FEE_SPLIT_BPS = 10000;

    /// @notice Which currency LP-fee recipients are paid in. Chosen once at launch,
    ///         applies to every future claim on the pool.
    /// BOTH (0)      — pay out ETH and tokens as accrued.
    /// ETH_ONLY (1)  — sell accrued tokens to ETH on claim, pay only ETH.
    /// TOKEN_ONLY (2)— buy tokens with accrued ETH on claim, pay only tokens.
    enum FeeClaimMode { BOTH, ETH_ONLY, TOKEN_ONLY }

    struct FeeSplit {
        address[5] wallets;
        uint16[5] percentages;
        uint8 count;
    }

    struct LaunchInfo {
        address token;
        address beneficiary;
        address creator;
        PoolId poolId;
        PoolKey poolKey;
        uint256 targetMcapETH;
        uint256 createdAt;
        uint256 createdBlock;
        uint24 lpFeeBps;
        FeeClaimMode feeClaimMode;
        string name;
        string symbol;
        FeeSplit feeSplit;
    }

    struct CreateParams {
        string name;
        string symbol;
        address beneficiary;
        uint256 targetMcapETH;
        uint24 lpFeeBps;
        FeeClaimMode feeClaimMode;
        FeeSplit feeSplit;
        bool isBranded;
        bytes32 salt;
    }

    struct PoolState {
        address token;
        address beneficiary;
        uint256 startingMCAP;
        uint256 graduationMCAP;
        uint256 totalSupply;
        uint256[5] positionTokenIds;
        bool[5] positionMinted;
        bool[5] positionRetired;
        uint256 recycledETH;
        bool activated;
        bool graduated;
    }

    SteakhouseConfig public immutable config;
    IPoolManager public immutable poolManager;
    address public immutable positionManager;
    address public helper;

    uint256 public totalLaunches;
    mapping(address => LaunchInfo) internal _launchByToken;
    mapping(PoolId => LaunchInfo) internal _launchByPoolId;
    mapping(PoolId => PoolState) public poolStates;
    mapping(uint256 => PoolId) public tokenIdToPoolId;
    address[] public allTokens;

    event TokenLaunched(
        address indexed token,
        address indexed beneficiary,
        address indexed creator,
        PoolId poolId,
        uint256 targetMcapETH,
        uint160 sqrtPriceX96,
        string name,
        string symbol
    );

    constructor(
        SteakhouseConfig _config,
        IPoolManager _pool_manager,
        address _position_manager,
        address _owner
    ) Ownable(_owner) {
        config = _config;
        poolManager = _pool_manager;
        positionManager = _position_manager;
    }

    /// @notice AntiPVP registry (duplicate name+symbol cooldown). Optional: when
    ///         address(0), no anti-PVP check is applied (parity with pre-existing
    ///         behavior). When set, createLaunch rejects a name+symbol combo that
    ///         is still within its cooldown and registers the combo on success.
    ///         The registry must be owned by THIS core so registerCombo succeeds.
    IAntiPVPRegistry public antiPvpRegistry;

    event AntiPVPRegistrySet(address indexed registry);
    error AntiPVPDuplicateCombo();

    function setHelper(address _helper) external onlyOwner {
        helper = _helper;
    }

    function setAntiPVPRegistry(address _registry) external onlyOwner {
        antiPvpRegistry = IAntiPVPRegistry(_registry);
        emit AntiPVPRegistrySet(_registry);
    }

    function createLaunch(CreateParams calldata params)
        external
        payable
        nonReentrant
        returns (address token, PoolId poolId)
    {
        if (config.paused()) revert("Paused");

        uint256 minBootstrap = config.MIN_BOOTSTRAP_ETH();
        uint256 unbranded_fee = 0.001 ether;
        uint256 vanity_fee = 0.0005 ether;
        uint256 bootstrapAmount;
        uint256 expectedValue;
        bytes32 salt;

        require(bytes(params.name).length > 0 && bytes(params.name).length <= 32, "Invalid name");
        require(bytes(params.symbol).length > 0 && bytes(params.symbol).length <= 10, "Invalid symbol");
        require(params.beneficiary != address(0), "Zero beneficiary");

        // AntiPVP: reject a name+symbol combo still within cooldown (if enabled).
        if (address(antiPvpRegistry) != address(0)) {
            if (!antiPvpRegistry.isComboAvailable(params.name, params.symbol)) {
                revert AntiPVPDuplicateCombo();
            }
        }
        require(params.targetMcapETH >= MIN_TARGET_MCAP && params.targetMcapETH <= MAX_TARGET_MCAP, "Invalid MCAP");
        require(params.lpFeeBps >= MIN_LP_FEE && params.lpFeeBps <= MAX_LP_FEE, "Invalid LP fee");
        require(uint8(params.feeClaimMode) <= uint8(FeeClaimMode.TOKEN_ONLY), "Invalid claim mode");
        _validateFeeSplit(params.feeSplit);

        // Calculate expected value based on branded/unbranded + vanity
        expectedValue = minBootstrap;
        if (!params.isBranded) {
            expectedValue += unbranded_fee;
        }
        if (params.salt != bytes32(0)) {
            expectedValue += vanity_fee;
        }
        require(msg.value == expectedValue, "Exact payment required");

        // Send fees to treasury
        uint256 feesForTreasury = 0;
        if (!params.isBranded) {
            feesForTreasury += unbranded_fee;
        }
        if (params.salt != bytes32(0)) {
            feesForTreasury += vanity_fee;
        }
        if (feesForTreasury > 0) {
            (bool ok, ) = config.treasury().call{value: feesForTreasury}("");
            require(ok, "Treasury transfer failed");
        }

        bootstrapAmount = minBootstrap;

        salt = _tokenSalt(params.salt, msg.sender, params.name, params.symbol);
        token = _deployToken(params, salt);

        PoolKey memory key = _createPoolKey(token, params.lpFeeBps);
        uint160 sqrtPriceX96 = PriceMath.calculateSqrtPrice(params.targetMcapETH, TOTAL_SUPPLY, key.tickSpacing);
        poolId = key.toId();

        poolManager.initialize(key, sqrtPriceX96);

        PoolState memory poolState = PoolState({
            token: token,
            beneficiary: params.beneficiary,
            startingMCAP: params.targetMcapETH,
            graduationMCAP: params.targetMcapETH * config.POSITION_MCAP_MULTIPLIER(),
            totalSupply: TOTAL_SUPPLY,
            positionTokenIds: [uint256(0), 0, 0, 0, 0],
            positionMinted: [false, false, false, false, false],
            positionRetired: [false, false, false, false, false],
            recycledETH: bootstrapAmount,
            activated: true,
            graduated: false
        });
        poolStates[poolId] = poolState;

        LaunchInfo memory info = LaunchInfo({
            token: token,
            beneficiary: params.beneficiary,
            creator: msg.sender,
            poolId: poolId,
            poolKey: key,
            targetMcapETH: params.targetMcapETH,
            createdAt: block.timestamp,
            createdBlock: block.number,
            lpFeeBps: params.lpFeeBps,
            feeClaimMode: params.feeClaimMode,
            name: params.name,
            symbol: params.symbol,
            feeSplit: params.feeSplit
        });

        _launchByToken[token] = info;
        _launchByPoolId[poolId] = info;
        allTokens.push(token);
        totalLaunches++;

        emit TokenLaunched(
            token,
            params.beneficiary,
            msg.sender,
            poolId,
            params.targetMcapETH,
            sqrtPriceX96,
            params.name,
            params.symbol
        );

        // AntiPVP: record this name+symbol so a duplicate can't relaunch until
        // the cooldown elapses (if enabled). Registry must be owned by this core.
        if (address(antiPvpRegistry) != address(0)) {
            antiPvpRegistry.registerCombo(params.name, params.symbol);
        }

        // Atomic liquidity bootstrap: hand the full supply to the helper and mint
        // all 5 positions so the token is instantly tradeable after createLaunch.
        require(helper != address(0), "Helper unset");
        TokenSteakHouse(token).transfer(helper, TOTAL_SUPPLY);
        ISteakhouseHelperMinter(helper).mintAllDirectPositions(poolId);

        return (token, poolId);
    }

    function _createPoolKey(address token, uint24 lpFeeBps) internal pure returns (PoolKey memory) {
        Currency currency0 = Currency.wrap(address(0));
        Currency currency1 = Currency.wrap(token);

        return PoolKey({
            currency0: currency0,
            currency1: currency1,
            fee: lpFeeBps,
            tickSpacing: 200,
            hooks: IHooks(address(0))
        });
    }

    function _tokenSalt(
        bytes32 userSalt,
        address creator,
        string calldata name,
        string calldata symbol
    ) internal view returns (bytes32) {
        return keccak256(abi.encode(
            "STEAKHOUSE_TOKEN_V1",
            block.chainid,
            address(this),
            creator,
            totalLaunches,
            name,
            symbol,
            userSalt
        ));
    }

    function _deployToken(CreateParams calldata params, bytes32 salt) internal returns (address token) {
        bytes memory code = params.isBranded
            ? type(TokenSteakHouse).creationCode
            : type(Token).creationCode;
        bytes memory args = abi.encode(params.name, params.symbol, address(this), params.beneficiary);
        bytes memory bytecode = abi.encodePacked(code, args);

        assembly {
            token := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
        }
        require(token != address(0), "Invalid salt");
    }

    function _validateFeeSplit(FeeSplit calldata split) internal pure {
        if (split.count == 0) {
            for (uint256 i = 0; i < 5; i++) {
                require(split.wallets[i] == address(0) && split.percentages[i] == 0, "Fee split not clean");
            }
            return;
        }
        require(split.count <= 5, "Too many fee recipients");

        uint256 total;
        for (uint256 i = 0; i < split.count; i++) {
            require(split.wallets[i] != address(0), "Zero fee wallet");
            require(split.percentages[i] > 0, "Zero fee pct");
            total += split.percentages[i];
        }
        for (uint256 i = split.count; i < 5; i++) {
            require(split.wallets[i] == address(0), "Dirty fee wallet");
            require(split.percentages[i] == 0, "Dirty fee pct");
        }
        require(total == FEE_SPLIT_BPS, "Fee split != 100%");
    }

    function launchByToken(address token) external view returns (LaunchInfo memory) {
        return _launchByToken[token];
    }

    function launchByPoolId(PoolId poolId) external view returns (LaunchInfo memory) {
        return _launchByPoolId[poolId];
    }

    function getPoolState(PoolId poolId) external view returns (
        address token,
        address beneficiary,
        uint256 startingMCAP,
        uint256 graduationMCAP,
        uint256 totalSupply,
        uint256 recycledETH,
        bool activated,
        bool graduated
    ) {
        PoolState storage state = poolStates[poolId];
        return (
            state.token,
            state.beneficiary,
            state.startingMCAP,
            state.graduationMCAP,
            state.totalSupply,
            state.recycledETH,
            state.activated,
            state.graduated
        );
    }

    function getPoolFlags(PoolId poolId) external view returns (
        uint256[5] memory positionTokenIds,
        bool[5] memory positionMinted,
        bool[5] memory positionRetired
    ) {
        PoolState storage state = poolStates[poolId];
        return (state.positionTokenIds, state.positionMinted, state.positionRetired);
    }

    /**
     * @notice Returns the current direct-pool position state for display and fee collection.
     * @dev Direct pools use five overlapping ranges, so more than one position can
     *      be active at the current tick. Fee collection should use
     *      collectiblePositions, not activePositions: inactive positions may still
     *      hold fees earned before the price moved.
     * @param poolId The pool to inspect
     * @return currentTick Current Uniswap V4 pool tick
     * @return activePositions Positions whose range currently contains currentTick
     * @return positionTokenIds Position-manager NFT IDs for each direct position
     * @return collectiblePositions Minted positions that have not been retired
     * @return graduated Whether the direct pool has graduated
     */
    function getCurrentPositionStatus(PoolId poolId)
        external
        view
        returns (
            int24 currentTick,
            bool[5] memory activePositions,
            uint256[5] memory positionTokenIds,
            bool[5] memory collectiblePositions,
            bool graduated
        )
    {
        LaunchInfo storage info = _launchByPoolId[poolId];
        require(info.token != address(0), "Launch not found");

        PoolState storage state = poolStates[poolId];
        (, currentTick,,) = poolManager.getSlot0(poolId);

        (int24[5] memory tickLowers, int24[5] memory tickUppers,) =
            PriceMath.calculatePositionRanges(info.targetMcapETH, state.totalSupply, info.poolKey.tickSpacing);

        positionTokenIds = state.positionTokenIds;
        graduated = state.graduated;

        for (uint256 i = 0; i < 5; i++) {
            collectiblePositions[i] = state.positionMinted[i] && !state.positionRetired[i];
            activePositions[i] = collectiblePositions[i]
                && currentTick >= tickLowers[i]
                && currentTick < tickUppers[i];
        }
    }

    function getLaunchInfo(PoolId poolId) external view returns (
        address token,
        PoolKey memory poolKey,
        address beneficiary,
        address creator,
        uint256 targetMcapETH,
        uint256 createdAt,
        string memory name,
        string memory symbol
    ) {
        LaunchInfo storage info = _launchByPoolId[poolId];
        return (
            info.token,
            info.poolKey,
            info.beneficiary,
            info.creator,
            info.targetMcapETH,
            info.createdAt,
            info.name,
            info.symbol
        );
    }

    function getPoolIdForPosition(uint256 tokenId) external view returns (PoolId) {
        return tokenIdToPoolId[tokenId];
    }

    function getFeeSplit(PoolId poolId)
        external
        view
        returns (address[5] memory wallets, uint16[5] memory percentages, uint8 count)
    {
        FeeSplit storage split = _launchByPoolId[poolId].feeSplit;
        wallets = split.wallets;
        percentages = split.percentages;
        count = split.count;
    }

    /// @notice Per-launch fee-claim mode, chosen at createLaunch and immutable afterwards.
    function getFeeClaimMode(PoolId poolId) external view returns (FeeClaimMode) {
        return _launchByPoolId[poolId].feeClaimMode;
    }

    function clearRecycledETH(PoolId poolId) external {
        require(msg.sender == helper, "Only helper");
        uint256 amount = poolStates[poolId].recycledETH;
        poolStates[poolId].recycledETH = 0;
        if (amount > 0) {
            (bool success,) = helper.call{value: amount}("");
            require(success, "ETH transfer failed");
        }
    }

    function addRecycledETH(PoolId poolId, uint256 amount) external {
        require(msg.sender == helper, "Only helper");
        poolStates[poolId].recycledETH += amount;
    }

    function storePositionTokenId(PoolId poolId, uint256 positionIndex, uint256 tokenId) external {
        require(msg.sender == helper, "Only helper");
        poolStates[poolId].positionTokenIds[positionIndex] = tokenId;
        poolStates[poolId].positionMinted[positionIndex] = true;
        tokenIdToPoolId[tokenId] = poolId;
    }

    function markPositionRetired(PoolId poolId, uint256 positionIndex) external {
        require(msg.sender == helper, "Only helper");
        poolStates[poolId].positionRetired[positionIndex] = true;
    }

    receive() external payable {}
}
Chain explorer1737msChain node81ms