AtomicGMEMERelaunch

0xcff179cb6e8b0de7968daf4870b6424a63762c47

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
873 bytes
ABI entries
23
13 read · 0 write
License
none

Contract information

Address
0xcff179cb6e8b0de7968daf4870b6424a63762c47
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x5d50cb1d75…555f4d6161

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (13)

CANONICAL_GME()address
DEAD()address
LP_FEE()uint24
PERMIT2()address
POSITION_MANAGER()address
ROBINHOOD_CHAIN_ID()uint256
TICK_SPACING()int24
UNIVERSAL_ROUTER()address
ethPositionTokenId()uint256
feeCollector()address
gmePositionTokenId()uint256
launchRecipient()address
token()address

Events (1)

AtomicLaunch

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "collector",
        "type": "address"
      },
      {
        "internalType": "int24",
        "name": "mainAbsoluteTick",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "ethPreBuyTick",
        "type": "int24"
      },
      {
        "internalType": "uint128",
        "name": "ethLiquidityBudget",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "initialBuyETH",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "minimumTokensBought",
        "type": "uint128"
      }
    ],
    "stateMutability": "payable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "InitialBuyFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidFeeCollector",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRecipient",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "int24",
        "name": "tick",
        "type": "int24"
      }
    ],
    "name": "InvalidTick",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "supplied",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "required",
        "type": "uint256"
      }
    ],
    "name": "InvalidValue",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "PositionNotLocked",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TransferFailed",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "actual",
        "type": "uint256"
      }
    ],
    "name": "WrongChain",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "launchRecipient",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "feeCollector",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethPositionTokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "gmePositionTokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "initialBuyETH",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokensBought",
        "type": "uint256"
      }
    ],
    "name": "AtomicLaunch",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "CANONICAL_GME",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEAD",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "LP_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PERMIT2",
    "outputs": [
      {
        "internalType": "contract IAllowanceTransfer",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POSITION_MANAGER",
    "outputs": [
      {
        "internalType": "contract IPositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ROBINHOOD_CHAIN_ID",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TICK_SPACING",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "UNIVERSAL_ROUTER",
    "outputs": [
      {
        "internalType": "contract IUniversalRouterGMEME",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ethPositionTokenId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeCollector",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "gmePositionTokenId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchRecipient",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "token",
    "outputs": [
      {
        "internalType": "contract GameStopMemeStock",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

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

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {Pool} from "@uniswap/v4-core/src/libraries/Pool.sol";
import {IPositionManager} from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol";
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
import {LiquidityAmounts} from "@uniswap/v4-periphery/src/libraries/LiquidityAmounts.sol";
import {PositionFeesForwarder} from "@uniswap/liquidity-launcher/src/periphery/PositionFeesForwarder.sol";
import {GameStopMemeStock} from "./GameStopMemeStock.sol";

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

/// @notice One-shot GMEME relaunch. The constructor atomically deploys the token, creates and permanently
/// locks both pools, performs the initial market buy, and refunds unused ETH. It has no callable admin logic.
contract AtomicGMEMERelaunch {
    uint256 public constant ROBINHOOD_CHAIN_ID = 4663;
    uint24 public constant LP_FEE = 10_000;
    int24 public constant TICK_SPACING = 200;

    address public constant CANONICAL_GME = 0x1b0E319c6A659F002271B69dB8A7df2F911c153E;
    IPositionManager public constant POSITION_MANAGER =
        IPositionManager(0x58daec3116aae6D93017bAAea7749052E8a04fA7);
    IAllowanceTransfer public constant PERMIT2 =
        IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3);
    IUniversalRouterGMEME public constant UNIVERSAL_ROUTER =
        IUniversalRouterGMEME(0x8876789976dEcBfCbBbe364623C63652db8C0904);
    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    GameStopMemeStock public immutable token;
    address public immutable launchRecipient;
    address public immutable feeCollector;
    uint256 public immutable ethPositionTokenId;
    uint256 public immutable gmePositionTokenId;

    struct RouterExactInputSingleParams {
        PoolKey poolKey;
        bool zeroForOne;
        uint128 amountIn;
        uint128 amountOutMinimum;
        uint256 minHopPriceX36;
        bytes hookData;
    }

    event AtomicLaunch(
        address indexed token,
        address indexed launchRecipient,
        address indexed feeCollector,
        uint256 ethPositionTokenId,
        uint256 gmePositionTokenId,
        uint256 initialBuyETH,
        uint256 tokensBought
    );

    error WrongChain(uint256 actual);
    error InvalidRecipient();
    error InvalidFeeCollector();
    error InvalidValue(uint256 supplied, uint256 required);
    error InvalidTick(int24 tick);
    error PositionNotLocked(uint256 tokenId);
    error InitialBuyFailed();
    error TransferFailed();

    constructor(
        address recipient,
        address collector,
        int24 mainAbsoluteTick,
        int24 ethPreBuyTick,
        uint128 ethLiquidityBudget,
        uint128 initialBuyETH,
        uint128 minimumTokensBought
    ) payable {
        if (block.chainid != ROBINHOOD_CHAIN_ID) revert WrongChain(block.chainid);
        if (
            recipient == address(0) || recipient == address(1) || recipient == address(2)
                || recipient.code.length != 0
        ) revert InvalidRecipient();
        if (msg.value != uint256(ethLiquidityBudget) + uint256(initialBuyETH)) {
            revert InvalidValue(msg.value, uint256(ethLiquidityBudget) + uint256(initialBuyETH));
        }
        if (mainAbsoluteTick <= 0 || mainAbsoluteTick % TICK_SPACING != 0) revert InvalidTick(mainAbsoluteTick);
        if (ethPreBuyTick <= 0) revert InvalidTick(ethPreBuyTick);

        PositionFeesForwarder forwarder = PositionFeesForwarder(payable(collector));
        if (
            collector.code.length == 0 || address(forwarder.positionManager()) != address(POSITION_MANAGER)
                || forwarder.operator() != DEAD || forwarder.timelockBlockNumber() != type(uint256).max
                || forwarder.feeRecipient() != recipient
        ) revert InvalidFeeCollector();

        launchRecipient = recipient;
        feeCollector = collector;
        token = new GameStopMemeStock(address(this));

        if (!IERC20(address(token)).approve(address(PERMIT2), type(uint256).max)) revert TransferFailed();
        PERMIT2.approve(address(token), address(POSITION_MANAGER), type(uint160).max, type(uint48).max);

        PoolKey memory ethKey = PoolKey(
            Currency.wrap(address(0)), Currency.wrap(address(token)), LP_FEE, TICK_SPACING, IHooks(address(0))
        );
        uint160 ethSqrtPriceX96 = TickMath.getSqrtPriceAtTick(ethPreBuyTick);
        if (POSITION_MANAGER.initializePool(ethKey, ethSqrtPriceX96) != ethPreBuyTick) {
            revert InvalidTick(ethPreBuyTick);
        }

        uint128 ethLiquidity = LiquidityAmounts.getLiquidityForAmount0(
            ethSqrtPriceX96,
            TickMath.getSqrtPriceAtTick(TickMath.maxUsableTick(TICK_SPACING)),
            ethLiquidityBudget
        );
        uint128 maxPerTick = Pool.tickSpacingToMaxLiquidityPerTick(TICK_SPACING);
        if (ethLiquidity > maxPerTick) ethLiquidity = maxPerTick;

        ethPositionTokenId = POSITION_MANAGER.nextTokenId();
        _mintPosition(
            ethKey,
            TickMath.minUsableTick(TICK_SPACING),
            TickMath.maxUsableTick(TICK_SPACING),
            ethLiquidity,
            ethLiquidityBudget,
            type(uint128).max,
            collector,
            ethLiquidityBudget
        );

        bool tokenIsCurrency0 = address(token) < CANONICAL_GME;
        int24 mainPoolTick = tokenIsCurrency0 ? -mainAbsoluteTick : mainAbsoluteTick;
        PoolKey memory gmeKey = tokenIsCurrency0
            ? PoolKey(
                Currency.wrap(address(token)),
                Currency.wrap(CANONICAL_GME),
                LP_FEE,
                TICK_SPACING,
                IHooks(address(0))
            )
            : PoolKey(
                Currency.wrap(CANONICAL_GME),
                Currency.wrap(address(token)),
                LP_FEE,
                TICK_SPACING,
                IHooks(address(0))
            );
        uint160 mainSqrtPriceX96 = TickMath.getSqrtPriceAtTick(mainPoolTick);
        if (POSITION_MANAGER.initializePool(gmeKey, mainSqrtPriceX96) != mainPoolTick) {
            revert InvalidTick(mainPoolTick);
        }

        uint256 mainTokenAmount = token.balanceOf(address(this));
        int24 mainLower = tokenIsCurrency0 ? mainPoolTick : TickMath.minUsableTick(TICK_SPACING);
        int24 mainUpper = tokenIsCurrency0 ? TickMath.maxUsableTick(TICK_SPACING) : mainPoolTick;
        uint128 mainLiquidity = tokenIsCurrency0
            ? LiquidityAmounts.getLiquidityForAmount0(
                TickMath.getSqrtPriceAtTick(mainLower), TickMath.getSqrtPriceAtTick(mainUpper), mainTokenAmount
            )
            : LiquidityAmounts.getLiquidityForAmount1(
                TickMath.getSqrtPriceAtTick(mainLower), TickMath.getSqrtPriceAtTick(mainUpper), mainTokenAmount
            );
        if (mainLiquidity > maxPerTick) mainLiquidity = maxPerTick;

        gmePositionTokenId = POSITION_MANAGER.nextTokenId();
        _mintPosition(
            gmeKey,
            mainLower,
            mainUpper,
            mainLiquidity,
            tokenIsCurrency0 ? uint128(mainTokenAmount) : uint128(0),
            tokenIsCurrency0 ? uint128(0) : uint128(mainTokenAmount),
            collector,
            0
        );

        uint256 roundingRemainder = token.balanceOf(address(this));
        if (roundingRemainder != 0 && !token.transfer(DEAD, roundingRemainder)) revert TransferFailed();

        _initialBuy(ethKey, initialBuyETH, minimumTokensBought);
        uint256 bought = token.balanceOf(address(this));
        if (bought < minimumTokensBought || !token.transfer(recipient, bought)) revert InitialBuyFailed();

        if (IERC721(address(POSITION_MANAGER)).ownerOf(ethPositionTokenId) != collector) {
            revert PositionNotLocked(ethPositionTokenId);
        }
        if (IERC721(address(POSITION_MANAGER)).ownerOf(gmePositionTokenId) != collector) {
            revert PositionNotLocked(gmePositionTokenId);
        }

        emit AtomicLaunch(address(token), recipient, collector, ethPositionTokenId, gmePositionTokenId, initialBuyETH, bought);
    }

    function _mintPosition(
        PoolKey memory key,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity,
        uint128 amount0Max,
        uint128 amount1Max,
        address owner,
        uint256 nativeValue
    ) private {
        bytes memory actions = abi.encodePacked(uint8(Actions.MINT_POSITION), uint8(Actions.SETTLE_PAIR));
        bytes[] memory params = new bytes[](2);
        params[0] = abi.encode(key, tickLower, tickUpper, liquidity, amount0Max, amount1Max, owner, bytes(""));
        params[1] = abi.encode(key.currency0, key.currency1);
        POSITION_MANAGER.modifyLiquidities{value: nativeValue}(
            abi.encode(actions, params), block.timestamp + 10 minutes
        );
    }

    function _initialBuy(PoolKey memory ethKey, uint128 amountIn, uint128 minimumOut) private {
        bytes memory actions = abi.encodePacked(
            uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL)
        );
        bytes[] memory params = new bytes[](3);
        params[0] = abi.encode(
            RouterExactInputSingleParams({
                poolKey: ethKey,
                zeroForOne: true,
                amountIn: amountIn,
                amountOutMinimum: minimumOut,
                minHopPriceX36: 0,
                hookData: bytes("")
            })
        );
        params[1] = abi.encode(Currency.wrap(address(0)), uint256(amountIn));
        params[2] = abi.encode(Currency.wrap(address(token)), uint256(minimumOut));

        bytes[] memory inputs = new bytes[](1);
        inputs[0] = abi.encode(actions, params);
        UNIVERSAL_ROUTER.execute{value: amountIn}(hex"10", inputs, block.timestamp + 10 minutes);
    }
}
Chain explorer3435msChain node79ms