QuiverV4Launcher

0xa47370826b78a130c1706c092c17862c2af27a84

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
24,504 bytes
ABI entries
53
23 read · 5 write
License
none

Contract information

Address
0xa47370826b78a130c1706c092c17862c2af27a84
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xc94b3a2638…a53dc13002

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (23)

DEFAULT_POOL_FEE()uint24
DEFAULT_TICK_SPACING()int24
LAUNCH_SUPPLY()uint256
MAX_ALLOCATIONS()uint256
MAX_TOTAL_ALLOCATION_BPS()uint256
MAX_VIEW_PAGE()uint256
feePreset(uint8)tuple
gate()address
hook()address
launchCount()uint256
launchedPoolIdAt(uint256)bytes32
launches(bytes32)address, address, address, uint256, uint24, int24
launchesRange(uint256, uint256)tuple[]
owner()address
paused()bool
pendingOwner()address
permaLock()address
permit2()address
poolIdOfToken(address)bytes32
poolManager()address
positionManager()address
universalRouter()address
weth()address

Events (6)

FirstBuyLaunchedOwnershipTransferStartedOwnershipTransferredPausedSetSupplyAllocated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "poolManager_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "positionManager_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "permit2_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "hook_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "gate_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "permaLock_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "universalRouter_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "target",
        "type": "address"
      }
    ],
    "name": "AddressEmptyCode",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "AddressInsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FailedInnerCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidAllocation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidFdv",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidGateSigner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPreset",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LaunchesPaused",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MintFailed",
    "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": "PageTooLarge",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PriceOutOfRange",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SupplyNotDeposited",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TransferFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "wethSpent",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokensOut",
        "type": "uint256"
      }
    ],
    "name": "FirstBuy",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "indexed": false,
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "gateSigner",
        "type": "address"
      }
    ],
    "name": "Launched",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "bool",
        "name": "paused",
        "type": "bool"
      }
    ],
    "name": "PausedSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "bps",
        "type": "uint16"
      }
    ],
    "name": "SupplyAllocated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DEFAULT_POOL_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEFAULT_TICK_SPACING",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LAUNCH_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_ALLOCATIONS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_TOTAL_ALLOCATION_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_VIEW_PAGE",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint8",
        "name": "preset",
        "type": "uint8"
      }
    ],
    "name": "feePreset",
    "outputs": [
      {
        "components": [
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          }
        ],
        "internalType": "struct QuiverV4Launcher.FeePreset",
        "name": "value",
        "type": "tuple"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "gate",
    "outputs": [
      {
        "internalType": "contract QuiverV4Gate",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "hook",
    "outputs": [
      {
        "internalType": "contract QuiverV4Hook",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "imageURI",
        "type": "string"
      },
      {
        "internalType": "uint256",
        "name": "startingFdvWei",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "gated",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "liquidityExclusive",
            "type": "bool"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "gateSigner",
            "type": "address"
          },
          {
            "internalType": "uint8",
            "name": "feePreset",
            "type": "uint8"
          },
          {
            "internalType": "uint128",
            "name": "firstBuyMinTokens",
            "type": "uint128"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "account",
                "type": "address"
              },
              {
                "internalType": "uint16",
                "name": "weightBps",
                "type": "uint16"
              }
            ],
            "internalType": "struct QuiverV4PermaLock.SplitRecipient[]",
            "name": "feeSplit",
            "type": "tuple[]"
          },
          {
            "components": [
              {
                "internalType": "address",
                "name": "recipient",
                "type": "address"
              },
              {
                "internalType": "uint16",
                "name": "bps",
                "type": "uint16"
              }
            ],
            "internalType": "struct QuiverV4Launcher.Allocation[]",
            "name": "allocations",
            "type": "tuple[]"
          }
        ],
        "internalType": "struct QuiverV4Launcher.LaunchParams",
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "launch",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "index",
        "type": "uint256"
      }
    ],
    "name": "launchedPoolIdAt",
    "outputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "launches",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "gateSigner",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "offset",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "limit",
        "type": "uint256"
      }
    ],
    "name": "launchesRange",
    "outputs": [
      {
        "components": [
          {
            "internalType": "PoolId",
            "name": "poolId",
            "type": "bytes32"
          },
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "gateSigner",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "tokenId",
            "type": "uint256"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          }
        ],
        "internalType": "struct QuiverV4Launcher.LaunchView[]",
        "name": "page",
        "type": "tuple[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "permaLock",
    "outputs": [
      {
        "internalType": "contract QuiverV4PermaLock",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "permit2",
    "outputs": [
      {
        "internalType": "contract IAllowanceTransfer",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "poolIdOfToken",
    "outputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionManager",
    "outputs": [
      {
        "internalType": "contract IPositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bool",
        "name": "paused_",
        "type": "bool"
      }
    ],
    "name": "setPaused",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "universalRouter",
    "outputs": [
      {
        "internalType": "contract IUniversalRouterLauncher",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract IWETHLauncher",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2026 Quiver (quiver-app.io)
pragma solidity 0.8.26;

import { Ownable2Step } from "openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import { IPoolManager } from "v4-core/src/interfaces/IPoolManager.sol";
import { IHooks } from "v4-core/src/interfaces/IHooks.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 { TickMath } from "v4-core/src/libraries/TickMath.sol";
import { IPositionManager } from "v4-periphery/src/interfaces/IPositionManager.sol";
import { Actions } from "v4-periphery/src/libraries/Actions.sol";
import { LiquidityAmounts } from "v4-periphery/src/libraries/LiquidityAmounts.sol";
import { IAllowanceTransfer } from "permit2/src/interfaces/IAllowanceTransfer.sol";
import { QuiverTokenV2 } from "./QuiverTokenV2.sol";
import { QuiverV4Hook } from "./QuiverV4Hook.sol";
import { QuiverV4Gate } from "./QuiverV4Gate.sol";
import { QuiverV4PermaLock } from "./QuiverV4PermaLock.sol";
import { UniversalRouterV211Encoder } from "./libraries/UniversalRouterV211Encoder.sol";

interface IERC721PositionTransfer {
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function ownerOf(uint256 tokenId) external view returns (address);
}

interface IUniversalRouterLauncher {
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
    function poolManager() external view returns (IPoolManager);
}

interface IWETHLauncher is IERC20 {
    function withdraw(uint256 amount) external;
}

/// @notice Atomic V2 launch coordinator for plain tokens and shared-hook v4 pools.
contract QuiverV4Launcher is Ownable2Step {
    using PoolIdLibrary for PoolKey;
    using SafeERC20 for IERC20;

    uint256 public constant LAUNCH_SUPPLY = 1_000_000_000e18;
    uint24 public constant DEFAULT_POOL_FEE = 10_000;
    int24 public constant DEFAULT_TICK_SPACING = 200;
    uint256 public constant MAX_ALLOCATIONS = 10;
    uint256 public constant MAX_TOTAL_ALLOCATION_BPS = 5_000;

    struct FeePreset {
        uint24 fee;
        int24 tickSpacing;
    }

    struct Allocation {
        address recipient;
        uint16 bps;
    }

    struct LaunchParams {
        bool gated;
        bool liquidityExclusive;
        address creator;
        address gateSigner;
        uint8 feePreset;
        uint128 firstBuyMinTokens;
        QuiverV4PermaLock.SplitRecipient[] feeSplit;
        Allocation[] allocations;
    }

    struct Launch {
        address token;
        address creator;
        address gateSigner;
        uint256 tokenId;
        uint24 fee;
        int24 tickSpacing;
    }

    struct LaunchView {
        PoolId poolId;
        address token;
        address creator;
        address gateSigner;
        uint256 tokenId;
        uint24 fee;
        int24 tickSpacing;
    }

    uint256 public constant MAX_VIEW_PAGE = 500;

    IPoolManager public immutable poolManager;
    IPositionManager public immutable positionManager;
    IAllowanceTransfer public immutable permit2;
    QuiverV4Hook public immutable hook;
    QuiverV4Gate public immutable gate;
    QuiverV4PermaLock public immutable permaLock;
    IUniversalRouterLauncher public immutable universalRouter;
    IWETHLauncher public immutable weth;

    mapping(PoolId => Launch) public launches;
    mapping(address => PoolId) public poolIdOfToken;
    PoolId[] internal _launchedPoolIds;
    bool public paused;

    event Launched(
        address indexed creator,
        address indexed token,
        PoolId indexed poolId,
        uint256 tokenId,
        uint160 sqrtPriceX96,
        uint24 fee,
        int24 tickSpacing,
        address gateSigner
    );
    event SupplyAllocated(address indexed token, address indexed recipient, uint256 amount, uint16 bps);
    event FirstBuy(address indexed token, address indexed creator, uint256 wethSpent, uint256 tokensOut);
    event PausedSet(bool paused);

    error ZeroAddress();
    error LaunchesPaused();
    error InvalidFdv();
    error InvalidPreset();
    error InvalidAllocation();
    error PriceOutOfRange();
    error SupplyNotDeposited();
    error MintFailed();
    error TransferFailed();
    error PageTooLarge();
    error InvalidGateSigner();

    constructor(
        address poolManager_,
        address positionManager_,
        address permit2_,
        address hook_,
        address gate_,
        address permaLock_,
        address universalRouter_,
        address weth_
    ) Ownable(msg.sender) {
        if (
            poolManager_ == address(0) || positionManager_ == address(0) || permit2_ == address(0)
                || hook_ == address(0) || gate_ == address(0) || permaLock_ == address(0)
                || universalRouter_ == address(0) || weth_ == address(0)
        ) revert ZeroAddress();
        poolManager = IPoolManager(poolManager_);
        positionManager = IPositionManager(positionManager_);
        permit2 = IAllowanceTransfer(permit2_);
        hook = QuiverV4Hook(hook_);
        gate = QuiverV4Gate(payable(gate_));
        permaLock = QuiverV4PermaLock(payable(permaLock_));
        universalRouter = IUniversalRouterLauncher(universalRouter_);
        weth = IWETHLauncher(weth_);
        if (address(positionManager.poolManager()) != poolManager_) revert ZeroAddress();
        if (address(universalRouter.poolManager()) != poolManager_) revert ZeroAddress();
    }

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

    function launch(
        string calldata name,
        string calldata symbol,
        string calldata imageURI,
        uint256 startingFdvWei,
        LaunchParams calldata params
    ) external payable returns (address token, PoolId poolId, uint256 tokenId) {
        if (paused) revert LaunchesPaused();
        if (startingFdvWei == 0) revert InvalidFdv();
        if (!params.gated && params.gateSigner != address(0)) revert InvalidGateSigner();
        _validateAllocations(params.allocations);
        address creator = params.creator == address(0) ? msg.sender : params.creator;
        FeePreset memory preset = feePreset(params.feePreset);

        bytes32 salt = keccak256(abi.encodePacked(msg.sender, _launchedPoolIds.length, blockhash(block.number - 1)));
        token = address(new QuiverTokenV2{ salt: salt }(name, symbol, LAUNCH_SUPPLY, imageURI));
        bool tokenIsToken0 = token < address(weth);
        PoolKey memory key = PoolKey({
            currency0: Currency.wrap(tokenIsToken0 ? token : address(weth)),
            currency1: Currency.wrap(tokenIsToken0 ? address(weth) : token),
            fee: preset.fee,
            tickSpacing: preset.tickSpacing,
            hooks: IHooks(address(hook))
        });
        poolId = key.toId();
        uint160 sqrtPriceX96 =
            tokenIsToken0 ? _sqrtPriceX96(startingFdvWei, LAUNCH_SUPPLY) : _sqrtPriceX96(LAUNCH_SUPPLY, startingFdvWei);

        hook.registerPool(key, token, creator, sqrtPriceX96, params.gated, params.liquidityExclusive);
        gate.registerPool(key, token, creator, params.gateSigner, params.gated);
        int24 currentTick = poolManager.initialize(key, sqrtPriceX96);

        uint256 allocated = _allocate(token, params.allocations);
        uint256 lpSupply = LAUNCH_SUPPLY - allocated;
        (int24 tickLower, int24 tickUpper, uint128 liquidity) =
            _position(tokenIsToken0, currentTick, preset.tickSpacing, lpSupply);

        IERC20(token).forceApprove(address(permit2), lpSupply);
        permit2.approve(token, address(positionManager), uint160(lpSupply), uint48(block.timestamp));
        tokenId = positionManager.nextTokenId();
        bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR));
        bytes[] memory mintParams = new bytes[](2);
        mintParams[0] = abi.encode(
            key,
            tickLower,
            tickUpper,
            uint256(liquidity),
            tokenIsToken0 ? uint128(lpSupply) : uint128(0),
            tokenIsToken0 ? uint128(0) : uint128(lpSupply),
            address(this),
            bytes("")
        );
        mintParams[1] = abi.encode(key.currency0, key.currency1);
        positionManager.modifyLiquidities(abi.encode(actions, mintParams), block.timestamp);
        permit2.approve(token, address(positionManager), 0, 0);
        IERC20(token).forceApprove(address(permit2), 0);
        if (IERC721PositionTransfer(address(positionManager)).ownerOf(tokenId) != address(this)) revert MintFailed();
        if (IERC20(token).balanceOf(address(this)) > lpSupply / 100) revert SupplyNotDeposited();

        IERC721PositionTransfer(address(positionManager))
            .safeTransferFrom(address(this), address(permaLock), tokenId, abi.encode(creator, params.feeSplit));
        launches[poolId] = Launch({
            token: token,
            creator: creator,
            gateSigner: params.gateSigner,
            tokenId: tokenId,
            fee: preset.fee,
            tickSpacing: preset.tickSpacing
        });
        poolIdOfToken[token] = poolId;
        _launchedPoolIds.push(poolId);

        if (msg.value != 0) {
            _firstBuy(key, token, creator, params.firstBuyMinTokens);
        }
        hook.closeBootstrap(poolId);
        emit Launched(creator, token, poolId, tokenId, sqrtPriceX96, preset.fee, preset.tickSpacing, params.gateSigner);
    }

    function feePreset(uint8 preset) public pure returns (FeePreset memory value) {
        if (preset == 0 || preset == 4) return FeePreset(DEFAULT_POOL_FEE, DEFAULT_TICK_SPACING);
        if (preset == 1) return FeePreset(100, 1);
        if (preset == 2) return FeePreset(500, 10);
        if (preset == 3) return FeePreset(3_000, 60);
        revert InvalidPreset();
    }

    function setPaused(bool paused_) external onlyOwner {
        paused = paused_;
        emit PausedSet(paused_);
    }

    function launchCount() external view returns (uint256) {
        return _launchedPoolIds.length;
    }

    function launchedPoolIdAt(uint256 index) external view returns (PoolId) {
        return _launchedPoolIds[index];
    }

    /// @notice Returns a bounded, contiguous page of complete immutable launch records.
    /// @dev Designed for database restoration without one call per PoolId/record.
    function launchesRange(uint256 offset, uint256 limit) external view returns (LaunchView[] memory page) {
        if (limit > MAX_VIEW_PAGE) revert PageTooLarge();
        uint256 count = _launchedPoolIds.length;
        if (offset >= count || limit == 0) return new LaunchView[](0);
        uint256 end = offset + limit;
        if (end > count) end = count;
        page = new LaunchView[](end - offset);
        for (uint256 index = offset; index < end; ++index) {
            PoolId poolId = _launchedPoolIds[index];
            Launch storage record = launches[poolId];
            page[index - offset] = LaunchView({
                poolId: poolId,
                token: record.token,
                creator: record.creator,
                gateSigner: record.gateSigner,
                tokenId: record.tokenId,
                fee: record.fee,
                tickSpacing: record.tickSpacing
            });
        }
    }

    function _firstBuy(PoolKey memory key, address token, address creator, uint128 minimumOut) private {
        if (msg.value > type(uint128).max) revert TransferFailed();
        bool zeroForOne = Currency.unwrap(key.currency0) == address(weth);
        uint256 tokenBefore = IERC20(token).balanceOf(address(this));
        uint256 wethBefore = weth.balanceOf(address(this));
        (bytes memory commands, bytes[] memory inputs) = UniversalRouterV211Encoder.exactInputWethBuy(
            key, zeroForOne, uint128(msg.value), minimumOut, bytes(""), address(weth), address(this)
        );
        universalRouter.execute{ value: msg.value }(commands, inputs, block.timestamp);
        uint256 tokensOut = IERC20(token).balanceOf(address(this)) - tokenBefore;
        if (tokensOut < minimumOut) revert TransferFailed();
        IERC20(token).safeTransfer(creator, tokensOut);

        uint256 refund = weth.balanceOf(address(this)) - wethBefore;
        uint256 spent = msg.value - refund;
        if (refund != 0) {
            weth.withdraw(refund);
            (bool ok,) = creator.call{ value: refund }("");
            if (!ok) revert TransferFailed();
        }
        emit FirstBuy(token, creator, spent, tokensOut);
    }

    function _position(bool tokenIsToken0, int24 currentTick, int24 spacing, uint256 amount)
        private
        pure
        returns (int24 lower, int24 upper, uint128 liquidity)
    {
        if (tokenIsToken0) {
            lower = (currentTick / spacing) * spacing;
            if (lower <= currentTick) lower += spacing;
            upper = TickMath.maxUsableTick(spacing);
            liquidity = LiquidityAmounts.getLiquidityForAmount0(
                TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), amount
            );
        } else {
            lower = TickMath.minUsableTick(spacing);
            upper = (currentTick / spacing) * spacing;
            if (upper > currentTick) upper -= spacing;
            liquidity = LiquidityAmounts.getLiquidityForAmount1(
                TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), amount
            );
        }
        if (lower >= upper || liquidity == 0) revert PriceOutOfRange();
    }

    function _validateAllocations(Allocation[] calldata allocations) private pure {
        if (allocations.length > MAX_ALLOCATIONS) revert InvalidAllocation();
        uint256 totalBps;
        for (uint256 i; i < allocations.length; ++i) {
            if (allocations[i].recipient == address(0) || allocations[i].bps == 0) revert InvalidAllocation();
            totalBps += allocations[i].bps;
        }
        if (totalBps > MAX_TOTAL_ALLOCATION_BPS) revert InvalidAllocation();
    }

    function _allocate(address token, Allocation[] calldata allocations) private returns (uint256 total) {
        for (uint256 i; i < allocations.length; ++i) {
            address recipient = allocations[i].recipient;
            if (
                recipient == token || recipient == address(this) || recipient == address(positionManager)
                    || recipient == address(permaLock) || recipient == address(hook) || recipient == address(gate)
            ) revert InvalidAllocation();
            uint256 amount = LAUNCH_SUPPLY / 10_000 * allocations[i].bps;
            total += amount;
            IERC20(token).safeTransfer(recipient, amount);
            emit SupplyAllocated(token, recipient, amount, allocations[i].bps);
        }
    }

    function _sqrtPriceX96(uint256 numerator, uint256 denominator) private pure returns (uint160) {
        if (numerator > type(uint256).max >> 64) revert PriceOutOfRange();
        uint256 result = _isqrt((numerator << 64) / denominator) << 64;
        if (result < TickMath.MIN_SQRT_PRICE || result >= TickMath.MAX_SQRT_PRICE) revert PriceOutOfRange();
        return uint160(result);
    }

    function _isqrt(uint256 n) private pure returns (uint256) {
        if (n < 2) return n;
        uint256 x = n;
        uint256 y = (x + 1) / 2;
        while (y < x) {
            x = y;
            y = (x + n / x) / 2;
        }
        return x;
    }
}
Chain explorer3582msChain node91ms