$LONG IS LIVE ON ROBINHOOD CHAIN NO SHORTS NO TOKENS UP FRONT LOSSES FEED THE POOL DEPOSIT ETH · PICK A MULTIPLIER · EARN LONG
[ PERMANENT LIQUIDITY ]

THE LP LOCK

Launch LP for $LONG is burned into a one-way locker. The Uniswap V3 position NFT can never leave. The only callable path after lock is fee collection — and only by the immutable owner.

CONTRACT · UniswapV3LpLockerCHAIN · ROBINHOOD 4663OWNER · 0x909645ae
[ WHY IT'S SAFE ]

Small surface. Hard guarantees.

[ ONE WAY ]

No unlock path

There is no decreaseLiquidity, no transferOut, no burn-of-principal. Once burnLP succeeds, the NFT stays in the contract forever.

[ OWNER ONLY ]

Two gated functions

burnLP and safeClaim both require msg.sender == owner. The owner address is immutable — set once in the constructor.

[ FEES ONLY ]

Collect, never principal

safeClaim calls Uniswap’s collect and sends token0/token1 fees to the owner. Liquidity stays in the pool.

[ SINGLE NFT ]

Lock once

positionId can be set only once. A second burnLP reverts with AlreadyLocked — no swapping the locked position later.

[ THE FLOW ]

Lock once. Claim forever.

  1. 01

    Mint LP

    Seed the LONG/WETH Uniswap V3 pool. You receive a position NFT.

  2. 02

    burnLP

    Owner approves the locker and calls burnLP. The NFT moves in — permanently.

  3. 03

    safeClaim

    Trading fees accrue on the position. Only the owner can collect them.

[ SURFACE AREA ]

Two functions. That's it.

The entire mutable API is owner-gated. Below is the real logic the locker exposes — nothing else can move the NFT or redirect fees.

burnLP(uint256 tokenId)

Pulls the Uniswap V3 position NFT into the locker and records it. Reverts if already locked or tokenId is zero.

ONLY OWNER
function burnLP(uint256 tokenId) external onlyOwner {
    if (positionId != 0) revert AlreadyLocked();
    if (tokenId == 0) revert BadToken();

    IERC721(address(positionManager))
        .transferFrom(msg.sender, address(this), tokenId);
    positionId = tokenId;
    emit Locked(tokenId);
}
safeClaim()

Collects accrued V3 trading fees to the owner. Does not touch liquidity. Reverts if nothing is locked yet.

ONLY OWNER
function safeClaim() external onlyOwner
    returns (uint256 amount0, uint256 amount1)
{
    uint256 id = positionId;
    if (id == 0) revert NothingLocked();

    (amount0, amount1) = positionManager.collect(
        CollectParams({
            tokenId: id,
            recipient: owner,
            amount0Max: type(uint128).max,
            amount1Max: type(uint128).max
        })
    );
    emit Claimed(amount0, amount1);
}
[ WHAT ISN'T THERE ]

Absence is the feature.

Safety here is mostly about what the contract cannot do. If a function that could unlock liquidity doesn't exist, it can't be called — by anyone, ever.

  • decreaseLiquidity
  • transfer / safeTransfer of the NFT
  • setOwner / renounce with a new fee sink
  • emergency withdraw
  • pause that freezes fee routing to someone else
[ FULL SOURCE ]

UniswapV3LpLocker.sol

GITHUB ↗
src/periphery/UniswapV3LpLocker.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {INonfungiblePositionManager} from "../interfaces/IUniswapV3.sol";

contract UniswapV3LpLocker {
    address public immutable owner;
    INonfungiblePositionManager public immutable positionManager;

    uint256 public positionId;

    error NotOwner();
    error ZeroAddress();
    error AlreadyLocked();
    error NothingLocked();
    error BadToken();

    event Locked(uint256 indexed tokenId);
    event Claimed(uint256 amount0, uint256 amount1);

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

    constructor(address positionManager_, address owner_) {
        if (positionManager_ == address(0) || owner_ == address(0)) {
            revert ZeroAddress();
        }
        positionManager = INonfungiblePositionManager(positionManager_);
        owner = owner_;
    }

    function burnLP(uint256 tokenId) external onlyOwner {
        if (positionId != 0) revert AlreadyLocked();
        if (tokenId == 0) revert BadToken();

        IERC721(address(positionManager))
            .transferFrom(msg.sender, address(this), tokenId);
        positionId = tokenId;
        emit Locked(tokenId);
    }

    function safeClaim() external onlyOwner
        returns (uint256 amount0, uint256 amount1)
    {
        uint256 id = positionId;
        if (id == 0) revert NothingLocked();

        (amount0, amount1) = positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: id,
                recipient: owner,
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );
        emit Claimed(amount0, amount1);
    }
}
[ NEXT ]

Liquidity that can't walk away.

When the locker is deployed and the seed NFT is burned in, this page will point at the live address. Until then the placeholder above is ready to copy — swap it the moment we go live.