HamsterLaunchpad
0xe4331d2b57ab6a4451a0f6f8e1a315824a2902dd
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
6,704 bytes
ABI entries
16
11 read · 2 write
License
none
Contract information
- Address
- 0xe4331d2b57ab6a4451a0f6f8e1a315824a2902dd
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xEb01Feac05…F9270E867B
- Creation tx
- 0x569782e7e4…ea543721f2
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (11)
FEE() → uint24
MAX_TICK() → int24
SUPPLY() → uint256
TICK_SPACING() → int24
WETH() → address
allTokens(uint256) → address
launches(address) → address, address, uint256, address
platform() → address
platformBps() → uint16
positionManager() → address
totalLaunches() → uint256
Events (2)
FeesClaimedLaunched
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_weth",
"type": "address"
},
{
"internalType": "address",
"name": "_pm",
"type": "address"
},
{
"internalType": "address",
"name": "_platform",
"type": "address"
},
{
"internalType": "uint16",
"name": "_platformBps",
"type": "uint16"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "toCreator0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "toCreator1",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "toPlatform0",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "toPlatform1",
"type": "uint256"
}
],
"name": "FeesClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "pool",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "symbol",
"type": "string"
}
],
"name": "Launched",
"type": "event"
},
{
"inputs": [],
"name": "FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_TICK",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "SUPPLY",
"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": "WETH",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allTokens",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "claimFees",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
}
],
"name": "launch",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "launches",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint256",
"name": "positionId",
"type": "uint256"
},
{
"internalType": "address",
"name": "pool",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "platform",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "platformBps",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "contract INonfungiblePositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalLaunches",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {HamsterToken} from "./HamsterToken.sol";
/// Minimal SushiSwap/Uniswap V3 interfaces (function selectors only — ABIs, not
/// implementations). We call the canonical V3 stack directly; no launchpad
/// middleman, no third-party factory.
interface INonfungiblePositionManager {
function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96)
external
payable
returns (address pool);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function mint(MintParams calldata p)
external
payable
returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
function collect(CollectParams calldata p) external payable returns (uint256 amount0, uint256 amount1);
}
interface IV3Pool {
function slot0()
external
view
returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool);
}
interface IERC20Min {
function approve(address s, uint256 v) external returns (bool);
function transfer(address to, uint256 v) external returns (bool);
function balanceOf(address a) external view returns (uint256);
}
/// @title HamsterLaunchpad
/// @notice HamsterBunker's own launchpad. It deploys an immutable HamsterToken,
/// opens a SushiSwap V3 pool for it, seeds the ENTIRE supply as
/// single-sided liquidity, and locks the LP position here forever (no
/// withdraw path — the LP can never be pulled). Trading fees accrue to
/// the locked position and are split creator / platform on claim.
///
/// Fully independent: tokens launch through THIS contract only. They
/// never touch any third-party launchpad. Source is open + verified.
///
/// The single-sided tick range is computed on-chain from the pool's
/// current tick, so the caller only needs to pass the opening price
/// (sqrtPriceX96). Nothing here can be steered to grief other launches.
contract HamsterLaunchpad {
address public immutable WETH;
INonfungiblePositionManager public immutable positionManager;
address public immutable platform; // HamsterBunker fee recipient
uint16 public immutable platformBps; // platform cut of trading fees (e.g. 1000 = 10%)
uint24 public constant FEE = 10000; // 1% pool
int24 public constant TICK_SPACING = 200; // 1% fee tier spacing
int24 public constant MAX_TICK = 887200; // 887272 max, aligned down to spacing
uint256 public constant SUPPLY = 1_000_000_000 ether; // fixed 1B per launch
struct Launch {
address token;
address creator;
uint256 positionId;
address pool;
}
mapping(address => Launch) public launches; // token => launch
address[] public allTokens;
event Launched(address indexed token, address indexed creator, address pool, uint256 positionId, string name, string symbol);
event FeesClaimed(address indexed token, uint256 toCreator0, uint256 toCreator1, uint256 toPlatform0, uint256 toPlatform1);
constructor(address _weth, address _pm, address _platform, uint16 _platformBps) {
require(_platformBps <= 2000, "platformBps too high"); // never more than 20%
WETH = _weth;
positionManager = INonfungiblePositionManager(_pm);
platform = _platform;
platformBps = _platformBps;
}
function totalLaunches() external view returns (uint256) {
return allTokens.length;
}
/// @param salt CREATE2 salt (backend picks it so the token address, and thus
/// its ordering vs WETH, is known before computing sqrtPriceX96).
/// @param sqrtPriceX96 opening price (backend computes it for the salt).
function launch(bytes32 salt, string calldata name, string calldata symbol, address creator, uint160 sqrtPriceX96)
external
returns (address token, uint256 positionId)
{
require(creator != address(0), "creator=0");
// 1. deploy the immutable token, full supply to this launchpad
HamsterToken t = new HamsterToken{salt: salt}(name, symbol, SUPPLY, address(this));
token = address(t);
require(launches[token].token == address(0), "exists");
// 2. order tokens and open the pool at the opening price
bool tokenIs0 = token < WETH;
(address token0, address token1) = tokenIs0 ? (token, WETH) : (WETH, token);
address pool = positionManager.createAndInitializePoolIfNecessary(token0, token1, FEE, sqrtPriceX96);
// 3. read the current tick and build a SINGLE-SIDED range holding only
// the token, so the whole supply sits above (token0) / below (token1)
// spot and gets sold into WETH as the price climbs.
(, int24 cur,,,,,) = IV3Pool(pool).slot0();
int24 tickLower;
int24 tickUpper;
if (tokenIs0) {
tickLower = (_floorDiv(cur, TICK_SPACING) + 1) * TICK_SPACING; // strictly above spot
tickUpper = MAX_TICK;
} else {
tickUpper = _floorDiv(cur, TICK_SPACING) * TICK_SPACING;
if (tickUpper >= cur) tickUpper -= TICK_SPACING; // strictly below spot
tickLower = -MAX_TICK;
}
// 4. seed ALL supply as single-sided liquidity and lock the position here
IERC20Min(token).approve(address(positionManager), SUPPLY);
(positionId,,,) = positionManager.mint(
INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: FEE,
tickLower: tickLower,
tickUpper: tickUpper,
amount0Desired: tokenIs0 ? SUPPLY : 0,
amount1Desired: tokenIs0 ? 0 : SUPPLY,
amount0Min: 0,
amount1Min: 0,
recipient: address(this), // the LP NFT stays here forever = locked
deadline: block.timestamp
})
);
launches[token] = Launch({token: token, creator: creator, positionId: positionId, pool: pool});
allTokens.push(token);
emit Launched(token, creator, pool, positionId, name, symbol);
}
/// Harvest the locked position's trading fees and split them creator/platform.
/// Permissionless — funds only ever go to the creator and the platform.
function claimFees(address token) external {
Launch memory L = launches[token];
require(L.token != address(0), "unknown token");
(uint256 amt0, uint256 amt1) = positionManager.collect(
INonfungiblePositionManager.CollectParams({
tokenId: L.positionId,
recipient: address(this),
amount0Max: type(uint128).max,
amount1Max: type(uint128).max
})
);
(address token0, address token1) = token < WETH ? (token, WETH) : (WETH, token);
uint256 p0 = (amt0 * platformBps) / 10000;
uint256 p1 = (amt1 * platformBps) / 10000;
_pay(token0, platform, p0);
_pay(token1, platform, p1);
_pay(token0, L.creator, amt0 - p0);
_pay(token1, L.creator, amt1 - p1);
emit FeesClaimed(token, amt0 - p0, amt1 - p1, p0, p1);
}
function _pay(address erc20, address to, uint256 v) internal {
if (v > 0) IERC20Min(erc20).transfer(to, v);
}
/// floor division for ticks (Solidity `/` truncates toward zero).
function _floorDiv(int24 a, int24 b) internal pure returns (int24 q) {
q = a / b;
if ((a % b != 0) && ((a < 0) != (b < 0))) q -= 1;
}
}
Chain explorer5032msChain node77ms