# slop — token launchpad (tokenslop.fun) · MAINNET > slop is a permissionless fair-launch bonding-curve token launchpad. Post the > meme, mint the coin, ride the curve. Agents are first-class users: everything > a human can do here, you can do over MCP or plain HTTP. Non-custodial — we > never hold keys or funds. Runs on Robinhood Chain mainnet, chain id 4663 > (ETH gas, Arbitrum-stack L2). Real money — extreme risk. ## How it works (for agents) - Anyone (including you) can launch a token. The full 1B supply is minted to a bonding curve; nobody gets a free allocation. - 800M tokens trade on a constant-product curve; price rises deterministically with ETH in. When all 800M sell, the token GRADUATES (CurveCompleted event) and its liquidity migrates to a locked DEX pool (LP is protocol-owned and non-withdrawable by design). - Fees: 1% curve trade fee + flat 0.0005 ETH launch fee — always read LIVE from the contract (launchFeeWei / tradeFeeBps via GET /v1/config + /v1/quote); never hardcode. The trade fee is hard-capped at 2% in code. Every fee funds a provably unruggable protocol: verified source, graduation LP locked forever, and the token's CREATOR earns 24% of graduated-pool fees via the FeeSplitter (non-custodial, claimable anytime). Curve reserves can never be withdrawn by anyone. ## Connect - MCP server (stdio): `npx slop-mcp` — tools: get_new_launches, list_tokens, get_token, get_trades, get_candles, get_holders, get_quote, get_wallet, launch_token, buy_token, sell_token. Env: API_URL, RPC_URL, optional AGENT_PRIVATE_KEY (local signing; guard your keys — this is mainnet). - REST API: see /v1/openapi.yaml on the API host, or /llms-full.txt here (this file + the OpenAPI spec inline). Start with GET /v1/config (live contract addresses), GET /v1/tokens, GET /v1/quote. - No key? POST /v1/tx/launch|buy|sell returns an unsigned transaction AND a `signingUrl` — give that link (or a QR of it) to your human; it opens a one-tap review-and-sign page. Keys never leave their wallet. ## Chain facts - Chain id: 4663 (Robinhood Chain mainnet), currency ETH - Public RPC: https://rpc.mainnet.chain.robinhood.com - Explorer: https://robinhoodchain.blockscout.com - Bridge in (seconds via Relay): https://relay.link/bridge/robinhood (canonical, slower: https://portal.arbitrum.io/bridge?destinationChain=robinhood-chain) - Contract addresses: always read live from GET /v1/config — do not hardcode. ## Rules - MAINNET — real ETH, real risk. Only trade what you can afford to lose. - Amounts: ETH in wei, tokens in 18-decimal base units, as decimal strings. - Quotes include the (currently 1%) trade fee; use slippage protection (minTokensOut / minEthOut). Launches must include launchFeeWei (currently 0.0005 ETH) in msg.value. - Be a good bot: poll /v1/tokens no faster than every 2–3s. ## Slop Studio — render promo content for RHC memecoins (PAID API, no free tier) - What: text packs, image cards + video clips built from LIVE chain data (price, holders, volume, chart — honest numbers, can't be faked). You render it, YOU post it. EVERY render is paid — there is no free tier. - Base URL: https://studio-api.tokenslop.fun/v1 (separate from the launchpad API). - Templates/prices: GET /v1/templates — never hardcode ids or prices. Launch card: text-pack $0.50 · image cards $1 · spotlight-clip $3 · board-clip $5. Payment rails come from its `payments` object ({card, chainPay}) — feature-detect, never assume (chainPay is a parked rail, false in prod). - Card checkout (retail, no account): POST /v1/checkout {kind:"render", template, chain:"rhc", tokenAddress, links?:{website,x}, sponsored?} → {orderId, checkoutUrl} → a HUMAN pays the Stripe link (statement: SLOPSTUDIO; ~30 min expiry) → the webhook enqueues the render AUTOMATICALLY (do NOT call /v1/render) → poll GET /v1/checkout/{orderId} (created → paid → consumed + jobId) → poll GET /v1/jobs/{jobId} every 2–5s until done → result.mediaUrl (presigned, EXPIRES — download promptly) + result.caption (ready-to-post text from the same snapshot). A failed paid job resets its order → ONE free retry: POST /v1/render {payment:{orderId}}. - API keys (credits — the autonomous path): POST /v1/keys (secret shown once) → fund via POST /v1/checkout {kind:"credits", packId} (Bearer required; packs $15→1,650cr $40→4,600cr $100→12,500cr; 1 credit = $0.01) → Bearer sk_live_… on POST /v1/render debits credits; failed jobs auto-refund; GET /v1/keys/me = balance. Bare /v1/render (no key, no paid order) → 402. - Eligibility: chain-native meme/community tokens ONLY. Equity-category (tokenized stocks), security-like, risk-flagged, and unbucketable tokens are refused at checkout AND render intake → 403 token_not_eligible (no override). - Disclosure: every retail render carries a non-removable "paid promotional content · made with Slop Studio" line ("sponsored by the $SYM team" when sponsored:true) + a "Live $SYM stats on tokenslop.fun" outro. - MCP (same npx slop-mcp server): list_content_templates, create_content, get_content_job, get_content_checkout (returns {orderId, checkoutUrl} for a human to pay; poll it with {order_id} for the jobId). Env: STUDIO_API_URL, optional STUDIO_API_KEY (credits). Chain-pay quote tools exist ONLY when the catalog advertises payments.chainPay:true (parked; hidden in prod). - Errors are stable machine codes — switch on error.code (payment_required, order_unpaid, order_consumed, order_mismatch, token_not_eligible, insufficient_credits, rate_limited, token_risk_blocked, …). - Human door: https://tokenslop.fun/studio · dev docs: /developers ## OpenAPI (inline copy — live source of truth: GET /v1/openapi.yaml on the API host) ```yaml openapi: 3.1.0 info: title: Launchpad API (Robinhood Chain testnet) version: 0.1.0 description: | Public, keyless, non-custodial API for a fair-launch bonding-curve token launchpad on Robinhood Chain (chain id 46630, ETH gas). Read endpoints return market data; POST /v1/tx/* endpoints return UNSIGNED transactions plus a `signingUrl` the human can open to review and sign in one tap — the API never holds keys or funds. Amounts are decimal strings in wei (ETH) or 18-decimal base units (tokens). TESTNET ONLY. servers: - url: http://localhost:4010 paths: /v1/config: get: summary: Chain id, launchpad/fee-vault addresses, supply constants responses: { "200": { description: OK } } /v1/tokens: get: summary: List/search tokens (the launch feed) parameters: - { name: sort, in: query, schema: { type: string, enum: [new, volume, mcap, trades] } } - { name: q, in: query, description: "name, ticker, contract address, or creator address", schema: { type: string } } - { name: limit, in: query, schema: { type: integer, maximum: 200 } } responses: { "200": { description: "tokens[] with priceWei, marketCapWei, curveProgress (0..1 to graduation), change1h/24h, spark[]" } } /v1/tokens/{address}: get: summary: One token's full state parameters: [{ name: address, in: path, required: true, schema: { type: string } }] responses: { "200": { description: OK }, "404": { description: unknown token } } /v1/tokens/{address}/trades: get: summary: Recent trades (newest first) parameters: [{ name: address, in: path, required: true, schema: { type: string } }] responses: { "200": { description: OK } } /v1/tokens/{address}/candles: get: summary: OHLCV candles (prices in wei per token) parameters: - { name: address, in: path, required: true, schema: { type: string } } - { name: interval, in: query, description: seconds, schema: { type: integer, default: 300 } } responses: { "200": { description: OK } } /v1/tokens/{address}/holders: get: summary: Top holders with supply percentages parameters: [{ name: address, in: path, required: true, schema: { type: string } }] responses: { "200": { description: OK } } /v1/trades/recent: get: summary: Latest trades across all tokens (the live feed) responses: { "200": { description: OK } } /v1/chain/tokens: get: summary: Full ranked RHC token universe (chain-wide discovery, actives first, long tail included) description: | THE LIST CONTRACT the discovery board renders. Served from the cached, SQLite-persisted universe crawl (never a per-request Blockscout hit). Response: { source, fetchedTop, universeComplete, fetchedAt, activeCount, total, # ranked rows matching the filters, BEFORE paging count, offset, # this page categories: {memecoin, stablecoin, equity, wrapped}, tokens: [{ address, name, symbol, decimals, priceUsd, marketCapUsd, volume24hUsd, holders (int|null — null = unknown), iconUrl, reputation, category, active, copycat?, dupes? (+change1h/change24h/pairAddress/liquidityUsd/txns24h when CHAIN_STORE_ENABLED=1) }] } universeComplete=false ⇒ Blockscout degraded mid-crawl; serving the best-known snapshot (monitor via /v1/health.universe). parameters: - { name: category, in: query, schema: { type: string, enum: [memecoin, stablecoin, equity, wrapped, all], default: memecoin } } - { name: limit, in: query, schema: { type: integer, default: 500, maximum: 5000 } } - { name: offset, in: query, schema: { type: integer, default: 0 } } - { name: all, in: query, description: include scam-flagged/uncategorized rows, schema: { type: boolean } } responses: { "200": { description: "see endpoint description" } } /v1/chain/search: get: summary: Full-universe token search (any of the ~66k RHC tokens, long tail included) description: | Proxies Blockscout server-side search, then enriches hits from the crawled universe + a bounded round of token-detail fetches so rows carry REAL holders/mcap/volume (Blockscout's search payload has none — a raw proxy showed "0 holders" on every row). holders is null when genuinely unknown; clients must hide it, never print 0. Also the backend of the slop-scope cross-search fallback (no dead-end searches). parameters: [{ name: q, in: query, required: true, schema: { type: string } }] responses: { "200": { description: "{ query, tokens: ChainToken[] } ranked by mcap, then holders" } } /v1/chain/tokens/{address}/candles: get: summary: First-party OHLCV for any pooled RHC token (persistent Swap-event candles; falls back to a live pool scan for untracked tokens and promotes them) description: | THE CHART CONTRACT for external tokens (/v1/chain/candles?token= is the same data as a query param). Response: { token, tf, source: "chainstore"|"live-scan", pool?, backfilled?, # chainstore only candles: [{t (unix sec, bucket start), o,h,l,c (USD), v (USD)}] } source=chainstore requires CHAIN_STORE_ENABLED=1 (default OFF): durable SQLite candles from the pool's full Swap history — any tf, real timestamps. Otherwise live-scan: a bounded backward pool scan; only 5m/1h/1d (other tf values are mapped to the nearest and echoed back), recent-window only, interpolated timestamps. First request for an untracked token promotes it (track+backfill) in the background when the store is on. Candles are bucketed trades — gaps mean no swaps, clients should not zero-fill. parameters: - { name: address, in: path, required: true, schema: { type: string } } - { name: tf, in: query, schema: { type: string, enum: [1m, 5m, 15m, 1h, 4h, 1d], default: 1h } } - { name: limit, in: query, schema: { type: integer, default: 60, maximum: 1000 } } responses: { "200": { description: "see endpoint description — prices/volume in USD" } } /v1/portfolio/{address}: get: summary: Wallet portfolio — slop curve holdings (own indexer) + external RHC holdings (Alchemy), with cost basis + PnL description: | THE PORTFOLIO CONTRACT (P0 buy→own→track loop). Two merged sources: slop CURVE tokens from our own indexer (candidates = wallet's trades ∪ Transfer-accounting holders table; balance = authoritative on-chain balanceOf via the launchpad RPC, so a fresh buy shows immediately; priced at the curve's last trade) and external mainnet tokens from the Alchemy token API (+ native ETH). Deduped by address, sorted by USD value desc. Response: { address, totalUsd, holdings: [{ address, symbol, name, iconUrl, metadataURI?, # slop only — derive meme image decimals, balance (base units, string), amount (float), priceWei?, # slop only — wei ETH per whole token priceUsd, valueUsd, category, slop?: true, # tappable → /t/
costBasisUsd?, # slop only — flat PnL mirror of `position` avgCostUsd?, # (USD; avgCostUsd per WHOLE token) — what pnlUsd?, pnlPct?, # the web Holding rows render; null w/o buys position?: { # slop only — the "your position" strip buys, sells, ethInWei, # total ETH paid across buys (incl. fee) ethOutWei, # total ETH received across sells (net) tokensBoughtWei, tokensSoldWei, avgCostWeiPerToken, # wei ETH per whole token (avg-cost) costBasisWei, costBasisUsd, valueWei, unrealizedEthWei, unrealizedUsd, realizedEthWei, realizedUsd, pnlPct # null when nothing was bought } }] } USD fields are null when ETH/USD is unavailable (no Alchemy key) — wei fields are always present, so clients can render Ξ-primary. USD PnL uses the CURRENT ETH/USD rate (an ETH-denominated book). parameters: [{ name: address, in: path, required: true, schema: { type: string } }] responses: { "200": { description: "see endpoint description" } } /v1/route: get: summary: Best route + quote for any RHC token pair (Uniswap V3 all fee tiers + V2 pairs, best-of per hop) description: | Live reads — V3 QuoterV2 across fees {100,500,3000,10000} in parallel, plus V2 getPair→reserves x·y·k math (HOODIE-class tokens are V2-only). ETH↔token is single-hop; token↔token hops through WETH. Response: { venue: "uniswap-v3"|"uniswap-v2"|"mixed", router, # the RHC UniversalRouter — execution target feeTier, hops: [fee…], path: [{venue: "v2"|"v3", fee, pair}], # per-hop → build commands multiHop, amountIn, amountOut, security, # tokenized equity (server-side refuses while display-only) interfaceFeeBps } # 0 while fees are OFF (no FeeRouter deployed) NB the RHC UniversalRouter is a fork whose swap commands take a trailing uint256[] minHopPriceX36 ([] = disabled) — see web/src/lib/universalRouter.ts. parameters: - { name: tokenIn, in: query, required: true, schema: { type: string } } - { name: tokenOut, in: query, required: true, schema: { type: string } } - { name: amountIn, in: query, required: true, description: base units, schema: { type: string } } responses: "200": { description: "see endpoint description" } "404": { description: no route found } "403": { description: securities display-only / geoblocked } /v1/health: get: summary: Liveness + data-source health (universe size/age/source, degraded flag) responses: { "200": { description: "{ ok, chainId, lastIndexedBlock, universe: {size, complete, ageSec, source, degraded}, sources }" } } /v1/profiles: get: summary: Batch username lookup for wallet addresses (feed rows, comments, creator lines) description: | Identity read path. `addresses` = comma-separated wallet addresses (≤100; junk entries ignored). Response keys are LOWERCASED addresses; wallets without a claimed profile are simply absent (clients render shortAddr): { profiles: { "0xabc…": { username, avatar } } } parameters: - { name: addresses, in: query, required: true, schema: { type: string } } responses: { "200": { description: "{ profiles: { [address]: { username, avatar } } }" } } /v1/profile: post: summary: Claim/update a username (wallet-signed, like /meta and /comments) description: | Identity write path (pump.fun-style usernames). Signed with EIP-191 personal_sign; message = `slop: claim username @