UniLaunchPad

0xc9c85b08354dfb2a760ab3042a7d9b86ba98197e

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
7,315 bytes
ABI entries
31
11 read · 5 write
License
none

Contract information

Address
0xc9c85b08354dfb2a760ab3042a7d9b86ba98197e
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x1e7a8e531c…449a63c56e

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (11)

POOL_FEE()uint24
TOTAL_SUPPLY()uint256
creatorOf(address)address
graduator()address
locker()address
owner()address
poolOf(address)address
positionManager()address
tokenImplementation()address
v3Factory()address
weth()address

Events (5)

GraduatorUpdatedLaunchedLockerSetOwnerUpdatedTokenCreated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "v3Factory_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "positionManager_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "BadCallback",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BadValue",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LockerAlreadySet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LockerNotSet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PoolExists",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SlippageExceeded",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "graduator",
        "type": "address"
      }
    ],
    "name": "GraduatorUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "positionId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "initialBuyEth",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "initialTokensOut",
        "type": "uint256"
      }
    ],
    "name": "Launched",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "locker",
        "type": "address"
      }
    ],
    "name": "LockerSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnerUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "indexed": false,
        "internalType": "string",
        "name": "metadataURI",
        "type": "string"
      }
    ],
    "name": "TokenCreated",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "POOL_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOTAL_SUPPLY",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "metadataURI",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "imageURI",
        "type": "string"
      },
      {
        "internalType": "uint256",
        "name": "minInitialTokensOut",
        "type": "uint256"
      },
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "create",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "creatorOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "graduator",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "locker",
    "outputs": [
      {
        "internalType": "contract LpLocker",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "poolOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionManager",
    "outputs": [
      {
        "internalType": "contract INonfungiblePositionManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "graduator_",
        "type": "address"
      }
    ],
    "name": "setGraduator",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "locker_",
        "type": "address"
      }
    ],
    "name": "setLocker",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "setOwner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tokenImplementation",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "int256",
        "name": "amount0Delta",
        "type": "int256"
      },
      {
        "internalType": "int256",
        "name": "amount1Delta",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "uniswapV3SwapCallback",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "v3Factory",
    "outputs": [
      {
        "internalType": "contract IUniswapV3Factory",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract IWETH9",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

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

import {UniPadToken} from "./UniPadToken.sol";
import {LpLocker} from "./LpLocker.sol";
import {
    IUniswapV3Factory,
    IUniswapV3Pool,
    INonfungiblePositionManager,
    IWETH9
} from "./interfaces/IUniswapV3.sol";

contract UniLaunchPad {

    uint256 public constant TOTAL_SUPPLY = 1_000_000_000e18;
    uint24 public constant POOL_FEE = 10_000;

    int24 internal constant START_TICK = 204_200;
    int24 internal constant MIN_TICK = -887_200;
    int24 internal constant MAX_TICK = 887_200;

    uint160 internal constant SQRT_PRICE_NEG_START = 2917122157712197017744680;
    uint160 internal constant SQRT_PRICE_POS_START = 2151813121295408910812139624586144;

    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    address public immutable tokenImplementation;
    IWETH9 public immutable weth;
    IUniswapV3Factory public immutable v3Factory;
    INonfungiblePositionManager public immutable positionManager;

    address public owner;
    LpLocker public locker;

    address public graduator;

    mapping(address => address) public poolOf;
    mapping(address => address) public creatorOf;

    address private expectedPool;

    uint256 private locked = 1;

    event TokenCreated(
        address indexed token,
        address indexed creator,
        string name,
        string symbol,
        string metadataURI
    );
    event Launched(
        address indexed token,
        address indexed pool,
        uint256 indexed positionId,
        address creator,
        uint256 initialBuyEth,
        uint256 initialTokensOut
    );
    event OwnerUpdated(address owner);
    event LockerSet(address locker);
    event GraduatorUpdated(address graduator);

    error NotOwner();
    error LockerAlreadySet();
    error LockerNotSet();
    error BadValue();
    error PoolExists();
    error SlippageExceeded();
    error Reentrancy();
    error BadCallback();

    constructor(address weth_, address v3Factory_, address positionManager_) {
        tokenImplementation = address(new UniPadToken());
        weth = IWETH9(weth_);
        v3Factory = IUniswapV3Factory(v3Factory_);
        positionManager = INonfungiblePositionManager(positionManager_);
        owner = msg.sender;
    }

    modifier nonReentrant() {
        if (locked != 1) revert Reentrancy();
        locked = 2;
        _;
        locked = 1;
    }

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

    function setLocker(address locker_) external onlyOwner {
        if (address(locker) != address(0)) revert LockerAlreadySet();
        locker = LpLocker(locker_);
        emit LockerSet(locker_);
    }

    function setOwner(address newOwner) external onlyOwner {
        owner = newOwner;
        emit OwnerUpdated(newOwner);
    }

    function setGraduator(address graduator_) external onlyOwner {
        graduator = graduator_;
        emit GraduatorUpdated(graduator_);
    }

    function create(
        string calldata name,
        string calldata symbol,
        string calldata metadataURI,
        string calldata imageURI,
        uint256 minInitialTokensOut,
        bytes32 salt
    ) external payable nonReentrant returns (address token) {
        if (address(locker) == address(0)) revert LockerNotSet();

        token = _cloneDeterministic(tokenImplementation, keccak256(abi.encodePacked(msg.sender, salt)));
        UniPadToken(token).initialize(name, symbol, TOTAL_SUPPLY, metadataURI, imageURI);

        bool tokenIs0 = token < address(weth);
        (address token0, address token1) = tokenIs0 ? (token, address(weth)) : (address(weth), token);
        uint160 initSqrt = tokenIs0 ? SQRT_PRICE_NEG_START : SQRT_PRICE_POS_START;
        int24 tickLower = tokenIs0 ? -START_TICK : MIN_TICK;
        int24 tickUpper = tokenIs0 ? MAX_TICK : START_TICK;

        if (v3Factory.getPool(token0, token1, POOL_FEE) != address(0)) revert PoolExists();

        address pool = positionManager.createAndInitializePoolIfNecessary(token0, token1, POOL_FEE, initSqrt);
        poolOf[token] = pool;
        creatorOf[token] = msg.sender;

        (uint160 sqrtP,,,,,,) = IUniswapV3Pool(pool).slot0();
        if (sqrtP != initSqrt) revert PoolExists();

        UniPadToken(token).setTradeConfig(pool, msg.sender, address(locker));

        UniPadToken(token).approve(address(positionManager), TOTAL_SUPPLY);
        (uint256 tokenId,,,) = positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: token0,
                token1: token1,
                fee: POOL_FEE,
                tickLower: tickLower,
                tickUpper: tickUpper,
                amount0Desired: tokenIs0 ? TOTAL_SUPPLY : 0,
                amount1Desired: tokenIs0 ? 0 : TOTAL_SUPPLY,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(locker),
                deadline: block.timestamp
            })
        );
        UniPadToken(token).approve(address(positionManager), 0);
        locker.register(tokenId, msg.sender);

        uint256 dust = UniPadToken(token).balanceOf(address(this));
        if (dust > 0) UniPadToken(token).transfer(address(0xdead), dust);

        emit TokenCreated(token, msg.sender, name, symbol, metadataURI);

        uint256 tokensOut = 0;
        if (msg.value > 0) {
            tokensOut = _initialBuy(token, pool, tokenIs0, msg.value, minInitialTokensOut, msg.sender);
        }
        emit Launched(token, pool, tokenId, msg.sender, msg.value, tokensOut);
    }

    function _initialBuy(
        address token,
        address pool,
        bool tokenIs0,
        uint256 ethIn,
        uint256 minOut,
        address recipient
    ) internal returns (uint256 tokensOut) {
        weth.deposit{value: ethIn}();

        bool zeroForOne = !tokenIs0;
        uint160 limit = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1;

        expectedPool = pool;
        (int256 a0, int256 a1) = IUniswapV3Pool(pool).swap(
            recipient, zeroForOne, int256(ethIn), limit, abi.encode(token, tokenIs0)
        );
        expectedPool = address(0);

        tokensOut = tokenIs0 ? uint256(-a0) : uint256(-a1);
        if (tokensOut < minOut) revert SlippageExceeded();
    }

    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
        if (msg.sender != expectedPool || expectedPool == address(0)) revert BadCallback();
        (address token, bool tokenIs0) = abi.decode(data, (address, bool));
        if (amount0Delta > 0) {
            if (tokenIs0) UniPadToken(token).transfer(msg.sender, uint256(amount0Delta));
            else weth.transfer(msg.sender, uint256(amount0Delta));
        }
        if (amount1Delta > 0) {
            if (tokenIs0) weth.transfer(msg.sender, uint256(amount1Delta));
            else UniPadToken(token).transfer(msg.sender, uint256(amount1Delta));
        }
    }

    function _cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        bytes memory bytecode = abi.encodePacked(
            hex"3d602d80600a3d3981f3363d3d373d3d3d363d73",
            implementation,
            hex"5af43d82803e903d91602b57fd5bf3"
        );
        /// @solidity memory-safe-assembly
        assembly {
            instance := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
        }
        if (instance == address(0)) revert BadValue();
    }

    receive() external payable {
        if (msg.sender != address(weth)) revert BadValue();
    }
}
Chain explorer2935msChain node80ms