diff --git a/app/src/app/(home)/loading.tsx b/app/src/app/(home)/loading.tsx index 9d91e2e..13271d4 100644 --- a/app/src/app/(home)/loading.tsx +++ b/app/src/app/(home)/loading.tsx @@ -1,10 +1,11 @@ import { Sk, SkPost, SkRow } from "@/components/Skeleton"; +import { LaunchListHeader } from "@/components/launchpad/LaunchRow"; -/** Home skeleton: hero + totals, list header, rows, side column. Same grid as page.tsx so nothing shifts. */ +/** The readable hero, open market toolbar and support rail follow page.tsx. */ export default function Loading() { return ( -
-
+
+
{[0, 1, 2].map((i) => )} @@ -28,27 +29,35 @@ export default function Loading() {
-
+
+
+ +
+ {[0, 1, 2].map((i) =>
)} +
+
-
-
- - +
+
+
+ +
+
+
-
- - - +
+
{[0, 1, 2, 3, 4, 5].map((i) => )}
+
    {Array.from({ length: 8 }, (_, i) => ( ))}
-
); } diff --git a/app/src/app/(home)/page.tsx b/app/src/app/(home)/page.tsx index 5dc61f3..9aef62c 100644 --- a/app/src/app/(home)/page.tsx +++ b/app/src/app/(home)/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import Link from "next/link"; import LaunchHero from "@/components/launchpad/LaunchHero"; import LaunchList from "@/components/launchpad/LaunchList"; +import LaunchBrowser from "@/components/launchpad/LaunchBrowser"; import LaunchTape from "@/components/launchpad/LaunchTape"; import { PostsFeed } from "@/components/launchpad/Posts"; import { listFeed } from "@/lib/launchpad/postsServer"; @@ -30,29 +31,31 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s return ( <> -
- +
+
+
- -
+
); diff --git a/app/src/app/agents/page.tsx b/app/src/app/agents/page.tsx index bc13ee1..b105dac 100644 --- a/app/src/app/agents/page.tsx +++ b/app/src/app/agents/page.tsx @@ -1,8 +1,10 @@ import type { Metadata } from "next"; import { pageMetadata } from "@/lib/seo"; -import { ArrowDown, ArrowRight, ArrowUpRight, Braces, FileCode2, Terminal } from "lucide-react"; +import { ArrowDown, ArrowUpRight, Braces, FileCode2, Terminal } from "lucide-react"; import SectionIntro from "@/components/sections/SectionIntro"; +import DocumentationContents from "@/components/sections/DocumentationContents"; import AgentsCodeBlock from "@/components/sections/AgentsCodeBlock"; +import { agentExamples } from "@/components/sections/agent-examples"; import shell from "@/components/sections/SectionShell.module.css"; import styles from "@/components/sections/Agents.module.css"; import { launchpad } from "@/lib/launchpad/config"; @@ -25,16 +27,9 @@ const endpoints = [ ] as const; export default function AgentsPage() { - const factory = launchpad("base").factory ?? ""; - const locker = launchpad("base").locker ?? ""; - const launchExample = `cast send ${factory} \\ - "launch((string,string,string,address,uint256,int24,uint24,bytes32,(address,uint16)[]))" \\ - "(My Token,MYT,,0x0000000000000000000000000000000000000000,0,184200,10000,0x$(openssl rand -hex 32),[(0xYourWallet,10000)])" \\ - --rpc-url https://mainnet.base.org --private-key $PK`; - return (
- No API key, no signup, no platform fee. A launch is one contract call from any wallet with a little ETH for gas. Everything the site shows is also JSON.}> + No API key, no signup, no platform fee. A launch is one contract call from your wallet. Keep ETH for gas on Base and Robinhood Chain, or USDC on Arc. Everything the site shows is also JSON.}> Explore the source @@ -46,10 +41,7 @@ export default function AgentsPage() {
@@ -68,15 +60,15 @@ export default function AgentsPage() {
; })} -

Call launch(params) on that chain's factory. The example below launches on Base with an ETH quote.

- -

Example only. Replace the wallet placeholder and review the parameters before sending a transaction. Never share your private key.

+

Call launch(params) on that chain's factory. Choose a network below to see its address and chain ID. Base and Robinhood examples use ETH; Arc uses ERC-20 USDC with 6 decimals and an opening market cap of approximately 10,000 USDC.

+ +

Example only, not an executable transaction. Replace the placeholders, create a unique salt and simulate the call with your own wallet tooling before signing. Copying does not connect a wallet or send anything. Never share your private key.

-
quote
ETH uses address zero. For GITLAWB use 0x5F980Dcfc4c0fa3911554cf5ab288ed0eb13DBa3 on Base or 0xd1b0d44E4f6ed940fcC7A9F59Bf30Daf62cCFe3D on Robinhood Chain; for USDG on Robinhood Chain, 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168. For any ERC-20 quote call findSalt(...) first so the token address sorts above the quote.
+
quote
ETH uses address zero on Base and Robinhood Chain. Arc requires the ERC-20 USDC address, not address zero; native USDC accounting uses 18 decimals while the ERC-20 quote uses 6. {CHAIN_KEYS.map((chain) => {CHAIN_LABELS[chain]} quote addresses: {launchpad(chain).quotes.filter((quote) => quote.key !== "eth").map((quote, index) => {index > 0 ? "; " : ""}{quote.symbol} {quote.address})}. )}For any ERC-20 quote call findSalt(...) first so the token address sorts above the quote.
supply
0 means the default 1B supply.
-
startTick
Sets the opening market cap. A multiple of 200; 184200 ≈ 10 ETH FDV for 1B supply.
+
startTick
Sets the opening market cap. A multiple of 200; 184200 ≈ 10 ETH FDV for 1B supply. The examples derive this value using the chosen quote's decimals. Do not reuse an ETH tick for Arc's 6-decimal USDC quote.
lpFee
In pips: 0, 10000 = 1%, or 30000 = 3%.
recipients
Empty means fees are burned. Otherwise, shares are in bps and sum to 10000.
@@ -98,7 +90,7 @@ export default function AgentsPage() {
{endpoint.path}

{endpoint.description}

{"query" in endpoint && {endpoint.query}}
)} - # image / description / links\nGET ${SITE_URL}/llms.txt`} /> +

Raw token and quote amounts are decimal integer strings. Use quote_decimals for quote amounts, not a blanket 18 decimals. price_quote and fdv_quote are numbers in the token's quote asset; explicit price_usd, fdv_usd and volume_usd fields are USD values and may be null when pricing is unavailable.

Poll modestly and back off on errors. Indexing and server-side caching mean responses are not a guarantee of the latest chain state, even when an HTTP response uses no-store.

@@ -109,13 +101,13 @@ export default function AgentsPage() {
03

Trade & collect

Standard pools. Direct contract calls.

Swap

Speak Uniswap v4

Pools are plain Uniswap v4, with tick spacing 200 and no hook. The quote can be ETH, GITLAWB (Base and Robinhood Chain), USDG (Robinhood Chain), USDC (Arc) or a supported issuer-registry stock token. Get the actual pool key with poolKeyOf(token) on the factory. Swap through the Universal Router with a V4_SWAP command, or any router that speaks v4.

-
Collect

Pay out accrued fees

Anyone may call collect(tokenId) on that chain's locker to pay out accrued fees. Base locker: {locker}. Use the matching chain address above.

+
Collect

Pay out accrued fees

Anyone may call collect(tokenId) on that chain's locker to pay out accrued fees. Use the matching chain address above. Simulate the call first; collecting still needs a signed transaction and network gas.

Tokenized stocks are third-party securities offered under Regulation S, not to US persons. The site recognizes them from issuer registries, not on-chain names.

POST

Sync your transaction

After a transaction you sent, request immediate indexing with this endpoint. Background polling normally runs about every 15s; receipt availability and RPC errors can delay indexing.

- +
diff --git a/app/src/app/api/launch/watchlist/route.ts b/app/src/app/api/launch/watchlist/route.ts new file mode 100644 index 0000000..5037e9e --- /dev/null +++ b/app/src/app/api/launch/watchlist/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from "next/server"; +import { dbConfigured } from "@/lib/db"; +import { rateLimited } from "@/lib/launchpad/editServer"; +import { ethUsd } from "@/lib/launchpad/ethPrice"; +import { WatchlistInputError, getWatchlistData, parseWatchlistRequest, readWatchlistBody } from "@/lib/launchpad/watchlistData"; + +export const dynamic = "force-dynamic"; + +/** Read-only POST keeps the user's device-local token list out of URLs. Nothing is persisted. */ +export async function POST(req: Request) { + const headers = { "cache-control": "no-store" }; + const ip = (req.headers.get("fly-client-ip") || req.headers.get("x-forwarded-for") || "").split(",")[0].trim() || "0.0.0.0"; + if (rateLimited(`watchlist:ip:${ip}`, 60)) { + return NextResponse.json({ error: "Too many refreshes. Try again shortly." }, { status: 429, headers: { ...headers, "retry-after": "60" } }); + } + try { + const body = await readWatchlistBody(req); + const at = Date.now(); + const items = parseWatchlistRequest(body, at); + if (!dbConfigured()) return NextResponse.json({ error: "Watchlist activity is unavailable.", indexed: false }, { status: 503, headers }); + const data = await getWatchlistData(items, at, items.length ? await ethUsd() : null); + return NextResponse.json(data, { status: data.indexed ? 200 : 503, headers }); + } catch (error) { + if (error instanceof WatchlistInputError) return NextResponse.json({ error: error.message }, { status: error.status, headers }); + return NextResponse.json({ error: "Watchlist activity could not be refreshed. Your saved tokens are unchanged.", indexed: false }, { status: 503, headers }); + } +} diff --git a/app/src/app/feed/loading.tsx b/app/src/app/feed/loading.tsx index 80da540..2cfab79 100644 --- a/app/src/app/feed/loading.tsx +++ b/app/src/app/feed/loading.tsx @@ -10,11 +10,11 @@ export default function Loading() { return (
-
-
+
+
@@ -24,9 +24,11 @@ export default function Loading() {
-
-
-

+
+
+
+

+
    {Array.from({ length: 6 }, (_, i) => ( @@ -36,8 +38,8 @@ export default function Loading() {