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
- Creator
- 0x7B7DE29bc1…C6b0DaB694
- 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