Launcher

0x0578f2fb55882e18ce121014d0ef79563f5794f9

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
3,646 bytes
ABI entries
12
5 read · 1 write
License
mit

Contract information

Address
0x0578f2fb55882e18ce121014d0ef79563f5794f9
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xfcd37b3aa7…fc6588e613

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (5)

lpTokenOf(address)uint256
permit2()address
poolManager()address
posManager()address
router()address

Events (1)

LpMinted

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_pm",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_posMgr",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_router",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_permit2",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "LenMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RefundFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroSniper",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "lpTokenId",
        "type": "uint256"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "LpMinted",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "address",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "bytes",
        "name": "mintUnlockData",
        "type": "bytes"
      },
      {
        "internalType": "uint256",
        "name": "mintEthValue",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "buyCommands",
        "type": "bytes"
      },
      {
        "internalType": "bytes[]",
        "name": "buyInputs",
        "type": "bytes[]"
      },
      {
        "internalType": "uint256[]",
        "name": "buyEthValues",
        "type": "uint256[]"
      },
      {
        "internalType": "address[]",
        "name": "sniperWallets",
        "type": "address[]"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "name": "atomicLaunch",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "lpTokenOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "permit2",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "posManager",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "router",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

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

interface IERC20 {
    function approve(address spender, uint256 value) external returns (bool);
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function balanceOf(address) external view returns (uint256);
}

library SafeT {
    function safeTransfer(IERC20 t, address to, uint256 amount) internal {
        (bool ok, bytes memory data) = address(t).call(
            abi.encodeWithSelector(t.transfer.selector, to, amount)
        );
        require(ok && (data.length == 0 || abi.decode(data, (bool))), "Launcher: transfer fail");
    }
    function safeTransferFrom(IERC20 t, address from, address to, uint256 amount) internal {
        (bool ok, bytes memory data) = address(t).call(
            abi.encodeWithSelector(t.transferFrom.selector, from, to, amount)
        );
        require(ok && (data.length == 0 || abi.decode(data, (bool))), "Launcher: transferFrom fail");
    }
    function safeApprove(IERC20 t, address spender, uint256 amount) internal {
        (bool ok, bytes memory data) = address(t).call(
            abi.encodeWithSelector(t.approve.selector, spender, amount)
        );
        require(ok && (data.length == 0 || abi.decode(data, (bool))), "Launcher: approve fail");
    }
}

struct PoolKey {
    address currency0;
    address currency1;
    uint24  fee;
    int24   tickSpacing;
    address hooks;
}

interface IPoolManager {
    function initialize(PoolKey memory key, uint160 sqrtPriceX96) external returns (int24);
}

interface IPositionManager {
    function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable;
    function nextTokenId() external view returns (uint256);
}

interface IPermit2 {
    function approve(address token, address spender, uint160 amount, uint48 expiration) external;
}

interface IUniversalRouter {
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}

contract Launcher {
    using SafeT for IERC20;

    address public immutable poolManager;
    address public immutable posManager;
    address public immutable router;
    address public immutable permit2;

    error LenMismatch();
    error RefundFailed();
    error ZeroSniper();

    mapping(address => uint256) public lpTokenOf;
    event LpMinted(address indexed token, uint256 indexed lpTokenId, address indexed recipient);

    constructor(address _pm, address _posMgr, address _router, address _permit2) {
        poolManager = _pm;
        posManager  = _posMgr;
        router      = _router;
        permit2     = _permit2;
    }

    function atomicLaunch(
        address token,
        uint256 tokenAmount,
        PoolKey calldata key,
        uint160 sqrtPriceX96,
        bytes calldata mintUnlockData,
        uint256 mintEthValue,
        bytes calldata buyCommands,
        bytes[] calldata buyInputs,
        uint256[] calldata buyEthValues,
        address[] calldata sniperWallets,
        uint256 deadline
    ) external payable {
        if (buyInputs.length != buyEthValues.length) revert LenMismatch();
        if (buyInputs.length != sniperWallets.length) revert LenMismatch();

        for (uint256 j = 0; j < sniperWallets.length; ++j) {
            if (sniperWallets[j] == address(0)) revert ZeroSniper();
        }

        IERC20(token).safeTransferFrom(msg.sender, address(this), tokenAmount);
        IERC20(token).safeApprove(permit2, type(uint256).max);
        IPermit2(permit2).approve(token, posManager, type(uint160).max, type(uint48).max);

        IPoolManager(poolManager).initialize(key, sqrtPriceX96);

        uint256 lpId = IPositionManager(posManager).nextTokenId();
        IPositionManager(posManager).modifyLiquidities{value: mintEthValue}(mintUnlockData, deadline);
        lpTokenOf[token] = lpId;
        emit LpMinted(token, lpId, msg.sender);

        uint256 n = buyInputs.length;
        for (uint256 i = 0; i < n;) {
            uint256 pre = IERC20(token).balanceOf(address(this));
            bytes[] memory single = new bytes[](1);
            single[0] = buyInputs[i];
            IUniversalRouter(router).execute{value: buyEthValues[i]}(buyCommands, single, deadline);
            uint256 got = IERC20(token).balanceOf(address(this)) - pre;
            if (got > 0) IERC20(token).safeTransfer(sniperWallets[i], got);
            unchecked { ++i; }
        }

        uint256 leftoverTok = IERC20(token).balanceOf(address(this));
        if (leftoverTok > 0) IERC20(token).safeTransfer(msg.sender, leftoverTok);

        uint256 leftover = address(this).balance;
        if (leftover > 0) {
            (bool ok,) = msg.sender.call{value: leftover}("");
            if (!ok) revert RefundFailed();
        }
    }

    receive() external payable {}
}
Chain explorer3679msChain node83ms