diff --git a/app/src/app/layout.tsx b/app/src/app/layout.tsx index 83e0eea1..77bd3e85 100644 --- a/app/src/app/layout.tsx +++ b/app/src/app/layout.tsx @@ -9,7 +9,7 @@ import { getLaunchFeed, getLaunchTotals } from "@/lib/launchpad/queries"; import { ethUsd } from "@/lib/launchpad/ethPrice"; import { SITE_URL, explorerAddress } from "@/lib/chainPublic"; import { launchpad } from "@/lib/launchpad/config"; -import { BRAND, BRAND_DOMAIN, BRAND_TLD, BRAND_X, SITE_DESCRIPTION, SITE_TITLE, SOCIAL_DESCRIPTION } from "@/lib/brand"; +import { BRAND, BRAND_DOMAIN, BRAND_GITHUB, BRAND_TLD, BRAND_X, SITE_DESCRIPTION, SITE_TITLE, SOCIAL_DESCRIPTION } from "@/lib/brand"; import Mark from "@/components/launchpad/Mark"; import RouteProgress from "@/components/RouteProgress"; import { Suspense } from "react"; @@ -59,6 +59,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo how it works agents llms.txt + + source · GitHub ↗ + {b.factory ? ( factory · Base ↗ diff --git a/app/src/app/llms.txt/route.ts b/app/src/app/llms.txt/route.ts index 042d0533..82b60289 100644 --- a/app/src/app/llms.txt/route.ts +++ b/app/src/app/llms.txt/route.ts @@ -4,14 +4,35 @@ import { BRAND_DOMAIN } from "@/lib/brand"; export const dynamic = "force-dynamic"; +/** One "- : …" line per chain; links only for deployed contracts (factory / locker are nullable until configured). */ +function verificationLine(label: string, c: { factory: string | null; locker: string | null }, verifiers: [string, (a: string) => string][]): string { + if (!c.factory && !c.locker) return `- ${label}: (not deployed yet)`; + const parts = verifiers.map(([name, url]) => { + const links = [c.factory ? `${url(c.factory)} (factory)` : null, c.locker ? `${url(c.locker)} (locker)` : null].filter(Boolean).join(", "); + return `${name} ${links}`; + }); + return `- ${label}: ${parts.join("; ")}`; +} + export function GET() { const b = launchpad("base"); const r = launchpad("robinhood"); const body = `# ${BRAND_DOMAIN} -> Open-source (contracts verified on Sourcify; code release pending), zero-fee token launchpad on Base (8453) and Robinhood Chain (4663). One transaction deploys a token and locks 100% of its supply as +> Open-source (MIT — https://github.com/Gitlawb/openlaunch), zero-fee token launchpad on Base (8453) and Robinhood Chain (4663). One transaction deploys a token and locks 100% of its supply as > Uniswap v4 liquidity, forever. No platform fee: the factory and locker have no fee address at all. +## Source verification +${verificationLine("Base (8453)", b, [ + ["Basescan", (a) => `https://basescan.org/address/${a}#code`], + ["Blockscout", (a) => `https://base.blockscout.com/address/${a}?tab=contract`], + ["Sourcify", (a) => `https://repo.sourcify.dev/8453/${a}`], +])} +${verificationLine("Robinhood Chain (4663)", r, [ + ["Blockscout", (a) => `https://robinhoodchain.blockscout.com/address/${a}?tab=contract`], + ["Sourcify", (a) => `https://repo.sourcify.dev/4663/${a}`], +])} + ## What a launch does 1. deploys a fixed-supply ERC-20 (1,000,000,000; no mint/pause/blacklist/tax/owner) 2. initializes a Uniswap v4 pool quote/token (quote = ETH, or USDG on Robinhood Chain; tick spacing 200, no hook) at the chosen start tick diff --git a/app/src/app/rules/page.tsx b/app/src/app/rules/page.tsx index e39d1a02..5ac46962 100644 --- a/app/src/app/rules/page.tsx +++ b/app/src/app/rules/page.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import { btn, cardPad } from "@/components/ui"; import { launchpad } from "@/lib/launchpad/config"; import { CHAIN_KEYS, CHAIN_LABELS, explorerAddress } from "@/lib/chainPublic"; +import { BRAND_GITHUB } from "@/lib/brand"; export const metadata: Metadata = { title: "How it works", description: "What a launch does on-chain, what it costs (gas), and what can never happen to your liquidity." }; @@ -20,6 +21,19 @@ const NEVER = [ ["No admin", "No owner, no pause, no upgrade, no allowlist. The contracts are the same for everyone, forever."], ] as const; +/** Where each chain's contract source is verified (links go to the explorer's contract record). */ +const VERIFIERS: Record<(typeof CHAIN_KEYS)[number], { name: string; url: (addr: string) => string }[]> = { + base: [ + { name: "Basescan", url: (a) => `https://basescan.org/address/${a}#code` }, + { name: "Blockscout", url: (a) => `https://base.blockscout.com/address/${a}?tab=contract` }, + { name: "Sourcify", url: (a) => `https://repo.sourcify.dev/8453/${a}` }, + ], + robinhood: [ + { name: "Blockscout", url: (a) => `https://robinhoodchain.blockscout.com/address/${a}?tab=contract` }, + { name: "Sourcify", url: (a) => `https://repo.sourcify.dev/4663/${a}` }, + ], +}; + export default function RulesPage() { return (
@@ -108,10 +122,24 @@ export default function RulesPage() { not deployed yet )}

+ {c.factory && c.locker ? ( +

+ Source verified:{" "} + {VERIFIERS[k].map((v, i) => ( + + {i > 0 ? " · " : ""} + {v.name} (factory,{" "} + locker) + + ))} +

+ ) : null} ); })} -

Identical bytecode on both chains. Uniswap v4's PoolManager, PositionManager, Permit2 and Universal Router are Uniswap's canonical deployments. MIT-licensed source, verified on Sourcify.

+

Identical bytecode on both chains. Uniswap v4's PoolManager, PositionManager, Permit2 and Universal Router are Uniswap's canonical deployments. MIT-licensed source on{" "} + GitHub ↗; the verification records for each chain are linked above. +

diff --git a/app/src/components/launchpad/LaunchHero.tsx b/app/src/components/launchpad/LaunchHero.tsx index dd3e57d0..a7a5d696 100644 --- a/app/src/components/launchpad/LaunchHero.tsx +++ b/app/src/components/launchpad/LaunchHero.tsx @@ -3,6 +3,7 @@ import { btn, pill } from "@/components/ui"; import LiveTotals from "./LiveTotals"; import { launchpad } from "@/lib/launchpad/config"; import { explorerAddress } from "@/lib/chainPublic"; +import { BRAND_GITHUB } from "@/lib/brand"; export default function LaunchHero({ configured }: { ethUsd?: number | null; configured: boolean }) { const b = launchpad("base"); @@ -11,8 +12,11 @@ export default function LaunchHero({ configured }: { ethUsd?: number | null; con
0% platform fee + + open source · GitHub ↗ + - open source · verified contracts + verified contracts Base + Robinhood Chain · Uniswap v4 liquidity locked forever diff --git a/app/src/lib/brand.ts b/app/src/lib/brand.ts index af4aa7fa..3900bb4c 100644 --- a/app/src/lib/brand.ts +++ b/app/src/lib/brand.ts @@ -4,7 +4,7 @@ export const BRAND_TLD = ".lol"; export const BRAND_DOMAIN = `${BRAND}${BRAND_TLD}`; // openlaunch.lol /** @openlaunchlol and the openlaunchlol GitHub org were squatted after the rename — never link them. */ export const BRAND_X = "gitlawb"; -export const BRAND_GITHUB = "https://github.com/Gitlawb"; +export const BRAND_GITHUB = "https://github.com/Gitlawb/openlaunch"; // MIT, contracts + site export const TAGLINE = "launch a token. Free. Open source. On Base or Robinhood Chain."; /** * Per-surface copy (each surface truncates at a different point): diff --git a/app/src/lib/launchpad/creator.test.ts b/app/src/lib/launchpad/creator.test.ts index 485616f7..37505211 100644 --- a/app/src/lib/launchpad/creator.test.ts +++ b/app/src/lib/launchpad/creator.test.ts @@ -60,7 +60,7 @@ test("share card shaping", () => { const now = Date.parse("2026-09-06T12:00:00Z"); const c = shapeCard({ name: "Dollar Dog", symbol: "DDOG", chain: "robinhood", fdv_usd: 12345, fdv_quote: 12345, quote_symbol: "USDG", change_from_launch: 0.234, lp_fee: 10000, recipients: [{ payout: DEAD, bps: 10000 }], block_time: "2026-09-06T09:30:00Z" }, now); assert.deepEqual(c, { title: "Dollar Dog", symbol: "DDOG", chainLabel: "Robinhood Chain", mcap: "$12.3K", change: "+23%", up: true, fee: "1% fee, burned", age: "2h old", quote: null }); - assert.deepEqual(shapeCard({ name: "Base Stock Test", symbol: "BSTK", chain: "base", fdv_usd: 25206, fdv_quote: 109.6, quote_symbol: "NVDAc", change_from_launch: 0, lp_fee: 10000, recipients: [{ payout: "0x63f300b7b21fba52a8d874d90f7e1b301ae858c4", bps: 10000 }], block_time: "2026-09-06T09:30:00Z" }, now).quote, { symbol: "NVDAc", ticker: "NVDA" }, "stock quotes get a ticker tile on the card"); + assert.deepEqual(shapeCard({ name: "Base Stock Test", symbol: "BSTK", chain: "base", fdv_usd: 25206, fdv_quote: 109.6, quote_symbol: "NVDAc", change_from_launch: 0, lp_fee: 10000, recipients: [{ payout: "0x00000000000000000000000000000000000c0ffe", bps: 10000 }], block_time: "2026-09-06T09:30:00Z" }, now).quote, { symbol: "NVDAc", ticker: "NVDA" }, "stock quotes get a ticker tile on the card"); assert.equal(feeLabel(0, []), "0% fee"); assert.equal(feeLabel(30000, [{ payout: "0x1", bps: 10000 }]), "3% fee → beneficiary"); assert.equal(ageLabel("2026-09-06T11:59:30Z", now), "1m old"); diff --git a/app/src/lib/launchpad/holders.test.ts b/app/src/lib/launchpad/holders.test.ts index a57090aa..3406a0ec 100644 --- a/app/src/lib/launchpad/holders.test.ts +++ b/app/src/lib/launchpad/holders.test.ts @@ -4,7 +4,7 @@ import { DEAD_ADDR, ZERO_ADDR, applyTransfer, creatorActivity, fmtShare, holderT const PM = "0x498581fF718922c3f8e6A244956aF099B2652b2b"; const LOCKER = "0xcd1680D26922fcd9CabFbb8a56bA40C333fD842a"; -const DEV = "0x63f300b7b21fba52a8d874d90f7e1b301ae858c4"; +const DEV = "0x00000000000000000000000000000000000c0ffe"; const A = "0x" + "a".repeat(40); const B = "0x" + "b".repeat(40); const SUPPLY = 10n ** 27n; diff --git a/app/src/lib/launchpad/metaShared.test.ts b/app/src/lib/launchpad/metaShared.test.ts index 5e965fa0..263de5bb 100644 --- a/app/src/lib/launchpad/metaShared.test.ts +++ b/app/src/lib/launchpad/metaShared.test.ts @@ -2,7 +2,7 @@ import { test } from "node:test"; import assert from "node:assert/strict"; import { metaUriFor, metaWriteDecision, validateMeta } from "./metaShared.ts"; -const L = "0x63F300b7b21FbA52A8D874D90F7E1B301AE858c4"; +const L = "0x00000000000000000000000000000000000c0ffe"; const SALT = "0x" + "a".repeat(64); const KEY = "0x" + "B".repeat(64); const base = { chain: "base" as const, launcher: L, salt: SALT, name: "Clear Sky", symbol: "sky" }; @@ -22,7 +22,7 @@ test("validateMeta: meta_key is optional (defaults to the salt) and normalized t test("metaUriFor is keyed by (launcher, meta_key) and is stable while the salt changes", () => { const u1 = metaUriFor(L, KEY); - assert.match(u1, /\/api\/launch\/meta\/0x63f300b7b21fba52a8d874d90f7e1b301ae858c4\/0xbbbb/); + assert.match(u1, /\/api\/launch\/meta\/0x00000000000000000000000000000000000c0ffe\/0xbbbb/); assert.equal(metaUriFor(L.toLowerCase(), KEY.toLowerCase()), u1, "case-insensitive"); assert.notEqual(metaUriFor(L, SALT), u1, "a different key → different URI"); });