Launchpad

0xf04aa0c4fe4c4dbe4aa60d007206f8046fa4e0b2

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
21,481 bytes
ABI entries
105
41 read · 16 write
License
gnu_gpl_v2

Contract information

Address
0xf04aa0c4fe4c4dbe4aa60d007206f8046fa4e0b2
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0xc0a8a2ef4f…4a72a97b4a

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (41)

BPS_DENOMINATOR()uint16
CREATE_TOKEN_AUTHORIZATION_TYPEHASH()bytes32
CREATION_FEE()uint256
DEAD()address
DEFAULT_ANTI_SNIPE_DURATION()uint32
EIP712_DOMAIN_TYPEHASH()bytes32
EIP712_NAME_HASH()bytes32
EIP712_VERSION_HASH()bytes32
GRADUATION_MARKET_CAP()uint256
INITIAL_MARKET_CAP()uint256
TOTAL_SUPPLY()uint256
WETH()address
accruedCreationFees()uint256
antiSnipeDuration()uint32
antiSnipeEnabled()bool
antiSnipeMaxBuyBps()uint16
authorizationDigest(address, tuple)bytes32
authorizationSigner()address
creationPaused()bool
creatorBlacklisted(address)bool
currentMarketCap(address)uint256
domainSeparator()bytes32
hasReachedGraduation(address)bool
hashTokenMetadata(tuple)bytes32
launches(address)address, address, uint256, uint160, uint256, bool, bool
locker()address
maxInitialBuyEth()uint256
metadataRegistry()address
owner()address
pendingOwner()address
poolFee()uint24
positionManager()address
predictTokenAddress(bytes32)address
pricing()address
swapRouter()address
tickSpacing()int24
tokenDeployer()address
tokenMetadata(address)tuple
treasury()address
usedSalts(bytes32)bool
v3Factory()address

Events (20)

AntiSnipeMaxBuyBpsSetAuthorizationSignerSetCreationFeesWithdrawnCreationPausedSetCreatorBlacklistSetDefaultAntiSnipeConfigSetInitialTokenDistributionLaunchPoolSeededLaunchPriceConfiguredLaunchSettingsConfiguredMaxInitialBuyEthSetOwnershipTransferStartedOwnershipTransferredTokenAntiSnipeConfigSetTokenAuthorizationUsedTokenBoughtTokenCreatedTokenGraduatedTokenMaxDevInitialBuyEthSetTreasurySet

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "positionManager_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "swapRouter_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "v3Factory_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "owner_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "treasury_",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "poolFee_",
        "type": "uint24"
      },
      {
        "internalType": "uint256",
        "name": "maxInitialBuyEth_",
        "type": "uint256"
      },
      {
        "internalType": "uint16",
        "name": "antiSnipeMaxBuyBps_",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "creatorFeeShareBps_",
        "type": "uint16"
      },
      {
        "internalType": "address",
        "name": "authorizationSigner_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AlreadyGraduated",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AuthorizationExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CreationIsPaused",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CreatorIsBlacklisted",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EthTransferFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "GraduationNotReached",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientAccruedFees",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAuthorizationSignature",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidBps",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidFeeTier",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidInitialBuy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidLiquidityPosition",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPayment",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPeripheryConfiguration",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyPendingOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyTreasury",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      }
    ],
    "name": "PoolAlreadyExists",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PoolCreationMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PoolInitializationMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SaltAlreadyUsed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TokenCallFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UnknownLaunch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroTokenOutput",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "antiSnipeMaxBuyBps",
        "type": "uint16"
      }
    ],
    "name": "AntiSnipeMaxBuyBpsSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousSigner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newSigner",
        "type": "address"
      }
    ],
    "name": "AuthorizationSignerSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "CreationFeesWithdrawn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "CreationPausedSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "blacklisted",
        "type": "bool"
      }
    ],
    "name": "CreatorBlacklistSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "maxBuyBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint32",
        "name": "protectionDuration",
        "type": "uint32"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "DefaultAntiSnipeConfigSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "poolBalance",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "deadBalance",
        "type": "uint256"
      }
    ],
    "name": "InitialTokenDistribution",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tick",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "uint128",
        "name": "liquidity",
        "type": "uint128"
      }
    ],
    "name": "LaunchPoolSeeded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "startTick",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "actualInitialMarketCap",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "creationFeePaid",
        "type": "bool"
      }
    ],
    "name": "LaunchPriceConfigured",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "maxDevInitialBuyEth",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "maxBuyBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint32",
        "name": "protectionDuration",
        "type": "uint32"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "antiSnipeEnabled",
        "type": "bool"
      }
    ],
    "name": "LaunchSettingsConfigured",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "maxInitialBuyEth",
        "type": "uint256"
      }
    ],
    "name": "MaxInitialBuyEthSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "currentOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "pendingOwner",
        "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": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "maxBuyBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint32",
        "name": "protectionDuration",
        "type": "uint32"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "TokenAntiSnipeConfigSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creatorFeeReceiver",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bytes32",
        "name": "authorizationDigest",
        "type": "bytes32"
      }
    ],
    "name": "TokenAuthorizationUsed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "buyer",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethOffered",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethRefunded",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenAmountOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "creationFeeCharged",
        "type": "uint256"
      }
    ],
    "name": "TokenBought",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "positionTokenId",
        "type": "uint256"
      }
    ],
    "name": "TokenCreated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "marketCap",
        "type": "uint256"
      }
    ],
    "name": "TokenGraduated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "maxDevInitialBuyEth",
        "type": "uint256"
      }
    ],
    "name": "TokenMaxDevInitialBuyEthSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "treasury",
        "type": "address"
      }
    ],
    "name": "TreasurySet",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS_DENOMINATOR",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CREATE_TOKEN_AUTHORIZATION_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CREATION_FEE",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEAD",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ANTI_SNIPE_DURATION",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "EIP712_DOMAIN_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "EIP712_NAME_HASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "EIP712_VERSION_HASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "GRADUATION_MARKET_CAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "INITIAL_MARKET_CAP",
    "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": [],
    "name": "WETH",
    "outputs": [
      {
        "internalType": "contract IWETH",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "accruedCreationFees",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "antiSnipeDuration",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "antiSnipeEnabled",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "antiSnipeMaxBuyBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "components": [
          {
            "components": [
              {
                "internalType": "string",
                "name": "name",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "ticker",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "description",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "image",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "website",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "twitter",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "discord",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "telegram",
                "type": "string"
              }
            ],
            "internalType": "struct TokenMetadataInput",
            "name": "metadata",
            "type": "tuple"
          },
          {
            "internalType": "address",
            "name": "creatorFeeReceiver",
            "type": "address"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "initialBuyEth",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minTokensOut",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "authorizationDeadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct Launchpad.CreateTokenParams",
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "authorizationDigest",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "authorizationSigner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minTokensOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "buy",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amountOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "components": [
              {
                "internalType": "string",
                "name": "name",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "ticker",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "description",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "image",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "website",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "twitter",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "discord",
                "type": "string"
              },
              {
                "internalType": "string",
                "name": "telegram",
                "type": "string"
              }
            ],
            "internalType": "struct TokenMetadataInput",
            "name": "metadata",
            "type": "tuple"
          },
          {
            "internalType": "address",
            "name": "creatorFeeReceiver",
            "type": "address"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          },
          {
            "internalType": "uint256",
            "name": "initialBuyEth",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "minTokensOut",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "authorizationDeadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct Launchpad.CreateTokenParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "authorization",
        "type": "bytes"
      }
    ],
    "name": "createToken",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creationPaused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      }
    ],
    "name": "creatorBlacklisted",
    "outputs": [
      {
        "internalType": "bool",
        "name": "blacklisted",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "currentMarketCap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "domainSeparator",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "hasReachedGraduation",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "ticker",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "image",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "website",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "twitter",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "discord",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "telegram",
            "type": "string"
          }
        ],
        "internalType": "struct TokenMetadataInput",
        "name": "metadata",
        "type": "tuple"
      }
    ],
    "name": "hashTokenMetadata",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "launchToken",
        "type": "address"
      }
    ],
    "name": "launches",
    "outputs": [
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "positionTokenId",
        "type": "uint256"
      },
      {
        "internalType": "uint160",
        "name": "graduationSqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "uint256",
        "name": "maxDevInitialBuyEth",
        "type": "uint256"
      },
      {
        "internalType": "bool",
        "name": "creationFeePaid",
        "type": "bool"
      },
      {
        "internalType": "bool",
        "name": "graduated",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "locker",
    "outputs": [
      {
        "internalType": "contract LaunchLocker",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxInitialBuyEth",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "metadataRegistry",
    "outputs": [
      {
        "internalType": "contract LaunchMetadataRegistry",
        "name": "",
        "type": "address"
      }
    ],
    "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": [],
    "name": "poolFee",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionManager",
    "outputs": [
      {
        "internalType": "contract INonfungiblePositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "predictTokenAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pricing",
    "outputs": [
      {
        "internalType": "contract LaunchPricing",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "antiSnipeMaxBuyBps_",
        "type": "uint16"
      }
    ],
    "name": "setAntiSnipeMaxBuyBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "authorizationSigner_",
        "type": "address"
      }
    ],
    "name": "setAuthorizationSigner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "setCreationPaused",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "blacklisted",
        "type": "bool"
      }
    ],
    "name": "setCreatorBlacklisted",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "creatorFeeShareBps",
        "type": "uint16"
      }
    ],
    "name": "setCreatorFeeShareBps",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "maxBuyBps",
        "type": "uint16"
      },
      {
        "internalType": "uint32",
        "name": "protectionDuration",
        "type": "uint32"
      },
      {
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "setDefaultAntiSnipeConfig",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "maxInitialBuyEth_",
        "type": "uint256"
      }
    ],
    "name": "setMaxInitialBuyEth",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "maxBuyBps",
        "type": "uint16"
      },
      {
        "internalType": "uint32",
        "name": "protectionDuration",
        "type": "uint32"
      },
      {
        "internalType": "bool",
        "name": "enabled",
        "type": "bool"
      }
    ],
    "name": "setTokenAntiSnipeConfig",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "maxDevInitialBuyEth",
        "type": "uint256"
      }
    ],
    "name": "setTokenMaxDevInitialBuyEth",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "treasury_",
        "type": "address"
      }
    ],
    "name": "setTreasury",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "swapRouter",
    "outputs": [
      {
        "internalType": "contract IV3SwapRouter",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "syncGraduation",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "marketCap",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tickSpacing",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokenDeployer",
    "outputs": [
      {
        "internalType": "contract LaunchTokenDeployer",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "tokenMetadata",
    "outputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "ticker",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "image",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "website",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "twitter",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "discord",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "telegram",
            "type": "string"
          }
        ],
        "internalType": "struct TokenMetadataInput",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pendingOwner_",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "treasury",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "usedSalts",
    "outputs": [
      {
        "internalType": "bool",
        "name": "used",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "v3Factory",
    "outputs": [
      {
        "internalType": "contract IV3Factory",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address payable",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdrawCreationFees",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {
    INonfungiblePositionManager,
    IV3Factory,
    IV3PeripheryState,
    IV3Pool,
    IV3SwapRouter,
    IWETH,
    SafeTransferLib,
    TokenMetadataInput
} from "./LaunchpadInterfaces.sol";
import {LaunchLocker} from "./LaunchLocker.sol";
import {LaunchMetadataRegistry} from "./LaunchMetadataRegistry.sol";
import {LaunchPricing} from "./LaunchPricing.sol";
import {LaunchToken} from "./LaunchToken.sol";
import {LaunchTokenDeployer} from "./LaunchTokenDeployer.sol";

// ************************************************
//  _  _   __    __  ____  ____  ____     _  _  ____  _  _  ____ 
// / )( \ /  \  /  \(    \(  __)(    \   ( \/ )(  __)( \/ )(  __)
// ) __ ((  O )(  O )) D ( ) _)  ) D ( _ / \/ \ ) _) / \/ \ ) _) 
// \_)(_/ \__/  \__/(____/(____)(____/(_)\_)(_/(____)\_)(_/(____)
//
// Website: https://hooded.meme
// Author: SwapHood Team (kellkell)
//
// ************************************************

/// @notice Creates fixed-supply token launches backed from inception by a permanent one-sided V3 position.
/// @dev Compile with the optimizer enabled at 200 runs. This keeps runtime and initcode below EIP-170/EIP-3860.
contract Launchpad {
    using SafeTransferLib for address;

    error OnlyOwner();
    error OnlyPendingOwner();
    error OnlyTreasury();
    error InvalidAddress();
    error InvalidBps();
    error InvalidFeeTier();
    error InvalidPayment();
    error InvalidInitialBuy();
    error CreatorIsBlacklisted();
    error CreationIsPaused();
    error UnknownLaunch();
    error AlreadyGraduated();
    error GraduationNotReached();
    error InsufficientAccruedFees();
    error EthTransferFailed();
    error Reentrancy();
    error InvalidLiquidityPosition();
    error ZeroTokenOutput();
    error InvalidPeripheryConfiguration();
    error PoolAlreadyExists(address token, address pool);
    error PoolCreationMismatch();
    error PoolInitializationMismatch();
    error AuthorizationExpired();
    error InvalidAuthorizationSignature();
    error SaltAlreadyUsed();

    uint256 public constant TOTAL_SUPPLY = 1_000_000_000 ether;
    uint256 public constant INITIAL_MARKET_CAP = 2 ether;
    uint256 public constant GRADUATION_MARKET_CAP = 8 ether;
    uint256 public constant CREATION_FEE = 0.0005 ether;
    uint16 public constant BPS_DENOMINATOR = 10_000;
    uint32 public constant DEFAULT_ANTI_SNIPE_DURATION = 60 seconds;
    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256(
        "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
    );
    bytes32 public constant CREATE_TOKEN_AUTHORIZATION_TYPEHASH = keccak256(
        "CreateTokenAuthorization(address creator,address creatorFeeReceiver,bytes32 salt,bytes32 metadataHash,uint256 initialBuyEth,uint256 minTokensOut,uint256 deadline,uint256 authorizationDeadline)"
    );
    bytes32 public constant EIP712_NAME_HASH = keccak256("SwapHood Launchpad");
    bytes32 public constant EIP712_VERSION_HASH = keccak256("1");
    uint256 private constant SECP256K1N_DIV_2 =
        0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

    struct Launch {
        address creator;
        address pool;
        uint256 positionTokenId;
        uint160 graduationSqrtPriceX96;
        uint256 maxDevInitialBuyEth;
        bool creationFeePaid;
        bool graduated;
    }

    struct CreateTokenParams {
        TokenMetadataInput metadata;
        address creatorFeeReceiver;
        bytes32 salt;
        uint256 initialBuyEth;
        uint256 minTokensOut;
        uint256 deadline;
        uint256 authorizationDeadline;
    }

    struct CreatedLaunch {
        address token;
        address pool;
        uint256 positionTokenId;
        int24 startTick;
        uint160 startSqrtPriceX96;
        uint160 graduationSqrtPriceX96;
        uint128 seededLiquidity;
    }

    INonfungiblePositionManager public immutable positionManager;
    IV3SwapRouter public immutable swapRouter;
    IV3Factory public immutable v3Factory;
    IWETH public immutable WETH;
    LaunchLocker public immutable locker;
    LaunchMetadataRegistry public immutable metadataRegistry;
    LaunchPricing public immutable pricing;
    LaunchTokenDeployer public immutable tokenDeployer;
    uint24 public immutable poolFee;
    int24 public immutable tickSpacing;

    address public owner;
    address public pendingOwner;
    address public treasury;
    address public authorizationSigner;
    uint256 public maxInitialBuyEth;
    uint16 public antiSnipeMaxBuyBps;
    uint32 public antiSnipeDuration;
    bool public antiSnipeEnabled;
    uint256 public accruedCreationFees;
    bool public creationPaused;

    mapping(address launchToken => Launch launch) public launches;
    mapping(address creator => bool blacklisted) public creatorBlacklisted;
    mapping(bytes32 salt => bool used) public usedSalts;

    uint256 private immutable _initialChainId;
    bytes32 private immutable _initialDomainSeparator;

    uint256 private _locked = 1;

    event TokenCreated(
        address indexed token,
        address indexed creator,
        address indexed pool,
        uint256 positionTokenId
    );
    event TokenAuthorizationUsed(
        address indexed token,
        bytes32 indexed salt,
        address indexed creatorFeeReceiver,
        bytes32 authorizationDigest
    );
    event LaunchPriceConfigured(
        address indexed token, int24 startTick, uint256 actualInitialMarketCap, bool creationFeePaid
    );
    event LaunchPoolSeeded(
        address indexed token,
        address indexed pool,
        uint160 sqrtPriceX96,
        int24 tick,
        uint128 liquidity
    );
    /// @notice Initial per-token limits that were configured before dynamic indexers
    /// can discover the token address through TokenCreated.
    event LaunchSettingsConfigured(
        address indexed token,
        uint256 maxDevInitialBuyEth,
        uint16 maxBuyBps,
        uint32 protectionDuration,
        bool antiSnipeEnabled
    );
    /// @notice Exact post-seeding balances for transfers that occur before TokenCreated.
    event InitialTokenDistribution(
        address indexed token, uint256 poolBalance, uint256 deadBalance
    );
    event TokenBought(
        address indexed token,
        address indexed buyer,
        uint256 ethOffered,
        uint256 ethRefunded,
        uint256 tokenAmountOut,
        uint256 creationFeeCharged
    );
    event TokenGraduated(address indexed token, uint256 marketCap);
    event CreatorBlacklistSet(address indexed creator, bool blacklisted);
    event MaxInitialBuyEthSet(uint256 maxInitialBuyEth);
    event TokenMaxDevInitialBuyEthSet(address indexed token, uint256 maxDevInitialBuyEth);
    event AntiSnipeMaxBuyBpsSet(uint16 antiSnipeMaxBuyBps);
    event DefaultAntiSnipeConfigSet(uint16 maxBuyBps, uint32 protectionDuration, bool enabled);
    event TokenAntiSnipeConfigSet(
        address indexed token, uint16 maxBuyBps, uint32 protectionDuration, bool enabled
    );
    event CreationPausedSet(bool paused);
    event TreasurySet(address indexed treasury);
    event AuthorizationSignerSet(address indexed previousSigner, address indexed newSigner);
    event CreationFeesWithdrawn(address indexed to, uint256 amount);
    event OwnershipTransferStarted(address indexed currentOwner, address indexed pendingOwner);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    modifier onlyOwner() {
        if (msg.sender != owner) revert OnlyOwner();
        _;
    }

    modifier nonReentrant() {
        if (_locked != 1) revert Reentrancy();
        _locked = 2;
        _;
        _locked = 1;
    }

    constructor(
        address positionManager_,
        address swapRouter_,
        address v3Factory_,
        address weth_,
        address owner_,
        address treasury_,
        uint24 poolFee_,
        uint256 maxInitialBuyEth_,
        uint16 antiSnipeMaxBuyBps_,
        uint16 creatorFeeShareBps_,
        address authorizationSigner_
    ) {
        if (
            positionManager_ == address(0) || swapRouter_ == address(0) || v3Factory_ == address(0)
                || weth_ == address(0) || owner_ == address(0) || treasury_ == address(0)
                || authorizationSigner_ == address(0)
        ) revert InvalidAddress();
        if (antiSnipeMaxBuyBps_ == 0 || antiSnipeMaxBuyBps_ > BPS_DENOMINATOR) revert InvalidBps();
        if (creatorFeeShareBps_ > BPS_DENOMINATOR) revert InvalidBps();

        int24 tickSpacing_ = IV3Factory(v3Factory_).feeAmountTickSpacing(poolFee_);
        (bool whitelistRequested, bool feeEnabled) =
            IV3Factory(v3Factory_).feeAmountTickSpacingExtraInfo(poolFee_);
        if (tickSpacing_ <= 0 || !feeEnabled || whitelistRequested) revert InvalidFeeTier();
        address poolDeployer_ = IV3Factory(v3Factory_).poolDeployer();
        if (
            poolDeployer_ == address(0) || IV3PeripheryState(positionManager_).deployer() != poolDeployer_
                || IV3PeripheryState(swapRouter_).deployer() != poolDeployer_
                || IV3PeripheryState(positionManager_).factory() != v3Factory_
                || IV3PeripheryState(positionManager_).WETH9() != weth_
                || IV3PeripheryState(swapRouter_).factory() != v3Factory_
                || IV3PeripheryState(swapRouter_).WETH9() != weth_
        ) revert InvalidPeripheryConfiguration();

        positionManager = INonfungiblePositionManager(positionManager_);
        swapRouter = IV3SwapRouter(swapRouter_);
        v3Factory = IV3Factory(v3Factory_);
        WETH = IWETH(weth_);
        owner = owner_;
        treasury = treasury_;
        authorizationSigner = authorizationSigner_;
        poolFee = poolFee_;
        tickSpacing = tickSpacing_;
        maxInitialBuyEth = maxInitialBuyEth_;
        antiSnipeMaxBuyBps = antiSnipeMaxBuyBps_;
        antiSnipeDuration = DEFAULT_ANTI_SNIPE_DURATION;
        antiSnipeEnabled = true;
        locker = new LaunchLocker(positionManager_, address(this), treasury_, creatorFeeShareBps_);
        metadataRegistry = new LaunchMetadataRegistry(address(this));
        pricing = new LaunchPricing();
        tokenDeployer = new LaunchTokenDeployer(address(this));
        _initialChainId = block.chainid;
        _initialDomainSeparator = _buildDomainSeparator();

        emit OwnershipTransferred(address(0), owner_);
        emit TreasurySet(treasury_);
        emit AuthorizationSignerSet(address(0), authorizationSigner_);
        emit MaxInitialBuyEthSet(maxInitialBuyEth_);
        emit AntiSnipeMaxBuyBpsSet(antiSnipeMaxBuyBps_);
        emit DefaultAntiSnipeConfigSet(antiSnipeMaxBuyBps_, DEFAULT_ANTI_SNIPE_DURATION, true);
    }

    receive() external payable {
        if (msg.sender != address(WETH)) revert InvalidPayment();
    }

    /// @notice Deploys an authorized CREATE3 token and atomically creates, initializes, and funds its V3 pool.
    /// @dev With no initial buy, send no ETH. The first later buy must use buy() and pays CREATION_FEE.
    function createToken(CreateTokenParams calldata params, bytes calldata authorization)
        external
        payable
        nonReentrant
        returns (address, address, uint256)
    {
        if (creationPaused) revert CreationIsPaused();
        if (creatorBlacklisted[msg.sender]) revert CreatorIsBlacklisted();
        if (params.creatorFeeReceiver == address(0)) revert InvalidAddress();
        if (params.authorizationDeadline < block.timestamp) revert AuthorizationExpired();
        if (usedSalts[params.salt]) revert SaltAlreadyUsed();
        metadataRegistry.validateMetadata(params.metadata);

        bytes32 digest = _authorizationDigest(msg.sender, params);
        if (_recoverSigner(digest, authorization) != authorizationSigner) {
            revert InvalidAuthorizationSignature();
        }

        if (params.initialBuyEth == 0) {
            if (msg.value != 0) revert InvalidPayment();
        } else {
            if (params.initialBuyEth > maxInitialBuyEth || params.deadline < block.timestamp) {
                revert InvalidInitialBuy();
            }
            if (msg.value != params.initialBuyEth + CREATION_FEE) revert InvalidPayment();
        }

        usedSalts[params.salt] = true;
        CreatedLaunch memory created = _deployAndSeed(
            params.metadata.name,
            params.metadata.ticker,
            params.salt,
            msg.sender,
            params.creatorFeeReceiver
        );
        _recordLaunch(created, msg.sender, params.initialBuyEth != 0);
        metadataRegistry.registerMetadata(created.token, params.metadata);
        emit TokenAuthorizationUsed(
            created.token, params.salt, params.creatorFeeReceiver, digest
        );

        if (params.initialBuyEth != 0) {
            accruedCreationFees += CREATION_FEE;
            LaunchToken(created.token).activateTrading();
            (uint256 amountOut, uint256 refund) = _executeBuy(
                created.token, params.initialBuyEth, msg.sender, params.minTokensOut, params.deadline
            );
            emit TokenBought(created.token, msg.sender, params.initialBuyEth, refund, amountOut, CREATION_FEE);
            _syncGraduation(created.token, false);
        }

        return (created.token, created.pool, created.positionTokenId);
    }

    /// @notice Buys through the configured V3 router. This is mandatory only for the first buy when launch fee is unpaid.
    function buy(address token, uint256 minTokensOut, uint256 deadline)
        external
        payable
        nonReentrant
        returns (uint256 amountOut)
    {
        Launch storage launch = launches[token];
        if (launch.creator == address(0)) revert UnknownLaunch();
        if (deadline < block.timestamp) revert InvalidInitialBuy();

        uint256 amountIn = msg.value;
        uint256 feeCharged;
        if (!launch.creationFeePaid) {
            if (amountIn <= CREATION_FEE) revert InvalidPayment();
            uint256 buyAmount = amountIn - CREATION_FEE;
            if (msg.sender == launch.creator && buyAmount > launch.maxDevInitialBuyEth) {
                revert InvalidInitialBuy();
            }
            launch.creationFeePaid = true;
            accruedCreationFees += CREATION_FEE;
            feeCharged = CREATION_FEE;
            amountIn = buyAmount;
            LaunchToken(token).activateTrading();
        } else if (amountIn == 0) {
            revert InvalidPayment();
        }

        uint256 refund;
        (amountOut, refund) = _executeBuy(token, amountIn, msg.sender, minTokensOut, deadline);
        emit TokenBought(token, msg.sender, amountIn, refund, amountOut, feeCharged);
        _syncGraduation(token, false);
    }

    function syncGraduation(address token) external returns (uint256 marketCap) {
        return _syncGraduation(token, true);
    }

    /// @notice True at the exact 8 ETH spot-market-cap threshold, independent of the persisted event state.
    function hasReachedGraduation(address token) public view returns (bool) {
        Launch storage launch = launches[token];
        if (launch.creator == address(0)) return false;
        if (launch.graduated || LaunchToken(token).graduated()) return true;
        (uint160 sqrtPriceX96,,,,,,) = IV3Pool(launch.pool).slot0();
        return token < address(WETH)
            ? sqrtPriceX96 >= launch.graduationSqrtPriceX96
            : sqrtPriceX96 <= launch.graduationSqrtPriceX96;
    }

    function currentMarketCap(address token) public view returns (uint256) {
        Launch storage launch = launches[token];
        if (launch.creator == address(0)) revert UnknownLaunch();
        (uint160 sqrtPriceX96,,,,,,) = IV3Pool(launch.pool).slot0();
        return pricing.marketCapFromSqrtPrice(token < address(WETH), sqrtPriceX96);
    }

    function tokenMetadata(address token) external view returns (TokenMetadataInput memory) {
        return metadataRegistry.tokenMetadata(token);
    }

    function predictTokenAddress(bytes32 salt) external view returns (address) {
        return tokenDeployer.predictTokenAddress(salt);
    }

    function domainSeparator() public view returns (bytes32) {
        return block.chainid == _initialChainId ? _initialDomainSeparator : _buildDomainSeparator();
    }

    function hashTokenMetadata(TokenMetadataInput calldata metadata) external pure returns (bytes32) {
        return keccak256(abi.encode(metadata));
    }

    function authorizationDigest(address creator, CreateTokenParams calldata params)
        external
        view
        returns (bytes32)
    {
        return _authorizationDigest(creator, params);
    }

    function setCreatorBlacklisted(address creator, bool blacklisted) external onlyOwner {
        creatorBlacklisted[creator] = blacklisted;
        emit CreatorBlacklistSet(creator, blacklisted);
    }

    function setMaxInitialBuyEth(uint256 maxInitialBuyEth_) external onlyOwner {
        maxInitialBuyEth = maxInitialBuyEth_;
        emit MaxInitialBuyEthSet(maxInitialBuyEth_);
    }

    /// @notice Changes the deferred creator-buy cap for an existing launch.
    /// @dev A completed initial purchase cannot be changed retroactively; this affects launches not yet activated.
    function setTokenMaxDevInitialBuyEth(address token, uint256 maxDevInitialBuyEth) external onlyOwner {
        Launch storage launch = launches[token];
        if (launch.creator == address(0)) revert UnknownLaunch();
        launch.maxDevInitialBuyEth = maxDevInitialBuyEth;
        emit TokenMaxDevInitialBuyEthSet(token, maxDevInitialBuyEth);
    }

    /// @dev Applies to future launches; each deployed token snapshots its own limit.
    function setAntiSnipeMaxBuyBps(uint16 antiSnipeMaxBuyBps_) external onlyOwner {
        if (antiSnipeMaxBuyBps_ == 0 || antiSnipeMaxBuyBps_ > BPS_DENOMINATOR) revert InvalidBps();
        antiSnipeMaxBuyBps = antiSnipeMaxBuyBps_;
        emit AntiSnipeMaxBuyBpsSet(antiSnipeMaxBuyBps_);
        emit DefaultAntiSnipeConfigSet(antiSnipeMaxBuyBps_, antiSnipeDuration, antiSnipeEnabled);
    }

    function setDefaultAntiSnipeConfig(uint16 maxBuyBps, uint32 protectionDuration, bool enabled)
        external
        onlyOwner
    {
        _validateAntiSnipeConfig(maxBuyBps, protectionDuration, enabled);
        antiSnipeMaxBuyBps = maxBuyBps;
        antiSnipeDuration = protectionDuration;
        antiSnipeEnabled = enabled;
        emit AntiSnipeMaxBuyBpsSet(maxBuyBps);
        emit DefaultAntiSnipeConfigSet(maxBuyBps, protectionDuration, enabled);
    }

    function setTokenAntiSnipeConfig(
        address token,
        uint16 maxBuyBps,
        uint32 protectionDuration,
        bool enabled
    ) external onlyOwner {
        if (launches[token].creator == address(0)) revert UnknownLaunch();
        _validateAntiSnipeConfig(maxBuyBps, protectionDuration, enabled);
        LaunchToken(token).setAntiSnipeConfig(maxBuyBps, protectionDuration, enabled);
        emit TokenAntiSnipeConfigSet(token, maxBuyBps, protectionDuration, enabled);
    }

    function setCreatorFeeShareBps(uint16 creatorFeeShareBps) external onlyOwner {
        locker.setCreatorFeeShareBps(creatorFeeShareBps);
    }

    function setCreationPaused(bool paused) external onlyOwner {
        creationPaused = paused;
        emit CreationPausedSet(paused);
    }

    function setTreasury(address treasury_) external onlyOwner {
        if (treasury_ == address(0)) revert InvalidAddress();
        treasury = treasury_;
        locker.setTreasury(treasury_);
        emit TreasurySet(treasury_);
    }

    function setAuthorizationSigner(address authorizationSigner_) external onlyOwner {
        if (authorizationSigner_ == address(0)) revert InvalidAddress();
        address previousSigner = authorizationSigner;
        authorizationSigner = authorizationSigner_;
        emit AuthorizationSignerSet(previousSigner, authorizationSigner_);
    }

    function withdrawCreationFees(address payable to, uint256 amount) external nonReentrant {
        if (msg.sender != treasury) revert OnlyTreasury();
        if (to == address(0)) revert InvalidAddress();
        if (amount > accruedCreationFees) revert InsufficientAccruedFees();
        accruedCreationFees -= amount;
        _sendEth(to, amount);
        emit CreationFeesWithdrawn(to, amount);
    }

    function transferOwnership(address pendingOwner_) external onlyOwner {
        if (pendingOwner_ == address(0)) revert InvalidAddress();
        pendingOwner = pendingOwner_;
        emit OwnershipTransferStarted(owner, pendingOwner_);
    }

    function acceptOwnership() external {
        if (msg.sender != pendingOwner) revert OnlyPendingOwner();
        address previousOwner = owner;
        owner = msg.sender;
        pendingOwner = address(0);
        emit OwnershipTransferred(previousOwner, msg.sender);
    }

    function _executeBuy(
        address token,
        uint256 amountIn,
        address recipient,
        uint256 minTokensOut,
        uint256 deadline
    ) internal returns (uint256 amountOut, uint256 refund) {
        uint256 wethBalanceBefore = WETH.balanceOf(address(this));
        WETH.deposit{value: amountIn}();
        address(WETH).safeApprove(address(swapRouter), amountIn);

        amountOut = swapRouter.exactInputSingle(
            IV3SwapRouter.ExactInputSingleParams({
                tokenIn: address(WETH),
                tokenOut: token,
                fee: poolFee,
                recipient: recipient,
                deadline: deadline,
                amountIn: amountIn,
                amountOutMinimum: minTokensOut,
                sqrtPriceLimitX96: 0
            })
        );
        if (amountOut == 0) revert ZeroTokenOutput();
        address(WETH).safeApprove(address(swapRouter), 0);

        uint256 wethBalanceAfter = WETH.balanceOf(address(this));
        refund = wethBalanceAfter - wethBalanceBefore;
        if (refund != 0) {
            WETH.withdraw(refund);
            _sendEth(payable(recipient), refund);
        }
    }

    function _deployAndSeed(
        string memory name,
        string memory symbol,
        bytes32 salt,
        address creator,
        address creatorFeeReceiver
    )
        internal
        returns (CreatedLaunch memory created)
    {
        address expectedToken = tokenDeployer.predictTokenAddress(salt);
        address existingPool = v3Factory.getPool(expectedToken, address(WETH), poolFee);
        if (existingPool != address(0)) revert PoolAlreadyExists(expectedToken, existingPool);
        created.token = tokenDeployer.deployToken(salt, name, symbol);
        if (created.token != expectedToken) revert PoolCreationMismatch();

        bool tokenIsToken0 = created.token < address(WETH);
        (created.startTick, created.startSqrtPriceX96) =
            pricing.openingTickAndPrice(tokenIsToken0, tickSpacing, INITIAL_MARKET_CAP);
        created.graduationSqrtPriceX96 =
            pricing.marketCapSqrtPriceX96(tokenIsToken0, GRADUATION_MARKET_CAP);
        (created.pool, created.positionTokenId, created.seededLiquidity) = _createPoolAndLockPosition(
            created.token,
            tokenIsToken0,
            created.startTick,
            created.startSqrtPriceX96,
            creator,
            creatorFeeReceiver
        );
    }

    function _recordLaunch(CreatedLaunch memory created, address creator, bool feePaid) internal {
        launches[created.token] = Launch({
            creator: creator,
            pool: created.pool,
            positionTokenId: created.positionTokenId,
            graduationSqrtPriceX96: created.graduationSqrtPriceX96,
            maxDevInitialBuyEth: maxInitialBuyEth,
            creationFeePaid: feePaid,
            graduated: false
        });

        uint256 actualInitialMarketCap =
            pricing.marketCapFromSqrtPrice(created.token < address(WETH), created.startSqrtPriceX96);
        emit TokenCreated(created.token, creator, created.pool, created.positionTokenId);
        emit LaunchPriceConfigured(created.token, created.startTick, actualInitialMarketCap, feePaid);
        emit LaunchPoolSeeded(
            created.token,
            created.pool,
            created.startSqrtPriceX96,
            created.startTick,
            created.seededLiquidity
        );
        emit LaunchSettingsConfigured(
            created.token,
            maxInitialBuyEth,
            antiSnipeMaxBuyBps,
            antiSnipeDuration,
            antiSnipeEnabled
        );
        emit InitialTokenDistribution(
            created.token,
            LaunchToken(created.token).balanceOf(created.pool),
            LaunchToken(created.token).balanceOf(DEAD)
        );
    }

    function _createPoolAndLockPosition(
        address token,
        bool tokenIsToken0,
        int24 startTick,
        uint160 startSqrtPriceX96,
        address creator,
        address creatorFeeReceiver
    ) internal returns (address pool, uint256 positionTokenId, uint128 seededLiquidity) {
        address token0 = tokenIsToken0 ? token : address(WETH);
        address token1 = tokenIsToken0 ? address(WETH) : token;
        pool = _createAndInitializePool(token, token0, token1, startTick, startSqrtPriceX96);
        LaunchToken(token).configureLaunch(
            pool,
            address(locker),
            antiSnipeMaxBuyBps,
            antiSnipeDuration,
            antiSnipeEnabled
        );

        (positionTokenId, seededLiquidity) =
            _mintOneSidedPosition(token, tokenIsToken0, token0, token1, startTick);
        _requirePoolPrice(pool, startSqrtPriceX96);

        // V3 liquidity rounding can leave a few indivisible token units. They are made permanently inaccessible.
        uint256 launchTokenDust = LaunchToken(token).balanceOf(address(this));
        if (launchTokenDust != 0) LaunchToken(token).transfer(DEAD, launchTokenDust);
        locker.registerPosition(
            token, positionTokenId, creator, creatorFeeReceiver, token0, token1
        );
    }

    function _createAndInitializePool(
        address token,
        address token0,
        address token1,
        int24 startTick,
        uint160 startSqrtPriceX96
    ) internal returns (address pool) {
        address existingPool = v3Factory.getPool(token0, token1, poolFee);
        if (existingPool != address(0)) revert PoolAlreadyExists(token, existingPool);

        pool = v3Factory.createPool(token0, token1, poolFee);
        if (
            pool == address(0) || pool.code.length == 0
                || v3Factory.getPool(token0, token1, poolFee) != pool
        ) revert PoolCreationMismatch();

        (uint160 priceBefore,,,,,,) = IV3Pool(pool).slot0();
        if (priceBefore != 0) revert PoolInitializationMismatch();
        IV3Pool(pool).initialize(startSqrtPriceX96);
        (uint160 initializedPrice, int24 initializedTick,,,,,) = IV3Pool(pool).slot0();
        if (initializedPrice != startSqrtPriceX96 || initializedTick != startTick) {
            revert PoolInitializationMismatch();
        }
    }

    function _requirePoolPrice(address pool, uint160 expectedPrice) internal view {
        (uint160 price,,,,,,) = IV3Pool(pool).slot0();
        if (price != expectedPrice) revert PoolInitializationMismatch();
    }

    function _mintOneSidedPosition(
        address token,
        bool tokenIsToken0,
        address token0,
        address token1,
        int24 startTick
    ) internal returns (uint256 positionTokenId, uint128 liquidity) {
        token.safeApprove(address(positionManager), TOTAL_SUPPLY);
        uint256 amount0;
        uint256 amount1;
        (positionTokenId, liquidity, amount0, amount1) = positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: token0,
                token1: token1,
                fee: poolFee,
                tickLower: tokenIsToken0 ? startTick : pricing.minUsableTick(tickSpacing),
                tickUpper: tokenIsToken0 ? pricing.maxUsableTick(tickSpacing) : startTick,
                amount0Desired: tokenIsToken0 ? TOTAL_SUPPLY : 0,
                amount1Desired: tokenIsToken0 ? 0 : TOTAL_SUPPLY,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(locker),
                deadline: block.timestamp
            })
        );
        token.safeApprove(address(positionManager), 0);
        if (liquidity == 0 || (tokenIsToken0 ? amount1 != 0 : amount0 != 0)) revert InvalidLiquidityPosition();
        _verifyLockedPosition(positionTokenId, tokenIsToken0, token0, token1, startTick, liquidity);
    }

    function _verifyLockedPosition(
        uint256 positionTokenId,
        bool tokenIsToken0,
        address token0,
        address token1,
        int24 startTick,
        uint128 expectedLiquidity
    ) internal view {
        if (positionManager.ownerOf(positionTokenId) != address(locker)) revert InvalidLiquidityPosition();
        INonfungiblePositionManager.PositionView memory position = positionManager.positions(positionTokenId);
        int24 expectedLower = tokenIsToken0 ? startTick : pricing.minUsableTick(tickSpacing);
        int24 expectedUpper = tokenIsToken0 ? pricing.maxUsableTick(tickSpacing) : startTick;
        if (
            position.token0 != token0 || position.token1 != token1 || position.fee != poolFee
                || position.tickLower != expectedLower || position.tickUpper != expectedUpper
                || position.liquidity != expectedLiquidity || position.liquidity == 0
        ) revert InvalidLiquidityPosition();
    }

    function _syncGraduation(address token, bool revertIfNotReached) internal returns (uint256 marketCap) {
        Launch storage launch = launches[token];
        if (launch.creator == address(0)) revert UnknownLaunch();
        if (launch.graduated) {
            if (revertIfNotReached) revert AlreadyGraduated();
            return currentMarketCap(token);
        }
        if (!hasReachedGraduation(token)) {
            if (revertIfNotReached) revert GraduationNotReached();
            return currentMarketCap(token);
        }

        launch.graduated = true;
        LaunchToken(token).markGraduated();
        marketCap = currentMarketCap(token);
        emit TokenGraduated(token, marketCap);
    }

    function _authorizationDigest(address creator, CreateTokenParams calldata params)
        internal
        view
        returns (bytes32)
    {
        bytes32 structHash = keccak256(
            abi.encode(
                CREATE_TOKEN_AUTHORIZATION_TYPEHASH,
                creator,
                params.creatorFeeReceiver,
                params.salt,
                keccak256(abi.encode(params.metadata)),
                params.initialBuyEth,
                params.minTokensOut,
                params.deadline,
                params.authorizationDeadline
            )
        );
        return keccak256(abi.encodePacked(hex"1901", domainSeparator(), structHash));
    }

    function _recoverSigner(bytes32 digest, bytes calldata signature)
        internal
        pure
        returns (address signer)
    {
        if (signature.length != 65) revert InvalidAuthorizationSignature();

        bytes32 r;
        bytes32 s;
        uint8 v;
        assembly ("memory-safe") {
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 0x20))
            v := byte(0, calldataload(add(signature.offset, 0x40)))
        }
        if (uint256(s) > SECP256K1N_DIV_2 || (v != 27 && v != 28)) {
            revert InvalidAuthorizationSignature();
        }
        signer = ecrecover(digest, v, r, s);
        if (signer == address(0)) revert InvalidAuthorizationSignature();
    }

    function _buildDomainSeparator() internal view returns (bytes32) {
        return keccak256(
            abi.encode(
                EIP712_DOMAIN_TYPEHASH,
                EIP712_NAME_HASH,
                EIP712_VERSION_HASH,
                block.chainid,
                address(this)
            )
        );
    }

    function _validateAntiSnipeConfig(uint16 maxBuyBps, uint32 protectionDuration, bool enabled)
        internal
        pure
    {
        if (maxBuyBps > BPS_DENOMINATOR) revert InvalidBps();
        if (enabled && (maxBuyBps == 0 || protectionDuration == 0)) revert InvalidBps();
    }

    function _sendEth(address payable to, uint256 amount) internal {
        (bool success,) = to.call{value: amount}("");
        if (!success) revert EthTransferFailed();
    }
}
Chain explorer5214msChain node91ms