HissTokenV33

0xd71817c1336acf3f5fd52604fb8cd62d101d8888

Verification
Verified
v0.8.34+commit.80d5c536
Type
Contract
4,390 bytes
ABI entries
28
20 read · 5 write
License
none

Contract information

Address
0xd71817c1336acf3f5fd52604fb8cd62d101d8888
Chain
Robinhood Chain (4663)
Compiler
v0.8.34+commit.80d5c536
Optimization
Enabled
Creation tx
0x9dbf7ae8c9…c3b6183576

Token

Name
MEDIA
Symbol
MEDIA
Decimals
18
Holders
37

Read contract (20)

DOMAIN_SEPARATOR()bytes32
PERMIT_TYPEHASH()bytes32
allowance(address, address)uint256
balanceOf(address)uint256
decimals()uint8
description()string
launchpad()address
limitEndBlock()uint256
limitExempt(address)bool
logo()string
maxWallet()uint256
metadataURI()string
name()string
nonces(address)uint256
socials()string, string, string
symbol()string
telegram()string
totalSupply()uint256
twitter()string
website()string

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "metadataURI",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "logo",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "description",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "website",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "twitter",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "telegram",
            "type": "string"
          }
        ],
        "internalType": "struct TokenMeta",
        "name": "meta_",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "supply_",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "holder_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DOMAIN_SEPARATOR",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PERMIT_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "allowance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pool_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "positionManager_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "creator_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "burn_",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "windowBlocks_",
        "type": "uint256"
      }
    ],
    "name": "armLimits",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "description",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchpad",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "limitEndBlock",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "limitExempt",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "logo",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWallet",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "metadataURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "nonces",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      },
      {
        "internalType": "uint8",
        "name": "v",
        "type": "uint8"
      },
      {
        "internalType": "bytes32",
        "name": "r",
        "type": "bytes32"
      },
      {
        "internalType": "bytes32",
        "name": "s",
        "type": "bytes32"
      }
    ],
    "name": "permit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "socials",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "telegram",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transfer",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "twitter",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "website",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

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


/// ---------------------------------------------------------------------------
/// HissLPLocker — immutable, ownerless liquidity vault for a single Uniswap V3
/// position NFT.
/// ---------------------------------------------------------------------------
///
/// Design goals (GMGN "Burnt/Locked LP" scanner parity):
///   • Bytecode-provably rug-proof — the LP NFT can NEVER leave this contract.
///   • No owner. No admin. No upgradability. No selfdestruct.
///   • No path that calls `approve` / `setApprovalForAll` / `transferFrom` /
///     `safeTransferFrom` / `decreaseLiquidity` / `burn` on the position
///     manager. GMGN's LP-lock heuristic verifies this by reading bytecode.
///   • ONLY function that touches the NFT is `collect()`, which pulls the
///     accrued swap-fee balances (never the underlying liquidity) and forwards
///     them to the launchpad, where the creator/platform fee split happens.
///
/// One locker is deployed per launched token. `positionManager`, `tokenId`
/// and `launchpad` are all `immutable` and baked into the runtime code —
/// nothing can be reconfigured after deployment.
/// ---------------------------------------------------------------------------

interface INonfungiblePositionManagerLite {
    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function collect(CollectParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);
}

contract HissLPLocker {
    address public immutable positionManager;
    address public immutable launchpad;
    uint256 public immutable tokenId;

    event FeesCollected(uint256 amount0, uint256 amount1);

    constructor(address positionManager_, address launchpad_, uint256 tokenId_) {
        positionManager = positionManager_;
        launchpad = launchpad_;
        tokenId = tokenId_;
    }

    /// ERC721Receiver hook — required so `safeTransferFrom` from the launchpad
    /// (which mints the LP position) can hand the NFT to this locker. The
    /// hook only accepts one specific tokenId — any accidental transfer of a
    /// different position NFT reverts, so this locker can hold ONE and only
    /// ONE position for its entire lifetime.
    function onERC721Received(
        address, /* operator */
        address, /* from */
        uint256 receivedId,
        bytes calldata /* data */
    ) external view returns (bytes4) {
        require(msg.sender == positionManager, "only position manager");
        require(receivedId == tokenId, "wrong tokenId");
        return this.onERC721Received.selector;
    }

    /// Collect accrued swap fees from the locked position and send them to
    /// the launchpad for the creator/platform fee split. Permissionless —
    /// anyone may call. Cannot pull the underlying liquidity.
    function collect() external returns (uint256 amount0, uint256 amount1) {
        (amount0, amount1) = INonfungiblePositionManagerLite(positionManager).collect(
            INonfungiblePositionManagerLite.CollectParams({
                tokenId: tokenId,
                recipient: launchpad,
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );
        emit FeesCollected(amount0, amount1);
    }
}



/// ---------------------------------------------------------------------------
/// SirHissLaunchpadV33 — pool-native launchpad, trustless edition
/// ---------------------------------------------------------------------------
/// Everything V3.2 does (immutable ERC-20 with on-chain branding, full supply
/// into a single-sided Uniswap V3 position, per-token immutable HissLPLocker,
/// optional atomic dev buy, pull-pattern ETH fee claims) with a 70/30
/// creator/platform fee split, PLUS:
///
///   1. ANTI-SNIPE — for LIMIT_WINDOW blocks after launch no wallet can hold
///      more than 2% of supply (pool / launchpad / position manager / creator
///      / burn address exempt). Enforced inside the token, bytecode-provable,
///      auto-expires. Snipers can no longer vacuum a fresh launch.
///
///   2. CREATE2 DETERMINISTIC + VANITY ADDRESSES — tokens deploy via CREATE2
///      so the address is known BEFORE the launch tx lands. `predictToken()`
///      + `tokenInitCodeHash()` let the frontend mine a salt off-chain so
///      every HISS token address ends in the vanity suffix 0x…8888. The salt
///      is namespaced by creator, so nobody can front-run your address.
///
///   3. ON-CHAIN GRADUATION — permissionless `checkGraduation(token)` latches
///      `graduated[token]` and emits `Graduated` once 800M tokens (80% of
///      supply) have been bought out of the pool — the exact metric the UI
///      progress bar tracks (~6 ETH of depth). Note this is a MILESTONE, not
///      a migration: the token is already in its final Uniswap V3 pool from
///      block one, so there is no liquidity move, no downtime, no wrapper.
///      Third parties (GMGN, DexScreener) can detect the milestone from the
///      chain instead of trusting the app.
///
///   4. ZERO ADMIN — no owner, no transferOwnership. The only mutable knob is
///      feeRecipient rotating ITSELF (key rotation without centralization).
///
///   5. launchFee() view (constant 0) — integrators can query it today, so a
///      future paid version keeps the same ABI.
///
///   6. EIP-2612 permit() on the token — enables true one-transaction sells
///      through HissZap (no separate approval tx, ever).
///
/// The TokenLaunched event is byte-compatible with V3.2 so existing indexers
/// keep working unchanged.
///
/// Deploy (Robinhood Chain mainnet, chain id 4663):
///   feeRecipient_:    platform fee wallet (0x0 = deployer)
///   positionManager_: 0x73991a25c818bf1f1128deaab1492d45638de0d3
///   swapRouter_:      0xcaf681a66d020601342297493863e78c959e5cb2
/// Compile with solc 0.8.24+, optimization enabled (200 runs).
/// ---------------------------------------------------------------------------

interface INonfungiblePositionManager {
    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function createAndInitializePoolIfNecessary(
        address token0,
        address token1,
        uint24 fee,
        uint160 sqrtPriceX96
    ) external payable returns (address pool);

    function mint(MintParams calldata params)
        external
        payable
        returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    function collect(CollectParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    function WETH9() external view returns (address);
}

interface ISwapRouter02 {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    function exactInputSingle(ExactInputSingleParams calldata params)
        external
        payable
        returns (uint256 amountOut);
}

interface IWETH9 {
    function withdraw(uint256 amount) external;
    function balanceOf(address account) external view returns (uint256);
}

struct TokenMeta {
    string name;
    string symbol;
    string metadataURI;
    string logo;
    string description;
    string website;
    string twitter;
    string telegram;
}

/// Immutable ERC-20 with on-chain branding, EIP-2612 permit and a
/// self-expiring anti-snipe max-wallet cap. No owner, no mint, no tax,
/// no blacklist, no pause.
contract HissTokenV33 {
    string public name;
    string public symbol;
    string public metadataURI;
    string public logo;
    string public description;
    string public website;
    string public twitter;
    string public telegram;
    uint8 public constant decimals = 18;
    uint256 public immutable totalSupply;

    // ------------------------- anti-snipe state ---------------------------
    /// 2% of supply — the most any non-exempt wallet can hold inside the
    /// limit window.
    uint256 public immutable maxWallet;
    /// Last block (inclusive) on which the max-wallet cap applies. Zero until
    /// the launchpad arms it in the launch transaction; permanently past
    /// after the window, so post-window transfers cost one cold SLOAD + CMP.
    uint256 public limitEndBlock;
    /// Addresses allowed to exceed the cap during the window: launchpad,
    /// pool, position manager, creator, burn address.
    mapping(address => bool) public limitExempt;
    /// The factory that deployed this token — the only address that may arm
    /// the anti-snipe window, exactly once, during the launch transaction.
    address public immutable launchpad;

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    // --------------------------- EIP-2612 ---------------------------------
    mapping(address => uint256) public nonces;
    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
    bytes32 private immutable _domainSeparator;
    uint256 private immutable _cachedChainId;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(TokenMeta memory meta_, uint256 supply_, address holder_) {
        name = meta_.name;
        symbol = meta_.symbol;
        metadataURI = meta_.metadataURI;
        logo = meta_.logo;
        description = meta_.description;
        website = meta_.website;
        twitter = meta_.twitter;
        telegram = meta_.telegram;
        totalSupply = supply_;
        maxWallet = (supply_ * 200) / 10_000; // 2%
        launchpad = msg.sender;
        limitExempt[msg.sender] = true;
        balanceOf[holder_] = supply_;
        emit Transfer(address(0), holder_, supply_);

        _cachedChainId = block.chainid;
        _domainSeparator = _computeDomainSeparator();
    }

    /// One-time arm of the anti-snipe window, called by the launchpad inside
    /// the launch transaction (after the pool exists). Not callable again and
    /// not callable by anyone else — bytecode-provably not an admin lever.
    function armLimits(
        address pool_,
        address positionManager_,
        address creator_,
        address burn_,
        uint256 windowBlocks_
    ) external {
        require(msg.sender == launchpad, "only launchpad");
        require(limitEndBlock == 0, "armed");
        limitExempt[pool_] = true;
        limitExempt[positionManager_] = true;
        limitExempt[creator_] = true;
        limitExempt[burn_] = true;
        limitEndBlock = block.number + windowBlocks_;
    }

    function socials()
        external
        view
        returns (string memory, string memory, string memory)
    {
        return (website, twitter, telegram);
    }

    function approve(address spender, uint256 amount) external returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    function transferFrom(address from, address to, uint256 amount) external returns (bool) {
        uint256 allowed = allowance[from][msg.sender];
        require(allowed >= amount, "allowance");
        if (allowed != type(uint256).max) {
            allowance[from][msg.sender] = allowed - amount;
        }
        _transfer(from, to, amount);
        return true;
    }

    // --------------------------- EIP-2612 ---------------------------------

    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        // Recompute if the chain forked so signatures stay domain-bound.
        return block.chainid == _cachedChainId ? _domainSeparator : _computeDomainSeparator();
    }

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(block.timestamp <= deadline, "permit expired");
        bytes32 digest = keccak256(
            abi.encodePacked(
                "\x19\x01",
                DOMAIN_SEPARATOR(),
                keccak256(
                    abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)
                )
            )
        );
        address recovered = ecrecover(digest, v, r, s);
        require(recovered != address(0) && recovered == owner, "invalid signature");
        allowance[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    function _computeDomainSeparator() private view returns (bytes32) {
        return keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes(name)),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );
    }

    // ------------------------------ internal ------------------------------

    function _transfer(address from, address to, uint256 amount) internal {
        require(to != address(0), "zero address");
        uint256 bal = balanceOf[from];
        require(bal >= amount, "balance");
        balanceOf[from] = bal - amount;
        uint256 newBal = balanceOf[to] + amount;
        // Anti-snipe: inside the limit window non-exempt wallets are capped
        // at 2% of supply. After limitEndBlock this is a single comparison.
        if (block.number <= limitEndBlock && !limitExempt[to]) {
            require(newBal <= maxWallet, "max wallet");
        }
        balanceOf[to] = newBal;
        emit Transfer(from, to, amount);
    }
}

contract SirHissLaunchpadV33 {
    // ----------------------------- constants ------------------------------
    uint256 public constant TOTAL_SUPPLY = 1_000_000_000e18;
    uint24 public constant POOL_FEE = 10_000; // 1% fee tier (tick spacing 200)
    uint256 public constant CREATOR_FEE_SHARE_BPS = 7_000; // 70% creator / 30% platform
    uint256 internal constant BPS = 10_000;

    /// Launch is free. The view exists so integrators query it today and a
    /// future paid deployment keeps an identical ABI.
    uint256 public constant launchFee = 0;

    /// Anti-snipe window: max-wallet cap applies for this many blocks after
    /// launch, then expires automatically.
    uint256 public constant LIMIT_WINDOW_BLOCKS = 100;
    /// Anti-snipe cap, mirrored from the token for easy off-chain reads.
    uint256 public constant MAX_WALLET_BPS = 200; // 2%

    /// Graduation milestone: once this many tokens (80% of supply) have been
    /// bought out of the locked pool the token is considered graduated
    /// (latched on-chain via checkGraduation). Matches the UI progress bar's
    /// 800M semantics — NOT a migration; the pool never moves.
    uint256 public constant GRADUATION_TOKENS_SOLD = 800_000_000e18;

    int24 internal constant BOUNDARY_TICK = 203_200;
    int24 internal constant EXTREME_TICK = 887_200;

    uint160 internal constant SQRT_PRICE_TOKEN0 = 3051384182559527864182507;
    uint160 internal constant SQRT_PRICE_TOKEN1 = 2057132553568326054787615825908002;

    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    // ------------------------------- state --------------------------------
    /// Platform fee wallet. NOT an owner: the only thing it can do is rotate
    /// itself (key hygiene). There is no other admin surface on this contract.
    address public feeRecipient;
    INonfungiblePositionManager public immutable positionManager;
    ISwapRouter02 public immutable swapRouter;
    address public immutable weth;

    struct Launch {
        address creator;
        address pool;
        address lpLocker; // per-token immutable locker holding the LP NFT
        uint256 lpTokenId;
        bool tokenIsToken0;
        bool exists;
    }

    mapping(address => Launch) public launches;
    address[] public allTokens;
    mapping(address => uint256) public pendingEth;
    /// Latched to true by checkGraduation() once GRADUATION_TOKENS_SOLD
    /// tokens have been bought out of the pool. Never un-latches.
    mapping(address => bool) public graduated;

    uint256 private locked = 1;

    // ------------------------------- events -------------------------------
    /// Byte-compatible with V3.2 — same signature, same topic0 — so existing
    /// indexers decode V3.3 launches without changes.
    event TokenLaunched(
        address indexed token,
        address indexed creator,
        address pool,
        address lpLocker,
        string name,
        string symbol,
        string metadataURI,
        uint256 devBuyEth
    );
    event FeesClaimed(
        address indexed token,
        uint256 ethToCreator,
        uint256 ethToPlatform,
        uint256 tokensToCreator,
        uint256 tokensToPlatform
    );
    event Claimed(address indexed account, uint256 amount);
    event Graduated(address indexed token, address indexed pool, uint256 tokensSold);
    event FeeRecipientRotated(address indexed oldRecipient, address indexed newRecipient);

    modifier nonReentrant() {
        require(locked == 1, "reentrancy");
        locked = 2;
        _;
        locked = 1;
    }

    constructor(address feeRecipient_, address positionManager_, address swapRouter_) {
        feeRecipient = feeRecipient_ == address(0) ? msg.sender : feeRecipient_;
        positionManager = INonfungiblePositionManager(positionManager_);
        swapRouter = ISwapRouter02(swapRouter_);
        weth = INonfungiblePositionManager(positionManager_).WETH9();
    }

    // ------------------------------ launch --------------------------------

    /// Launch a token straight into a Uniswap V3 pool whose LP NFT is then
    /// transferred to a per-token immutable HissLPLocker.
    ///
    /// `salt` — CREATE2 salt, typically mined off-chain (see predictToken)
    /// so the token address ends in the HISS vanity suffix. The effective
    /// salt is namespaced by msg.sender, so a mined address cannot be
    /// front-run by anyone else.
    ///
    /// Any ETH sent executes an atomic dev buy in the same transaction —
    /// before any sniper can act. Tokens go to `msg.sender`.
    function createToken(TokenMeta calldata meta, bytes32 salt, uint256 minDevBuyTokens)
        external
        payable
        nonReentrant
        returns (address token)
    {
        return _createToken(meta, salt, minDevBuyTokens, msg.sender);
    }

    /// Same as createToken, but the atomic dev buy lands on `devBuyRecipient`
    /// (pass address(0) to keep msg.sender). Gift recipients are still subject
    /// to the 2% anti-snipe max-wallet during the limit window.
    function createToken(
        TokenMeta calldata meta,
        bytes32 salt,
        uint256 minDevBuyTokens,
        address devBuyRecipient
    ) external payable nonReentrant returns (address token) {
        address to = devBuyRecipient == address(0) ? msg.sender : devBuyRecipient;
        return _createToken(meta, salt, minDevBuyTokens, to);
    }

    function _createToken(
        TokenMeta calldata meta,
        bytes32 salt,
        uint256 minDevBuyTokens,
        address devBuyRecipient
    ) internal returns (address token) {
        require(bytes(meta.name).length >= 2 && bytes(meta.name).length <= 32, "bad name");
        require(bytes(meta.symbol).length >= 2 && bytes(meta.symbol).length <= 8, "bad symbol");

        token = address(
            new HissTokenV33{salt: _effectiveSalt(msg.sender, salt)}(
                meta,
                TOTAL_SUPPLY,
                address(this)
            )
        );

        (address pool, address locker) = _poolAndLock(token);

        _devBuy(token, minDevBuyTokens, devBuyRecipient);

        emit TokenLaunched(
            token,
            msg.sender,
            pool,
            locker,
            meta.name,
            meta.symbol,
            meta.metadataURI,
            msg.value - launchFee
        );
    }

    /// Create + initialize the pool, arm the token's anti-snipe window,
    /// deposit the full supply as a single-sided position, and lock the LP
    /// NFT in a fresh per-token immutable HissLPLocker.
    function _poolAndLock(address token) internal returns (address pool, address locker) {
        bool tokenIsToken0 = token < weth;
        (address token0, address token1) = tokenIsToken0 ? (token, weth) : (weth, token);
        pool = positionManager.createAndInitializePoolIfNecessary(
            token0,
            token1,
            POOL_FEE,
            tokenIsToken0 ? SQRT_PRICE_TOKEN0 : SQRT_PRICE_TOKEN1
        );

        // Arm the anti-snipe window now that the pool address is known.
        HissTokenV33(token).armLimits(
            pool,
            address(positionManager),
            msg.sender,
            DEAD,
            LIMIT_WINDOW_BLOCKS
        );

        HissTokenV33(token).approve(address(positionManager), TOTAL_SUPPLY);
        (uint256 lpTokenId, , uint256 amount0, uint256 amount1) = positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: token0,
                token1: token1,
                fee: POOL_FEE,
                tickLower: tokenIsToken0 ? -BOUNDARY_TICK : -EXTREME_TICK,
                tickUpper: tokenIsToken0 ? EXTREME_TICK : BOUNDARY_TICK,
                amount0Desired: tokenIsToken0 ? TOTAL_SUPPLY : 0,
                amount1Desired: tokenIsToken0 ? 0 : TOTAL_SUPPLY,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(this),
                deadline: block.timestamp
            })
        );

        uint256 pooled = tokenIsToken0 ? amount0 : amount1;
        if (TOTAL_SUPPLY > pooled) {
            HissTokenV33(token).transfer(DEAD, TOTAL_SUPPLY - pooled);
        }

        // Deploy per-token locker and hand it the LP NFT. The locker is
        // immutable, ownerless and can never transfer the NFT out —
        // GMGN's scanner treats this as fully "Locked LP".
        locker = address(new HissLPLocker(address(positionManager), address(this), lpTokenId));
        positionManager.safeTransferFrom(address(this), locker, lpTokenId);

        launches[token] = Launch({
            creator: msg.sender,
            pool: pool,
            lpLocker: locker,
            lpTokenId: lpTokenId,
            tokenIsToken0: tokenIsToken0,
            exists: true
        });
        allTokens.push(token);
    }

    /// Atomic dev buy: swap the sent ETH (minus launch fee) for the new token
    /// in the launch transaction, before any sniper can act.
    function _devBuy(address token, uint256 minDevBuyTokens, address recipient) internal {
        uint256 devBuyEth = msg.value - launchFee;
        if (devBuyEth == 0) return;
        swapRouter.exactInputSingle{value: devBuyEth}(
            ISwapRouter02.ExactInputSingleParams({
                tokenIn: weth,
                tokenOut: token,
                fee: POOL_FEE,
                recipient: recipient,
                amountIn: devBuyEth,
                amountOutMinimum: minDevBuyTokens,
                sqrtPriceLimitX96: 0
            })
        );
    }

    // ------------------------- address prediction -------------------------

    /// The CREATE2 init code hash for a token with the given metadata.
    /// Frontends hash this once, then mine salts locally (one keccak per
    /// candidate) until predictToken ends in the desired vanity suffix.
    function tokenInitCodeHash(TokenMeta calldata meta) public view returns (bytes32) {
        return keccak256(
            abi.encodePacked(
                type(HissTokenV33).creationCode,
                abi.encode(meta, TOTAL_SUPPLY, address(this))
            )
        );
    }

    /// Predict the token address `creator` would get for (salt, meta) —
    /// exact match for what createToken deploys.
    function predictToken(address creator, bytes32 salt, TokenMeta calldata meta)
        external
        view
        returns (address)
    {
        return address(
            uint160(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            bytes1(0xff),
                            address(this),
                            _effectiveSalt(creator, salt),
                            tokenInitCodeHash(meta)
                        )
                    )
                )
            )
        );
    }

    function _effectiveSalt(address creator, bytes32 salt) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(creator, salt));
    }

    // ----------------------------- graduation -----------------------------

    /// Permissionless: latch the graduation flag and emit the milestone event
    /// once 800M tokens have been bought out of the locked pool. Also invoked
    /// opportunistically by claimFees so most tokens graduate without anyone
    /// calling this. Purely a milestone — the pool and liquidity never move.
    function checkGraduation(address token) public {
        Launch storage l = launches[token];
        require(l.exists, "unknown token");
        _maybeGraduate(token, l.pool);
    }

    function _maybeGraduate(address token, address pool) internal {
        if (graduated[token]) return;
        // Tokens sold = total supply minus what's still sitting in the pool
        // (the same semantics the app's progress bar uses).
        uint256 inPool = HissTokenV33(token).balanceOf(pool);
        uint256 sold = TOTAL_SUPPLY > inPool ? TOTAL_SUPPLY - inPool : 0;
        if (sold >= GRADUATION_TOKENS_SOLD) {
            graduated[token] = true;
            emit Graduated(token, pool, sold);
        }
    }

    // ---------------------------- fee claiming ----------------------------

    /// Route the locked position's accrued 1% swap fees through the locker
    /// (which forwards them to this launchpad) and split them 70/30 between
    /// the creator and the platform. Anyone can call.
    function claimFees(address token) external nonReentrant {
        Launch storage l = launches[token];
        require(l.exists, "unknown token");

        (uint256 amount0, uint256 amount1) = HissLPLocker(l.lpLocker).collect();

        (uint256 tokenAmt, uint256 wethAmt) =
            l.tokenIsToken0 ? (amount0, amount1) : (amount1, amount0);

        uint256 ethCreator;
        uint256 ethPlatform;
        if (wethAmt > 0) {
            IWETH9(weth).withdraw(wethAmt);
            ethCreator = (wethAmt * CREATOR_FEE_SHARE_BPS) / BPS;
            ethPlatform = wethAmt - ethCreator;
            pendingEth[l.creator] += ethCreator;
            pendingEth[feeRecipient] += ethPlatform;
        }

        uint256 tokCreator;
        uint256 tokPlatform;
        if (tokenAmt > 0) {
            tokCreator = (tokenAmt * CREATOR_FEE_SHARE_BPS) / BPS;
            tokPlatform = tokenAmt - tokCreator;
            require(HissTokenV33(token).transfer(l.creator, tokCreator), "token transfer");
            require(HissTokenV33(token).transfer(feeRecipient, tokPlatform), "token transfer");
        }

        emit FeesClaimed(token, ethCreator, ethPlatform, tokCreator, tokPlatform);

        _maybeGraduate(token, l.pool);
    }

    /// Withdraw accumulated ETH fees (pull pattern).
    function claim() external nonReentrant {
        uint256 amount = pendingEth[msg.sender];
        require(amount > 0, "nothing to claim");
        pendingEth[msg.sender] = 0;
        _send(msg.sender, amount);
        emit Claimed(msg.sender, amount);
    }

    // ------------------------------- views --------------------------------

    function tokenCount() external view returns (uint256) {
        return allTokens.length;
    }

    function poolOf(address token) external view returns (address) {
        return launches[token].pool;
    }

    function lockerOf(address token) external view returns (address) {
        return launches[token].lpLocker;
    }

    // --------------------------- fee recipient -----------------------------

    /// Key rotation only — the fee wallet may point future platform fees at a
    /// new address. It cannot touch launches, lockers, tokens, creator fees
    /// or anything else. There is no owner on this contract.
    function rotateFeeRecipient(address newRecipient) external {
        require(msg.sender == feeRecipient, "not fee recipient");
        require(newRecipient != address(0), "zero address");
        emit FeeRecipientRotated(feeRecipient, newRecipient);
        feeRecipient = newRecipient;
    }

    // ------------------------------ internal ------------------------------

    function _send(address to, uint256 amount) internal {
        if (amount == 0) return;
        (bool ok, ) = to.call{value: amount}("");
        require(ok, "eth send failed");
    }

    receive() external payable {}
}
Chain explorer789msChain node84ms