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
- Creator
- 0x288BA0FDe7…DED6B5c829
- 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